Stage 5: Add Prisma integration and enhance mission management

- Introduced Prisma as a dependency in package.json and updated pnpm-lock.yaml.
- Created Prisma module and service for database interactions.
- Added initial Prisma schema and migration for user, survivor, mission, and related entities.
- Implemented throttling in the API using @nestjs/throttler for rate limiting.
- Enhanced mission management logic to utilize Prisma for database transactions.
- Updated missions controller and service to handle mission state and participant management.
- Added Twitch PubSub service for real-time updates on mission states.
This commit is contained in:
Maurycy
2026-05-07 15:42:52 +00:00
parent e8523d270e
commit 21f1a5319f
22 changed files with 1676 additions and 96 deletions

View File

@@ -198,6 +198,69 @@ describe('resolveEncounter', () => {
expect(result.logText.length).toBeGreaterThan(0);
}
});
// Edge cases: modifier overflow / degenerate inputs
it('massive positive perk modifier clamps probability to 1 — never throws', () => {
const perk: Perk = {
id: '00000000-0000-4000-a000-000000000003',
key: 'overpowered',
name: '',
description: '',
tags: [],
modifiers: [{ target: 'successChance', type: 'additive', amount: 100 }],
};
const result = resolveEncounter(
makeInput({ encounter: { key: 'gen', baseProbability: 0.5, tags: [] },
survivor: { id: '00000000-0000-4000-a000-000000000002', state: 'active',
stats: { objectives: 5, survival: 5, altruism: 5 }, perkSlots: [perk], hookCount: 0 } })
);
expect(result.success).toBe(true); // clamped to 1 → always succeeds
});
it('massive negative perk modifier clamps probability to 0 — never throws', () => {
const perk: Perk = {
id: '00000000-0000-4000-a000-000000000003',
key: 'cursed',
name: '',
description: '',
tags: [],
modifiers: [{ target: 'successChance', type: 'additive', amount: -100 }],
};
const result = resolveEncounter(
makeInput({ encounter: { key: 'gen', baseProbability: 0.5, tags: [] },
survivor: { id: '00000000-0000-4000-a000-000000000002', state: 'active',
stats: { objectives: 5, survival: 5, altruism: 5 }, perkSlots: [perk], hookCount: 0 } })
);
expect(result.success).toBe(false); // clamped to 0 → always fails
});
it('empty perkSlots array is handled without error', () => {
expect(() =>
resolveEncounter(makeInput({ survivor: {
id: '00000000-0000-4000-a000-000000000002', state: 'active',
stats: { objectives: 5, survival: 5, altruism: 5 }, perkSlots: [], hookCount: 0,
} }))
).not.toThrow();
});
it('sacrificed survivor produces no state change on failure', () => {
const result = resolveEncounter(makeInput({
encounter: { key: 'gen', baseProbability: 0, tags: [] },
survivor: { id: '00000000-0000-4000-a000-000000000002', state: 'sacrificed',
stats: { objectives: 1, survival: 1, altruism: 1 }, perkSlots: [], hookCount: 2 },
}));
expect(result.success).toBe(false);
expect(result.survivorStateChange).toBeNull();
});
it('idle survivor produces no state change on failure', () => {
const result = resolveEncounter(makeInput({
encounter: { key: 'gen', baseProbability: 0, tags: [] },
survivor: { id: '00000000-0000-4000-a000-000000000002', state: 'idle',
stats: { objectives: 1, survival: 1, altruism: 1 }, perkSlots: [], hookCount: 0 },
}));
expect(result.survivorStateChange).toBeNull();
});
});
// ── Property-based tests ──────────────────────────────────────────────────────