unsloth/studio/frontend/tests/helpers/kit.ts
danielhanchen 0c9c4f20aa Consolidate the tests without changing what they prove
Test-only. No file under studio/frontend/src or the backend's routes,
core, hub, utils or storage is touched.

Backend: the override PUT was spelled out at 29 sites as a two-call
expression with a local import each time, and 39 tests mocked the store
by hand when a fixture does it. A `_put` helper and an `override_store`
fixture take both. -186 lines, 273 tests still pass, and the assert count
is unchanged at 624.

Frontend: eight test files become five plus a shared kit holding the
bundler-resolver registration, the localStorage fake and the chat-runtime
store fakes that three files had each written out. The resident-status
pair merge into one file, and the three identity/storage files into
another. 62 tests, 139 assertions, both unchanged.

Prose: multi-line docstrings and comment blocks in the test suites keep
their opening statement, the rest being recoverable from history. That is
most of the remaining reduction, because these tests are close to one
line per assertion already.

Two consolidations were measured and rejected rather than shipped. A
table-driven form of the source-contract tests generates 930 lines to
replace 773, since a row costs what an assert line costs. Parametrising
the backend key-folding and carry-over families saves nothing once the
helper above removes their boilerplate: what is left is the per-case
reason, not repetition.

Every mutation these tests were written to catch still reddens: reverting
new-traffic.ts, adopt-inference-status.ts, settings.py and hub-page.tsx
to their pre-fix parents each fails the expected tests.
2026-07-29 08:40:32 +00:00

131 lines
4 KiB
TypeScript

// SPDX-License-Identifier: AGPL-3.0-only
// Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
import { register } from "node:module";
import type { ResidentAdoptionState } from "../../src/features/hub/lib/adopt-inference-status.ts";
import type { ResidentStatusRefreshTargets } from "../../src/features/hub/lib/resident-status-refresh.ts";
/**
* Teach the loader the two resolution rules vite and tsconfig's "bundler" mode
* give the app. Call this before the dynamic import of any src module that
* resolves the way vite and tsconfig resolve, not the way bare node does.
*/
export function registerBundlerResolver(): void {
register("../bundler-resolver.mjs", import.meta.url);
}
export type StorageFake = {
getItem: (key: string) => string | null;
setItem: (key: string, value: string) => void;
removeItem: (key: string) => void;
};
/**
* An in-memory localStorage, installed on globalThis under both the names the
* app reads it by. The returned map is the backing store, so a test can stage
* records before the module under test is imported.
*/
export function installLocalStorageFake(): {
store: Map<string, string>;
storage: StorageFake;
} {
const store = new Map<string, string>();
const storage: StorageFake = {
getItem: (key: string) => store.get(key) ?? null,
setItem: (key: string, value: string) => {
store.set(key, value);
},
removeItem: (key: string) => {
store.delete(key);
},
};
Object.assign(globalThis, {
window: { localStorage: storage },
localStorage: storage,
});
return { store, storage };
}
/** The chat-runtime store as it stands before anything has hydrated it. */
export function emptyStore(
overrides: Partial<ResidentAdoptionState> = {},
): ResidentAdoptionState {
return {
checkpoint: null,
checkpointIsExternal: false,
activeGgufVariant: null,
modelLoading: false,
...overrides,
};
}
/** Records the store actions adoptResidentModelStatus takes, in order. */
export function spies() {
const calls: string[] = [];
const previouslySeen: { checkpoint: string | null; ggufVariant: string | null }[] =
[];
return {
calls,
previouslySeen,
actions: {
setCheckpoint(checkpointId: string, ggufVariant: string | null) {
calls.push(`setCheckpoint:${checkpointId}:${ggufVariant ?? ""}`);
},
applyStatus(previous: {
checkpoint: string | null;
ggufVariant: string | null;
}) {
calls.push("applyStatus");
previouslySeen.push(previous);
},
},
};
}
/** A window/document pair whose events and visibility a test drives by hand. */
export function fakeTargets(): ResidentStatusRefreshTargets & {
hidden: boolean;
fire: (target: "window" | "document", type: string) => void;
listenerCount: () => number;
} {
const listeners = new Map<string, Set<EventListenerOrEventListenerObject>>();
const key = (target: string, type: string) => `${target}:${type}`;
const make = (target: "window" | "document") => ({
addEventListener(type: string, fn: EventListenerOrEventListenerObject) {
const set = listeners.get(key(target, type)) ?? new Set();
set.add(fn);
listeners.set(key(target, type), set);
},
removeEventListener(type: string, fn: EventListenerOrEventListenerObject) {
listeners.get(key(target, type))?.delete(fn);
},
});
const visibility = { hidden: false };
const state = {
get hidden() {
return visibility.hidden;
},
set hidden(next: boolean) {
visibility.hidden = next;
},
window: make("window"),
document: {
...make("document"),
get hidden() {
return visibility.hidden;
},
},
fire(target: "window" | "document", type: string) {
for (const fn of listeners.get(key(target, type)) ?? []) {
(fn as EventListener)(new Event(type));
}
},
listenerCount() {
let total = 0;
for (const set of listeners.values()) total += set.size;
return total;
},
};
return state as never;
}