Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
00a83d0
feat(projects): add project identity and lifecycle foundation
mzxchandra Oct 2, 2026
aa9b415
feat(projects): create projects with their initial environment
mzxchandra Oct 2, 2026
91cba23
docs(projects): record project files follow-up
mzxchandra Oct 2, 2026
0980ad1
docs(projects): explain project and workspace creation flows
mzxchandra Oct 2, 2026
a14b448
fix(projects): stage activation after compatible writers deploy
mzxchandra Oct 2, 2026
5121347
feat(projects): enforce membership after the staged backfill
mzxchandra Oct 2, 2026
523338a
fix(projects): retry writes after concurrent membership changes
mzxchandra Oct 2, 2026
dc5e301
fix(projects): align integration fixtures with membership constraints
mzxchandra Oct 2, 2026
c1c8e5d
refactor(projects): prepare compatible writers for the SQL backfill
mzxchandra Oct 2, 2026
2e94a13
Merge branch 'feat/project-entity-foundation' into codex/project-enti…
mzxchandra Oct 2, 2026
5e11d7d
refactor(projects): backfill through bounded SQL migration batches
mzxchandra Oct 2, 2026
99189f2
fix(projects): clean up automatically created fixture Projects
mzxchandra Oct 2, 2026
78f773d
Merge branch 'feat/project-entity-foundation' into codex/project-enti…
mzxchandra Oct 2, 2026
2ecbfdf
fix(projects): backfill only families missing membership
mzxchandra Oct 2, 2026
30bc9d8
chore(projects): merge staging into foundation
mzxchandra Oct 3, 2026
b5cc3c4
chore(projects): merge updated foundation into enforcement
mzxchandra Oct 3, 2026
2afc652
fix(projects): bound the trigger installation lock window
mzxchandra Oct 3, 2026
6dfbc4f
fix(workflows): guard restore against concurrent workspace archive
mzxchandra Oct 3, 2026
8965210
fix(projects): keep workflow lifecycle guards at workspace scope
mzxchandra Oct 3, 2026
552ce68
fix(workflows): guard restore against concurrent workspace archive
mzxchandra Oct 3, 2026
8be18e5
Merge branch 'feat/project-entity-foundation' into codex/project-enti…
mzxchandra Oct 3, 2026
0b7eed6
fix(projects): close lifecycle races and surface rollout conflicts
mzxchandra Oct 5, 2026
b8c44e9
Merge branch 'feat/project-entity-foundation' into codex/project-enti…
mzxchandra Oct 5, 2026
634b19e
fix(workflows): return not found when import loses archive race
mzxchandra Oct 5, 2026
40ea011
Merge branch 'feat/project-entity-foundation' into codex/project-enti…
mzxchandra Oct 5, 2026
fd874d0
fix(projects): tighten rollout checks and deduplicate lifecycle valid…
mzxchandra Oct 5, 2026
d3c56a0
test(projects): detect missing lifecycle validation calls
mzxchandra Oct 5, 2026
8f36f2d
test(projects): verify concurrent lifecycle lock contention
mzxchandra Oct 5, 2026
7ec83f0
chore(projects): merge staging into membership enforcement
mzxchandra Oct 6, 2026
ef5498a
test(projects): update new workspace fixtures for enforcement
mzxchandra Oct 6, 2026
41b3a28
chore(projects): sync enforcement migrations with staging
mzxchandra Oct 6, 2026
10a5f06
chore(projects): follow staging table migration
mzxchandra Oct 6, 2026
28b0017
chore(projects): merge staging and advance enforcement migration
mzxchandra Oct 8, 2026
e2b8dfa
fix(tests): create Project membership in desktop and CLI fixtures
mzxchandra Oct 8, 2026
3cf5483
Merge staging into project enforcement and advance migration to 0402
mzxchandra Oct 8, 2026
947fda1
Merge pinned staging and advance Project enforcement to 0403
mzxchandra Oct 8, 2026
ab1e8cb
Merge pinned staging table ordering changes and provision dispatcher …
mzxchandra Oct 8, 2026
d4635ed
Merge pinned staging acquisition attribution changes
mzxchandra Oct 8, 2026
83bdee2
fix(tests): restore Project enforcement fixtures after migration renu…
mzxchandra Oct 8, 2026
61ffede
fix(tests): scope migration probes and align remaining Project fixtures
mzxchandra Oct 9, 2026
4297631
refactor(projects): enforce membership through workspace project column
mzxchandra Oct 9, 2026
f781687
merge compatibility fixture validation into column enforcement
mzxchandra Oct 9, 2026
363a0e1
Merge branch 'feat/project-workspace-column-expand' into codex/projec…
mzxchandra Oct 9, 2026
744dd0d
Merge column-only Project membership and retire connector after rollout
mzxchandra Oct 9, 2026
51ff1dc
Merge staging and order Project enforcement after column expansion
mzxchandra Oct 9, 2026
d782078
Merge Project transfer fixture corrections into enforcement
mzxchandra Oct 9, 2026
1376167
test(projects): provision required membership in transfer unit fixtures
mzxchandra Oct 9, 2026
0933be0
Merge workspace response projection from Project column expansion
mzxchandra Oct 9, 2026
3e4c88c
feat(projects): prepare legacy membership with a resumable operator tool
mzxchandra Oct 9, 2026
c375ebc
Merge branch 'feat/project-workspace-column-expand' into codex/projec…
mzxchandra Oct 9, 2026
243af21
Merge branch 'feat/project-workspace-column-expand' into codex/projec…
mzxchandra Oct 9, 2026
786ac33
Merge branch 'feat/project-workspace-column-expand' into codex/projec…
mzxchandra Oct 9, 2026
c240b7f
Merge branch 'feat/project-workspace-column-expand' into codex/projec…
mzxchandra Oct 9, 2026
39f54a0
fix(projects): support PostgreSQL 16 preparation and current fixtures
mzxchandra Oct 9, 2026
6d4185b
Merge branch 'feat/project-workspace-column-expand' into codex/projec…
mzxchandra Oct 9, 2026
8f06830
fix(projects): enforce safe schema push provisioning
mzxchandra Oct 9, 2026
677186f
Merge branch 'feat/project-workspace-column-expand' into codex/projec…
mzxchandra Oct 9, 2026
c2b95cb
test(projects): provision newly merged workspace fixtures
mzxchandra Oct 9, 2026
50361dc
Merge branch 'feat/project-workspace-column-expand' into codex/projec…
mzxchandra Oct 9, 2026
d1c7457
Merge branch 'feat/project-workspace-column-expand' into codex/projec…
mzxchandra Oct 9, 2026
97a439e
Merge branch 'feat/project-workspace-column-expand' into codex/projec…
mzxchandra Oct 9, 2026
7f8eb18
Merge branch 'feat/project-workspace-column-expand' into codex/projec…
mzxchandra Oct 9, 2026
b653f5e
fix(projects): close operator verification and contention gaps
mzxchandra Oct 9, 2026
38d192d
test(billing): stabilize member ledger timeout checks
mzxchandra Oct 9, 2026
8c3c334
fix(projects): await cleanup and bound backfill artifacts
mzxchandra Oct 9, 2026
454e2e2
fix(projects): honor operator cancellation and status exits
mzxchandra Oct 9, 2026
fb27e1b
fix(projects): reject verification of conflicted plans
mzxchandra Oct 9, 2026
88d10da
fix(projects): defer archive repair on database contention
mzxchandra Oct 9, 2026
4025e5a
fix(projects): coordinate read-only maintenance commands
mzxchandra Oct 9, 2026
c5ff743
fix(projects): run bounded membership backfill before enforcement
mzxchandra Oct 9, 2026
72da20a
Merge trigger-free expansion into project enforcement
mzxchandra Oct 9, 2026
7613c14
test(projects): verify column-only archive repair assignment
mzxchandra Oct 9, 2026
8e35470
Merge schema-push index fix into project enforcement
mzxchandra Oct 9, 2026
e92ba0b
ci(projects): use standard integration coverage for enforcement
mzxchandra Oct 9, 2026
bdf50b2
fix(projects): reconcile assigned environments before enforcement
mzxchandra Oct 9, 2026
ecfdf8b
chore(projects): integrate validated column expansion into enforcement
mzxchandra Oct 9, 2026
d9743bb
fix(projects): preserve backfill repair and resume guarantees
mzxchandra Oct 10, 2026
639c8ca
test(projects): align fixture lifecycle with enforced project ownership
mzxchandra Oct 10, 2026
1e4499e
fix(projects): integrate reviewed repair and fixture corrections
mzxchandra Oct 10, 2026
c704293
test(projects): skip archive repair suite when Redis is unset
mzxchandra Oct 10, 2026
b2718cb
merge: integrate renumbered Project expansion and staging fixtures
mzxchandra Oct 10, 2026
d0a9792
chore(projects): normalize enforcement SQL whitespace
mzxchandra Oct 10, 2026
d8a9627
feat(projects): commit membership authority before reconciliation
mzxchandra Oct 10, 2026
32f49e3
merge: integrate Project authority switch into enforcement
mzxchandra Oct 10, 2026
406a78f
merge: sync Project enforcement with expansion 0406
mzxchandra Oct 10, 2026
e5da5c8
merge: synchronize expansion fixture lint fix
mzxchandra Oct 10, 2026
d1443c8
merge: synchronize expansion billing fixture fix
mzxchandra Oct 10, 2026
14554d0
Merge bounded authority barrier and replay review fixes
mzxchandra Oct 10, 2026
357740a
fix(projects): pin operator schema and pace detach repairs
mzxchandra Oct 10, 2026
69df502
Merge branch 'feat/project-workspace-column-expand' into codex/projec…
mzxchandra Oct 10, 2026
36b0074
refactor(projects): replace integrity triggers with native foreign keys
mzxchandra Oct 10, 2026
d4e3da6
Merge branch 'feat/project-workspace-column-expand' into codex/projec…
mzxchandra Oct 10, 2026
d2f0893
fix(projects): retire rollout marker with legacy membership
mzxchandra Oct 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions .github/scripts/check-project-rollout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/usr/bin/env python3
"""Read-only, fail-closed ECS retirement check for the Project column contract migration.
The expected digest acknowledges fully deployed #8830 transaction-barrier and durable-authority support
and retirement of every incompatible server and relevant old worker job before switching. Every live and supported
rollback application must tolerate project_workspace being absent.
AWS checks below independently verify ECS retirement, not worker drainage.
"""
import argparse
import datetime
import json
import re
import subprocess
import sys


