268 lines
9.3 KiB
Markdown
268 lines
9.3 KiB
Markdown
---
|
|
phase: 21-chat-foundation
|
|
plan: 00
|
|
type: execute
|
|
wave: 0
|
|
depends_on: []
|
|
files_modified:
|
|
- server/src/__tests__/chat-service.test.ts
|
|
- server/src/__tests__/chat-routes.test.ts
|
|
- ui/src/components/ChatMarkdownMessage.test.tsx
|
|
- ui/src/components/ChatInput.test.tsx
|
|
autonomous: true
|
|
requirements: [HIST-01, CHAT-02, CHAT-03, CHAT-04, CHAT-05, CHAT-06, INPUT-07]
|
|
|
|
must_haves:
|
|
truths:
|
|
- "Test stubs exist and can be executed by vitest without errors"
|
|
- "Each stub has describe blocks with placeholder tests that skip or pass trivially"
|
|
artifacts:
|
|
- path: "server/src/__tests__/chat-service.test.ts"
|
|
provides: "Test scaffold for chat service (HIST-01, CHAT-04, CHAT-05, CHAT-06)"
|
|
contains: "describe.*chatService"
|
|
- path: "server/src/__tests__/chat-routes.test.ts"
|
|
provides: "Test scaffold for chat routes (POST conversation, GET list, POST message)"
|
|
contains: "describe.*chatRoutes"
|
|
- path: "ui/src/components/ChatMarkdownMessage.test.tsx"
|
|
provides: "Test scaffold for markdown rendering (CHAT-02, CHAT-03)"
|
|
contains: "describe.*ChatMarkdownMessage"
|
|
- path: "ui/src/components/ChatInput.test.tsx"
|
|
provides: "Test scaffold for keyboard shortcuts (INPUT-07)"
|
|
contains: "describe.*ChatInput"
|
|
key_links: []
|
|
---
|
|
|
|
<objective>
|
|
Create Wave 0 test stubs for the four key test files needed by Plans 01-05.
|
|
|
|
Purpose: Satisfy the Nyquist rule — every implementation task must have a pre-existing test file with describe blocks and placeholder expectations. Plans 01 and 02 depend on these stubs existing before they execute.
|
|
Output: Four test files with describe/it blocks that vitest can discover and run.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
|
@$HOME/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/phases/21-chat-foundation/21-RESEARCH.md
|
|
|
|
<interfaces>
|
|
From server/src/__tests__/activity-routes.test.ts (reference pattern for server tests):
|
|
```typescript
|
|
import express from "express";
|
|
import request from "supertest";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { errorHandler } from "../middleware/index.js";
|
|
|
|
const mockService = vi.hoisted(() => ({ list: vi.fn(), create: vi.fn() }));
|
|
vi.mock("../services/activity.js", () => ({ activityService: () => mockService }));
|
|
|
|
function createApp() {
|
|
const app = express();
|
|
app.use(express.json());
|
|
// ... mock actor middleware
|
|
}
|
|
```
|
|
|
|
From ui/src/components/MarkdownBody.test.tsx (reference pattern for UI component tests):
|
|
```typescript
|
|
// @vitest-environment node
|
|
import { describe, expect, it } from "vitest";
|
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
import { ThemeProvider } from "../context/ThemeContext";
|
|
```
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Create server-side test stubs (chat-service + chat-routes)</name>
|
|
<files>server/src/__tests__/chat-service.test.ts, server/src/__tests__/chat-routes.test.ts</files>
|
|
<read_first>
|
|
- server/src/__tests__/activity-routes.test.ts (full file — reference for mock pattern, createApp, supertest usage)
|
|
</read_first>
|
|
<action>
|
|
Create `server/src/__tests__/chat-service.test.ts`:
|
|
|
|
```typescript
|
|
import { describe, it, expect } from "vitest";
|
|
|
|
describe("chatService", () => {
|
|
describe("createConversation", () => {
|
|
it.todo("creates a conversation row with companyId");
|
|
it.todo("returns the created conversation with id and timestamps");
|
|
});
|
|
|
|
describe("listConversations", () => {
|
|
it.todo("returns conversations sorted by updatedAt DESC");
|
|
it.todo("excludes soft-deleted conversations");
|
|
it.todo("supports cursor-based pagination with hasMore");
|
|
it.todo("limits results to max 100");
|
|
});
|
|
|
|
describe("getConversation", () => {
|
|
it.todo("returns conversation by id");
|
|
it.todo("throws notFound for non-existent conversation");
|
|
it.todo("throws notFound for soft-deleted conversation");
|
|
});
|
|
|
|
describe("updateConversation", () => {
|
|
it.todo("updates title");
|
|
it.todo("sets pinnedAt timestamp");
|
|
it.todo("clears pinnedAt when set to null");
|
|
it.todo("sets archivedAt timestamp");
|
|
it.todo("bumps updatedAt on every update");
|
|
});
|
|
|
|
describe("softDeleteConversation", () => {
|
|
it.todo("sets deletedAt timestamp");
|
|
it.todo("throws notFound if already deleted");
|
|
});
|
|
|
|
describe("addMessage", () => {
|
|
it.todo("inserts a message row with conversationId and role");
|
|
it.todo("bumps conversation updatedAt after insert");
|
|
it.todo("auto-sets title from first user message when title is null");
|
|
it.todo("does not overwrite existing title on subsequent messages");
|
|
});
|
|
|
|
describe("listMessages", () => {
|
|
it.todo("returns messages for conversation sorted by createdAt DESC");
|
|
it.todo("supports cursor-based pagination");
|
|
});
|
|
});
|
|
```
|
|
|
|
Create `server/src/__tests__/chat-routes.test.ts`:
|
|
|
|
```typescript
|
|
import { describe, it, expect } from "vitest";
|
|
|
|
describe("chatRoutes", () => {
|
|
describe("POST /companies/:companyId/conversations", () => {
|
|
it.todo("creates a conversation and returns 201");
|
|
it.todo("accepts optional title and agentId");
|
|
});
|
|
|
|
describe("GET /companies/:companyId/conversations", () => {
|
|
it.todo("returns paginated conversation list");
|
|
it.todo("supports cursor query param");
|
|
});
|
|
|
|
describe("GET /conversations/:id", () => {
|
|
it.todo("returns conversation by id");
|
|
it.todo("returns 404 for non-existent conversation");
|
|
});
|
|
|
|
describe("PATCH /conversations/:id", () => {
|
|
it.todo("updates conversation fields");
|
|
});
|
|
|
|
describe("DELETE /conversations/:id", () => {
|
|
it.todo("soft-deletes and returns 204");
|
|
});
|
|
|
|
describe("POST /conversations/:id/messages", () => {
|
|
it.todo("creates a message and returns 201");
|
|
it.todo("rejects invalid role");
|
|
});
|
|
|
|
describe("GET /conversations/:id/messages", () => {
|
|
it.todo("returns paginated message list");
|
|
});
|
|
});
|
|
```
|
|
|
|
Both files use `it.todo()` which vitest marks as skipped — they run without error and serve as scaffolds for implementation tasks to fill in.
|
|
</action>
|
|
<verify>
|
|
<automated>cd /opt/nexus && pnpm vitest run server/src/__tests__/chat-service.test.ts server/src/__tests__/chat-routes.test.ts --reporter=verbose 2>&1 | tail -5</automated>
|
|
</verify>
|
|
<done>Server test stubs exist with describe/it.todo blocks covering all chatService methods and chatRoutes endpoints. Vitest runs them without error.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Create UI test stubs (ChatMarkdownMessage + ChatInput)</name>
|
|
<files>ui/src/components/ChatMarkdownMessage.test.tsx, ui/src/components/ChatInput.test.tsx</files>
|
|
<read_first>
|
|
- ui/src/components/MarkdownBody.test.tsx (reference for UI component test pattern — renderToStaticMarkup, ThemeProvider wrapper)
|
|
- ui/src/components/IssueRow.test.tsx (reference for component test with RTL if used)
|
|
</read_first>
|
|
<action>
|
|
Create `ui/src/components/ChatMarkdownMessage.test.tsx`:
|
|
|
|
```tsx
|
|
// @vitest-environment node
|
|
import { describe, it, expect } from "vitest";
|
|
|
|
describe("ChatMarkdownMessage", () => {
|
|
describe("markdown rendering (CHAT-02)", () => {
|
|
it.todo("renders plain text as paragraph");
|
|
it.todo("renders code blocks with hljs classes for syntax highlighting");
|
|
it.todo("renders GFM tables");
|
|
it.todo("renders headings, lists, and links");
|
|
});
|
|
|
|
describe("code block features (CHAT-03)", () => {
|
|
it.todo("renders language label from code fence");
|
|
it.todo("renders copy button with aria-label");
|
|
it.todo("extracts code text content for clipboard");
|
|
});
|
|
});
|
|
```
|
|
|
|
Create `ui/src/components/ChatInput.test.tsx`:
|
|
|
|
```tsx
|
|
// @vitest-environment node
|
|
import { describe, it, expect } from "vitest";
|
|
|
|
describe("ChatInput", () => {
|
|
describe("keyboard shortcuts (INPUT-07)", () => {
|
|
it.todo("calls onSend when Enter is pressed without Shift");
|
|
it.todo("inserts newline when Shift+Enter is pressed");
|
|
it.todo("clears input when Escape is pressed");
|
|
it.todo("does not send when input is empty");
|
|
});
|
|
|
|
describe("auto-resize (INPUT-01)", () => {
|
|
it.todo("textarea has max-height constraint");
|
|
});
|
|
|
|
describe("submit state", () => {
|
|
it.todo("disables send button when isSubmitting is true");
|
|
});
|
|
});
|
|
```
|
|
|
|
Both files use `it.todo()` for the same reason as the server stubs.
|
|
</action>
|
|
<verify>
|
|
<automated>cd /opt/nexus && pnpm vitest run ui/src/components/ChatMarkdownMessage.test.tsx ui/src/components/ChatInput.test.tsx --reporter=verbose 2>&1 | tail -5</automated>
|
|
</verify>
|
|
<done>UI test stubs exist with describe/it.todo blocks covering ChatMarkdownMessage rendering and ChatInput keyboard shortcuts. Vitest runs them without error.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
- All four test files exist and vitest discovers them
|
|
- `pnpm vitest run server/src/__tests__/chat-service.test.ts` exits 0
|
|
- `pnpm vitest run server/src/__tests__/chat-routes.test.ts` exits 0
|
|
- `pnpm vitest run ui/src/components/ChatMarkdownMessage.test.tsx` exits 0
|
|
- `pnpm vitest run ui/src/components/ChatInput.test.tsx` exits 0
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- Four test stub files created with describe blocks matching the requirements they cover
|
|
- Vitest runs all four files without errors (todo tests are skipped, not failed)
|
|
- Implementation plans (01-05) can reference these files in their verify commands
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/21-chat-foundation/21-00-SUMMARY.md`
|
|
</output>
|