Files
maternal-app/maternal-app/maternal-app-backend/test/children.e2e-spec.ts
Andrei 0531573d3f chore: Migrate ESLint to v9 flat config format
Created new eslint.config.mjs with flat config:
- Migrated from .eslintrc.js to eslint.config.mjs
- Added globals package for Node.js and Jest globals
- Configured TypeScript parser and plugins
- Maintained all existing rules and Prettier integration

ESLint now running successfully with v9 flat config.

Note: 39 unused variable warnings found - these are minor code
quality issues that can be addressed in a separate cleanup PR.

🤖 Generated with Claude Code
2025-10-02 15:49:58 +00:00

318 lines
9.7 KiB
TypeScript

import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';
import { DataSource } from 'typeorm';
describe('Children (e2e)', () => {
let app: INestApplication;
let dataSource: DataSource;
// Test user and auth
const testUser = {
email: `test-children-${Date.now()}@example.com`,
password: 'SecurePass123!',
name: 'Test Parent',
deviceInfo: {
deviceId: 'test-device-children',
platform: 'ios',
model: 'iPhone 14',
osVersion: '17.0',
},
};
let accessToken: string;
let userId: string;
let familyId: string;
let childId: string;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
);
await app.init();
dataSource = app.get(DataSource);
// Register user and get auth token
const registerRes = await request(app.getHttpServer())
.post('/api/v1/auth/register')
.send(testUser);
accessToken = registerRes.body.data.tokens.accessToken;
userId = registerRes.body.data.user.id;
familyId = registerRes.body.data.family.id;
});
afterAll(async () => {
// Cleanup
if (childId) {
await dataSource.query('DELETE FROM children WHERE id = $1', [childId]);
}
if (userId) {
await dataSource.query('DELETE FROM refresh_tokens WHERE user_id = $1', [
userId,
]);
await dataSource.query('DELETE FROM device_registry WHERE user_id = $1', [
userId,
]);
await dataSource.query('DELETE FROM family_members WHERE user_id = $1', [
userId,
]);
}
if (familyId) {
await dataSource.query('DELETE FROM families WHERE id = $1', [familyId]);
}
if (userId) {
await dataSource.query('DELETE FROM users WHERE id = $1', [userId]);
}
await app.close();
});
describe('POST /api/v1/children', () => {
it('should create a child', () => {
return request(app.getHttpServer())
.post(`/api/v1/children?familyId=${familyId}`)
.set('Authorization', `Bearer ${accessToken}`)
.send({
name: 'Emma',
birthDate: '2023-06-15',
gender: 'female',
})
.expect(201)
.expect((res) => {
expect(res.body.success).toBe(true);
expect(res.body.data).toHaveProperty('child');
expect(res.body.data.child.name).toBe('Emma');
expect(res.body.data.child.gender).toBe('female');
expect(res.body.data.child.id).toMatch(/^chd_/);
expect(res.body.data.child.familyId).toBe(familyId);
// Store for subsequent tests
childId = res.body.data.child.id;
});
});
it('should require authentication', () => {
return request(app.getHttpServer())
.post(`/api/v1/children?familyId=${familyId}`)
.send({
name: 'Test Child',
birthDate: '2023-01-01',
})
.expect(401);
});
it('should require familyId query parameter', () => {
return request(app.getHttpServer())
.post('/api/v1/children')
.set('Authorization', `Bearer ${accessToken}`)
.send({
name: 'Test Child',
birthDate: '2023-01-01',
})
.expect(201)
.expect((res) => {
expect(res.body.success).toBe(false);
expect(res.body.error.code).toBe('VALIDATION_ERROR');
});
});
it('should validate required fields', () => {
return request(app.getHttpServer())
.post(`/api/v1/children?familyId=${familyId}`)
.set('Authorization', `Bearer ${accessToken}`)
.send({
name: 'Test Child',
// Missing birthDate
})
.expect(400);
});
it('should validate birthDate format', () => {
return request(app.getHttpServer())
.post(`/api/v1/children?familyId=${familyId}`)
.set('Authorization', `Bearer ${accessToken}`)
.send({
name: 'Test Child',
birthDate: 'invalid-date',
})
.expect(400);
});
});
describe('GET /api/v1/children', () => {
it('should get all children for a family', () => {
return request(app.getHttpServer())
.get(`/api/v1/children?familyId=${familyId}`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(200)
.expect((res) => {
expect(res.body.success).toBe(true);
expect(res.body.data).toHaveProperty('children');
expect(Array.isArray(res.body.data.children)).toBe(true);
expect(res.body.data.children.length).toBeGreaterThan(0);
expect(res.body.data.children[0].name).toBe('Emma');
});
});
it('should get all children across all families when no familyId provided', () => {
return request(app.getHttpServer())
.get('/api/v1/children')
.set('Authorization', `Bearer ${accessToken}`)
.expect(200)
.expect((res) => {
expect(res.body.success).toBe(true);
expect(res.body.data).toHaveProperty('children');
expect(Array.isArray(res.body.data.children)).toBe(true);
});
});
it('should require authentication', () => {
return request(app.getHttpServer()).get('/api/v1/children').expect(401);
});
});
describe('GET /api/v1/children/:id', () => {
it('should get a specific child', () => {
return request(app.getHttpServer())
.get(`/api/v1/children/${childId}`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(200)
.expect((res) => {
expect(res.body.success).toBe(true);
expect(res.body.data).toHaveProperty('child');
expect(res.body.data.child.id).toBe(childId);
expect(res.body.data.child.name).toBe('Emma');
});
});
it('should return 404 for non-existent child', () => {
return request(app.getHttpServer())
.get('/api/v1/children/chd_nonexistent')
.set('Authorization', `Bearer ${accessToken}`)
.expect(404);
});
it('should require authentication', () => {
return request(app.getHttpServer())
.get(`/api/v1/children/${childId}`)
.expect(401);
});
});
describe('GET /api/v1/children/:id/age', () => {
it('should get child age', () => {
return request(app.getHttpServer())
.get(`/api/v1/children/${childId}/age`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(200)
.expect((res) => {
expect(res.body.success).toBe(true);
expect(res.body.data).toHaveProperty('ageInMonths');
expect(res.body.data).toHaveProperty('ageInYears');
expect(res.body.data).toHaveProperty('remainingMonths');
expect(typeof res.body.data.ageInMonths).toBe('number');
});
});
it('should return 404 for non-existent child', () => {
return request(app.getHttpServer())
.get('/api/v1/children/chd_nonexistent/age')
.set('Authorization', `Bearer ${accessToken}`)
.expect(404);
});
});
describe('PATCH /api/v1/children/:id', () => {
it('should update a child', () => {
return request(app.getHttpServer())
.patch(`/api/v1/children/${childId}`)
.set('Authorization', `Bearer ${accessToken}`)
.send({
name: 'Emma Updated',
medicalInfo: {
allergies: ['peanuts'],
},
})
.expect(200)
.expect((res) => {
expect(res.body.success).toBe(true);
expect(res.body.data.child.name).toBe('Emma Updated');
expect(res.body.data.child.medicalInfo.allergies).toEqual([
'peanuts',
]);
});
});
it('should return 404 for non-existent child', () => {
return request(app.getHttpServer())
.patch('/api/v1/children/chd_nonexistent')
.set('Authorization', `Bearer ${accessToken}`)
.send({
name: 'Test',
})
.expect(404);
});
it('should require authentication', () => {
return request(app.getHttpServer())
.patch(`/api/v1/children/${childId}`)
.send({
name: 'Test',
})
.expect(401);
});
});
describe('DELETE /api/v1/children/:id', () => {
it('should soft delete a child', async () => {
await request(app.getHttpServer())
.delete(`/api/v1/children/${childId}`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(200)
.expect((res) => {
expect(res.body.success).toBe(true);
expect(res.body.message).toBe('Child deleted successfully');
});
// Verify child is soft deleted (not visible in list)
await request(app.getHttpServer())
.get(`/api/v1/children?familyId=${familyId}`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(200)
.expect((res) => {
const deletedChild = res.body.data.children.find(
(c: any) => c.id === childId,
);
expect(deletedChild).toBeUndefined();
});
});
it('should return 404 for non-existent child', () => {
return request(app.getHttpServer())
.delete('/api/v1/children/chd_nonexistent')
.set('Authorization', `Bearer ${accessToken}`)
.expect(404);
});
it('should require authentication', () => {
return request(app.getHttpServer())
.delete(`/api/v1/children/${childId}`)
.expect(401);
});
});
});