1package main
2
3import (
4 "bytes"
5 "os"
6 "path/filepath"
7 "testing"
8 "testing/fstest"
9
10 "github.com/andybalholm/brotli"
11
12 "github.com/bnema/dumber/assets"
13)
14
15func writeFixture(t *testing.T, dir, name string, data []byte) {
16 t.Helper()
17 if err := os.WriteFile(filepath.Join(dir, name), data, 0o644); err != nil {
18 t.Fatalf("write fixture %s: %v", name, err)
19 }
20}
21
22func fixtureBundle(t *testing.T, wasm, js, css []byte) string {
23 t.Helper()
24 dir := t.TempDir()
25 writeFixture(t, dir, "systemviews.wasm", wasm)
26 writeFixture(t, dir, "systemviews.wasm.br", brotliCompressForGeneratorTest(t, wasm))
27 writeFixture(t, dir, "wasm_exec.js", js)
28 writeFixture(t, dir, "systemviews.css", css)
29 return dir
30}
31
32func brotliCompressForGeneratorTest(t *testing.T, data []byte) []byte {
33 t.Helper()
34 var buf bytes.Buffer
35 w := brotli.NewWriter(&buf)
36 if _, err := w.Write(data); err != nil {
37 t.Fatalf("brotli write: %v", err)
38 }
39 if err := w.Close(); err != nil {
40 t.Fatalf("brotli close: %v", err)
41 }
42 return buf.Bytes()
43}
44
45func TestCollectManifestPinsAllFiles(t *testing.T) {
46 t.Parallel()
47
48 dir := fixtureBundle(t, []byte("\x00asm-m"), []byte("/* js */"), []byte("/* css */"))
49 m, err := collectManifest(os.DirFS(dir))
50 if err != nil {
51 t.Fatalf("collectManifest() error = %v", err)
52 }
53 if m.Version != assets.ManifestVersion {
54 t.Fatalf("version = %d", m.Version)
55 }
56 if len(m.Files) != len(assets.ManifestFiles) {
57 t.Fatalf("pinned %d files", len(m.Files))
58 }
59 if err := m.Verify(os.DirFS(dir)); err != nil {
60 t.Fatalf("collected manifest does not verify: %v", err)
61 }
62}
63
64func TestRunWritesReproducibleManifest(t *testing.T) {
65 t.Parallel()
66
67 dir := fixtureBundle(t, []byte("\x00asm-repro"), []byte("/* js */"), []byte("/* css */"))
68 if err := run(dir, false, false); err != nil {
69 t.Fatalf("run() error = %v", err)
70 }
71 first, err := os.ReadFile(filepath.Join(dir, assets.ManifestName))
72 if err != nil {
73 t.Fatalf("read manifest: %v", err)
74 }
75 if err := run(dir, false, false); err != nil {
76 t.Fatalf("run() again error = %v", err)
77 }
78 second, readErr := os.ReadFile(filepath.Join(dir, assets.ManifestName))
79 if readErr != nil {
80 t.Fatalf("read manifest: %v", readErr)
81 }
82 if !bytes.Equal(first, second) {
83 t.Fatal("manifest generation is not reproducible")
84 }
85 if err := run(dir, true, false); err != nil {
86 t.Fatalf("run(check) error = %v", err)
87 }
88}
89
90func TestRunFailsOnMissingFile(t *testing.T) {
91 t.Parallel()
92
93 dir := t.TempDir()
94 writeFixture(t, dir, "systemviews.wasm", []byte("\x00asm"))
95 writeFixture(t, dir, "systemviews.wasm.br", brotliCompressForGeneratorTest(t, []byte("\x00asm")))
96 // wasm_exec.js and systemviews.css missing: loud failure, no manifest.
97 if err := run(dir, false, false); err == nil {
98 t.Fatal("run() with missing files succeeded, want error")
99 }
100 if _, err := os.Stat(filepath.Join(dir, assets.ManifestName)); !os.IsNotExist(err) {
101 t.Fatal("partial manifest written on failure")
102 }
103}
104
105func TestRunFailsOnStaleDigest(t *testing.T) {
106 t.Parallel()
107
108 dir := fixtureBundle(t, []byte("\x00asm-v1"), []byte("/* js */"), []byte("/* css */"))
109 if err := run(dir, false, false); err != nil {
110 t.Fatalf("run() error = %v", err)
111 }
112 // Mutate an artifact after generation: check must fail, never bless it.
113 writeFixture(t, dir, "systemviews.css", []byte("/* css v2 */"))
114 if err := run(dir, true, false); err == nil {
115 t.Fatal("run(check) with mutated artifact succeeded, want error")
116 }
117}
118
119func TestCollectManifestRejectsRawCompressedMismatch(t *testing.T) {
120 t.Parallel()
121
122 fsys := fstest.MapFS{
123 "systemviews.wasm": {Data: []byte("\x00asm-raw")},
124 "systemviews.wasm.br": {Data: brotliCompressForGeneratorTest(t, []byte("\x00asm-other"))},
125 "wasm_exec.js": {Data: []byte("/* js */")},
126 "systemviews.css": {Data: []byte("/* css */")},
127 }
128 if _, err := collectManifest(fsys); err == nil {
129 t.Fatal("collectManifest() with raw/compressed mismatch succeeded, want error")
130 }
131}
132
133func TestRunCheckFailsWithoutManifest(t *testing.T) {
134 t.Parallel()
135
136 if err := run(t.TempDir(), true, false); err == nil {
137 t.Fatal("run(check) without manifest succeeded, want error")
138 }
139}
140
141func TestRunCheckRejectsCompressedOnlySwap(t *testing.T) {
142 t.Parallel()
143
144 dir := fixtureBundle(t, []byte("\x00asm-v1"), []byte("/* js */"), []byte("/* css */"))
145 if err := run(dir, false, false); err != nil {
146 t.Fatalf("run() error = %v", err)
147 }
148 // Swap only the compressed artifact for different valid bytes: raw
149 // and manifest still agree, but serving would decode the stale .br.
150 writeFixture(t, dir, "systemviews.wasm.br", brotliCompressForGeneratorTest(t, []byte("\x00asm-v2")))
151 if err := run(dir, true, false); err == nil {
152 t.Fatal("run(check) with swapped compressed WASM succeeded, want error")
153 }
154}
155
156func TestRunCheckIfPresentSkipsMissingBundle(t *testing.T) {
157 t.Parallel()
158
159 if err := run(t.TempDir(), false, true); err != nil {
160 t.Fatalf("run(check-if-present) without bundle error = %v", err)
161 }
162}
163
164func TestRunCheckIfPresentRejectsStaleBundle(t *testing.T) {
165 t.Parallel()
166
167 dir := fixtureBundle(t, []byte("\x00asm-v1"), []byte("/* js */"), []byte("/* css */"))
168 if err := run(dir, false, false); err != nil {
169 t.Fatalf("run() error = %v", err)
170 }
171 writeFixture(t, dir, "systemviews.css", []byte("/* css v2 */"))
172 if err := run(dir, false, true); err == nil {
173 t.Fatal("run(check-if-present) with stale artifact succeeded, want error")
174 }
175}
176
177func TestRunCheckIfPresentRejectsPartialBundle(t *testing.T) {
178 t.Parallel()
179
180 // Compressed-only partial bundle: serving would use the stale .br,
181 // so check-if-present must validate (and fail) instead of skipping.
182 dir := t.TempDir()
183 writeFixture(t, dir, "systemviews.wasm.br", brotliCompressForGeneratorTest(t, []byte("\x00asm-stale")))
184 if err := run(dir, false, true); err == nil {
185 t.Fatal("run(check-if-present) with .br-only bundle succeeded, want error")
186 }
187
188 // Raw-only partial bundle: same rule in the other direction.
189 dir = t.TempDir()
190 writeFixture(t, dir, "systemviews.wasm", []byte("\x00asm-raw-only"))
191 if err := run(dir, false, true); err == nil {
192 t.Fatal("run(check-if-present) with raw-only bundle succeeded, want error")
193 }
194}
195
196func TestRequireCompressedAgreementRejectsSentinelSize(t *testing.T) {
197 t.Parallel()
198
199 // Both artifacts at max+1 with agreeing bytes: the bound must reject
200 // before equality blesses what the runtime loader refuses.
201 const bound = 16
202 raw := bytes.Repeat([]byte{0x7a}, bound+1)
203 fsys := fstest.MapFS{
204 "systemviews.wasm": {Data: raw},
205 "systemviews.wasm.br": {Data: brotliCompressForGeneratorTest(t, raw)},
206 }
207 if err := requireCompressedAgreement(fsys, raw, bound); err == nil {
208 t.Fatal("requireCompressedAgreement at max+1 succeeded, want oversize error")
209 }
210 if _, err := readBoundedManifestFile(fsys, "systemviews.wasm", bound); err == nil {
211 t.Fatal("readBoundedManifestFile at max+1 succeeded, want oversize error")
212 }
213}