Milestones 0 and 1: foundation and money in (20261007.10)
Foundation: single arm64 container (Hono + SQLite via Drizzle, React + Vite), local sign-in with long-lived sessions and passkeys, roles (read-only, full access, admin), nightly and pre-upgrade backups, light and dark themes, date-based version numbers, and the ideas lightbulb behind IDEAS_ENABLED. Money in: accounts with balances, CSV import with bank detection, a column mapping pop-up for unknown layouts, duplicate checks, transfer pairing and undo; one-tap review with learned rules; activity with search, splits, tags, notes and cash entries. Statement files are blocked from commits by a pre-commit hook; tests use synthetic fixtures only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
49
server/test/ideas.test.ts
Normal file
49
server/test/ideas.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import { Client, makeApp, setUpAdmin, type TestApp } from './helpers';
|
||||
|
||||
let t: TestApp;
|
||||
afterEach(() => t.cleanup());
|
||||
|
||||
describe('ideas box', () => {
|
||||
it('does not exist when IDEAS_ENABLED is off', async () => {
|
||||
t = await makeApp({ IDEAS_ENABLED: 'false' });
|
||||
const admin = await setUpAdmin(t);
|
||||
expect((await admin.get('/api/v1/config')).json.ideasEnabled).toBe(false);
|
||||
expect((await admin.post('/api/v1/ideas', { title: 'Hello' })).status).toBe(404);
|
||||
expect((await admin.get('/api/v1/ideas')).status).toBe(404);
|
||||
});
|
||||
|
||||
it('takes ideas from anyone signed in and lets an admin triage and export them', async () => {
|
||||
t = await makeApp({ IDEAS_ENABLED: 'true' });
|
||||
const admin = await setUpAdmin(t);
|
||||
const added = await admin.post('/api/v1/people', { displayName: 'Sam', username: 'sam', access: 'read', password: 'sams-password' });
|
||||
expect(added.status).toBe(200);
|
||||
const sam = new Client(t);
|
||||
await sam.post('/api/v1/auth/login', { username: 'sam', password: 'sams-password' });
|
||||
|
||||
expect((await new Client(t).post('/api/v1/ideas', { title: 'Anonymous' })).status).toBe(401);
|
||||
expect((await sam.post('/api/v1/ideas', { title: ' ' })).status).toBe(400);
|
||||
|
||||
const idea = await sam.post('/api/v1/ideas', {
|
||||
title: 'Show a coffee total',
|
||||
description: 'How much did we spend on coffee this year?',
|
||||
path: '/insights',
|
||||
});
|
||||
expect(idea.status).toBe(200);
|
||||
expect(idea.json).toMatchObject({ status: 'new', author: 'Sam', appVersion: '20261007.01', path: '/insights' });
|
||||
|
||||
expect((await sam.get('/api/v1/ideas')).status).toBe(403);
|
||||
const list = (await admin.get('/api/v1/ideas')).json;
|
||||
expect(list).toHaveLength(1);
|
||||
|
||||
const planned = await admin.patch(`/api/v1/ideas/${idea.json.id}`, { status: 'planned', adminNote: 'Milestone 2' });
|
||||
expect(planned.json).toMatchObject({ status: 'planned', adminNote: 'Milestone 2' });
|
||||
expect((await admin.get('/api/v1/ideas?status=new')).json).toHaveLength(0);
|
||||
|
||||
const md = await admin.get('/api/v1/ideas/export.md');
|
||||
expect(md.headers.get('content-type')).toContain('text/markdown');
|
||||
expect(md.text).toContain(`## #${idea.json.id}: Show a coffee total`);
|
||||
expect(md.text).toContain('- Status: planned');
|
||||
expect(md.text).toContain('How much did we spend on coffee this year?');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user