def aws(region, *args):
result = subprocess.run(
['aws', '--region', region, '--no-cli-pager', '--cli-connect-timeout', '10', '--cli-read-timeout', '30', *args, '--output', 'json'],
capture_output=True, text=True, timeout=90, check=False,
)
if result.returncode:
raise RuntimeError('AWS preflight read failed; check deployment-read permissions')
return json.loads(result.stdout)


def latest_execution(region, pipeline):
executions = aws(region, 'codepipeline', 'list-pipeline-executions', '--pipeline-name', pipeline).get('pipelineExecutionSummaries', [])
def epoch(execution):
value = execution['startTime']
return value if isinstance(value, (int, float)) else datetime.datetime.fromisoformat(value.replace('Z', '+00:00')).timestamp()
if not executions:
raise RuntimeError('No application deployment execution was found')
return max(executions, key=epoch)


def matches_release(execution, digest):
return execution.get('status') == 'Succeeded' and any(
revision.get('actionName') == 'ECR_Source' and revision.get('revisionId') == digest
for revision in execution.get('sourceRevisions', [])
)


def verify(environment, region, digest):
if not re.fullmatch(r'sha256:[0-9a-f]{64}', digest):
raise RuntimeError('Set the environment-specific PROJECT_COLUMN_ENFORCEMENT_READY_IMAGE_DIGEST after verifying #8830 authority-aware readers/writers, transaction barriers and all incompatible server/worker drainage')
pipeline = f'sim-{environment}-{region}-app-deployment'
Comment thread
mzxchandra marked this conversation as resolved.
execution = latest_execution(region, pipeline)
if not matches_release(execution, digest):
raise RuntimeError('The latest app pipeline has not completed for the acknowledged image; traffic cutover alone is insufficient')
execution_id = execution['pipelineExecutionId']
group = aws(region, 'deploy', 'get-deployment-group', '--application-name', f'sim-{environment}-{region}-ecs-app',
'--deployment-group-name', f'sim-{environment}-{region}-app-dg')['deploymentGroupInfo']
services = group.get('ecsServices', [])
if len(services) != 1:
raise RuntimeError('Expected exactly one application ECS service')
cluster, service = services[0]['clusterName'], services[0]['serviceName']
description = aws(region, 'ecs', 'describe-services', '--cluster', cluster, '--services', service)
if description.get('failures') or len(description.get('services', [])) != 1:
raise RuntimeError('Cannot inspect the application ECS service')
record = description['services'][0]
if record.get('desiredCount', 0) < 1 or record.get('runningCount') != record['desiredCount'] or record.get('pendingCount') != 0:
raise RuntimeError('Application ECS service is not stable')
arns = set()
for status in ('RUNNING', 'STOPPED'):
arns.update(aws(region, 'ecs', 'list-tasks', '--cluster', cluster, '--service-name', service,
'--desired-status', status).get('taskArns', []))
live = []
ordered = sorted(arns)
for start in range(0, len(ordered), 100):
response = aws(region, 'ecs', 'describe-tasks', '--cluster', cluster, '--tasks', *ordered[start:start + 100])
if response.get('failures'):
raise RuntimeError('Cannot account for every ECS task')
if len(response.get('tasks', [])) != len(ordered[start:start + 100]):
raise RuntimeError('Incomplete ECS task response')
live.extend(task for task in response['tasks'] if task.get('lastStatus') != 'STOPPED')
if len(live) != record['desiredCount']:
raise RuntimeError('Old, stopping, or pending ECS tasks remain')
for task in live:
app = [container for container in task.get('containers', []) if container.get('name') == 'app']
if task.get('lastStatus') != 'RUNNING' or task.get('desiredStatus') != 'RUNNING' or len(app) != 1 or app[0].get('imageDigest') != digest:
raise RuntimeError('A live ECS task does not match the acknowledged compatible release')
latest = latest_execution(region, pipeline)
if latest.get('pipelineExecutionId') != execution_id or not matches_release(latest, digest):
Comment thread
mzxchandra marked this conversation as resolved.
raise RuntimeError('Application deployment changed during preflight')
print(json.dumps({'ecsRetired': True, 'expectedImageDigest': digest, 'pipelineExecutionId': execution_id,
'operatorAcknowledgedColumnWritersAndWorkers': True}))


