Repository navigation
Expand file tree
/
Copy pathcache_test.go
More file actions
187 lines (177 loc) · 6.64 KB
/
Copy pathcache_test.go
File metadata and controls
187 lines (177 loc) · 6.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
package main
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/Agent-Field/codeaf/internal/cachedir"
"github.com/Agent-Field/codeaf/internal/home"
"github.com/Agent-Field/codeaf/internal/manual"
)
func seedCache(t *testing.T) string {
t.Helper()
root := t.TempDir()
t.Setenv(home.EnvVar, root)
dir := filepath.Join(cachedir.Root(), "toolchain", "go-mod")
if err := os.MkdirAll(dir, 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "blob"), make([]byte, 4096), 0o644); err != nil {
t.Fatal(err)
}
return root
}
// The guard itself: every answer to the prompt that is not [cacheCleanWord] —
// a y, an empty line, a closed stdin — keeps the cache. A destructive prompt
// fails closed.
func TestCacheCleanKeepsEverythingUnlessTheWordIsTyped(t *testing.T) {
seedCache(t)
_ = captureAside(t)
for _, answer := range []string{"y\n", "yes\n", "\n", ""} {
var out strings.Builder
if err := runCacheWith([]string{"clean"}, strings.NewReader(answer), &out); err != nil {
t.Fatalf("answer %q errored: %v", answer, err)
}
if !strings.Contains(out.String(), "kept — nothing was deleted.") {
t.Fatalf("answer %q did not report keeping:\n%s", answer, out.String())
}
if cachedir.Size() == 0 {
t.Fatalf("answer %q deleted the cache", answer)
}
}
}
// The word proceeds, the blast radius was said first, and the receipt names
// what left.
func TestCacheCleanActsOnTheTypedWord(t *testing.T) {
seedCache(t)
commentary := captureAside(t)
var out strings.Builder
if err := runCacheWith([]string{"clean"}, strings.NewReader(cacheCleanWord+"\n"), &out); err != nil {
t.Fatal(err)
}
text := commentary.String() + out.String()
for _, want := range []string{"This deletes the shared build cache", "not touched", "4.0 KB freed"} {
if !strings.Contains(text, want) {
t.Errorf("the clean transcript is missing %q:\n%s", want, text)
}
}
if cachedir.Size() != 0 {
t.Fatal("the cache survived a confirmed clean")
}
}
// --yes is the scripted door: no prompt, no stdin read, same receipt.
func TestCacheCleanYesSkipsTheQuestion(t *testing.T) {
seedCache(t)
commentary := captureAside(t)
var out strings.Builder
if err := runCacheWith([]string{"clean", "--yes"}, strings.NewReader(""), &out); err != nil {
t.Fatal(err)
}
if strings.Contains(commentary.String()+out.String(), `Type "`) {
t.Fatalf("--yes still asked:\n%s%s", commentary.String(), out.String())
}
if cachedir.Size() != 0 {
t.Fatal("--yes did not clean")
}
}
// The reading form answers over both states, and an empty cache is a sentence
// rather than a zero.
func TestBareCacheAnswersBothWays(t *testing.T) {
seedCache(t)
var out strings.Builder
if err := runCacheWith(nil, strings.NewReader(""), &out); err != nil {
t.Fatal(err)
}
if !strings.Contains(out.String(), "4.0 KB") {
t.Fatalf("bare cache did not say the size:\n%s", out.String())
}
if _, err := cachedir.Clean(); err != nil {
t.Fatal(err)
}
out.Reset()
if err := runCacheWith(nil, strings.NewReader(""), &out); err != nil {
t.Fatal(err)
}
if !strings.Contains(out.String(), "the cache is empty") {
t.Fatalf("an empty cache did not say so:\n%s", out.String())
}
}
// Row 28. The prompt was on stdout, which is where the answer goes, so
// `codeaf cache clean | tee clean.log` put the question in the log file and
// left the person looking at a blank terminal waiting for a word they could not
// see. The receipt stays on stdout: that IS the answer.
func TestTheCacheQuestionIsAskedOffTheAnswerStream(t *testing.T) {
seedCache(t)
commentary := captureAside(t)
var answer strings.Builder
if err := runCacheWith([]string{"clean"}, strings.NewReader(cacheCleanWord+"\n"), &answer); err != nil {
t.Fatal(err)
}
for _, want := range []string{"This deletes the shared build cache", "not touched", `Type "` + cacheCleanWord + `"`} {
if !strings.Contains(commentary.String(), want) {
t.Errorf("the question is missing %q from the aside:\n%s", want, commentary.String())
}
if strings.Contains(answer.String(), want) {
t.Errorf("stdout carries %q, which is part of the question and not the answer:\n%s", want, answer.String())
}
}
if !strings.Contains(answer.String(), "freed") {
t.Errorf("the receipt is not on stdout, where a script reads it:\n%s", answer.String())
}
}
// Row 21. One destructive gesture, two surfaces, one word. The chat cannot pass
// a flag, so it has always wanted `/cache clean now`; the terminal wanted the
// word "clean", and a person who had learned one typed it at the other's
// prompt — at the one prompt in the product that deletes gigabytes.
func TestBothSurfacesConfirmTheCacheDeletionWithTheSameWord(t *testing.T) {
page, found := manual.Chat().Page("commands")
if !found {
t.Fatal("the manual has no `commands` page to check the chat's word against")
}
if !strings.Contains(page, "/cache clean "+cacheCleanWord) {
t.Fatalf("the chat's confirming word is not %q — the terminal prompt and the chat have drifted apart again", cacheCleanWord)
}
if !strings.Contains(usageText, `type "`+cacheCleanWord+`"`) {
t.Fatalf("`codeaf --help` does not tell a person to type %q before the cache is deleted", cacheCleanWord)
}
seedCache(t)
_ = captureAside(t)
var answer strings.Builder
if err := runCacheWith([]string{"clean"}, strings.NewReader("clean\n"), &answer); err != nil {
t.Fatal(err)
}
if cachedir.Size() == 0 {
t.Fatalf("the old word still deletes the cache — there are two confirming words again")
}
if !strings.Contains(answer.String(), "kept — nothing was deleted.") {
t.Fatalf("a word that is not %q did not keep the cache:\n%s", cacheCleanWord, answer.String())
}
}
func TestCacheQuestionNamesRetainedLegacyLocation(t *testing.T) {
t.Setenv(home.EnvVar, t.TempDir())
path := home.Join("workspace-caches", "legacy", "cache")
if err := os.MkdirAll(path, 0700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(path, "compiler"), []byte("cache"), 0600); err != nil {
t.Fatal(err)
}
var shown strings.Builder
if err := runCacheWith(nil, strings.NewReader(""), &shown); err != nil {
t.Fatal(err)
}
if !strings.Contains(shown.String(), home.Join("workspace-caches")) {
t.Fatalf("cache display omitted affected old location: %s", shown.String())
}
commentary := captureAside(t)
var output strings.Builder
if err := runCacheWith([]string{"clean"}, strings.NewReader("no\n"), &output); err != nil {
t.Fatal(err)
}
if !strings.Contains(commentary.String(), home.Join("workspace-caches")) {
t.Fatalf("cleanup confirmation omitted affected old location: %s", commentary.String())
}
if _, err := os.Stat(filepath.Join(path, "compiler")); err != nil {
t.Fatal("confirmation cleaned cache without authorization")
}
}