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.
131 lines
4 KiB
TypeScript
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;
|
|
}
|