fix(smoke): use scoped PostHog live bindings

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-08-20 21:20:02 +00:00
1 parent eb962ef283
commit 0b2759fcca
4 files changed
+99 -27

No files matched your search

+36 -10
View File
@@ -1,9 +1,9 @@
const REQUIRED_ENVIRONMENT = [
"PAPERCLIP_E2E_BASE_URL",
"PAPERCLIP_E2E_EMAIL",
"PAPERCLIP_DEV_LOGIN_PASSWORD",
"POSTHOG_PROJECT_ID",
"INTEGRATIONS_POSTHOG_PAPERCLIP_E2E_EMAIL",
"INTEGRATIONS_POSTHOG_PAPERCLIP_DEV_LOGIN_PASSWORD",
"INTEGRATIONS_POSTHOG_POSTHOG_PROJECT_ID",
];
const REQUIRED_PROJECT_ID = "483530";
export class PosthogLivePreflightError extends Error {
constructor(code, details = {}) {
@@ -19,15 +19,31 @@ function requiredValue(environment, key) {
return typeof value === "string" ? value.trim() : "";
}
export function preflightPosthogLive(environment = process.env) {
export function parsePosthogLiveArguments(args = []) {
if (args.length === 0) return {};
if (args.length === 1 && !args[0].startsWith("-")) return { baseUrl: args[0] };
if (args.length === 2 && args[0] === "--base-url" && args[1].trim()) {
return { baseUrl: args[1] };
}
throw new PosthogLivePreflightError("invalid_arguments");
}
export function preflightPosthogLive(environment = process.env, options = {}) {
const missing = REQUIRED_ENVIRONMENT.filter((key) => requiredValue(environment, key) === "");
if (missing.length > 0) {
throw new PosthogLivePreflightError("missing_environment", { missing });
}
const baseUrlValue = typeof options.baseUrl === "string" && options.baseUrl.trim()
? options.baseUrl.trim()
: requiredValue(environment, "PAPERCLIP_API_URL");
if (!baseUrlValue) {
throw new PosthogLivePreflightError("missing_base_url");
}
let baseUrl;
try {
baseUrl = new URL(requiredValue(environment, "PAPERCLIP_E2E_BASE_URL"));
baseUrl = new URL(baseUrlValue);
} catch {
throw new PosthogLivePreflightError("invalid_base_url");
}
@@ -41,11 +57,14 @@ export function preflightPosthogLive(environment = process.env) {
}
baseUrl.pathname = baseUrl.pathname.replace(/\/+$/, "") || "/";
const projectId = requiredValue(environment, "POSTHOG_PROJECT_ID");
const projectId = requiredValue(environment, "INTEGRATIONS_POSTHOG_POSTHOG_PROJECT_ID");
if (!/^\d+$/.test(projectId)) {
throw new PosthogLivePreflightError("invalid_project_id");
}
const email = requiredValue(environment, "PAPERCLIP_E2E_EMAIL");
if (projectId !== REQUIRED_PROJECT_ID) {
throw new PosthogLivePreflightError("unexpected_project_id");
}
const email = requiredValue(environment, "INTEGRATIONS_POSTHOG_PAPERCLIP_E2E_EMAIL");
if (!email.includes("@")) {
throw new PosthogLivePreflightError("invalid_email");
}
@@ -53,17 +72,18 @@ export function preflightPosthogLive(environment = process.env) {
return {
baseUrl: baseUrl.origin,
email,
password: environment.PAPERCLIP_DEV_LOGIN_PASSWORD,
password: environment.INTEGRATIONS_POSTHOG_PAPERCLIP_DEV_LOGIN_PASSWORD,
projectId,
};
}
export async function preparePosthogLiveSmoke({
environment = process.env,
baseUrl,
fetchImpl = globalThis.fetch,
loadBrowser,
}) {
const config = preflightPosthogLive(environment);
const config = preflightPosthogLive(environment, { baseUrl });
let response;
try {
response = await fetchImpl(new URL("/api/health", config.baseUrl), {
@@ -217,5 +237,11 @@ export function preflightFailureMessage(error) {
if (error.code === "health_http_error") {
return `PostHog live smoke preflight failed: /api/health returned HTTP ${error.details.status}.`;
}
if (error.code === "missing_base_url") {
return "PostHog live smoke preflight failed: pass the target Paperclip URL or run inside a Paperclip heartbeat.";
}
if (error.code === "invalid_arguments") {
return "PostHog live smoke preflight failed: expected an optional Paperclip URL or --base-url <url>.";
}
return `PostHog live smoke preflight failed: ${error.code}.`;
}
+3
View File
@@ -5,6 +5,7 @@ import path from "node:path";
import {
assertSanitizedEvidence,
extractProjectSummary,
parsePosthogLiveArguments,
parseSanitizedAgentProof,
PosthogLivePreflightError,
preflightFailureMessage,
@@ -776,7 +777,9 @@ async function runSmoke({ config, chromium }) {
async function main() {
let prepared;
try {
const options = parsePosthogLiveArguments(process.argv.slice(2));
prepared = await preparePosthogLiveSmoke({
baseUrl: options.baseUrl,
loadBrowser: () => import("@playwright/test"),
});
} catch (error) {
+51 -10
View File
@@ -3,6 +3,7 @@ import test from "node:test";
import {
assertSanitizedEvidence,
extractProjectSummary,
parsePosthogLiveArguments,
parseSanitizedAgentProof,
PosthogLivePreflightError,
preflightPosthogLive,
@@ -10,22 +11,24 @@ import {
} from "./posthog-live-lib.mjs";
const COMPLETE_ENV = {
PAPERCLIP_E2E_BASE_URL: "https://paperclip.example.test",
PAPERCLIP_E2E_EMAIL: "operator@example.test",
PAPERCLIP_DEV_LOGIN_PASSWORD: "not-a-real-password",
POSTHOG_PROJECT_ID: "483530",
PAPERCLIP_API_URL: "https://paperclip.example.test/api",
INTEGRATIONS_POSTHOG_PAPERCLIP_E2E_EMAIL: "operator@example.test",
INTEGRATIONS_POSTHOG_PAPERCLIP_DEV_LOGIN_PASSWORD: "not-a-real-password",
INTEGRATIONS_POSTHOG_POSTHOG_PROJECT_ID: "483530",
};
test("preflight reports only missing binding names", () => {
assert.throws(
() => preflightPosthogLive({ PAPERCLIP_DEV_LOGIN_PASSWORD: "present" }),
() => preflightPosthogLive({
PAPERCLIP_API_URL: "https://paperclip.example.test/api",
INTEGRATIONS_POSTHOG_PAPERCLIP_DEV_LOGIN_PASSWORD: "present",
}),
(error) => {
assert.ok(error instanceof PosthogLivePreflightError);
assert.equal(error.code, "missing_environment");
assert.deepEqual(error.details.missing, [
"PAPERCLIP_E2E_BASE_URL",
"PAPERCLIP_E2E_EMAIL",
"POSTHOG_PROJECT_ID",
"INTEGRATIONS_POSTHOG_PAPERCLIP_E2E_EMAIL",
"INTEGRATIONS_POSTHOG_POSTHOG_PROJECT_ID",
]);
assert.doesNotMatch(error.message, /present/);
return true;
@@ -40,16 +43,54 @@ test("preflight rejects credential-bearing and non-HTTPS remote URLs", () => {
"http://example.test",
]) {
assert.throws(
() => preflightPosthogLive({ ...COMPLETE_ENV, PAPERCLIP_E2E_BASE_URL: baseUrl }),
() => preflightPosthogLive({ ...COMPLETE_ENV, PAPERCLIP_API_URL: baseUrl }),
(error) => error instanceof PosthogLivePreflightError && error.code === "unsafe_base_url",
);
}
assert.equal(
preflightPosthogLive({ ...COMPLETE_ENV, PAPERCLIP_E2E_BASE_URL: "http://127.0.0.1:3100" }).baseUrl,
preflightPosthogLive(COMPLETE_ENV, { baseUrl: "http://127.0.0.1:3100" }).baseUrl,
"http://127.0.0.1:3100",
);
});
test("preflight derives the current Paperclip origin and accepts an explicit target", () => {
assert.equal(preflightPosthogLive(COMPLETE_ENV).baseUrl, "https://paperclip.example.test");
assert.equal(
preflightPosthogLive(COMPLETE_ENV, { baseUrl: "https://other-paperclip.example.test" }).baseUrl,
"https://other-paperclip.example.test",
);
assert.throws(
() => preflightPosthogLive({ ...COMPLETE_ENV, PAPERCLIP_API_URL: "" }),
(error) => error instanceof PosthogLivePreflightError && error.code === "missing_base_url",
);
});
test("preflight fails closed unless the PostHog project is exactly 483530", () => {
assert.throws(
() => preflightPosthogLive({
...COMPLETE_ENV,
INTEGRATIONS_POSTHOG_POSTHOG_PROJECT_ID: "42",
}),
(error) => error instanceof PosthogLivePreflightError && error.code === "unexpected_project_id",
);
});
test("live smoke arguments accept a target URL without another environment binding", () => {
assert.deepEqual(parsePosthogLiveArguments([]), {});
assert.deepEqual(
parsePosthogLiveArguments(["https://paperclip.example.test"]),
{ baseUrl: "https://paperclip.example.test" },
);
assert.deepEqual(
parsePosthogLiveArguments(["--base-url", "https://paperclip.example.test"]),
{ baseUrl: "https://paperclip.example.test" },
);
assert.throws(
() => parsePosthogLiveArguments(["--unknown"]),
(error) => error instanceof PosthogLivePreflightError && error.code === "invalid_arguments",
);
});
test("browser loading happens only after binding and health preflight", async () => {
let fetchCalled = false;
let browserLoaded = false;