if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--environment', required=True, choices=['production', 'staging'])
parser.add_argument('--region', required=True)
parser.add_argument('--expected-image-digest', required=True)
args = parser.parse_args()
try:
verify(args.environment, args.region, args.expected_image_digest)
except (RuntimeError, ValueError, KeyError, TypeError, subprocess.TimeoutExpired) as error:
print(f'Project column rollout preflight refused: {error}', file=sys.stderr)
sys.exit(1)
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ jobs:
# in place before the new app version deploys (replaces the removed ECS
# migration sidecar)
migrate:
permissions:
contents: read
id-token: write
name: migrate
needs: [checks]
# Explicit need results instead of the implicit success(): a skipped job
Expand All @@ -132,9 +135,16 @@ jobs:
STAGING_DATABASE_URL: ${{ secrets.STAGING_DATABASE_URL }}
MIGRATION_DATABASE_URL: ${{ secrets.MIGRATION_DATABASE_URL }}
STAGING_MIGRATION_DATABASE_URL: ${{ secrets.STAGING_MIGRATION_DATABASE_URL }}
AWS_ROLE_TO_ASSUME: ${{ secrets.AWS_ROLE_TO_ASSUME }}
STAGING_AWS_ROLE_TO_ASSUME: ${{ secrets.STAGING_AWS_ROLE_TO_ASSUME }}
AWS_REGION: ${{ secrets.AWS_REGION }}
STAGING_AWS_REGION: ${{ secrets.STAGING_AWS_REGION }}

