mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-15 13:58:20 +00:00
123 lines
4.5 KiB
TypeScript
123 lines
4.5 KiB
TypeScript
import { afterEach, describe, expect, it } from "vitest";
|
|
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { loadHyperframeRuntimeSource } from "@hyperframes/core";
|
|
import { loadRuntimeSource } from "./runtimeSource.js";
|
|
import { createStudioServer, type StudioServer } from "./studioServer.js";
|
|
|
|
describe("loadRuntimeSource", () => {
|
|
it("loads runtime source from the published core entrypoint", async () => {
|
|
await expect(loadRuntimeSource()).resolves.toBe(loadHyperframeRuntimeSource());
|
|
});
|
|
});
|
|
|
|
describe("Studio thumbnail GPU capture plumbing", () => {
|
|
it("uses the shared auto probe, resolved launch mode, requirement guard, and completion-aware seek", () => {
|
|
const source = readFileSync(new URL("./studioServer.ts", import.meta.url), "utf8");
|
|
expect(source).toContain("resolveCaptureBrowserGpuMode");
|
|
expect(source).toContain("{ browserGpuMode: resolvedGpuMode }");
|
|
expect(source).toContain("assertWebGpuRequirement");
|
|
expect(source).toContain("await seekCompositionTimeline(page, opts.seekTime");
|
|
});
|
|
});
|
|
|
|
describe("createStudioServer autoProxy plumbing", () => {
|
|
const dirs: string[] = [];
|
|
let server: StudioServer | undefined;
|
|
|
|
function tmpProject(): string {
|
|
const dir = mkdtempSync(join(tmpdir(), "hf-studio-server-test-"));
|
|
dirs.push(dir);
|
|
return dir;
|
|
}
|
|
|
|
afterEach(() => {
|
|
server?.watcher.close();
|
|
server = undefined;
|
|
for (const dir of dirs.splice(0)) rmSync(dir, { recursive: true, force: true });
|
|
});
|
|
|
|
it("hyperframes.json media.autoProxy=false flows through to the adapter", () => {
|
|
const projectDir = tmpProject();
|
|
writeFileSync(
|
|
join(projectDir, "hyperframes.json"),
|
|
JSON.stringify({ media: { autoProxy: false } }),
|
|
);
|
|
|
|
server = createStudioServer({ projectDir });
|
|
|
|
expect(server.adapter.autoProxy).toBe(false);
|
|
});
|
|
|
|
it("defaults the adapter to autoProxy=true when neither option nor config disables it", () => {
|
|
server = createStudioServer({ projectDir: tmpProject() });
|
|
expect(server.adapter.autoProxy).toBe(true);
|
|
});
|
|
|
|
it("an explicit option (the preview command's resolved --proxy flag) wins over config", () => {
|
|
const projectDir = tmpProject();
|
|
writeFileSync(
|
|
join(projectDir, "hyperframes.json"),
|
|
JSON.stringify({ media: { autoProxy: false } }),
|
|
);
|
|
|
|
server = createStudioServer({ projectDir, autoProxy: true });
|
|
|
|
expect(server.adapter.autoProxy).toBe(true);
|
|
});
|
|
|
|
it("advertises the GPU policy used for thumbnail capture", async () => {
|
|
const projectDir = tmpProject();
|
|
server = createStudioServer({ projectDir, browserGpuMode: "software" });
|
|
|
|
const response = await server.app.request("/__hyperframes_config");
|
|
|
|
expect(response.status).toBe(200);
|
|
await expect(response.json()).resolves.toMatchObject({ browserGpuMode: "software" });
|
|
});
|
|
});
|
|
|
|
describe("host guarding on identity-bearing responses", () => {
|
|
const dirs: string[] = [];
|
|
let server: StudioServer | undefined;
|
|
|
|
function tmpProject(): string {
|
|
const dir = mkdtempSync(join(tmpdir(), "hf-studio-host-test-"));
|
|
dirs.push(dir);
|
|
return dir;
|
|
}
|
|
|
|
afterEach(() => {
|
|
server?.watcher.close();
|
|
server = undefined;
|
|
for (const dir of dirs.splice(0)) rmSync(dir, { recursive: true, force: true });
|
|
});
|
|
|
|
// NOTE: the SPA-injection branch itself is covered in telemetryIdentity.test.ts
|
|
// via buildStudioHeadScriptsForHost. It cannot be asserted here: this route
|
|
// only reaches the injection branch when packages/studio/dist is built,
|
|
// which is true locally and false in the CI test lane, so a route-level
|
|
// assertion on the returned HTML passes on a dev box and fails in CI.
|
|
|
|
it("refuses the identity endpoint for a hostile Host", async () => {
|
|
server = createStudioServer({ projectDir: tmpProject() });
|
|
const res = await server.app.request("/api/telemetry-identity", {
|
|
headers: { host: "evil.example.com" },
|
|
});
|
|
expect(res.status).toBe(403);
|
|
expect(await res.text()).not.toContain('distinctId":"');
|
|
});
|
|
|
|
it("serves the identity endpoint on a loopback Host", async () => {
|
|
server = createStudioServer({ projectDir: tmpProject() });
|
|
const res = await server.app.request("/api/telemetry-identity", {
|
|
headers: { host: "127.0.0.1:5173" },
|
|
});
|
|
expect(res.status).toBe(200);
|
|
// The seed is no longer served here at all — Studio gets decisions
|
|
// injected instead, so nothing needs it over HTTP.
|
|
expect(Object.keys((await res.json()) as object)).toEqual(["distinctId"]);
|
|
});
|
|
});
|