Files
reactor/test/basic/register-child.basic.test.js
znetsixe c84dd781a3 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>
2026-05-11 19:44:09 +02:00

85 lines
2.8 KiB
JavaScript

'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 { makeUiConfig } = require('../helpers/factories');
function makeRED() { return { nodes: { getNode: () => 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() {},
};
}
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 {
// BaseNodeAdapter._scheduleRegistration uses a 100ms setTimeout; wait
// slightly longer to let it fire.
await new Promise((r) => setTimeout(r, 130));
// 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);
}
});