-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcore.test.js
More file actions
100 lines (92 loc) · 2.99 KB
/
Copy pathcore.test.js
File metadata and controls
100 lines (92 loc) · 2.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#!/usr/bin/env node
/* ==============================================
* TEST — tiny runner (<=50 col house)
* Proves the three claims that matter:
* 1. replay determinism
* 2. an UNWIRED side effect crashes the test
* 3. two nodes converge over sync
* No framework. `node test.js`.
* ============================================ */
'use strict';
const { createRuntime } = require('./core');
const calc = require('./calculator');
const { Deps, withDeps, processorsFromDeps } =
require('./deps');
const { createBus, inMemorySync } = require('./sync');
const { connect } = require('./mesh');
let pass = 0, fail = 0;
function ok(name, cond) {
console.log((cond ? ' ok ' : ' FAIL ') + name);
cond ? pass++ : fail++;
}
const keys = ks => ks.map(k => ({ type: 'KEY', key: k }));
async function main() {
// -- 1. replay determinism --------------------
{
const rt = createRuntime(calc.machine, {});
for (const a of keys(['9', '*', '6']))
await rt.dispatch(a);
const live = rt.getState().value;
const rebuilt = rt.replay(rt.getLog()).value;
ok('replay is deterministic (54==54)',
live === 54 && rebuilt === 54);
}
// -- 2. unwired side effect crashes -----------
// '=' fires a 'speak' effect. speak.say is
// unimplemented by default, so dispatch must
// reject. That is the safety net: a test can
// never silently perform real I/O.
{
const rt = createRuntime(calc.machine, {
processors: processorsFromDeps(Deps),
});
for (const a of keys(['2', '+', '2']))
await rt.dispatch(a);
let threw = false;
try { await rt.dispatch({ type: 'KEY', key: '=' }); }
catch (e) {
threw = /unimplemented/.test(e.message);
}
ok('unwired speak crashes the test', threw);
}
// -- 2b. same effect, now WIRED, does not crash
{
const said = [];
await withDeps(
{ speak: { say: t => said.push(t) } },
async () => {
const rt = createRuntime(calc.machine, {
processors: processorsFromDeps(Deps),
});
for (const a of keys(['2', '+', '2', '=']))
await rt.dispatch(a);
ok('wired speak was called once',
said.length === 1 &&
said[0] === 'equals 4');
});
}
// -- 3. two nodes converge over sync ----------
// Send actions to node A only; node B must reach
// the same value with no direct contact.
{
const bus = createBus();
const a = createRuntime(calc.machine,
{ nodeId: 'A' });
const b = createRuntime(calc.machine,
{ nodeId: 'B' });
connect(a, inMemorySync(bus));
connect(b, inMemorySync(bus));
for (const act of keys(['7', '*', '6']))
await a.dispatch(act);
await new Promise(r => setTimeout(r, 5));
ok('node B converged to node A (42)',
b.getState().value === 42 &&
a.getState().value === 42);
ok('B holds all 3 facts from A',
b.getLog().length === 3);
}
console.log('\n ' + pass + ' passed, '
+ fail + ' failed');
process.exit(fail ? 1 : 0);
}
main();