ds4/spec/documents/item/spell/calculate-spell-price.spec.ts
Johannes Loher 045991073b
All checks were successful
ci/woodpecker/pr/checks Pipeline was successful
chore: reformat with 2 spaces and single quotes
2023-07-10 22:25:25 +02:00

234 lines
7.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-FileCopyrightText: 2022 Johannes Loher
//
// SPDX-License-Identifier: MIT
import { describe, expect, it } from 'vitest';
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);
},
);
},
);
});