checkpoint: pre-split branch cleanup

This commit is contained in:
ZenchantLive 2026-03-03 16:43:42 -08:00
parent 4c2ae2e5b7
commit b5db7a7753
276 changed files with 35912 additions and 60119 deletions

View file

@ -1,38 +1,38 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { GET as eventsGet } from '../../src/app/api/events/route';
import { getIssuesWatchManager } from '../../src/lib/watcher';
test.afterEach(async () => {
await getIssuesWatchManager().stopAll();
});
test.after(async () => {
await getIssuesWatchManager().stopAll();
});
test('events route returns SSE response with expected headers', async () => {
const response = await eventsGet(new Request('http://localhost/api/events?projectRoot=C:/Repo/Test'));
assert.equal(response.status, 200);
assert.equal(response.headers.get('content-type')?.includes('text/event-stream'), true);
assert.equal(response.headers.get('cache-control')?.includes('no-cache'), true);
const reader = response.body?.getReader();
if (reader) {
await reader.cancel();
}
});
test('events route emits initial connected frame', async () => {
const response = await eventsGet(new Request('http://localhost/api/events?projectRoot=C:/Repo/Test'));
const reader = response.body?.getReader();
assert.equal(Boolean(reader), true);
const first = await reader!.read();
const chunk = new TextDecoder().decode(first.value);
assert.equal(chunk.includes(': connected'), true);
await reader!.cancel();
});
import test from 'node:test';
import assert from 'node:assert/strict';
import { GET as eventsGet } from '../../src/app/api/events/route';
import { getIssuesWatchManager } from '../../src/lib/watcher';
test.afterEach(async () => {
await getIssuesWatchManager().stopAll();
});
test.after(async () => {
await getIssuesWatchManager().stopAll();
});
test('events route returns SSE response with expected headers', async () => {
const response = await eventsGet(new Request('http://localhost/api/events?projectRoot=C:/Repo/Test'));
assert.equal(response.status, 200);
assert.equal(response.headers.get('content-type')?.includes('text/event-stream'), true);
assert.equal(response.headers.get('cache-control')?.includes('no-cache'), true);
const reader = response.body?.getReader();
if (reader) {
await reader.cancel();
}
});
test('events route emits initial connected frame', async () => {
const response = await eventsGet(new Request('http://localhost/api/events?projectRoot=C:/Repo/Test'));
const reader = response.body?.getReader();
assert.equal(Boolean(reader), true);
const first = await reader!.read();
const chunk = new TextDecoder().decode(first.value);
assert.equal(chunk.includes(': connected'), true);
await reader!.cancel();
});

View file

@ -52,4 +52,4 @@ test('comment route returns 400 for missing comment text', async () => {
const data = await readJson(response);
assert.equal(data.ok, false);
assert.equal(typeof data.error.message, 'string');
});
});

View file

