chapter-analyzer.test.ts 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import { afterEach, describe, expect, it, vi } from "vitest";
  2. import { mkdtemp, rm } from "node:fs/promises";
  3. import { tmpdir } from "node:os";
  4. import { join } from "node:path";
  5. import { ChapterAnalyzerAgent } from "../agents/chapter-analyzer.js";
  6. import type { BookConfig } from "../models/book.js";
  7. import { countChapterLength } from "../utils/length-metrics.js";
  8. const ZERO_USAGE = {
  9. promptTokens: 0,
  10. completionTokens: 0,
  11. totalTokens: 0,
  12. } as const;
  13. describe("ChapterAnalyzerAgent", () => {
  14. afterEach(() => {
  15. vi.restoreAllMocks();
  16. });
  17. it("counts English chapter content using words instead of characters", async () => {
  18. const bookDir = await mkdtemp(join(tmpdir(), "inkos-chapter-analyzer-"));
  19. const englishContent = "He looked at the sky and waited.";
  20. const agent = new ChapterAnalyzerAgent({
  21. client: {
  22. provider: "openai",
  23. apiFormat: "chat",
  24. stream: false,
  25. defaults: {
  26. temperature: 0.7,
  27. maxTokens: 4096,
  28. thinkingBudget: 0,
  29. extra: {},
  30. },
  31. },
  32. model: "test-model",
  33. projectRoot: process.cwd(),
  34. });
  35. const book: BookConfig = {
  36. id: "english-book",
  37. title: "English Book",
  38. platform: "other",
  39. genre: "other",
  40. status: "active",
  41. targetChapters: 10,
  42. chapterWordCount: 2200,
  43. language: "en",
  44. createdAt: "2026-03-22T00:00:00.000Z",
  45. updatedAt: "2026-03-22T00:00:00.000Z",
  46. };
  47. vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
  48. .mockResolvedValue({
  49. content: [
  50. "=== CHAPTER_TITLE ===",
  51. "A Quiet Sky",
  52. "",
  53. "=== CHAPTER_CONTENT ===",
  54. englishContent,
  55. "",
  56. "=== PRE_WRITE_CHECK ===",
  57. "",
  58. "=== POST_SETTLEMENT ===",
  59. "",
  60. "=== UPDATED_STATE ===",
  61. "| Field | Value |",
  62. "| --- | --- |",
  63. "| Chapter | 1 |",
  64. "",
  65. "=== UPDATED_LEDGER ===",
  66. "",
  67. "=== UPDATED_HOOKS ===",
  68. "| hook_id | status |",
  69. "| --- | --- |",
  70. "| h1 | open |",
  71. "",
  72. "=== CHAPTER_SUMMARY ===",
  73. "| 1 | A Quiet Sky |",
  74. "",
  75. "=== UPDATED_SUBPLOTS ===",
  76. "",
  77. "=== UPDATED_EMOTIONAL_ARCS ===",
  78. "",
  79. "=== UPDATED_CHARACTER_MATRIX ===",
  80. "",
  81. ].join("\n"),
  82. usage: ZERO_USAGE,
  83. });
  84. try {
  85. const output = await agent.analyzeChapter({
  86. book,
  87. bookDir,
  88. chapterNumber: 1,
  89. chapterContent: englishContent,
  90. });
  91. expect(output.wordCount).toBe(countChapterLength(englishContent, "en_words"));
  92. expect(output.wordCount).toBe(7);
  93. } finally {
  94. await rm(bookDir, { recursive: true, force: true });
  95. }
  96. });
  97. });