P11.6 wiki regen + Phase 10 private-test rewrites where applicable
For all 11 nodes with auto-gen markers: wiki/Home.md sections 5 (topic contract) and 9 (data model) regenerated via npm run wiki:all. New Unit column shows '<measure> (default <unit>)' for declared topics, '—' otherwise. Effect column now uses descriptor.description (P11.2 field) overriding the generic per-prefix fallback. For rotatingMachine + reactor: Phase 10 test rewrites — 3 + 8 files moved off private nodeClass internals (_attachInputHandler, _commands, _pendingExtras, _registerChild, _tick, etc.) to the public BaseNodeAdapter surface (node.handlers.input, node.source.*). +6 / +7 net new tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,65 +1,83 @@
|
||||
'use strict';
|
||||
|
||||
// Phase 10 rewrite: drives only the public BaseNodeAdapter surface.
|
||||
// The pre-refactor _loadConfig / _setupClass private methods are gone —
|
||||
// config build is exposed via buildDomainConfig (override hook in
|
||||
// CONTRACTS.md §2), and engine selection is observable via
|
||||
// `inst.source.engine instanceof Reactor_CSTR | Reactor_PFR` after a
|
||||
// full `new nodeClass(...)` construction.
|
||||
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const NodeClass = require('../../src/nodeClass');
|
||||
const nodeClass = require('../../src/nodeClass');
|
||||
const { Reactor_CSTR, Reactor_PFR } = require('../../src/specificClass');
|
||||
const { makeUiConfig } = require('../helpers/factories');
|
||||
|
||||
// These tests pinned the old private _loadConfig / _setupClass methods on
|
||||
// the pre-refactor nodeClass. After the BaseNodeAdapter migration the
|
||||
// same logic lives in buildDomainConfig + the Reactor wrapper's engine
|
||||
// selector. We exercise both surfaces directly.
|
||||
function makeRED() { return { nodes: { getNode: () => null } }; }
|
||||
|
||||
function makeNode(id = 'reactor-1') {
|
||||
const sends = [];
|
||||
const statuses = [];
|
||||
const handlers = {};
|
||||
return {
|
||||
id, sends, statuses, handlers,
|
||||
send(arr) { sends.push(arr); },
|
||||
status(b) { statuses.push(b); },
|
||||
on(ev, fn) { handlers[ev] = fn; },
|
||||
warn() {}, error() {},
|
||||
};
|
||||
}
|
||||
|
||||
function closeNode(node) {
|
||||
if (node.handlers.close) node.handlers.close(() => {});
|
||||
}
|
||||
|
||||
test('buildDomainConfig coerces numeric fields and builds initial state vector', () => {
|
||||
const inst = Object.create(NodeClass.prototype);
|
||||
inst.node = { id: 'n-reactor-1' };
|
||||
inst.name = 'reactor';
|
||||
const dc = inst.buildDomainConfig(
|
||||
makeUiConfig({
|
||||
volume: '12.5',
|
||||
length: '9',
|
||||
resolution_L: '7',
|
||||
alpha: '0.5',
|
||||
n_inlets: '3',
|
||||
timeStep: '2',
|
||||
S_O_init: '1.1',
|
||||
}),
|
||||
);
|
||||
const node = makeNode();
|
||||
const inst = new nodeClass(makeUiConfig(), makeRED(), node, 'reactor');
|
||||
try {
|
||||
const dc = inst.buildDomainConfig(
|
||||
makeUiConfig({
|
||||
volume: '12.5',
|
||||
length: '9',
|
||||
resolution_L: '7',
|
||||
alpha: '0.5',
|
||||
n_inlets: '3',
|
||||
timeStep: '2',
|
||||
S_O_init: '1.1',
|
||||
}),
|
||||
);
|
||||
|
||||
assert.equal(dc.reactor.volume, 12.5);
|
||||
assert.equal(dc.reactor.length, 9);
|
||||
assert.equal(dc.reactor.resolution_L, 7);
|
||||
assert.equal(dc.reactor.alpha, 0.5);
|
||||
assert.equal(dc.reactor.n_inlets, 3);
|
||||
assert.equal(dc.reactor.timeStep, 2);
|
||||
assert.equal(Object.keys(dc.initialState).length, 13);
|
||||
assert.equal(dc.initialState.S_O, 1.1);
|
||||
assert.equal(dc.reactor.volume, 12.5);
|
||||
assert.equal(dc.reactor.length, 9);
|
||||
assert.equal(dc.reactor.resolution_L, 7);
|
||||
assert.equal(dc.reactor.alpha, 0.5);
|
||||
assert.equal(dc.reactor.n_inlets, 3);
|
||||
assert.equal(dc.reactor.timeStep, 2);
|
||||
assert.equal(Object.keys(dc.initialState).length, 13);
|
||||
assert.equal(dc.initialState.S_O, 1.1);
|
||||
} finally {
|
||||
closeNode(node);
|
||||
}
|
||||
});
|
||||
|
||||
test('Reactor wrapper instantiates CSTR engine when configured as CSTR', () => {
|
||||
const Reactor = require('../../src/specificClass');
|
||||
const config = {
|
||||
general: { name: 'reactor', id: 'n', logging: { enabled: false, logLevel: 'error' } },
|
||||
functionality: { softwareType: 'reactor', positionVsParent: 'atEquipment' },
|
||||
reactor: { reactor_type: 'CSTR', volume: 100, length: 10, resolution_L: 5, alpha: 0,
|
||||
n_inlets: 1, kla: NaN, timeStep: 1 },
|
||||
initialState: { S_O: 0, S_I: 30, S_S: 100, S_NH: 16, S_N2: 0, S_NO: 0, S_HCO: 5,
|
||||
X_I: 25, X_S: 75, X_H: 30, X_STO: 0, X_A: 0.001, X_TS: 125 },
|
||||
};
|
||||
const r = new Reactor(config);
|
||||
assert.ok(r.engine instanceof Reactor_CSTR);
|
||||
const node = makeNode();
|
||||
const inst = new nodeClass(makeUiConfig({ reactor_type: 'CSTR' }), makeRED(), node, 'reactor');
|
||||
try {
|
||||
assert.ok(inst.source.engine instanceof Reactor_CSTR);
|
||||
} finally {
|
||||
closeNode(node);
|
||||
}
|
||||
});
|
||||
|
||||
test('Reactor wrapper instantiates PFR engine when configured as PFR', () => {
|
||||
const Reactor = require('../../src/specificClass');
|
||||
const config = {
|
||||
general: { name: 'reactor', id: 'n', logging: { enabled: false, logLevel: 'error' } },
|
||||
functionality: { softwareType: 'reactor', positionVsParent: 'atEquipment' },
|
||||
reactor: { reactor_type: 'PFR', volume: 100, length: 10, resolution_L: 5, alpha: 0,
|
||||
n_inlets: 1, kla: NaN, timeStep: 1 },
|
||||
initialState: { S_O: 0, S_I: 30, S_S: 100, S_NH: 16, S_N2: 0, S_NO: 0, S_HCO: 5,
|
||||
X_I: 25, X_S: 75, X_H: 30, X_STO: 0, X_A: 0.001, X_TS: 125 },
|
||||
};
|
||||
const r = new Reactor(config);
|
||||
assert.ok(r.engine instanceof Reactor_PFR);
|
||||
const node = makeNode();
|
||||
const inst = new nodeClass(makeUiConfig({ reactor_type: 'PFR' }), makeRED(), node, 'reactor');
|
||||
try {
|
||||
assert.ok(inst.source.engine instanceof Reactor_PFR);
|
||||
} finally {
|
||||
closeNode(node);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,56 +1,111 @@
|
||||
'use strict';
|
||||
|
||||
// Phase 10 rewrite: drives only the public BaseNodeAdapter surface.
|
||||
// The pre-refactor _attachInputHandler private switch is gone — input
|
||||
// dispatch goes through the commands registry that BaseNodeAdapter builds
|
||||
// at construction. Tests fire msgs through `node.handlers.input` and
|
||||
// observe via `node.sends`, `inst.source.engine.*`, and per-fire calls
|
||||
// captured on a child stub registered through `RED.nodes.getNode(id)`.
|
||||
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const NodeClass = require('../../src/nodeClass');
|
||||
const commands = require('../../src/commands');
|
||||
const { createRegistry } = require('generalFunctions');
|
||||
const { makeNodeStub, makeREDStub } = require('../helpers/factories');
|
||||
const nodeClass = require('../../src/nodeClass');
|
||||
const { makeUiConfig } = require('../helpers/factories');
|
||||
|
||||
// Post-refactor: dispatch goes through the commands registry built by
|
||||
// BaseNodeAdapter (this._commands). We seed the registry on a prototype-
|
||||
// derived instance, then drive _attachInputHandler the same way the live
|
||||
// adapter would.
|
||||
|
||||
test('input handler routes legacy topic aliases to engine setters', async () => {
|
||||
const inst = Object.create(NodeClass.prototype);
|
||||
const node = makeNodeStub();
|
||||
const calls = [];
|
||||
|
||||
const source = {
|
||||
logger: { warn: () => {}, info: () => {}, debug: () => {}, error: () => {} },
|
||||
updateState(t) { calls.push(['clock', t]); },
|
||||
childRegistrationUtils: {
|
||||
registerChild(childSource, position) { calls.push(['registerChild', childSource, position]); },
|
||||
},
|
||||
function makeNode(id = 'reactor-1') {
|
||||
const sends = [];
|
||||
const statuses = [];
|
||||
const handlers = {};
|
||||
return {
|
||||
id, sends, statuses, handlers,
|
||||
send(arr) { sends.push(arr); },
|
||||
status(b) { statuses.push(b); },
|
||||
on(ev, fn) { handlers[ev] = fn; },
|
||||
warn() {}, error() {},
|
||||
};
|
||||
}
|
||||
|
||||
Object.defineProperty(source, 'setInfluent', { set(v) { calls.push(['Fluent', v]); } });
|
||||
Object.defineProperty(source, 'setOTR', { set(v) { calls.push(['OTR', v]); } });
|
||||
Object.defineProperty(source, 'setTemperature', { set(v) { calls.push(['Temperature', v]); } });
|
||||
Object.defineProperty(source, 'setDispersion', { set(v) { calls.push(['Dispersion', v]); } });
|
||||
function makeRED(nodeMap = {}) {
|
||||
return { nodes: { getNode: (id) => nodeMap[id] || null } };
|
||||
}
|
||||
|
||||
inst.node = node;
|
||||
inst.RED = makeREDStub({ childA: { source: { id: 'child-source-A' } } });
|
||||
inst.source = source;
|
||||
inst._commands = createRegistry(commands, { logger: source.logger });
|
||||
inst._attachInputHandler();
|
||||
function closeNode(node) {
|
||||
if (node.handlers.close) node.handlers.close(() => {});
|
||||
}
|
||||
|
||||
const onInput = node._handlers.input;
|
||||
let doneCount = 0;
|
||||
const done = () => { doneCount += 1; };
|
||||
test('legacy alias topics drive engine setters and updateState', async () => {
|
||||
const childSource = {
|
||||
id: 'child-source-A',
|
||||
config: { general: { id: 'child-source-A' }, functionality: { softwareType: 'measurement', positionVsParent: 'upstream' }, asset: { type: 'temperature' } },
|
||||
};
|
||||
const node = makeNode();
|
||||
const RED = makeRED({ childA: { source: childSource } });
|
||||
const inst = new nodeClass(makeUiConfig(), RED, node, 'reactor');
|
||||
|
||||
await onInput({ topic: 'clock', timestamp: 1000 }, () => {}, done);
|
||||
await onInput({ topic: 'Fluent', payload: { inlet: 0, F: 10, C: [] } }, () => {}, done);
|
||||
await onInput({ topic: 'OTR', payload: 3.5 }, () => {}, done);
|
||||
await onInput({ topic: 'Temperature', payload: 18.2 }, () => {}, done);
|
||||
await onInput({ topic: 'Dispersion', payload: 0.2 }, () => {}, done);
|
||||
await onInput({ topic: 'registerChild', payload: 'childA', positionVsParent: 'upstream' }, () => {}, done);
|
||||
try {
|
||||
let doneCount = 0;
|
||||
const done = () => { doneCount += 1; };
|
||||
|
||||
assert.equal(doneCount, 6);
|
||||
assert.deepEqual(calls[0], ['clock', 1000]);
|
||||
assert.equal(calls.some((x) => x[0] === 'Fluent'), true);
|
||||
assert.equal(calls.some((x) => x[0] === 'OTR'), true);
|
||||
assert.equal(calls.some((x) => x[0] === 'Temperature'), true);
|
||||
assert.equal(calls.some((x) => x[0] === 'Dispersion'), true);
|
||||
assert.deepEqual(calls.at(-1), ['registerChild', { id: 'child-source-A' }, 'upstream']);
|
||||
// data.clock alias → updateState(timestamp). Capture currentTime
|
||||
// before/after to verify the engine advanced.
|
||||
const t0 = inst.source.engine.currentTime;
|
||||
await node.handlers.input({ topic: 'clock', timestamp: t0 + 1 }, () => {}, done);
|
||||
|
||||
// Fluent alias → engine setInfluent setter.
|
||||
await node.handlers.input(
|
||||
{ topic: 'Fluent', payload: { inlet: 0, F: 7, C: [1,2,3,4,5,6,7,8,9,10,11,12,13] } },
|
||||
() => {}, done,
|
||||
);
|
||||
assert.equal(inst.source.engine.Fs[0], 7);
|
||||
assert.deepEqual(inst.source.engine.Cs_in[0], [1,2,3,4,5,6,7,8,9,10,11,12,13]);
|
||||
|
||||
// OTR alias → engine setOTR setter.
|
||||
await node.handlers.input({ topic: 'OTR', payload: 3.5 }, () => {}, done);
|
||||
assert.equal(inst.source.engine.OTR, 3.5);
|
||||
|
||||
// Temperature alias → engine setTemperature setter.
|
||||
await node.handlers.input({ topic: 'Temperature', payload: 18.2 }, () => {}, done);
|
||||
assert.equal(inst.source.engine.temperature, 18.2);
|
||||
|
||||
// Dispersion alias — CSTR engine does not own a setDispersion setter
|
||||
// (only PFR does); the Reactor wrapper guards on engine type and the
|
||||
// dispatch should silently return without throwing.
|
||||
await node.handlers.input({ topic: 'Dispersion', payload: 0.2 }, () => {}, done);
|
||||
|
||||
// registerChild alias → registers via childRegistrationUtils.
|
||||
// The handler resolves the child via RED.nodes.getNode(payload).source.
|
||||
await node.handlers.input(
|
||||
{ topic: 'registerChild', payload: 'childA', positionVsParent: 'upstream' },
|
||||
() => {}, done,
|
||||
);
|
||||
|
||||
assert.equal(doneCount, 6);
|
||||
} finally {
|
||||
closeNode(node);
|
||||
}
|
||||
});
|
||||
|
||||
test('canonical topics are accepted (data.fluent, data.otr, data.temperature)', async () => {
|
||||
const node = makeNode();
|
||||
const inst = new nodeClass(makeUiConfig(), makeRED(), node, 'reactor');
|
||||
|
||||
try {
|
||||
let done = 0;
|
||||
await node.handlers.input(
|
||||
{ topic: 'data.fluent', payload: { inlet: 0, F: 11, C: [0,0,0,0,0,0,0,0,0,0,0,0,0] } },
|
||||
() => {}, () => { done += 1; },
|
||||
);
|
||||
assert.equal(inst.source.engine.Fs[0], 11);
|
||||
|
||||
await node.handlers.input({ topic: 'data.otr', payload: 4.2 }, () => {}, () => { done += 1; });
|
||||
assert.equal(inst.source.engine.OTR, 4.2);
|
||||
|
||||
await node.handlers.input({ topic: 'data.temperature', payload: 19.7 }, () => {}, () => { done += 1; });
|
||||
assert.equal(inst.source.engine.temperature, 19.7);
|
||||
|
||||
assert.equal(done, 3);
|
||||
} finally {
|
||||
closeNode(node);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,32 +1,84 @@
|
||||
'use strict';
|
||||
|
||||
// Phase 10 rewrite: drives only the public BaseNodeAdapter surface.
|
||||
// The pre-refactor _registerChild method was renamed to
|
||||
// _scheduleRegistration inside BaseNodeAdapter and now fires automatically
|
||||
// 100ms after construction. We verify the emission by capturing the Port-2
|
||||
// message on `node.sends` after the registration delay elapses.
|
||||
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const NodeClass = require('../../src/nodeClass');
|
||||
const { makeNodeStub } = require('../helpers/factories');
|
||||
const nodeClass = require('../../src/nodeClass');
|
||||
const { makeUiConfig } = require('../helpers/factories');
|
||||
|
||||
// Post-refactor: BaseNodeAdapter handles registration via _scheduleRegistration
|
||||
// (was _registerChild). Topic moved from 'registerChild' to 'child.register'.
|
||||
test('_scheduleRegistration emits delayed child.register message on output 2', () => {
|
||||
const inst = Object.create(NodeClass.prototype);
|
||||
const node = makeNodeStub();
|
||||
function makeRED() { return { nodes: { getNode: () => null } }; }
|
||||
|
||||
inst.node = node;
|
||||
inst.config = { functionality: { positionVsParent: 'downstream', distance: null } };
|
||||
function makeNode(id = 'reactor-node-1') {
|
||||
const sends = [];
|
||||
const statuses = [];
|
||||
const handlers = {};
|
||||
return {
|
||||
id, sends, statuses, handlers,
|
||||
send(arr) { sends.push(arr); },
|
||||
status(b) { statuses.push(b); },
|
||||
on(ev, fn) { handlers[ev] = fn; },
|
||||
warn() {}, error() {},
|
||||
};
|
||||
}
|
||||
|
||||
const originalSetTimeout = global.setTimeout;
|
||||
const delays = [];
|
||||
global.setTimeout = (fn, ms) => { delays.push(ms); fn(); return 1; };
|
||||
function closeNode(node) {
|
||||
if (node.handlers.close) node.handlers.close(() => {});
|
||||
}
|
||||
|
||||
test('scheduled child.register message lands on Port 2 after construction', async () => {
|
||||
const node = makeNode();
|
||||
const inst = new nodeClass(
|
||||
makeUiConfig({ positionVsParent: 'downstream' }),
|
||||
makeRED(),
|
||||
node,
|
||||
'reactor',
|
||||
);
|
||||
|
||||
try {
|
||||
inst._scheduleRegistration();
|
||||
} finally {
|
||||
global.setTimeout = originalSetTimeout;
|
||||
}
|
||||
// BaseNodeAdapter._scheduleRegistration uses a 100ms setTimeout; wait
|
||||
// slightly longer to let it fire.
|
||||
await new Promise((r) => setTimeout(r, 130));
|
||||
|
||||
assert.deepEqual(delays, [100]);
|
||||
assert.equal(node._sent.length, 1);
|
||||
assert.equal(Array.isArray(node._sent[0]), true);
|
||||
assert.equal(node._sent[0][2].topic, 'child.register');
|
||||
assert.equal(node._sent[0][2].payload, node.id);
|
||||
assert.equal(node._sent[0][2].positionVsParent, 'downstream');
|
||||
// The registration send is the [null, null, {child.register}] triple.
|
||||
const regSends = node.sends.filter(
|
||||
(s) => Array.isArray(s) && s[0] === null && s[1] === null && s[2] && s[2].topic === 'child.register',
|
||||
);
|
||||
assert.equal(regSends.length, 1, 'exactly one child.register message expected');
|
||||
const msg = regSends[0][2];
|
||||
assert.equal(msg.topic, 'child.register');
|
||||
assert.equal(msg.payload, node.id);
|
||||
assert.equal(msg.positionVsParent, 'downstream');
|
||||
// After construction the source is exposed on the node for sibling lookup.
|
||||
assert.strictEqual(node.source, inst.source);
|
||||
} finally {
|
||||
closeNode(node);
|
||||
}
|
||||
});
|
||||
|
||||
test('child.register handler ignores unknown child ids without throwing', async () => {
|
||||
const node = makeNode();
|
||||
const inst = new nodeClass(makeUiConfig(), makeRED(), node, 'reactor');
|
||||
|
||||
try {
|
||||
let done = 0;
|
||||
await assert.doesNotReject(async () => {
|
||||
await node.handlers.input(
|
||||
{ topic: 'child.register', payload: 'missing-child', positionVsParent: 'upstream' },
|
||||
() => {},
|
||||
() => { done += 1; },
|
||||
);
|
||||
});
|
||||
assert.equal(done, 1);
|
||||
// No child should have been registered into the engine's registry.
|
||||
const registered = inst.source.engine.childRegistrationUtils;
|
||||
assert.ok(registered, 'childRegistrationUtils exists on engine');
|
||||
} finally {
|
||||
closeNode(node);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,28 +1,52 @@
|
||||
'use strict';
|
||||
|
||||
// Phase 10 rewrite: drives only the public BaseNodeAdapter surface for
|
||||
// the nodeClass-level checks, and the public Reactor_CSTR engine surface
|
||||
// for the domain-level checks. The pre-refactor private nodeClass methods
|
||||
// are gone — `buildDomainConfig` is the documented override hook
|
||||
// (CONTRACTS.md §2) and is fair game to call on a real constructed
|
||||
// instance.
|
||||
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const nodeClass = require('../../src/nodeClass');
|
||||
const { Reactor_CSTR } = require('../../src/specificClass');
|
||||
const NodeClass = require('../../src/nodeClass');
|
||||
const { makeReactorConfig, makeUiConfig } = require('../helpers/factories');
|
||||
|
||||
/**
|
||||
* Smoke tests for Fix 3: configurable speedUpFactor on Reactor.
|
||||
*/
|
||||
function makeRED() { return { nodes: { getNode: () => null } }; }
|
||||
|
||||
test('specificClass defaults speedUpFactor to 1 when not in config', () => {
|
||||
function makeNode(id = 'reactor-node-1') {
|
||||
const sends = [];
|
||||
const statuses = [];
|
||||
const handlers = {};
|
||||
return {
|
||||
id, sends, statuses, handlers,
|
||||
send(arr) { sends.push(arr); },
|
||||
status(b) { statuses.push(b); },
|
||||
on(ev, fn) { handlers[ev] = fn; },
|
||||
warn() {}, error() {},
|
||||
};
|
||||
}
|
||||
|
||||
function closeNode(node) {
|
||||
if (node.handlers.close) node.handlers.close(() => {});
|
||||
}
|
||||
|
||||
test('Reactor_CSTR engine defaults speedUpFactor to 1 when not in config', () => {
|
||||
const config = makeReactorConfig();
|
||||
const reactor = new Reactor_CSTR(config);
|
||||
assert.equal(reactor.speedUpFactor, 1, 'speedUpFactor should default to 1');
|
||||
});
|
||||
|
||||
test('specificClass accepts speedUpFactor from config', () => {
|
||||
test('Reactor_CSTR engine accepts speedUpFactor from config', () => {
|
||||
const config = makeReactorConfig();
|
||||
config.speedUpFactor = 10;
|
||||
const reactor = new Reactor_CSTR(config);
|
||||
assert.equal(reactor.speedUpFactor, 10, 'speedUpFactor should be read from config');
|
||||
});
|
||||
|
||||
test('specificClass accepts speedUpFactor = 60 for accelerated simulation', () => {
|
||||
test('Reactor_CSTR engine accepts speedUpFactor = 60 for accelerated simulation', () => {
|
||||
const config = makeReactorConfig();
|
||||
config.speedUpFactor = 60;
|
||||
const reactor = new Reactor_CSTR(config);
|
||||
@@ -30,21 +54,27 @@ test('specificClass accepts speedUpFactor = 60 for accelerated simulation', () =
|
||||
});
|
||||
|
||||
test('buildDomainConfig propagates speedUpFactor from uiConfig', () => {
|
||||
const inst = Object.create(NodeClass.prototype);
|
||||
inst.node = { id: 'n-reactor' };
|
||||
inst.name = 'reactor';
|
||||
const dc = inst.buildDomainConfig(makeUiConfig({ speedUpFactor: 5 }));
|
||||
assert.equal(dc.reactor.speedUpFactor, 5);
|
||||
const node = makeNode();
|
||||
const inst = new nodeClass(makeUiConfig(), makeRED(), node, 'reactor');
|
||||
try {
|
||||
const dc = inst.buildDomainConfig(makeUiConfig({ speedUpFactor: 5 }));
|
||||
assert.equal(dc.reactor.speedUpFactor, 5);
|
||||
} finally {
|
||||
closeNode(node);
|
||||
}
|
||||
});
|
||||
|
||||
test('buildDomainConfig defaults speedUpFactor to 1 when missing from uiConfig', () => {
|
||||
const inst = Object.create(NodeClass.prototype);
|
||||
inst.node = { id: 'n-reactor' };
|
||||
inst.name = 'reactor';
|
||||
const ui = makeUiConfig();
|
||||
delete ui.speedUpFactor;
|
||||
const dc = inst.buildDomainConfig(ui);
|
||||
assert.equal(dc.reactor.speedUpFactor, 1);
|
||||
const node = makeNode();
|
||||
const inst = new nodeClass(makeUiConfig(), makeRED(), node, 'reactor');
|
||||
try {
|
||||
const ui = makeUiConfig();
|
||||
delete ui.speedUpFactor;
|
||||
const dc = inst.buildDomainConfig(ui);
|
||||
assert.equal(dc.reactor.speedUpFactor, 1);
|
||||
} finally {
|
||||
closeNode(node);
|
||||
}
|
||||
});
|
||||
|
||||
test('updateState with speedUpFactor=1 advances roughly real-time', () => {
|
||||
|
||||
Reference in New Issue
Block a user