187 lines
5.2 KiB
TypeScript
187 lines
5.2 KiB
TypeScript
/**
|
|
* @vitest-environment node
|
|
*/
|
|
|
|
import { eq } from 'drizzle-orm'
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
import { db, schema } from '../src/db'
|
|
|
|
/**
|
|
* API User Stats E2E Tests
|
|
*
|
|
* These tests verify the user-stats API endpoints work correctly.
|
|
*/
|
|
|
|
describe('User Stats API', () => {
|
|
let testUserId: string
|
|
let testGuestId: string
|
|
|
|
beforeEach(async () => {
|
|
// Create a test user with unique guest ID
|
|
testGuestId = `test-guest-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
const [user] = await db.insert(schema.users).values({ guestId: testGuestId }).returning()
|
|
testUserId = user.id
|
|
})
|
|
|
|
afterEach(async () => {
|
|
// Clean up: delete test user (cascade deletes stats)
|
|
await db.delete(schema.users).where(eq(schema.users.id, testUserId))
|
|
})
|
|
|
|
describe('GET /api/user-stats', () => {
|
|
it('creates stats with defaults if none exist', async () => {
|
|
const [stats] = await db.insert(schema.userStats).values({ userId: testUserId }).returning()
|
|
|
|
expect(stats).toBeDefined()
|
|
expect(stats.gamesPlayed).toBe(0)
|
|
expect(stats.totalWins).toBe(0)
|
|
expect(stats.favoriteGameType).toBeNull()
|
|
expect(stats.bestTime).toBeNull()
|
|
expect(stats.highestAccuracy).toBe(0)
|
|
})
|
|
|
|
it('returns existing stats', async () => {
|
|
// Create stats
|
|
await db.insert(schema.userStats).values({
|
|
userId: testUserId,
|
|
gamesPlayed: 10,
|
|
totalWins: 7,
|
|
favoriteGameType: 'abacus-numeral',
|
|
bestTime: 5000,
|
|
highestAccuracy: 0.95,
|
|
})
|
|
|
|
const stats = await db.query.userStats.findFirst({
|
|
where: eq(schema.userStats.userId, testUserId),
|
|
})
|
|
|
|
expect(stats).toBeDefined()
|
|
expect(stats?.gamesPlayed).toBe(10)
|
|
expect(stats?.totalWins).toBe(7)
|
|
expect(stats?.favoriteGameType).toBe('abacus-numeral')
|
|
expect(stats?.bestTime).toBe(5000)
|
|
expect(stats?.highestAccuracy).toBe(0.95)
|
|
})
|
|
})
|
|
|
|
describe('PATCH /api/user-stats', () => {
|
|
it('creates new stats if none exist', async () => {
|
|
const [stats] = await db
|
|
.insert(schema.userStats)
|
|
.values({
|
|
userId: testUserId,
|
|
gamesPlayed: 1,
|
|
totalWins: 1,
|
|
})
|
|
.returning()
|
|
|
|
expect(stats).toBeDefined()
|
|
expect(stats.gamesPlayed).toBe(1)
|
|
expect(stats.totalWins).toBe(1)
|
|
})
|
|
|
|
it('updates existing stats', async () => {
|
|
// Create initial stats
|
|
await db.insert(schema.userStats).values({
|
|
userId: testUserId,
|
|
gamesPlayed: 5,
|
|
totalWins: 3,
|
|
})
|
|
|
|
// Update
|
|
const [updated] = await db
|
|
.update(schema.userStats)
|
|
.set({
|
|
gamesPlayed: 6,
|
|
totalWins: 4,
|
|
favoriteGameType: 'complement-pairs',
|
|
})
|
|
.where(eq(schema.userStats.userId, testUserId))
|
|
.returning()
|
|
|
|
expect(updated.gamesPlayed).toBe(6)
|
|
expect(updated.totalWins).toBe(4)
|
|
expect(updated.favoriteGameType).toBe('complement-pairs')
|
|
})
|
|
|
|
it('updates only provided fields', async () => {
|
|
// Create initial stats
|
|
await db.insert(schema.userStats).values({
|
|
userId: testUserId,
|
|
gamesPlayed: 10,
|
|
totalWins: 5,
|
|
bestTime: 3000,
|
|
})
|
|
|
|
// Update only gamesPlayed
|
|
const [updated] = await db
|
|
.update(schema.userStats)
|
|
.set({ gamesPlayed: 11 })
|
|
.where(eq(schema.userStats.userId, testUserId))
|
|
.returning()
|
|
|
|
expect(updated.gamesPlayed).toBe(11)
|
|
expect(updated.totalWins).toBe(5) // unchanged
|
|
expect(updated.bestTime).toBe(3000) // unchanged
|
|
})
|
|
|
|
it('allows setting favoriteGameType', async () => {
|
|
await db.insert(schema.userStats).values({
|
|
userId: testUserId,
|
|
})
|
|
|
|
const [updated] = await db
|
|
.update(schema.userStats)
|
|
.set({ favoriteGameType: 'abacus-numeral' })
|
|
.where(eq(schema.userStats.userId, testUserId))
|
|
.returning()
|
|
|
|
expect(updated.favoriteGameType).toBe('abacus-numeral')
|
|
})
|
|
|
|
it('allows setting bestTime and highestAccuracy', async () => {
|
|
await db.insert(schema.userStats).values({
|
|
userId: testUserId,
|
|
})
|
|
|
|
const [updated] = await db
|
|
.update(schema.userStats)
|
|
.set({
|
|
bestTime: 2500,
|
|
highestAccuracy: 0.98,
|
|
})
|
|
.where(eq(schema.userStats.userId, testUserId))
|
|
.returning()
|
|
|
|
expect(updated.bestTime).toBe(2500)
|
|
expect(updated.highestAccuracy).toBe(0.98)
|
|
})
|
|
})
|
|
|
|
describe('Cascade delete behavior', () => {
|
|
it('deletes stats when user is deleted', async () => {
|
|
// Create stats
|
|
await db.insert(schema.userStats).values({
|
|
userId: testUserId,
|
|
gamesPlayed: 10,
|
|
totalWins: 5,
|
|
})
|
|
|
|
// Verify stats exist
|
|
let stats = await db.query.userStats.findFirst({
|
|
where: eq(schema.userStats.userId, testUserId),
|
|
})
|
|
expect(stats).toBeDefined()
|
|
|
|
// Delete user
|
|
await db.delete(schema.users).where(eq(schema.users.id, testUserId))
|
|
|
|
// Verify stats are gone
|
|
stats = await db.query.userStats.findFirst({
|
|
where: eq(schema.userStats.userId, testUserId),
|
|
})
|
|
expect(stats).toBeUndefined()
|
|
})
|
|
})
|
|
})
|