mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 06:30:03 +00:00
P1 — the required Test lane was red, and it was my test. The
hostile-Host SPA case asserted a 200, which only holds when
packages/studio/dist is built: true on a dev box, false in CI, so it
passed locally and failed there. The Host split moved into a pure
buildStudioHeadScriptsForHost() and is asserted directly; the route test
no longer depends on build state. Verified by running the CLI suite with
the bundle moved aside — 2330 pass.
P1 — studio:* still bypassed most privacy controls. It honoured two
localStorage keys but not navigator.doNotTrack,
VITE_HYPERFRAMES_NO_TELEMETRY, Vite dev mode or API-key eligibility, and
canary enrolment honoured a different single control. New
telemetry/policy.ts is the one answer to "may this profile be measured",
consumed by both transports and by enrolment. It imports only ./config,
so no cycle with the modules that import it. Each control is asserted
individually.
P1 — LAN/remote preview lost the authoritative decisions. Withholding
the whole head script for any non-loopback Host also dropped the safe
{enabled, forced} map, sending a supported HYPERFRAMES_PREVIEW_HOST=
0.0.0.0 Studio back to re-deriving. Identity injection is now gated
separately from decision injection: identity is loopback-only, decisions
always publish.
P1 — the breaker latch was neither authoritative nor truthfully
persisted. syncInstallState swallowed its own failures so
writeConfigWithResult always reported ok, and reads took the flag only
from config.json. The latch is now merged into every effective read,
which makes install-state authoritative and closes both the failed-mirror
and stale-concurrent-writer paths; the write additionally reports
mirrored: false rather than swallowing.
P2 — public contracts. canary-rollouts.mdx said a config wipe loses the
breaker (it does not) and documented the superseded {name: boolean} map
with unconditional CLI precedence; both corrected, with the precedence
ladder written out and the override exception stated explicitly. PR body
rewritten — it still named ~/.local/state, claimed state survives
deleting ~/.hyperframes, and carried stale counts.
Tests: 2330 CLI (bundle absent), 3151 Studio, 24 core. Fault injection:
reverting each fix alone fails 5 CLI / 5 Studio.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
77 lines
3.0 KiB
TypeScript
77 lines
3.0 KiB
TypeScript
// @vitest-environment happy-dom
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
// The `studio:*` path predates telemetry/config.ts and shipped its own
|
|
// opt-out key and its own send loop, so it sat outside both contracts the
|
|
// canary work established: the documented opt-out did not silence it, and its
|
|
// events carried no cohort assignment. These pin both.
|
|
|
|
vi.mock("../telemetry/canary", () => ({
|
|
canaryEventProperties: () => ({ "$feature/canary-test-one": "true" }),
|
|
}));
|
|
|
|
// One shared policy now governs this transport, telemetry/client.ts and
|
|
// canary enrolment. Exercised directly here so each case names the control
|
|
// under test rather than relying on ambient import.meta.env.
|
|
const policyState = { allowed: true };
|
|
vi.mock("../telemetry/policy", () => ({
|
|
browserTelemetryAllowed: () => policyState.allowed,
|
|
}));
|
|
|
|
describe("studioTelemetry — shared opt-out and canary properties", () => {
|
|
let trackStudioEvent: typeof import("./studioTelemetry").trackStudioEvent;
|
|
let fetchMock: ReturnType<typeof vi.fn>;
|
|
|
|
beforeEach(async () => {
|
|
policyState.allowed = true;
|
|
localStorage.clear();
|
|
vi.resetModules();
|
|
vi.useFakeTimers();
|
|
fetchMock = vi.fn(() => Promise.resolve({ ok: true } as Response));
|
|
vi.stubGlobal("fetch", fetchMock);
|
|
({ trackStudioEvent } = await import("./studioTelemetry"));
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
/** Drain the queue and return the events the batch would have sent. */
|
|
async function sentEvents(): Promise<Array<Record<string, unknown>>> {
|
|
await vi.runOnlyPendingTimersAsync();
|
|
if (fetchMock.mock.calls.length === 0) return [];
|
|
const body = fetchMock.mock.calls[0]?.[1] as { body?: string } | undefined;
|
|
const parsed = JSON.parse(body?.body ?? "{}") as { batch?: Array<Record<string, unknown>> };
|
|
return parsed.batch ?? [];
|
|
}
|
|
|
|
// Every control the shared policy enforces — documented key, legacy key,
|
|
// navigator.doNotTrack, VITE_HYPERFRAMES_NO_TELEMETRY, Vite dev mode, API
|
|
// key eligibility. Before the policy was shared this transport honoured
|
|
// only the legacy key, so all of the others still emitted `studio:*`.
|
|
it("sends nothing when the shared policy refuses", async () => {
|
|
policyState.allowed = false;
|
|
trackStudioEvent("thing_happened");
|
|
expect(await sentEvents()).toHaveLength(0);
|
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("attaches canary assignments to every event", async () => {
|
|
trackStudioEvent("thing_happened");
|
|
const events = await sentEvents();
|
|
expect(events).toHaveLength(1);
|
|
expect(events[0]?.["properties"]).toMatchObject({
|
|
"$feature/canary-test-one": "true",
|
|
});
|
|
});
|
|
|
|
it("lets an explicit property win over the canary mixin", async () => {
|
|
trackStudioEvent("thing_happened", { "$feature/canary-test-one": "false" });
|
|
const events = await sentEvents();
|
|
expect(events[0]?.["properties"]).toMatchObject({
|
|
"$feature/canary-test-one": "false",
|
|
});
|
|
});
|
|
});
|