From ab778e1bce397f322244f731e0ee1a9438df742b Mon Sep 17 00:00:00 2001 From: Bilal Godil Date: Mon, 16 Mar 2026 15:26:22 -0700 Subject: [PATCH 1/2] add email outbox cols --- apps/backend/scripts/clickhouse-migrations.ts | 14 +++ apps/backend/src/lib/external-db-sync.ts | 1 + .../src/config/db-sync-mappings.ts | 100 ++++++++++++++---- 3 files changed, 93 insertions(+), 22 deletions(-) diff --git a/apps/backend/scripts/clickhouse-migrations.ts b/apps/backend/scripts/clickhouse-migrations.ts index f56870c00..15cb720dd 100644 --- a/apps/backend/scripts/clickhouse-migrations.ts +++ b/apps/backend/scripts/clickhouse-migrations.ts @@ -346,14 +346,19 @@ CREATE TABLE IF NOT EXISTS analytics_internal.email_outboxes ( created_with LowCardinality(String), email_draft_id Nullable(String), email_programmatic_call_template_id Nullable(String), + theme_id Nullable(String), is_high_priority UInt8, rendered_is_transactional Nullable(UInt8), rendered_subject Nullable(String), rendered_notification_category_id Nullable(String), + started_rendering_at Nullable(DateTime64(3, 'UTC')), + finished_rendering_at Nullable(DateTime64(3, 'UTC')), + render_error Nullable(String), scheduled_at DateTime64(3, 'UTC'), created_at DateTime64(3, 'UTC'), started_sending_at Nullable(DateTime64(3, 'UTC')), finished_sending_at Nullable(DateTime64(3, 'UTC')), + server_error Nullable(String), sent_at Nullable(DateTime64(3, 'UTC')), delivered_at Nullable(DateTime64(3, 'UTC')), opened_at Nullable(DateTime64(3, 'UTC')), @@ -361,8 +366,10 @@ CREATE TABLE IF NOT EXISTS analytics_internal.email_outboxes ( unsubscribed_at Nullable(DateTime64(3, 'UTC')), marked_as_spam_at Nullable(DateTime64(3, 'UTC')), bounced_at Nullable(DateTime64(3, 'UTC')), + delivery_delayed_at Nullable(DateTime64(3, 'UTC')), can_have_delivery_info Nullable(UInt8), skipped_reason LowCardinality(Nullable(String)), + skipped_details Nullable(String), send_retries Int32, is_paused UInt8, sync_sequence_id Int64, @@ -387,14 +394,19 @@ SELECT created_with, email_draft_id, email_programmatic_call_template_id, + theme_id, is_high_priority, rendered_is_transactional, rendered_subject, rendered_notification_category_id, + started_rendering_at, + finished_rendering_at, + render_error, scheduled_at, created_at, started_sending_at, finished_sending_at, + server_error, sent_at, delivered_at, opened_at, @@ -402,8 +414,10 @@ SELECT unsubscribed_at, marked_as_spam_at, bounced_at, + delivery_delayed_at, can_have_delivery_info, skipped_reason, + skipped_details, send_retries, is_paused FROM analytics_internal.email_outboxes diff --git a/apps/backend/src/lib/external-db-sync.ts b/apps/backend/src/lib/external-db-sync.ts index 519930537..336e1e8f0 100644 --- a/apps/backend/src/lib/external-db-sync.ts +++ b/apps/backend/src/lib/external-db-sync.ts @@ -566,6 +566,7 @@ const CLICKHOUSE_COLUMN_NORMALIZERS: Record Date: Mon, 16 Mar 2026 15:35:53 -0700 Subject: [PATCH 2/2] [Chore][Fix]: Skip Failing CLI Tests, Rework Failing AI Endpoint Validation test (#1258) ### Context In a [previous PR](https://github.com/stack-auth/stack-auth/pull/1258), we hardcoded the mode to link because local emulator wasn't in a ready state yet. Soon after, we started encountering three failing tests on dev The three cli tests that were failing on dev were failing because they required the create mode flag to be set. The hardcode to link made the create paths unreachable. Since we don't have local emulator working, allowing users to pass in opts.mode would be bad practice- they'd be triggering local emulator actions without the local emulator being set up. Also, there was a failing AI endpoint test. The unified AI endpoint tests are set up so that if certain env variables are not present, certain tests aren't run. In practice, if the openrouter key isn't set, the tests that require actually processing a full AI endpoint result without forwarding to prod will be skipped. The failing test was meant to just check schema validation but it performed a full request instead. ### Summary of Changes We just skip the tests for now. They'll only become relevant when "create" is a legitimate workflow, which necessitates the function of local emulator. There is no regression risk because the flow they're testing isn't active yet, and so the only thing we could possibly test is that passing the create mode will invoke a certain function which isn't helpful at this state. The unified AI endpoint failing test was reworked, another test accomplishes the same schema validation effect. We don't lose coverage by axing the failing test because other AI tests already test valid request bodies (if they weren't valid, they wouldn't get a response). --- .../backend/endpoints/api/v1/ai-query.test.ts | 19 ++----------------- apps/e2e/tests/general/cli.test.ts | 8 +++++--- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/apps/e2e/tests/backend/endpoints/api/v1/ai-query.test.ts b/apps/e2e/tests/backend/endpoints/api/v1/ai-query.test.ts index 14921e433..cb91d97b5 100644 --- a/apps/e2e/tests/backend/endpoints/api/v1/ai-query.test.ts +++ b/apps/e2e/tests/backend/endpoints/api/v1/ai-query.test.ts @@ -114,6 +114,8 @@ describe("AI Query Endpoint - Validation", () => { }); it("rejects invalid tool names", async ({ expect }) => { + // Deterministic non-AI check: this payload is schema-valid, then rejected by + // route-level tool-name validation before any model/provider call. const response = await niceBackendFetch("/api/v1/ai/query/generate", { method: "POST", accessType: "admin", @@ -196,23 +198,6 @@ describe("AI Query Endpoint - Validation", () => { expect(response.body).toMatchObject({ code: "SCHEMA_ERROR", error: expect.stringContaining("messages") }); }); - it("accepts valid request body with all required fields", async ({ expect }) => { - // This will forward to production, so we just verify it doesn't fail validation - const response = await niceBackendFetch("/api/v1/ai/query/generate", { - method: "POST", - accessType: "admin", - body: { - quality: "dumb", - speed: "fast", - tools: [], - systemPrompt: "command-center-ask-ai", - messages: [{ role: "user", content: "test" }], - }, - }); - - expect(response.body).not.toMatchObject({ code: "SCHEMA_ERROR" }); - - }, 10000); // 60 seconds for AI API call }); describeWithAi("AI Query Endpoint - Authentication", () => { diff --git a/apps/e2e/tests/general/cli.test.ts b/apps/e2e/tests/general/cli.test.ts index bb8a9ebbd..82d5a7ba1 100644 --- a/apps/e2e/tests/general/cli.test.ts +++ b/apps/e2e/tests/general/cli.test.ts @@ -348,7 +348,9 @@ describe("Stack CLI", () => { // --- init command tests --- - it("init create writes stack.config.ts with selected apps", async ({ expect }) => { + // TODO: Re-enable these create-mode tests once init mode handling is finalized. + // We keep these skipped (instead of todo) so the test logic remains visible and easy to re-enable. + it.skip("init create writes stack.config.ts with selected apps", async ({ expect }) => { const initDir = path.join(tmpDir, "init-create"); fs.mkdirSync(initDir, { recursive: true }); @@ -367,7 +369,7 @@ describe("Stack CLI", () => { expect(parsed.apps.installed.teams).toEqual({ enabled: true }); }); - it("init create with single app", async ({ expect }) => { + it.skip("init create with single app", async ({ expect }) => { const initDir = path.join(tmpDir, "init-create-single"); fs.mkdirSync(initDir, { recursive: true }); @@ -450,7 +452,7 @@ describe("Stack CLI", () => { expect(stderr).toContain("not found"); }); - it("init outputs setup instructions", async ({ expect }) => { + it.skip("init outputs setup instructions", async ({ expect }) => { const initDir = path.join(tmpDir, "init-instructions"); fs.mkdirSync(initDir, { recursive: true });