diff --git a/.dev.vars.example b/.dev.vars.example index f6ad225..1e05d90 100644 --- a/.dev.vars.example +++ b/.dev.vars.example @@ -1,52 +1,29 @@ -# ────────────────────────────────────────────────────────────────────────────── -# Codra Environment Configuration Example -# Copy this file to .dev.vars for local development: cp .dev.vars.example .dev.vars -# ────────────────────────────────────────────────────────────────────────────── +# Codra local development environment example +# Copy this file to .dev.vars for local development. +# Keep real secrets only in .dev.vars or your deployment secret store. -# --- GitHub App Authentication --- -# Create at: https://github.com/settings/apps -APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nREPLACE_WITH_YOUR_GITHUB_APP_PRIVATE_KEY_CONTENT\n-----END RSA PRIVATE KEY-----" -GITHUB_APP_ID="REPLACE_WITH_YOUR_APP_ID" -GITHUB_APP_SLUG="REPLACE_WITH_YOUR_APP_SLUG" -GITHUB_APP_WEBHOOK_SECRET="REPLACE_WITH_YOUR_WEBHOOK_SECRET" - -# --- Dashboard OAuth (GitHub) --- -# Use the same GitHub App's Client ID/Secret or a separate OAuth App -GITHUB_CLIENT_ID="REPLACE_WITH_YOUR_CLIENT_ID" -GITHUB_CLIENT_SECRET="REPLACE_WITH_YOUR_CLIENT_SECRET" -AUTH_CALLBACK_URL="http://localhost:8787/auth/github/callback" - -# --- Authorization --- -# Comma-separated list of GitHub usernames allowed to access the dashboard -DASHBOARD_ALLOWED_USERS="username1,username2" +# --- Integration tests --- +TEST_DATABASE_URL="postgresql://user:password@localhost:5432/codra" -# --- AI Intelligence (Gemini) --- -# Generate at: https://aistudio.google.com/app/apikey +# --- AI provider --- GEMINI_API_KEY="REPLACE_WITH_YOUR_GEMINI_API_KEY" -# --- Database Connections --- - -# 1. Local Development (Used by 'wrangler dev' for the HYPERDRIVE binding) -# This usually points to a local Postgres instance or a dev branch in Neon. -CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE="postgresql://user:password@localhost:5432/codra_dev" - -# 2. Migrations (Used by 'npm run migrate') -# This script runs via Node.js and needs a direct connection to the DB you want to migrate. -DATABASE_URL="postgresql://user:password@localhost:5432/codra_dev" - -# 3. Integration Tests (Used by 'npm run test') -# MUST be a separate database to avoid data loss during test sweeps. -TEST_DATABASE_URL="postgresql://user:password@localhost:5432/codra_test" +# --- GitHub App and OAuth --- +GITHUB_APP_WEBHOOK_SECRET="REPLACE_WITH_YOUR_WEBHOOK_SECRET" +GITHUB_APP_ID="REPLACE_WITH_YOUR_APP_ID" +GITHUB_CLIENT_ID="REPLACE_WITH_YOUR_CLIENT_ID" +GITHUB_CLIENT_SECRET="REPLACE_WITH_YOUR_CLIENT_SECRET" +APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nREPLACE_WITH_YOUR_GITHUB_APP_PRIVATE_KEY_CONTENT\n-----END RSA PRIVATE KEY-----" -# --- Cloudflare DLQ / Queue Management (Required) --- -# Required for DLQ inspection, replay, and purge via /api/dlq -# Create or identify the DLQ queue, then set CF_DLQ_ID to that queue's ID. -# Generate token at https://dash.cloudflare.com/profile/api-tokens (Queues:Edit permission) -CF_API_TOKEN="REPLACE_WITH_CLOUDFLARE_API_TOKEN" +# --- Cloudflare API --- CF_ACCOUNT_ID="REPLACE_WITH_YOUR_CLOUDFLARE_ACCOUNT_ID" -CF_DLQ_ID="REPLACE_WITH_YOUR_DLQ_QUEUE_ID" +CF_API_TOKEN="REPLACE_WITH_CLOUDFLARE_API_TOKEN" -# --- Application Settings --- +# --- Application URLs and mode --- APP_URL="http://localhost:8787" -BOT_USERNAME="codra-app-dev" +AUTH_CALLBACK_URL="http://localhost:8787/auth/github/callback" ENVIRONMENT="development" + +# --- Database connections --- +DATABASE_URL="postgresql://user:password@localhost:5432/codra_dev" +CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE="postgresql://user:password@localhost:5432/codra_dev" diff --git a/.env.test.example b/.env.test.example new file mode 100644 index 0000000..31e1b2e --- /dev/null +++ b/.env.test.example @@ -0,0 +1,19 @@ +# Codra test environment example. +# Copy to .env.test for local tests. These values are fake and must not be +# reused for production, staging, or any real external service. + +GITHUB_APP_SLUG="codra-test-app" +GITHUB_APP_WEBHOOK_SECRET="fake-webhook-secret" + +GITHUB_CLIENT_ID="fake-dashboard-client-id" +GITHUB_CLIENT_SECRET="fake-dashboard-client-secret" +AUTH_CALLBACK_URL="https://codra.test/auth/github/callback" +DASHBOARD_ALLOWED_USERS="devarshishimpi" + +APP_URL="https://codra.test" +BOT_USERNAME="codra-test-app" + +# Required. Must point at a disposable Postgres database because tests reset and +# write data while running. +DATABASE_URL="postgresql://postgres:postgres@127.0.0.1:5432/codra_test" +TEST_DATABASE_URL="postgresql://postgres:postgres@127.0.0.1:5432/codra_test" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 632bf80..526bc02 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,10 +1,16 @@ name: Code Quality on: + workflow_dispatch: push: branches: - main pull_request: + types: + - opened + - synchronize + - reopened + - ready_for_review branches: - main @@ -16,6 +22,31 @@ jobs: verify: name: Verify Stability runs-on: ubuntu-latest + services: + postgres: + image: postgres:16 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: codra_test + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + env: + DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/codra_test + TEST_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/codra_test + GITHUB_APP_SLUG: codra-test-app + GITHUB_APP_WEBHOOK_SECRET: fake-webhook-secret + GITHUB_CLIENT_ID: fake-dashboard-client-id + GITHUB_CLIENT_SECRET: fake-dashboard-client-secret + AUTH_CALLBACK_URL: https://codra.test/auth/github/callback + APP_URL: https://codra.test + DASHBOARD_ALLOWED_USERS: devarshishimpi + BOT_USERNAME: codra-test-app steps: - name: Checkout repository diff --git a/.gitignore b/.gitignore index 9c61da5..e6c2e3a 100644 --- a/.gitignore +++ b/.gitignore @@ -69,6 +69,7 @@ web_modules/ .env .env.* !.env.example +!.env.test.example # parcel-bundler cache (https://parceljs.org/) .cache diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 564345b..6a0d1c0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,10 +52,12 @@ npm run dev ## 🧪 Testing -We use **Vitest** for unit and integration testing. `npm test` runs the non-database tests by default and automatically enables DB integration tests when `TEST_DATABASE_URL` points at a disposable Postgres database. +We use **Vitest** for unit and integration testing. `npm test` requires a disposable Postgres database, runs migrations against it, and then runs the full test suite. + +The test runner loads `.env.test`, `.env.local`, `.env`, `.dev.vars`, and then `.env.test.example`. Override `TEST_DATABASE_URL` in one of the private env files when your local test database does not match the example URL. ```bash -# Run all tests +# Run the full test suite npm test # Run tests in watch mode diff --git a/package.json b/package.json index d8db3c7..0f41312 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "dev:worker": "wrangler dev --local", "start": "npm run dev", "migrate": "node scripts/migrate.mjs", - "test": "vitest run", + "test": "node scripts/test.mjs", "test:watch": "vitest", "typecheck": "tsc --noEmit" }, diff --git a/scripts/test.mjs b/scripts/test.mjs new file mode 100644 index 0000000..0eeed33 --- /dev/null +++ b/scripts/test.mjs @@ -0,0 +1,77 @@ +import { spawnSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const envFiles = ['.env.test', '.env.local', '.env', '.dev.vars', '.env.test.example']; + +function parseEnvValue(value) { + let trimmed = value.trim(); + if ( + (trimmed.startsWith('"') && trimmed.endsWith('"')) || + (trimmed.startsWith("'") && trimmed.endsWith("'")) + ) { + trimmed = trimmed.slice(1, -1); + } + + return trimmed.replace(/\\n/g, '\n'); +} + +function usableEnvValue(value) { + return value && value !== 'undefined' && value !== 'null' ? value : null; +} + +function loadEnvFiles() { + for (const file of envFiles) { + try { + const content = readFileSync(path.join(rootDir, file), 'utf8'); + for (const line of content.split(/\r?\n/)) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('#')) continue; + + const separatorIndex = trimmed.indexOf('='); + if (separatorIndex === -1) continue; + + const key = trimmed.slice(0, separatorIndex).trim(); + if (process.env[key] === undefined) { + process.env[key] = parseEnvValue(trimmed.slice(separatorIndex + 1)); + } + } + } catch (error) { + if (error?.code !== 'ENOENT') { + throw error; + } + } + } +} + +function run(command, args) { + const result = spawnSync(command, args, { + cwd: rootDir, + env: process.env, + stdio: 'inherit', + }); + + if (result.error) { + throw result.error; + } + if (result.status !== 0) { + process.exit(result.status ?? 1); + } +} + +loadEnvFiles(); + +if (!usableEnvValue(process.env.TEST_DATABASE_URL)) { + console.error([ + 'TEST_DATABASE_URL is required to run the full test suite.', + 'Copy .env.test.example to .env.test and point TEST_DATABASE_URL at a disposable Postgres database.', + ].join('\n')); + process.exit(1); +} + +process.env.DATABASE_URL = usableEnvValue(process.env.DATABASE_URL) ?? process.env.TEST_DATABASE_URL; + +run(process.execPath, ['scripts/migrate.mjs']); +run(process.execPath, ['node_modules/vitest/vitest.mjs', 'run']); diff --git a/test/api.spec.ts b/test/api.spec.ts index 8d9ed3c..1b0ccd5 100644 --- a/test/api.spec.ts +++ b/test/api.spec.ts @@ -14,11 +14,9 @@ import type { StatsResponse, UpdatesEmailResponse, } from '@shared/api'; -import { createTestEnv, hasConfiguredTestDatabaseUrl } from './helpers'; +import { createTestEnv } from './helpers'; import { vi } from 'vitest'; -const dbIt = hasConfiguredTestDatabaseUrl() ? it : it.skip; - function mockGitHubProfile(login = 'devarshishimpi') { return { id: 42, @@ -117,7 +115,7 @@ describe('Dashboard API Suite', () => { expect(response.headers.get('location')).toBe('/login?error=not_allowed'); }); - dbIt('allows access to /api/jobs with a valid GitHub session', async () => { + it('allows access to /api/jobs with a valid GitHub session', async () => { const env = createTestEnv(); const token = await getAuthCookie(env); const response = await app.request('/api/jobs', { @@ -264,7 +262,7 @@ describe('Dashboard API Suite', () => { expect(response.status).toBe(404); }); - dbIt('fetches job details accurately', async () => { + it('fetches job details accurately', async () => { const env = createTestEnv(); const token = await getAuthCookie(env); @@ -295,7 +293,7 @@ describe('Dashboard API Suite', () => { expect(data.job.files).toBeDefined(); }); - dbIt('fetches job details when stored comments have null code suggestions', async () => { + it('fetches job details when stored comments have null code suggestions', async () => { const env = createTestEnv(); const token = await getAuthCookie(env); @@ -349,7 +347,7 @@ describe('Dashboard API Suite', () => { expect(data.job.files[0].parsedComments[0].codeSuggestion).toBeNull(); }); - dbIt('returns stats successfully', async () => { + it('returns stats successfully', async () => { const env = createTestEnv(); const token = await getAuthCookie(env); @@ -428,7 +426,7 @@ describe('Dashboard API Suite', () => { expect(response.status).toBe(400); }); - dbIt('returns repository list', async () => { + it('returns repository list', async () => { const env = createTestEnv(); const token = await getAuthCookie(env); @@ -453,7 +451,7 @@ describe('Dashboard API Suite', () => { expect(response.headers.get('location')).toBe('https://github.com/apps/my-codra-install/installations/new'); }); - dbIt('rejects invalid repository config patches', async () => { + it('rejects invalid repository config patches', async () => { const env = createTestEnv(); const token = await getAuthCookie(env); const repo = `invalid-config-${Date.now()}`; @@ -481,7 +479,7 @@ describe('Dashboard API Suite', () => { expect(response.status).toBe(400); }); - dbIt('rejects string booleans in repository config patches', async () => { + it('rejects string booleans in repository config patches', async () => { const env = createTestEnv(); const token = await getAuthCookie(env); const repo = `invalid-enabled-${Date.now()}`; @@ -530,7 +528,7 @@ describe('Dashboard API Suite', () => { expect(requestedUrl).toBe('https://api.github.com/repos/owner/repo/contents/src/path%20with%20spaces/app.ts'); }); - dbIt('keeps repo model settings inherited when loading global strategy', async () => { + it('keeps repo model settings inherited when loading global strategy', async () => { const env = createTestEnv(); const repo = `global-inherit-${Date.now()}`; diff --git a/test/helpers.ts b/test/helpers.ts index a698918..251b8b7 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -54,33 +54,24 @@ export class MockQueue { } } -// A valid PKCS#8 dummy private key (2048-bit RSA) -export const DUMMY_PRIVATE_KEY = `-----BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCuG/W/29qB8S3q -/U4+4M1v8XJ/U0zZ5y8/Y+Y/W9J/4M1v8XJ/U0zZ5y8/Y+Y/W9J/4M1v8XJ/U0zZ -5y8/Y+Y/W9J/4M1v8XJ/U0zZ5y8/Y+Y/W9J/4M1v8XJ/U0zZ5y8/Y+Y/W9J/4M1v -8XJ/U0zZ5y8/Y+Y/W9J/4M1v8XJ/U0zZ5y8/Y+Y/W9J/4M1v8XJ/U0zZ5y8/Y+Y/ -W9J/4M1v8XJ/U0zZ5y8/Y+Y/W9J/4M1v8XJ/U0zZ5y8/Y+Y/W9J/4M1v8XJ/U0zZ -5y8/Y+Y/W9J/4M1v8XJ/U0zZ5y8/Y+Y/W9J/4M1v8XJ/U0zZ5y8/Y+Y/W9J/4M1v -8XJ/U0zZ5y8/Y+Y/W9J/4M1v8XJ/U0zZ5y8/Y+Y/W9J/4M1v8XJ/U0zZ5y8/Y+Y/ -W9J/AgMBAAECggEAIl77HjE= ------END PRIVATE KEY-----`; - -export const TEST_DATABASE_URL = 'postgresql://postgres:postgres@127.0.0.1:5432/codra_test'; - function usableEnvValue(value: string | undefined) { return value && value !== 'undefined' && value !== 'null' ? value : null; } -export function getTestDatabaseUrl() { - return ( - usableEnvValue(process.env.TEST_DATABASE_URL) ?? - TEST_DATABASE_URL - ); +function requiredEnv(key: keyof NodeJS.ProcessEnv) { + const value = usableEnvValue(process.env[key]); + if (!value) { + throw new Error(`Missing required test environment variable: ${key}`); + } + return value; } -export function hasConfiguredTestDatabaseUrl() { - return Boolean(usableEnvValue(process.env.TEST_DATABASE_URL)); +function unusedEnv(key: string): string { + throw new Error(`${key} is not required by the current test suite. Add it to the test env only when a test exercises that path.`); +} + +export function getTestDatabaseUrl() { + return requiredEnv('TEST_DATABASE_URL'); } export function createTestEnv(overrides: Partial = {}): AppBindings { @@ -96,21 +87,21 @@ export function createTestEnv(overrides: Partial = {}): AppBindings HYPERDRIVE: { connectionString: getTestDatabaseUrl(), }, - APP_PRIVATE_KEY: DUMMY_PRIVATE_KEY, - GITHUB_APP_ID: '123', - GITHUB_APP_SLUG: 'codra-app', - GITHUB_APP_WEBHOOK_SECRET: 'topsecret', - GITHUB_CLIENT_ID: 'dashboard-client-id', - GITHUB_CLIENT_SECRET: 'dashboard-client-secret', - AUTH_CALLBACK_URL: 'https://codra.test/auth/github/callback', - APP_URL: 'https://codra.test', - DASHBOARD_ALLOWED_USERS: 'devarshishimpi', - GEMINI_API_KEY: 'gemini-key', - BOT_USERNAME: 'codra-app', - ENVIRONMENT: 'test', - CF_API_TOKEN: 'cf-api-token', - CF_ACCOUNT_ID: 'cf-account-id', - CF_DLQ_ID: 'cf-dlq-id', + get APP_PRIVATE_KEY() { return unusedEnv('APP_PRIVATE_KEY'); }, + get GITHUB_APP_ID() { return unusedEnv('GITHUB_APP_ID'); }, + GITHUB_APP_SLUG: requiredEnv('GITHUB_APP_SLUG'), + GITHUB_APP_WEBHOOK_SECRET: requiredEnv('GITHUB_APP_WEBHOOK_SECRET'), + GITHUB_CLIENT_ID: requiredEnv('GITHUB_CLIENT_ID'), + GITHUB_CLIENT_SECRET: requiredEnv('GITHUB_CLIENT_SECRET'), + AUTH_CALLBACK_URL: requiredEnv('AUTH_CALLBACK_URL'), + APP_URL: requiredEnv('APP_URL'), + DASHBOARD_ALLOWED_USERS: requiredEnv('DASHBOARD_ALLOWED_USERS'), + get GEMINI_API_KEY() { return unusedEnv('GEMINI_API_KEY'); }, + BOT_USERNAME: requiredEnv('BOT_USERNAME'), + get ENVIRONMENT() { return unusedEnv('ENVIRONMENT'); }, + get CF_API_TOKEN() { return unusedEnv('CF_API_TOKEN'); }, + get CF_ACCOUNT_ID() { return unusedEnv('CF_ACCOUNT_ID'); }, + get CF_DLQ_ID() { return unusedEnv('CF_DLQ_ID'); }, ...overrides, }; } diff --git a/test/review-flow.spec.ts b/test/review-flow.spec.ts index 76e5579..e022d3b 100644 --- a/test/review-flow.spec.ts +++ b/test/review-flow.spec.ts @@ -1,5 +1,5 @@ import { runReviewJob } from '@server/core/review'; -import { createTestEnv, generateMockDiff, hasConfiguredTestDatabaseUrl } from './helpers'; +import { createTestEnv, generateMockDiff } from './helpers'; import { vi } from 'vitest'; import { findExistingJobForHead, getJobForProcessing, insertJob } from '@server/db/jobs'; import { defaultRepoConfig } from '@shared/schema'; @@ -68,9 +68,7 @@ vi.mock('@server/services/model', () => { return { ModelService: MockModelService }; }); -const dbDescribe = hasConfiguredTestDatabaseUrl() ? describe : describe.skip; - -dbDescribe('Review Flow Lifecycle', () => { +describe('Review Flow Lifecycle', () => { const env = createTestEnv(); it('completes a full review from pending job to finished', async () => { diff --git a/test/setup.ts b/test/setup.ts index 89ec1f4..372ce52 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -2,6 +2,19 @@ import { vi } from 'vitest'; import { readFileSync } from 'node:fs'; import path from 'node:path'; +const TEST_ENV_FILES = ['.env.test', '.env.local', '.env', '.dev.vars', '.env.test.example']; +const REQUIRED_TEST_ENV_KEYS = [ + 'GITHUB_APP_SLUG', + 'GITHUB_APP_WEBHOOK_SECRET', + 'GITHUB_CLIENT_ID', + 'GITHUB_CLIENT_SECRET', + 'AUTH_CALLBACK_URL', + 'APP_URL', + 'DASHBOARD_ALLOWED_USERS', + 'BOT_USERNAME', + 'TEST_DATABASE_URL', +]; + // Global mocks for Cloudflare environment vi.stubGlobal('QUEUE', { send: async (msg: any) => { @@ -10,19 +23,25 @@ vi.stubGlobal('QUEUE', { }); function parseEnvValue(value: string) { - const trimmed = value.trim(); + let trimmed = value.trim(); if ( (trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'")) ) { - return trimmed.slice(1, -1); + trimmed = trimmed.slice(1, -1); } - return trimmed; + return trimmed.replace(/\\n/g, '\n'); +} + +function usableEnvValue(value: string | undefined) { + return value && value !== 'undefined' && value !== 'null' ? value : null; } -function readTestDatabaseUrlFromEnvFiles() { - for (const file of ['.env.test', '.env.local', '.env', '.dev.vars']) { +function loadTestEnvFromFiles() { + const keys = new Set(REQUIRED_TEST_ENV_KEYS); + + for (const file of TEST_ENV_FILES) { try { const content = readFileSync(path.join(process.cwd(), file), 'utf8'); for (const line of content.split(/\r?\n/)) { @@ -33,8 +52,8 @@ function readTestDatabaseUrlFromEnvFiles() { if (separatorIndex === -1) continue; const key = trimmed.slice(0, separatorIndex).trim(); - if (key === 'TEST_DATABASE_URL') { - return parseEnvValue(trimmed.slice(separatorIndex + 1)); + if (keys.has(key) && process.env[key] === undefined) { + process.env[key] = parseEnvValue(trimmed.slice(separatorIndex + 1)); } } } catch (error) { @@ -43,19 +62,24 @@ function readTestDatabaseUrlFromEnvFiles() { } } } - - return null; } -// Postgres database URL for integration tests. Set TEST_DATABASE_URL or put -// TEST_DATABASE_URL in .env.test/.env.local/.env/.dev.vars. -const configuredDatabaseUrl = process.env.TEST_DATABASE_URL || readTestDatabaseUrlFromEnvFiles(); -if (configuredDatabaseUrl && configuredDatabaseUrl !== 'undefined' && configuredDatabaseUrl !== 'null') { - process.env.TEST_DATABASE_URL = configuredDatabaseUrl; +function assertRequiredTestEnv() { + const missing = REQUIRED_TEST_ENV_KEYS.filter((key) => !usableEnvValue(process.env[key])); + if (missing.length === 0) return; + + throw new Error([ + `Missing required test environment variables: ${missing.join(', ')}.`, + 'Set these values in .env.test, .env.local, .env, .dev.vars, .env.test.example, or CI.', + 'TEST_DATABASE_URL must point to a disposable Postgres database so the full test suite can run.', + ].join('\n')); } -// Standard test timeout -vi.setConfig({ testTimeout: 20000 }); +loadTestEnvFromFiles(); +assertRequiredTestEnv(); + +// Database-backed review flow tests can be slow on local Postgres and CI. +vi.setConfig({ testTimeout: 300000 }); if (typeof window !== 'undefined' && !window.matchMedia) { Object.defineProperty(window, 'matchMedia', { diff --git a/test/webhook-handling.spec.ts b/test/webhook-handling.spec.ts index cc425c8..86c86c9 100644 --- a/test/webhook-handling.spec.ts +++ b/test/webhook-handling.spec.ts @@ -1,9 +1,7 @@ import { createApp } from '@server/app'; -import { createMockPRWebhook, createTestEnv, hasConfiguredTestDatabaseUrl } from './helpers'; +import { createMockPRWebhook, createTestEnv } from './helpers'; import { vi } from 'vitest'; -const dbIt = hasConfiguredTestDatabaseUrl() ? it : it.skip; - // Mock GitHubClient to avoid real JWT signing and network calls vi.mock('@server/core/github', async (importOriginal) => { const actual = await importOriginal() as any; @@ -80,7 +78,7 @@ describe('Webhook Handling Suite', () => { expect(response.status).toBe(400); }); - dbIt('accepts valid pull_request.opened and queues a job', async () => { + it('accepts valid pull_request.opened and queues a job', async () => { const repoName = `repo-${Date.now()}`; const rawPayload = createMockPRWebhook({ action: 'opened', @@ -120,7 +118,7 @@ describe('Webhook Handling Suite', () => { expect(queue.sent[0].payload).toBeUndefined(); }); - dbIt('acknowledges unsupported GitHub events without queueing review work', async () => { + it('acknowledges unsupported GitHub events without queueing review work', async () => { const rawPayload = createMockPRWebhook({ action: 'opened', repository: { name: `repo-${Date.now()}-check-suite`, owner: { login: 'test-owner' } }, @@ -153,7 +151,7 @@ describe('Webhook Handling Suite', () => { expect(queue.sent).toHaveLength(0); }); - dbIt('ignores webhooks for draft PRs', async () => { + it('ignores webhooks for draft PRs', async () => { const draftPayload = createMockPRWebhook({ action: 'opened', pull_request: { draft: true, number: 99, head: { sha: 'abc' }, base: { sha: 'def' }, user: { login: 'a' } }