202 lines
6.8 KiB
TypeScript
202 lines
6.8 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
|
|
/**
|
|
* Standings Snapshots Tests
|
|
* Phase 4.2: Test snapshot creation and 7-day comparison logic
|
|
*
|
|
* These are unit tests that verify the snapshot logic works correctly.
|
|
* Integration tests that use the actual database are in the e2e test suite.
|
|
*/
|
|
|
|
/**
|
|
* Helper function to simulate 7-day rank change calculation
|
|
* This mirrors the logic in getSevenDayStandingsChange
|
|
*/
|
|
function calculateSevenDayChange(
|
|
currentRank: number,
|
|
sevenDayOldRank: number | null
|
|
): number {
|
|
if (sevenDayOldRank === null) {
|
|
return 0; // No snapshot from 7 days ago
|
|
}
|
|
// Positive number = improvement (was 5th, now 3rd = +2)
|
|
// Negative number = decline (was 3rd, now 5th = -2)
|
|
return sevenDayOldRank - currentRank;
|
|
}
|
|
|
|
/**
|
|
* Helper function to simulate snapshot deduplication logic
|
|
* This mirrors the logic in createDailySnapshot
|
|
*/
|
|
function shouldCreateSnapshot(
|
|
existingSnapshotDate: string | null,
|
|
today: string
|
|
): boolean {
|
|
// Don't create if snapshot already exists for today
|
|
return existingSnapshotDate !== today;
|
|
}
|
|
|
|
describe("Standings Snapshots", () => {
|
|
describe("7-Day Rank Change Calculation", () => {
|
|
it("should calculate positive change when rank improves", () => {
|
|
const change = calculateSevenDayChange(1, 3);
|
|
expect(change).toBe(2); // Moved from 3rd to 1st = +2
|
|
});
|
|
|
|
it("should calculate negative change when rank declines", () => {
|
|
const change = calculateSevenDayChange(5, 2);
|
|
expect(change).toBe(-3); // Moved from 2nd to 5th = -3
|
|
});
|
|
|
|
it("should return 0 when rank stays the same", () => {
|
|
const change = calculateSevenDayChange(2, 2);
|
|
expect(change).toBe(0);
|
|
});
|
|
|
|
it("should return 0 when no snapshot exists from 7 days ago", () => {
|
|
const change = calculateSevenDayChange(1, null);
|
|
expect(change).toBe(0);
|
|
});
|
|
|
|
it("should handle large rank improvements", () => {
|
|
const change = calculateSevenDayChange(1, 10);
|
|
expect(change).toBe(9); // Moved from 10th to 1st = +9
|
|
});
|
|
|
|
it("should handle large rank declines", () => {
|
|
const change = calculateSevenDayChange(10, 1);
|
|
expect(change).toBe(-9); // Moved from 1st to 10th = -9
|
|
});
|
|
});
|
|
|
|
describe("Snapshot Deduplication", () => {
|
|
it("should create snapshot when no existing snapshot for today", () => {
|
|
const today = "2025-01-13";
|
|
const shouldCreate = shouldCreateSnapshot(null, today);
|
|
expect(shouldCreate).toBe(true);
|
|
});
|
|
|
|
it("should not create snapshot when snapshot already exists for today", () => {
|
|
const today = "2025-01-13";
|
|
const existingDate = "2025-01-13";
|
|
const shouldCreate = shouldCreateSnapshot(existingDate, today);
|
|
expect(shouldCreate).toBe(false);
|
|
});
|
|
|
|
it("should create snapshot when last snapshot was yesterday", () => {
|
|
const today = "2025-01-13";
|
|
const existingDate = "2025-01-12";
|
|
const shouldCreate = shouldCreateSnapshot(existingDate, today);
|
|
expect(shouldCreate).toBe(true);
|
|
});
|
|
|
|
it("should create snapshot when last snapshot was 7 days ago", () => {
|
|
const today = "2025-01-13";
|
|
const existingDate = "2025-01-06";
|
|
const shouldCreate = shouldCreateSnapshot(existingDate, today);
|
|
expect(shouldCreate).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("Snapshot Data Integrity", () => {
|
|
it("should preserve all tiebreaker data in snapshot", () => {
|
|
const standing = {
|
|
totalPoints: 150.5,
|
|
currentRank: 1,
|
|
firstPlaceCount: 2,
|
|
secondPlaceCount: 1,
|
|
thirdPlaceCount: 0,
|
|
fourthPlaceCount: 0,
|
|
fifthPlaceCount: 0,
|
|
sixthPlaceCount: 0,
|
|
seventhPlaceCount: 0,
|
|
eighthPlaceCount: 0,
|
|
participantsRemaining: 5,
|
|
};
|
|
|
|
// Snapshot should contain all the same data
|
|
const snapshot = {
|
|
totalPoints: standing.totalPoints,
|
|
rank: standing.currentRank,
|
|
firstPlaceCount: standing.firstPlaceCount,
|
|
secondPlaceCount: standing.secondPlaceCount,
|
|
thirdPlaceCount: standing.thirdPlaceCount,
|
|
fourthPlaceCount: standing.fourthPlaceCount,
|
|
fifthPlaceCount: standing.fifthPlaceCount,
|
|
sixthPlaceCount: standing.sixthPlaceCount,
|
|
seventhPlaceCount: standing.seventhPlaceCount,
|
|
eighthPlaceCount: standing.eighthPlaceCount,
|
|
participantsRemaining: standing.participantsRemaining,
|
|
};
|
|
|
|
expect(snapshot.totalPoints).toBe(standing.totalPoints);
|
|
expect(snapshot.rank).toBe(standing.currentRank);
|
|
expect(snapshot.firstPlaceCount).toBe(standing.firstPlaceCount);
|
|
expect(snapshot.participantsRemaining).toBe(standing.participantsRemaining);
|
|
});
|
|
});
|
|
|
|
describe("Movement Scenarios", () => {
|
|
it("should handle team moving from last to first", () => {
|
|
const scenarios = [
|
|
{ current: 1, old: 8, expected: 7 }, // Last to first = +7
|
|
{ current: 8, old: 1, expected: -7 }, // First to last = -7
|
|
];
|
|
|
|
scenarios.forEach(({ current, old, expected }) => {
|
|
const change = calculateSevenDayChange(current, old);
|
|
expect(change).toBe(expected);
|
|
});
|
|
});
|
|
|
|
it("should handle multiple teams with different movements", () => {
|
|
const teams = [
|
|
{ id: "A", currentRank: 1, oldRank: 3, expectedChange: 2 },
|
|
{ id: "B", currentRank: 2, oldRank: 1, expectedChange: -1 },
|
|
{ id: "C", currentRank: 3, oldRank: 2, expectedChange: -1 },
|
|
{ id: "D", currentRank: 4, oldRank: null, expectedChange: 0 },
|
|
];
|
|
|
|
teams.forEach((team) => {
|
|
const change = calculateSevenDayChange(team.currentRank, team.oldRank);
|
|
expect(change).toBe(team.expectedChange);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Historical Tracking", () => {
|
|
it("should maintain chronological order of snapshots", () => {
|
|
const snapshots = [
|
|
{ date: "2025-01-13", rank: 1 },
|
|
{ date: "2025-01-12", rank: 2 },
|
|
{ date: "2025-01-11", rank: 3 },
|
|
{ date: "2025-01-10", rank: 2 },
|
|
{ date: "2025-01-09", rank: 1 },
|
|
];
|
|
|
|
// Should be sorted by date descending (newest first)
|
|
const sorted = [...snapshots].sort((a, b) =>
|
|
b.date.localeCompare(a.date)
|
|
);
|
|
|
|
expect(sorted[0].date).toBe("2025-01-13");
|
|
expect(sorted[4].date).toBe("2025-01-09");
|
|
});
|
|
|
|
it("should show rank progression over time", () => {
|
|
const history = [
|
|
{ date: "2025-01-09", rank: 5, points: 50 },
|
|
{ date: "2025-01-10", rank: 4, points: 75 },
|
|
{ date: "2025-01-11", rank: 3, points: 100 },
|
|
{ date: "2025-01-12", rank: 2, points: 125 },
|
|
{ date: "2025-01-13", rank: 1, points: 150 },
|
|
];
|
|
|
|
// Verify progression shows improvement
|
|
for (let i = 1; i < history.length; i++) {
|
|
expect(history[i].rank).toBeLessThan(history[i - 1].rank);
|
|
expect(history[i].points).toBeGreaterThan(history[i - 1].points);
|
|
}
|
|
});
|
|
});
|
|
});
|