Replace @vercel/postgres, Blob, and Upstash with lib/sql.js, MinIO object storage, and CT 102 Redis rate limits. Add Dockerfile for Dokploy deploy, homelab runbooks, Neon data-copy helper, and point CI smoke/visual at the homelab URL instead of Vercel previews. Co-authored-by: Cursor <cursoragent@cursor.com>
82 lines
2.1 KiB
JavaScript
82 lines
2.1 KiB
JavaScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
vi.mock('../../lib/sql.js', () => ({ sql: vi.fn() }));
|
|
vi.mock('../../lib/permission-middleware.js', () => ({
|
|
getUserFromRequest: vi.fn(),
|
|
}));
|
|
|
|
import { sql } from '../../lib/sql.js';
|
|
import { getUserFromRequest } from '../../lib/permission-middleware.js';
|
|
import handler from '../../pages/api/user-cards.js';
|
|
|
|
function createRes() {
|
|
const res = {
|
|
statusCode: 200,
|
|
body: null,
|
|
status(code) {
|
|
res.statusCode = code;
|
|
return res;
|
|
},
|
|
json(data) {
|
|
res.body = data;
|
|
return res;
|
|
},
|
|
};
|
|
return res;
|
|
}
|
|
|
|
describe('POST /api/user-cards', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
getUserFromRequest.mockResolvedValue({ userId: 1, email: 'a@b.c', role: 'user' });
|
|
sql.mockResolvedValue({ rows: [] });
|
|
});
|
|
|
|
it('returns 400 when quantity is not a number', async () => {
|
|
const req = {
|
|
method: 'POST',
|
|
body: { cardId: 42, quantity: 'abc' },
|
|
};
|
|
const res = createRes();
|
|
|
|
await handler(req, res);
|
|
|
|
expect(res.statusCode).toBe(400);
|
|
expect(res.body.error).toBe('Quantity must be at least 1');
|
|
expect(sql).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('returns 400 when quantity is zero', async () => {
|
|
const req = {
|
|
method: 'POST',
|
|
body: { cardId: 42, quantity: 0 },
|
|
};
|
|
const res = createRes();
|
|
|
|
await handler(req, res);
|
|
|
|
expect(res.statusCode).toBe(400);
|
|
expect(res.body.error).toBe('Quantity must be at least 1');
|
|
expect(sql).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('inserts parsed quantity for a new owned card', async () => {
|
|
sql.mockResolvedValueOnce({ rows: [] });
|
|
|
|
const req = {
|
|
method: 'POST',
|
|
body: { cardId: 42, quantity: '3', condition: 'LP', is_foil: true },
|
|
};
|
|
const res = createRes();
|
|
|
|
await handler(req, res);
|
|
|
|
expect(res.statusCode).toBe(200);
|
|
expect(sql).toHaveBeenCalledTimes(2);
|
|
const insertCall = sql.mock.calls[1];
|
|
expect(insertCall[2]).toBe(42);
|
|
expect(insertCall[3]).toBe(3);
|
|
expect(insertCall[4]).toBe('LP');
|
|
expect(insertCall[5]).toBe(true);
|
|
});
|
|
});
|