mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
test(session-content): add integration tests for session CRUD operations
Solution-ID: SOL-1735386000003 Issue-ID: ISS-1766921318981-17 Task-ID: T2
This commit is contained in:
205
ccw/tests/integration/session-content.test.ts
Normal file
205
ccw/tests/integration/session-content.test.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
* Integration tests for session-manager: read/write/update session content.
|
||||
*
|
||||
* Notes:
|
||||
* - Targets the runtime implementation shipped in `ccw/dist`.
|
||||
* - Uses a real temporary directory as the project root.
|
||||
*/
|
||||
|
||||
import { after, afterEach, before, beforeEach, describe, it } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
|
||||
const originalCwd = process.cwd();
|
||||
const projectRoot = mkdtempSync(join(tmpdir(), 'ccw-session-content-'));
|
||||
|
||||
const sessionManagerUrl = new URL('../../dist/tools/session-manager.js', import.meta.url);
|
||||
sessionManagerUrl.searchParams.set('t', String(Date.now()));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let sessionManager: any;
|
||||
|
||||
function workflowPath(...parts: string[]): string {
|
||||
return join(projectRoot, '.workflow', ...parts);
|
||||
}
|
||||
|
||||
function readJson(filePath: string): any {
|
||||
return JSON.parse(readFileSync(filePath, 'utf8'));
|
||||
}
|
||||
|
||||
describe('session-manager integration: read/write/update', async () => {
|
||||
before(async () => {
|
||||
process.chdir(projectRoot);
|
||||
sessionManager = await import(sessionManagerUrl.href);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(workflowPath(), { recursive: true, force: true });
|
||||
});
|
||||
|
||||
after(() => {
|
||||
process.chdir(originalCwd);
|
||||
rmSync(projectRoot, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let lastInit: any;
|
||||
let sessionId = 'WFS-content-1';
|
||||
|
||||
beforeEach(async () => {
|
||||
sessionId = `WFS-content-${Date.now()}`;
|
||||
lastInit = await sessionManager.handler({
|
||||
operation: 'init',
|
||||
session_id: sessionId,
|
||||
metadata: { type: 'workflow', description: 'Content test session' },
|
||||
});
|
||||
assert.equal(lastInit.success, true);
|
||||
});
|
||||
|
||||
it('reads session metadata and returns structured JSON', async () => {
|
||||
const res = await sessionManager.handler({
|
||||
operation: 'read',
|
||||
session_id: sessionId,
|
||||
content_type: 'session',
|
||||
});
|
||||
|
||||
assert.equal(res.success, true);
|
||||
assert.equal(res.result.session_id, sessionId);
|
||||
assert.equal(res.result.is_json, true);
|
||||
assert.equal(res.result.content.session_id, sessionId);
|
||||
assert.equal(res.result.location, 'active');
|
||||
});
|
||||
|
||||
it('writes and reads plan content (IMPL_PLAN.md)', async () => {
|
||||
const planText = '# Plan\n\nDo the thing.\n';
|
||||
const write = await sessionManager.handler({
|
||||
operation: 'write',
|
||||
session_id: sessionId,
|
||||
content_type: 'plan',
|
||||
content: planText,
|
||||
});
|
||||
assert.equal(write.success, true);
|
||||
|
||||
const read = await sessionManager.handler({
|
||||
operation: 'read',
|
||||
session_id: sessionId,
|
||||
content_type: 'plan',
|
||||
});
|
||||
assert.equal(read.success, true);
|
||||
assert.equal(read.result.is_json, false);
|
||||
assert.equal(read.result.content, planText);
|
||||
assert.equal(existsSync(join(lastInit.result.path, 'IMPL_PLAN.md')), true);
|
||||
});
|
||||
|
||||
it('writes and reads task JSON content', async () => {
|
||||
const taskId = 'T1';
|
||||
const task = { id: taskId, title: 'My Task', status: 'pending' };
|
||||
|
||||
const write = await sessionManager.handler({
|
||||
operation: 'write',
|
||||
session_id: sessionId,
|
||||
content_type: 'task',
|
||||
path_params: { task_id: taskId },
|
||||
content: task,
|
||||
});
|
||||
assert.equal(write.success, true);
|
||||
assert.deepEqual(write.result.written_content, task);
|
||||
|
||||
const read = await sessionManager.handler({
|
||||
operation: 'read',
|
||||
session_id: sessionId,
|
||||
content_type: 'task',
|
||||
path_params: { task_id: taskId },
|
||||
});
|
||||
assert.equal(read.success, true);
|
||||
assert.equal(read.result.is_json, true);
|
||||
assert.deepEqual(read.result.content, task);
|
||||
});
|
||||
|
||||
it('writes and reads summary content', async () => {
|
||||
const taskId = 'T2';
|
||||
const summary = 'Summary text\n';
|
||||
|
||||
const write = await sessionManager.handler({
|
||||
operation: 'write',
|
||||
session_id: sessionId,
|
||||
content_type: 'summary',
|
||||
path_params: { task_id: taskId },
|
||||
content: summary,
|
||||
});
|
||||
assert.equal(write.success, true);
|
||||
assert.equal(write.result.written_content, summary);
|
||||
|
||||
const read = await sessionManager.handler({
|
||||
operation: 'read',
|
||||
session_id: sessionId,
|
||||
content_type: 'summary',
|
||||
path_params: { task_id: taskId },
|
||||
});
|
||||
assert.equal(read.success, true);
|
||||
assert.equal(read.result.is_json, false);
|
||||
assert.equal(read.result.content, summary);
|
||||
});
|
||||
|
||||
it('updates existing JSON content via shallow merge', async () => {
|
||||
const taskId = 'T3';
|
||||
const write = await sessionManager.handler({
|
||||
operation: 'write',
|
||||
session_id: sessionId,
|
||||
content_type: 'task',
|
||||
path_params: { task_id: taskId },
|
||||
content: { id: taskId, title: 'Merge target', status: 'pending' },
|
||||
});
|
||||
assert.equal(write.success, true);
|
||||
|
||||
const update = await sessionManager.handler({
|
||||
operation: 'update',
|
||||
session_id: sessionId,
|
||||
content_type: 'task',
|
||||
path_params: { task_id: taskId },
|
||||
content: { status: 'completed', extra: { ok: true } },
|
||||
});
|
||||
assert.equal(update.success, true);
|
||||
assert.deepEqual(update.result.fields_updated, ['status', 'extra']);
|
||||
|
||||
const filePath = join(lastInit.result.path, '.task', `${taskId}.json`);
|
||||
const onDisk = readJson(filePath);
|
||||
assert.equal(onDisk.status, 'completed');
|
||||
assert.equal(onDisk.title, 'Merge target');
|
||||
assert.deepEqual(onDisk.extra, { ok: true });
|
||||
});
|
||||
|
||||
it('returns clear errors for non-existent sessions', async () => {
|
||||
const res = await sessionManager.handler({
|
||||
operation: 'read',
|
||||
session_id: 'WFS-does-not-exist',
|
||||
content_type: 'plan',
|
||||
});
|
||||
assert.equal(res.success, false);
|
||||
assert.ok(res.error.includes('not found'));
|
||||
});
|
||||
|
||||
it('rejects invalid content types via schema validation', async () => {
|
||||
const res = await sessionManager.handler({
|
||||
operation: 'read',
|
||||
session_id: sessionId,
|
||||
content_type: 'not-a-type',
|
||||
});
|
||||
assert.equal(res.success, false);
|
||||
assert.ok(res.error.includes('Invalid params'));
|
||||
});
|
||||
|
||||
it('rejects path traversal characters in path_params', async () => {
|
||||
const res = await sessionManager.handler({
|
||||
operation: 'read',
|
||||
session_id: sessionId,
|
||||
content_type: 'task',
|
||||
path_params: { task_id: '../evil' },
|
||||
});
|
||||
assert.equal(res.success, false);
|
||||
assert.ok(res.error.includes('path traversal'));
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user