# Same ordering for dev (schema push before the dev image lands in ECR)
migrate-dev:
permissions:
contents: read
id-token: write
name: migrate-dev
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
uses: ./.github/workflows/migrate.yml
Expand Down
36 changes: 36 additions & 0 deletions .github/workflows/migrate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ on:
required: false
STAGING_MIGRATION_DATABASE_URL:
required: false
AWS_ROLE_TO_ASSUME:
required: false
STAGING_AWS_ROLE_TO_ASSUME:
required: false
AWS_REGION:
required: false
STAGING_AWS_REGION:
required: false
workflow_dispatch:
inputs:
environment:
Expand All @@ -31,6 +39,7 @@ on:

permissions:
contents: read
id-token: write

jobs:
migrate:
Expand All @@ -51,6 +60,33 @@ jobs:
provider: ${{ vars.CI_PROVIDER }}
node-version: ''

- name: Check Project column expansion and whether enforcement needs rollout evidence
id: project-contract
if: inputs.environment != 'dev'
working-directory: ./packages/db
env:
DATABASE_URL: ${{ inputs.environment == 'production' && secrets.DATABASE_URL || inputs.environment == 'staging' && secrets.STAGING_DATABASE_URL || '' }}
MIGRATION_DATABASE_URL: ${{ inputs.environment == 'production' && secrets.MIGRATION_DATABASE_URL || inputs.environment == 'staging' && secrets.STAGING_MIGRATION_DATABASE_URL || '' }}
run: bun --no-env-file scripts/project-contract-required.ts >> "$GITHUB_OUTPUT"

