Skip to content

Commit e3c609f

Browse files
Ark0Nclaude
andcommitted
test: add coverage for lastUsedCase partial update and strict schema rejection
Tests that partial PUT /api/settings with just lastUsedCase works correctly and that including modelConfig triggers strict Zod schema rejection (the bug fixed in #49). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 52e774f commit e3c609f

2 files changed

Lines changed: 32 additions & 2 deletions

File tree

CLAUDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
66

77
| Task | Command |
88
|------|---------|
9-
| Dev server | `npx tsx src/index.ts web` |
9+
| Dev server | `npm run dev` (or `npx tsx src/index.ts web`) |
1010
| Type check | `tsc --noEmit` |
1111
| Lint | `npm run lint` (fix: `npm run lint:fix`) |
1212
| Format | `npm run format` (check: `npm run format:check`) |
@@ -78,7 +78,7 @@ Codeman is a Claude Code session manager with web interface and autonomous Ralph
7878
| Production start | `npm run start` |
7979
| Production logs | `journalctl --user -u codeman-web -f` |
8080
81-
**CI**: `.github/workflows/ci.yml` runs `typecheck`, `lint`, `format:check` on push to master (Node 22). Tests excluded (they spawn tmux).
81+
**CI**: `.github/workflows/ci.yml` runs `typecheck`, `lint`, `format:check` on push to master/main and on PRs (Node 22). Tests excluded (they spawn tmux).
8282
8383
**Code style**: Prettier (`singleQuote: true`, `printWidth: 120`, `trailingComma: "es5"`). ESLint flat config (`eslint.config.js`) allows `no-console`, warns on `@typescript-eslint/no-explicit-any`. Ignores: `app.js`, `scripts/**/*.mjs`, `src/web/public/vendor/**`, `tools/**`, `remotion/**`.
8484

test/routes/system-routes.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,36 @@ describe('system-routes', () => {
366366
expect(body.success).toBe(false);
367367
});
368368

369+
it('saves lastUsedCase as partial update without overwriting other settings', async () => {
370+
mockedReadFile.mockResolvedValue(
371+
JSON.stringify({ showCost: true, showTokenCount: false, subagentTrackingEnabled: true }) as never
372+
);
373+
374+
const res = await harness.app.inject({
375+
method: 'PUT',
376+
url: '/api/settings',
377+
payload: { lastUsedCase: 'my-test-case' },
378+
});
379+
expect(res.statusCode).toBe(200);
380+
expect(JSON.parse(res.body).success).toBe(true);
381+
382+
const writtenContent = JSON.parse(mockedWriteFile.mock.calls[0][1] as string);
383+
expect(writtenContent.lastUsedCase).toBe('my-test-case');
384+
expect(writtenContent.showCost).toBe(true);
385+
expect(writtenContent.showTokenCount).toBe(false);
386+
expect(writtenContent.subagentTrackingEnabled).toBe(true);
387+
});
388+
389+
it('rejects settings with modelConfig (strict schema prevents full-object PUT)', async () => {
390+
const res = await harness.app.inject({
391+
method: 'PUT',
392+
url: '/api/settings',
393+
payload: { lastUsedCase: 'test', modelConfig: { model: 'something' } },
394+
});
395+
// Fastify rejects unknown fields at schema validation level (400) before handler runs
396+
expect(res.statusCode).toBe(400);
397+
});
398+
369399
it('rejects non-object body', async () => {
370400
const res = await harness.app.inject({
371401
method: 'PUT',

0 commit comments

Comments
 (0)