Files
botfights/server/src/routes/bots-cache.test.ts
T

103 lines
4.0 KiB
TypeScript
Raw Normal View History

import { describe, it, expect, vi, beforeEach } from 'vitest'
// Mock DB and dependencies to import bots module
vi.mock('../db/index.js', () => ({
db: {
select: vi.fn().mockReturnValue({
from: vi.fn().mockReturnValue({
where: vi.fn().mockReturnValue({
limit: vi.fn().mockResolvedValue([]),
}),
}),
}),
},
schema: {
bots: { id: 'id', name: 'name', publicKey: 'publicKey', eloRating: 'eloRating', wins: 'wins', losses: 'losses', winStreak: 'winStreak', tier: 'tier', avatarSeed: 'avatarSeed', archetype: 'archetype', botType: 'botType', hasWallet: 'hasWallet', zapsReceived: 'zapsReceived', isActive: 'isActive', webhookUrl: 'webhookUrl', secretHash: 'secretHash', bestStreak: 'bestStreak', satsWagered: 'satsWagered' },
walletConnections: { id: 'id', botId: 'botId' },
},
}))
vi.mock('../engine/scoring.js', () => ({
TIER_NAMES: ['Baby', 'Bronze', 'Silver', 'Gold', 'Platinum', 'Diamond', 'Legend'],
TIER_COLORS: ['#999', '#cd7f32', '#c0c0c0', '#ffd700', '#e5e4e2', '#b9f2ff', '#ff6b6b'],
}))
vi.mock('../engine/achievements.js', () => ({
computeAchievements: vi.fn().mockReturnValue([]),
}))
vi.mock('../engine/orchestrator.js', () => ({
isAllowedWebhookUrl: vi.fn().mockReturnValue(true),
}))
vi.mock('../engine/webhook-test.js', () => ({
testWebhook: vi.fn().mockResolvedValue({ success: true }),
}))
vi.mock('../middleware/rate-limit.js', () => ({
rateLimit: () => async (_c: any, next: any) => next(),
}))
const { invalidateLeaderboardCache, _leaderboardCache } = await import('./bots.js')
describe('leaderboard cache selective invalidation', () => {
beforeEach(() => {
_leaderboardCache.clear()
})
it('invalidates __alltime__ and current but preserves historical season', () => {
const now = Date.now()
_leaderboardCache.set('__alltime__', { data: 'alltime', expiresAt: now + 30_000 })
_leaderboardCache.set('current', { data: 'current-season', expiresAt: now + 30_000 })
_leaderboardCache.set('s1', { data: 'season-1-history', expiresAt: now + 30_000 })
_leaderboardCache.set('s2', { data: 'season-2-history', expiresAt: now + 30_000 })
invalidateLeaderboardCache()
expect(_leaderboardCache.has('__alltime__')).toBe(false)
expect(_leaderboardCache.has('current')).toBe(false)
// Historical seasons preserved
expect(_leaderboardCache.has('s1')).toBe(true)
expect(_leaderboardCache.has('s2')).toBe(true)
})
it('also invalidates specific season when seasonId passed', () => {
const now = Date.now()
_leaderboardCache.set('__alltime__', { data: 'alltime', expiresAt: now + 30_000 })
_leaderboardCache.set('current', { data: 'current', expiresAt: now + 30_000 })
_leaderboardCache.set('s1', { data: 'season-1', expiresAt: now + 30_000 })
_leaderboardCache.set('s2', { data: 'season-2', expiresAt: now + 30_000 })
invalidateLeaderboardCache('s1')
expect(_leaderboardCache.has('__alltime__')).toBe(false)
expect(_leaderboardCache.has('current')).toBe(false)
expect(_leaderboardCache.has('s1')).toBe(false)
// Other seasons preserved
expect(_leaderboardCache.has('s2')).toBe(true)
})
it('no-op on empty cache', () => {
expect(_leaderboardCache.size).toBe(0)
invalidateLeaderboardCache()
expect(_leaderboardCache.size).toBe(0)
})
it('only removes targeted keys, leaves other entries', () => {
const now = Date.now()
// 5 cached entries
_leaderboardCache.set('__alltime__', { data: 'a', expiresAt: now + 30_000 })
_leaderboardCache.set('current', { data: 'c', expiresAt: now + 30_000 })
_leaderboardCache.set('s1', { data: '1', expiresAt: now + 30_000 })
_leaderboardCache.set('s2', { data: '2', expiresAt: now + 30_000 })
_leaderboardCache.set('s3', { data: '3', expiresAt: now + 30_000 })
invalidateLeaderboardCache('s2')
// Removed: __alltime__, current, s2
expect(_leaderboardCache.size).toBe(2)
expect(_leaderboardCache.has('s1')).toBe(true)
expect(_leaderboardCache.has('s3')).toBe(true)
})
})