merge: #1599 gate src/lib coverage (#1594)

This commit is contained in:
cliffhall 2026-07-01 22:48:03 -04:00
commit e19d11070a
4 changed files with 211 additions and 4 deletions

View file

@ -44,6 +44,7 @@ export function downloadJsonFile(filename: string, json: string): void {
* nothing usable remains.
*/
export function fileNameFromUri(uri: string): string {
/* v8 ignore next -- String.prototype.split always returns a non-empty array, so .pop() is never undefined; the `?? ""` fallback is unreachable. */
const tail = uri.split(/[\\/]/).pop() ?? "";
const safe = tail
.replace(/[\p{Cc}\p{Cf}]+/gu, "")

View file

@ -0,0 +1,111 @@
/**
* Tests for the browser `InspectorClientEnvironment` assembly.
*
* The three remote factories are mocked so we can (a) assert the `baseUrl` /
* `authToken` derived from `window.location` are threaded into each, and
* (b) capture the internal `fetchFn` wrapper and invoke it to prove it
* delegates to `globalThis.fetch` (exercising the arrow body that exists to
* preserve the global receiver). `BrowserNavigation` and the OAuth storage
* accessor are the real implementations.
*/
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { BrowserNavigation } from "@inspector/core/auth/browser/index.js";
import type { RedirectUrlProvider } from "@inspector/core/auth/index.js";
interface CapturedOptions {
baseUrl: string;
authToken: string | undefined;
fetchFn: typeof fetch;
}
const captured: {
transport?: CapturedOptions;
fetch?: CapturedOptions;
logger?: CapturedOptions;
} = {};
vi.mock("@inspector/core/mcp/remote/index.js", () => ({
createRemoteTransport: (opts: CapturedOptions) => {
captured.transport = opts;
return { transport: true };
},
createRemoteFetch: (opts: CapturedOptions) => {
captured.fetch = opts;
return (async () => new Response("ok")) as unknown as typeof fetch;
},
createRemoteLogger: (opts: CapturedOptions) => {
captured.logger = opts;
return { info: vi.fn() };
},
}));
import { createWebEnvironment } from "./environmentFactory";
const REDIRECT: RedirectUrlProvider = {
getRedirectUrl: () => "http://localhost/callback",
};
describe("createWebEnvironment", () => {
beforeEach(() => {
captured.transport = undefined;
captured.fetch = undefined;
captured.logger = undefined;
});
afterEach(() => {
vi.restoreAllMocks();
});
it("threads the window.location origin and auth token into every remote factory", () => {
const expectedBaseUrl = `${window.location.protocol}//${window.location.host}`;
const { environment, logger } = createWebEnvironment("tok-123", REDIRECT);
for (const opts of [captured.transport, captured.fetch, captured.logger]) {
expect(opts?.baseUrl).toBe(expectedBaseUrl);
expect(opts?.authToken).toBe("tok-123");
}
// The returned logger is the same instance the factory produced.
expect(logger).toBe(environment.logger);
expect(environment.transport).toEqual({ transport: true });
expect(typeof environment.fetch).toBe("function");
});
it("passes an undefined auth token straight through", () => {
createWebEnvironment(undefined, REDIRECT);
expect(captured.logger?.authToken).toBeUndefined();
});
it("wraps fetch so the call preserves the global receiver", async () => {
const spy = vi
.spyOn(globalThis, "fetch")
.mockResolvedValue(new Response("delegated", { status: 200 }));
createWebEnvironment("tok", REDIRECT);
// Every factory receives the identical wrapper; invoking it must delegate.
const wrapper = captured.transport!.fetchFn;
const res = await wrapper("http://example.test/x");
expect(spy).toHaveBeenCalledWith("http://example.test/x");
expect(res.status).toBe(200);
});
it("builds a BrowserNavigation and wires the OAuth storage + redirect provider", () => {
const onBeforeRedirect = vi.fn();
const { environment } = createWebEnvironment(
"tok",
REDIRECT,
onBeforeRedirect,
);
const oauth = environment.oauth;
expect(oauth?.navigation).toBeInstanceOf(BrowserNavigation);
expect(oauth?.redirectUrlProvider).toBe(REDIRECT);
expect(oauth?.storage).toBeDefined();
});
it("works without an onBeforeOAuthRedirect callback", () => {
const { environment } = createWebEnvironment(undefined, REDIRECT);
expect(environment.oauth?.navigation).toBeInstanceOf(BrowserNavigation);
});
});

View file

@ -0,0 +1,98 @@
/**
* Tests for the custom-headers value helpers: constructing headers, filtering
* enabled/non-blank ones, converting to/from a plain record, and the legacy
* bearer-token migration.
*/
import { describe, it, expect } from "vitest";
import {
createEmptyHeader,
createHeaderFromBearerToken,
getEnabledHeaders,
headersToRecord,
recordToHeaders,
migrateFromLegacyAuth,
type CustomHeaders,
} from "./customHeaders";
describe("createEmptyHeader", () => {
it("returns an enabled header with blank name and value", () => {
expect(createEmptyHeader()).toEqual({
name: "",
value: "",
enabled: true,
});
});
});
describe("createHeaderFromBearerToken", () => {
it("defaults to an Authorization: Bearer header when no name is given", () => {
expect(createHeaderFromBearerToken("abc")).toEqual({
name: "Authorization",
value: "Bearer abc",
enabled: true,
});
});
it("prefixes Bearer when the header name is Authorization (any case)", () => {
expect(createHeaderFromBearerToken("abc", "authorization")).toEqual({
name: "authorization",
value: "Bearer abc",
enabled: true,
});
});
it("uses the raw token for non-Authorization header names", () => {
expect(createHeaderFromBearerToken("abc", "X-Api-Key")).toEqual({
name: "X-Api-Key",
value: "abc",
enabled: true,
});
});
});
describe("getEnabledHeaders", () => {
it("keeps only enabled headers with non-blank name and value", () => {
const headers: CustomHeaders = [
{ name: "A", value: "1", enabled: true },
{ name: "B", value: "2", enabled: false },
{ name: "", value: "3", enabled: true },
{ name: "C", value: " ", enabled: true },
];
expect(getEnabledHeaders(headers)).toEqual([
{ name: "A", value: "1", enabled: true },
]);
});
});
describe("headersToRecord", () => {
it("trims names/values and drops disabled or blank entries", () => {
const headers: CustomHeaders = [
{ name: " X-One ", value: " a ", enabled: true },
{ name: "X-Two", value: "b", enabled: false },
];
expect(headersToRecord(headers)).toEqual({ "X-One": "a" });
});
});
describe("recordToHeaders", () => {
it("maps each record entry to an enabled header", () => {
expect(recordToHeaders({ "X-A": "1", "X-B": "2" })).toEqual([
{ name: "X-A", value: "1", enabled: true },
{ name: "X-B", value: "2", enabled: true },
]);
});
});
describe("migrateFromLegacyAuth", () => {
it("returns a single header for a bearer token", () => {
expect(migrateFromLegacyAuth("abc", "X-Api-Key")).toEqual([
{ name: "X-Api-Key", value: "abc", enabled: true },
]);
});
it("returns an empty list when there is no bearer token", () => {
expect(migrateFromLegacyAuth()).toEqual([]);
expect(migrateFromLegacyAuth("")).toEqual([]);
});
});

View file

@ -101,10 +101,7 @@ export default defineConfig(({ command }) => {
include: [
'src/components/**/*.{ts,tsx}',
'src/utils/**/*.{ts,tsx}',
// Pure, fully-tested sandbox CSP builder library. Listed by file (not a
// `src/lib/**` glob) so the untested legacy siblings in src/lib are not
// pulled under the gate before they have tests of their own.
'src/lib/sandbox-csp.{ts,tsx}',
'src/lib/**/*.{ts,tsx}',
'clients/web/server/**/*.{ts,tsx}',
path.join(repoRoot, 'core/mcp/**/*.{ts,tsx}'),
path.join(repoRoot, 'core/react/**/*.{ts,tsx}'),