import { readFile } from "node:fs/promises"; import path from "node:path"; import { describe, expect, test } from "vitest"; import { defaultAppRoleSeeds, defaultPermissionSeedDescriptors } from "@hivegrid/db/permissions-seed.server"; import { missingReadinessEnv } from "./run-playwright-a11y.mjs"; import { E2E_READINESS_API_KEY_ALIAS, E2E_READINESS_API_LOG_ENDPOINT, E2E_READINESS_API_LOG_METHOD, E2E_READINESS_ADMIN_EMAIL, E2E_READINESS_ADMIN_PASSWORD, E2E_READINESS_CHAT_EVENT_STREAM_ID, E2E_READINESS_CHAT_ID, E2E_READINESS_CHAT_MEMBER_ID, E2E_READINESS_CHAT_NAME, E2E_READINESS_CHAT_PEER_MEMBER_ID, E2E_READINESS_PLUGIN_ID, E2E_READINESS_PLUGIN_VERSION, E2E_READINESS_WEBHOOK_WORKFLOW_ID, E2E_READINESS_WAITING_EXECUTION_ID, E2E_READINESS_WAITING_MANUAL_BLOCK_EXECUTION_ID, E2E_READINESS_WAITING_WORKFLOW_ID, E2E_READINESS_WORKFLOW_EXECUTION_ID, E2E_READINESS_WORKFLOW_ID, E2E_READINESS_WORKFLOW_LOG_BLOCK_EXECUTION_ID, E2E_READINESS_WORKFLOW_OUTBOX_DRAIN_ASSERTION, E2E_READINESS_WORKFLOW_OUTBOX_DRAIN_BLOCKER, E2E_READINESS_LIFECYCLE_PLUGIN_ID, E2E_READINESS_LIFECYCLE_PLUGIN_VERSION, E2E_READINESS_NO_TENANT_EMAIL, E2E_READINESS_NO_TENANT_PASSWORD, E2E_READINESS_PLUGIN_READONLY_EMAIL, E2E_READINESS_PLUGIN_READONLY_PASSWORD, E2E_READINESS_PLUGIN_READONLY_ROLE_NAME, E2E_READINESS_PLATFORM_LEAD_SOURCE_ID, E2E_READINESS_PLATFORM_LEAD_SOURCE_KEY, E2E_READINESS_PLATFORM_LEAD_SOURCE_NAME, E2E_READINESS_PIPELINE_ID, E2E_READINESS_PIPELINE_NAME, E2E_READINESS_PIPELINE_STAGES, E2E_READINESS_ONBOARDING_CODE, E2E_READINESS_ONBOARDING_ID, E2E_READINESS_ONBOARDING_STEPS, E2E_READINESS_SUBSCRIPTION_PLAN_BILLING_PROVIDER_ID, E2E_READINESS_SUBSCRIPTION_PRICE_BILLING_PROVIDER_ID, E2E_READINESS_TENANT_NAME, E2E_READINESS_TENANT_SLUG, E2E_READINESS_UPGRADE_PLAN_BILLING_PROVIDER_ID, E2E_READINESS_UPGRADE_PRICE_BILLING_PROVIDER_ID, E2E_READINESS_USER_EMAIL, E2E_READINESS_USER_PASSWORD, appFixturePath, assertCanRunE2eReadinessSeed, buildE2eReadinessEnv, buildE2eReadinessEmailSequenceDefinitions, buildE2eReadinessLifecyclePluginManifest, buildE2eReadinessManualWorkflowDefinition, buildE2eReadinessPluginManifest, buildE2eReadinessPluginSettingsHtml, buildE2eReadinessPlatformLeadSource, buildE2eReadinessWaitingWorkflowDefinition, buildE2eReadinessWebhookWorkflowDefinition, renderE2eReadinessEnvFile, } from "./seed-e2e-readiness"; describe("seed-e2e-readiness", () => { test("uses only the canonical disposable local admin credentials", () => { expect(E2E_READINESS_ADMIN_EMAIL).toBe("admin@email.com"); expect(E2E_READINESS_ADMIN_PASSWORD).toBe("password"); }); test("uses the independent operator identity schema", async () => { const source = await readFile(path.resolve(process.cwd(), "scripts/seed-e2e-readiness.ts"), "utf8"); expect(source).not.toContain("adminUser.deleteMany({ where: { userId:"); expect(source).not.toContain("adminUser.upsert({\n where: { userId:"); expect(source).toContain('adminRole.findUnique({ where: { systemKey: "super_admin" }'); expect(source).toContain("adminUserRole.create({"); }); test("refuses production and requires an explicit local opt-in", () => { expect(() => assertCanRunE2eReadinessSeed({ DATABASE_URL: "postgresql://localhost/app", E2E_LOCAL_READINESS_SEED: "1", NODE_ENV: "production", }) ).toThrow("production"); expect(() => assertCanRunE2eReadinessSeed({ DATABASE_URL: "postgresql://localhost/app", NODE_ENV: "development", }) ).toThrow("E2E_LOCAL_READINESS_SEED=1"); expect(() => assertCanRunE2eReadinessSeed({ E2E_LOCAL_READINESS_SEED: "1", NODE_ENV: "development", }) ).toThrow("DATABASE_URL"); }); test("builds the readiness env contract consumed by the Playwright gate", () => { const env = buildE2eReadinessEnv({ tenantSlug: E2E_READINESS_TENANT_SLUG, chatId: E2E_READINESS_CHAT_ID, workflowId: "wf_123", webhookWorkflowId: "wf_webhook_123", waitingWorkflowId: "wf_waiting_123", waitingExecutionId: "wfe_waiting_123", pluginBasePath: "/tmp/e2e-plugins", }); expect(env).toMatchObject({ E2E_TENANT_EMAIL: E2E_READINESS_USER_EMAIL, E2E_TENANT_PASSWORD: E2E_READINESS_USER_PASSWORD, E2E_NO_TENANT_EMAIL: E2E_READINESS_NO_TENANT_EMAIL, E2E_NO_TENANT_PASSWORD: E2E_READINESS_NO_TENANT_PASSWORD, E2E_PLUGIN_READONLY_EMAIL: E2E_READINESS_PLUGIN_READONLY_EMAIL, E2E_PLUGIN_READONLY_PASSWORD: E2E_READINESS_PLUGIN_READONLY_PASSWORD, E2E_TENANT_ACCOUNT_NAME: E2E_READINESS_TENANT_NAME, E2E_TENANT_ACCOUNT_SLUG: E2E_READINESS_TENANT_SLUG, E2E_REGISTRATION_EXPECTS_EMAIL_VERIFICATION: "0", E2E_TENANT_DASHBOARD_PATH: "/e2e-readiness/dashboard", E2E_TENANT_CHAT_PATH: "/e2e-readiness/chat/chat_e2e_readiness", E2E_TENANT_SETTINGS_ACCOUNT_PATH: "/e2e-readiness/settings/workspace", E2E_TENANT_SETTINGS_PROFILE_PATH: "/e2e-readiness/settings/profile", E2E_TENANT_SUBSCRIPTION_PATH: "/e2e-readiness/settings/billing", E2E_TENANT_MEMBERS_PATH: "/e2e-readiness/settings/team", E2E_TENANT_ROLES_AND_PERMISSIONS_PATH: "/e2e-readiness/settings/team/roles", E2E_TENANT_NOTIFICATIONS_PATH: "/e2e-readiness/settings/profile/notifications", E2E_TENANT_LOGS_PATH: "/e2e-readiness/settings/compliance", E2E_TENANT_LOG_EVENTS_PATH: "/e2e-readiness/settings/compliance/events", E2E_TENANT_AUDIT_EXPORT_PATH: "/e2e-readiness/settings/compliance/audit-export", E2E_TENANT_PRIVACY_PATH: "/e2e-readiness/settings/profile/data", E2E_TENANT_API_PATH: "/e2e-readiness/settings/integrations/api", E2E_TENANT_API_KEYS_PATH: "/e2e-readiness/settings/integrations/api/keys", E2E_TENANT_API_LOGS_PATH: "/e2e-readiness/settings/integrations/api/logs", E2E_TENANT_POLICIES_PATH: "/e2e-readiness/settings/workspace/policies", E2E_ADMIN_EMAIL_SEQUENCES_PATH: "/email-sequences", E2E_ADMIN_PLATFORM_LEADS_PATH: "/leads", E2E_ADMIN_PLATFORM_LEAD_SOURCE_NAME: E2E_READINESS_PLATFORM_LEAD_SOURCE_NAME, E2E_PLUGIN_SETTINGS_PATH: "/e2e-readiness/settings/integrations/plugins", E2E_WORKFLOW_ENGINE_PATH: "/e2e-readiness/workflow-engine/workflows", E2E_WORKFLOW_BUILDER_PATH: "/e2e-readiness/workflow-engine/workflows/wf_123", E2E_WORKFLOW_RUN_API_PATH: "/e2e-readiness/workflow-engine/workflows/wf_123/run/api", E2E_WORKFLOW_RUN_MANUAL_PATH: "/e2e-readiness/workflow-engine/workflows/wf_123/run/manual", E2E_WORKFLOW_WEBHOOK_BUILDER_PATH: "/e2e-readiness/workflow-engine/workflows/wf_webhook_123", E2E_WORKFLOW_WEBHOOK_RUN_MANUAL_PATH: "/e2e-readiness/workflow-engine/workflows/wf_webhook_123/run/manual", E2E_WORKFLOW_WAITING_BUILDER_PATH: "/e2e-readiness/workflow-engine/workflows/wf_waiting_123", E2E_WORKFLOW_WAITING_RUN_MANUAL_PATH: "/e2e-readiness/workflow-engine/workflows/wf_waiting_123/run/manual", E2E_WORKFLOW_WAITING_RUN_API_PATH: "/e2e-readiness/workflow-engine/workflows/wf_waiting_123/run/api", E2E_WORKFLOW_WAITING_EXECUTIONS_PATH: "/e2e-readiness/workflow-engine/workflows/wf_waiting_123/executions", E2E_WORKFLOW_WAITING_EXECUTION_ID: "wfe_waiting_123", E2E_ALLOW_CHAT_SEND: "0", E2E_ALLOW_WORKFLOW_RUN: "1", E2E_ALLOW_WORKFLOW_WEBHOOK_RUN: "0", E2E_WORKFLOW_WEBHOOK_TARGET_URL: "", E2E_REQUIRE_WORKFLOW_OUTBOX_DRAIN: "0", E2E_WORKFLOW_OUTBOX_DRAIN_EXPECTED_STATUS: "success", E2E_WORKFLOW_OUTBOX_DRAIN_ASSERTION: "operator-gated", E2E_WORKFLOW_OUTBOX_DRAIN_BLOCKER: "Workflow outbox-drain readiness is operator-gated: set E2E_REQUIRE_WORKFLOW_OUTBOX_DRAIN=1 only when the general worker is running and the seeded manual workflow can reach the expected success execution status.", PLUGIN_BINARY_BASE_PATH: "/tmp/e2e-plugins", }); expect(missingReadinessEnv({ env })).toEqual([]); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_TENANT_EMAIL="e2e.readiness@example.test"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_NO_TENANT_EMAIL="e2e.no-tenant@example.test"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_NO_TENANT_PASSWORD="password"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_PLUGIN_READONLY_EMAIL="e2e.plugin-readonly@example.test"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_PLUGIN_READONLY_PASSWORD="password"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_REGISTRATION_EXPECTS_EMAIL_VERIFICATION="0"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_TENANT_CHAT_PATH="/e2e-readiness/chat/chat_e2e_readiness"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_ALLOW_CHAT_SEND="0"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_TENANT_SETTINGS_ACCOUNT_PATH="/e2e-readiness/settings/workspace"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_TENANT_MEMBERS_PATH="/e2e-readiness/settings/team"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_TENANT_PRIVACY_PATH="/e2e-readiness/settings/profile/data"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_TENANT_API_LOGS_PATH="/e2e-readiness/settings/integrations/api/logs"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_ADMIN_EMAIL_SEQUENCES_PATH="/email-sequences"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_ADMIN_PLATFORM_LEADS_PATH="/leads"'); expect(renderE2eReadinessEnvFile(env)).toContain(`E2E_ADMIN_PLATFORM_LEAD_SOURCE_NAME="${E2E_READINESS_PLATFORM_LEAD_SOURCE_NAME}"`); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_ALLOW_WORKFLOW_WEBHOOK_RUN="0"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_WORKFLOW_WEBHOOK_TARGET_URL=""'); expect(renderE2eReadinessEnvFile(env)).toContain( 'E2E_WORKFLOW_WAITING_EXECUTIONS_PATH="/e2e-readiness/workflow-engine/workflows/wf_waiting_123/executions"' ); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_WORKFLOW_WAITING_EXECUTION_ID="wfe_waiting_123"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_REQUIRE_WORKFLOW_OUTBOX_DRAIN="0"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_WORKFLOW_OUTBOX_DRAIN_EXPECTED_STATUS="success"'); expect(renderE2eReadinessEnvFile(env)).toContain('E2E_WORKFLOW_OUTBOX_DRAIN_ASSERTION="operator-gated"'); }); test("documents workflow outbox-drain readiness as an operator-gated expected-success probe", () => { expect(E2E_READINESS_WORKFLOW_OUTBOX_DRAIN_ASSERTION).toBe("operator-gated"); expect(E2E_READINESS_WORKFLOW_OUTBOX_DRAIN_BLOCKER).toBe( "Workflow outbox-drain readiness is operator-gated: set E2E_REQUIRE_WORKFLOW_OUTBOX_DRAIN=1 only when the general worker is running and the seeded manual workflow can reach the expected success execution status." ); }); test("defines an active platform-lead source routed to the seeded operator", () => { const source = buildE2eReadinessPlatformLeadSource("admin_e2e"); expect(source).toEqual({ id: E2E_READINESS_PLATFORM_LEAD_SOURCE_ID, key: E2E_READINESS_PLATFORM_LEAD_SOURCE_KEY, displayName: E2E_READINESS_PLATFORM_LEAD_SOURCE_NAME, enabled: true, defaultAssigneeAdminUserId: "admin_e2e", notifyAdminUserIds: ["admin_e2e"], rateLimitPerMinute: 60, metadata: { fixture: "e2e-readiness" }, }); }); test("defines deterministic API key and log fixture identifiers", () => { expect(E2E_READINESS_API_KEY_ALIAS).toBe("E2E Readiness API Key"); expect(E2E_READINESS_API_LOG_ENDPOINT).toBe("/v1/e2e-readiness"); expect(E2E_READINESS_API_LOG_METHOD).toBe("GET"); }); test("defines a deterministic private chat readiness fixture", () => { expect(E2E_READINESS_CHAT_ID).toBe("chat_e2e_readiness"); expect(E2E_READINESS_CHAT_NAME).toBe("E2E Readiness Team Chat"); expect(E2E_READINESS_CHAT_EVENT_STREAM_ID).toBe("chat_stream_e2e_readiness"); expect(E2E_READINESS_CHAT_MEMBER_ID).toBe("chat_member_e2e_readiness"); expect(E2E_READINESS_CHAT_PEER_MEMBER_ID).toBe("chat_peer_member_e2e_readiness"); }); test("defines a deterministic default project pipeline fixture", () => { expect(E2E_READINESS_PIPELINE_ID).toBe("pipeline_e2e_readiness"); expect(E2E_READINESS_PIPELINE_NAME).toBe("E2E Readiness Projects"); expect(E2E_READINESS_PIPELINE_STAGES).toEqual([ expect.objectContaining({ id: "pipeline_stage_e2e_readiness_open", stageKind: "offen", winProbability: 20 }), expect.objectContaining({ id: "pipeline_stage_e2e_readiness_won", stageKind: "gewonnen", winProbability: 100 }), expect.objectContaining({ id: "pipeline_stage_e2e_readiness_lost", stageKind: "verloren", winProbability: 0 }), ]); }); test("defines the canonical first-steps fixture required by the onboarding smoke", () => { expect(E2E_READINESS_ONBOARDING_ID).toBe("shk-first-steps-v1"); expect(E2E_READINESS_ONBOARDING_CODE).toBe("shk-first-steps"); expect(E2E_READINESS_ONBOARDING_STEPS).toEqual([ { id: "shk-first-steps-profile-v1", order: 10, block: "profile" }, { id: "shk-first-steps-contacts-v1", order: 20, block: "contacts" }, { id: "shk-first-steps-projects-v1", order: 30, block: "projects" }, { id: "shk-first-steps-billing-v1", order: 40, block: "billing" }, ]); }); test("defines separate owned and unowned subscription fixtures for billing smokes", () => { expect(E2E_READINESS_SUBSCRIPTION_PLAN_BILLING_PROVIDER_ID).toBe("e2e_readiness_plan"); expect(E2E_READINESS_SUBSCRIPTION_PRICE_BILLING_PROVIDER_ID).toBe("e2e_readiness_price_monthly"); expect(E2E_READINESS_UPGRADE_PLAN_BILLING_PROVIDER_ID).toBe("e2e_readiness_upgrade_plan"); expect(E2E_READINESS_UPGRADE_PRICE_BILLING_PROVIDER_ID).toBe("e2e_readiness_upgrade_price_monthly"); }); test("keeps the plugin read-only fixture on the default User app role contract", () => { expect(E2E_READINESS_PLUGIN_READONLY_EMAIL).toBe("e2e.plugin-readonly@example.test"); expect(E2E_READINESS_PLUGIN_READONLY_PASSWORD).toBe("password"); expect(E2E_READINESS_PLUGIN_READONLY_ROLE_NAME).toBe("User"); expect(defaultAppRoleSeeds.find((role) => role.name === E2E_READINESS_PLUGIN_READONLY_ROLE_NAME)).toMatchObject({ type: "app", assignToNewUsers: true, }); expect(defaultPermissionSeedDescriptors.find((permission) => permission.name === "app.plugins.use")?.inRoles).toContain( E2E_READINESS_PLUGIN_READONLY_ROLE_NAME ); expect(defaultPermissionSeedDescriptors.find((permission) => permission.name === "app.plugins.manage")?.inRoles).not.toContain( E2E_READINESS_PLUGIN_READONLY_ROLE_NAME ); }); test("keeps the plugin fixture shaped for consent, revoke, and iframe settings coverage", () => { const manifest = buildE2eReadinessPluginManifest(); expect(manifest.id).toBe(E2E_READINESS_PLUGIN_ID); expect(manifest.version).toBe(E2E_READINESS_PLUGIN_VERSION); expect(manifest.capabilities.map((capability) => capability.name)).toEqual(["log", "audit"]); expect(manifest.hooks).toEqual([{ name: "workflow.executed", export: "onWorkflowExecuted" }]); expect(manifest.ui?.settings?.entry).toBe("settings/index.html"); expect(manifest.ui?.settings?.sha256).toMatch(/^[a-f0-9]{64}$/u); }); test("keeps the plugin settings HTML compatible with the hardened iframe protocol", () => { const html = buildE2eReadinessPluginSettingsHtml(); expect(html).toContain('meta[name="plugin-id"]'); expect(html).toContain('meta[name="plugin-settings-channel"]'); expect(html).toContain('meta[name="plugin-settings-protocol"]'); expect(html).toContain("parent.postMessage"); expect(html).toContain("type: 'plugin.settings.set'"); expect(html).toContain("version: protocolVersion"); expect(html).toContain("pluginId: pluginId"); expect(html).toContain("channelId: channelId"); expect(html).toContain("requestId: 'seed-fixture-save'"); expect(html).toContain("key: 'theme'"); expect(html).toContain("value: 'ready'"); expect(html).toContain("