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:
@@ -5,12 +5,14 @@
|
|||||||
const {
|
const {
|
||||||
getTimeSlot,
|
getTimeSlot,
|
||||||
getPreDanceBufferSlots,
|
getPreDanceBufferSlots,
|
||||||
|
getPostDanceBufferSlots,
|
||||||
getCoverableHeats,
|
getCoverableHeats,
|
||||||
hasCollision,
|
hasCollision,
|
||||||
getLocationScore,
|
getLocationScore,
|
||||||
buildDivisionSlotMap,
|
buildDivisionSlotMap,
|
||||||
MAX_RECORDINGS_PER_PERSON,
|
MAX_RECORDINGS_PER_PERSON,
|
||||||
HEAT_BUFFER_BEFORE,
|
HEAT_BUFFER_BEFORE,
|
||||||
|
HEAT_BUFFER_AFTER,
|
||||||
} = require('../services/matching');
|
} = require('../services/matching');
|
||||||
|
|
||||||
describe('Matching Service - Unit Tests', () => {
|
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', () => {
|
describe('getCoverableHeats', () => {
|
||||||
it('should return all heats when recorder has no heats', () => {
|
it('should return all heats when recorder has no heats', () => {
|
||||||
const dancerHeats = [
|
const dancerHeats = [
|
||||||
@@ -107,6 +161,26 @@ describe('Matching Service - Unit Tests', () => {
|
|||||||
// Novice H1 blocked, Intermediate H1 available
|
// Novice H1 blocked, Intermediate H1 available
|
||||||
expect(result.map(h => h.id)).toEqual([2]);
|
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', () => {
|
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)
|
// 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);
|
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', () => {
|
describe('getLocationScore', () => {
|
||||||
@@ -202,6 +291,10 @@ describe('Matching Service - Unit Tests', () => {
|
|||||||
it('should have correct heat buffer before', () => {
|
it('should have correct heat buffer before', () => {
|
||||||
expect(HEAT_BUFFER_BEFORE).toBe(1);
|
expect(HEAT_BUFFER_BEFORE).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should have correct heat buffer after', () => {
|
||||||
|
expect(HEAT_BUFFER_AFTER).toBe(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Schedule Config - buildDivisionSlotMap', () => {
|
describe('Schedule Config - buildDivisionSlotMap', () => {
|
||||||
|
|||||||
@@ -552,10 +552,12 @@ module.exports = {
|
|||||||
getCoverableHeats,
|
getCoverableHeats,
|
||||||
getTimeSlot,
|
getTimeSlot,
|
||||||
getPreDanceBufferSlots,
|
getPreDanceBufferSlots,
|
||||||
|
getPostDanceBufferSlots,
|
||||||
getLocationScore,
|
getLocationScore,
|
||||||
buildDivisionSlotMap,
|
buildDivisionSlotMap,
|
||||||
getEffectiveTier,
|
getEffectiveTier,
|
||||||
getRecordingStatsForUsers,
|
getRecordingStatsForUsers,
|
||||||
MAX_RECORDINGS_PER_PERSON,
|
MAX_RECORDINGS_PER_PERSON,
|
||||||
HEAT_BUFFER_BEFORE,
|
HEAT_BUFFER_BEFORE,
|
||||||
|
HEAT_BUFFER_AFTER,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user