Repository navigation
Expand file tree
/
Copy pathvalidation_baseline_test.go
More file actions
196 lines (156 loc) · 4.64 KB
/
Copy pathvalidation_baseline_test.go
File metadata and controls
196 lines (156 loc) · 4.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package moduledoc
import (
"go/ast"
"go/parser"
"go/token"
"go/types"
"testing"
"golang.org/x/tools/go/packages"
)
// TestModuleValidationOriginal tests the strict validation logic of the original implementation
func TestModuleValidationOriginal(t *testing.T) {
t.Run("ValidModulePattern", func(t *testing.T) {
// Test the valid pattern: registration + implementation + static ID
source := `
package test
import "github.com/caddyserver/caddy/v2"
func init() {
caddy.RegisterModule(new(ValidModule))
}
type ValidModule struct{}
func (*ValidModule) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "app.test.valid",
New: func() caddy.Module { return new(ValidModule) },
}
}
`
// This should work with the original implementation
testModuleSource(t, source, true, "Valid module should be detected")
})
t.Run("MissingImplementation", func(t *testing.T) {
// Test registration without implementation
source := `
package test
import "github.com/caddyserver/caddy/v2"
func init() {
caddy.RegisterModule(new(IncompleteModule))
}
type IncompleteModule struct{}
// Missing CaddyModule() method
`
// This should fail in original implementation
testModuleSource(t, source, false, "Module without implementation should fail")
})
t.Run("ConstantModuleID", func(t *testing.T) {
source := `
package test
import "github.com/caddyserver/caddy/v2"
func init() {
caddy.RegisterModule(new(ComputedModule))
}
type ComputedModule struct{}
func (*ComputedModule) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: caddy.ModuleID("computed." + "dynamic"),
New: func() caddy.Module { return new(ComputedModule) },
}
}
`
testModuleSource(t, source, true, "Constant module ID should be detected")
})
t.Run("NonStaticModuleID", func(t *testing.T) {
source := `
package test
import "github.com/caddyserver/caddy/v2"
func init() {
caddy.RegisterModule(new(ComputedModule))
}
type ComputedModule struct{}
var moduleID = "computed.dynamic"
func (*ComputedModule) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: caddy.ModuleID(moduleID),
New: func() caddy.Module { return new(ComputedModule) },
}
}
`
testModuleSource(t, source, false, "Runtime-computed module ID should be skipped")
})
t.Run("constModuleID", func(t *testing.T) {
source := `
package test
import "github.com/caddyserver/caddy/v2"
func init() {
caddy.RegisterModule(new(ConstIDModule))
}
type ConstIDModule struct{}
const moduleID = "const.mod"
func (*ConstIDModule) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: caddy.ModuleID(moduleID),
New: func() caddy.Module { return new(ConstIDModule) },
}
}
`
testModuleSource(t, source, true, "const module ID should be successful")
})
}
// testModuleSource is a helper that tests module detection on source code
func testModuleSource(t *testing.T, source string, expectSuccess bool, message string) {
t.Helper()
fset := token.NewFileSet()
// Accept any registration argument so invalid module fixtures reach validation.
caddyFile, err := parser.ParseFile(fset, "caddy.go", `package caddy
type ModuleID string
type Module interface { CaddyModule() ModuleInfo }
type ModuleInfo struct {
ID ModuleID
New func() Module
}
func RegisterModule(any) {}
`, 0)
if err != nil {
t.Fatalf("Failed to parse Caddy stub: %v", err)
}
caddyPkg, err := new(types.Config).Check(caddyCorePackagePath, fset, []*ast.File{caddyFile}, nil)
if err != nil {
t.Fatalf("Failed to type-check Caddy stub: %v", err)
}
file, err := parser.ParseFile(fset, "test.go", source, parser.ParseComments)
if err != nil {
t.Fatalf("Failed to parse source: %v", err)
}
info := &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Uses: make(map[*ast.Ident]types.Object),
Defs: make(map[*ast.Ident]types.Object),
}
cfg := types.Config{Importer: registrationTestImporter{caddyPkg}}
typesPkg, err := cfg.Check("test", fset, []*ast.File{file}, info)
if err != nil {
t.Fatalf("Failed to type-check source: %v", err)
}
pkg := &packages.Package{
Name: "test",
Syntax: []*ast.File{file},
Types: typesPkg,
TypesInfo: info,
}
driver := New(nil)
moduleIdents, err := driver.findCaddyModuleIdents(pkg)
if expectSuccess {
if err != nil {
t.Errorf("%s: Expected success but got error: %v", message, err)
}
if len(moduleIdents) == 0 {
t.Errorf("%s: Expected to find modules but found none", message)
}
t.Logf("%s: ✓ Found %d modules", message, len(moduleIdents))
} else {
if err == nil && len(moduleIdents) > 0 {
t.Errorf("%s: Expected failure/skip but found modules: %v", message, moduleIdents)
}
t.Logf("%s: ✓ Correctly failed or skipped as expected", message)
}
}