diff --git a/apps/api/.env.test b/apps/api/.env.test new file mode 100644 index 0000000..e71fab2 --- /dev/null +++ b/apps/api/.env.test @@ -0,0 +1,11 @@ +# Development .env for API\ +# DATABASE_URL="postgresql://neondb_owner:npg_PCxbvGSROi20@ep-spring-term-a1xllvq2-pooler.ap-southeast-1.aws.neon.tech/neondb?sslmode=require&channel_binding=require" +DATABASE_URL="postgresql://composter:composter@localhost:5435/composter_test?schema=public" +BETTER_AUTH_SECRET="232522c037dd020b411fda839874791acf1dc4c9c15f3c41996ff27cc68481ba" +NODE_ENV="development" +PORT=3000 +RESEND_API_KEY="" +FROM_EMAIL="" +# Local URLs +BETTER_AUTH_URL="http://localhost:3000" +CLIENT_URL="http://localhost:5173" diff --git a/docker-compose.test.yaml b/docker-compose.test.yaml new file mode 100644 index 0000000..d1e1204 --- /dev/null +++ b/docker-compose.test.yaml @@ -0,0 +1,13 @@ +services: + postgres-test: + image: postgres:17 + container_name: composter-db-test + restart: "no" + ports: + - "5435:5432" + environment: + POSTGRES_USER: composter + POSTGRES_PASSWORD: composter + POSTGRES_DB: composter_test + tmpfs: + - /var/lib/postgresql/data \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ca437a0..3f80dd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@changesets/cli": "^2.27.0", "@playwright/test": "^1.58.0", "@types/node": "^25.0.10", + "dotenv-cli": "^11.0.0", "prettier": "^3.0.0", "turbo": "^2.3.0" } @@ -8887,6 +8888,51 @@ "url": "https://dotenvx.com" } }, + "node_modules/dotenv-cli": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-11.0.0.tgz", + "integrity": "sha512-r5pA8idbk7GFWuHEU7trSTflWcdBpQEK+Aw17UrSHjS6CReuhrrPcyC3zcQBPQvhArRHnBo/h6eLH1fkCvNlww==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.6", + "dotenv": "^17.1.0", + "dotenv-expand": "^12.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "dotenv": "cli.js" + } + }, + "node_modules/dotenv-expand": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz", + "integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/drizzle-orm": { "version": "0.45.1", "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.1.tgz", diff --git a/package.json b/package.json index 0a88702..b83556f 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,8 @@ "format": "prettier --write \"**/*.{ts,tsx,md}\"", "test:e2e": "npx playwright test", "test:e2e:watch": "npx playwright test --watch", + "setup:dev": "node scripts/test-bootstrap.js dev", + "setup:test": "node scripts/test-bootstrap.js test", "show-report:e2e": "npx playwright show-report", "changeset": "changeset", "version-packages": "changeset version", @@ -23,6 +25,7 @@ "@changesets/cli": "^2.27.0", "@playwright/test": "^1.58.0", "@types/node": "^25.0.10", + "dotenv-cli": "^11.0.0", "prettier": "^3.0.0", "turbo": "^2.3.0" }, diff --git a/playwright.config.ts b/playwright.config.ts index 1169a89..03a83bf 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,4 +1,8 @@ import { defineConfig, devices } from '@playwright/test'; +import dotenv from 'dotenv'; +import path from 'path'; + +dotenv.config({ path: path.resolve(__dirname, 'apps/api/.env.test') }); /** * Read environment variables from file. @@ -26,7 +30,7 @@ export default defineConfig({ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('')`. */ - baseURL: 'http://localhost:3000', + baseURL: process.env.CLIENT_URL || 'http://localhost:5173', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace:'on', @@ -86,9 +90,9 @@ export default defineConfig({ ], /* Run your local dev server before starting the tests */ - // webServer: { - // command: 'npm run start', - // url: 'http://localhost:3000', - // reuseExistingServer: !process.env.CI, - // }, + webServer: { + command: 'npm run dev', + url: 'http://localhost:5173', + reuseExistingServer: !process.env.CI, + }, }); diff --git a/scripts/test-bootstrap.js b/scripts/test-bootstrap.js new file mode 100644 index 0000000..c3d7360 --- /dev/null +++ b/scripts/test-bootstrap.js @@ -0,0 +1,74 @@ +const { execSync, spawn } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +const mode = process.argv[2]; + +if(!['dev', 'test'].includes(mode)) { + console.error("Usage: node scripts/test-bootstrap.js dev or test"); + process.exit(0); +} + +const isTest = mode === 'test' +const ROOT_DIR = process.cwd(); +const API_DIR = path.join(ROOT_DIR, 'apps/api'); + +//configuration +const CONFIG = { + dockerFile: isTest ? 'docker-compose.test.yaml' : 'docker-compose.yaml', + envFile: isTest ? '.env.test' : '.env', +} + +const run = (cmd, cwd = ROOT_DIR) => { + try { + console.log(`> ${cmd}`); + execSync(cmd, { stdio: 'inherit', cwd, env: process.env }); + } catch (e) { + console.error(`❌ Command failed: ${cmd}`); + process.exit(1); + } +}; + +async function main() { + console.log(`🚀 Setting up ${mode.toUpperCase()} environment...`); + + if(!fs.existsSync(ROOT_DIR, 'node_modules')){ + run('npm install'); + } + + console.log('🐳 Resetting Database Containers...'); + try{ + execSync(`docker compose -f ${CONFIG.dockerFile} down -v`) + } catch(error){ + //Ignore if already down + } + + console.log('🐳 Starting Database...'); + run(`docker compose -f ${CONFIG.dockerFile} up -d`); + + console.log(`Waiting for db to be ready...`) + await new Promise(r=>setTimeout(r, 5000)); + + console.log('🔄 Syncing Schema...'); + + const envFile = `npx dotenv-cli -e ${CONFIG.envFile} --` + + if(isTest){ + run(`${envFile} npx prisma migrate dev --name test`, API_DIR) + + console.log(`better-auth migrations`) + run(`${envFile} npx @better-auth/cli migrate --config auth/auth.js`, API_DIR) + } else { + run(`${envFile} npx prisma migrate dev --name dev`, API_DIR) + + console.log(`better-auth migrations`) + run(`${envFile} npx @better-auth/cli migrate --config auth/auth.js`, API_DIR) + } + + console.log(`✅ Setup complete!`) +} + +main().catch((e)=>{ + console.error(e); + process.exit(1); +}) \ No newline at end of file diff --git a/tests/api/health.spec.ts b/tests/api/health.spec.ts new file mode 100644 index 0000000..0b48448 --- /dev/null +++ b/tests/api/health.spec.ts @@ -0,0 +1,6 @@ +import {test, expect } from "@playwright/test"; + +test('API Health Check', async({request}) =>{ + const response = await request.get('api/health') + expect(response.ok()).toBeTruthy() +}) \ No newline at end of file