ee2cf19b74
New file tests/experimentDailyStatuses.test.js — 9 tests covering GET /api/experiments/:id/daily-statuses: - 404 on unknown experiment - all statuses returned when no filter - analysisOnly=true keeps only non-null analysis_summary records - analysisOnly=true returns [] when none have analysis_summary - the exact null/object mix that caused the production 500 now passes - analysisOnly=false (explicit) returns all records - ?date= param is forwarded to Prisma correctly - empty-array response on zero statuses - returned records include expected fields Fixed tests/calendar.test.js: existing tests were silently broken by the cache added in the perf commit. Both test files now mock lib/cache to always miss, so each test exercises the real route logic in isolation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
132 lines
5.1 KiB
JavaScript
132 lines
5.1 KiB
JavaScript
const request = require('supertest');
|
|
|
|
jest.mock('../src/lib/prisma', () => require('./__mocks__/prisma'));
|
|
jest.mock('../src/lib/cache', () => ({ get: jest.fn(() => undefined), set: jest.fn(), del: jest.fn(), delByPrefix: jest.fn() }));
|
|
|
|
const prisma = require('../src/lib/prisma');
|
|
const app = require('../src/app');
|
|
|
|
const EXP_ID = 'aaaaaaaa-0000-0000-0000-000000000001';
|
|
|
|
const EXPERIMENT = {
|
|
id: EXP_ID,
|
|
title: 'Calendar Test Study',
|
|
created_at: new Date('2024-01-01').toISOString(),
|
|
};
|
|
|
|
// Helper: build a DailyStatus record
|
|
function makeStatus({ date, vitals = null, customFields = null }) {
|
|
return {
|
|
date: new Date(date),
|
|
experiment_description: null,
|
|
vitals,
|
|
treatment: null,
|
|
notes: null,
|
|
custom_fields: customFields,
|
|
};
|
|
}
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
describe('GET /api/experiments/:id/calendar', () => {
|
|
it('returns 404 when experiment does not exist', async () => {
|
|
prisma.experiment.findUnique.mockResolvedValue(null);
|
|
const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar?field=vitals`);
|
|
expect(res.status).toBe(404);
|
|
expect(res.body.error).toMatch(/not found/i);
|
|
});
|
|
|
|
it('returns empty days object when no statuses exist', async () => {
|
|
prisma.experiment.findUnique.mockResolvedValue(EXPERIMENT);
|
|
prisma.dailyStatus.findMany.mockResolvedValue([]);
|
|
const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar?field=vitals`);
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.days).toEqual({});
|
|
expect(res.body.field).toBe('vitals');
|
|
});
|
|
|
|
it('counts subjects with non-empty builtin field per day', async () => {
|
|
prisma.experiment.findUnique.mockResolvedValue(EXPERIMENT);
|
|
prisma.dailyStatus.findMany.mockResolvedValue([
|
|
makeStatus({ date: '2026-03-10', vitals: '120/80' }),
|
|
makeStatus({ date: '2026-03-10', vitals: '110/70' }),
|
|
makeStatus({ date: '2026-03-11', vitals: '115/75' }),
|
|
makeStatus({ date: '2026-03-12', vitals: null }), // null — not counted
|
|
makeStatus({ date: '2026-03-12', vitals: '' }), // empty — not counted
|
|
]);
|
|
|
|
const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar?field=vitals`);
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.days).toEqual({
|
|
'2026-03-10': 2,
|
|
'2026-03-11': 1,
|
|
});
|
|
});
|
|
|
|
it('counts subjects with non-empty custom field per day', async () => {
|
|
const FIELD_ID = 'cccccccc-0000-0000-0000-000000000099';
|
|
prisma.experiment.findUnique.mockResolvedValue(EXPERIMENT);
|
|
prisma.dailyStatus.findMany.mockResolvedValue([
|
|
makeStatus({ date: '2026-04-01', customFields: { [FIELD_ID]: '320' } }),
|
|
makeStatus({ date: '2026-04-01', customFields: { [FIELD_ID]: '310' } }),
|
|
makeStatus({ date: '2026-04-01', customFields: { [FIELD_ID]: null } }), // null — not counted
|
|
makeStatus({ date: '2026-04-02', customFields: { [FIELD_ID]: '315' } }),
|
|
]);
|
|
|
|
const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar?field=${FIELD_ID}`);
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.days).toEqual({
|
|
'2026-04-01': 2,
|
|
'2026-04-02': 1,
|
|
});
|
|
});
|
|
|
|
it('ignores whitespace-only custom field values', async () => {
|
|
const FIELD_ID = 'dddddddd-0000-0000-0000-000000000099';
|
|
prisma.experiment.findUnique.mockResolvedValue(EXPERIMENT);
|
|
prisma.dailyStatus.findMany.mockResolvedValue([
|
|
makeStatus({ date: '2026-04-05', customFields: { [FIELD_ID]: ' ' } }),
|
|
makeStatus({ date: '2026-04-05', customFields: { [FIELD_ID]: '280' } }),
|
|
]);
|
|
|
|
const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar?field=${FIELD_ID}`);
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.days).toEqual({ '2026-04-05': 1 });
|
|
});
|
|
|
|
it('returns days from multiple months correctly', async () => {
|
|
prisma.experiment.findUnique.mockResolvedValue(EXPERIMENT);
|
|
prisma.dailyStatus.findMany.mockResolvedValue([
|
|
makeStatus({ date: '2026-02-15', vitals: '100/60' }),
|
|
makeStatus({ date: '2026-03-01', vitals: '105/65' }),
|
|
makeStatus({ date: '2026-03-01', vitals: '108/68' }),
|
|
]);
|
|
|
|
const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar?field=vitals`);
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.days['2026-02-15']).toBe(1);
|
|
expect(res.body.days['2026-03-01']).toBe(2);
|
|
});
|
|
|
|
it('returns field key in response body', async () => {
|
|
prisma.experiment.findUnique.mockResolvedValue(EXPERIMENT);
|
|
prisma.dailyStatus.findMany.mockResolvedValue([]);
|
|
const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar?field=treatment`);
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.field).toBe('treatment');
|
|
});
|
|
|
|
it('handles missing field param gracefully (empty days)', async () => {
|
|
prisma.experiment.findUnique.mockResolvedValue(EXPERIMENT);
|
|
prisma.dailyStatus.findMany.mockResolvedValue([
|
|
makeStatus({ date: '2026-04-01', vitals: 'present' }),
|
|
]);
|
|
// No ?field= → all values undefined, nothing counted
|
|
const res = await request(app).get(`/api/experiments/${EXP_ID}/calendar`);
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.days).toEqual({});
|
|
});
|
|
});
|