rastrillo / fiesta Public

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

Plain git — no account needed to clone.

Download

Download this file

1//go:build browser
2
3package harness
4
5import (
6 "fmt"
7 "net/http"
8 "strings"
9 "testing"
10 "time"
11
12 "github.com/chromedp/chromedp"
13)
14
15// probeBuild serves a page that walks into every watcher on purpose:
16// optionally a console error, then a 500 the page asked for with POST
17// (so the method-correlation is provable), then a plain 404 — and
18// reports #done only after the fetches settle, which is what the test
19// synchronises on.
20func probeBuild(withConsoleError bool) func(string) http.Handler {
21 return func(string) http.Handler {
22 mux := http.NewServeMux()
23 mux.HandleFunc("POST /boom", func(w http.ResponseWriter, req *http.Request) {
24 http.Error(w, "boom", http.StatusInternalServerError)
25 })
26 mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, req *http.Request) {
27 noise := ""
28 if withConsoleError {
29 noise = `console.error("kaboom");`
30 }
31 w.Header().Set("Content-Type", "text/html; charset=utf-8")
32 fmt.Fprintf(w, `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>probe</title></head><body><p id="up">up</p><script>
33(async () => {
34 %s
35 await fetch("/boom", { method: "POST" });
36 await fetch("/missing");
37 const done = document.createElement("p");
38 done.id = "done";
39 done.textContent = "done";
40 document.body.append(done);
41})();
42</script></body></html>`, noise)
43 })
44 return mux
45 }
46}
47
48// waitForProblems polls until at least n problems accumulated: CDP
49// event delivery is asynchronous, and a fixed sleep is either flaky or
50// slow. Returns whatever accumulated by the deadline either way — the
51// caller's assertions name what is missing.
52func waitForProblems(t *testing.T, r *Rig, n int) []string {
53 t.Helper()
54 deadline := time.Now().Add(5 * time.Second)
55 for {
56 r.mu.Lock()
57 got := len(r.problems)
58 r.mu.Unlock()
59 if got >= n || time.Now().After(deadline) {
60 return r.take()
61 }
62 time.Sleep(50 * time.Millisecond)
63 }
64}
65
66func TestWatchersCollectEveryLoudFailure(t *testing.T) {
67 r := New(t, probeBuild(true))
68 r.Run(
69 chromedp.Navigate(r.Origin+"/"),
70 chromedp.WaitVisible("#done", chromedp.ByQuery),
71 )
72 probs := waitForProblems(t, r, 3)
73 joined := strings.Join(probs, "\n")
74 for _, want := range []string{
75 "console.error: kaboom",
76 // The method rides in from requestWillBeSent, correlated by
77 // RequestID — a response has no method of its own.
78 "HTTP 500 POST",
79 "/boom",
80 "HTTP 404 GET",
81 "/missing",
82 } {
83 if !strings.Contains(joined, want) {
84 t.Errorf("problems are missing %q:\n%s", want, joined)
85 }
86 }
87}
88
89// Allow quietens the response AND the log-domain mirror Chromium emits
90// alongside it — the mirror carries only a URL, so it is matched by
91// path. With every probe allowed, the drive must end silent.
92func TestAllowQuietensExpectedProbesAndTheirMirrors(t *testing.T) {
93 r := New(t, probeBuild(false))
94 r.Allow(http.MethodPost, "/boom", http.StatusInternalServerError)
95 r.Allow(http.MethodGet, "/missing", http.StatusNotFound)
96 r.Run(
97 chromedp.Navigate(r.Origin+"/"),
98 chromedp.WaitVisible("#done", chromedp.ByQuery),
99 )
100 // The fetches settled before #done appeared, but give straggler
101 // events a beat before declaring silence.
102 time.Sleep(500 * time.Millisecond)
103 if probs := r.take(); len(probs) > 0 {
104 t.Errorf("allowed probes still reported problems:\n%s", strings.Join(probs, "\n"))
105 }
106}
107
108// The browser probes /favicon.ico on its own; New pre-allows the 404
109// so every app doesn't rediscover it. The allowance is data, so this
110// needs no favicon request to prove the wiring.
111func TestFaviconProbeIsPreAllowed(t *testing.T) {
112 r := New(t, probeBuild(false))
113 if !r.responseAllowed(http.MethodGet, r.Origin+"/favicon.ico", http.StatusNotFound) {
114 t.Error("GET /favicon.ico 404 is not pre-allowed")
115 }
116 if !r.logEntryAllowed(r.Origin + "/favicon.ico") {
117 t.Error("the favicon 404's log-domain mirror is not pre-allowed")
118 }
119}
120