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-review/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 { buildReviewIssueRows, buildFinalReviewSummary } = moduleExports; const seoReport = { id: "11111111-1111-1111-1111-111111111111", article_id: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", review_kind: "SEO", draft_id: "dddddddd-dddd-dddd-dddd-dddddddddddd", score: 72, issues: [ { id: "meta-description-length", suggestion_id: "22222222-2222-2222-2222-222222222222", severity: "HIGH", location: "meta_description", message: "Meta description too short.", suggested_fix: "Use 120-160 chars.", suggested_rewrite: "Expanded meta description text.", status: "PENDING", }, ], recommended_slug: "seo-language-review-guide", recommended_title: "SEO Language Review Guide", schema_json: { "@type": "Article" }, rules_snapshot: { meta_description_min: 120 }, created_at: "2026-05-22T12:00:00Z", }; const languageReport = { ...seoReport, id: "33333333-3333-3333-3333-333333333333", review_kind: "LANGUAGE", issues: [ { id: "long-sentence", suggestion_id: "44444444-4444-4444-4444-444444444444", severity: "MEDIUM", location: "body_markdown", message: "Sentence exceeds 24 words.", suggested_fix: "Split sentence.", suggested_rewrite: "This sentence is shorter.", status: "ACCEPTED", }, ], }; const rows = buildReviewIssueRows(seoReport, languageReport); assert.equal(rows.length, 2); assert.equal(rows[0].reviewKind, "SEO"); assert.equal(rows[0].suggestionId, "22222222-2222-2222-2222-222222222222"); assert.equal(rows[1].reviewKind, "LANGUAGE"); assert.equal(rows[1].status, "ACCEPTED"); const summary = buildFinalReviewSummary({ article_id: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", unresolved_count: 2, unresolved_issues: [ { review_kind: "SEO", suggestion_id: "22222222-2222-2222-2222-222222222222", severity: "HIGH", location: "meta_description", message: "Meta description too short.", status: "PENDING", }, { review_kind: "LANGUAGE", suggestion_id: "55555555-5555-5555-5555-555555555555", severity: "MEDIUM", location: "body_markdown", message: "Sentence exceeds 24 words.", status: "PENDING", }, ], }); assert.equal(summary.unresolvedCount, 2); assert.equal(summary.unresolvedTitles.length, 2); assert.ok(summary.unresolvedTitles[0].startsWith("SEO:"));