- 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
112 lines
3.4 KiB
TypeScript
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;
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
});
|