- name: Configure AWS for Project connector retirement verification
if: steps.project-contract.outputs.required == 'true'
uses: aws-actions/configure-aws-credentials@e7f100cf4c008499ea8adda475de1042d6975c7b
with:
role-to-assume: ${{ inputs.environment == 'production' && secrets.AWS_ROLE_TO_ASSUME || secrets.STAGING_AWS_ROLE_TO_ASSUME }}
aws-region: ${{ inputs.environment == 'production' && secrets.AWS_REGION || secrets.STAGING_AWS_REGION }}

# Acknowledge #8830 only after authority-aware readers/writers deploy and all incompatible
# servers and relevant old Trigger.dev runs drain. The runner commits the column-authority
# switch before backfill, validation, and connector retirement; retries never revert it.
# The preflight independently checks ECS retirement; it does not inspect worker runs.
- name: Require completed authority-aware rollout before Project cutover
if: steps.project-contract.outputs.required == 'true'
env:
ENVIRONMENT: ${{ inputs.environment }}
EXPECTED_IMAGE_DIGEST: ${{ inputs.environment == 'production' && vars.PROJECT_COLUMN_ENFORCEMENT_READY_IMAGE_DIGEST_PRODUCTION || inputs.environment == 'staging' && vars.PROJECT_COLUMN_ENFORCEMENT_READY_IMAGE_DIGEST_STAGING || '' }}
run: python3 .github/scripts/check-project-rollout.py --environment "$ENVIRONMENT" --region "$AWS_REGION" --expected-image-digest "$EXPECTED_IMAGE_DIGEST"
Comment thread
mzxchandra marked this conversation as resolved.

# The expression maps the explicit environment input to exactly one repo
# secret, so the job never holds another environment's database URL. An
# unknown environment resolves to empty and the guard below fails the job.
Expand Down
19 changes: 14 additions & 5 deletions apps/desktop/e2e/fixtures/live-sim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -651,9 +651,16 @@ export class SimDatabase {
}

async close(): Promise<void> {
if (this.userIds.length > 0)
await this.sql`delete from "user" where id in ${this.sql(this.userIds)}`.catch(() => {})
await this.sql.end({ timeout: 5 })
try {
if (this.userIds.length > 0)
await this.sql.begin(async (tx) => {
await tx`delete from workspace where owner_id in ${tx(this.userIds)}`
await tx`delete from project where owner_id in ${tx(this.userIds)}`
await tx`delete from "user" where id in ${tx(this.userIds)}`
})
} finally {
await this.sql.end({ timeout: 5 })
}
}

