docs(dashboardapi): output-coverage manifest + populated/degraded tests (#43)
Per .claude/rules/output-coverage.md every node ships test/_output-manifest.md
enumerating every output across every state. This manifest covers all the
outputs added by slices #34-#42 in this PRD:
- Port 0 upsert message: every key (topic, url, method, headers, payload,
meta) with type and tested states.
- Port 1: explicit "not used" with rationale.
- Port 2: explicit "not used" with rationale.
- Structured log outputs: 5 events (regen-emitted, regen-skipped,
manual-regen-requested, parent-panels-deduped, flows:started) with
fields and corresponding test.
- specificClass return shapes: 6 methods with populated + degraded states.
- Anti-patterns enforced: no payload:null, absent vs null discipline,
tab id avoidance in predicate.
- test/_output-manifest.md: the manifest.
- test/basic/slice43-output-manifest.basic.test.js: 6 cross-cutting tests
exercising populated AND degraded states (token absent, folderUid absent,
template missing, diff-skip, regen logging, manual regen).
Backfill manifests for other nodes tracked in IMPROVEMENTS_BACKLOG.
Closes #43
2026-05-26 18:08:48 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
// Output-coverage tests per .claude/rules/output-coverage.md and
|
|
|
|
|
// test/_output-manifest.md. Every output is exercised in both populated
|
|
|
|
|
// and degraded states.
|
|
|
|
|
|
|
|
|
|
const test = require('node:test');
|
|
|
|
|
const assert = require('node:assert/strict');
|
|
|
|
|
|
|
|
|
|
const DashboardApi = require('../../src/specificClass.js');
|
|
|
|
|
const handlers = require('../../src/commands/handlers.js');
|
|
|
|
|
|
|
|
|
|
function makeChild(id, name = id, softwareType = 'measurement') {
|
|
|
|
|
return {
|
|
|
|
|
config: {
|
|
|
|
|
general: { id, name },
|
|
|
|
|
functionality: { softwareType, positionVsParent: 'downstream' },
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function makeCtx(nodeId = 'dApi-1') {
|
|
|
|
|
const sends = [];
|
|
|
|
|
const logs = [];
|
|
|
|
|
return {
|
|
|
|
|
sends,
|
|
|
|
|
logs,
|
|
|
|
|
ctx: {
|
|
|
|
|
node: { id: nodeId },
|
|
|
|
|
RED: { nodes: { getNode: () => null } },
|
|
|
|
|
send: (m) => sends.push(m),
|
|
|
|
|
logger: null,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Port 0 message shape: populated ────────────────────────────────────
|
2026-05-27 21:02:38 +02:00
|
|
|
test('Port 0 emit has all required keys when token + folderUid configured', async () => {
|
docs(dashboardapi): output-coverage manifest + populated/degraded tests (#43)
Per .claude/rules/output-coverage.md every node ships test/_output-manifest.md
enumerating every output across every state. This manifest covers all the
outputs added by slices #34-#42 in this PRD:
- Port 0 upsert message: every key (topic, url, method, headers, payload,
meta) with type and tested states.
- Port 1: explicit "not used" with rationale.
- Port 2: explicit "not used" with rationale.
- Structured log outputs: 5 events (regen-emitted, regen-skipped,
manual-regen-requested, parent-panels-deduped, flows:started) with
fields and corresponding test.
- specificClass return shapes: 6 methods with populated + degraded states.
- Anti-patterns enforced: no payload:null, absent vs null discipline,
tab id avoidance in predicate.
- test/_output-manifest.md: the manifest.
- test/basic/slice43-output-manifest.basic.test.js: 6 cross-cutting tests
exercising populated AND degraded states (token absent, folderUid absent,
template missing, diff-skip, regen logging, manual regen).
Backfill manifests for other nodes tracked in IMPROVEMENTS_BACKLOG.
Closes #43
2026-05-26 18:08:48 +02:00
|
|
|
const api = new DashboardApi({
|
|
|
|
|
grafanaConnector: { protocol: 'http', host: 'grafana', port: 3000, bearerToken: 'tok', folderUid: 'rnd-folder' },
|
|
|
|
|
});
|
|
|
|
|
api.lastFlowsStartedDiff = null; // cold start
|
|
|
|
|
const { sends, ctx } = makeCtx();
|
2026-05-27 21:02:38 +02:00
|
|
|
await handlers.registerChild(api, { topic: 'child.register', payload: makeChild('m-1', 'FT-001') }, ctx);
|
docs(dashboardapi): output-coverage manifest + populated/degraded tests (#43)
Per .claude/rules/output-coverage.md every node ships test/_output-manifest.md
enumerating every output across every state. This manifest covers all the
outputs added by slices #34-#42 in this PRD:
- Port 0 upsert message: every key (topic, url, method, headers, payload,
meta) with type and tested states.
- Port 1: explicit "not used" with rationale.
- Port 2: explicit "not used" with rationale.
- Structured log outputs: 5 events (regen-emitted, regen-skipped,
manual-regen-requested, parent-panels-deduped, flows:started) with
fields and corresponding test.
- specificClass return shapes: 6 methods with populated + degraded states.
- Anti-patterns enforced: no payload:null, absent vs null discipline,
tab id avoidance in predicate.
- test/_output-manifest.md: the manifest.
- test/basic/slice43-output-manifest.basic.test.js: 6 cross-cutting tests
exercising populated AND degraded states (token absent, folderUid absent,
template missing, diff-skip, regen logging, manual regen).
Backfill manifests for other nodes tracked in IMPROVEMENTS_BACKLOG.
Closes #43
2026-05-26 18:08:48 +02:00
|
|
|
|
|
|
|
|
assert.ok(sends.length >= 1);
|
|
|
|
|
const m = sends[0];
|
|
|
|
|
assert.equal(m.topic, 'create');
|
|
|
|
|
assert.equal(m.method, 'POST');
|
|
|
|
|
assert.equal(m.headers['Accept'], 'application/json');
|
|
|
|
|
assert.equal(m.headers['Content-Type'], 'application/json');
|
|
|
|
|
assert.equal(m.headers.Authorization, 'Bearer tok');
|
|
|
|
|
assert.match(m.url, /^http:\/\/grafana:3000\/api\/dashboards\/db$/);
|
|
|
|
|
assert.equal(m.payload.overwrite, true);
|
|
|
|
|
assert.ok(m.payload.dashboard, 'dashboard JSON present');
|
|
|
|
|
assert.equal(m.payload.folderUid, 'rnd-folder');
|
|
|
|
|
// meta
|
|
|
|
|
assert.equal(m.meta.nodeId, 'm-1');
|
|
|
|
|
assert.equal(m.meta.softwareType, 'measurement');
|
|
|
|
|
assert.equal(typeof m.meta.uid, 'string');
|
|
|
|
|
assert.equal(m.meta.title, 'FT-001');
|
|
|
|
|
assert.equal(m.meta.trigger, 'child.register');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ── Port 0 degraded: token absent, folderUid absent ───────────────────
|
2026-05-27 21:02:38 +02:00
|
|
|
test('Port 0 emit omits Authorization header when no bearerToken configured', async () => {
|
docs(dashboardapi): output-coverage manifest + populated/degraded tests (#43)
Per .claude/rules/output-coverage.md every node ships test/_output-manifest.md
enumerating every output across every state. This manifest covers all the
outputs added by slices #34-#42 in this PRD:
- Port 0 upsert message: every key (topic, url, method, headers, payload,
meta) with type and tested states.
- Port 1: explicit "not used" with rationale.
- Port 2: explicit "not used" with rationale.
- Structured log outputs: 5 events (regen-emitted, regen-skipped,
manual-regen-requested, parent-panels-deduped, flows:started) with
fields and corresponding test.
- specificClass return shapes: 6 methods with populated + degraded states.
- Anti-patterns enforced: no payload:null, absent vs null discipline,
tab id avoidance in predicate.
- test/_output-manifest.md: the manifest.
- test/basic/slice43-output-manifest.basic.test.js: 6 cross-cutting tests
exercising populated AND degraded states (token absent, folderUid absent,
template missing, diff-skip, regen logging, manual regen).
Backfill manifests for other nodes tracked in IMPROVEMENTS_BACKLOG.
Closes #43
2026-05-26 18:08:48 +02:00
|
|
|
const api = new DashboardApi({}); // no creds
|
|
|
|
|
api.lastFlowsStartedDiff = null;
|
|
|
|
|
const { sends, ctx } = makeCtx();
|
2026-05-27 21:02:38 +02:00
|
|
|
await handlers.registerChild(api, { topic: 'child.register', payload: makeChild('m-2') }, ctx);
|
docs(dashboardapi): output-coverage manifest + populated/degraded tests (#43)
Per .claude/rules/output-coverage.md every node ships test/_output-manifest.md
enumerating every output across every state. This manifest covers all the
outputs added by slices #34-#42 in this PRD:
- Port 0 upsert message: every key (topic, url, method, headers, payload,
meta) with type and tested states.
- Port 1: explicit "not used" with rationale.
- Port 2: explicit "not used" with rationale.
- Structured log outputs: 5 events (regen-emitted, regen-skipped,
manual-regen-requested, parent-panels-deduped, flows:started) with
fields and corresponding test.
- specificClass return shapes: 6 methods with populated + degraded states.
- Anti-patterns enforced: no payload:null, absent vs null discipline,
tab id avoidance in predicate.
- test/_output-manifest.md: the manifest.
- test/basic/slice43-output-manifest.basic.test.js: 6 cross-cutting tests
exercising populated AND degraded states (token absent, folderUid absent,
template missing, diff-skip, regen logging, manual regen).
Backfill manifests for other nodes tracked in IMPROVEMENTS_BACKLOG.
Closes #43
2026-05-26 18:08:48 +02:00
|
|
|
const m = sends[0];
|
|
|
|
|
assert.equal(m.headers.Authorization, undefined,
|
|
|
|
|
'Authorization should be absent (not empty string, not null)');
|
|
|
|
|
assert.equal(m.payload.folderUid, undefined,
|
|
|
|
|
'folderUid should be absent when empty');
|
|
|
|
|
assert.equal('folderId' in m.payload, false,
|
|
|
|
|
'folderId should also be absent (not 0)');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ── Port 0 degraded: no template for softwareType ─────────────────────
|
2026-05-27 21:02:38 +02:00
|
|
|
test('Port 0 emits no message when child softwareType has no template', async () => {
|
docs(dashboardapi): output-coverage manifest + populated/degraded tests (#43)
Per .claude/rules/output-coverage.md every node ships test/_output-manifest.md
enumerating every output across every state. This manifest covers all the
outputs added by slices #34-#42 in this PRD:
- Port 0 upsert message: every key (topic, url, method, headers, payload,
meta) with type and tested states.
- Port 1: explicit "not used" with rationale.
- Port 2: explicit "not used" with rationale.
- Structured log outputs: 5 events (regen-emitted, regen-skipped,
manual-regen-requested, parent-panels-deduped, flows:started) with
fields and corresponding test.
- specificClass return shapes: 6 methods with populated + degraded states.
- Anti-patterns enforced: no payload:null, absent vs null discipline,
tab id avoidance in predicate.
- test/_output-manifest.md: the manifest.
- test/basic/slice43-output-manifest.basic.test.js: 6 cross-cutting tests
exercising populated AND degraded states (token absent, folderUid absent,
template missing, diff-skip, regen logging, manual regen).
Backfill manifests for other nodes tracked in IMPROVEMENTS_BACKLOG.
Closes #43
2026-05-26 18:08:48 +02:00
|
|
|
const api = new DashboardApi({});
|
|
|
|
|
api.lastFlowsStartedDiff = null;
|
|
|
|
|
const { sends, ctx } = makeCtx();
|
|
|
|
|
// 'nonexistent' has no config/<>.json file
|
2026-05-27 21:02:38 +02:00
|
|
|
await handlers.registerChild(api, { topic: 'child.register', payload: makeChild('m-3', 'm-3', 'nonexistent') }, ctx);
|
docs(dashboardapi): output-coverage manifest + populated/degraded tests (#43)
Per .claude/rules/output-coverage.md every node ships test/_output-manifest.md
enumerating every output across every state. This manifest covers all the
outputs added by slices #34-#42 in this PRD:
- Port 0 upsert message: every key (topic, url, method, headers, payload,
meta) with type and tested states.
- Port 1: explicit "not used" with rationale.
- Port 2: explicit "not used" with rationale.
- Structured log outputs: 5 events (regen-emitted, regen-skipped,
manual-regen-requested, parent-panels-deduped, flows:started) with
fields and corresponding test.
- specificClass return shapes: 6 methods with populated + degraded states.
- Anti-patterns enforced: no payload:null, absent vs null discipline,
tab id avoidance in predicate.
- test/_output-manifest.md: the manifest.
- test/basic/slice43-output-manifest.basic.test.js: 6 cross-cutting tests
exercising populated AND degraded states (token absent, folderUid absent,
template missing, diff-skip, regen logging, manual regen).
Backfill manifests for other nodes tracked in IMPROVEMENTS_BACKLOG.
Closes #43
2026-05-26 18:08:48 +02:00
|
|
|
assert.equal(sends.length, 0, 'no upsert message should be emitted when template missing');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ── Diff-skip path: no emission, logged outcome:no-diff ───────────────
|
2026-05-27 21:02:38 +02:00
|
|
|
test('Diff-skip suppresses Port 0 emission AND records the skip in source.logger', async () => {
|
docs(dashboardapi): output-coverage manifest + populated/degraded tests (#43)
Per .claude/rules/output-coverage.md every node ships test/_output-manifest.md
enumerating every output across every state. This manifest covers all the
outputs added by slices #34-#42 in this PRD:
- Port 0 upsert message: every key (topic, url, method, headers, payload,
meta) with type and tested states.
- Port 1: explicit "not used" with rationale.
- Port 2: explicit "not used" with rationale.
- Structured log outputs: 5 events (regen-emitted, regen-skipped,
manual-regen-requested, parent-panels-deduped, flows:started) with
fields and corresponding test.
- specificClass return shapes: 6 methods with populated + degraded states.
- Anti-patterns enforced: no payload:null, absent vs null discipline,
tab id avoidance in predicate.
- test/_output-manifest.md: the manifest.
- test/basic/slice43-output-manifest.basic.test.js: 6 cross-cutting tests
exercising populated AND degraded states (token absent, folderUid absent,
template missing, diff-skip, regen logging, manual regen).
Backfill manifests for other nodes tracked in IMPROVEMENTS_BACKLOG.
Closes #43
2026-05-26 18:08:48 +02:00
|
|
|
const api = new DashboardApi({});
|
|
|
|
|
// Set diff so the predicate returns false (no overlap with subtree).
|
|
|
|
|
api.lastFlowsStartedDiff = { added: ['unrelated'], changed: [], removed: [], rewired: [] };
|
|
|
|
|
// Stub logger to capture
|
|
|
|
|
const captured = [];
|
|
|
|
|
api.logger = { info: (e) => captured.push(e), debug: () => {} };
|
|
|
|
|
|
|
|
|
|
const { sends, ctx } = makeCtx('dApi-1');
|
2026-05-27 21:02:38 +02:00
|
|
|
await handlers.registerChild(api, { topic: 'child.register', payload: makeChild('m-4') }, ctx);
|
docs(dashboardapi): output-coverage manifest + populated/degraded tests (#43)
Per .claude/rules/output-coverage.md every node ships test/_output-manifest.md
enumerating every output across every state. This manifest covers all the
outputs added by slices #34-#42 in this PRD:
- Port 0 upsert message: every key (topic, url, method, headers, payload,
meta) with type and tested states.
- Port 1: explicit "not used" with rationale.
- Port 2: explicit "not used" with rationale.
- Structured log outputs: 5 events (regen-emitted, regen-skipped,
manual-regen-requested, parent-panels-deduped, flows:started) with
fields and corresponding test.
- specificClass return shapes: 6 methods with populated + degraded states.
- Anti-patterns enforced: no payload:null, absent vs null discipline,
tab id avoidance in predicate.
- test/_output-manifest.md: the manifest.
- test/basic/slice43-output-manifest.basic.test.js: 6 cross-cutting tests
exercising populated AND degraded states (token absent, folderUid absent,
template missing, diff-skip, regen logging, manual regen).
Backfill manifests for other nodes tracked in IMPROVEMENTS_BACKLOG.
Closes #43
2026-05-26 18:08:48 +02:00
|
|
|
|
|
|
|
|
assert.equal(sends.length, 0, 'no upsert emitted when subtree unchanged');
|
|
|
|
|
const skipLog = captured.find((e) => e.event === 'regen-skipped');
|
|
|
|
|
assert.ok(skipLog, 'skip log emitted');
|
|
|
|
|
assert.equal(skipLog.outcome, 'no-diff');
|
|
|
|
|
assert.equal(skipLog.trigger, 'child.register');
|
|
|
|
|
assert.equal(skipLog.dashboardApiId, 'dApi-1');
|
|
|
|
|
assert.equal(skipLog.childId, 'm-4');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ── Successful regen logs structured fields per N-4 ───────────────────
|
2026-05-27 21:02:38 +02:00
|
|
|
test('Successful regen logs event=regen-emitted with N-4 fields', async () => {
|
docs(dashboardapi): output-coverage manifest + populated/degraded tests (#43)
Per .claude/rules/output-coverage.md every node ships test/_output-manifest.md
enumerating every output across every state. This manifest covers all the
outputs added by slices #34-#42 in this PRD:
- Port 0 upsert message: every key (topic, url, method, headers, payload,
meta) with type and tested states.
- Port 1: explicit "not used" with rationale.
- Port 2: explicit "not used" with rationale.
- Structured log outputs: 5 events (regen-emitted, regen-skipped,
manual-regen-requested, parent-panels-deduped, flows:started) with
fields and corresponding test.
- specificClass return shapes: 6 methods with populated + degraded states.
- Anti-patterns enforced: no payload:null, absent vs null discipline,
tab id avoidance in predicate.
- test/_output-manifest.md: the manifest.
- test/basic/slice43-output-manifest.basic.test.js: 6 cross-cutting tests
exercising populated AND degraded states (token absent, folderUid absent,
template missing, diff-skip, regen logging, manual regen).
Backfill manifests for other nodes tracked in IMPROVEMENTS_BACKLOG.
Closes #43
2026-05-26 18:08:48 +02:00
|
|
|
const api = new DashboardApi({});
|
|
|
|
|
api.lastFlowsStartedDiff = null; // cold start → always regen
|
|
|
|
|
const captured = [];
|
|
|
|
|
api.logger = { info: (e) => captured.push(e), debug: () => {} };
|
|
|
|
|
|
|
|
|
|
const { ctx } = makeCtx('dApi-1');
|
2026-05-27 21:02:38 +02:00
|
|
|
await handlers.registerChild(api, { topic: 'child.register', payload: makeChild('m-5') }, ctx);
|
docs(dashboardapi): output-coverage manifest + populated/degraded tests (#43)
Per .claude/rules/output-coverage.md every node ships test/_output-manifest.md
enumerating every output across every state. This manifest covers all the
outputs added by slices #34-#42 in this PRD:
- Port 0 upsert message: every key (topic, url, method, headers, payload,
meta) with type and tested states.
- Port 1: explicit "not used" with rationale.
- Port 2: explicit "not used" with rationale.
- Structured log outputs: 5 events (regen-emitted, regen-skipped,
manual-regen-requested, parent-panels-deduped, flows:started) with
fields and corresponding test.
- specificClass return shapes: 6 methods with populated + degraded states.
- Anti-patterns enforced: no payload:null, absent vs null discipline,
tab id avoidance in predicate.
- test/_output-manifest.md: the manifest.
- test/basic/slice43-output-manifest.basic.test.js: 6 cross-cutting tests
exercising populated AND degraded states (token absent, folderUid absent,
template missing, diff-skip, regen logging, manual regen).
Backfill manifests for other nodes tracked in IMPROVEMENTS_BACKLOG.
Closes #43
2026-05-26 18:08:48 +02:00
|
|
|
|
|
|
|
|
const emitLog = captured.find((e) => e.event === 'regen-emitted');
|
|
|
|
|
assert.ok(emitLog, 'regen-emitted log present');
|
|
|
|
|
assert.equal(emitLog.trigger, 'child.register');
|
|
|
|
|
assert.equal(emitLog.dashboardApiId, 'dApi-1');
|
|
|
|
|
assert.equal(emitLog.childId, 'm-5');
|
|
|
|
|
assert.equal(typeof emitLog.dashboardCount, 'number');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ── Manual regen logs manual-regen-requested + emits with trigger:manual ─
|
2026-05-27 21:02:38 +02:00
|
|
|
test('Manual regen logs manual-regen-requested and stamps trigger=manual', async () => {
|
docs(dashboardapi): output-coverage manifest + populated/degraded tests (#43)
Per .claude/rules/output-coverage.md every node ships test/_output-manifest.md
enumerating every output across every state. This manifest covers all the
outputs added by slices #34-#42 in this PRD:
- Port 0 upsert message: every key (topic, url, method, headers, payload,
meta) with type and tested states.
- Port 1: explicit "not used" with rationale.
- Port 2: explicit "not used" with rationale.
- Structured log outputs: 5 events (regen-emitted, regen-skipped,
manual-regen-requested, parent-panels-deduped, flows:started) with
fields and corresponding test.
- specificClass return shapes: 6 methods with populated + degraded states.
- Anti-patterns enforced: no payload:null, absent vs null discipline,
tab id avoidance in predicate.
- test/_output-manifest.md: the manifest.
- test/basic/slice43-output-manifest.basic.test.js: 6 cross-cutting tests
exercising populated AND degraded states (token absent, folderUid absent,
template missing, diff-skip, regen logging, manual regen).
Backfill manifests for other nodes tracked in IMPROVEMENTS_BACKLOG.
Closes #43
2026-05-26 18:08:48 +02:00
|
|
|
const api = new DashboardApi({});
|
|
|
|
|
api.recordChild(makeChild('m-6'));
|
|
|
|
|
const captured = [];
|
|
|
|
|
api.logger = { info: (e) => captured.push(e), debug: () => {} };
|
|
|
|
|
|
|
|
|
|
const { sends, ctx } = makeCtx();
|
2026-05-27 21:02:38 +02:00
|
|
|
await handlers.regenerateDashboard(api, { topic: 'regenerate-dashboard', payload: {} }, ctx);
|
docs(dashboardapi): output-coverage manifest + populated/degraded tests (#43)
Per .claude/rules/output-coverage.md every node ships test/_output-manifest.md
enumerating every output across every state. This manifest covers all the
outputs added by slices #34-#42 in this PRD:
- Port 0 upsert message: every key (topic, url, method, headers, payload,
meta) with type and tested states.
- Port 1: explicit "not used" with rationale.
- Port 2: explicit "not used" with rationale.
- Structured log outputs: 5 events (regen-emitted, regen-skipped,
manual-regen-requested, parent-panels-deduped, flows:started) with
fields and corresponding test.
- specificClass return shapes: 6 methods with populated + degraded states.
- Anti-patterns enforced: no payload:null, absent vs null discipline,
tab id avoidance in predicate.
- test/_output-manifest.md: the manifest.
- test/basic/slice43-output-manifest.basic.test.js: 6 cross-cutting tests
exercising populated AND degraded states (token absent, folderUid absent,
template missing, diff-skip, regen logging, manual regen).
Backfill manifests for other nodes tracked in IMPROVEMENTS_BACKLOG.
Closes #43
2026-05-26 18:08:48 +02:00
|
|
|
|
|
|
|
|
const reqLog = captured.find((e) => e.event === 'manual-regen-requested');
|
|
|
|
|
assert.ok(reqLog, 'manual-regen-requested log present');
|
|
|
|
|
assert.equal(reqLog.cachedChildCount, 1);
|
|
|
|
|
|
|
|
|
|
if (sends.length > 0) {
|
|
|
|
|
assert.equal(sends[0].meta.trigger, 'manual');
|
|
|
|
|
}
|
|
|
|
|
});
|