rastrillo / fiesta Public

Clone
git clone https://amadan.net/rastrillo/fiesta

Plain git — no account needed to clone.

Download

Download this file

1package check
2
3import (
4 "encoding/json"
5 "os"
6 "path/filepath"
7 "strings"
8 "testing"
9
10 "amadan.net/rastrillo/fiesta"
11 "amadan.net/rastrillo/fiesta/example"
12)
13
14// The seeded fixtures: the pages rastrillo's browser drives built by
15// hand at the seed commit, written out once under testdata/fixtures/
16// and listed in index.json by the file, builder and case they came
17// from. A drive ported here reads its page through fixture rather
18// than retyping it, so the page it drives is the page the rastrillo
19// drive drove.
20
21// fixtureRow is one entry of testdata/fixtures/index.json. A row
22// names either a path under testdata/fixtures/ or an example state
23// that carries the same markup; the two reference rows have no path.
24type fixtureRow struct {
25 File string `json:"file"`
26 Builder string `json:"builder"`
27 Case string `json:"case"`
28 Path string `json:"path"`
29 Kind string `json:"kind"`
30 Request string `json:"request"`
31 Example string `json:"example"`
32 Note string `json:"note"`
33}
34
35// fixtureDir is where the index and the files live, under check/.
36const fixtureDir = "testdata/fixtures"
37
38// fixtureIndex reads index.json.
39func fixtureIndex(t *testing.T) []fixtureRow {
40 t.Helper()
41 b, err := os.ReadFile(filepath.Join(fixtureDir, "index.json"))
42 if err != nil {
43 t.Fatal(err)
44 }
45 var rows []fixtureRow
46 if err := json.Unmarshal(b, &rows); err != nil {
47 t.Fatalf("%s/index.json: %v", fixtureDir, err)
48 }
49 return rows
50}
51
52// fixture returns the contents of the fixture file the index lists for
53// the {file, builder, case} row: the body or the whole document, as
54// the row's kind says, exactly as the rastrillo builder wrote it. It fails
55// the test when the row is missing, when the row names an example
56// rather than a path, or when the file is absent.
57func fixture(t *testing.T, file, builder, kase string) string {
58 t.Helper()
59 for _, r := range fixtureIndex(t) {
60 if r.File != file || r.Builder != builder || r.Case != kase {
61 continue
62 }
63 if r.Path == "" {
64 t.Fatalf("fixture %s %s %q has no path (it names example %q); read it through the example package", file, builder, kase, r.Example)
65 }
66 b, err := os.ReadFile(filepath.Join(fixtureDir, filepath.FromSlash(r.Path)))
67 if err != nil {
68 t.Fatalf("fixture %s %s %q: %v", file, builder, kase, err)
69 }
70 return string(b)
71 }
72 t.Fatalf("fixture %s %s %q is not in %s/index.json", file, builder, kase, fixtureDir)
73 return ""
74}
75
76// fixtureExample returns the markup of the example state the index
77// names for the {file, builder, case} row: the two rows whose rastrillo
78// page was a styleguide sample carry "example/state" instead of a
79// path, and read through the example package. It fails the test when
80// the row is missing, when the row names a path rather than an
81// example, or when the state cannot be read.
82func fixtureExample(t *testing.T, file, builder, kase string) string {
83 t.Helper()
84 for _, r := range fixtureIndex(t) {
85 if r.File != file || r.Builder != builder || r.Case != kase {
86 continue
87 }
88 if r.Example == "" {
89 t.Fatalf("fixture %s %s %q names a path (%s), not an example; read it through fixture", file, builder, kase, r.Path)
90 }
91 name, state, ok := strings.Cut(r.Example, "/")
92 if !ok {
93 t.Fatalf("fixture %s %s %q names example %q, which is not example/state", file, builder, kase, r.Example)
94 }
95 markup, err := example.Markup(fiesta.FS(), name, state)
96 if err != nil {
97 t.Fatalf("fixture %s %s %q: %v", file, builder, kase, err)
98 }
99 return markup
100 }
101 t.Fatalf("fixture %s %s %q is not in %s/index.json", file, builder, kase, fixtureDir)
102 return ""
103}
104
105// TestEveryFixtureRowResolves: every row that names a path names a
106// file that exists and is not empty, and the rows without a path are
107// the example references, which name an example. The count is floored
108// at 100 so an index that lost its rows could not pass by listing
109// nothing.
110func TestEveryFixtureRowResolves(t *testing.T) {
111 rows := fixtureIndex(t)
112 if len(rows) < 100 {
113 t.Fatalf("%s/index.json lists %d rows; the seed wrote 137", fixtureDir, len(rows))
114 }
115 seen := map[[3]string]bool{}
116 for _, r := range rows {
117 key := [3]string{r.File, r.Builder, r.Case}
118 if seen[key] {
119 t.Errorf("%s %s %q is listed twice", r.File, r.Builder, r.Case)
120 }
121 seen[key] = true
122 if r.Path == "" {
123 if r.Example == "" {
124 t.Errorf("%s %s %q has neither a path nor an example", r.File, r.Builder, r.Case)
125 }
126 continue
127 }
128 info, err := os.Stat(filepath.Join(fixtureDir, filepath.FromSlash(r.Path)))
129 if err != nil {
130 t.Errorf("%s %s %q: %v", r.File, r.Builder, r.Case, err)
131 continue
132 }
133 if info.Size() == 0 {
134 t.Errorf("%s %s %q: %s is empty", r.File, r.Builder, r.Case, r.Path)
135 }
136 if got := fixture(t, r.File, r.Builder, r.Case); int64(len(got)) != info.Size() {
137 t.Errorf("%s %s %q: fixture returned %d bytes of a %d-byte file", r.File, r.Builder, r.Case, len(got), info.Size())
138 }
139 }
140}
141