mirror of
https://github.com/yusufipk/OpenFrame.git
synced 2026-09-11 09:36:08 +00:00
test: add unit, API, component and end-to-end test suites
The repo had no automated tests. Every change was verified by hand. Adds four layers, 2023 tests in total, runnable with one command: - 1191 unit tests over the pure logic in lib/, including the full computeProjectAccess permission matrix and the billing gate - 167 component and hook tests in jsdom, covering the hooks that hold real logic rather than presentational wrappers - 647 API integration tests against a real Postgres, with only auth() mocked, including a data-driven sweep asserting that none of the 60 route modules answers 2xx to an unauthenticated caller - 18 Playwright specs driving a real browser against a real build Infrastructure: vitest.config.ts with three projects, a disposable Postgres and MinIO in docker-compose.test.yml, factories and helpers under tests/, scripts/test.sh as the single entry point, a pre-push hook running bun run verify, and CI split into check, test and e2e jobs. The test database is built with prisma db push plus a replay of the hand-written SQL, because prisma migrate deploy cannot build this schema from empty: the migration history has no captured baseline. This mirrors what scripts/docker-db-bootstrap.ts already does in production, and tests/setup/db-global.ts carries a drift guard so a new migration fails the run until someone reviews it. Production code is unchanged apart from one pure-function extraction out of use-video-player.ts, which was too large to test in jsdom. Several tests pin behaviour that looks wrong, each marked KNOWN BUG in place. TESTING.md section 12 records where the plan turned out to be wrong, and AGENTS.md now states which layer a change needs a test in.
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
# Disposable infrastructure for the automated test suites. Everything here is
|
||||
# on non-default ports so it can never collide with the dev stack in
|
||||
# docker-compose.yml.
|
||||
#
|
||||
# The `openframe-test` network is declared external so that the test runner
|
||||
# container can reach these services by service name. Create it once with:
|
||||
# podman network create openframe-test
|
||||
#
|
||||
# Two tiers, split by compose profile:
|
||||
#
|
||||
# (no profile) postgres-test. Needed by the `api` Vitest project and by the
|
||||
# Playwright suite. Started by `bun run test:db:up`.
|
||||
# e2e minio-test + minio-test-init (real S3-compatible storage for
|
||||
# the direct video upload flow) and app-test (the app under
|
||||
# test). Started by `scripts/test.sh e2e`.
|
||||
#
|
||||
# Usage:
|
||||
# podman compose -f docker-compose.test.yml up -d --wait postgres-test
|
||||
# podman run --rm --network openframe-test -v "$PWD":/workspace:z -w /workspace \
|
||||
# docker.io/oven/bun:alpine sh -c "bun run test:api"
|
||||
#
|
||||
# podman compose -f docker-compose.test.yml --profile e2e up -d --wait \
|
||||
# postgres-test minio-test
|
||||
# scripts/test.sh e2e
|
||||
#
|
||||
# app-test is opt-in and only needed when you want the app under test to run as
|
||||
# a container instead of being started by Playwright's own `webServer`:
|
||||
# podman compose -f docker-compose.test.yml --profile e2e up -d --wait app-test
|
||||
# E2E_BASE_URL=http://app-test:3100 scripts/test.sh e2e
|
||||
|
||||
services:
|
||||
postgres-test:
|
||||
image: postgres:16-alpine
|
||||
container_name: openframe-postgres-test
|
||||
environment:
|
||||
POSTGRES_USER: openframe
|
||||
POSTGRES_PASSWORD: openframe
|
||||
POSTGRES_DB: openframe_test
|
||||
# fsync off: the data directory is a tmpfs that is thrown away anyway, so
|
||||
# durability buys nothing and costs a lot of wall clock time.
|
||||
command: ['postgres', '-c', 'fsync=off', '-c', 'full_page_writes=off']
|
||||
tmpfs:
|
||||
- /var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U openframe -d openframe_test']
|
||||
interval: 2s
|
||||
timeout: 3s
|
||||
retries: 30
|
||||
ports:
|
||||
- '127.0.0.1:55432:5432'
|
||||
networks:
|
||||
- openframe-test
|
||||
|
||||
# S3-compatible object storage for the direct video upload path. The browser
|
||||
# PUTs the file straight at this endpoint with a presigned URL, so the
|
||||
# hostname the app signs for has to be the hostname the browser resolves:
|
||||
# both the Playwright container and the app under test are on this network,
|
||||
# so both use `minio-test:9000`. MinIO's default CORS policy allows every
|
||||
# origin and exposes ETag, which is exactly what lib/client/r2-video-upload.ts
|
||||
# needs, so no bucket CORS configuration is involved.
|
||||
minio-test:
|
||||
profiles: ['e2e']
|
||||
image: docker.io/minio/minio:latest
|
||||
container_name: openframe-minio-test
|
||||
command: ['server', '/data', '--console-address', ':9001']
|
||||
environment:
|
||||
MINIO_ROOT_USER: openframe
|
||||
MINIO_ROOT_PASSWORD: openframe-test-secret
|
||||
# lib/r2.ts builds its S3 client with `region: 'auto'`, so MinIO has to
|
||||
# accept that region name in the SigV4 signature.
|
||||
MINIO_REGION_NAME: auto
|
||||
# Same reasoning as postgres-test: the data is disposable, so keep it in RAM.
|
||||
tmpfs:
|
||||
- /data
|
||||
healthcheck:
|
||||
test: ['CMD', 'mc', 'ready', 'local']
|
||||
interval: 2s
|
||||
timeout: 3s
|
||||
retries: 30
|
||||
ports:
|
||||
- '127.0.0.1:59000:9000'
|
||||
- '127.0.0.1:59001:9001'
|
||||
networks:
|
||||
- openframe-test
|
||||
|
||||
# Creates the bucket. Nothing at runtime does: ensureR2BucketExists() lives in
|
||||
# scripts/self-host-bootstrap.ts, not on the request path, so a missing bucket
|
||||
# would surface as a presigned PUT returning NoSuchBucket.
|
||||
minio-test-init:
|
||||
profiles: ['e2e']
|
||||
image: docker.io/minio/mc:latest
|
||||
container_name: openframe-minio-test-init
|
||||
depends_on:
|
||||
minio-test:
|
||||
condition: service_healthy
|
||||
entrypoint:
|
||||
- sh
|
||||
- -c
|
||||
- >
|
||||
mc alias set testminio http://minio-test:9000 openframe openframe-test-secret &&
|
||||
mc mb --ignore-existing testminio/openframe-test &&
|
||||
mc ls testminio
|
||||
networks:
|
||||
- openframe-test
|
||||
|
||||
# The app under test, for the case where you do not want Playwright to start
|
||||
# it. This is the same command playwright.config.ts uses for its `webServer`,
|
||||
# run against the mounted working tree so `.next` stays warm between runs.
|
||||
#
|
||||
# NODE_ENV is left to `next build` / `next start` (production). The runtime
|
||||
# values below are duplicated from .env.test.example on purpose: `next build`
|
||||
# never loads `.env.test`, and NEXT_PUBLIC_APP_URL is inlined at build time.
|
||||
app-test:
|
||||
profiles: ['e2e']
|
||||
image: docker.io/oven/bun:alpine
|
||||
container_name: openframe-app-test
|
||||
depends_on:
|
||||
postgres-test:
|
||||
condition: service_healthy
|
||||
minio-test:
|
||||
condition: service_healthy
|
||||
working_dir: /workspace
|
||||
volumes:
|
||||
- .:/workspace:z
|
||||
environment:
|
||||
DATABASE_URL: postgresql://openframe:openframe@postgres-test:5432/openframe_test?schema=public
|
||||
NEXTAUTH_URL: http://app-test:3100
|
||||
NEXTAUTH_SECRET: test-secret-not-used-for-anything-real
|
||||
NEXT_PUBLIC_APP_URL: http://app-test:3100
|
||||
OPENFRAME_ENABLE_STRIPE: 'true'
|
||||
STRIPE_SECRET_KEY: sk_test_openframe_dummy
|
||||
STRIPE_PRICE_ID: price_test_openframe_dummy
|
||||
OPENFRAME_REQUIRE_INVITE_CODE: 'true'
|
||||
INVITE_CODE: test-invite
|
||||
TRUSTED_PROXY_MODE: none
|
||||
OPENFRAME_ENABLE_S3_VIDEO_UPLOADS: 'true'
|
||||
OPENFRAME_ENABLE_BUNNY_UPLOADS: 'false'
|
||||
R2_ENDPOINT: http://minio-test:9000
|
||||
R2_ACCESS_KEY_ID: openframe
|
||||
R2_SECRET_ACCESS_KEY: openframe-test-secret
|
||||
R2_BUCKET_NAME: openframe-test
|
||||
PORT: '3100'
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- './node_modules/.bin/next build && ./node_modules/.bin/next start -p 3100 -H 0.0.0.0'
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'wget -q -O /dev/null http://127.0.0.1:3100/login']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 120
|
||||
ports:
|
||||
- '127.0.0.1:3100:3100'
|
||||
networks:
|
||||
- openframe-test
|
||||
|
||||
networks:
|
||||
openframe-test:
|
||||
external: true
|
||||
Reference in New Issue
Block a user