Changing speedy reservation to use court ranking and adding some tests for selection of court

This commit is contained in:
Collin Duncan 2024-05-22 11:09:59 +02:00
parent 6dfe776b14
commit 696aa590ab
No known key found for this signature in database
2 changed files with 147 additions and 8 deletions

View file

@ -12,7 +12,7 @@ import { MonitorType } from '../../monitoring/entity'
import { Opponent, Reservation } from '../../reservations/entity' import { Opponent, Reservation } from '../../reservations/entity'
import { EmptyPage } from '../pages/empty' import { EmptyPage } from '../pages/empty'
const BAAN_RESERVEREN_ROOT_URL = 'https://squashcity.baanreserveren.nl' export const BAAN_RESERVEREN_ROOT_URL = 'https://squashcity.baanreserveren.nl'
export enum BaanReserverenUrls { export enum BaanReserverenUrls {
Reservations = '/reservations', Reservations = '/reservations',
@ -33,7 +33,7 @@ interface BaanReserverenSession {
} }
// TODO: Add to DB to make configurable // TODO: Add to DB to make configurable
enum CourtSlot { export enum CourtSlot {
One = '51', One = '51',
Two = '52', Two = '52',
Three = '53', Three = '53',
@ -67,8 +67,8 @@ const CourtSlotToNumber: Record<CourtSlot, number> = {
// Lower is better // Lower is better
const CourtRank: Record<CourtSlot, number> = { const CourtRank: Record<CourtSlot, number> = {
[CourtSlot.One]: 0, [CourtSlot.One]: 2,
[CourtSlot.Two]: 0, [CourtSlot.Two]: 1,
[CourtSlot.Three]: 0, [CourtSlot.Three]: 0,
[CourtSlot.Four]: 0, [CourtSlot.Four]: 0,
[CourtSlot.Five]: 99, // shitty [CourtSlot.Five]: 99, // shitty
@ -77,9 +77,9 @@ const CourtRank: Record<CourtSlot, number> = {
[CourtSlot.Eight]: 0, [CourtSlot.Eight]: 0,
[CourtSlot.Nine]: 0, [CourtSlot.Nine]: 0,
[CourtSlot.Ten]: 0, [CourtSlot.Ten]: 0,
[CourtSlot.Eleven]: 1, // no one likes upstairs [CourtSlot.Eleven]: 10, // no one likes upstairs
[CourtSlot.Twelve]: 1, // no one likes upstairs [CourtSlot.Twelve]: 9, // no one likes upstairs
[CourtSlot.Thirteen]: 1, // no one likes upstairs [CourtSlot.Thirteen]: 9, // no one likes upstairs
} as const } as const
enum StartTimeClass { enum StartTimeClass {
@ -680,7 +680,11 @@ export class BaanReserverenService {
const time = date.format('HH:mm') const time = date.format('HH:mm')
for (const [timeClass, times] of Object.entries(StartTimeClassStartTimes)) { for (const [timeClass, times] of Object.entries(StartTimeClassStartTimes)) {
if (times.includes(time)) { if (times.includes(time)) {
return StartTimeClassCourtSlots[timeClass as StartTimeClass] const courtSlots = [
...StartTimeClassCourtSlots[timeClass as StartTimeClass],
]
// sort by ranking
return courtSlots.sort((a, b) => CourtRank[a] - CourtRank[b])
} }
} }
} }

View file

@ -0,0 +1,135 @@
import { getQueueToken } from '@nestjs/bull'
import { ConfigService } from '@nestjs/config'
import { Test, TestingModule } from '@nestjs/testing'
import dayjs from '../../../src/common/dayjs'
import { LoggerService } from '../../../src/logger/service.logger'
import { MONITORING_QUEUE_NAME } from '../../../src/monitoring/config'
import { Reservation } from '../../../src/reservations/entity'
import {
BAAN_RESERVEREN_ROOT_URL,
BaanReserverenService,
CourtSlot,
} from '../../../src/runner/baanreserveren/service'
import { EmptyPage } from '../../../src/runner/pages/empty'
describe('baanreserveren.service', () => {
let module: TestingModule
let pageGotoSpy: jest.SpyInstance
let brService: BaanReserverenService
beforeAll(async () => {
pageGotoSpy = jest
.fn()
.mockImplementation(() => Promise.resolve({ status: () => 200 }))
module = await Test.createTestingModule({
providers: [
BaanReserverenService,
{
provide: ConfigService,
useValue: { getOrThrow: jest.fn().mockReturnValue('test') },
},
{
provide: LoggerService,
useValue: { debug: jest.fn(), warn: jest.fn() },
},
{
provide: EmptyPage,
useValue: {
waitForNetworkIdle: jest.fn().mockResolvedValue(null),
waitForSelector: jest.fn().mockResolvedValue(undefined),
goto: pageGotoSpy,
url: jest
.fn()
.mockReturnValue({ includes: jest.fn().mockReturnValue(true) }),
$: jest.fn().mockResolvedValue(undefined),
},
},
{
provide: getQueueToken(MONITORING_QUEUE_NAME),
useValue: { add: jest.fn() },
},
],
}).compile()
brService = module.get<BaanReserverenService>(BaanReserverenService)
})
describe('performSpeedyReservation', () => {
it.each([
[18, 15, CourtSlot.Seven, CourtSlot.Six],
[18, 30, CourtSlot.Three, CourtSlot.One],
[18, 45, CourtSlot.Twelve, CourtSlot.Thirteen],
])(
'should try highest ranked court first',
async (startHour, startMinute, preferredCourt, backupCourt) => {
const start = dayjs()
.set('hour', startHour)
.set('minute', startMinute)
.set('second', 0)
.set('millisecond', 0)
const reservation = new Reservation({
id: '1',
ownerId: '1',
dateRangeStart: start,
dateRangeEnd: start.add(45, 'minute'),
opponents: [],
})
await brService.performSpeedyReservation(reservation)
expect(pageGotoSpy).toHaveBeenCalledWith(
`${BAAN_RESERVEREN_ROOT_URL}/reservations/make/${preferredCourt}/${
start.valueOf() / 1000
}`,
)
expect(pageGotoSpy).not.toHaveBeenCalledWith(
`${BAAN_RESERVEREN_ROOT_URL}/reservations/make/${backupCourt}/${
start.valueOf() / 1000
}`,
)
},
)
it.each([
[18, 15, CourtSlot.Seven, CourtSlot.Eight],
[18, 30, CourtSlot.Three, CourtSlot.Four],
[18, 45, CourtSlot.Twelve, CourtSlot.Thirteen],
])(
'should try backup if first rank is taken',
async (startHour, startMinute, preferredCourt, backupCourt) => {
pageGotoSpy.mockImplementation((url: string) => {
if (
url ===
`${BAAN_RESERVEREN_ROOT_URL}/reservations/make/${preferredCourt}/${
start.valueOf() / 1000
}`
) {
return Promise.resolve({ status: () => 400 }) // fail on the preferred court
}
return Promise.resolve({ status: () => 200 })
})
const start = dayjs()
.set('hour', startHour)
.set('minute', startMinute)
.set('second', 0)
.set('millisecond', 0)
const reservation = new Reservation({
id: '1',
ownerId: '1',
dateRangeStart: start,
dateRangeEnd: start.add(45, 'minute'),
opponents: [],
})
await brService.performSpeedyReservation(reservation)
expect(pageGotoSpy).toHaveBeenCalledWith(
`${BAAN_RESERVEREN_ROOT_URL}/reservations/make/${preferredCourt}/${
start.valueOf() / 1000
}`,
)
expect(pageGotoSpy).toHaveBeenCalledWith(
`${BAAN_RESERVEREN_ROOT_URL}/reservations/make/${backupCourt}/${
start.valueOf() / 1000
}`,
)
},
)
})
})