Scaffold project and implement config, DB schema/queries
- Set up package.json (ESM, scripts), tsconfig.json, vitest.config.ts - Install runtime and dev dependencies - Add CLAUDE.md with architecture notes and code quality rules - Config module with env var parsing and JWT_SECRET validation - DB schema: users + files tables with FK cascade - DB queries: createUser, getUserBy*, createFile, getFileById, getFilesByUserId, deleteFile - Tests for config, db/users, db/files (15 tests passing) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
77
tests/unit/db-files.test.ts
Normal file
77
tests/unit/db-files.test.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { initDb } from '../../src/db/schema.ts';
|
||||
import { createUser } from '../../src/db/users.ts';
|
||||
import { createFile, getFileById, getFilesByUserId, deleteFile } from '../../src/db/files.ts';
|
||||
import type Database from 'better-sqlite3';
|
||||
|
||||
describe('db/files', () => {
|
||||
let db: Database.Database;
|
||||
let userId: number;
|
||||
|
||||
beforeEach(() => {
|
||||
db = initDb(':memory:');
|
||||
const user = createUser(db, { username: 'alice', passwordHash: 'hash' });
|
||||
userId = user.id;
|
||||
});
|
||||
|
||||
function makeFile(overrides: Partial<Parameters<typeof createFile>[1]> = {}) {
|
||||
return createFile(db, {
|
||||
id: 'file1',
|
||||
userId,
|
||||
originalName: 'photo.jpg',
|
||||
mimeType: 'image/jpeg',
|
||||
size: 1024,
|
||||
storedName: 'file1.jpg',
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
||||
it('creates a file and retrieves by id', () => {
|
||||
const file = makeFile();
|
||||
expect(file.id).toBe('file1');
|
||||
expect(file.user_id).toBe(userId);
|
||||
expect(file.original_name).toBe('photo.jpg');
|
||||
expect(file.mime_type).toBe('image/jpeg');
|
||||
expect(file.size).toBe(1024);
|
||||
expect(file.stored_name).toBe('file1.jpg');
|
||||
|
||||
const found = getFileById(db, 'file1');
|
||||
expect(found).toEqual(file);
|
||||
});
|
||||
|
||||
it('returns undefined for unknown file id', () => {
|
||||
expect(getFileById(db, 'nope')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('lists files by user ordered newest first', () => {
|
||||
makeFile({ id: 'a', storedName: 'a.jpg' });
|
||||
makeFile({ id: 'b', storedName: 'b.jpg' });
|
||||
const files = getFilesByUserId(db, userId);
|
||||
expect(files).toHaveLength(2);
|
||||
expect(files.map((f) => f.id)).toContain('a');
|
||||
});
|
||||
|
||||
it('returns empty array for user with no files', () => {
|
||||
expect(getFilesByUserId(db, userId)).toEqual([]);
|
||||
});
|
||||
|
||||
it('deletes a file by id and userId', () => {
|
||||
makeFile();
|
||||
const deleted = deleteFile(db, 'file1', userId);
|
||||
expect(deleted).toBe(true);
|
||||
expect(getFileById(db, 'file1')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns false when deleting a file belonging to another user', () => {
|
||||
makeFile();
|
||||
const deleted = deleteFile(db, 'file1', 999);
|
||||
expect(deleted).toBe(false);
|
||||
expect(getFileById(db, 'file1')).toBeDefined();
|
||||
});
|
||||
|
||||
it('cascades delete when user is deleted', () => {
|
||||
makeFile();
|
||||
db.prepare('DELETE FROM users WHERE id = ?').run(userId);
|
||||
expect(getFileById(db, 'file1')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user