78 lines
3.1 KiB
TypeScript
78 lines
3.1 KiB
TypeScript
import { assert, assertEquals } from "jsr:@std/assert";
|
|
import {
|
|
fetchAllMapScoresByHash,
|
|
fetchBLLeaderboardsByHash,
|
|
fetchFriends,
|
|
normalizeAccuracy,
|
|
} from "./beatleader.ts";
|
|
import { fetchBeatSaverMapById } from "./beatsaver.ts";
|
|
|
|
const MAP_KEY = "4f4e4";
|
|
const PLAYER_ID = "76561199407393962";
|
|
const MODE = "Standard";
|
|
|
|
function normalizeDifficultyName(value: string | null | undefined) {
|
|
return (value ?? "").toLowerCase().replace(/\s+/g, "").replace("expert+", "expertplus");
|
|
}
|
|
|
|
function leaderboardId(lb: { id?: string | number | null; leaderboardId?: string | number | null }) {
|
|
const id = lb.id ?? lb.leaderboardId;
|
|
return id == null ? "" : String(id);
|
|
}
|
|
|
|
Deno.test({
|
|
name: "live lookup: map hash -> expertplus leaderboard -> mutual friend scores",
|
|
sanitizeOps: false,
|
|
sanitizeResources: false,
|
|
async fn() {
|
|
const map = await fetchBeatSaverMapById(MAP_KEY);
|
|
assert(map?.versions?.[0]?.hash, `BeatSaver map ${MAP_KEY} should resolve to a hash`);
|
|
const hash = String(map.versions[0].hash).toLowerCase();
|
|
|
|
const leaderboards = await fetchBLLeaderboardsByHash(hash);
|
|
assert(leaderboards.length > 0, "Expected BeatLeader leaderboards for hash");
|
|
|
|
const expertPlus = leaderboards.find((lb) => {
|
|
const modeOk = (lb.difficulty?.modeName ?? "").toLowerCase() === MODE.toLowerCase();
|
|
const diff = normalizeDifficultyName(lb.difficulty?.difficultyName);
|
|
return modeOk && diff === "expertplus";
|
|
});
|
|
assert(expertPlus, "Expected Standard ExpertPlus leaderboard");
|
|
|
|
const nonExpertPlus = leaderboards.find((lb) => {
|
|
const modeOk = (lb.difficulty?.modeName ?? "").toLowerCase() === MODE.toLowerCase();
|
|
const diff = normalizeDifficultyName(lb.difficulty?.difficultyName);
|
|
return modeOk && diff !== "expertplus";
|
|
});
|
|
assert(nonExpertPlus, "Expected a Standard non-ExpertPlus leaderboard");
|
|
assert(
|
|
leaderboardId(expertPlus) !== leaderboardId(nonExpertPlus),
|
|
"ExpertPlus should map to a different leaderboard id than another difficulty",
|
|
);
|
|
|
|
const mutualFriends = await fetchFriends(PLAYER_ID, "mutual", 100);
|
|
const mutualIds = new Set(mutualFriends.map((f) => f.id));
|
|
assert(mutualIds.size > 0, "Expected at least one mutual friend");
|
|
|
|
const [expertPlusScores, allMapScores] = await Promise.all([
|
|
fetchAllMapScoresByHash(hash, [expertPlus], 120),
|
|
fetchAllMapScoresByHash(hash, leaderboards, 120),
|
|
]);
|
|
assert(expertPlusScores.length > 0, "Expected some ExpertPlus scores");
|
|
assert(allMapScores.length > 0, "Expected map to have scores across leaderboards");
|
|
|
|
const mutualScores = allMapScores.filter((score) => {
|
|
const playerId = score.playerId ?? (typeof score.player === "object" ? score.player?.id : null);
|
|
return playerId != null && mutualIds.has(String(playerId));
|
|
});
|
|
assert(mutualScores.length > 0, "Expected at least one mutual friend score on this map");
|
|
|
|
const accuracies = mutualScores
|
|
.map((score) => normalizeAccuracy(score.accuracy ?? score.acc))
|
|
.filter((value): value is number => value !== null);
|
|
assert(accuracies.length > 0, "Expected mutual scores to include accuracy values");
|
|
assert(accuracies.some((acc) => acc > 0), "Expected positive accuracy values");
|
|
assertEquals(hash.length, 40);
|
|
},
|
|
});
|