| import { describe, it, expect, beforeEach } from 'vitest'; |
| import { EncounterService } from '../lib/db/encounterService'; |
| import { db } from '../lib/db'; |
| import { EncounterType } from '../lib/db/schema'; |
| import type { PicletInstance } from '../lib/db/schema'; |
|
|
| describe('EncounterService', () => { |
| beforeEach(async () => { |
| |
| await db.picletInstances.clear(); |
| await db.encounters.clear(); |
| await db.gameState.clear(); |
| }); |
|
|
| describe('generateEncounters', () => { |
| it('should return empty array when no piclets are discovered', async () => { |
| |
| const picletCount = await db.picletInstances.count(); |
| expect(picletCount).toBe(0); |
|
|
| |
| const encounters = await EncounterService.generateEncounters(); |
|
|
| |
| expect(encounters).toHaveLength(0); |
| |
| |
| const dbEncounters = await db.encounters.toArray(); |
| expect(dbEncounters).toHaveLength(0); |
| }); |
|
|
| it('should return only "Your First Piclet!" when piclets are discovered but not caught', async () => { |
| |
| const testPiclet: Omit<PicletInstance, 'id'> = { |
| typeId: 'test-piclet', |
| nickname: 'Test Piclet', |
| primaryType: 'beast' as any, |
| currentHp: 100, |
| maxHp: 100, |
| level: 5, |
| xp: 0, |
| attack: 50, |
| defense: 50, |
| fieldAttack: 40, |
| fieldDefense: 40, |
| speed: 50, |
| baseHp: 100, |
| baseAttack: 50, |
| baseDefense: 50, |
| baseFieldAttack: 40, |
| baseFieldDefense: 40, |
| baseSpeed: 50, |
| moves: [], |
| nature: 'hardy', |
| specialAbility: { name: 'Test', description: 'Test', trigger: 'onBattleStart', effects: [] }, |
| specialAbilityUnlockLevel: 1, |
| isInRoster: false, |
| caught: false, |
| bst: 300, |
| tier: 'low', |
| role: 'balanced', |
| variance: 0, |
| imageUrl: 'https://test.com/piclet.png', |
| imageCaption: 'test caption', |
| concept: 'test', |
| description: 'A test piclet', |
| imagePrompt: 'test prompt' |
| }; |
| await db.picletInstances.add(testPiclet); |
|
|
| |
| const encounters = await EncounterService.generateEncounters(); |
|
|
| |
| expect(encounters).toHaveLength(1); |
| expect(encounters[0].type).toBe(EncounterType.WILD_PICLET); |
| expect(encounters[0].title).toBe('Your First Piclet!'); |
| expect(encounters[0].description).toBe('A friendly piclet appears! This one seems easy to catch.'); |
| expect(encounters[0].enemyLevel).toBe(5); |
| }); |
|
|
| it('should return shop, health center, and wild encounters when player has caught piclets', async () => { |
| |
| const testPiclet: Omit<PicletInstance, 'id'> = { |
| typeId: 'test-001', |
| nickname: 'Testy', |
| primaryType: 'beast' as any, |
| level: 5, |
| xp: 0, |
| currentHp: 20, |
| maxHp: 20, |
| attack: 10, |
| defense: 10, |
| fieldAttack: 10, |
| fieldDefense: 10, |
| speed: 10, |
| baseHp: 20, |
| baseAttack: 10, |
| baseDefense: 10, |
| baseFieldAttack: 10, |
| baseFieldDefense: 10, |
| baseSpeed: 10, |
| moves: [], |
| nature: 'hardy', |
| isInRoster: true, |
| rosterPosition: 0, |
| caughtAt: new Date(), |
| bst: 60, |
| tier: 'common', |
| role: 'balanced', |
| variance: 1, |
| imageUrl: 'https://test.com/piclet.png', |
| imageCaption: 'Test', |
| concept: 'test', |
| imagePrompt: 'test' |
| }; |
| await db.picletInstances.add(testPiclet); |
|
|
| |
| const testMonster = { |
| name: 'Test Monster', |
| imageUrl: 'https://test.com/monster.png', |
| imageCaption: 'A test monster', |
| concept: 'test concept', |
| imagePrompt: 'test prompt', |
| createdAt: new Date() |
| }; |
| await db.monsters.add(testMonster); |
|
|
| |
| const encounters = await EncounterService.generateEncounters(); |
|
|
| |
| expect(encounters.length).toBeGreaterThanOrEqual(4); |
| |
| |
| const encounterTypes = encounters.map(e => e.type); |
| expect(encounterTypes).toContain(EncounterType.SHOP); |
| expect(encounterTypes).toContain(EncounterType.HEALTH_CENTER); |
| |
| |
| const wildCount = encounters.filter(e => e.type === EncounterType.WILD_PICLET).length; |
| expect(wildCount).toBeGreaterThanOrEqual(2); |
| expect(wildCount).toBeLessThanOrEqual(3); |
| |
| |
| const shopEncounter = encounters.find(e => e.type === EncounterType.SHOP); |
| expect(shopEncounter?.title).toBe('Piclet Shop'); |
| |
| |
| const healthEncounter = encounters.find(e => e.type === EncounterType.HEALTH_CENTER); |
| expect(healthEncounter?.title).toBe('Health Center'); |
| }); |
|
|
| it('should not include shop/health center with first catch encounter', async () => { |
| |
| await db.monsters.add({ |
| name: 'Discovered Piclet', |
| imageUrl: 'https://test.com/discovered.png', |
| concept: 'discovered', |
| imagePrompt: 'discovered prompt', |
| imageCaption: 'discovered caption', |
| createdAt: new Date() |
| }); |
|
|
| |
| const caughtCount = await db.picletInstances.count(); |
| expect(caughtCount).toBe(0); |
|
|
| |
| const encounters = await EncounterService.generateEncounters(); |
|
|
| |
| expect(encounters).toHaveLength(1); |
| expect(encounters[0].title).toBe('Your First Piclet!'); |
| |
| |
| const hasShop = encounters.some(e => e.type === EncounterType.SHOP); |
| const hasHealthCenter = encounters.some(e => e.type === EncounterType.HEALTH_CENTER); |
| expect(hasShop).toBe(false); |
| expect(hasHealthCenter).toBe(false); |
| }); |
| }); |
|
|
| describe('shouldRefreshEncounters', () => { |
| it('should return true after 2 hours', async () => { |
| |
| const twoHoursAgo = new Date(Date.now() - (2.5 * 60 * 60 * 1000)); |
| await db.gameState.add({ |
| lastEncounterRefresh: twoHoursAgo, |
| lastPlayed: new Date(), |
| progressPoints: 0, |
| trainersDefeated: 0, |
| picletsCapured: 0, |
| battlesLost: 0 |
| }); |
|
|
| |
| const shouldRefresh = await EncounterService.shouldRefreshEncounters(); |
|
|
| |
| expect(shouldRefresh).toBe(true); |
| }); |
|
|
| it('should return false within 2 hours', async () => { |
| |
| const oneHourAgo = new Date(Date.now() - (1 * 60 * 60 * 1000)); |
| await db.gameState.add({ |
| lastEncounterRefresh: oneHourAgo, |
| lastPlayed: new Date(), |
| progressPoints: 0, |
| trainersDefeated: 0, |
| picletsCapured: 0, |
| battlesLost: 0 |
| }); |
|
|
| |
| const shouldRefresh = await EncounterService.shouldRefreshEncounters(); |
|
|
| |
| expect(shouldRefresh).toBe(false); |
| }); |
| }); |
| }); |