diff --git a/.gitea/workflows/redeploy-tenants-on-main.yml b/.gitea/workflows/redeploy-tenants-on-main.yml index 6cd8f8a3..9471d0bd 100644 --- a/.gitea/workflows/redeploy-tenants-on-main.yml +++ b/.gitea/workflows/redeploy-tenants-on-main.yml @@ -9,11 +9,12 @@ name: redeploy-tenants-on-main # - Workflow-level env.GITHUB_SERVER_URL pinned per # feedback_act_runner_github_server_url. # - `continue-on-error: true` on each job (RFC §1 contract). -# - ~~**Gitea workflow_run trigger limitation**~~ FIXED: replaced with -# push+paths filter per this PR. Gitea 1.22.6 does not support -# `workflow_run` (task #81). The push trigger fires on every -# commit to publish-workspace-server-image.yml which is the -# same signal (only successful runs commit to main). +# - **Gitea workflow_run trigger limitation**: Gitea 1.22.6's support +# for the `workflow_run` event is partial. If this never fires on a +# real publish-workspace-server-image completion, the follow-up +# triage PR should replace the trigger with a push-with-paths-filter +# on .gitea/workflows/publish-workspace-server-image.yml. Until +# then continue-on-error+dead-workflow doesn't break anything. # # Auto-refresh prod tenant EC2s after every main merge. @@ -49,11 +50,10 @@ name: redeploy-tenants-on-main # target_tag=, re-pulling the older image on every tenant. on: - push: + workflow_run: + workflows: ['publish-workspace-server-image'] + types: [completed] branches: [main] - paths: - - '.gitea/workflows/publish-workspace-server-image.yml' - workflow_dispatch: permissions: contents: read # No write scopes needed — the workflow hits an external CP endpoint, diff --git a/.gitea/workflows/redeploy-tenants-on-staging.yml b/.gitea/workflows/redeploy-tenants-on-staging.yml index 40c4894d..c987ccf7 100644 --- a/.gitea/workflows/redeploy-tenants-on-staging.yml +++ b/.gitea/workflows/redeploy-tenants-on-staging.yml @@ -9,13 +9,12 @@ name: redeploy-tenants-on-staging # - Workflow-level env.GITHUB_SERVER_URL pinned per # feedback_act_runner_github_server_url. # - `continue-on-error: true` on each job (RFC §1 contract). -# - ~~**Gitea workflow_run trigger limitation**~~ FIXED: replaced with -# push+paths filter per this PR. Gitea 1.22.6 does not support -# `workflow_run` (task #81). The push trigger fires on every -# commit to publish-workspace-server-image.yml which is the -# same signal (only successful runs commit to main). Removed -# `workflow_run.conclusion==success` job if since push implies -# the workflow completed and committed. +# - **Gitea workflow_run trigger limitation**: Gitea 1.22.6's support +# for the `workflow_run` event is partial. If this never fires on a +# real publish-workspace-server-image completion, the follow-up +# triage PR should replace the trigger with a push-with-paths-filter +# on .gitea/workflows/publish-workspace-server-image.yml. Until +# then continue-on-error+dead-workflow doesn't break anything. # # Auto-refresh staging tenant EC2s after every staging-branch merge. @@ -51,11 +50,10 @@ name: redeploy-tenants-on-staging # of a known-good build. on: - push: - branches: [staging] - paths: - - '.gitea/workflows/publish-workspace-server-image.yml' - workflow_dispatch: + workflow_run: + workflows: ['publish-workspace-server-image'] + types: [completed] + branches: [main] permissions: contents: read # No write scopes needed — the workflow hits an external CP endpoint, @@ -74,6 +72,12 @@ env: jobs: redeploy: + # Skip the auto-trigger if publish-workspace-server-image didn't + # actually succeed. workflow_run fires on any completion state; we + # don't want to redeploy against a half-built image. + # NOTE (Gitea port): workflow_dispatch trigger dropped; only the + # workflow_run path remains. + if: ${{ github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest # Phase 3 (RFC #219 §1): surface broken workflows without blocking. continue-on-error: true diff --git a/.gitea/workflows/staging-verify.yml b/.gitea/workflows/staging-verify.yml index 7aeaadcd..6c2f8635 100644 --- a/.gitea/workflows/staging-verify.yml +++ b/.gitea/workflows/staging-verify.yml @@ -11,14 +11,11 @@ name: Staging verify # - Workflow-level env.GITHUB_SERVER_URL pinned per # feedback_act_runner_github_server_url. # - `continue-on-error: true` on each job (RFC §1 contract). -# - ~~**Gitea workflow_run trigger limitation**~~ FIXED: replaced with -# push+paths filter per this PR. Gitea 1.22.6 does not support -# `workflow_run` (task #81). The push trigger fires on every -# commit to publish-workspace-server-image.yml. Removed the -# `workflow_run.conclusion==success` job if since the push trigger -# doesn't carry completion state — the smoke test is the safety net -# (it will detect and abort on a bad image regardless). Added -# workflow_dispatch for manual runs. +# - **Gitea workflow_run trigger limitation**: Gitea 1.22.6's support +# for the `workflow_run` event is partial. If this never fires on a +# real publish-workspace-server-image completion, the follow-up +# triage PR should replace the trigger with a push-with-paths-filter +# on the same publish workflow's path (i.e. `.gitea/workflows/publish-workspace-server-image.yml`). # # Runs the canary smoke suite against the staging canary tenant fleet @@ -62,11 +59,9 @@ name: Staging verify # are populated. on: - push: - branches: [staging] - paths: - - '.gitea/workflows/publish-workspace-server-image.yml' - workflow_dispatch: + workflow_run: + workflows: ["publish-workspace-server-image"] + types: [completed] permissions: contents: read packages: write @@ -83,6 +78,10 @@ env: jobs: staging-smoke: + # Skip when the upstream workflow failed — no image to test against. + # workflow_dispatch trigger dropped in this Gitea port; only the + # workflow_run path remains. + if: ${{ github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest # Phase 3 (RFC #219 §1): surface broken workflows without blocking. continue-on-error: true diff --git a/canvas/src/components/__tests__/OrgTemplatesSection.test.tsx b/canvas/src/components/__tests__/OrgTemplatesSection.test.tsx index 59bdda12..f464036a 100644 --- a/canvas/src/components/__tests__/OrgTemplatesSection.test.tsx +++ b/canvas/src/components/__tests__/OrgTemplatesSection.test.tsx @@ -1,102 +1,237 @@ // @vitest-environment jsdom -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -import { render, screen, waitFor, fireEvent, cleanup } from "@testing-library/react"; -// Tests for the default-collapsed + expand-on-click behavior of the -// org templates drawer. Before this change the section rendered all -// org cards inline, which pushed the individual workspace templates -// off-screen when there were ≥3 orgs on disk. Collapsed-by-default -// keeps the scroll focused on the primary deploy path. - -vi.mock("@/lib/api", () => ({ - api: { - get: vi.fn().mockResolvedValue([ - { dir: "free-beats-all", name: "Free Beats All", description: "d1", workspaces: 3 }, - { dir: "medo-smoke", name: "MeDo Smoke Test", description: "d2", workspaces: 1 }, - ]), - post: vi.fn().mockResolvedValue({}), - }, +/** + * Tests for OrgTemplatesSection — collapsible org template import list. + * + * Covers: + * - Header with count badge (visible only when expanded) + * - Collapsed by default, aria-expanded toggles on click + * - aria-controls targets org-templates-body div + * - Empty state when no org templates + * - Loading spinner + * - Org template cards: name, description, workspace count + * - Import button per card + * - Preflight modal opens when org has required_env + * - Preflight onProceed fires import + * - Preflight onCancel closes modal + * - Direct import (no modal) when org has no env requirements + * - Import button disabled while that org is importing + */ +// ── ALL mocks MUST be before imports (vi.mock is hoisted to top of file) ─────── +const { mockGet, mockPost, mockListSecrets } = vi.hoisted(() => ({ + mockGet: vi.fn(), + mockPost: vi.fn(), + mockListSecrets: vi.fn(), })); -vi.mock("../Spinner", () => ({ Spinner: () => null })); -vi.mock("../MissingKeysModal", () => ({ MissingKeysModal: () => null })); -vi.mock("../ConfirmDialog", () => ({ ConfirmDialog: () => null })); -vi.mock("@/lib/deploy-preflight", () => ({ checkDeploySecrets: vi.fn() })); +vi.mock("@/lib/api", () => ({ + api: { get: mockGet, post: mockPost }, +})); +vi.mock("@/lib/api/secrets", () => ({ + listSecrets: mockListSecrets, +})); + +vi.mock("@/store/canvas", () => ({ + useCanvasStore: Object.assign( + vi.fn(), + { getState: () => ({ nodes: [], hydrate: vi.fn() }) }, + ), +})); + +vi.mock("../Spinner", () => ({ + Spinner: () =>