diff --git a/server/src/modules/leveling/lib/apply-attribute-boost.spec.ts b/server/src/modules/leveling/lib/apply-attribute-boost.spec.ts new file mode 100644 index 0000000..034b29e --- /dev/null +++ b/server/src/modules/leveling/lib/apply-attribute-boost.spec.ts @@ -0,0 +1,41 @@ +import { applyAttributeBoost, isValidBoostSet } from './apply-attribute-boost'; + +describe('applyAttributeBoost', () => { + it('adds +2 when score is 10 (below 18)', () => { + expect(applyAttributeBoost(10)).toBe(12); + }); + + it('adds +2 when score is 17 (still below 18)', () => { + expect(applyAttributeBoost(17)).toBe(19); + }); + + it('adds +1 when score is exactly 18 (PF2e cap rule — Pitfall #8)', () => { + expect(applyAttributeBoost(18)).toBe(19); + }); + + it('adds +1 when score is 20 (above cap)', () => { + expect(applyAttributeBoost(20)).toBe(21); + }); + + it('handles edge case: very high score (24)', () => { + expect(applyAttributeBoost(24)).toBe(25); + }); +}); + +describe('isValidBoostSet', () => { + it('accepts exactly 4 distinct abilities', () => { + expect(isValidBoostSet(['STR', 'DEX', 'CON', 'INT'])).toBe(true); + }); + + it('rejects duplicates (3 distinct + 1 repeat)', () => { + expect(isValidBoostSet(['STR', 'STR', 'CON', 'INT'])).toBe(false); + }); + + it('rejects too few (3 abilities)', () => { + expect(isValidBoostSet(['STR', 'DEX', 'CON'])).toBe(false); + }); + + it('rejects too many (5 abilities)', () => { + expect(isValidBoostSet(['STR', 'DEX', 'CON', 'INT', 'WIS'])).toBe(false); + }); +});