beadboard/tests/guards/graph-responsive-contract.test.mjs
zenchantlive 4ee550c333 feat(telemetry): complete bb-buff.1.3 - Backend Liveness Refactor
STORY:
The session backend needed to aggregate agent health from a live
telemetry stream rather than static bead metadata. This refactor
makes liveness signals real-time and accurate.

COLLABORATION:
We extended the ActivityEvent model with a native 'heartbeat' kind,
updated extendActivityLease() to emit through the activity bus, and
refactored getAgentLivenessMap() to prioritize heartbeat activity
history over stale bead metadata.

DELIVERABLES:
- ActivityEvent extended with 'heartbeat' kind
- extendActivityLease() emits heartbeats through activity bus
- getAgentLivenessMap() prefers telemetry over static metadata
- Registry APIs support projectRoot injection for testing
- Tests verify preference logic via TDD

VERIFICATION:
- 93/93 tests PASSING
- Heartbeat override verified in isolated temp projects

CLOSES: bb-buff.1.3
BLOCKS: bb-buff.3.2, bb-buff.3.3, bb-buff.2.1
2026-02-15 21:14:05 -08:00

82 lines
4.2 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import path from 'node:path';
const ROOT = process.cwd();
async function read(relativePath) {
return fs.readFile(path.join(ROOT, relativePath), 'utf8');
}
test('graph page defines tabbed layout, epic chips, and mobile fallback', async () => {
const graphPage = await read('src/components/graph/dependency-graph-page.tsx');
// Tabbed layout: Tasks and Dependencies tabs
assert.match(graphPage, /WorkflowTabs/, 'should use WorkflowTabs component');
assert.match(graphPage, /activeTab/, 'should track active tab state');
// Epic chip strip replaces sidebar
assert.match(graphPage, /EpicChipStrip/, 'should use EpicChipStrip component');
// Mobile panel toggle preserved
assert.match(graphPage, /Switch to Graph/);
assert.match(graphPage, /Back to Selection/);
// Task card grid extracted
assert.match(graphPage, /TaskCardGrid/, 'should use TaskCardGrid component');
// Task details drawer
assert.match(graphPage, /TaskDetailsDrawer/, 'should use TaskDetailsDrawer drawer');
assert.match(graphPage, /projectRoot=\{projectRoot\}/, 'drawer should receive project root for edits');
assert.match(graphPage, /onIssueUpdated=\{.*refreshIssues\(\)\}/, 'drawer should trigger refresh after edits');
// Dependency flow strip
assert.match(graphPage, /DependencyFlowStrip/, 'should use DependencyFlowStrip component');
// Graph section with ReactFlow
assert.match(graphPage, /GraphSection/, 'should use GraphSection component');
assert.match(graphPage, /ReactFlowProvider/, 'should wrap graph in ReactFlowProvider');
// Edge options and node types still configured
assert.match(graphPage, /defaultEdgeOptions/);
assert.match(graphPage, /nodeTypes/);
// Actionable node detection
assert.match(graphPage, /actionableNodeIds/, 'should compute actionable (unblocked) nodes');
assert.match(graphPage, /ui-field/, 'graph filters should use shared dark field styling');
assert.match(graphPage, /ui-select/, 'graph select should use shared dark select styling');
});
test('extracted graph section has viewport and legend', async () => {
const graphSection = await read('src/components/graph/graph-section.tsx');
assert.match(graphSection, /className=\"workflow-graph-flow\"/, 'graph should have workflow-graph-flow class');
assert.match(graphSection, /workflow-graph-legend/, 'should have legend');
assert.match(graphSection, /translateExtent=\{\[/, 'should set translate extent');
assert.match(graphSection, /defaultEdgeOptions=\{/, 'should pass edge options');
assert.match(graphSection, /blockerAnalysis/, 'should show blocker stats');
assert.match(graphSection, /hideClosed/, 'should support hideClosed state in legend');
assert.match(graphSection, /!hideClosed/, 'done legend should be hidden when closed items are hidden');
assert.match(graphSection, /Read left to right/, 'legend should include plain directional hint');
assert.match(graphSection, /Left = blockers/, 'legend should include left/right dependency meaning');
assert.match(graphSection, /Right = work unblocked by this task/, 'legend should include downstream meaning');
assert.match(graphSection, /min-h-\[24rem\]/, 'graph container should enforce bounded minimum height');
assert.match(graphSection, /md:min-h-\[35rem\]/, 'graph container should scale minimum height on desktop');
});
test('graph node card supports tooltips and actionable glow', async () => {
const nodeCard = await read('src/components/graph/graph-node-card.tsx');
assert.match(nodeCard, /isActionable/, 'should check actionable state');
assert.match(nodeCard, /ring-emerald-400/, 'actionable nodes should have green glow');
assert.match(nodeCard, /node-select-pulse/, 'selected nodes should pulse');
assert.match(nodeCard, /blockerTooltipLines/, 'should display blocker tooltip');
assert.match(nodeCard, /isDimmed/, 'should support dimming non-chain nodes');
assert.match(nodeCard, /Ready to work/, 'actionable tooltip text');
});
test('graph edges expose explicit relation labels', async () => {
const graphPage = await read('src/components/graph/dependency-graph-page.tsx');
assert.match(graphPage, /label:\s*'BLOCKS'/, 'edges should include plain-language relation labels');
});