Files
GEOAgentArticleOptimizer/src/lib/db/__tests__/repository.test.ts
T
2026-07-08 13:19:35 +08:00

233 lines
7.3 KiB
TypeScript

import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { createSqliteRepository } from "../sqlite-repository";
describe("createSqliteRepository", () => {
let tempDir: string;
let dbPath: string;
beforeEach(() => {
tempDir = mkdtempSync(join(tmpdir(), "geo-repository-"));
dbPath = join(tempDir, "app.db");
});
afterEach(() => {
rmSync(tempDir, { recursive: true, force: true });
});
test("creates and reads an article job through the async repository interface", async () => {
const repository = createSqliteRepository(dbPath);
const job = await repository.createArticleJob({
source_title: "Title",
source_body: "Body",
image_inputs: [],
publish_platform: "official_site",
user_instructions: "",
});
await expect(repository.getArticleJob(job.id)).resolves.toMatchObject({
id: job.id,
source_title: "Title",
export_paths: {},
});
});
test("persists scoring, publication, performance, and calibration event", async () => {
const repository = createSqliteRepository(dbPath);
const job = await repository.createArticleJob({
source_title: "Title",
source_body: "Body",
image_inputs: [],
publish_platform: "official_site",
user_instructions: "",
});
const article = await repository.saveOptimizedArticle(job.id, {
title: "Optimized",
summary: "Summary",
body_markdown: "Body",
image_suggestions: [],
changed_sections: [],
requires_user_confirmation: [],
});
await repository.saveRubricVersion({
id: "rubric_geo_v1",
version: "v1",
name: "GEO rubric",
dimensions: [],
formula: "weighted_average_0_to_10",
is_active: true,
created_at: "2026-06-24T00:00:00.000Z",
});
const scoringRun = await repository.saveScoringRun({
id: "score_1",
job_id: job.id,
revision: article.revision ?? 1,
rubric_version_id: "rubric_geo_v1",
dimension_scores: { readability: 4 },
composite_score: 8,
rationale: "Readable",
created_at: "2026-06-24T00:00:00.000Z",
});
const publication = await repository.createPublicationRecord({
job_id: job.id,
revision: article.revision ?? 1,
platform: "official_site",
url: "https://example.com/article",
published_at: "2026-06-24T12:00:00.000Z",
status: "published",
notes: "官网首发",
});
const snapshot = await repository.savePerformanceSnapshot({
id: "perf_1",
publication_id: publication.id,
source: "manual",
window_label: "T+7d",
metrics: { views: 1200 },
feedback_summary: "用户追问案例依据",
snapshot_at: "2026-07-01T12:00:00.000Z",
});
const event = await repository.saveCalibrationEvent({
id: "cal_1",
publication_id: publication.id,
scoring_run_id: scoringRun.id,
performance_snapshot_id: snapshot.id,
direction: "better_than_expected",
observations: ["表现高于预期"],
recommended_action: "继续积累样本",
created_at: "2026-07-01T12:10:00.000Z",
});
await expect(repository.listPublicationRecords(job.id)).resolves.toHaveLength(1);
await expect(repository.getLatestScoringRun(job.id, article.revision ?? 1))
.resolves.toMatchObject({ id: scoringRun.id, composite_score: 8 });
await expect(repository.listPerformanceSnapshots(publication.id)).resolves.toEqual([
expect.objectContaining({ id: snapshot.id }),
]);
expect(event.observations).toEqual(["表现高于预期"]);
});
test("creates, lists, updates, archives, and restores optimization cases", async () => {
const repository = createSqliteRepository(dbPath);
const created = await repository.createOptimizationCase({
case_type: "human_copy",
title: "人味文案优化:朋友圈",
summary: "原文摘要",
publish_target: "朋友圈",
source_excerpt: "原文摘要",
});
await repository.saveCaseInput({
case_id: created.id,
case_type: "human_copy",
article_job_id: null,
payload: {
source_text: "原文摘要",
goal: "自然一点",
intensity: "light",
user_instructions: "",
publish_target: "朋友圈",
},
});
await expect(
repository.listOptimizationCases({ include_archived: false }),
).resolves.toEqual([expect.objectContaining({ id: created.id })]);
await expect(
repository.updateOptimizationCaseMetadata(created.id, {
customer_name: "客户A",
brand_name: "品牌B",
project_tags: ["朋友圈"],
notes: "保留口语。",
}),
).resolves.toMatchObject({
customer_name: "客户A",
project_tags: ["朋友圈"],
});
await repository.archiveOptimizationCase(created.id);
await expect(
repository.listOptimizationCases({ include_archived: false }),
).resolves.toHaveLength(0);
await repository.restoreOptimizationCase(created.id);
await expect(repository.getOptimizationCaseDetail(created.id)).resolves.toMatchObject({
case: { id: created.id, status: "running" },
input: expect.objectContaining({ case_id: created.id }),
});
});
test("creates multiple result versions and binds publication to a version", async () => {
const repository = createSqliteRepository(dbPath);
const optimizationCase = await repository.createOptimizationCase({
case_type: "human_copy",
title: "人味文案优化:私域",
summary: "原文",
publish_target: "私域",
source_excerpt: "原文",
});
const first = await repository.createOptimizationResultVersion({
case_id: optimizationCase.id,
case_type: "human_copy",
status: "optimized",
article_job_id: null,
article_revision: null,
result_summary: "第一版",
payload: {
optimized_text: "第一版文案",
change_notes: [],
ai_taste_checks: [],
warnings: [],
},
process_summary: [],
llm_audit_summary: [],
error_stage: null,
error_summary: null,
});
const second = await repository.createOptimizationResultVersion({
case_id: optimizationCase.id,
case_type: "human_copy",
status: "optimized",
article_job_id: null,
article_revision: null,
result_summary: "第二版",
payload: {
optimized_text: "第二版文案",
change_notes: [],
ai_taste_checks: [],
warnings: [],
},
process_summary: [],
llm_audit_summary: [],
error_stage: null,
error_summary: null,
});
expect(first.version).toBe(1);
expect(second.version).toBe(2);
const publication = await repository.createPublicationRecord({
result_version_id: second.id,
job_id: null,
revision: null,
publish_target: "私域",
url: "https://example.com/private",
published_at: "2026-07-08T12:00:00.000Z",
status: "published",
notes: "客户私域发布",
});
await expect(
repository.listPublicationRecordsForResultVersion(second.id),
).resolves.toEqual([expect.objectContaining({ id: publication.id })]);
});
});