interaction-models.test.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import { describe, expect, it } from "vitest";
  2. import {
  3. AutomationModeSchema,
  4. InteractionIntentTypeSchema,
  5. ExecutionStatusSchema,
  6. InteractionSessionSchema,
  7. bindActiveBook,
  8. clearPendingDecision,
  9. isTerminalExecutionStatus,
  10. appendInteractionMessage,
  11. appendInteractionEvent,
  12. } from "../index.js";
  13. describe("interaction models", () => {
  14. it("parses supported automation modes", () => {
  15. expect(AutomationModeSchema.parse("auto")).toBe("auto");
  16. expect(AutomationModeSchema.parse("semi")).toBe("semi");
  17. expect(AutomationModeSchema.parse("manual")).toBe("manual");
  18. });
  19. it("parses supported interaction intents", () => {
  20. expect(InteractionIntentTypeSchema.parse("create_book")).toBe("create_book");
  21. expect(InteractionIntentTypeSchema.parse("write_next")).toBe("write_next");
  22. expect(InteractionIntentTypeSchema.parse("rewrite_chapter")).toBe("rewrite_chapter");
  23. expect(InteractionIntentTypeSchema.parse("explain_failure")).toBe("explain_failure");
  24. });
  25. it("recognizes terminal execution statuses", () => {
  26. expect(isTerminalExecutionStatus(ExecutionStatusSchema.parse("completed"))).toBe(true);
  27. expect(isTerminalExecutionStatus(ExecutionStatusSchema.parse("failed"))).toBe(true);
  28. expect(isTerminalExecutionStatus(ExecutionStatusSchema.parse("idle"))).toBe(false);
  29. expect(isTerminalExecutionStatus(ExecutionStatusSchema.parse("writing"))).toBe(false);
  30. });
  31. it("binds the active book without disturbing unrelated session fields", () => {
  32. const session = InteractionSessionSchema.parse({
  33. sessionId: "session-1",
  34. projectRoot: "/tmp/project",
  35. automationMode: "semi",
  36. messages: [],
  37. pendingDecision: {
  38. kind: "approve-chapter",
  39. bookId: "book-a",
  40. chapterNumber: 3,
  41. summary: "Chapter 3 is waiting for review.",
  42. },
  43. currentExecution: {
  44. status: "waiting_human",
  45. bookId: "book-a",
  46. chapterNumber: 3,
  47. stageLabel: "waiting for approval",
  48. },
  49. });
  50. expect(bindActiveBook(session, "book-b")).toEqual({
  51. ...session,
  52. activeBookId: "book-b",
  53. });
  54. });
  55. it("clears pending decisions while keeping the rest of the session intact", () => {
  56. const session = InteractionSessionSchema.parse({
  57. sessionId: "session-2",
  58. projectRoot: "/tmp/project",
  59. activeBookId: "book-a",
  60. automationMode: "auto",
  61. messages: [],
  62. pendingDecision: {
  63. kind: "choose-repair-mode",
  64. bookId: "book-a",
  65. chapterNumber: 8,
  66. summary: "Choose whether to local-fix or rewrite chapter 8.",
  67. },
  68. });
  69. expect(clearPendingDecision(session)).toEqual({
  70. ...session,
  71. pendingDecision: undefined,
  72. });
  73. });
  74. it("appends interaction messages in timestamp order", () => {
  75. const session = InteractionSessionSchema.parse({
  76. sessionId: "session-3",
  77. projectRoot: "/tmp/project",
  78. automationMode: "semi",
  79. messages: [],
  80. });
  81. const next = appendInteractionMessage(session, {
  82. role: "user",
  83. content: "continue",
  84. timestamp: 1,
  85. });
  86. expect(next.messages).toEqual([{
  87. role: "user",
  88. content: "continue",
  89. timestamp: 1,
  90. }]);
  91. });
  92. it("appends interaction events in timestamp order", () => {
  93. const session = InteractionSessionSchema.parse({
  94. sessionId: "session-4",
  95. projectRoot: "/tmp/project",
  96. automationMode: "semi",
  97. messages: [],
  98. events: [],
  99. });
  100. const next = appendInteractionEvent(session, {
  101. kind: "task.completed",
  102. timestamp: 2,
  103. status: "completed",
  104. bookId: "harbor",
  105. detail: "Completed write_next for harbor.",
  106. });
  107. expect(next.events).toEqual([{
  108. kind: "task.completed",
  109. timestamp: 2,
  110. status: "completed",
  111. bookId: "harbor",
  112. detail: "Completed write_next for harbor.",
  113. }]);
  114. });
  115. });