carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package edge
2
3import (
4 "encoding/json"
5 "os/exec"
6 "regexp"
7 "testing"
8)
9
10// The pending page's retry script, run for real under node against a fake
11// browser: storage, window.name, location, the clock and setTimeout. The
12// substring checks in pending_test.go pin the markup; these pin what the
13// script DOES. Skipped where node is not installed.
14
15type jsTab struct {
16 Storage string `json:"storage"` // "ok", "throws"
17 WindowName string `json:"windowName"`
18 WindowNames bool `json:"windowNameWorks"`
19 Session string `json:"session"` // prior sessionStorage value for the key
20 Now int64 `json:"now"`
21}
22
23type jsOutcome struct {
24 Reload bool `json:"reload"`
25 Replaced string `json:"replaced"`
26 Heading string `json:"heading"`
27 Session string `json:"session"`
28 Window string `json:"windowName"`
29}
30
31var scriptRE = regexp.MustCompile(`(?s)<script>(.*)</script>`)
32
33func runPendingScript(t *testing.T, fallback string, tab jsTab) jsOutcome {
34 t.Helper()
35 node, err := exec.LookPath("node")
36 if err != nil {
37 t.Skip("node not installed")
38 }
39 m := scriptRE.FindSubmatch(renderPendingPage(fallback))
40 if m == nil {
41 t.Fatal("no script in the pending page")
42 }
43 in, _ := json.Marshal(tab)
44 prog := `
45const tab = ` + string(in) + `;
46const out = {reload:false, replaced:"", heading:"", session:"", windowName:""};
47const store = {};
48if (tab.session) store["carlos-pending:x.go.tito.example"] = tab.session;
49global.sessionStorage = {
50 getItem(k){ if (tab.storage==="throws") throw new Error("SecurityError"); return k in store ? store[k] : null; },
51 setItem(k,v){ if (tab.storage==="throws") throw new Error("SecurityError"); store[k]=String(v); },
52};
53let wn = tab.windowName;
54global.window = {};
55Object.defineProperty(global.window, "name", {
56 get(){ return wn; },
57 set(v){ if (tab.windowNameWorks) wn = String(v); },
58});
59global.location = {
60 host: "x.go.tito.example", href: "https://x.go.tito.example/p",
61 replace(u){ if (u === this.href) out.reload = true; else out.replaced = u; },
62};
63Date.now = () => tab.now;
64global.setTimeout = (fn) => fn();
65const els = {h:{textContent:""}, b:{textContent:""}};
66global.document = { title:"", getElementById(id){ return els[id]; } };
67` + string(m[1]) + `
68out.heading = els.h.textContent;
69out.session = store["carlos-pending:x.go.tito.example"] || "";
70out.windowName = wn;
71console.log(JSON.stringify(out));
72`
73 cmd := exec.Command(node, "-e", prog)
74 b, err := cmd.CombinedOutput()
75 if err != nil {
76 t.Fatalf("node: %v\n%s", err, b)
77 }
78 var o jsOutcome
79 if err := json.Unmarshal(b, &o); err != nil {
80 t.Fatalf("node output %q: %v", b, err)
81 }
82 return o
83}
84
85func TestPendingScriptFirstLoadRetries(t *testing.T) {
86 o := runPendingScript(t, "", jsTab{Storage: "ok", Now: 1_000_000})
87 if !o.Reload || o.Heading != "" {
88 t.Fatalf("first load did not schedule a reload: %+v", o)
89 }
90}
91
92func TestPendingScriptGivesUpAfterTheWindow(t *testing.T) {
93 // Seen continuously (last 5s ago) since 95s ago.
94 o := runPendingScript(t, "", jsTab{Storage: "ok", Now: 1_000_000,
95 Session: `{"first":905000,"last":995000}`})
96 if o.Reload || o.Heading != goneHeading {
97 t.Fatalf("did not give up after the window: %+v", o)
98 }
99}
100
101func TestPendingScriptGivesUpToTheFallback(t *testing.T) {
102 o := runPendingScript(t, "https://www.example.com/", jsTab{Storage: "ok", Now: 1_000_000,
103 Session: `{"first":905000,"last":995000}`})
104 if o.Replaced != "https://www.example.com/" {
105 t.Fatalf("did not go to the fallback: %+v", o)
106 }
107}
108
109func TestPendingScriptStaleStartIsAFreshEpisode(t *testing.T) {
110 // First seen long ago, but not seen for a minute: a new episode.
111 o := runPendingScript(t, "", jsTab{Storage: "ok", Now: 1_000_000,
112 Session: `{"first":100000,"last":940000}`})
113 if !o.Reload {
114 t.Fatalf("a stale start time made the page give up at once: %+v", o)
115 }
116}
117
118func TestPendingScriptFallsBackToWindowName(t *testing.T) {
119 o := runPendingScript(t, "", jsTab{Storage: "throws", WindowNames: true, Now: 1_000_000})
120 if !o.Reload || o.Window == "" {
121 t.Fatalf("with sessionStorage throwing, window.name was not used: %+v", o)
122 }
123 // ...and window.name carries the window across reloads.
124 o = runPendingScript(t, "", jsTab{Storage: "throws", WindowNames: true, Now: 1_000_000,
125 WindowName: `{"first":905000,"last":995000,"k":"carlos-pending:x.go.tito.example"}`})
126 if o.Reload || o.Heading != goneHeading {
127 t.Fatalf("window.name state did not end the window: %+v", o)
128 }
129}
130
131func TestPendingScriptWithNoStorageNeverLoops(t *testing.T) {
132 o := runPendingScript(t, "", jsTab{Storage: "throws", WindowNames: false, Now: 1_000_000})
133 if o.Reload || o.Heading != goneHeading {
134 t.Fatalf("with no storage at all the page reloaded instead of giving up: %+v", o)
135 }
136}
137