Implement getHistoricalAveragesOfParkingStructureCounts for InMemoryParkingRepository.ts, and add tests

This commit is contained in:
2025-07-02 19:10:16 -04:00
parent 8b5d80a9d6
commit ca2a66509b
3 changed files with 167 additions and 4 deletions

View File

@@ -5,6 +5,7 @@ import {
} from "../../src/repositories/InMemoryParkingRepository";
import { IParkingStructure, IParkingStructureTimestampRecord } from "../../src/entities/ParkingRepositoryEntities";
import { CircularQueue } from "../../src/types/CircularQueue";
import { ParkingStructureCountOptions } from "../../src/repositories/ParkingGetterRepository";
describe("InMemoryParkingRepository", () => {
let repository: InMemoryParkingRepository;
@@ -145,4 +146,112 @@ describe("InMemoryParkingRepository", () => {
expect(result).toEqual(testStructure);
});
});
describe("getHistoricalAveragesOfParkingStructureCounts", () => {
const sortingCallback = (a: IParkingStructureTimestampRecord, b: IParkingStructureTimestampRecord) => a.timestampMs - b.timestampMs;
const setupHistoricalData = (records: IParkingStructureTimestampRecord[]) => {
const queue = new CircularQueue<IParkingStructureTimestampRecord>(10);
records.forEach(record => queue.appendWithSorting(record, sortingCallback));
historicalData.set(testStructure.id, queue);
};
it("should return empty array for non-existent structure or no data", async () => {
const options: ParkingStructureCountOptions = {
startUnixEpochMs: 1000,
endUnixEpochMs: 2000,
intervalMs: 500
};
// Non-existent structure
expect(await repository.getHistoricalAveragesOfParkingStructureCounts("non-existent", options)).toEqual([]);
// Structure with no historical data
await repository.addOrUpdateParkingStructure(testStructure);
expect(await repository.getHistoricalAveragesOfParkingStructureCounts(testStructure.id, options)).toEqual([]);
});
it("should calculate averages for single and multiple intervals", async () => {
const records = [
{ id: testStructure.id, spotsAvailable: 80, timestampMs: 1100 },
{ id: testStructure.id, spotsAvailable: 70, timestampMs: 1200 },
{ id: testStructure.id, spotsAvailable: 50, timestampMs: 1600 },
{ id: testStructure.id, spotsAvailable: 40, timestampMs: 1700 }
];
setupHistoricalData(records);
await repository.addOrUpdateParkingStructure(testStructure);
// Single interval test
const singleIntervalOptions: ParkingStructureCountOptions = {
startUnixEpochMs: 1000,
endUnixEpochMs: 1500,
intervalMs: 500
};
const singleResult = await repository.getHistoricalAveragesOfParkingStructureCounts(testStructure.id, singleIntervalOptions);
expect(singleResult).toHaveLength(1);
expect(singleResult[0]).toEqual({
fromUnixEpochMs: 1000,
toUnixEpochMs: 1500,
averageSpotsAvailable: 75 // (80 + 70) / 2
});
// Multiple intervals test
const multipleIntervalOptions: ParkingStructureCountOptions = {
startUnixEpochMs: 1000,
endUnixEpochMs: 2000,
intervalMs: 500
};
const multipleResult = await repository.getHistoricalAveragesOfParkingStructureCounts(testStructure.id, multipleIntervalOptions);
expect(multipleResult).toHaveLength(2);
expect(multipleResult[0]).toEqual({
fromUnixEpochMs: 1000,
toUnixEpochMs: 1500,
averageSpotsAvailable: 75 // (80 + 70) / 2
});
expect(multipleResult[1]).toEqual({
fromUnixEpochMs: 1500,
toUnixEpochMs: 2000,
averageSpotsAvailable: 45 // (50 + 40) / 2
});
});
it("should handle edge cases: skipped intervals and boundaries", async () => {
const records = [
{ id: testStructure.id, spotsAvailable: 90, timestampMs: 1000 }, // start boundary - included
{ id: testStructure.id, spotsAvailable: 80, timestampMs: 1500 }, // interval boundary - included in second
{ id: testStructure.id, spotsAvailable: 60, timestampMs: 2100 } // skip interval, in third
];
setupHistoricalData(records);
await repository.addOrUpdateParkingStructure(testStructure);
const options: ParkingStructureCountOptions = {
startUnixEpochMs: 1000,
endUnixEpochMs: 2500,
intervalMs: 500
};
const result = await repository.getHistoricalAveragesOfParkingStructureCounts(testStructure.id, options);
expect(result).toHaveLength(3);
expect(result[0]).toEqual({
fromUnixEpochMs: 1000,
toUnixEpochMs: 1500,
averageSpotsAvailable: 90 // only record at 1000ms
});
expect(result[1]).toEqual({
fromUnixEpochMs: 1500,
toUnixEpochMs: 2000,
averageSpotsAvailable: 80 // only record at 1500ms
});
expect(result[2]).toEqual({
fromUnixEpochMs: 2000,
toUnixEpochMs: 2500,
averageSpotsAvailable: 60 // only record at 2100ms
});
});
});
});