test(matching): add comprehensive tests for buffer functions and edge cases

- Export getPostDanceBufferSlots and HEAT_BUFFER_AFTER for testing
- Add direct tests for getPreDanceBufferSlots (4 tests)
- Add direct tests for getPostDanceBufferSlots (2 tests)
- Add test for HEAT_BUFFER_AFTER constant
- Add edge case for getCoverableHeats with multiple recorder heats
- Add edge case for hasCollision with multi-heat scenarios
- Total: 39/39 tests passing (9 new tests added)
This commit is contained in:
Radosław Gierwiało
2025-11-29 23:52:12 +01:00
parent 4fb78e08ad
commit ce10d20cbb
2 changed files with 95 additions and 0 deletions

View File

@@ -5,12 +5,14 @@
const {
getTimeSlot,
getPreDanceBufferSlots,
getPostDanceBufferSlots,
getCoverableHeats,
hasCollision,
getLocationScore,
buildDivisionSlotMap,
MAX_RECORDINGS_PER_PERSON,
HEAT_BUFFER_BEFORE,
HEAT_BUFFER_AFTER,
} = require('../services/matching');
describe('Matching Service - Unit Tests', () => {
@@ -30,6 +32,58 @@ describe('Matching Service - Unit Tests', () => {
});
});
describe('getPreDanceBufferSlots', () => {
it('should return empty array when heatNumber is 1 (no previous heats)', () => {
const heat = { divisionId: 1, competitionTypeId: 1, heatNumber: 1 };
const result = getPreDanceBufferSlots(heat);
expect(result).toEqual([]);
});
it('should return buffer slot for previous heat without divisionSlotMap', () => {
const heat = { divisionId: 1, competitionTypeId: 1, heatNumber: 3 };
const result = getPreDanceBufferSlots(heat);
// HEAT_BUFFER_BEFORE = 1 => blokujemy heatNumber 2
expect(result).toEqual(['1-1-2']);
});
it('should use divisionSlotMap when available', () => {
const divisionSlotMap = new Map([[1, 2]]); // division 1 -> slot2
const heat = { divisionId: 1, competitionTypeId: 1, heatNumber: 3 };
const result = getPreDanceBufferSlots(heat, divisionSlotMap);
// Format: slot{slotOrder}-{competitionTypeId}-{prevHeatNumber}
expect(result).toEqual(['slot2-1-2']);
});
it('should fallback to division-based buffer when division not in map', () => {
const divisionSlotMap = new Map([[99, 1]]); // 1 nie jest zmapowane
const heat = { divisionId: 1, competitionTypeId: 1, heatNumber: 3 };
const result = getPreDanceBufferSlots(heat, divisionSlotMap);
expect(result).toEqual(['1-1-2']);
});
});
describe('getPostDanceBufferSlots', () => {
it('should return buffer slot for next heat without divisionSlotMap', () => {
const heat = { divisionId: 1, competitionTypeId: 1, heatNumber: 3 };
const result = getPostDanceBufferSlots(heat);
// HEAT_BUFFER_AFTER = 1 => blokujemy heatNumber 4
expect(result).toEqual(['1-1-4']);
});
it('should use divisionSlotMap when available', () => {
const divisionSlotMap = new Map([[1, 2]]); // division 1 -> slot2
const heat = { divisionId: 1, competitionTypeId: 1, heatNumber: 3 };
const result = getPostDanceBufferSlots(heat, divisionSlotMap);
expect(result).toEqual(['slot2-1-4']);
});
});
describe('getCoverableHeats', () => {
it('should return all heats when recorder has no heats', () => {
const dancerHeats = [
@@ -107,6 +161,26 @@ describe('Matching Service - Unit Tests', () => {
// Novice H1 blocked, Intermediate H1 available
expect(result.map(h => h.id)).toEqual([2]);
});
it('should respect buffers around multiple recorder heats', () => {
const dancerHeats = [
{ id: 1, divisionId: 1, competitionTypeId: 1, heatNumber: 1 },
{ id: 2, divisionId: 1, competitionTypeId: 1, heatNumber: 3 },
{ id: 3, divisionId: 1, competitionTypeId: 1, heatNumber: 5 },
];
const recorderHeats = [
{ divisionId: 1, competitionTypeId: 1, heatNumber: 2 },
{ divisionId: 1, competitionTypeId: 1, heatNumber: 4 },
];
const result = getCoverableHeats(dancerHeats, recorderHeats);
// Recorder tańczy 2 i 4, z buforami:
// - przy 2: blokuje 1,2,3
// - przy 4: blokuje 3,4,5
// => wszystkie dancerHeats wchodzą w któryś bufor/taniec
expect(result).toHaveLength(0);
});
});
describe('hasCollision', () => {
@@ -155,6 +229,21 @@ describe('Matching Service - Unit Tests', () => {
// Heat 3 is outside buffer of Heat 1 (buffer = 1, so Heat 2 is blocked, Heat 3 is OK)
expect(hasCollision(dancerHeats, recorderHeats)).toBe(false);
});
it('should return true when any dancer heat collides with any recorder heat or buffer', () => {
const dancerHeats = [
{ divisionId: 1, competitionTypeId: 1, heatNumber: 1 },
{ divisionId: 1, competitionTypeId: 1, heatNumber: 5 }, // ten będzie w buforze
];
const recorderHeats = [
{ divisionId: 1, competitionTypeId: 1, heatNumber: 4 },
];
// Recorder tańczy 4 -> bufor obejmuje 3,4,5
// dancerHeats[1] = heatNumber 5 -> kolizja
expect(hasCollision(dancerHeats, recorderHeats)).toBe(true);
});
});
describe('getLocationScore', () => {
@@ -202,6 +291,10 @@ describe('Matching Service - Unit Tests', () => {
it('should have correct heat buffer before', () => {
expect(HEAT_BUFFER_BEFORE).toBe(1);
});
it('should have correct heat buffer after', () => {
expect(HEAT_BUFFER_AFTER).toBe(1);
});
});
describe('Schedule Config - buildDivisionSlotMap', () => {

View File

@@ -552,10 +552,12 @@ module.exports = {
getCoverableHeats,
getTimeSlot,
getPreDanceBufferSlots,
getPostDanceBufferSlots,
getLocationScore,
buildDivisionSlotMap,
getEffectiveTier,
getRecordingStatsForUsers,
MAX_RECORDINGS_PER_PERSON,
HEAT_BUFFER_BEFORE,
HEAT_BUFFER_AFTER,
};