import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import ts from "typescript"; const testDir = path.dirname(fileURLToPath(import.meta.url)); const sourcePath = path.resolve(testDir, "../src/features/article-detail/model.ts"); const source = readFileSync(sourcePath, "utf8"); const compiled = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 }, }); const moduleExports = {}; new Function("exports", compiled.outputText)(moduleExports); const { buildArticleTimeline, buildJobDetailRows, buildDetailSummary, buildJobControlState } = moduleExports; const detail = { article: { id: "a1", target_site_id: "site1", status: "PARALLEL_PRODUCTION_RUNNING", publishing_status: "PUBLISH_NOT_STARTED", brief_description: "desc", language: "en", content_type: "longform_guide", created_at: "2026-05-21T00:00:00Z", updated_at: "2026-05-21T00:00:00Z", }, target_site: { name: "Demo Site" }, workflow_template_snapshot: { id: "workflow-1", name: "Demo workflow", slug: "demo-workflow", version: 2, stage_summary: [ { id: "stage-2", stable_key: "draft", display_name: "Draft assembly", position: 2, owner_role: "EDITOR", runner_profile_key: "draft-agent", requires_human_approval: false, }, { id: "stage-1", stable_key: "intake", display_name: "Intake", position: 1, owner_role: "EDITOR", runner_profile_key: "intake-agent", requires_human_approval: true, }, ], }, timeline: [ { id: "2", article_id: "a1", entry_type: "AGENT_JOB", source: "AGENT", event_type: "SECTION_SCAFFOLD_FAILED", from_status: null, to_status: null, actor_user_id: null, job_id: "job2", job_type: "SECTION_SCAFFOLD", job_status: "FAILED", retry_eligible: true, cancel_eligible: false, safe_failure_summary: "Section failed.", payload: {}, created_at: "2026-05-21T00:00:02Z", }, { id: "1", article_id: "a1", entry_type: "WORKFLOW_EVENT", source: "USER", event_type: "PLAN_APPROVED", from_status: "PLAN_REVIEW_REQUIRED", to_status: "RESEARCH_RUNNING", actor_user_id: "u1", job_id: null, job_type: null, job_status: null, retry_eligible: false, cancel_eligible: false, safe_failure_summary: null, payload: {}, created_at: "2026-05-21T00:00:01Z", }, ], workflow_events: [], agent_jobs: [ { id: "job2", article_id: "a1", parent_job_id: null, attempt: 1, job_type: "SECTION_SCAFFOLD", agent_profile: "fake-section-scaffold", status: "FAILED", workspace_path: null, input_files: [], output_files: [], payload: { last_successful_step: "Outline written" }, error_category: "UNSUPPORTED_CLAIMS_FOUND", error_message: "Unsupported claims introduced during scaffolding: 1", stdout: "token=[REDACTED]", stderr: "", exit_code: 1, duration_ms: 5, queued_at: "2026-05-21T00:00:00Z", started_at: "2026-05-21T00:00:01Z", finished_at: "2026-05-21T00:00:02Z", retry_eligible: true, retry_block_reason: null, cancel_eligible: false, safe_failure_summary: "Section failed safely.", }, ], publish_commit: null, }; const timeline = buildArticleTimeline(detail); assert.equal(timeline.length, 2); assert.equal(timeline[0].eventType, "PLAN_APPROVED"); assert.equal(timeline[1].eventType, "SECTION_SCAFFOLD_FAILED"); assert.equal(timeline[1].source, "AGENT"); assert.equal(timeline[1].retryEligible, true); const jobRows = buildJobDetailRows(detail.agent_jobs); assert.equal(jobRows.length, 1); assert.equal(jobRows[0].jobType, "SECTION_SCAFFOLD"); assert.equal(jobRows[0].status, "FAILED"); assert.equal(jobRows[0].errorCategory, "UNSUPPORTED_CLAIMS_FOUND"); assert.equal(jobRows[0].errorMessage, "Unsupported claims introduced during scaffolding: 1"); assert.equal(jobRows[0].lastSuccessfulStep, "Outline written"); assert.equal(jobRows[0].retryEligible, true); assert.deepEqual( buildJobControlState(jobRows[0], "ADMIN"), { showRetry: true, showCancel: false, showAdminLogs: true, showSafeSummary: false }, ); assert.deepEqual( buildJobControlState(jobRows[0], "EDITOR"), { showRetry: false, showCancel: false, showAdminLogs: false, showSafeSummary: true }, ); const summary = buildDetailSummary(detail, { viewerRole: "EDITOR", viewerEmail: "editor@example.com", }); assert.equal(summary.viewerRole, "EDITOR"); assert.equal(summary.viewerEmail, "editor@example.com"); assert.equal(summary.targetSite, "Demo Site"); assert.equal(summary.workflowTemplate.name, "Demo workflow"); assert.equal(summary.workflowTemplate.version, 2); assert.deepEqual( summary.workflowTemplate.stages.map((stage) => stage.stableKey), ["intake", "draft"], ); assert.equal(summary.timeline.length, 2); assert.equal(summary.jobDetails.length, 1);