返回 last30days-skill
extract_test.go
根目录 / mcp / internal / engine / extract_test.go
1 package engine
2
3 import (
4 "os"
5 "path/filepath"
6 "sync"
7 "testing"
8 "testing/fstest"
9 )
10
11 func newTestFS() fstest.MapFS {
12 return fstest.MapFS{
13 "last30days.py": &fstest.MapFile{Data: []byte("# last30days entry\n"), Mode: 0o644},
14 "lib/__init__.py": &fstest.MapFile{Data: []byte(""), Mode: 0o644},
15 "lib/env.py": &fstest.MapFile{Data: []byte("# env helpers\n"), Mode: 0o644},
16 }
17 }
18
19 func TestEnsureExtractsEngine(t *testing.T) {
20 src := newTestFS()
21 base := t.TempDir()
22
23 cacheDir, err := Ensure(src, base, "v1")
24 if err != nil {
25 t.Fatalf("Ensure: %v", err)
26 }
27 if cacheDir != filepath.Join(base, cacheSubdir, "v1") {
28 t.Fatalf("cacheDir = %q, want %q", cacheDir, filepath.Join(base, cacheSubdir, "v1"))
29 }
30 mustReadFile(t, filepath.Join(cacheDir, "last30days.py"), "# last30days entry\n")
31 mustReadFile(t, filepath.Join(cacheDir, "lib/env.py"), "# env helpers\n")
32 mustReadFile(t, filepath.Join(cacheDir, SentinelFilename), "v1")
33 }
34
35 func TestEnsureSkipsWhenSentinelMatches(t *testing.T) {
36 src := newTestFS()
37 base := t.TempDir()
38
39 cacheDir, err := Ensure(src, base, "v1")
40 if err != nil {
41 t.Fatalf("first Ensure: %v", err)
42 }
43 target := filepath.Join(cacheDir, "last30days.py")
44 info1, err := os.Stat(target)
45 if err != nil {
46 t.Fatalf("stat: %v", err)
47 }
48
49 // Reset the sync.Once so a second call would re-extract if not for the
50 // sentinel short-circuit. Without the reset, sync.Once would skip the
51 // extraction regardless of sentinel state.
52 resetOnce(cacheDir)
53
54 if _, err := Ensure(src, base, "v1"); err != nil {
55 t.Fatalf("second Ensure: %v", err)
56 }
57 info2, err := os.Stat(target)
58 if err != nil {
59 t.Fatalf("stat second: %v", err)
60 }
61 if !info2.ModTime().Equal(info1.ModTime()) {
62 t.Fatalf("expected file untouched on sentinel match; got mtime %v -> %v", info1.ModTime(), info2.ModTime())
63 }
64 }
65
66 func TestEnsureReExtractsOnVersionChange(t *testing.T) {
67 v1 := fstest.MapFS{
68 "last30days.py": &fstest.MapFile{Data: []byte("v1\n"), Mode: 0o644},
69 }
70 v2 := fstest.MapFS{
71 "last30days.py": &fstest.MapFile{Data: []byte("v2\n"), Mode: 0o644},
72 }
73 base := t.TempDir()
74
75 cache1, err := Ensure(v1, base, "v1")
76 if err != nil {
77 t.Fatalf("Ensure v1: %v", err)
78 }
79 cache2, err := Ensure(v2, base, "v2")
80 if err != nil {
81 t.Fatalf("Ensure v2: %v", err)
82 }
83 if cache1 == cache2 {
84 t.Fatalf("expected distinct cache dirs per version, got %q == %q", cache1, cache2)
85 }
86 mustReadFile(t, filepath.Join(cache1, "last30days.py"), "v1\n")
87 mustReadFile(t, filepath.Join(cache2, "last30days.py"), "v2\n")
88 }
89
90 func TestEnsureConcurrentFirstCall(t *testing.T) {
91 src := newTestFS()
92 base := t.TempDir()
93
94 const goroutines = 10
95 var wg sync.WaitGroup
96 wg.Add(goroutines)
97 results := make([]string, goroutines)
98 errs := make([]error, goroutines)
99 for i := 0; i < goroutines; i++ {
100 i := i
101 go func() {
102 defer wg.Done()
103 results[i], errs[i] = Ensure(src, base, "v1")
104 }()
105 }
106 wg.Wait()
107
108 for i, err := range errs {
109 if err != nil {
110 t.Fatalf("goroutine %d: %v", i, err)
111 }
112 }
113 for i := 1; i < goroutines; i++ {
114 if results[i] != results[0] {
115 t.Fatalf("goroutine 0 saw %q, goroutine %d saw %q", results[0], i, results[i])
116 }
117 }
118 mustReadFile(t, filepath.Join(results[0], "last30days.py"), "# last30days entry\n")
119 }
120
121 func TestEnsureRejectsEmptyVersion(t *testing.T) {
122 if _, err := Ensure(newTestFS(), t.TempDir(), ""); err == nil {
123 t.Fatal("expected error for empty version")
124 }
125 }
126
127 func TestEnsureReturnsErrorWhenCacheUnwritable(t *testing.T) {
128 // Place the cache root at a path that cannot exist (a regular file).
129 // MkdirAll will refuse and Ensure must surface a wrapped error.
130 base := t.TempDir()
131 blocker := filepath.Join(base, "blocker")
132 if err := os.WriteFile(blocker, []byte("not a dir"), 0o644); err != nil {
133 t.Fatalf("setup: %v", err)
134 }
135
136 _, err := Ensure(newTestFS(), blocker, "v1")
137 if err == nil {
138 t.Fatal("expected error when cache parent is not a directory")
139 }
140 }
141
142 func TestEnsureUserCacheHonorsOverride(t *testing.T) {
143 override := t.TempDir()
144 t.Setenv(CacheEnvOverride, override)
145
146 src := newTestFS()
147 cacheDir, err := EnsureUserCache(src, "v1")
148 if err != nil {
149 t.Fatalf("EnsureUserCache: %v", err)
150 }
151 want := filepath.Join(override, cacheSubdir, "v1")
152 if cacheDir != want {
153 t.Fatalf("cacheDir = %q, want %q", cacheDir, want)
154 }
155 mustReadFile(t, filepath.Join(cacheDir, "last30days.py"), "# last30days entry\n")
156 }
157
158 func mustReadFile(t *testing.T, path, want string) {
159 t.Helper()
160 data, err := os.ReadFile(path)
161 if err != nil {
162 t.Fatalf("read %s: %v", path, err)
163 }
164 if string(data) != want {
165 t.Fatalf("%s: got %q, want %q", path, string(data), want)
166 }
167 }
168
168 lines GO