/** A user with a workspace, a session as the desktop sign-in creates, and one chat per title. */
Expand All @@ -669,8 +676,10 @@ export class SimDatabase {
await tx`insert into "user" (id, name, email, normalized_email, email_verified, created_at, updated_at)
values (${userId}, ${name}, ${email}, ${email}, true, now(), now())`
await tx`insert into user_stats (id, user_id) values (${generateId()}, ${userId})`
await tx`insert into workspace (id, name, owner_id, billed_account_user_id)
values (${workspaceId}, 'Desktop tools E2E', ${userId}, ${userId})`
await tx`insert into project (id, name, owner_id)
values (${workspaceId}, 'E2E fixture project', ${userId})`
await tx`insert into workspace (id, project_id, name, owner_id, billed_account_user_id)
values (${workspaceId}, ${workspaceId}, 'Desktop tools E2E', ${userId}, ${userId})`
await tx`insert into permissions (id, user_id, entity_type, entity_id, permission_type)
values (${generateId()}, ${userId}, 'workspace', ${workspaceId}, 'admin')`
await tx`insert into session (id, token, user_id, user_agent, expires_at, created_at, updated_at)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Redis through the production reconnect route, for the live tail and the replay batch a view
* attaches with after a reload.
*/
import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures'
import { authMock, authMockFns } from '@sim/testing/mocks/auth.mock'
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'

Expand Down Expand Up @@ -72,7 +73,7 @@ describe.runIf(Boolean(redisUrl))('desktop executor binding on a chat stream', (
createdAt: now,
updatedAt: now,
})
await db.insert(workspace).values({
await insertWorkspaceFixture(db, {
id: workspaceId,
name: 'Desktop stream fixture',
ownerId: userId,
Expand Down Expand Up @@ -105,7 +106,7 @@ describe.runIf(Boolean(redisUrl))('desktop executor binding on a chat stream', (
await db.delete(copilotChats).where(eq(copilotChats.id, chatId))
await db.delete(desktopDevices).where(eq(desktopDevices.id, deviceId))
await db.delete(permissions).where(eq(permissions.userId, userId))
await db.delete(workspace).where(eq(workspace.id, workspaceId))
await deleteWorkspaceFixture(db, eq(workspace.id, workspaceId))
await db.delete(user).where(eq(user.id, userId))
await closeRedisConnection()
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures'
/**
* The member sync scheduler's reclaim against real PostgreSQL: a members-mode connector whose
* member lease went stale is put back on the failure ladder, and a connector in any other access
Expand Down Expand Up @@ -51,7 +52,7 @@ describe('member sync reclaim in PostgreSQL', () => {
})

afterAll(async () => {
await db.delete(workspace).where(eq(workspace.id, ids.workspaceId))
await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId))
await db.delete(organization).where(eq(organization.id, ids.organizationId))
await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId]))
await db.$client.end()
Expand Down
3 changes: 2 additions & 1 deletion apps/sim/app/api/v1/knowledge/route.integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import type { Principal } from '@sim/auth/principal'
import { db } from '@sim/db'
import { document, organization, user, workspace } from '@sim/db/schema'
import { deleteWorkspaceFixture } from '@sim/db/testing/workspace-fixtures'
import { authMock, authMockFns, createMockRequest } from '@sim/testing'
import { generateId } from '@sim/utils/id'
import { eq, inArray } from 'drizzle-orm'
Expand Down Expand Up @@ -85,7 +86,7 @@ describe('knowledge-base document totals in PostgreSQL', () => {
})

afterAll(async () => {
await db.delete(workspace).where(eq(workspace.id, ids.workspaceId))
await deleteWorkspaceFixture(db, eq(workspace.id, ids.workspaceId))
await db.delete(organization).where(eq(organization.id, ids.organizationId))
await db.delete(user).where(inArray(user.id, [ids.aliceId, ids.bobId]))
vi.unstubAllGlobals()
Expand Down
28 changes: 19 additions & 9 deletions apps/sim/background/cleanup-soft-deletes.integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,28 @@ async function seedRelease(deletedAt: string | null, sizeBytes: number, patch: n

describe('Retention cleanup of deleted owned bodies', () => {
beforeAll(async () => {
await control`INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at)
VALUES (${userId}, 'Test cleanup fixture', ${`${userId}@example.test`}, true, now(), now())`
await control`INSERT INTO user_stats (id, user_id, storage_used_bytes)
VALUES (${generateId()}, ${userId}, 100)`
await control`INSERT INTO workspace (id, name, owner_id, billed_account_user_id, storage_used_bytes)
VALUES (${workspaceId}, 'Test cleanup fixtures', ${userId}, ${userId}, 100)`
await control.begin(async (tx) => {
await tx`INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at)
VALUES (${userId}, 'Test cleanup fixture', ${`${userId}@example.test`}, true, now(), now())`
await tx`INSERT INTO user_stats (id, user_id, storage_used_bytes)
VALUES (${generateId()}, ${userId}, 100)`
await tx`INSERT INTO project (id, name, owner_id)
VALUES (${workspaceId}, 'Test cleanup project', ${userId})`
await tx`INSERT INTO workspace (id, project_id, name, owner_id, billed_account_user_id, storage_used_bytes)
VALUES (${workspaceId}, ${workspaceId}, 'Test cleanup fixtures', ${userId}, ${userId}, 100)`
})
})

afterAll(async () => {
await control`DELETE FROM workspace WHERE id = ${workspaceId}`
await control`DELETE FROM "user" WHERE id = ${userId}`
await control.end()
try {
await control.begin(async (tx) => {
await tx`DELETE FROM workspace WHERE id = ${workspaceId}`
await tx`DELETE FROM project WHERE id = ${workspaceId} AND owner_id = ${userId}`
await tx`DELETE FROM "user" WHERE id = ${userId}`
})
} finally {
await control.end()
}
})

it('purges an expired test with its source and releases the source bytes', async () => {
Expand Down
12 changes: 9 additions & 3 deletions apps/sim/background/cleanup-table-row-ttl.integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,11 @@ describe.skipIf(!migrated)('Expiration with real PostgreSQL transactions', () =>
beforeAll(async () => {
await control`INSERT INTO "user" (id, name, email, email_verified, created_at, updated_at)
VALUES (${userId}, 'Expiration integration fixture', ${`${userId}@example.test`}, true, now(), now())`
await control`INSERT INTO workspace (id, name, owner_id, billed_account_user_id)
VALUES (${workspaceId}, 'Expiration integration fixtures', ${userId}, ${userId})`
await control.begin(async (tx) => {
await tx`INSERT INTO project (id, name, owner_id) VALUES (${workspaceId}, 'Fixture project', ${userId})`
await tx`INSERT INTO workspace (id, project_id, name, owner_id, billed_account_user_id)
VALUES (${workspaceId}, ${workspaceId}, 'Expiration integration fixtures', ${userId}, ${userId})`
})
})

beforeEach(async () => {
Expand All @@ -124,7 +127,10 @@ describe.skipIf(!migrated)('Expiration with real PostgreSQL transactions', () =>
})

afterAll(async () => {
await control`DELETE FROM workspace WHERE id = ${workspaceId}`
await control.begin(async (tx) => {
await tx`DELETE FROM workspace WHERE id = ${workspaceId}`
await tx`DELETE FROM project WHERE id = ${workspaceId}`
})
await control`DELETE FROM "user" WHERE id = ${userId}`
writeFileSync(
join(tmpdir(), 'expiration-qa-measurements.json'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
user,
workspace,
} from '@sim/db/schema'
import { deleteWorkspaceFixture, insertWorkspaceFixture } from '@sim/db/testing/workspace-fixtures'
import { envFlagsMock } from '@sim/testing/mocks/env-flags.mock'
import { generateId } from '@sim/utils/id'
import { and, eq, inArray } from 'drizzle-orm'
Expand Down Expand Up @@ -92,7 +93,8 @@ describe('chat-delegated workspace access requests', () => {
createdAt: now,
}))
)
await db.insert(workspace).values(
await insertWorkspaceFixture(
db,
[workspaceId, otherWorkspaceId].map((id) => ({
id,
name: 'Access request fixture',
Expand Down Expand Up @@ -138,7 +140,7 @@ describe('chat-delegated workspace access requests', () => {
.delete(permissionAccessRequest)
.where(eq(permissionAccessRequest.organizationId, organizationId))
await db.delete(auditLog).where(eq(auditLog.workspaceId, workspaceId))
await db.delete(workspace).where(inArray(workspace.id, [workspaceId, otherWorkspaceId]))
await deleteWorkspaceFixture(db, inArray(workspace.id, [workspaceId, otherWorkspaceId]))
await db.delete(organization).where(eq(organization.id, organizationId))
await db.delete(user).where(inArray(user.id, [requesterId, peerId]))
})
Expand Down
Loading
Loading