ds4/spec/documents/item/spell/calculate-spell-price.spec.ts

235 lines
7.1 KiB
TypeScript
Raw Normal View History

// SPDX-FileCopyrightText: 2022 Johannes Loher
//
// SPDX-License-Identifier: MIT
import { describe, expect, it } from 'vitest';
2022-08-22 22:05:14 +02:00
import { calculateSpellPrice } from '../../../../src/documents/item/spell/calculate-spell-price';
import type { CooldownDuration, DS4SpellDataSourceData } from '../../../../src/documents/item/spell/spell-data-source';
const defaultData: DS4SpellDataSourceData = {
description: '',
equipped: false,
spellType: 'spellcasting',
spellModifier: {
numerical: 0,
complex: '',
},
allowsDefense: false,
spellGroups: {
lightning: false,
earth: false,
water: false,
ice: false,
fire: false,
healing: false,
light: false,
air: false,
transport: false,
damage: false,
shadow: false,
protection: false,
mindAffecting: false,
demonology: false,
necromancy: false,
transmutation: false,
area: false,
},
maxDistance: {
value: '',
unit: 'meter',
},
effectRadius: {
value: '',
unit: 'meter',
},
duration: {
value: '',
unit: 'custom',
},
cooldownDuration: '0r',
minimumLevels: {
healer: null,
wizard: null,
sorcerer: null,
},
};
type TestCase = {
minimumLevel: number | null;
expected: number | null;
};
type CombinedTestCase = {
minimumLevels: DS4SpellDataSourceData['minimumLevels'];
expected: number | null;
description: string;
};
const testCases: Record<keyof DS4SpellDataSourceData['minimumLevels'], TestCase[]> = {
healer: [
{ minimumLevel: null, expected: null },
{ minimumLevel: 1, expected: 10 },
{ minimumLevel: 2, expected: 45 },
{ minimumLevel: 3, expected: 80 },
{ minimumLevel: 4, expected: 115 },
{ minimumLevel: 5, expected: 150 },
{ minimumLevel: 6, expected: 185 },
{ minimumLevel: 7, expected: 220 },
{ minimumLevel: 8, expected: 255 },
{ minimumLevel: 9, expected: 290 },
{ minimumLevel: 10, expected: 325 },
{ minimumLevel: 11, expected: 360 },
{ minimumLevel: 12, expected: 395 },
{ minimumLevel: 13, expected: 430 },
{ minimumLevel: 14, expected: 465 },
{ minimumLevel: 15, expected: 500 },
{ minimumLevel: 16, expected: 535 },
{ minimumLevel: 17, expected: 570 },
{ minimumLevel: 18, expected: 605 },
{ minimumLevel: 19, expected: 640 },
{ minimumLevel: 20, expected: 675 },
],
sorcerer: [
{ minimumLevel: null, expected: null },
{ minimumLevel: 1, expected: 10 },
{ minimumLevel: 2, expected: 75 },
{ minimumLevel: 3, expected: 140 },
{ minimumLevel: 4, expected: 205 },
{ minimumLevel: 5, expected: 270 },
{ minimumLevel: 6, expected: 335 },
{ minimumLevel: 7, expected: 400 },
{ minimumLevel: 8, expected: 465 },
{ minimumLevel: 9, expected: 530 },
{ minimumLevel: 10, expected: 595 },
{ minimumLevel: 11, expected: 660 },
{ minimumLevel: 12, expected: 725 },
{ minimumLevel: 13, expected: 790 },
{ minimumLevel: 14, expected: 855 },
{ minimumLevel: 15, expected: 920 },
{ minimumLevel: 16, expected: 985 },
{ minimumLevel: 17, expected: 1050 },
{ minimumLevel: 18, expected: 1115 },
{ minimumLevel: 19, expected: 1180 },
{ minimumLevel: 20, expected: 1245 },
],
wizard: [
{ minimumLevel: null, expected: null },
{ minimumLevel: 1, expected: 10 },
{ minimumLevel: 2, expected: 60 },
{ minimumLevel: 3, expected: 110 },
{ minimumLevel: 4, expected: 160 },
{ minimumLevel: 5, expected: 210 },
{ minimumLevel: 6, expected: 260 },
{ minimumLevel: 7, expected: 310 },
{ minimumLevel: 8, expected: 360 },
{ minimumLevel: 9, expected: 410 },
{ minimumLevel: 10, expected: 460 },
{ minimumLevel: 11, expected: 510 },
{ minimumLevel: 12, expected: 560 },
{ minimumLevel: 13, expected: 610 },
{ minimumLevel: 14, expected: 660 },
{ minimumLevel: 15, expected: 710 },
{ minimumLevel: 16, expected: 760 },
{ minimumLevel: 17, expected: 810 },
{ minimumLevel: 18, expected: 860 },
{ minimumLevel: 19, expected: 910 },
{ minimumLevel: 20, expected: 960 },
],
};
function buildCombinedTestCases(): CombinedTestCase[] {
const combinedTestCases: CombinedTestCase[] = [];
// permutation test cases
const isRelevantPermutationTestCase = (t: TestCase) =>
([null, 1, 10, 20] as (number | null)[]).includes(t.minimumLevel);
for (const healerTestCase of testCases.healer.filter(isRelevantPermutationTestCase)) {
for (const sorcererTestCase of testCases.sorcerer.filter(isRelevantPermutationTestCase)) {
for (const wizardTestCase of testCases.wizard.filter(isRelevantPermutationTestCase)) {
const expected =
healerTestCase.expected !== null || sorcererTestCase.expected !== null || wizardTestCase.expected !== null
? Math.min(
healerTestCase.expected ?? Infinity,
sorcererTestCase.expected ?? Infinity,
wizardTestCase.expected ?? Infinity,
)
: null;
const minimumLevels = {
healer: healerTestCase.minimumLevel,
sorcerer: sorcererTestCase.minimumLevel,
wizard: wizardTestCase.minimumLevel,
};
const description = JSON.stringify(minimumLevels);
combinedTestCases.push({
minimumLevels,
expected,
description,
});
}
}
}
// single test cases
const isRelevantSingleTestCase = (t: TestCase) => t.minimumLevel !== null;
for (const spellCasterClass of ['healer', 'sorcerer', 'wizard'] as const) {
for (const testCase of testCases[spellCasterClass].filter(isRelevantSingleTestCase)) {
const minimumLevels = {
...defaultData.minimumLevels,
[spellCasterClass]: testCase.minimumLevel,
};
const description = JSON.stringify(minimumLevels);
combinedTestCases.push({
minimumLevels,
expected: testCase.expected,
description,
});
}
}
return combinedTestCases;
}
describe('calculateSpellPrice', () => {
const cooldownDurations: { cooldownDuration: CooldownDuration; factor: number }[] = [
{ cooldownDuration: '0r', factor: 1 },
{ cooldownDuration: '1r', factor: 1 },
{ cooldownDuration: '2r', factor: 1 },
{ cooldownDuration: '5r', factor: 1 },
{ cooldownDuration: '10r', factor: 1 },
{ cooldownDuration: '100r', factor: 1 },
{ cooldownDuration: '1d', factor: 2 },
{ cooldownDuration: 'd20d', factor: 3 },
];
describe.each(cooldownDurations)(
'with cooldown duration set to $cooldownDuration',
({ cooldownDuration, factor }) => {
const dataWithCooldownDuration = {
...defaultData,
cooldownDuration,
};
it.each(buildCombinedTestCases())(
`returns ${factor} × $expected if the minimum leves are $description`,
({ minimumLevels, expected }) => {
// given
const data: DS4SpellDataSourceData = {
...dataWithCooldownDuration,
minimumLevels,
};
// when
const spellPrice = calculateSpellPrice(data);
// then
expect(spellPrice).toBe(expected !== null ? expected * factor : expected);
},
);
},
);
});