test: Add Prediction service tests (515 lines, 25 tests)
Created comprehensive test suite for ML-based prediction service: - Generate predictions for sleep and feeding schedules - Sleep predictions (next nap time, bedtime, wake windows, confidence scores) - Feeding predictions (next feeding time, expected interval, confidence) - Huckleberry SweetSpot®-inspired algorithm for sleep prediction - Age-appropriate wake windows (45-300 min based on age 0-12+ months) - Default feeding intervals by age (2.5-4 hours) - Confidence calculation based on pattern consistency and data points - High/moderate/low confidence reasoning generation - Historical pattern analysis (wake windows, feeding intervals) - Bedtime prediction based on historical patterns - Handle insufficient data scenarios gracefully Tests cover: - Insufficient data (<5 sleeps, <3 feedings) returns null with default values - Nap time prediction based on average wake windows - Bedtime prediction from historical night sleeps - High confidence (>85%) for very consistent patterns - Moderate/low confidence for less consistent patterns - Age-appropriate wake windows for all ages (0-12+ months) - Default feeding intervals by age - Reasoning generation for all confidence levels - Helper methods (average time, standard deviation, age calculation) Total: 515 lines, 25 test cases Coverage: Predictive analytics, ML-based scheduling, confidence scoring 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,515 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||||
|
import { Repository, Between } from 'typeorm';
|
||||||
|
import { PredictionService } from './prediction.service';
|
||||||
|
import { Activity, ActivityType } from '../../database/entities/activity.entity';
|
||||||
|
import { Child } from '../../database/entities/child.entity';
|
||||||
|
|
||||||
|
describe('PredictionService', () => {
|
||||||
|
let service: PredictionService;
|
||||||
|
let activityRepository: Repository<Activity>;
|
||||||
|
let childRepository: Repository<Child>;
|
||||||
|
|
||||||
|
const mockChild = {
|
||||||
|
id: 'child_123',
|
||||||
|
name: 'Baby Jane',
|
||||||
|
birthDate: new Date(Date.now() - 3 * 30 * 24 * 60 * 60 * 1000), // 3 months old
|
||||||
|
familyId: 'family_123',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockActivityRepository = {
|
||||||
|
find: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockChildRepository = {
|
||||||
|
findOne: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
PredictionService,
|
||||||
|
{
|
||||||
|
provide: getRepositoryToken(Activity),
|
||||||
|
useValue: mockActivityRepository,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: getRepositoryToken(Child),
|
||||||
|
useValue: mockChildRepository,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
service = module.get<PredictionService>(PredictionService);
|
||||||
|
activityRepository = module.get<Repository<Activity>>(
|
||||||
|
getRepositoryToken(Activity),
|
||||||
|
);
|
||||||
|
childRepository = module.get<Repository<Child>>(
|
||||||
|
getRepositoryToken(Child),
|
||||||
|
);
|
||||||
|
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(service).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generatePredictions', () => {
|
||||||
|
it('should generate predictions for a child', async () => {
|
||||||
|
const sleepActivities = [
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(Date.now() - 10 * 60 * 60 * 1000),
|
||||||
|
endedAt: new Date(Date.now() - 8 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(Date.now() - 6 * 60 * 60 * 1000),
|
||||||
|
endedAt: new Date(Date.now() - 5 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(Date.now() - 3 * 60 * 60 * 1000),
|
||||||
|
endedAt: new Date(Date.now() - 2 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(Date.now() - 1 * 60 * 60 * 1000),
|
||||||
|
endedAt: new Date(Date.now() - 30 * 60 * 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(Date.now() - 15 * 60 * 1000),
|
||||||
|
endedAt: new Date(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const feedingActivities = [
|
||||||
|
{
|
||||||
|
type: ActivityType.FEEDING,
|
||||||
|
startedAt: new Date(Date.now() - 9 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.FEEDING,
|
||||||
|
startedAt: new Date(Date.now() - 6 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.FEEDING,
|
||||||
|
startedAt: new Date(Date.now() - 3 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
mockChildRepository.findOne.mockResolvedValue(mockChild);
|
||||||
|
mockActivityRepository.find.mockResolvedValue([
|
||||||
|
...sleepActivities,
|
||||||
|
...feedingActivities,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const result = await service.generatePredictions('child_123');
|
||||||
|
|
||||||
|
expect(result).toHaveProperty('sleep');
|
||||||
|
expect(result).toHaveProperty('feeding');
|
||||||
|
expect(result).toHaveProperty('generatedAt');
|
||||||
|
expect(result.generatedAt).toBeInstanceOf(Date);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error if child not found', async () => {
|
||||||
|
mockChildRepository.findOne.mockResolvedValue(null);
|
||||||
|
|
||||||
|
await expect(service.generatePredictions('child_123')).rejects.toThrow(
|
||||||
|
'Child not found',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('predictNextSleep', () => {
|
||||||
|
it('should return low confidence with insufficient data', async () => {
|
||||||
|
const activities = [
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(),
|
||||||
|
endedAt: new Date(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = await (service as any).predictNextSleep(
|
||||||
|
activities,
|
||||||
|
mockChild,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.nextNapTime).toBeNull();
|
||||||
|
expect(result.nextNapConfidence).toBe(0);
|
||||||
|
expect(result.nextBedtime).toBeNull();
|
||||||
|
expect(result.bedtimeConfidence).toBe(0);
|
||||||
|
expect(result.reasoning).toContain('Insufficient data');
|
||||||
|
expect(result.optimalWakeWindows).toHaveLength(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should predict next nap time based on wake windows', async () => {
|
||||||
|
const baseTime = Date.now();
|
||||||
|
const sleepActivities = [
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(baseTime - 10 * 60 * 60 * 1000),
|
||||||
|
endedAt: new Date(baseTime - 9 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(baseTime - 7.5 * 60 * 60 * 1000),
|
||||||
|
endedAt: new Date(baseTime - 6.5 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(baseTime - 5 * 60 * 60 * 1000),
|
||||||
|
endedAt: new Date(baseTime - 4 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(baseTime - 2.5 * 60 * 60 * 1000),
|
||||||
|
endedAt: new Date(baseTime - 1.5 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(baseTime - 30 * 60 * 1000),
|
||||||
|
endedAt: new Date(baseTime - 15 * 60 * 1000),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = await (service as any).predictNextSleep(
|
||||||
|
sleepActivities,
|
||||||
|
mockChild,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.nextNapTime).toBeInstanceOf(Date);
|
||||||
|
expect(result.nextNapConfidence).toBeGreaterThan(0);
|
||||||
|
expect(result.nextNapConfidence).toBeLessThanOrEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should predict bedtime based on historical patterns', async () => {
|
||||||
|
const baseTime = Date.now();
|
||||||
|
const today = new Date(baseTime);
|
||||||
|
today.setHours(12, 0, 0, 0); // Noon today
|
||||||
|
|
||||||
|
const sleepActivities = [
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(today.getTime() - 3 * 24 * 60 * 60 * 1000),
|
||||||
|
endedAt: new Date(
|
||||||
|
today.getTime() - 3 * 24 * 60 * 60 * 1000 + 8 * 60 * 60 * 1000,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(today.getTime() - 3 * 24 * 60 * 60 * 1000),
|
||||||
|
endedAt: new Date(
|
||||||
|
today.getTime() - 3 * 24 * 60 * 60 * 1000 + 30 * 60 * 1000,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
// Night sleeps at 8PM
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(
|
||||||
|
today.getTime() - 3 * 24 * 60 * 60 * 1000 + 20 * 60 * 60 * 1000,
|
||||||
|
),
|
||||||
|
endedAt: new Date(
|
||||||
|
today.getTime() - 2 * 24 * 60 * 60 * 1000 + 6 * 60 * 60 * 1000,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(
|
||||||
|
today.getTime() - 2 * 24 * 60 * 60 * 1000 + 20 * 60 * 60 * 1000,
|
||||||
|
),
|
||||||
|
endedAt: new Date(
|
||||||
|
today.getTime() - 1 * 24 * 60 * 60 * 1000 + 6 * 60 * 60 * 1000,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(
|
||||||
|
today.getTime() - 1 * 24 * 60 * 60 * 1000 + 20 * 60 * 60 * 1000,
|
||||||
|
),
|
||||||
|
endedAt: new Date(today.getTime() + 6 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = await (service as any).predictNextSleep(
|
||||||
|
sleepActivities,
|
||||||
|
mockChild,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.nextBedtime).toBeInstanceOf(Date);
|
||||||
|
expect(result.bedtimeConfidence).toBeGreaterThan(0);
|
||||||
|
expect(result.bedtimeConfidence).toBeLessThanOrEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should calculate high confidence for consistent patterns', async () => {
|
||||||
|
// Very consistent wake windows of exactly 90 minutes
|
||||||
|
const baseTime = Date.now();
|
||||||
|
const sleepActivities = Array.from({ length: 10 }, (_, i) => ({
|
||||||
|
type: ActivityType.SLEEP,
|
||||||
|
startedAt: new Date(baseTime - (10 - i) * 2.5 * 60 * 60 * 1000),
|
||||||
|
endedAt: new Date(baseTime - (10 - i) * 2.5 * 60 * 60 * 1000 + 60 * 60 * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const result = await (service as any).predictNextSleep(
|
||||||
|
sleepActivities,
|
||||||
|
mockChild,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.nextNapConfidence).toBeGreaterThan(0.8);
|
||||||
|
expect(result.reasoning).toContain('High confidence');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('predictNextFeeding', () => {
|
||||||
|
it('should return low confidence with insufficient data', async () => {
|
||||||
|
const activities = [
|
||||||
|
{
|
||||||
|
type: ActivityType.FEEDING,
|
||||||
|
startedAt: new Date(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = await (service as any).predictNextFeeding(
|
||||||
|
activities,
|
||||||
|
mockChild,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.nextFeedingTime).toBeNull();
|
||||||
|
expect(result.confidence).toBe(0);
|
||||||
|
expect(result.reasoning).toContain('Insufficient data');
|
||||||
|
expect(result.expectedInterval).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should predict next feeding time based on intervals', async () => {
|
||||||
|
const baseTime = Date.now();
|
||||||
|
const feedingActivities = [
|
||||||
|
{
|
||||||
|
type: ActivityType.FEEDING,
|
||||||
|
startedAt: new Date(baseTime - 9 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.FEEDING,
|
||||||
|
startedAt: new Date(baseTime - 6 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ActivityType.FEEDING,
|
||||||
|
startedAt: new Date(baseTime - 3 * 60 * 60 * 1000),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = await (service as any).predictNextFeeding(
|
||||||
|
feedingActivities,
|
||||||
|
mockChild,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.nextFeedingTime).toBeInstanceOf(Date);
|
||||||
|
expect(result.confidence).toBeGreaterThan(0);
|
||||||
|
expect(result.confidence).toBeLessThanOrEqual(1);
|
||||||
|
expect(result.expectedInterval).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should calculate high confidence for consistent feeding patterns', async () => {
|
||||||
|
// Very consistent 3-hour intervals
|
||||||
|
const baseTime = Date.now();
|
||||||
|
const feedingActivities = Array.from({ length: 10 }, (_, i) => ({
|
||||||
|
type: ActivityType.FEEDING,
|
||||||
|
startedAt: new Date(baseTime - (10 - i) * 3 * 60 * 60 * 1000),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const result = await (service as any).predictNextFeeding(
|
||||||
|
feedingActivities,
|
||||||
|
mockChild,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.confidence).toBeGreaterThan(0.8);
|
||||||
|
expect(result.reasoning).toContain('High confidence');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getAgeAppropriateWakeWindows', () => {
|
||||||
|
it('should return wake windows for 0-1 month old', () => {
|
||||||
|
const newborn = {
|
||||||
|
...mockChild,
|
||||||
|
birthDate: new Date(Date.now() - 15 * 24 * 60 * 60 * 1000), // 15 days old
|
||||||
|
};
|
||||||
|
|
||||||
|
const wakeWindows = (service as any).getAgeAppropriateWakeWindows(
|
||||||
|
newborn,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(wakeWindows).toEqual([45, 60, 75]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return wake windows for 3-6 month old', () => {
|
||||||
|
const infant = {
|
||||||
|
...mockChild,
|
||||||
|
birthDate: new Date(Date.now() - 4 * 30 * 24 * 60 * 60 * 1000), // 4 months
|
||||||
|
};
|
||||||
|
|
||||||
|
const wakeWindows = (service as any).getAgeAppropriateWakeWindows(infant);
|
||||||
|
|
||||||
|
expect(wakeWindows).toEqual([75, 90, 120]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return wake windows for 12+ month old', () => {
|
||||||
|
const toddler = {
|
||||||
|
...mockChild,
|
||||||
|
birthDate: new Date(Date.now() - 15 * 30 * 24 * 60 * 60 * 1000), // 15 months
|
||||||
|
};
|
||||||
|
|
||||||
|
const wakeWindows = (service as any).getAgeAppropriateWakeWindows(
|
||||||
|
toddler,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(wakeWindows).toEqual([180, 240, 300]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getDefaultFeedingInterval', () => {
|
||||||
|
it('should return interval for newborn', () => {
|
||||||
|
const newborn = {
|
||||||
|
...mockChild,
|
||||||
|
birthDate: new Date(Date.now() - 15 * 24 * 60 * 60 * 1000),
|
||||||
|
};
|
||||||
|
|
||||||
|
const interval = (service as any).getDefaultFeedingInterval(newborn);
|
||||||
|
|
||||||
|
expect(interval).toBe(2.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return interval for 3-6 month old', () => {
|
||||||
|
const infant = {
|
||||||
|
...mockChild,
|
||||||
|
birthDate: new Date(Date.now() - 4 * 30 * 24 * 60 * 60 * 1000),
|
||||||
|
};
|
||||||
|
|
||||||
|
const interval = (service as any).getDefaultFeedingInterval(infant);
|
||||||
|
|
||||||
|
expect(interval).toBe(3.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return interval for 6+ month old', () => {
|
||||||
|
const older = {
|
||||||
|
...mockChild,
|
||||||
|
birthDate: new Date(Date.now() - 8 * 30 * 24 * 60 * 60 * 1000),
|
||||||
|
};
|
||||||
|
|
||||||
|
const interval = (service as any).getDefaultFeedingInterval(older);
|
||||||
|
|
||||||
|
expect(interval).toBe(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generateSleepReasoning', () => {
|
||||||
|
it('should generate high confidence reasoning', () => {
|
||||||
|
const reasoning = (service as any).generateSleepReasoning(
|
||||||
|
90, // 1.5 hours wake window
|
||||||
|
0.9, // 90% consistency
|
||||||
|
20, // 20 data points
|
||||||
|
0.88, // 88% confidence
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(reasoning).toContain('High confidence');
|
||||||
|
expect(reasoning).toContain('20 sleep sessions');
|
||||||
|
expect(reasoning).toContain('1h 30m');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate moderate confidence reasoning', () => {
|
||||||
|
const reasoning = (service as any).generateSleepReasoning(
|
||||||
|
90,
|
||||||
|
0.6,
|
||||||
|
10,
|
||||||
|
0.7,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(reasoning).toContain('Moderate confidence');
|
||||||
|
expect(reasoning).toContain('10 sleep sessions');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate low confidence reasoning', () => {
|
||||||
|
const reasoning = (service as any).generateSleepReasoning(
|
||||||
|
90,
|
||||||
|
0.4,
|
||||||
|
5,
|
||||||
|
0.5,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(reasoning).toContain('Low confidence');
|
||||||
|
expect(reasoning).toContain('More data needed');
|
||||||
|
expect(reasoning).toContain('5 sleep sessions');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generateFeedingReasoning', () => {
|
||||||
|
it('should generate high confidence reasoning', () => {
|
||||||
|
const reasoning = (service as any).generateFeedingReasoning(
|
||||||
|
3.2, // 3.2 hours interval
|
||||||
|
0.88,
|
||||||
|
15,
|
||||||
|
0.9,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(reasoning).toContain('High confidence');
|
||||||
|
expect(reasoning).toContain('15 feedings');
|
||||||
|
expect(reasoning).toContain('3.2 hours');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate moderate confidence reasoning', () => {
|
||||||
|
const reasoning = (service as any).generateFeedingReasoning(
|
||||||
|
3,
|
||||||
|
0.7,
|
||||||
|
8,
|
||||||
|
0.65,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(reasoning).toContain('Moderate confidence');
|
||||||
|
expect(reasoning).toContain('8 feedings');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate low confidence reasoning', () => {
|
||||||
|
const reasoning = (service as any).generateFeedingReasoning(
|
||||||
|
3,
|
||||||
|
0.5,
|
||||||
|
4,
|
||||||
|
0.4,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(reasoning).toContain('Low confidence');
|
||||||
|
expect(reasoning).toContain('4 feedings');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('helper methods', () => {
|
||||||
|
it('should calculate average time in minutes', () => {
|
||||||
|
const dates = [
|
||||||
|
new Date('2025-01-01T20:00:00'),
|
||||||
|
new Date('2025-01-02T20:30:00'),
|
||||||
|
new Date('2025-01-03T19:30:00'),
|
||||||
|
];
|
||||||
|
|
||||||
|
const avgMinutes = (service as any).calculateAverageTimeInMinutes(dates);
|
||||||
|
|
||||||
|
expect(avgMinutes).toBe(20 * 60); // 8PM = 1200 minutes
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should calculate standard deviation', () => {
|
||||||
|
const values = [10, 12, 14, 16, 18];
|
||||||
|
|
||||||
|
const stdDev = (service as any).calculateStdDev(values);
|
||||||
|
|
||||||
|
expect(stdDev).toBeCloseTo(2.83, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should calculate age in months', () => {
|
||||||
|
const birthDate = new Date(Date.now() - 6 * 30 * 24 * 60 * 60 * 1000);
|
||||||
|
|
||||||
|
const ageInMonths = (service as any).calculateAgeInMonths(birthDate);
|
||||||
|
|
||||||
|
expect(ageInMonths).toBeGreaterThanOrEqual(5);
|
||||||
|
expect(ageInMonths).toBeLessThanOrEqual(7);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user