rastrillo / fiesta Public

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

Plain git — no account needed to clone.

Download

Download this file

1package www_test
2
3import (
4 "bytes"
5 "crypto/sha256"
6 "encoding/hex"
7 "sort"
8 "strings"
9 "testing"
10 "testing/fstest"
11
12 xhtml "golang.org/x/net/html"
13
14 "amadan.net/rastrillo/fiesta"
15 "amadan.net/rastrillo/fiesta/internal/gallery"
16 "amadan.net/rastrillo/fiesta/internal/site"
17 "amadan.net/rastrillo/fiesta/internal/www"
18)
19
20// siteOf is a site source tree from path to content.
21func siteOf(files map[string]string) fstest.MapFS {
22 m := fstest.MapFS{}
23 for name, text := range files {
24 m[name] = &fstest.MapFile{Data: []byte(text)}
25 }
26 return m
27}
28
29// pages runs Pages over Fiesta's own tree and siteDir, failed loudly.
30// The landing page's template and catalogs are overlaid from the real
31// site source, since Pages renders the root page from them.
32func pages(t *testing.T, siteDir fstest.MapFS) map[string][]byte {
33 t.Helper()
34 out, err := www.Pages(fiesta.FS(), withLanding(t, siteDir))
35 if err != nil {
36 t.Fatalf("Pages: %v", err)
37 }
38 return out
39}
40
41// urlsOf reads every href and src on doc the way a browser would,
42// whatever the element, so a URL the gallery writes under its mount
43// and one a sample wrote are both seen.
44func urlsOf(t *testing.T, doc []byte) []string {
45 t.Helper()
46 var out []string
47 z := xhtml.NewTokenizer(bytes.NewReader(doc))
48 for {
49 switch z.Next() {
50 case xhtml.ErrorToken:
51 return out
52 case xhtml.StartTagToken, xhtml.SelfClosingTagToken:
53 _, more := z.TagName()
54 for more {
55 var k, v []byte
56 k, v, more = z.TagAttr()
57 if key := string(k); key == "href" || key == "src" {
58 out = append(out, string(v))
59 }
60 }
61 }
62 }
63}
64
65// TestPagesHasARootAndTheDesignSystem: the tree has a landing page at
66// the root and the design system's index under design-system/, and
67// neither is empty.
68func TestPagesHasARootAndTheDesignSystem(t *testing.T) {
69 out := pages(t, siteOf(nil))
70 for _, name := range []string{"index.html", "design-system/index.html"} {
71 if len(out[name]) == 0 {
72 t.Errorf("Pages wrote no %s", name)
73 }
74 }
75}
76
77// TestTheDesignSystemIsMounted: on the design system's index and on
78// one example page, every root-relative href and src starts with
79// /design-system/, so the gallery served there asks for nothing from
80// the site's root.
81func TestTheDesignSystemIsMounted(t *testing.T) {
82 out := pages(t, siteOf(nil))
83 for _, name := range []string{"design-system/index.html", "design-system/day/en/button.html"} {
84 doc, ok := out[name]
85 if !ok {
86 t.Errorf("Pages wrote no %s", name)
87 continue
88 }
89 rooted := 0
90 for _, u := range urlsOf(t, doc) {
91 if !strings.HasPrefix(u, "/") {
92 continue
93 }
94 rooted++
95 if !strings.HasPrefix(u, www.Mount+"/") {
96 t.Errorf("%s links %s, which is not under %s/", name, u, www.Mount)
97 }
98 }
99 if rooted == 0 {
100 t.Errorf("%s has no root-relative URL at all; the check above read nothing", name)
101 }
102 }
103}
104
105// TestTheReleaseBaseIsTheSitesOrigin: the base the design system
106// writes its release-file links under is this site's origin and its
107// /1/ rewrite, so a link to a shipped file is one of the site's own.
108//
109// Already passing when written: the constant was declared at its final
110// value. Proved live by a named mutation, gallery.ReleaseBase changed
111// to end in /2/, which it reported as "is https://fiesta.rastrillo.org/2/,
112// want https://fiesta.rastrillo.org/1/".
113func TestTheReleaseBaseIsTheSitesOrigin(t *testing.T) {
114 if gallery.ReleaseBase != www.Origin+"/1/" {
115 t.Errorf("gallery.ReleaseBase is %q, want %q", gallery.ReleaseBase, www.Origin+"/1/")
116 }
117}
118
119// TestAHiddenSiteFileIsRefused: a hidden file under the site source
120// fails the build, and the error names it, rather than being skipped
121// or published.
122func TestAHiddenSiteFileIsRefused(t *testing.T) {
123 _, err := www.Pages(fiesta.FS(), siteOf(map[string]string{
124 "static/x.css": "body{}\n",
125 "static/.DS_Store": "\x00",
126 }))
127 if err == nil {
128 t.Fatal("Pages accepted static/.DS_Store")
129 }
130 if !strings.Contains(err.Error(), "static/.DS_Store") {
131 t.Errorf("the error does not name the hidden file: %v", err)
132 }
133}
134
135// TestStaticFilesAreCopied: a file under static/ in the site source is
136// in the tree at the same path, byte for byte.
137func TestStaticFilesAreCopied(t *testing.T) {
138 want := "/* the site's own */\nbody{margin:0}\n"
139 out := pages(t, siteOf(map[string]string{"static/x.css": want}))
140 if got := string(out["static/x.css"]); got != want {
141 t.Errorf("static/x.css is %q, want %q", got, want)
142 }
143}
144
145// sumsOf is SHA256SUMS as build.Dist writes it: one `<hex> <path>`
146// line per file, sorted by path, the sums file itself left out.
147func sumsOf(files map[string][]byte) []byte {
148 names := make([]string, 0, len(files))
149 for name := range files {
150 if name != "SHA256SUMS" {
151 names = append(names, name)
152 }
153 }
154 sort.Strings(names)
155 var b bytes.Buffer
156 for _, name := range names {
157 sum := sha256.Sum256(files[name])
158 b.WriteString(hex.EncodeToString(sum[:]) + " " + name + "\n")
159 }
160 return b.Bytes()
161}
162
163// releases is a releases tree holding one small, checksummed 1.0.0.
164func releases() fstest.MapFS {
165 files := map[string][]byte{
166 "fiesta.css": []byte("/*! Fiesta 1.0.0 */\nbody{margin:0}\n"),
167 "LICENSE": []byte("MPL-2.0\n"),
168 }
169 files["SHA256SUMS"] = sumsOf(files)
170 m := fstest.MapFS{}
171 for name, data := range files {
172 m["1.0.0/"+name] = &fstest.MapFile{Data: data}
173 }
174 return m
175}
176
177// TestSiteBuildAcceptsThePages is a preservation test: site.Build
178// takes FS(Pages(...)) as its pages beside a checksummed 1.0.0/, and
179// what Pages wrote is in the site at the same paths beside the release.
180func TestSiteBuildAcceptsThePages(t *testing.T) {
181 out := pages(t, siteOf(map[string]string{"static/x.css": "body{}\n"}))
182 built, err := site.Build(releases(), www.FS(out), site.Rules{})
183 if err != nil {
184 t.Fatalf("site.Build refused the pages: %v", err)
185 }
186 for name, data := range out {
187 if !bytes.Equal(built[name], data) {
188 t.Errorf("%s in the site differs from Pages, or is missing", name)
189 }
190 }
191 if _, ok := built["1.0.0/fiesta.css"]; !ok {
192 t.Error("the site lost the release")
193 }
194 if got := string(built["_redirects"]); got != "/1/* /1.0.0/:splat 200\n" {
195 t.Errorf("_redirects is %q", got)
196 }
197}
198