Files
MatchMaster/tools/test-level-time-bonus.cjs

107 lines
4.9 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const { loadMembers, loadModule, resolveFile } = require('./new-player-ab-test-support.cjs');
const { LEVEL_TIME_BONUS } = loadModule('assets/Script/module/Config/LevelTimeBonusConfig.ts');
const configFile = 'assets/Script/module/Config/GameConfig.ts';
const readLevel = level => JSON.parse(fs.readFileSync(resolveFile(`assets/custom/Json/level${level}.json`), 'utf8'));
const plain = value => JSON.parse(JSON.stringify(value));
function config(info = {}) {
const Config = loadMembers(configFile, ['applyLevelData', 'LEVEL_INFO_init', 'getLevelConfigPath'], {
LEVEL_TIME_BONUS, console: { log() {} }, cc,
});
Config.GM_INFO = info;
return Config;
}
// Exercise the actual loader so file numbers, help levels and endless levels use the same key.
const requests = [];
const cc = {
v2: (x, y) => ({ x, y }), director: { getScene: () => null },
fx: { GameTool: { maxLevel: () => false } },
assetManager: {
loadBundle(_name, _options, done) { done(null, {}); },
loadAny(request, _options, done) {
requests.push(request.path);
done(null, { json: readLevel(Number(request.path.replace('Json/level', ''))) });
},
},
};
test('A, unassigned and other groups retain the original time for every configured level', () => {
assert.equal(Object.keys(LEVEL_TIME_BONUS).length, 100);
for (const info of [undefined, {}, { abTests: {} }, ...[0, 1, 3].map(layer_three => ({ abTests: { layer_three } }))]) {
const Config = config(info);
for (const level of Object.keys(LEVEL_TIME_BONUS)) {
const data = readLevel(level);
Config.applyLevelData(data, false, 0, Number(level));
assert.equal(Config.LEVEL_INFO[0].time, data.LEVEL_INFO[0].time);
assert.equal(Config.LEVEL_INFO, data.LEVEL_INFO);
}
}
});
test('B adds time once, preserves cached data and returns to original time when switched to A', () => {
const Config = config({ abTests: { layer_three: 2 } });
for (const [level, bonus] of Object.entries(LEVEL_TIME_BONUS)) {
assert.ok(Number.isInteger(bonus) && bonus > 0);
const data = readLevel(level), before = structuredClone(data);
Config.GM_INFO.abTests.layer_three = 2;
for (let retry = 0; retry < 2; retry++) {
Config.applyLevelData(data, false, 0, Number(level));
const expected = structuredClone(before.LEVEL_INFO);
expected[0].time += bonus;
assert.deepEqual(plain(Config.LEVEL_INFO), expected);
assert.equal(Config.BLOCK_INFO, data.BLOCK_INFO);
assert.equal(Config.WALL_INFO, data.WALL_INFO);
assert.deepEqual(data, before);
}
Config.GM_INFO.abTests.layer_three = 1;
Config.applyLevelData(data, false, 0, Number(level));
assert.equal(Config.LEVEL_INFO[0].time, before.LEVEL_INFO[0].time);
}
});
test('B leaves unlisted levels unchanged and uses the file number instead of a stale internal id', () => {
const Config = config({ abTests: { layer_three: 2 } });
for (const level of [1, 113, 115, 1282]) {
const data = readLevel(level);
Config.applyLevelData(data, false, 0, level);
assert.equal(Config.LEVEL_INFO, data.LEVEL_INFO);
}
const data = readLevel(222);
assert.equal(data.LEVEL_INFO[0].id, '158');
Config.applyLevelData(data, false, 0, 222);
assert.equal(Config.LEVEL_INFO[0].time, 70);
assert.equal(Config.LEVEL_INFO[0].id, '158');
});
test('loader applies B bonuses to the requested mainline, help and endless level', () => {
for (const mode of ['mainline', 'help', 'endless']) {
const Config = config({ level: mode === 'mainline' ? 221 : 0,
otherLevel: mode === 'help' ? 222 : 0, randomLevel: 222, abTests: { layer_three: 2 } });
cc.fx.GameConfig = Config;
Config.levelLoadRequest = Config.sceneLoadRequest = 0;
Config.takeEntryDownloadRelease = () => null;
Config.LEVEL_INFO_init(false, 0, mode === 'help', mode === 'endless');
assert.equal(requests.at(-1), 'Json/level222');
assert.equal(Config.LEVEL_INFO[0].time, 70, mode);
}
});
test('server A/B assignments select original and bonus times before entering the scene', () => {
const Manager = loadMembers('assets/Script/GameManager.ts', ['setABTests'], { cc });
const manager = new Manager();
const Config = config();
cc.fx.GameConfig = Config;
const seen = [];
Config.enterGameSceneWhenReady = (time, preparation) => seen.push([Config.LEVEL_INFO[0].time, time, preparation]);
const preparation = {};
for (const [group, expected] of [['A', 135], ['B', 165], [1, 135], [2, 165]]) {
manager.setABTests({ layer_3: { group } });
Config.applyLevelData(readLevel(114), true, 0.5, 114, preparation);
assert.deepEqual(seen.at(-1), [expected, 0.5, preparation]);
}
});