-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathwalk_test.go
More file actions
194 lines (179 loc) · 6.56 KB
/
Copy pathwalk_test.go
File metadata and controls
194 lines (179 loc) · 6.56 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
package engine
import (
"os"
"path/filepath"
"slices"
"testing"
"github.com/enola-labs/enola/internal/config"
)
// writeTree materializes a repo-relative file map under root, creating parents.
func writeTree(t *testing.T, root string, files map[string]string) {
t.Helper()
for rel, content := range files {
p := filepath.Join(root, filepath.FromSlash(rel))
if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
t.Fatalf("MkdirAll(%s): %v", filepath.Dir(p), err)
}
if err := os.WriteFile(p, []byte(content), 0o644); err != nil {
t.Fatalf("WriteFile(%s): %v", p, err)
}
}
}
// walkFixture builds a repo under a temp dir and walks it with the given ignore
// patterns, returning walkRepo's three results.
func walkFixture(t *testing.T, ignore []string, files map[string]string) ([]string, []string, walkSkips) {
t.Helper()
root := t.TempDir()
writeTree(t, root, files)
cfg := config.Default()
cfg.Ignore = ignore
eng, err := New(cfg)
if err != nil {
t.Fatalf("New: %v", err)
}
srcFiles, testFiles, skips, err := eng.walkRepo(root)
if err != nil {
t.Fatalf("walkRepo: %v", err)
}
return srcFiles, testFiles, skips
}
// TestWalkRepo_IgnoredDirectoryIsCounted pins GAP-EN-01.
//
// An ignored DIRECTORY is pruned with filepath.SkipDir, so the walker never
// visits the files inside it: they are neither seen nor skipped, they are in no
// bucket at all. Before this was fixed, `files_skipped` counted only the ignored
// FILES the walker happened to reach — on fairwayhub/golf-ui it reported 9 for a
// repo with 67,270 ignored files, and named not one of them.
//
// A pruned directory is recorded once, as a directory. Walking node_modules/
// purely to count its 55,041 files would cost a stat apiece and say nothing an
// architecture graph wants to know.
func TestWalkRepo_IgnoredDirectoryIsCounted(t *testing.T) {
files, testFiles, skips := walkFixture(t,
[]string{"node_modules/**", "**/*.test.ts"},
map[string]string{
"node_modules/left-pad/index.js": "module.exports = 1\n",
"node_modules/left-pad/README.md": "# left-pad\n",
"src/app.ts": "export const a = 1\n",
"src/app.test.ts": "test('a', () => {})\n",
})
if skips.dirCount != 1 {
t.Errorf("dirCount = %d, want 1 (node_modules/ was pruned and must be counted)", skips.dirCount)
}
if skips.count != 1 {
t.Errorf("count = %d, want 1 (src/app.test.ts is the only ignored FILE the walker reaches)", skips.count)
}
if len(files) != 1 || filepath.ToSlash(files[0]) != "src/app.ts" {
t.Errorf("files = %v, want exactly [src/app.ts]", files)
}
for _, f := range files {
if got := filepath.ToSlash(f); len(got) >= 13 && got[:13] == "node_modules/" {
t.Errorf("pruned subtree leaked into files: %q", got)
}
}
if len(testFiles) != 1 || filepath.ToSlash(testFiles[0]) != "src/app.test.ts" {
t.Errorf("testFiles = %v, want [src/app.test.ts] (a .test.ts is ignored for indexing but matches the default TestGlob, so it is collected for reference-only extraction)", testFiles)
}
}
// TestWalkRepo_SkippedSampleNamesTheGlob pins GAP-EN-02.
//
// isIgnored knew which pattern matched and threw it away, so the receipt could
// say what was skipped but never why. Diagnosing a mis-scoped ignore rule meant
// re-deriving the match by hand.
func TestWalkRepo_SkippedSampleNamesTheGlob(t *testing.T) {
_, _, skips := walkFixture(t,
[]string{"node_modules/**", "**/*.test.ts"},
map[string]string{
"node_modules/left-pad/index.js": "module.exports = 1\n",
"src/app.ts": "export const a = 1\n",
"src/app.test.ts": "test('a', () => {})\n",
})
want := []string{
"node_modules/ (glob: node_modules/**)",
"src/app.test.ts (glob: **/*.test.ts)",
}
for _, w := range want {
if !slices.Contains(skips.sample, w) {
t.Errorf("skipped_sample missing %q\ngot: %v", w, skips.sample)
}
}
}
// TestWalkRepo_OutputDirNotCountedAsSkippedDir pins the output-dir guard.
//
// .enola/ is enola's own output, not part of the source tree. Counting it would
// make dirs_skipped differ between a repo's first-ever snapshot (no .enola/ yet)
// and every snapshot after it — a phantom delta in diff_snapshot, for no signal.
func TestWalkRepo_OutputDirNotCountedAsSkippedDir(t *testing.T) {
files, _, skips := walkFixture(t,
[]string{".enola/**"},
map[string]string{
".enola/facts.jsonl": "{}\n",
".enola/receipt.json": "{}\n",
"src/app.ts": "export const a = 1\n",
})
if skips.dirCount != 0 {
t.Errorf("dirCount = %d, want 0 (the output dir is enola's own artifact)", skips.dirCount)
}
if len(skips.sample) != 0 {
t.Errorf("sample = %v, want empty", skips.sample)
}
if len(files) != 1 || filepath.ToSlash(files[0]) != "src/app.ts" {
t.Errorf("files = %v, want exactly [src/app.ts]", files)
}
}
// TestMatchGlob_ReturnsMatchedPattern guards the pattern-reporting split of
// matchAnyGlob. The "<prefix>/**/<fileglob>" branch must keep its `continue`:
// letting such a pattern fall through to the branches below matches it only when
// exactly one directory sits between prefix and file, which is an artifact of
// filepath.Match reading "**" as "*". That is the bug of fixed/28.
func TestMatchGlob_ReturnsMatchedPattern(t *testing.T) {
rubyTestGlobs := []string{"**/spec/**/*_spec.rb", "**/test/**/*_test.rb"}
tests := []struct {
name string
relPath string
patterns []string
want string
wantOK bool
}{
{
"directory glob names itself",
"node_modules", []string{"vendor/**", "node_modules/**"},
"node_modules/**", true,
},
{
"file glob names itself",
"src/app.test.ts", []string{"node_modules/**", "**/*.test.ts"},
"**/*.test.ts", true,
},
{
"dir-scoped glob names itself, not a near-miss",
"spec/models/user_spec.rb", []string{"vendor/**", "**/spec/**/*_spec.rb"},
"**/spec/**/*_spec.rb", true,
},
{
"nested dir-scoped glob",
"engines/core/spec/services/report_worker_spec.rb", rubyTestGlobs,
"**/spec/**/*_spec.rb", true,
},
{
// fixed/28: a production ActiveJob whose name ends in the token "test".
"production job ending in _ab_test matches nothing",
"app/jobs/reporting/cache_warmup_ab_test.rb", rubyTestGlobs,
"", false,
},
{
"first matching pattern wins",
"vendor/spec/thing_spec.rb", []string{"vendor/**", "**/spec/**/*_spec.rb"},
"vendor/**", true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, ok := matchGlob(tt.relPath, tt.patterns)
if ok != tt.wantOK || got != tt.want {
t.Errorf("matchGlob(%q, %v) = (%q, %v), want (%q, %v)",
tt.relPath, tt.patterns, got, ok, tt.want, tt.wantOK)
}
})
}
}