Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/dev/verify_version.mjs

3.0 KiB, 1 run

created by r2519314175:783, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1// verify_version.mjs — the browser notices when the gateway declares it too old.
2//
3// Phase-2 compatibility contract, client half: gateway.js sends `X-Daimond-Api` on every call and
4// inspects the reply. A 426, or an advertised `x-daimond-min-api` above this build's version, means
5// the tab is out of date -- it fires `daimond:stale`, which the updater turns into a forced reload.
6// Here we route a real gateway call to each of those two answers and confirm the event fires. The
7// loop guard (daimond-forced-from) is pre-set to this build so the assertion does not itself reload
8// the page out from under us; the forced-reload behaviour is covered in verify_updates.mjs.
9//
10// Needs an unlocked identity, because the only unconditional gateway call is bootstrap().
11import { open } from './harness.mjs';
12
13const ok = [], bad = [];
14const check = (name, pass, detail) => {
15 (pass ? ok : bad).push(name + (detail ? ' — ' + detail : ''));
16 console.log((pass ? ' ok ' : ' FAIL ') + name + (detail ? ' — ' + detail : ''));
17};
18
19const s = await open({ name: 'version', signIn: true, connect: false });
20const { page } = s;
21
22// Wait for both the updater and the gateway client to be live.
23await page.waitForFunction(() => !!window.DaimondUpdater && !!window.DaimondGateway, null, { timeout: 8000 }).catch(() => {});
24
25// Pre-set the loop guard to this build, so onStale reaches its "already tried" branch and does NOT
26// reload during the test.
27await page.evaluate(() => {
28 var b = window.DaimondUpdater && window.DaimondUpdater.booted();
29 try { if (b) sessionStorage.setItem('daimond-forced-from', b); } catch (e) {}
30});
31
32// Fire bootstrap() (its first call is POST /api/account) against a routed answer, and watch for the
33// staleness event.
34async function firesStaleWhen(fulfil) {
35 await page.unroute('**/api/account').catch(() => {});
36 await page.route('**/api/account', route => route.fulfill(fulfil));
37 return page.evaluate(() => new Promise(resolve => {
38 window.addEventListener('daimond:stale', () => resolve(true), { once: true });
39 try { window.DaimondGateway.bootstrap().catch(() => {}); } catch (e) {}
40 setTimeout(() => resolve(false), 3500);
41 }));
42}
43
44try {
45 const on426 = await firesStaleWhen({
46 status: 426, contentType: 'application/json',
47 body: JSON.stringify({ ok: false, error: 'old', min_api: 2, api: 2 }),
48 });
49 check('a 426 from the gateway fires daimond:stale', on426);
50
51 const onHeader = await firesStaleWhen({
52 status: 200, contentType: 'application/json',
53 headers: { 'x-daimond-min-api': '2', 'x-daimond-api': '2' },
54 body: JSON.stringify({ ok: true }),
55 });
56 check('an advertised min-api above this build fires daimond:stale', onHeader);
57
58 const errs = s.errs.filter(e => !/favicon|ERR_|Failed to load resource|401|426|502|Unauthorized/.test(e));
59 check('no unexpected console errors', errs.length === 0, errs.slice(0, 3).join(' | '));
60} catch (e) {
61 check('test harness ran to completion', false, String(e && e.message || e));
62} finally {
63 await s.close();
64}
65
66console.log(`\n${ok.length} passed, ${bad.length} failed`);
67process.exit(bad.length ? 1 : 0);