commit
32a36cceff
444 files changed
+67186
No files matched your search
@@ -0,0 +1,233 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import Mortality from '../Mortality.js';
|
||||
import Cycle from '../Cycle.js';
|
||||
import { query } from '../../database/db.js';
|
||||
|
||||
describe('Mortality Model', () => {
|
||||
const TEST_CYCLE_ID = 'TEST-MORTALITY-CYCLE';
|
||||
|
||||
beforeEach(async () => {
|
||||
// Clean up test data
|
||||
await query('DELETE FROM mortality_records WHERE cycle_id = $1', [TEST_CYCLE_ID]);
|
||||
await query('DELETE FROM cycles WHERE id = $1', [TEST_CYCLE_ID]);
|
||||
|
||||
// Create test cycle
|
||||
await Cycle.create({
|
||||
id: TEST_CYCLE_ID,
|
||||
totalDays: 35,
|
||||
currentDay: 1,
|
||||
startDate: '2026-04-01',
|
||||
status: 'Active',
|
||||
docInCount: 10000,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await query('DELETE FROM mortality_records WHERE cycle_id = $1', [TEST_CYCLE_ID]);
|
||||
await query('DELETE FROM cycles WHERE id = $1', [TEST_CYCLE_ID]);
|
||||
});
|
||||
|
||||
describe('upsert', () => {
|
||||
it('should insert a new mortality record', async () => {
|
||||
const record = await Mortality.upsert(TEST_CYCLE_ID, 1, 50, true, 9950);
|
||||
|
||||
expect(record).toBeDefined();
|
||||
expect(record.cycleId).toBe(TEST_CYCLE_ID);
|
||||
expect(record.day).toBe(1);
|
||||
expect(record.mortalityCount).toBe(50);
|
||||
expect(record.chickenCount).toBe(9950);
|
||||
expect(record.isEdited).toBe(true);
|
||||
});
|
||||
|
||||
it('should update an existing mortality record', async () => {
|
||||
// Insert initial record
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 1, 50, true, 9950);
|
||||
|
||||
// Update the same record
|
||||
const updated = await Mortality.upsert(TEST_CYCLE_ID, 1, 55, true, 9945);
|
||||
|
||||
expect(updated).toBeDefined();
|
||||
expect(updated.mortalityCount).toBe(55);
|
||||
expect(updated.chickenCount).toBe(9945);
|
||||
});
|
||||
|
||||
it('should handle afkir and panen fields', async () => {
|
||||
const record = await Mortality.upsert(
|
||||
TEST_CYCLE_ID,
|
||||
10,
|
||||
20,
|
||||
true,
|
||||
9500,
|
||||
null,
|
||||
100,
|
||||
'Culled due to disease',
|
||||
30
|
||||
);
|
||||
|
||||
expect(record).toBeDefined();
|
||||
expect(record.afkir).toBe(30);
|
||||
expect(record.panen).toBe(100);
|
||||
expect(record.beratPanen).toBe(0);
|
||||
expect(record.keterangan).toBe('Culled due to disease');
|
||||
});
|
||||
|
||||
it('should persist harvest weight', async () => {
|
||||
const record = await Mortality.upsert(
|
||||
TEST_CYCLE_ID,
|
||||
12,
|
||||
15,
|
||||
true,
|
||||
9985,
|
||||
null,
|
||||
80,
|
||||
'Harvest day',
|
||||
5,
|
||||
123.45
|
||||
);
|
||||
|
||||
expect(record).toBeDefined();
|
||||
expect(record.beratPanen).toBe(123.45);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getByDay', () => {
|
||||
it('should retrieve a mortality record by cycle and day', async () => {
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 5, 45, true, 9500);
|
||||
|
||||
const record = await Mortality.getByDay(TEST_CYCLE_ID, 5);
|
||||
|
||||
expect(record).toBeDefined();
|
||||
expect(record.day).toBe(5);
|
||||
expect(record.mortalityCount).toBe(45);
|
||||
});
|
||||
|
||||
it('should return null for non-existent record', async () => {
|
||||
const record = await Mortality.getByDay(TEST_CYCLE_ID, 999);
|
||||
expect(record).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle kandang-specific records', async () => {
|
||||
// Create records for different kandangs
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 1, 20, true, 4980, 1);
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 1, 30, true, 4970, 2);
|
||||
|
||||
const recordKandang1 = await Mortality.getByDay(TEST_CYCLE_ID, 1, 1);
|
||||
const recordKandang2 = await Mortality.getByDay(TEST_CYCLE_ID, 1, 2);
|
||||
|
||||
expect(recordKandang1.mortalityCount).toBe(20);
|
||||
expect(recordKandang2.mortalityCount).toBe(30);
|
||||
expect(recordKandang1.kandangId).toBe(1);
|
||||
expect(recordKandang2.kandangId).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getByCycle', () => {
|
||||
it('should retrieve all mortality records for a cycle', async () => {
|
||||
// Create multiple records
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 1, 50, true, 9950);
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 2, 45, true, 9905);
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 3, 40, true, 9865);
|
||||
|
||||
const records = await Mortality.getByCycle(TEST_CYCLE_ID);
|
||||
|
||||
expect(records).toHaveLength(3);
|
||||
expect(records[0].day).toBe(1);
|
||||
expect(records[1].day).toBe(2);
|
||||
expect(records[2].day).toBe(3);
|
||||
});
|
||||
|
||||
it('should filter by kandang when provided', async () => {
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 1, 20, true, 4980, 1);
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 2, 25, true, 4955, 1);
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 1, 30, true, 4970, 2);
|
||||
|
||||
const recordsKandang1 = await Mortality.getByCycle(TEST_CYCLE_ID, 1);
|
||||
const recordsKandang2 = await Mortality.getByCycle(TEST_CYCLE_ID, 2);
|
||||
|
||||
expect(recordsKandang1).toHaveLength(2);
|
||||
expect(recordsKandang2).toHaveLength(1);
|
||||
expect(recordsKandang1.every((r) => r.kandangId === 1)).toBe(true);
|
||||
expect(recordsKandang2.every((r) => r.kandangId === 2)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTotalsByCycle', () => {
|
||||
it('should aggregate mati, afkir, and panen across kandangs', async () => {
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 1, 20, true, 4980, 1, 5, 'Kandang 1', 3, 11.25);
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 1, 30, true, 4970, 2, 7, 'Kandang 2', 4, 8.75);
|
||||
|
||||
const totals = await Mortality.getTotalsByCycle(TEST_CYCLE_ID);
|
||||
|
||||
expect(totals).toHaveLength(1);
|
||||
expect(totals[0].day).toBe(1);
|
||||
expect(totals[0].mortalityCount).toBe(50);
|
||||
expect(totals[0].afkir).toBe(7);
|
||||
expect(totals[0].panen).toBe(12);
|
||||
expect(totals[0].beratPanen).toBe(20);
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
it('should delete a mortality record', async () => {
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 10, 50, true, 9500);
|
||||
|
||||
const deleted = await Mortality.delete(TEST_CYCLE_ID, 10);
|
||||
expect(deleted).toBe(true);
|
||||
|
||||
const record = await Mortality.getByDay(TEST_CYCLE_ID, 10);
|
||||
expect(record).toBeNull();
|
||||
});
|
||||
|
||||
it('should return false when deleting non-existent record', async () => {
|
||||
const deleted = await Mortality.delete(TEST_CYCLE_ID, 999);
|
||||
expect(deleted).toBe(false);
|
||||
});
|
||||
|
||||
it('should delete kandang-specific records', async () => {
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 1, 20, true, 4980, 1);
|
||||
await Mortality.upsert(TEST_CYCLE_ID, 1, 30, true, 4970, 2);
|
||||
|
||||
const deleted = await Mortality.delete(TEST_CYCLE_ID, 1, 1);
|
||||
expect(deleted).toBe(true);
|
||||
|
||||
// Verify only kandang 1 was deleted
|
||||
const record1 = await Mortality.getByDay(TEST_CYCLE_ID, 1, 1);
|
||||
const record2 = await Mortality.getByDay(TEST_CYCLE_ID, 1, 2);
|
||||
expect(record1).toBeNull();
|
||||
expect(record2).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatRecord', () => {
|
||||
it('should format database record to camelCase', () => {
|
||||
const dbRecord = {
|
||||
id: 1,
|
||||
cycle_id: TEST_CYCLE_ID,
|
||||
day: 5,
|
||||
mortality_count: 50,
|
||||
chicken_count: 9500,
|
||||
population: 9500,
|
||||
kandang_id: 1,
|
||||
is_edited: true,
|
||||
afkir: 10,
|
||||
panen: 100,
|
||||
berat_panen: 12.5,
|
||||
keterangan: 'Test note',
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
};
|
||||
|
||||
const formatted = Mortality.formatRecord(dbRecord);
|
||||
|
||||
expect(formatted.cycleId).toBe(TEST_CYCLE_ID);
|
||||
expect(formatted.mortalityCount).toBe(50);
|
||||
expect(formatted.chickenCount).toBe(9500);
|
||||
expect(formatted.kandangId).toBe(1);
|
||||
expect(formatted.isEdited).toBe(true);
|
||||
expect(formatted.afkir).toBe(10);
|
||||
expect(formatted.panen).toBe(100);
|
||||
expect(formatted.beratPanen).toBe(12.5);
|
||||
expect(formatted.keterangan).toBe('Test note');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user