fix: stabilize failing test regressions across src and launcher lanes

- Fix log pruning cutoff math using BigInt `mtimeNs` to avoid Bun mtime precision loss
- Fix stats CLI lifetime rebuild timestamp units in tests and log output; add `formatLoggedNumber` guard
- Use `performance.now()` in subtitle sidebar auto-follow to isolate from test time injection
- Harden renderer global cleanup tests with descriptor save/restore instead of assuming globals absent
- Isolate `node:http` fallback in stats-server test with stub and assertion
- Fix AniSkip fallback title: cleaned basename beats generic parent dirs; episode-only filenames still prefer series directory
This commit is contained in:
2026-04-03 22:04:52 -07:00
parent 864f4124ae
commit e4137d9760
10 changed files with 224 additions and 33 deletions

View File

@@ -236,7 +236,7 @@ test('stats cli command runs lifetime rebuild when cleanup lifetime mode is requ
getImmersionTracker: () => ({
rebuildLifetimeSummaries: async () => ({
appliedSessions: 4,
rebuiltAtMs: 1_710_000_000_000,
rebuiltAtMs: 1_710_000_000,
}),
}),
});
@@ -252,7 +252,7 @@ test('stats cli command runs lifetime rebuild when cleanup lifetime mode is requ
assert.deepEqual(calls, [
'ensureImmersionTrackerStarted',
'info:Stats lifetime rebuild complete: appliedSessions=4 rebuiltAtMs=1710000000000',
'info:Stats lifetime rebuild complete: appliedSessions=4 rebuiltAtMs=1710000000',
]);
assert.deepEqual(responses, [
{
@@ -285,6 +285,7 @@ async function waitForPendingAnimeMetadata(
test('tracker rebuildLifetimeSummaries backfills retained sessions and is idempotent', async () => {
const dbPath = makeDbPath();
const previousNowMs = globalThis.__subminerTestNowMs;
let tracker:
| import('../../core/services/immersion-tracker-service').ImmersionTrackerService
| null = null;
@@ -298,19 +299,23 @@ test('tracker rebuildLifetimeSummaries backfills retained sessions and is idempo
const { Database } = await import('../../core/services/immersion-tracker/sqlite');
try {
globalThis.__subminerTestNowMs = 1_700_000_000;
tracker = new ImmersionTrackerService({ dbPath });
tracker.handleMediaChange('/tmp/Frieren S01E01.mkv', 'Episode 1');
await waitForPendingAnimeMetadata(tracker);
tracker.recordCardsMined(2);
tracker.recordSubtitleLine('first line', 0, 1);
globalThis.__subminerTestNowMs = 1_700_001_000;
tracker.destroy();
tracker = null;
globalThis.__subminerTestNowMs = 1_700_002_000;
tracker2 = new ImmersionTrackerService({ dbPath });
tracker2.handleMediaChange('/tmp/Frieren S01E02.mkv', 'Episode 2');
await waitForPendingAnimeMetadata(tracker2);
tracker2.recordCardsMined(1);
tracker2.recordSubtitleLine('second line', 0, 1);
globalThis.__subminerTestNowMs = 1_700_003_000;
tracker2.destroy();
tracker2 = null;
@@ -357,8 +362,10 @@ test('tracker rebuildLifetimeSummaries backfills retained sessions and is idempo
`);
beforeDb.close();
globalThis.__subminerTestNowMs = 1_700_004_000;
tracker3 = new ImmersionTrackerService({ dbPath });
const firstRebuild = await tracker3.rebuildLifetimeSummaries();
globalThis.__subminerTestNowMs = 1_700_005_000;
const secondRebuild = await tracker3.rebuildLifetimeSummaries();
const rebuiltDb = new Database(dbPath);
@@ -405,6 +412,7 @@ test('tracker rebuildLifetimeSummaries backfills retained sessions and is idempo
assert.equal(secondRebuild.appliedSessions, firstRebuild.appliedSessions);
assert.ok(secondRebuild.rebuiltAtMs >= firstRebuild.rebuiltAtMs);
} finally {
globalThis.__subminerTestNowMs = previousNowMs;
tracker?.destroy();
tracker2?.destroy();
tracker3?.destroy();
@@ -417,7 +425,7 @@ test('stats cli command runs lifetime rebuild when requested', async () => {
getImmersionTracker: () => ({
rebuildLifetimeSummaries: async () => ({
appliedSessions: 4,
rebuiltAtMs: 1_710_000_000_000,
rebuiltAtMs: 1_710_000_000,
}),
}),
});
@@ -433,7 +441,7 @@ test('stats cli command runs lifetime rebuild when requested', async () => {
assert.deepEqual(calls, [
'ensureImmersionTrackerStarted',
'info:Stats lifetime rebuild complete: appliedSessions=4 rebuiltAtMs=1710000000000',
'info:Stats lifetime rebuild complete: appliedSessions=4 rebuiltAtMs=1710000000',
]);
assert.deepEqual(responses, [
{

View File

@@ -32,6 +32,10 @@ type BackgroundStatsStopResult = {
stale: boolean;
};
function formatLoggedNumber(value: number): string {
return Number.isFinite(value) ? value.toString() : String(value);
}
export function writeStatsCliCommandResponse(
responsePath: string,
payload: StatsCliCommandResponse,
@@ -143,7 +147,7 @@ export function createRunStatsCliCommandHandler(deps: {
}
const result = await tracker.rebuildLifetimeSummaries();
deps.logInfo(
`Stats lifetime rebuild complete: appliedSessions=${result.appliedSessions} rebuiltAtMs=${result.rebuiltAtMs}`,
`Stats lifetime rebuild complete: appliedSessions=${formatLoggedNumber(result.appliedSessions)} rebuiltAtMs=${formatLoggedNumber(result.rebuiltAtMs)}`,
);
writeResponseSafe(args.statsResponsePath, { ok: true });
return;