test(e2e): journey — graph render integrity vs DB (#395) - #433

Merged
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity
Jul 28, 2026
Merged

test(e2e): journey — graph render integrity vs DB (#395)#433
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity

Conversation

@AndresL230

@AndresL230AndresL230 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Journey #395: load the graph view (/tree) and assert the RENDERED knowledge graph against the database read over raw SQL — data assertions only, no force-layout geometry (positions are never read).

  • New spec frontend/e2e/graph.spec.ts (3 tests):
    1. Exact render counts vs DB — one rendered node per graph_nodes row plus one subject-root hub per DISTINCT enrolled course; one rendered edge per graph_edges row plus one hub spoke per course-linked node. test.fixme — red solely because of open bug Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 (below); the correct assertion is kept, not relaxed, so this is the ready-made acceptance test for the Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 fix.
    2. Concept-node integrity + mastery classification (passing) — every DB concept node renders exactly once (SVG label and the graph's accessibility list), every enrolled course's hub is present, at least every DB edge/spoke is drawn, and each node's DB mastery_score maps through the canonical thresholds (backend/config.py::get_mastery_tier) to the class the 2D graph encodes as node-circle opacity.
    3. Tier filter partition (passing) — selecting each mastery tier pill on /tree shows exactly the concepts whose DB score maps to that tier, and none other.
  • Additive harness seamfrontend/e2e/support/db.ts::dbQuery, a read-only raw-SQL helper so journeys compute expected values from the DB without hand-rolling pg plumbing (inherits the loopback-only safety guard). No new testids: the spec anchors on the registered graph-container testid plus structural/ARIA handles inside it (svg[aria-label="Knowledge graph"]<text>/<line>, the component's hidden a11y node list) — justification in the spec header; node identity matches on seeded concept names (DB data, not UI copy).
  • Harness convergence — carries the frontend/e2e/global-setup.ts fix from the test(e2e): journey — seeded session → dashboard #386 branch verbatim (storageState now includes the sapling_user localStorage identity; cookie-only state left authed pages on an infinite skeleton).

Bug #355 REPRODUCES on the migration-replayed local schema + rich seed

Probe (authed GET /api/graph/rich-user-active against the freshly seeded stack):

PROBE355 nodes_total=18 nodes_unique=17 dup_node_ids=["subject_root__rich-course-cs101"] edges_total=25 edges_unique=20 dup_edges=5

rich-user-active is enrolled in two offerings of the same abstract course (rich-off-cs101-f25 + rich-off-cs101-s26), and graph_service.get_graph synthesizes one subject_root__{course_id} per enrollment (loop over _user_enrolled_courses) instead of per distinct course — the CS subject root and its five hub spokes are emitted twice, exactly the duplicate-key defect #355 describes. This is the defect class this journey exists to catch: test 1 trips on it and is test.fixme(#355) with the correct assertions intact; tests 2–3 assert everything the duplication does not corrupt.

Verification — 10 consecutive local runs

10/10 green. Each run: 2 passed, 1 skipped — the skip is the test.fixme(#355) exact-count test; the two live tests passed on every run (~28s/run cold, ~11s/run warm). The full cycle was observed green twice back-to-back (20/20 runs total across both cycles).

Run12345678910
Resultpasspasspasspasspasspasspasspasspasspass

Cycle run under the shared stack lock: make e2e-up → probe → 10× npx playwright test e2e/graph.spec.tsmake e2e-down. A second probe on a volume carrying sibling-run residue reported the SAME single duplicate id (nodes_total=20 nodes_unique=19 dup_node_ids=["subject_root__rich-course-cs101"]) — totals drift with un-reset stack state, the dup signal does not; the spec itself is immune because every test truncates + re-seeds first.

Part of #402, closes#395

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • Added end-to-end coverage for graph rendering, node and edge counts, accessibility labels, mastery tiers, and tier filtering.
    • Added validation against database-backed graph data, including a documented known issue for duplicated subject hubs.
  • Accessibility

    • Improved stable identification of graph nodes and activation controls across 2D and 3D views.
  • Documentation

    • Expanded the test ID inventory to cover graph nodes, edges, accessibility elements, and zoom controls.

@coderabbitai

coderabbitaiBot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The graph components now expose stable test IDs and node identifiers across 2D and 3D renderers. A new Playwright suite derives graph expectations from database rows and validates rendered nodes, edges, mastery tiers, labels, counts, and filters.

Changes

Graph integrity validation

Layer / File(s)Summary
Graph test seams
frontend/src/components/KnowledgeGraph2D.tsx, frontend/src/components/KnowledgeGraph3D.tsx, frontend/eslint.config.mjs, docs/frontend-testids.md
Adds shared selectors for accessibility nodes, SVG nodes and edges, node IDs, node circles, activation controls, and zoom buttons; updates lint scope and test-ID documentation.
Database expectation mapping
frontend/e2e/graph.spec.ts
Loads graph data and enrolled courses, then derives expected counts, labels, hub IDs, and mastery-tier mappings.
Graph integrity specifications
frontend/e2e/graph.spec.ts
Checks rendered counts, node uniqueness, labels, opacity, edge coverage, and tier-filter partitioning, with a fixme for known hub duplication.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:jose-gael-cruz-lopez

Sequence Diagram(s)

sequenceDiagram
participant graphSpec as graph.spec.ts
participant database as Graph database
participant treePage as /tree page
participant graphDom as Graph DOM
graphSpec->>database: Load graph nodes, edges, and enrolled courses
graphSpec->>treePage: Navigate to /tree
treePage->>graphDom: Render SVG graph and accessibility node list
graphSpec->>graphDom: Validate IDs, counts, tiers, labels, opacity, and edges
graphSpec->>graphDom: Apply tier filters and verify node partitioning
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly matches the main change: an E2E graph integrity journey against the database for issue #395.
Description check✅ PassedThe description covers the summary, changes, related issue, and testing evidence, though it does not follow the template exactly.
Linked Issues check✅ PassedThe PR meets #395 by asserting graph counts and mastery mapping from DB rows with data-only checks and 10 local runs.
Out of Scope Changes check✅ PassedThe docs, test IDs, db helper, and global setup updates all support the graph integrity journey and are not out of scope.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/395-journey-graph-integrity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 28, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingc308689Commit Preview URL

Branch Preview URL
Jul 28 2026, 04:10 AM

AndresL230and others added 2 commits July 27, 2026 21:06
Loads /tree and asserts the rendered knowledge graph against raw-SQL
reads of graph_nodes/graph_edges/enrollments (new read-only dbQuery seam
in e2e/support/db.ts):
- exact node/edge render counts vs DB (+1 subject-root hub per distinct
enrolled course, +1 hub spoke per course node) — marked test.fixme
pending open bug #355, which duplicates the CS subject root on the
rich seed (two offerings of the same abstract course); the correct
assertion is kept, not relaxed
- every DB concept node renders exactly once, with the mastery class
derived from its DB mastery_score (config.get_mastery_tier thresholds)
encoded at the render layer (2D node-circle opacity)
- the /tree tier filter partitions nodes exactly by the DB-derived
classification
Data assertions only — no force-layout geometry is read.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review findings on PR #433: label-based node identification was a latent
gap (graph_nodes names are only unique PER COURSE, so cross-course name
or truncation-prefix collisions would fail a correct render — and the
label-keyed opacity map could silently mask a wrong class), and bare
structural/ARIA anchoring is not a sanctioned seam.
Fix both by minting graph-surface testids per the doc's process:
- KnowledgeGraph2D/3D a11y list: graph-node-items / graph-node-item
(+ data-node-id per entry) / graph-node-activate
- KnowledgeGraph2D SVG layer: graph-node (+ data-node-id),
graph-node-circle (the opacity-encoding mark), graph-edge,
graph-zoom-in/out/reset
- register all of it in docs/frontend-testids.md (graph surface row now
names the 2D/3D split) and add both files to the eslint enforcement
array (every intrinsic button is tagged — no suppressions needed)
The spec now identifies nodes exclusively by data-node-id and keys the
opacity map by id; rendered label text is still asserted per node, just
keyed by id instead of used as identity.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the test/395-journey-graph-integrity branch from ca9a2e7 to c308689CompareJuly 28, 2026 04:08

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/e2e/graph.spec.ts`:
- Around line 232-235: Replace the count-only assertion in
frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation covering every
expected DB edge and synthesized hub spoke, while preserving the existing
count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b170e05-e5bb-417d-a4f6-fbaa3a2c3c21

📥 Commits

Reviewing files that changed from the base of the PR and between c42717c and c308689.

📒 Files selected for processing (5)
  • docs/frontend-testids.md
  • frontend/e2e/graph.spec.ts
  • frontend/eslint.config.mjs
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/KnowledgeGraph3D.tsx

Comment on lines +232 to +235
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(await svgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Assert expected edge identities, not only a count floor.

Bug #355 currently adds five surplus spokes, so >= g.expectedEdgeCount still passes if up to five expected edges disappear. Expose each SVG line’s source/target IDs, then assert every DB edge and synthesized hub spoke is present; this remains valid while the exact-count test is fixme.

  • frontend/e2e/graph.spec.ts#L232-L235: compare the rendered edge endpoint pairs against all expected DB-edge and hub-spoke pairs.
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459: add stable source and target node-ID data attributes to each graph-edge line.
Proposed seam
 <line
data-testid="graph-edge"
+ data-source-node-id={s.id}+ data-target-node-id={t.id}
x1={s.x}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(awaitsvgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);
<line
key={i}
data-testid="graph-edge"
data-source-node-id={s.id}
data-target-node-id={t.id}
x1={s.x}
y1={s.y}
x2={t.x}
y2={t.y}
stroke="var(--text-muted)"
strokeOpacity={op}
strokeWidth={0.5+(l.strength||0.5)*1.2}
strokeLinecap="round"
/>
📍 Affects 2 files
  • frontend/e2e/graph.spec.ts#L232-L235 (this comment)
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/e2e/graph.spec.ts` around lines 232 - 235, Replace the count-only
assertion in frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation
covering every expected DB edge and synthesized hub spoke, while preserving the
existing count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.

@AndresL230

Copy link
Copy Markdown
CollaboratorAuthor

Code review

No issues found. Checked for bugs and CLAUDE.md compliance. (Panel findings fixed in-branch: the spec now anchors on id-keyed registered testids — graph-node-item + data-node-id on the a11y list, per docs/frontend-testids.md's Adding-a-surface process — which also structurally eliminates the latent label-collision gap; the db.ts helper converged on the merged queryRaw. Re-verified with a 3/3 confirmation cycle on the final content; the original 10/10×2 tally stands as acceptance evidence. The #355 acceptance test ships as test.fixme.)

🤖 Generated with Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(e2e): journey — graph render integrity vs DB

1 participant

@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

test(e2e): journey — graph render integrity vs DB (#395) - #433

Merged
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity
Jul 28, 2026
Merged

test(e2e): journey — graph render integrity vs DB (#395)#433
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity

Conversation

@AndresL230

@AndresL230AndresL230 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Journey #395: load the graph view (/tree) and assert the RENDERED knowledge graph against the database read over raw SQL — data assertions only, no force-layout geometry (positions are never read).

  • New spec frontend/e2e/graph.spec.ts (3 tests):
    1. Exact render counts vs DB — one rendered node per graph_nodes row plus one subject-root hub per DISTINCT enrolled course; one rendered edge per graph_edges row plus one hub spoke per course-linked node. test.fixme — red solely because of open bug Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 (below); the correct assertion is kept, not relaxed, so this is the ready-made acceptance test for the Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 fix.
    2. Concept-node integrity + mastery classification (passing) — every DB concept node renders exactly once (SVG label and the graph's accessibility list), every enrolled course's hub is present, at least every DB edge/spoke is drawn, and each node's DB mastery_score maps through the canonical thresholds (backend/config.py::get_mastery_tier) to the class the 2D graph encodes as node-circle opacity.
    3. Tier filter partition (passing) — selecting each mastery tier pill on /tree shows exactly the concepts whose DB score maps to that tier, and none other.
  • Additive harness seamfrontend/e2e/support/db.ts::dbQuery, a read-only raw-SQL helper so journeys compute expected values from the DB without hand-rolling pg plumbing (inherits the loopback-only safety guard). No new testids: the spec anchors on the registered graph-container testid plus structural/ARIA handles inside it (svg[aria-label="Knowledge graph"]<text>/<line>, the component's hidden a11y node list) — justification in the spec header; node identity matches on seeded concept names (DB data, not UI copy).
  • Harness convergence — carries the frontend/e2e/global-setup.ts fix from the test(e2e): journey — seeded session → dashboard #386 branch verbatim (storageState now includes the sapling_user localStorage identity; cookie-only state left authed pages on an infinite skeleton).

Bug #355 REPRODUCES on the migration-replayed local schema + rich seed

Probe (authed GET /api/graph/rich-user-active against the freshly seeded stack):

PROBE355 nodes_total=18 nodes_unique=17 dup_node_ids=["subject_root__rich-course-cs101"] edges_total=25 edges_unique=20 dup_edges=5

rich-user-active is enrolled in two offerings of the same abstract course (rich-off-cs101-f25 + rich-off-cs101-s26), and graph_service.get_graph synthesizes one subject_root__{course_id} per enrollment (loop over _user_enrolled_courses) instead of per distinct course — the CS subject root and its five hub spokes are emitted twice, exactly the duplicate-key defect #355 describes. This is the defect class this journey exists to catch: test 1 trips on it and is test.fixme(#355) with the correct assertions intact; tests 2–3 assert everything the duplication does not corrupt.

Verification — 10 consecutive local runs

10/10 green. Each run: 2 passed, 1 skipped — the skip is the test.fixme(#355) exact-count test; the two live tests passed on every run (~28s/run cold, ~11s/run warm). The full cycle was observed green twice back-to-back (20/20 runs total across both cycles).

Run12345678910
Resultpasspasspasspasspasspasspasspasspasspass

Cycle run under the shared stack lock: make e2e-up → probe → 10× npx playwright test e2e/graph.spec.tsmake e2e-down. A second probe on a volume carrying sibling-run residue reported the SAME single duplicate id (nodes_total=20 nodes_unique=19 dup_node_ids=["subject_root__rich-course-cs101"]) — totals drift with un-reset stack state, the dup signal does not; the spec itself is immune because every test truncates + re-seeds first.

Part of #402, closes#395

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • Added end-to-end coverage for graph rendering, node and edge counts, accessibility labels, mastery tiers, and tier filtering.
    • Added validation against database-backed graph data, including a documented known issue for duplicated subject hubs.
  • Accessibility

    • Improved stable identification of graph nodes and activation controls across 2D and 3D views.
  • Documentation

    • Expanded the test ID inventory to cover graph nodes, edges, accessibility elements, and zoom controls.

@coderabbitai

coderabbitaiBot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The graph components now expose stable test IDs and node identifiers across 2D and 3D renderers. A new Playwright suite derives graph expectations from database rows and validates rendered nodes, edges, mastery tiers, labels, counts, and filters.

Changes

Graph integrity validation

Layer / File(s)Summary
Graph test seams
frontend/src/components/KnowledgeGraph2D.tsx, frontend/src/components/KnowledgeGraph3D.tsx, frontend/eslint.config.mjs, docs/frontend-testids.md
Adds shared selectors for accessibility nodes, SVG nodes and edges, node IDs, node circles, activation controls, and zoom buttons; updates lint scope and test-ID documentation.
Database expectation mapping
frontend/e2e/graph.spec.ts
Loads graph data and enrolled courses, then derives expected counts, labels, hub IDs, and mastery-tier mappings.
Graph integrity specifications
frontend/e2e/graph.spec.ts
Checks rendered counts, node uniqueness, labels, opacity, edge coverage, and tier-filter partitioning, with a fixme for known hub duplication.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:jose-gael-cruz-lopez

Sequence Diagram(s)

sequenceDiagram
participant graphSpec as graph.spec.ts
participant database as Graph database
participant treePage as /tree page
participant graphDom as Graph DOM
graphSpec->>database: Load graph nodes, edges, and enrolled courses
graphSpec->>treePage: Navigate to /tree
treePage->>graphDom: Render SVG graph and accessibility node list
graphSpec->>graphDom: Validate IDs, counts, tiers, labels, opacity, and edges
graphSpec->>graphDom: Apply tier filters and verify node partitioning
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly matches the main change: an E2E graph integrity journey against the database for issue #395.
Description check✅ PassedThe description covers the summary, changes, related issue, and testing evidence, though it does not follow the template exactly.
Linked Issues check✅ PassedThe PR meets #395 by asserting graph counts and mastery mapping from DB rows with data-only checks and 10 local runs.
Out of Scope Changes check✅ PassedThe docs, test IDs, db helper, and global setup updates all support the graph integrity journey and are not out of scope.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/395-journey-graph-integrity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 28, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingc308689Commit Preview URL

Branch Preview URL
Jul 28 2026, 04:10 AM

AndresL230and others added 2 commits July 27, 2026 21:06
Loads /tree and asserts the rendered knowledge graph against raw-SQL
reads of graph_nodes/graph_edges/enrollments (new read-only dbQuery seam
in e2e/support/db.ts):
- exact node/edge render counts vs DB (+1 subject-root hub per distinct
enrolled course, +1 hub spoke per course node) — marked test.fixme
pending open bug #355, which duplicates the CS subject root on the
rich seed (two offerings of the same abstract course); the correct
assertion is kept, not relaxed
- every DB concept node renders exactly once, with the mastery class
derived from its DB mastery_score (config.get_mastery_tier thresholds)
encoded at the render layer (2D node-circle opacity)
- the /tree tier filter partitions nodes exactly by the DB-derived
classification
Data assertions only — no force-layout geometry is read.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review findings on PR #433: label-based node identification was a latent
gap (graph_nodes names are only unique PER COURSE, so cross-course name
or truncation-prefix collisions would fail a correct render — and the
label-keyed opacity map could silently mask a wrong class), and bare
structural/ARIA anchoring is not a sanctioned seam.
Fix both by minting graph-surface testids per the doc's process:
- KnowledgeGraph2D/3D a11y list: graph-node-items / graph-node-item
(+ data-node-id per entry) / graph-node-activate
- KnowledgeGraph2D SVG layer: graph-node (+ data-node-id),
graph-node-circle (the opacity-encoding mark), graph-edge,
graph-zoom-in/out/reset
- register all of it in docs/frontend-testids.md (graph surface row now
names the 2D/3D split) and add both files to the eslint enforcement
array (every intrinsic button is tagged — no suppressions needed)
The spec now identifies nodes exclusively by data-node-id and keys the
opacity map by id; rendered label text is still asserted per node, just
keyed by id instead of used as identity.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the test/395-journey-graph-integrity branch from ca9a2e7 to c308689CompareJuly 28, 2026 04:08

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/e2e/graph.spec.ts`:
- Around line 232-235: Replace the count-only assertion in
frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation covering every
expected DB edge and synthesized hub spoke, while preserving the existing
count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b170e05-e5bb-417d-a4f6-fbaa3a2c3c21

📥 Commits

Reviewing files that changed from the base of the PR and between c42717c and c308689.

📒 Files selected for processing (5)
  • docs/frontend-testids.md
  • frontend/e2e/graph.spec.ts
  • frontend/eslint.config.mjs
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/KnowledgeGraph3D.tsx

Comment on lines +232 to +235
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(await svgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Assert expected edge identities, not only a count floor.

Bug #355 currently adds five surplus spokes, so >= g.expectedEdgeCount still passes if up to five expected edges disappear. Expose each SVG line’s source/target IDs, then assert every DB edge and synthesized hub spoke is present; this remains valid while the exact-count test is fixme.

  • frontend/e2e/graph.spec.ts#L232-L235: compare the rendered edge endpoint pairs against all expected DB-edge and hub-spoke pairs.
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459: add stable source and target node-ID data attributes to each graph-edge line.
Proposed seam
 <line
data-testid="graph-edge"
+ data-source-node-id={s.id}+ data-target-node-id={t.id}
x1={s.x}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(awaitsvgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);
<line
key={i}
data-testid="graph-edge"
data-source-node-id={s.id}
data-target-node-id={t.id}
x1={s.x}
y1={s.y}
x2={t.x}
y2={t.y}
stroke="var(--text-muted)"
strokeOpacity={op}
strokeWidth={0.5+(l.strength||0.5)*1.2}
strokeLinecap="round"
/>
📍 Affects 2 files
  • frontend/e2e/graph.spec.ts#L232-L235 (this comment)
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/e2e/graph.spec.ts` around lines 232 - 235, Replace the count-only
assertion in frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation
covering every expected DB edge and synthesized hub spoke, while preserving the
existing count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.

@AndresL230

Copy link
Copy Markdown
CollaboratorAuthor

Code review

No issues found. Checked for bugs and CLAUDE.md compliance. (Panel findings fixed in-branch: the spec now anchors on id-keyed registered testids — graph-node-item + data-node-id on the a11y list, per docs/frontend-testids.md's Adding-a-surface process — which also structurally eliminates the latent label-collision gap; the db.ts helper converged on the merged queryRaw. Re-verified with a 3/3 confirmation cycle on the final content; the original 10/10×2 tally stands as acceptance evidence. The #355 acceptance test ships as test.fixme.)

🤖 Generated with Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(e2e): journey — graph render integrity vs DB

1 participant

@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(e2e): journey — graph render integrity vs DB (#395) - #433

Merged
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity
Jul 28, 2026
Merged

test(e2e): journey — graph render integrity vs DB (#395)#433
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity

Conversation

@AndresL230

@AndresL230AndresL230 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Journey #395: load the graph view (/tree) and assert the RENDERED knowledge graph against the database read over raw SQL — data assertions only, no force-layout geometry (positions are never read).

  • New spec frontend/e2e/graph.spec.ts (3 tests):
    1. Exact render counts vs DB — one rendered node per graph_nodes row plus one subject-root hub per DISTINCT enrolled course; one rendered edge per graph_edges row plus one hub spoke per course-linked node. test.fixme — red solely because of open bug Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 (below); the correct assertion is kept, not relaxed, so this is the ready-made acceptance test for the Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 fix.
    2. Concept-node integrity + mastery classification (passing) — every DB concept node renders exactly once (SVG label and the graph's accessibility list), every enrolled course's hub is present, at least every DB edge/spoke is drawn, and each node's DB mastery_score maps through the canonical thresholds (backend/config.py::get_mastery_tier) to the class the 2D graph encodes as node-circle opacity.
    3. Tier filter partition (passing) — selecting each mastery tier pill on /tree shows exactly the concepts whose DB score maps to that tier, and none other.
  • Additive harness seamfrontend/e2e/support/db.ts::dbQuery, a read-only raw-SQL helper so journeys compute expected values from the DB without hand-rolling pg plumbing (inherits the loopback-only safety guard). No new testids: the spec anchors on the registered graph-container testid plus structural/ARIA handles inside it (svg[aria-label="Knowledge graph"]<text>/<line>, the component's hidden a11y node list) — justification in the spec header; node identity matches on seeded concept names (DB data, not UI copy).
  • Harness convergence — carries the frontend/e2e/global-setup.ts fix from the test(e2e): journey — seeded session → dashboard #386 branch verbatim (storageState now includes the sapling_user localStorage identity; cookie-only state left authed pages on an infinite skeleton).

Bug #355 REPRODUCES on the migration-replayed local schema + rich seed

Probe (authed GET /api/graph/rich-user-active against the freshly seeded stack):

PROBE355 nodes_total=18 nodes_unique=17 dup_node_ids=["subject_root__rich-course-cs101"] edges_total=25 edges_unique=20 dup_edges=5

rich-user-active is enrolled in two offerings of the same abstract course (rich-off-cs101-f25 + rich-off-cs101-s26), and graph_service.get_graph synthesizes one subject_root__{course_id} per enrollment (loop over _user_enrolled_courses) instead of per distinct course — the CS subject root and its five hub spokes are emitted twice, exactly the duplicate-key defect #355 describes. This is the defect class this journey exists to catch: test 1 trips on it and is test.fixme(#355) with the correct assertions intact; tests 2–3 assert everything the duplication does not corrupt.

Verification — 10 consecutive local runs

10/10 green. Each run: 2 passed, 1 skipped — the skip is the test.fixme(#355) exact-count test; the two live tests passed on every run (~28s/run cold, ~11s/run warm). The full cycle was observed green twice back-to-back (20/20 runs total across both cycles).

Run12345678910
Resultpasspasspasspasspasspasspasspasspasspass

Cycle run under the shared stack lock: make e2e-up → probe → 10× npx playwright test e2e/graph.spec.tsmake e2e-down. A second probe on a volume carrying sibling-run residue reported the SAME single duplicate id (nodes_total=20 nodes_unique=19 dup_node_ids=["subject_root__rich-course-cs101"]) — totals drift with un-reset stack state, the dup signal does not; the spec itself is immune because every test truncates + re-seeds first.

Part of #402, closes#395

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • Added end-to-end coverage for graph rendering, node and edge counts, accessibility labels, mastery tiers, and tier filtering.
    • Added validation against database-backed graph data, including a documented known issue for duplicated subject hubs.
  • Accessibility

    • Improved stable identification of graph nodes and activation controls across 2D and 3D views.
  • Documentation

    • Expanded the test ID inventory to cover graph nodes, edges, accessibility elements, and zoom controls.

@coderabbitai

coderabbitaiBot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The graph components now expose stable test IDs and node identifiers across 2D and 3D renderers. A new Playwright suite derives graph expectations from database rows and validates rendered nodes, edges, mastery tiers, labels, counts, and filters.

Changes

Graph integrity validation

Layer / File(s)Summary
Graph test seams
frontend/src/components/KnowledgeGraph2D.tsx, frontend/src/components/KnowledgeGraph3D.tsx, frontend/eslint.config.mjs, docs/frontend-testids.md
Adds shared selectors for accessibility nodes, SVG nodes and edges, node IDs, node circles, activation controls, and zoom buttons; updates lint scope and test-ID documentation.
Database expectation mapping
frontend/e2e/graph.spec.ts
Loads graph data and enrolled courses, then derives expected counts, labels, hub IDs, and mastery-tier mappings.
Graph integrity specifications
frontend/e2e/graph.spec.ts
Checks rendered counts, node uniqueness, labels, opacity, edge coverage, and tier-filter partitioning, with a fixme for known hub duplication.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:jose-gael-cruz-lopez

Sequence Diagram(s)

sequenceDiagram
participant graphSpec as graph.spec.ts
participant database as Graph database
participant treePage as /tree page
participant graphDom as Graph DOM
graphSpec->>database: Load graph nodes, edges, and enrolled courses
graphSpec->>treePage: Navigate to /tree
treePage->>graphDom: Render SVG graph and accessibility node list
graphSpec->>graphDom: Validate IDs, counts, tiers, labels, opacity, and edges
graphSpec->>graphDom: Apply tier filters and verify node partitioning
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly matches the main change: an E2E graph integrity journey against the database for issue #395.
Description check✅ PassedThe description covers the summary, changes, related issue, and testing evidence, though it does not follow the template exactly.
Linked Issues check✅ PassedThe PR meets #395 by asserting graph counts and mastery mapping from DB rows with data-only checks and 10 local runs.
Out of Scope Changes check✅ PassedThe docs, test IDs, db helper, and global setup updates all support the graph integrity journey and are not out of scope.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/395-journey-graph-integrity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 28, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingc308689Commit Preview URL

Branch Preview URL
Jul 28 2026, 04:10 AM

AndresL230and others added 2 commits July 27, 2026 21:06
Loads /tree and asserts the rendered knowledge graph against raw-SQL
reads of graph_nodes/graph_edges/enrollments (new read-only dbQuery seam
in e2e/support/db.ts):
- exact node/edge render counts vs DB (+1 subject-root hub per distinct
enrolled course, +1 hub spoke per course node) — marked test.fixme
pending open bug #355, which duplicates the CS subject root on the
rich seed (two offerings of the same abstract course); the correct
assertion is kept, not relaxed
- every DB concept node renders exactly once, with the mastery class
derived from its DB mastery_score (config.get_mastery_tier thresholds)
encoded at the render layer (2D node-circle opacity)
- the /tree tier filter partitions nodes exactly by the DB-derived
classification
Data assertions only — no force-layout geometry is read.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review findings on PR #433: label-based node identification was a latent
gap (graph_nodes names are only unique PER COURSE, so cross-course name
or truncation-prefix collisions would fail a correct render — and the
label-keyed opacity map could silently mask a wrong class), and bare
structural/ARIA anchoring is not a sanctioned seam.
Fix both by minting graph-surface testids per the doc's process:
- KnowledgeGraph2D/3D a11y list: graph-node-items / graph-node-item
(+ data-node-id per entry) / graph-node-activate
- KnowledgeGraph2D SVG layer: graph-node (+ data-node-id),
graph-node-circle (the opacity-encoding mark), graph-edge,
graph-zoom-in/out/reset
- register all of it in docs/frontend-testids.md (graph surface row now
names the 2D/3D split) and add both files to the eslint enforcement
array (every intrinsic button is tagged — no suppressions needed)
The spec now identifies nodes exclusively by data-node-id and keys the
opacity map by id; rendered label text is still asserted per node, just
keyed by id instead of used as identity.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the test/395-journey-graph-integrity branch from ca9a2e7 to c308689CompareJuly 28, 2026 04:08

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/e2e/graph.spec.ts`:
- Around line 232-235: Replace the count-only assertion in
frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation covering every
expected DB edge and synthesized hub spoke, while preserving the existing
count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b170e05-e5bb-417d-a4f6-fbaa3a2c3c21

📥 Commits

Reviewing files that changed from the base of the PR and between c42717c and c308689.

📒 Files selected for processing (5)
  • docs/frontend-testids.md
  • frontend/e2e/graph.spec.ts
  • frontend/eslint.config.mjs
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/KnowledgeGraph3D.tsx

Comment on lines +232 to +235
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(await svgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Assert expected edge identities, not only a count floor.

Bug #355 currently adds five surplus spokes, so >= g.expectedEdgeCount still passes if up to five expected edges disappear. Expose each SVG line’s source/target IDs, then assert every DB edge and synthesized hub spoke is present; this remains valid while the exact-count test is fixme.

  • frontend/e2e/graph.spec.ts#L232-L235: compare the rendered edge endpoint pairs against all expected DB-edge and hub-spoke pairs.
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459: add stable source and target node-ID data attributes to each graph-edge line.
Proposed seam
 <line
data-testid="graph-edge"
+ data-source-node-id={s.id}+ data-target-node-id={t.id}
x1={s.x}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(awaitsvgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);
<line
key={i}
data-testid="graph-edge"
data-source-node-id={s.id}
data-target-node-id={t.id}
x1={s.x}
y1={s.y}
x2={t.x}
y2={t.y}
stroke="var(--text-muted)"
strokeOpacity={op}
strokeWidth={0.5+(l.strength||0.5)*1.2}
strokeLinecap="round"
/>
📍 Affects 2 files
  • frontend/e2e/graph.spec.ts#L232-L235 (this comment)
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/e2e/graph.spec.ts` around lines 232 - 235, Replace the count-only
assertion in frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation
covering every expected DB edge and synthesized hub spoke, while preserving the
existing count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.

@AndresL230

Copy link
Copy Markdown
CollaboratorAuthor

Code review

No issues found. Checked for bugs and CLAUDE.md compliance. (Panel findings fixed in-branch: the spec now anchors on id-keyed registered testids — graph-node-item + data-node-id on the a11y list, per docs/frontend-testids.md's Adding-a-surface process — which also structurally eliminates the latent label-collision gap; the db.ts helper converged on the merged queryRaw. Re-verified with a 3/3 confirmation cycle on the final content; the original 10/10×2 tally stands as acceptance evidence. The #355 acceptance test ships as test.fixme.)

🤖 Generated with Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(e2e): journey — graph render integrity vs DB

1 participant

@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(e2e): journey — graph render integrity vs DB (#395) - #433

Merged
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity
Jul 28, 2026
Merged

test(e2e): journey — graph render integrity vs DB (#395)#433
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity

Conversation

@AndresL230

@AndresL230AndresL230 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Journey #395: load the graph view (/tree) and assert the RENDERED knowledge graph against the database read over raw SQL — data assertions only, no force-layout geometry (positions are never read).

  • New spec frontend/e2e/graph.spec.ts (3 tests):
    1. Exact render counts vs DB — one rendered node per graph_nodes row plus one subject-root hub per DISTINCT enrolled course; one rendered edge per graph_edges row plus one hub spoke per course-linked node. test.fixme — red solely because of open bug Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 (below); the correct assertion is kept, not relaxed, so this is the ready-made acceptance test for the Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 fix.
    2. Concept-node integrity + mastery classification (passing) — every DB concept node renders exactly once (SVG label and the graph's accessibility list), every enrolled course's hub is present, at least every DB edge/spoke is drawn, and each node's DB mastery_score maps through the canonical thresholds (backend/config.py::get_mastery_tier) to the class the 2D graph encodes as node-circle opacity.
    3. Tier filter partition (passing) — selecting each mastery tier pill on /tree shows exactly the concepts whose DB score maps to that tier, and none other.
  • Additive harness seamfrontend/e2e/support/db.ts::dbQuery, a read-only raw-SQL helper so journeys compute expected values from the DB without hand-rolling pg plumbing (inherits the loopback-only safety guard). No new testids: the spec anchors on the registered graph-container testid plus structural/ARIA handles inside it (svg[aria-label="Knowledge graph"]<text>/<line>, the component's hidden a11y node list) — justification in the spec header; node identity matches on seeded concept names (DB data, not UI copy).
  • Harness convergence — carries the frontend/e2e/global-setup.ts fix from the test(e2e): journey — seeded session → dashboard #386 branch verbatim (storageState now includes the sapling_user localStorage identity; cookie-only state left authed pages on an infinite skeleton).

Bug #355 REPRODUCES on the migration-replayed local schema + rich seed

Probe (authed GET /api/graph/rich-user-active against the freshly seeded stack):

PROBE355 nodes_total=18 nodes_unique=17 dup_node_ids=["subject_root__rich-course-cs101"] edges_total=25 edges_unique=20 dup_edges=5

rich-user-active is enrolled in two offerings of the same abstract course (rich-off-cs101-f25 + rich-off-cs101-s26), and graph_service.get_graph synthesizes one subject_root__{course_id} per enrollment (loop over _user_enrolled_courses) instead of per distinct course — the CS subject root and its five hub spokes are emitted twice, exactly the duplicate-key defect #355 describes. This is the defect class this journey exists to catch: test 1 trips on it and is test.fixme(#355) with the correct assertions intact; tests 2–3 assert everything the duplication does not corrupt.

Verification — 10 consecutive local runs

10/10 green. Each run: 2 passed, 1 skipped — the skip is the test.fixme(#355) exact-count test; the two live tests passed on every run (~28s/run cold, ~11s/run warm). The full cycle was observed green twice back-to-back (20/20 runs total across both cycles).

Run12345678910
Resultpasspasspasspasspasspasspasspasspasspass

Cycle run under the shared stack lock: make e2e-up → probe → 10× npx playwright test e2e/graph.spec.tsmake e2e-down. A second probe on a volume carrying sibling-run residue reported the SAME single duplicate id (nodes_total=20 nodes_unique=19 dup_node_ids=["subject_root__rich-course-cs101"]) — totals drift with un-reset stack state, the dup signal does not; the spec itself is immune because every test truncates + re-seeds first.

Part of #402, closes#395

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • Added end-to-end coverage for graph rendering, node and edge counts, accessibility labels, mastery tiers, and tier filtering.
    • Added validation against database-backed graph data, including a documented known issue for duplicated subject hubs.
  • Accessibility

    • Improved stable identification of graph nodes and activation controls across 2D and 3D views.
  • Documentation

    • Expanded the test ID inventory to cover graph nodes, edges, accessibility elements, and zoom controls.

@coderabbitai

coderabbitaiBot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The graph components now expose stable test IDs and node identifiers across 2D and 3D renderers. A new Playwright suite derives graph expectations from database rows and validates rendered nodes, edges, mastery tiers, labels, counts, and filters.

Changes

Graph integrity validation

Layer / File(s)Summary
Graph test seams
frontend/src/components/KnowledgeGraph2D.tsx, frontend/src/components/KnowledgeGraph3D.tsx, frontend/eslint.config.mjs, docs/frontend-testids.md
Adds shared selectors for accessibility nodes, SVG nodes and edges, node IDs, node circles, activation controls, and zoom buttons; updates lint scope and test-ID documentation.
Database expectation mapping
frontend/e2e/graph.spec.ts
Loads graph data and enrolled courses, then derives expected counts, labels, hub IDs, and mastery-tier mappings.
Graph integrity specifications
frontend/e2e/graph.spec.ts
Checks rendered counts, node uniqueness, labels, opacity, edge coverage, and tier-filter partitioning, with a fixme for known hub duplication.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:jose-gael-cruz-lopez

Sequence Diagram(s)

sequenceDiagram
participant graphSpec as graph.spec.ts
participant database as Graph database
participant treePage as /tree page
participant graphDom as Graph DOM
graphSpec->>database: Load graph nodes, edges, and enrolled courses
graphSpec->>treePage: Navigate to /tree
treePage->>graphDom: Render SVG graph and accessibility node list
graphSpec->>graphDom: Validate IDs, counts, tiers, labels, opacity, and edges
graphSpec->>graphDom: Apply tier filters and verify node partitioning
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly matches the main change: an E2E graph integrity journey against the database for issue #395.
Description check✅ PassedThe description covers the summary, changes, related issue, and testing evidence, though it does not follow the template exactly.
Linked Issues check✅ PassedThe PR meets #395 by asserting graph counts and mastery mapping from DB rows with data-only checks and 10 local runs.
Out of Scope Changes check✅ PassedThe docs, test IDs, db helper, and global setup updates all support the graph integrity journey and are not out of scope.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/395-journey-graph-integrity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 28, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingc308689Commit Preview URL

Branch Preview URL
Jul 28 2026, 04:10 AM

AndresL230and others added 2 commits July 27, 2026 21:06
Loads /tree and asserts the rendered knowledge graph against raw-SQL
reads of graph_nodes/graph_edges/enrollments (new read-only dbQuery seam
in e2e/support/db.ts):
- exact node/edge render counts vs DB (+1 subject-root hub per distinct
enrolled course, +1 hub spoke per course node) — marked test.fixme
pending open bug #355, which duplicates the CS subject root on the
rich seed (two offerings of the same abstract course); the correct
assertion is kept, not relaxed
- every DB concept node renders exactly once, with the mastery class
derived from its DB mastery_score (config.get_mastery_tier thresholds)
encoded at the render layer (2D node-circle opacity)
- the /tree tier filter partitions nodes exactly by the DB-derived
classification
Data assertions only — no force-layout geometry is read.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review findings on PR #433: label-based node identification was a latent
gap (graph_nodes names are only unique PER COURSE, so cross-course name
or truncation-prefix collisions would fail a correct render — and the
label-keyed opacity map could silently mask a wrong class), and bare
structural/ARIA anchoring is not a sanctioned seam.
Fix both by minting graph-surface testids per the doc's process:
- KnowledgeGraph2D/3D a11y list: graph-node-items / graph-node-item
(+ data-node-id per entry) / graph-node-activate
- KnowledgeGraph2D SVG layer: graph-node (+ data-node-id),
graph-node-circle (the opacity-encoding mark), graph-edge,
graph-zoom-in/out/reset
- register all of it in docs/frontend-testids.md (graph surface row now
names the 2D/3D split) and add both files to the eslint enforcement
array (every intrinsic button is tagged — no suppressions needed)
The spec now identifies nodes exclusively by data-node-id and keys the
opacity map by id; rendered label text is still asserted per node, just
keyed by id instead of used as identity.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the test/395-journey-graph-integrity branch from ca9a2e7 to c308689CompareJuly 28, 2026 04:08

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/e2e/graph.spec.ts`:
- Around line 232-235: Replace the count-only assertion in
frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation covering every
expected DB edge and synthesized hub spoke, while preserving the existing
count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b170e05-e5bb-417d-a4f6-fbaa3a2c3c21

📥 Commits

Reviewing files that changed from the base of the PR and between c42717c and c308689.

📒 Files selected for processing (5)
  • docs/frontend-testids.md
  • frontend/e2e/graph.spec.ts
  • frontend/eslint.config.mjs
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/KnowledgeGraph3D.tsx

Comment on lines +232 to +235
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(await svgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Assert expected edge identities, not only a count floor.

Bug #355 currently adds five surplus spokes, so >= g.expectedEdgeCount still passes if up to five expected edges disappear. Expose each SVG line’s source/target IDs, then assert every DB edge and synthesized hub spoke is present; this remains valid while the exact-count test is fixme.

  • frontend/e2e/graph.spec.ts#L232-L235: compare the rendered edge endpoint pairs against all expected DB-edge and hub-spoke pairs.
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459: add stable source and target node-ID data attributes to each graph-edge line.
Proposed seam
 <line
data-testid="graph-edge"
+ data-source-node-id={s.id}+ data-target-node-id={t.id}
x1={s.x}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(awaitsvgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);
<line
key={i}
data-testid="graph-edge"
data-source-node-id={s.id}
data-target-node-id={t.id}
x1={s.x}
y1={s.y}
x2={t.x}
y2={t.y}
stroke="var(--text-muted)"
strokeOpacity={op}
strokeWidth={0.5+(l.strength||0.5)*1.2}
strokeLinecap="round"
/>
📍 Affects 2 files
  • frontend/e2e/graph.spec.ts#L232-L235 (this comment)
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/e2e/graph.spec.ts` around lines 232 - 235, Replace the count-only
assertion in frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation
covering every expected DB edge and synthesized hub spoke, while preserving the
existing count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.

@AndresL230

Copy link
Copy Markdown
CollaboratorAuthor

Code review

No issues found. Checked for bugs and CLAUDE.md compliance. (Panel findings fixed in-branch: the spec now anchors on id-keyed registered testids — graph-node-item + data-node-id on the a11y list, per docs/frontend-testids.md's Adding-a-surface process — which also structurally eliminates the latent label-collision gap; the db.ts helper converged on the merged queryRaw. Re-verified with a 3/3 confirmation cycle on the final content; the original 10/10×2 tally stands as acceptance evidence. The #355 acceptance test ships as test.fixme.)

🤖 Generated with Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(e2e): journey — graph render integrity vs DB

1 participant

@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

test(e2e): journey — graph render integrity vs DB (#395) - #433

Merged
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity
Jul 28, 2026
Merged

test(e2e): journey — graph render integrity vs DB (#395)#433
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity

Conversation

@AndresL230

@AndresL230AndresL230 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Journey #395: load the graph view (/tree) and assert the RENDERED knowledge graph against the database read over raw SQL — data assertions only, no force-layout geometry (positions are never read).

  • New spec frontend/e2e/graph.spec.ts (3 tests):
    1. Exact render counts vs DB — one rendered node per graph_nodes row plus one subject-root hub per DISTINCT enrolled course; one rendered edge per graph_edges row plus one hub spoke per course-linked node. test.fixme — red solely because of open bug Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 (below); the correct assertion is kept, not relaxed, so this is the ready-made acceptance test for the Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 fix.
    2. Concept-node integrity + mastery classification (passing) — every DB concept node renders exactly once (SVG label and the graph's accessibility list), every enrolled course's hub is present, at least every DB edge/spoke is drawn, and each node's DB mastery_score maps through the canonical thresholds (backend/config.py::get_mastery_tier) to the class the 2D graph encodes as node-circle opacity.
    3. Tier filter partition (passing) — selecting each mastery tier pill on /tree shows exactly the concepts whose DB score maps to that tier, and none other.
  • Additive harness seamfrontend/e2e/support/db.ts::dbQuery, a read-only raw-SQL helper so journeys compute expected values from the DB without hand-rolling pg plumbing (inherits the loopback-only safety guard). No new testids: the spec anchors on the registered graph-container testid plus structural/ARIA handles inside it (svg[aria-label="Knowledge graph"]<text>/<line>, the component's hidden a11y node list) — justification in the spec header; node identity matches on seeded concept names (DB data, not UI copy).
  • Harness convergence — carries the frontend/e2e/global-setup.ts fix from the test(e2e): journey — seeded session → dashboard #386 branch verbatim (storageState now includes the sapling_user localStorage identity; cookie-only state left authed pages on an infinite skeleton).

Bug #355 REPRODUCES on the migration-replayed local schema + rich seed

Probe (authed GET /api/graph/rich-user-active against the freshly seeded stack):

PROBE355 nodes_total=18 nodes_unique=17 dup_node_ids=["subject_root__rich-course-cs101"] edges_total=25 edges_unique=20 dup_edges=5

rich-user-active is enrolled in two offerings of the same abstract course (rich-off-cs101-f25 + rich-off-cs101-s26), and graph_service.get_graph synthesizes one subject_root__{course_id} per enrollment (loop over _user_enrolled_courses) instead of per distinct course — the CS subject root and its five hub spokes are emitted twice, exactly the duplicate-key defect #355 describes. This is the defect class this journey exists to catch: test 1 trips on it and is test.fixme(#355) with the correct assertions intact; tests 2–3 assert everything the duplication does not corrupt.

Verification — 10 consecutive local runs

10/10 green. Each run: 2 passed, 1 skipped — the skip is the test.fixme(#355) exact-count test; the two live tests passed on every run (~28s/run cold, ~11s/run warm). The full cycle was observed green twice back-to-back (20/20 runs total across both cycles).

Run12345678910
Resultpasspasspasspasspasspasspasspasspasspass

Cycle run under the shared stack lock: make e2e-up → probe → 10× npx playwright test e2e/graph.spec.tsmake e2e-down. A second probe on a volume carrying sibling-run residue reported the SAME single duplicate id (nodes_total=20 nodes_unique=19 dup_node_ids=["subject_root__rich-course-cs101"]) — totals drift with un-reset stack state, the dup signal does not; the spec itself is immune because every test truncates + re-seeds first.

Part of #402, closes#395

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • Added end-to-end coverage for graph rendering, node and edge counts, accessibility labels, mastery tiers, and tier filtering.
    • Added validation against database-backed graph data, including a documented known issue for duplicated subject hubs.
  • Accessibility

    • Improved stable identification of graph nodes and activation controls across 2D and 3D views.
  • Documentation

    • Expanded the test ID inventory to cover graph nodes, edges, accessibility elements, and zoom controls.

@coderabbitai

coderabbitaiBot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The graph components now expose stable test IDs and node identifiers across 2D and 3D renderers. A new Playwright suite derives graph expectations from database rows and validates rendered nodes, edges, mastery tiers, labels, counts, and filters.

Changes

Graph integrity validation

Layer / File(s)Summary
Graph test seams
frontend/src/components/KnowledgeGraph2D.tsx, frontend/src/components/KnowledgeGraph3D.tsx, frontend/eslint.config.mjs, docs/frontend-testids.md
Adds shared selectors for accessibility nodes, SVG nodes and edges, node IDs, node circles, activation controls, and zoom buttons; updates lint scope and test-ID documentation.
Database expectation mapping
frontend/e2e/graph.spec.ts
Loads graph data and enrolled courses, then derives expected counts, labels, hub IDs, and mastery-tier mappings.
Graph integrity specifications
frontend/e2e/graph.spec.ts
Checks rendered counts, node uniqueness, labels, opacity, edge coverage, and tier-filter partitioning, with a fixme for known hub duplication.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:jose-gael-cruz-lopez

Sequence Diagram(s)

sequenceDiagram
participant graphSpec as graph.spec.ts
participant database as Graph database
participant treePage as /tree page
participant graphDom as Graph DOM
graphSpec->>database: Load graph nodes, edges, and enrolled courses
graphSpec->>treePage: Navigate to /tree
treePage->>graphDom: Render SVG graph and accessibility node list
graphSpec->>graphDom: Validate IDs, counts, tiers, labels, opacity, and edges
graphSpec->>graphDom: Apply tier filters and verify node partitioning
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly matches the main change: an E2E graph integrity journey against the database for issue #395.
Description check✅ PassedThe description covers the summary, changes, related issue, and testing evidence, though it does not follow the template exactly.
Linked Issues check✅ PassedThe PR meets #395 by asserting graph counts and mastery mapping from DB rows with data-only checks and 10 local runs.
Out of Scope Changes check✅ PassedThe docs, test IDs, db helper, and global setup updates all support the graph integrity journey and are not out of scope.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/395-journey-graph-integrity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 28, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingc308689Commit Preview URL

Branch Preview URL
Jul 28 2026, 04:10 AM

AndresL230and others added 2 commits July 27, 2026 21:06
Loads /tree and asserts the rendered knowledge graph against raw-SQL
reads of graph_nodes/graph_edges/enrollments (new read-only dbQuery seam
in e2e/support/db.ts):
- exact node/edge render counts vs DB (+1 subject-root hub per distinct
enrolled course, +1 hub spoke per course node) — marked test.fixme
pending open bug #355, which duplicates the CS subject root on the
rich seed (two offerings of the same abstract course); the correct
assertion is kept, not relaxed
- every DB concept node renders exactly once, with the mastery class
derived from its DB mastery_score (config.get_mastery_tier thresholds)
encoded at the render layer (2D node-circle opacity)
- the /tree tier filter partitions nodes exactly by the DB-derived
classification
Data assertions only — no force-layout geometry is read.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review findings on PR #433: label-based node identification was a latent
gap (graph_nodes names are only unique PER COURSE, so cross-course name
or truncation-prefix collisions would fail a correct render — and the
label-keyed opacity map could silently mask a wrong class), and bare
structural/ARIA anchoring is not a sanctioned seam.
Fix both by minting graph-surface testids per the doc's process:
- KnowledgeGraph2D/3D a11y list: graph-node-items / graph-node-item
(+ data-node-id per entry) / graph-node-activate
- KnowledgeGraph2D SVG layer: graph-node (+ data-node-id),
graph-node-circle (the opacity-encoding mark), graph-edge,
graph-zoom-in/out/reset
- register all of it in docs/frontend-testids.md (graph surface row now
names the 2D/3D split) and add both files to the eslint enforcement
array (every intrinsic button is tagged — no suppressions needed)
The spec now identifies nodes exclusively by data-node-id and keys the
opacity map by id; rendered label text is still asserted per node, just
keyed by id instead of used as identity.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the test/395-journey-graph-integrity branch from ca9a2e7 to c308689CompareJuly 28, 2026 04:08

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/e2e/graph.spec.ts`:
- Around line 232-235: Replace the count-only assertion in
frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation covering every
expected DB edge and synthesized hub spoke, while preserving the existing
count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b170e05-e5bb-417d-a4f6-fbaa3a2c3c21

📥 Commits

Reviewing files that changed from the base of the PR and between c42717c and c308689.

📒 Files selected for processing (5)
  • docs/frontend-testids.md
  • frontend/e2e/graph.spec.ts
  • frontend/eslint.config.mjs
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/KnowledgeGraph3D.tsx

Comment on lines +232 to +235
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(await svgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Assert expected edge identities, not only a count floor.

Bug #355 currently adds five surplus spokes, so >= g.expectedEdgeCount still passes if up to five expected edges disappear. Expose each SVG line’s source/target IDs, then assert every DB edge and synthesized hub spoke is present; this remains valid while the exact-count test is fixme.

  • frontend/e2e/graph.spec.ts#L232-L235: compare the rendered edge endpoint pairs against all expected DB-edge and hub-spoke pairs.
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459: add stable source and target node-ID data attributes to each graph-edge line.
Proposed seam
 <line
data-testid="graph-edge"
+ data-source-node-id={s.id}+ data-target-node-id={t.id}
x1={s.x}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(awaitsvgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);
<line
key={i}
data-testid="graph-edge"
data-source-node-id={s.id}
data-target-node-id={t.id}
x1={s.x}
y1={s.y}
x2={t.x}
y2={t.y}
stroke="var(--text-muted)"
strokeOpacity={op}
strokeWidth={0.5+(l.strength||0.5)*1.2}
strokeLinecap="round"
/>
📍 Affects 2 files
  • frontend/e2e/graph.spec.ts#L232-L235 (this comment)
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/e2e/graph.spec.ts` around lines 232 - 235, Replace the count-only
assertion in frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation
covering every expected DB edge and synthesized hub spoke, while preserving the
existing count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.

@AndresL230

Copy link
Copy Markdown
CollaboratorAuthor

Code review

No issues found. Checked for bugs and CLAUDE.md compliance. (Panel findings fixed in-branch: the spec now anchors on id-keyed registered testids — graph-node-item + data-node-id on the a11y list, per docs/frontend-testids.md's Adding-a-surface process — which also structurally eliminates the latent label-collision gap; the db.ts helper converged on the merged queryRaw. Re-verified with a 3/3 confirmation cycle on the final content; the original 10/10×2 tally stands as acceptance evidence. The #355 acceptance test ships as test.fixme.)

🤖 Generated with Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(e2e): journey — graph render integrity vs DB

1 participant

@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(e2e): journey — graph render integrity vs DB (#395) - #433

Merged
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity
Jul 28, 2026
Merged

test(e2e): journey — graph render integrity vs DB (#395)#433
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity

Conversation

@AndresL230

@AndresL230AndresL230 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Journey #395: load the graph view (/tree) and assert the RENDERED knowledge graph against the database read over raw SQL — data assertions only, no force-layout geometry (positions are never read).

  • New spec frontend/e2e/graph.spec.ts (3 tests):
    1. Exact render counts vs DB — one rendered node per graph_nodes row plus one subject-root hub per DISTINCT enrolled course; one rendered edge per graph_edges row plus one hub spoke per course-linked node. test.fixme — red solely because of open bug Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 (below); the correct assertion is kept, not relaxed, so this is the ready-made acceptance test for the Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 fix.
    2. Concept-node integrity + mastery classification (passing) — every DB concept node renders exactly once (SVG label and the graph's accessibility list), every enrolled course's hub is present, at least every DB edge/spoke is drawn, and each node's DB mastery_score maps through the canonical thresholds (backend/config.py::get_mastery_tier) to the class the 2D graph encodes as node-circle opacity.
    3. Tier filter partition (passing) — selecting each mastery tier pill on /tree shows exactly the concepts whose DB score maps to that tier, and none other.
  • Additive harness seamfrontend/e2e/support/db.ts::dbQuery, a read-only raw-SQL helper so journeys compute expected values from the DB without hand-rolling pg plumbing (inherits the loopback-only safety guard). No new testids: the spec anchors on the registered graph-container testid plus structural/ARIA handles inside it (svg[aria-label="Knowledge graph"]<text>/<line>, the component's hidden a11y node list) — justification in the spec header; node identity matches on seeded concept names (DB data, not UI copy).
  • Harness convergence — carries the frontend/e2e/global-setup.ts fix from the test(e2e): journey — seeded session → dashboard #386 branch verbatim (storageState now includes the sapling_user localStorage identity; cookie-only state left authed pages on an infinite skeleton).

Bug #355 REPRODUCES on the migration-replayed local schema + rich seed

Probe (authed GET /api/graph/rich-user-active against the freshly seeded stack):

PROBE355 nodes_total=18 nodes_unique=17 dup_node_ids=["subject_root__rich-course-cs101"] edges_total=25 edges_unique=20 dup_edges=5

rich-user-active is enrolled in two offerings of the same abstract course (rich-off-cs101-f25 + rich-off-cs101-s26), and graph_service.get_graph synthesizes one subject_root__{course_id} per enrollment (loop over _user_enrolled_courses) instead of per distinct course — the CS subject root and its five hub spokes are emitted twice, exactly the duplicate-key defect #355 describes. This is the defect class this journey exists to catch: test 1 trips on it and is test.fixme(#355) with the correct assertions intact; tests 2–3 assert everything the duplication does not corrupt.

Verification — 10 consecutive local runs

10/10 green. Each run: 2 passed, 1 skipped — the skip is the test.fixme(#355) exact-count test; the two live tests passed on every run (~28s/run cold, ~11s/run warm). The full cycle was observed green twice back-to-back (20/20 runs total across both cycles).

Run12345678910
Resultpasspasspasspasspasspasspasspasspasspass

Cycle run under the shared stack lock: make e2e-up → probe → 10× npx playwright test e2e/graph.spec.tsmake e2e-down. A second probe on a volume carrying sibling-run residue reported the SAME single duplicate id (nodes_total=20 nodes_unique=19 dup_node_ids=["subject_root__rich-course-cs101"]) — totals drift with un-reset stack state, the dup signal does not; the spec itself is immune because every test truncates + re-seeds first.

Part of #402, closes#395

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • Added end-to-end coverage for graph rendering, node and edge counts, accessibility labels, mastery tiers, and tier filtering.
    • Added validation against database-backed graph data, including a documented known issue for duplicated subject hubs.
  • Accessibility

    • Improved stable identification of graph nodes and activation controls across 2D and 3D views.
  • Documentation

    • Expanded the test ID inventory to cover graph nodes, edges, accessibility elements, and zoom controls.

@coderabbitai

coderabbitaiBot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The graph components now expose stable test IDs and node identifiers across 2D and 3D renderers. A new Playwright suite derives graph expectations from database rows and validates rendered nodes, edges, mastery tiers, labels, counts, and filters.

Changes

Graph integrity validation

Layer / File(s)Summary
Graph test seams
frontend/src/components/KnowledgeGraph2D.tsx, frontend/src/components/KnowledgeGraph3D.tsx, frontend/eslint.config.mjs, docs/frontend-testids.md
Adds shared selectors for accessibility nodes, SVG nodes and edges, node IDs, node circles, activation controls, and zoom buttons; updates lint scope and test-ID documentation.
Database expectation mapping
frontend/e2e/graph.spec.ts
Loads graph data and enrolled courses, then derives expected counts, labels, hub IDs, and mastery-tier mappings.
Graph integrity specifications
frontend/e2e/graph.spec.ts
Checks rendered counts, node uniqueness, labels, opacity, edge coverage, and tier-filter partitioning, with a fixme for known hub duplication.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:jose-gael-cruz-lopez

Sequence Diagram(s)

sequenceDiagram
participant graphSpec as graph.spec.ts
participant database as Graph database
participant treePage as /tree page
participant graphDom as Graph DOM
graphSpec->>database: Load graph nodes, edges, and enrolled courses
graphSpec->>treePage: Navigate to /tree
treePage->>graphDom: Render SVG graph and accessibility node list
graphSpec->>graphDom: Validate IDs, counts, tiers, labels, opacity, and edges
graphSpec->>graphDom: Apply tier filters and verify node partitioning
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly matches the main change: an E2E graph integrity journey against the database for issue #395.
Description check✅ PassedThe description covers the summary, changes, related issue, and testing evidence, though it does not follow the template exactly.
Linked Issues check✅ PassedThe PR meets #395 by asserting graph counts and mastery mapping from DB rows with data-only checks and 10 local runs.
Out of Scope Changes check✅ PassedThe docs, test IDs, db helper, and global setup updates all support the graph integrity journey and are not out of scope.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/395-journey-graph-integrity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 28, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingc308689Commit Preview URL

Branch Preview URL
Jul 28 2026, 04:10 AM

AndresL230and others added 2 commits July 27, 2026 21:06
Loads /tree and asserts the rendered knowledge graph against raw-SQL
reads of graph_nodes/graph_edges/enrollments (new read-only dbQuery seam
in e2e/support/db.ts):
- exact node/edge render counts vs DB (+1 subject-root hub per distinct
enrolled course, +1 hub spoke per course node) — marked test.fixme
pending open bug #355, which duplicates the CS subject root on the
rich seed (two offerings of the same abstract course); the correct
assertion is kept, not relaxed
- every DB concept node renders exactly once, with the mastery class
derived from its DB mastery_score (config.get_mastery_tier thresholds)
encoded at the render layer (2D node-circle opacity)
- the /tree tier filter partitions nodes exactly by the DB-derived
classification
Data assertions only — no force-layout geometry is read.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review findings on PR #433: label-based node identification was a latent
gap (graph_nodes names are only unique PER COURSE, so cross-course name
or truncation-prefix collisions would fail a correct render — and the
label-keyed opacity map could silently mask a wrong class), and bare
structural/ARIA anchoring is not a sanctioned seam.
Fix both by minting graph-surface testids per the doc's process:
- KnowledgeGraph2D/3D a11y list: graph-node-items / graph-node-item
(+ data-node-id per entry) / graph-node-activate
- KnowledgeGraph2D SVG layer: graph-node (+ data-node-id),
graph-node-circle (the opacity-encoding mark), graph-edge,
graph-zoom-in/out/reset
- register all of it in docs/frontend-testids.md (graph surface row now
names the 2D/3D split) and add both files to the eslint enforcement
array (every intrinsic button is tagged — no suppressions needed)
The spec now identifies nodes exclusively by data-node-id and keys the
opacity map by id; rendered label text is still asserted per node, just
keyed by id instead of used as identity.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the test/395-journey-graph-integrity branch from ca9a2e7 to c308689CompareJuly 28, 2026 04:08

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/e2e/graph.spec.ts`:
- Around line 232-235: Replace the count-only assertion in
frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation covering every
expected DB edge and synthesized hub spoke, while preserving the existing
count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b170e05-e5bb-417d-a4f6-fbaa3a2c3c21

📥 Commits

Reviewing files that changed from the base of the PR and between c42717c and c308689.

📒 Files selected for processing (5)
  • docs/frontend-testids.md
  • frontend/e2e/graph.spec.ts
  • frontend/eslint.config.mjs
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/KnowledgeGraph3D.tsx

Comment on lines +232 to +235
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(await svgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Assert expected edge identities, not only a count floor.

Bug #355 currently adds five surplus spokes, so >= g.expectedEdgeCount still passes if up to five expected edges disappear. Expose each SVG line’s source/target IDs, then assert every DB edge and synthesized hub spoke is present; this remains valid while the exact-count test is fixme.

  • frontend/e2e/graph.spec.ts#L232-L235: compare the rendered edge endpoint pairs against all expected DB-edge and hub-spoke pairs.
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459: add stable source and target node-ID data attributes to each graph-edge line.
Proposed seam
 <line
data-testid="graph-edge"
+ data-source-node-id={s.id}+ data-target-node-id={t.id}
x1={s.x}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(awaitsvgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);
<line
key={i}
data-testid="graph-edge"
data-source-node-id={s.id}
data-target-node-id={t.id}
x1={s.x}
y1={s.y}
x2={t.x}
y2={t.y}
stroke="var(--text-muted)"
strokeOpacity={op}
strokeWidth={0.5+(l.strength||0.5)*1.2}
strokeLinecap="round"
/>
📍 Affects 2 files
  • frontend/e2e/graph.spec.ts#L232-L235 (this comment)
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/e2e/graph.spec.ts` around lines 232 - 235, Replace the count-only
assertion in frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation
covering every expected DB edge and synthesized hub spoke, while preserving the
existing count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.

@AndresL230

Copy link
Copy Markdown
CollaboratorAuthor

Code review

No issues found. Checked for bugs and CLAUDE.md compliance. (Panel findings fixed in-branch: the spec now anchors on id-keyed registered testids — graph-node-item + data-node-id on the a11y list, per docs/frontend-testids.md's Adding-a-surface process — which also structurally eliminates the latent label-collision gap; the db.ts helper converged on the merged queryRaw. Re-verified with a 3/3 confirmation cycle on the final content; the original 10/10×2 tally stands as acceptance evidence. The #355 acceptance test ships as test.fixme.)

🤖 Generated with Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(e2e): journey — graph render integrity vs DB

1 participant

@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(e2e): journey — graph render integrity vs DB (#395) - #433

Merged
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity
Jul 28, 2026
Merged

test(e2e): journey — graph render integrity vs DB (#395)#433
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity

Conversation

@AndresL230

@AndresL230AndresL230 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Journey #395: load the graph view (/tree) and assert the RENDERED knowledge graph against the database read over raw SQL — data assertions only, no force-layout geometry (positions are never read).

  • New spec frontend/e2e/graph.spec.ts (3 tests):
    1. Exact render counts vs DB — one rendered node per graph_nodes row plus one subject-root hub per DISTINCT enrolled course; one rendered edge per graph_edges row plus one hub spoke per course-linked node. test.fixme — red solely because of open bug Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 (below); the correct assertion is kept, not relaxed, so this is the ready-made acceptance test for the Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 fix.
    2. Concept-node integrity + mastery classification (passing) — every DB concept node renders exactly once (SVG label and the graph's accessibility list), every enrolled course's hub is present, at least every DB edge/spoke is drawn, and each node's DB mastery_score maps through the canonical thresholds (backend/config.py::get_mastery_tier) to the class the 2D graph encodes as node-circle opacity.
    3. Tier filter partition (passing) — selecting each mastery tier pill on /tree shows exactly the concepts whose DB score maps to that tier, and none other.
  • Additive harness seamfrontend/e2e/support/db.ts::dbQuery, a read-only raw-SQL helper so journeys compute expected values from the DB without hand-rolling pg plumbing (inherits the loopback-only safety guard). No new testids: the spec anchors on the registered graph-container testid plus structural/ARIA handles inside it (svg[aria-label="Knowledge graph"]<text>/<line>, the component's hidden a11y node list) — justification in the spec header; node identity matches on seeded concept names (DB data, not UI copy).
  • Harness convergence — carries the frontend/e2e/global-setup.ts fix from the test(e2e): journey — seeded session → dashboard #386 branch verbatim (storageState now includes the sapling_user localStorage identity; cookie-only state left authed pages on an infinite skeleton).

Bug #355 REPRODUCES on the migration-replayed local schema + rich seed

Probe (authed GET /api/graph/rich-user-active against the freshly seeded stack):

PROBE355 nodes_total=18 nodes_unique=17 dup_node_ids=["subject_root__rich-course-cs101"] edges_total=25 edges_unique=20 dup_edges=5

rich-user-active is enrolled in two offerings of the same abstract course (rich-off-cs101-f25 + rich-off-cs101-s26), and graph_service.get_graph synthesizes one subject_root__{course_id} per enrollment (loop over _user_enrolled_courses) instead of per distinct course — the CS subject root and its five hub spokes are emitted twice, exactly the duplicate-key defect #355 describes. This is the defect class this journey exists to catch: test 1 trips on it and is test.fixme(#355) with the correct assertions intact; tests 2–3 assert everything the duplication does not corrupt.

Verification — 10 consecutive local runs

10/10 green. Each run: 2 passed, 1 skipped — the skip is the test.fixme(#355) exact-count test; the two live tests passed on every run (~28s/run cold, ~11s/run warm). The full cycle was observed green twice back-to-back (20/20 runs total across both cycles).

Run12345678910
Resultpasspasspasspasspasspasspasspasspasspass

Cycle run under the shared stack lock: make e2e-up → probe → 10× npx playwright test e2e/graph.spec.tsmake e2e-down. A second probe on a volume carrying sibling-run residue reported the SAME single duplicate id (nodes_total=20 nodes_unique=19 dup_node_ids=["subject_root__rich-course-cs101"]) — totals drift with un-reset stack state, the dup signal does not; the spec itself is immune because every test truncates + re-seeds first.

Part of #402, closes#395

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • Added end-to-end coverage for graph rendering, node and edge counts, accessibility labels, mastery tiers, and tier filtering.
    • Added validation against database-backed graph data, including a documented known issue for duplicated subject hubs.
  • Accessibility

    • Improved stable identification of graph nodes and activation controls across 2D and 3D views.
  • Documentation

    • Expanded the test ID inventory to cover graph nodes, edges, accessibility elements, and zoom controls.

@coderabbitai

coderabbitaiBot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The graph components now expose stable test IDs and node identifiers across 2D and 3D renderers. A new Playwright suite derives graph expectations from database rows and validates rendered nodes, edges, mastery tiers, labels, counts, and filters.

Changes

Graph integrity validation

Layer / File(s)Summary
Graph test seams
frontend/src/components/KnowledgeGraph2D.tsx, frontend/src/components/KnowledgeGraph3D.tsx, frontend/eslint.config.mjs, docs/frontend-testids.md
Adds shared selectors for accessibility nodes, SVG nodes and edges, node IDs, node circles, activation controls, and zoom buttons; updates lint scope and test-ID documentation.
Database expectation mapping
frontend/e2e/graph.spec.ts
Loads graph data and enrolled courses, then derives expected counts, labels, hub IDs, and mastery-tier mappings.
Graph integrity specifications
frontend/e2e/graph.spec.ts
Checks rendered counts, node uniqueness, labels, opacity, edge coverage, and tier-filter partitioning, with a fixme for known hub duplication.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:jose-gael-cruz-lopez

Sequence Diagram(s)

sequenceDiagram
participant graphSpec as graph.spec.ts
participant database as Graph database
participant treePage as /tree page
participant graphDom as Graph DOM
graphSpec->>database: Load graph nodes, edges, and enrolled courses
graphSpec->>treePage: Navigate to /tree
treePage->>graphDom: Render SVG graph and accessibility node list
graphSpec->>graphDom: Validate IDs, counts, tiers, labels, opacity, and edges
graphSpec->>graphDom: Apply tier filters and verify node partitioning
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly matches the main change: an E2E graph integrity journey against the database for issue #395.
Description check✅ PassedThe description covers the summary, changes, related issue, and testing evidence, though it does not follow the template exactly.
Linked Issues check✅ PassedThe PR meets #395 by asserting graph counts and mastery mapping from DB rows with data-only checks and 10 local runs.
Out of Scope Changes check✅ PassedThe docs, test IDs, db helper, and global setup updates all support the graph integrity journey and are not out of scope.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/395-journey-graph-integrity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 28, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingc308689Commit Preview URL

Branch Preview URL
Jul 28 2026, 04:10 AM

AndresL230and others added 2 commits July 27, 2026 21:06
Loads /tree and asserts the rendered knowledge graph against raw-SQL
reads of graph_nodes/graph_edges/enrollments (new read-only dbQuery seam
in e2e/support/db.ts):
- exact node/edge render counts vs DB (+1 subject-root hub per distinct
enrolled course, +1 hub spoke per course node) — marked test.fixme
pending open bug #355, which duplicates the CS subject root on the
rich seed (two offerings of the same abstract course); the correct
assertion is kept, not relaxed
- every DB concept node renders exactly once, with the mastery class
derived from its DB mastery_score (config.get_mastery_tier thresholds)
encoded at the render layer (2D node-circle opacity)
- the /tree tier filter partitions nodes exactly by the DB-derived
classification
Data assertions only — no force-layout geometry is read.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review findings on PR #433: label-based node identification was a latent
gap (graph_nodes names are only unique PER COURSE, so cross-course name
or truncation-prefix collisions would fail a correct render — and the
label-keyed opacity map could silently mask a wrong class), and bare
structural/ARIA anchoring is not a sanctioned seam.
Fix both by minting graph-surface testids per the doc's process:
- KnowledgeGraph2D/3D a11y list: graph-node-items / graph-node-item
(+ data-node-id per entry) / graph-node-activate
- KnowledgeGraph2D SVG layer: graph-node (+ data-node-id),
graph-node-circle (the opacity-encoding mark), graph-edge,
graph-zoom-in/out/reset
- register all of it in docs/frontend-testids.md (graph surface row now
names the 2D/3D split) and add both files to the eslint enforcement
array (every intrinsic button is tagged — no suppressions needed)
The spec now identifies nodes exclusively by data-node-id and keys the
opacity map by id; rendered label text is still asserted per node, just
keyed by id instead of used as identity.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the test/395-journey-graph-integrity branch from ca9a2e7 to c308689CompareJuly 28, 2026 04:08

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/e2e/graph.spec.ts`:
- Around line 232-235: Replace the count-only assertion in
frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation covering every
expected DB edge and synthesized hub spoke, while preserving the existing
count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b170e05-e5bb-417d-a4f6-fbaa3a2c3c21

📥 Commits

Reviewing files that changed from the base of the PR and between c42717c and c308689.

📒 Files selected for processing (5)
  • docs/frontend-testids.md
  • frontend/e2e/graph.spec.ts
  • frontend/eslint.config.mjs
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/KnowledgeGraph3D.tsx

Comment on lines +232 to +235
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(await svgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Assert expected edge identities, not only a count floor.

Bug #355 currently adds five surplus spokes, so >= g.expectedEdgeCount still passes if up to five expected edges disappear. Expose each SVG line’s source/target IDs, then assert every DB edge and synthesized hub spoke is present; this remains valid while the exact-count test is fixme.

  • frontend/e2e/graph.spec.ts#L232-L235: compare the rendered edge endpoint pairs against all expected DB-edge and hub-spoke pairs.
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459: add stable source and target node-ID data attributes to each graph-edge line.
Proposed seam
 <line
data-testid="graph-edge"
+ data-source-node-id={s.id}+ data-target-node-id={t.id}
x1={s.x}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(awaitsvgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);
<line
key={i}
data-testid="graph-edge"
data-source-node-id={s.id}
data-target-node-id={t.id}
x1={s.x}
y1={s.y}
x2={t.x}
y2={t.y}
stroke="var(--text-muted)"
strokeOpacity={op}
strokeWidth={0.5+(l.strength||0.5)*1.2}
strokeLinecap="round"
/>
📍 Affects 2 files
  • frontend/e2e/graph.spec.ts#L232-L235 (this comment)
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/e2e/graph.spec.ts` around lines 232 - 235, Replace the count-only
assertion in frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation
covering every expected DB edge and synthesized hub spoke, while preserving the
existing count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.

@AndresL230

Copy link
Copy Markdown
CollaboratorAuthor

Code review

No issues found. Checked for bugs and CLAUDE.md compliance. (Panel findings fixed in-branch: the spec now anchors on id-keyed registered testids — graph-node-item + data-node-id on the a11y list, per docs/frontend-testids.md's Adding-a-surface process — which also structurally eliminates the latent label-collision gap; the db.ts helper converged on the merged queryRaw. Re-verified with a 3/3 confirmation cycle on the final content; the original 10/10×2 tally stands as acceptance evidence. The #355 acceptance test ships as test.fixme.)

🤖 Generated with Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(e2e): journey — graph render integrity vs DB

1 participant

@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

test(e2e): journey — graph render integrity vs DB (#395) - #433

Merged
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity
Jul 28, 2026
Merged

test(e2e): journey — graph render integrity vs DB (#395)#433
AndresL230 merged 2 commits into
mainfrom
test/395-journey-graph-integrity

Conversation

@AndresL230

@AndresL230AndresL230 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Journey #395: load the graph view (/tree) and assert the RENDERED knowledge graph against the database read over raw SQL — data assertions only, no force-layout geometry (positions are never read).

  • New spec frontend/e2e/graph.spec.ts (3 tests):
    1. Exact render counts vs DB — one rendered node per graph_nodes row plus one subject-root hub per DISTINCT enrolled course; one rendered edge per graph_edges row plus one hub spoke per course-linked node. test.fixme — red solely because of open bug Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 (below); the correct assertion is kept, not relaxed, so this is the ready-made acceptance test for the Duplicate subject_root node in /api/graph → React duplicate-key warnings in KnowledgeGraph2D #355 fix.
    2. Concept-node integrity + mastery classification (passing) — every DB concept node renders exactly once (SVG label and the graph's accessibility list), every enrolled course's hub is present, at least every DB edge/spoke is drawn, and each node's DB mastery_score maps through the canonical thresholds (backend/config.py::get_mastery_tier) to the class the 2D graph encodes as node-circle opacity.
    3. Tier filter partition (passing) — selecting each mastery tier pill on /tree shows exactly the concepts whose DB score maps to that tier, and none other.
  • Additive harness seamfrontend/e2e/support/db.ts::dbQuery, a read-only raw-SQL helper so journeys compute expected values from the DB without hand-rolling pg plumbing (inherits the loopback-only safety guard). No new testids: the spec anchors on the registered graph-container testid plus structural/ARIA handles inside it (svg[aria-label="Knowledge graph"]<text>/<line>, the component's hidden a11y node list) — justification in the spec header; node identity matches on seeded concept names (DB data, not UI copy).
  • Harness convergence — carries the frontend/e2e/global-setup.ts fix from the test(e2e): journey — seeded session → dashboard #386 branch verbatim (storageState now includes the sapling_user localStorage identity; cookie-only state left authed pages on an infinite skeleton).

Bug #355 REPRODUCES on the migration-replayed local schema + rich seed

Probe (authed GET /api/graph/rich-user-active against the freshly seeded stack):

PROBE355 nodes_total=18 nodes_unique=17 dup_node_ids=["subject_root__rich-course-cs101"] edges_total=25 edges_unique=20 dup_edges=5

rich-user-active is enrolled in two offerings of the same abstract course (rich-off-cs101-f25 + rich-off-cs101-s26), and graph_service.get_graph synthesizes one subject_root__{course_id} per enrollment (loop over _user_enrolled_courses) instead of per distinct course — the CS subject root and its five hub spokes are emitted twice, exactly the duplicate-key defect #355 describes. This is the defect class this journey exists to catch: test 1 trips on it and is test.fixme(#355) with the correct assertions intact; tests 2–3 assert everything the duplication does not corrupt.

Verification — 10 consecutive local runs

10/10 green. Each run: 2 passed, 1 skipped — the skip is the test.fixme(#355) exact-count test; the two live tests passed on every run (~28s/run cold, ~11s/run warm). The full cycle was observed green twice back-to-back (20/20 runs total across both cycles).

Run12345678910
Resultpasspasspasspasspasspasspasspasspasspass

Cycle run under the shared stack lock: make e2e-up → probe → 10× npx playwright test e2e/graph.spec.tsmake e2e-down. A second probe on a volume carrying sibling-run residue reported the SAME single duplicate id (nodes_total=20 nodes_unique=19 dup_node_ids=["subject_root__rich-course-cs101"]) — totals drift with un-reset stack state, the dup signal does not; the spec itself is immune because every test truncates + re-seeds first.

Part of #402, closes#395

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • Added end-to-end coverage for graph rendering, node and edge counts, accessibility labels, mastery tiers, and tier filtering.
    • Added validation against database-backed graph data, including a documented known issue for duplicated subject hubs.
  • Accessibility

    • Improved stable identification of graph nodes and activation controls across 2D and 3D views.
  • Documentation

    • Expanded the test ID inventory to cover graph nodes, edges, accessibility elements, and zoom controls.

@coderabbitai

coderabbitaiBot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The graph components now expose stable test IDs and node identifiers across 2D and 3D renderers. A new Playwright suite derives graph expectations from database rows and validates rendered nodes, edges, mastery tiers, labels, counts, and filters.

Changes

Graph integrity validation

Layer / File(s)Summary
Graph test seams
frontend/src/components/KnowledgeGraph2D.tsx, frontend/src/components/KnowledgeGraph3D.tsx, frontend/eslint.config.mjs, docs/frontend-testids.md
Adds shared selectors for accessibility nodes, SVG nodes and edges, node IDs, node circles, activation controls, and zoom buttons; updates lint scope and test-ID documentation.
Database expectation mapping
frontend/e2e/graph.spec.ts
Loads graph data and enrolled courses, then derives expected counts, labels, hub IDs, and mastery-tier mappings.
Graph integrity specifications
frontend/e2e/graph.spec.ts
Checks rendered counts, node uniqueness, labels, opacity, edge coverage, and tier-filter partitioning, with a fixme for known hub duplication.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:jose-gael-cruz-lopez

Sequence Diagram(s)

sequenceDiagram
participant graphSpec as graph.spec.ts
participant database as Graph database
participant treePage as /tree page
participant graphDom as Graph DOM
graphSpec->>database: Load graph nodes, edges, and enrolled courses
graphSpec->>treePage: Navigate to /tree
treePage->>graphDom: Render SVG graph and accessibility node list
graphSpec->>graphDom: Validate IDs, counts, tiers, labels, opacity, and edges
graphSpec->>graphDom: Apply tier filters and verify node partitioning
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly matches the main change: an E2E graph integrity journey against the database for issue #395.
Description check✅ PassedThe description covers the summary, changes, related issue, and testing evidence, though it does not follow the template exactly.
Linked Issues check✅ PassedThe PR meets #395 by asserting graph counts and mastery mapping from DB rows with data-only checks and 10 local runs.
Out of Scope Changes check✅ PassedThe docs, test IDs, db helper, and global setup updates all support the graph integrity journey and are not out of scope.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/395-journey-graph-integrity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jul 28, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-stagingc308689Commit Preview URL

Branch Preview URL
Jul 28 2026, 04:10 AM

AndresL230and others added 2 commits July 27, 2026 21:06
Loads /tree and asserts the rendered knowledge graph against raw-SQL
reads of graph_nodes/graph_edges/enrollments (new read-only dbQuery seam
in e2e/support/db.ts):
- exact node/edge render counts vs DB (+1 subject-root hub per distinct
enrolled course, +1 hub spoke per course node) — marked test.fixme
pending open bug #355, which duplicates the CS subject root on the
rich seed (two offerings of the same abstract course); the correct
assertion is kept, not relaxed
- every DB concept node renders exactly once, with the mastery class
derived from its DB mastery_score (config.get_mastery_tier thresholds)
encoded at the render layer (2D node-circle opacity)
- the /tree tier filter partitions nodes exactly by the DB-derived
classification
Data assertions only — no force-layout geometry is read.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review findings on PR #433: label-based node identification was a latent
gap (graph_nodes names are only unique PER COURSE, so cross-course name
or truncation-prefix collisions would fail a correct render — and the
label-keyed opacity map could silently mask a wrong class), and bare
structural/ARIA anchoring is not a sanctioned seam.
Fix both by minting graph-surface testids per the doc's process:
- KnowledgeGraph2D/3D a11y list: graph-node-items / graph-node-item
(+ data-node-id per entry) / graph-node-activate
- KnowledgeGraph2D SVG layer: graph-node (+ data-node-id),
graph-node-circle (the opacity-encoding mark), graph-edge,
graph-zoom-in/out/reset
- register all of it in docs/frontend-testids.md (graph surface row now
names the 2D/3D split) and add both files to the eslint enforcement
array (every intrinsic button is tagged — no suppressions needed)
The spec now identifies nodes exclusively by data-node-id and keys the
opacity map by id; rendered label text is still asserted per node, just
keyed by id instead of used as identity.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AndresL230
AndresL230force-pushed the test/395-journey-graph-integrity branch from ca9a2e7 to c308689CompareJuly 28, 2026 04:08

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/e2e/graph.spec.ts`:
- Around line 232-235: Replace the count-only assertion in
frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation covering every
expected DB edge and synthesized hub spoke, while preserving the existing
count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b170e05-e5bb-417d-a4f6-fbaa3a2c3c21

📥 Commits

Reviewing files that changed from the base of the PR and between c42717c and c308689.

📒 Files selected for processing (5)
  • docs/frontend-testids.md
  • frontend/e2e/graph.spec.ts
  • frontend/eslint.config.mjs
  • frontend/src/components/KnowledgeGraph2D.tsx
  • frontend/src/components/KnowledgeGraph3D.tsx

Comment on lines +232 to +235
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(await svgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Assert expected edge identities, not only a count floor.

Bug #355 currently adds five surplus spokes, so >= g.expectedEdgeCount still passes if up to five expected edges disappear. Expose each SVG line’s source/target IDs, then assert every DB edge and synthesized hub spoke is present; this remains valid while the exact-count test is fixme.

  • frontend/e2e/graph.spec.ts#L232-L235: compare the rendered edge endpoint pairs against all expected DB-edge and hub-spoke pairs.
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459: add stable source and target node-ID data attributes to each graph-edge line.
Proposed seam
 <line
data-testid="graph-edge"
+ data-source-node-id={s.id}+ data-target-node-id={t.id}
x1={s.x}
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Edge floor: at least every DB edge + hub spoke is drawn (exact equality
// is #355-blocked — the duplicate hub adds surplus spokes, but a MISSING
// edge must still fail here).
expect(awaitsvgEdges.count()).toBeGreaterThanOrEqual(g.expectedEdgeCount);
<line
key={i}
data-testid="graph-edge"
data-source-node-id={s.id}
data-target-node-id={t.id}
x1={s.x}
y1={s.y}
x2={t.x}
y2={t.y}
stroke="var(--text-muted)"
strokeOpacity={op}
strokeWidth={0.5+(l.strength||0.5)*1.2}
strokeLinecap="round"
/>
📍 Affects 2 files
  • frontend/e2e/graph.spec.ts#L232-L235 (this comment)
  • frontend/src/components/KnowledgeGraph2D.tsx#L448-L459
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/e2e/graph.spec.ts` around lines 232 - 235, Replace the count-only
assertion in frontend/e2e/graph.spec.ts:232-235 with endpoint-pair validation
covering every expected DB edge and synthesized hub spoke, while preserving the
existing count-floor check if needed. In
frontend/src/components/KnowledgeGraph2D.tsx:448-459, add stable source- and
target-node-ID data attributes to each graph-edge line so the test can compare
rendered identities against expected pairs.

@AndresL230

Copy link
Copy Markdown
CollaboratorAuthor

Code review

No issues found. Checked for bugs and CLAUDE.md compliance. (Panel findings fixed in-branch: the spec now anchors on id-keyed registered testids — graph-node-item + data-node-id on the a11y list, per docs/frontend-testids.md's Adding-a-surface process — which also structurally eliminates the latent label-collision gap; the db.ts helper converged on the merged queryRaw. Re-verified with a 3/3 confirmation cycle on the final content; the original 10/10×2 tally stands as acceptance evidence. The #355 acceptance test ships as test.fixme.)

🤖 Generated with Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(e2e): journey — graph render integrity vs DB

1 participant

@AndresL230