feat: Sprint 1 slice 1 — API signup + web signup/chat shell
Mirror to GitHub / mirror (push) Canceled after 0s

- api: Fastify POST /auth/signup (validation, scrypt hash, in-memory store)
- web: React+Vite SignUp form and chat shell with dev proxy
- 35 tests green (api 5, web 2, e2e 28)
This commit is contained in:
SinachPat
2026-08-15 19:53:13 +01:00
parent 2f53b1901b
commit 9733ba6e3c
21 changed files with 1706 additions and 14 deletions
+61
View File
@@ -0,0 +1,61 @@
import { describe, it, expect } from 'vitest';
import { buildApp } from '../src/app.ts';
describe('POST /auth/signup', () => {
it('creates a user and returns a session token', async () => {
const app = await buildApp();
const res = await app.inject({
method: 'POST',
url: '/auth/signup',
payload: { email: 'a@example.com', password: 'password123' },
});
expect(res.statusCode).toBe(201);
const body = res.json();
expect(body.user.email).toBe('a@example.com');
expect(body.user.id).toBeTruthy();
expect(body.sessionToken).toBeTruthy();
});
it('rejects an invalid email with 400', async () => {
const app = await buildApp();
const res = await app.inject({
method: 'POST',
url: '/auth/signup',
payload: { email: 'not-an-email', password: 'password123' },
});
expect(res.statusCode).toBe(400);
});
it('rejects a short password with 400', async () => {
const app = await buildApp();
const res = await app.inject({
method: 'POST',
url: '/auth/signup',
payload: { email: 'a@example.com', password: 'short' },
});
expect(res.statusCode).toBe(400);
});
it('rejects a duplicate email with 409', async () => {
const app = await buildApp();
const payload = { email: 'a@example.com', password: 'password123' };
await app.inject({ method: 'POST', url: '/auth/signup', payload });
const res = await app.inject({ method: 'POST', url: '/auth/signup', payload });
expect(res.statusCode).toBe(409);
});
it('does not return the password hash', async () => {
const app = await buildApp();
const res = await app.inject({
method: 'POST',
url: '/auth/signup',
payload: { email: 'a@example.com', password: 'password123' },
});
expect(res.json().user.passwordHash).toBeUndefined();
});
});