@ -1,109 +1,109 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { DELETE, GET, POST } from '../../src/app/api/projects/route';
async function withTempUserProfile(run: (userProfile: string) => Promise<void>): Promise<void> {
const previous = process.env.USERPROFILE;
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'beadboard-api-'));
process.env.USERPROFILE = tempDir;
try {
await run(tempDir);
} finally {
if (previous === undefined) {
delete process.env.USERPROFILE;
} else {
process.env.USERPROFILE = previous;
}
await fs.rm(tempDir, { recursive: true, force: true });
}
}
async function readJson(response: Response): Promise<unknown> {
return response.json();
}
test('GET /api/projects returns empty list initially', async () => {
await withTempUserProfile(async () => {
const response = await GET();
assert.equal(response.status, 200);
const body = (await readJson(response)) as { projects: unknown[] };
assert.deepEqual(body.projects, []);
});
});
test('POST /api/projects validates payload and path', async () => {
await withTempUserProfile(async () => {
const missing = await POST(new Request('http://localhost/api/projects', { method: 'POST', body: '{}' }));
assert.equal(missing.status, 400);
const missingBody = (await readJson(missing)) as { error: string };
assert.match(missingBody.error, /path/i);
const invalidPath = await POST(
new Request('http://localhost/api/projects', {
method: 'POST',
body: JSON.stringify({ path: '/tmp/project' }),
headers: { 'content-type': 'application/json' },
}),
);
assert.equal(invalidPath.status, 400);
});
});
test('POST deduplicates and GET returns normalized path', async () => {
await withTempUserProfile(async () => {
const first = await POST(
new Request('http://localhost/api/projects', {
method: 'POST',
body: JSON.stringify({ path: 'c:/Users/Zenchant/codex/beadboard/' }),
headers: { 'content-type': 'application/json' },
}),
);
assert.equal(first.status, 201);
const dup = await POST(
new Request('http://localhost/api/projects', {
method: 'POST',
body: JSON.stringify({ path: 'C:\\users\\zenchant\\codex\\beadboard' }),
headers: { 'content-type': 'application/json' },
}),
);
assert.equal(dup.status, 200);
const list = await GET();
const body = (await readJson(list)) as { projects: Array<{ path: string }> };
assert.deepEqual(body.projects, [{ path: 'C:/Users/Zenchant/codex/beadboard' }]);
});
});
test('DELETE /api/projects removes by normalized path', async () => {
await withTempUserProfile(async () => {
await POST(
new Request('http://localhost/api/projects', {
method: 'POST',
body: JSON.stringify({ path: 'D:/Repos/One' }),
headers: { 'content-type': 'application/json' },
}),
);
const removed = await DELETE(
new Request('http://localhost/api/projects', {
method: 'DELETE',
body: JSON.stringify({ path: 'd:\\repos\\one\\' }),
headers: { 'content-type': 'application/json' },
}),
);
assert.equal(removed.status, 200);
const list = await GET();
const body = (await readJson(list)) as { projects: unknown[] };
assert.deepEqual(body.projects, []);
});
});
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { DELETE, GET, POST } from '../../src/app/api/projects/route';
async function withTempUserProfile(run: (userProfile: string) => Promise<void>): Promise<void> {
const previous = process.env.USERPROFILE;
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'beadboard-api-'));
process.env.USERPROFILE = tempDir;
try {
await run(tempDir);
} finally {
if (previous === undefined) {
delete process.env.USERPROFILE;
} else {
process.env.USERPROFILE = previous;
}
await fs.rm(tempDir, { recursive: true, force: true });
}
}
async function readJson(response: Response): Promise<unknown> {
return response.json();
}
test('GET /api/projects returns empty list initially', async () => {
await withTempUserProfile(async () => {
const response = await GET();
assert.equal(response.status, 200);
const body = (await readJson(response)) as { projects: unknown[] };
assert.deepEqual(body.projects, []);
});
});
test('POST /api/projects validates payload and path', async () => {
await withTempUserProfile(async () => {
const missing = await POST(new Request('http://localhost/api/projects', { method: 'POST', body: '{}' }));
assert.equal(missing.status, 400);
const missingBody = (await readJson(missing)) as { error: string };
assert.match(missingBody.error, /path/i);
const invalidPath = await POST(
new Request('http://localhost/api/projects', {
method: 'POST',
body: JSON.stringify({ path: '/tmp/project' }),
headers: { 'content-type': 'application/json' },
}),
);
assert.equal(invalidPath.status, 400);
});
});
test('POST deduplicates and GET returns normalized path', async () => {
await withTempUserProfile(async () => {
const first = await POST(
new Request('http://localhost/api/projects', {
method: 'POST',
body: JSON.stringify({ path: 'c:/Users/Zenchant/codex/beadboard/' }),
headers: { 'content-type': 'application/json' },
}),
);
assert.equal(first.status, 201);
const dup = await POST(
new Request('http://localhost/api/projects', {
method: 'POST',
body: JSON.stringify({ path: 'C:\\users\\zenchant\\codex\\beadboard' }),
headers: { 'content-type': 'application/json' },
}),
);
assert.equal(dup.status, 200);
const list = await GET();
const body = (await readJson(list)) as { projects: Array<{ path: string }> };
assert.deepEqual(body.projects, [{ path: 'C:/Users/Zenchant/codex/beadboard' }]);
});
});
test('DELETE /api/projects removes by normalized path', async () => {
await withTempUserProfile(async () => {
await POST(
new Request('http://localhost/api/projects', {
method: 'POST',
body: JSON.stringify({ path: 'D:/Repos/One' }),
headers: { 'content-type': 'application/json' },
}),
);
const removed = await DELETE(
new Request('http://localhost/api/projects', {
method: 'DELETE',
body: JSON.stringify({ path: 'd:\\repos\\one\\' }),
headers: { 'content-type': 'application/json' },
}),
);
assert.equal(removed.status, 200);
const list = await GET();
const body = (await readJson(list)) as { projects: unknown[] };
assert.deepEqual(body.projects, []);
});
});

View file

@ -1,21 +1,21 @@
// @ts-ignore
import { expect, test, describe, mock } from 'bun:test';
import { GET } from '../../../src/app/api/swarm/archetypes/route';
// Mock the dependency
mock.module('../../../src/lib/server/beads-fs', () => ({
getArchetypes: async () => [
{ id: 'test-arch', name: 'Test', isBuiltIn: true }
]
}));
describe('/api/swarm/archetypes GET', () => {
test('returns 200 and a JSON array of archetypes', async () => {
const response = await GET();
expect(response.status).toBe(200);
const data = await response.json();
expect(Array.isArray(data)).toBe(true);
expect(data[0].id).toBe('test-arch');
});
});
// @ts-ignore
import { expect, test, describe, mock } from 'bun:test';
import { GET } from '../../../src/app/api/swarm/archetypes/route';
// Mock the dependency
mock.module('../../../src/lib/server/beads-fs', () => ({
getArchetypes: async () => [
{ id: 'test-arch', name: 'Test', isBuiltIn: true }
]
}));
describe('/api/swarm/archetypes GET', () => {
test('returns 200 and a JSON array of archetypes', async () => {
const response = await GET();
expect(response.status).toBe(200);
const data = await response.json();
expect(Array.isArray(data)).toBe(true);
expect(data[0].id).toBe('test-arch');
});
});