rpg-combat-pi-01/src/character-creation.spec.ts
Willem van den Ende d3cc48c0f7 feat: Character Creation user story — ADTs, properties, and Allium spec
- Level value object (1..10, level-capped health)
- Health value object (non-negative, pure sub/add)
- Status discriminated union (alive | dead)
- Character entity with private constructor and static factory
- 9 fast-check properties covering creation invariants
- Allium spec with entities, rules, and invariants
2026-06-12 20:23:48 +01:00

112 lines
3.4 KiB
TypeScript

import fc from 'fast-check';
import { describe, it } from 'vitest';
import { Character } from './Character.ts';
import { Level } from './Level.ts';
describe('CharacterCreation', () => {
describe('initial health', () => {
it('property: new character has health 1000', () => {
fc.assert(
fc.property(fc.string({ minLength: 1, maxLength: 50 }), (name) => {
const c = Character.create({ name, level: Level.create(1) });
return c.health.value === 1000;
}),
);
});
it('property: health is always 1000 at creation regardless of level', () => {
fc.assert(
fc.property(
fc.string({ minLength: 1, maxLength: 50 }),
fc.integer({ min: 1, max: 10 }),
(name, level) => {
const c = Character.create({ name, level: Level.create(level) });
return c.health.value === 1000;
},
),
);
});
});
describe('initial status', () => {
it('property: new character is always alive', () => {
fc.assert(
fc.property(fc.string({ minLength: 1, maxLength: 50 }), (name) => {
const c = Character.create({ name, level: Level.create(1) });
return c.status.kind === 'alive';
}),
);
});
});
describe('initial level', () => {
it('property: character stores the level given at creation', () => {
fc.assert(
fc.property(
fc.string({ minLength: 1, maxLength: 50 }),
fc.integer({ min: 1, max: 10 }),
(name, level) => {
const c = Character.create({ name, level: Level.create(level) });
return c.level.value === level;
},
),
);
});
});
describe('initial factions', () => {
it('property: new character belongs to no factions', () => {
fc.assert(
fc.property(fc.string({ minLength: 1, maxLength: 50 }), (name) => {
const c = Character.create({ name, level: Level.create(1) });
return c.factions.size === 0;
}),
);
});
});
describe('invariants', () => {
it('property: health is never negative on a new character', () => {
fc.assert(
fc.property(fc.string({ minLength: 1, maxLength: 50 }), (name) => {
const c = Character.create({ name, level: Level.create(1) });
return c.health.value >= 0;
}),
);
});
it('property: alive characters have positive health', () => {
fc.assert(
fc.property(fc.string({ minLength: 1, maxLength: 50 }), (name) => {
const c = Character.create({ name, level: Level.create(1) });
if (c.status.kind === 'alive') {
return c.health.value > 0;
}
return true;
}),
);
});
it('property: dead characters have zero health', () => {
fc.assert(
fc.property(fc.string({ minLength: 1, maxLength: 50 }), (name) => {
const c = Character.create({ name, level: Level.create(1) });
if (c.status.kind === 'dead') {
return c.health.value === 0;
}
return true;
}),
);
});
it('property: level is between 1 and 10', () => {
fc.assert(
fc.property(fc.integer({ min: 1, max: 10 }), (level) => {
const c = Character.create({ name: 'test', level: Level.create(level) });
return c.level.value >= 1 && c.level.value <= 10;
}),
);
});
});
});