Merge branch 'main' of github.com:open-reception/appointment-booking-software into 34-api-structure-and-third-party-libs

This commit is contained in:
Hendrik Belitz
2025-06-24 14:40:13 +02:00
13 changed files with 368 additions and 172 deletions
+40
View File
@@ -0,0 +1,40 @@
name: Main Branch Deploy
on:
push:
branches: [main]
jobs:
test-build-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "24"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run type checking
run: npm run check
- name: Run linting
run: npm run lint
- name: Run unit tests
run: npm run test:unit -- --run
- name: Build application
run: npm run build
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run E2E tests
run: npm run test:e2e
+35
View File
@@ -0,0 +1,35 @@
name: PR Checks
on:
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
jobs:
test-and-build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "24"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run type checking
run: npm run check
- name: Run linting
run: npm run lint
- name: Run unit tests
run: npm run test:unit -- --run
- name: Build application
run: npm run build
+80
View File
@@ -0,0 +1,80 @@
name: Release
on:
push:
tags:
- "v*"
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "24"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Update package.json version
run: |
TAG_VERSION="${{ github.ref_name }}"
VERSION="${TAG_VERSION#v}"
npm version "$VERSION" --no-git-tag-version
- name: Run type checking
run: npm run check
- name: Run linting
run: npm run lint
- name: Run unit tests
run: npm run test:unit -- --run
- name: Build application
run: npm run build
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run E2E tests
run: npm run test:e2e
- name: Build and push Docker images
run: npm run docker:build-and-push
- name: Create Release Archive
run: |
mkdir -p release-assets
tar -czf release-assets/open-reception-${{ github.ref_name }}.tar.gz build/
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
name: Release ${{ github.ref_name }}
generate_release_notes: true
body: |
## Release ${{ github.ref_name }}
This release includes the latest version of Open Reception appointment booking software.
### What's Changed
<!-- Release notes will be automatically generated above -->
### Installation
Extract the archive and follow the deployment instructions in the README.
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ github.event.before }}...${{ github.ref_name }}
files: |
release-assets/open-reception-${{ github.ref_name }}.tar.gz
draft: false
prerelease: ${{ contains(github.ref_name, '-') }}
+6 -2
View File
@@ -16,7 +16,11 @@ services:
networks:
- appointment-booking-dev
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-appointment_booking}"]
test:
[
"CMD-SHELL",
"pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-appointment_booking}"
]
interval: 10s
timeout: 5s
retries: 5
@@ -33,4 +37,4 @@ volumes:
networks:
appointment-booking-dev:
driver: bridge
driver: bridge
+8 -3
View File
@@ -19,7 +19,11 @@ services:
networks:
- open-reception-internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$(cat /run/secrets/postgres_user) -d $$(cat /run/secrets/postgres_db)"]
test:
[
"CMD-SHELL",
"pg_isready -U $$(cat /run/secrets/postgres_user) -d $$(cat /run/secrets/postgres_db)"
]
interval: 10s
timeout: 5s
retries: 5
@@ -56,7 +60,8 @@ services:
networks:
- open-reception-internal
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000/api/health"]
test:
["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
@@ -119,4 +124,4 @@ volumes:
networks:
open-reception-internal:
driver: bridge
internal: false
internal: false
+79 -57
View File
@@ -23,16 +23,20 @@ Development runs only the PostgreSQL database with a public port for development
### Setup Steps
1. **Clone the repository**
```bash
git clone <repository-url>
cd appointment-booking-software
```
2. **Configure environment**
```bash
cp .env.example .env
```
Edit `.env` and set secure values:
```
POSTGRES_DB=appointment_booking
POSTGRES_USER=postgres
@@ -41,20 +45,23 @@ Development runs only the PostgreSQL database with a public port for development
```
3. **Start development database**
```bash
npm run docker:dev:up
```
4. **Verify database connection**
```bash
# Check container status
docker ps
# Test connection
psql -h localhost -U postgres -d appointment_booking
```
5. **Install SvelteKit dependencies** (when SvelteKit code is added)
```bash
npm install
```
@@ -91,21 +98,23 @@ Production runs all three containers with security hardening and HTTPS.
### Setup Steps
1. **Configure production secrets**
Edit the secret files in the `secrets/` directory:
```bash
# Database configuration
echo "appointment_booking" > secrets/postgres_db.txt
echo "postgres" > secrets/postgres_user.txt
echo "$(openssl rand -base64 32)" > secrets/postgres_password.txt
# Set proper permissions
chmod 600 secrets/*.txt
```
2. **Configure domain**
Edit `Caddyfile` and replace `your-domain.com` with your actual domain:
```
yourdomain.com {
# ... rest of configuration
@@ -113,22 +122,24 @@ Production runs all three containers with security hardening and HTTPS.
```
3. **Build and start production stack**
```bash
# Build application image
npm run docker:prod:build
# Start all services
npm run docker:prod:up
```
4. **Verify deployment**
```bash
# Check all containers are running
docker ps
# Check application health
npm run docker:health
# View logs
npm run docker:prod:logs
```
@@ -143,7 +154,7 @@ npm run docker:prod:down # Stop all services
npm run docker:prod:logs # View all logs
npm run docker:prod:clean # Remove containers and volumes
npm run docker:health # Check container status
npm run docker:tag-and-push # Build, tag and push the app image to GHCR (Remember to change username accordingly first)
npm run docker:tag-and-push # Build, tag and push the app image to GHCR (Remember to change username accordingly first)
```
## SvelteKit Application Setup
@@ -153,34 +164,35 @@ When implementing the SvelteKit application, follow these steps:
### Database Connection
Create `src/lib/database.js`:
```javascript
import { readFileSync } from 'fs';
import pg from 'pg';
import { readFileSync } from "fs";
import pg from "pg";
function getDatabaseConfig() {
if (process.env.NODE_ENV === 'production') {
// Read from Docker secrets
const user = readFileSync('/run/secrets/postgres_user', 'utf8').trim();
const password = readFileSync('/run/secrets/postgres_password', 'utf8').trim();
const database = readFileSync('/run/secrets/postgres_db', 'utf8').trim();
return {
host: 'postgres',
port: 5432,
user,
password,
database
};
} else {
// Development configuration
return {
host: 'localhost',
port: process.env.POSTGRES_PORT || 5432,
user: process.env.POSTGRES_USER || 'postgres',
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DB || 'appointment_booking'
};
}
if (process.env.NODE_ENV === "production") {
// Read from Docker secrets
const user = readFileSync("/run/secrets/postgres_user", "utf8").trim();
const password = readFileSync("/run/secrets/postgres_password", "utf8").trim();
const database = readFileSync("/run/secrets/postgres_db", "utf8").trim();
return {
host: "postgres",
port: 5432,
user,
password,
database
};
} else {
// Development configuration
return {
host: "localhost",
port: process.env.POSTGRES_PORT || 5432,
user: process.env.POSTGRES_USER || "postgres",
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DB || "appointment_booking"
};
}
}
export const pool = new pg.Pool(getDatabaseConfig());
@@ -189,54 +201,55 @@ export const pool = new pg.Pool(getDatabaseConfig());
### Health Check Endpoint
Create `src/routes/health/+server.js`:
```javascript
import { json } from '@sveltejs/kit';
import { pool } from '$lib/database.js';
import { json } from "@sveltejs/kit";
import { pool } from "$lib/database.js";
export async function GET() {
try {
await pool.query('SELECT 1');
return json({ status: 'healthy', timestamp: new Date().toISOString() });
} catch (error) {
return json(
{ status: 'unhealthy', error: error.message },
{ status: 500 }
);
}
try {
await pool.query("SELECT 1");
return json({ status: "healthy", timestamp: new Date().toISOString() });
} catch (error) {
return json({ status: "unhealthy", error: error.message }, { status: 500 });
}
}
```
### SvelteKit Configuration
Update `vite.config.js`:
```javascript
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [sveltekit()],
server: {
host: '0.0.0.0',
port: 5173
}
plugins: [sveltekit()],
server: {
host: "0.0.0.0",
port: 5173
}
});
```
## Security Features
### Container Security
- **Rootless containers**: All containers (sans caddy, which needs to access privileged ports) run as non-root users
- **Read-only filesystems**: Containers have read-only root filesystems where possible
- **No new privileges**: Containers cannot escalate privileges
- **Minimal capabilities**: Only necessary Linux capabilities are granted
### Network Security
- **Internal networks**: Database and app communicate via internal Docker network
- **No exposed ports**: Only Caddy exposes ports to the host
- **HTTPS only**: Caddy handles automatic HTTPS with Let's Encrypt
### Data Security
- **Docker secrets**: Sensitive data stored as Docker secrets
- **Encrypted connections**: Database connections use SSL/TLS
- **Security headers**: Comprehensive security headers via Caddy
@@ -244,16 +257,19 @@ export default defineConfig({
## Monitoring and Logs
### Log Locations
- **Caddy logs**: Stored in `/data/access.log` inside Caddy container
- **Application logs**: Available via `docker logs`
- **Database logs**: Available via `docker logs`
### Health Checks
- **Application**: `GET /health` endpoint
- **Database**: Built-in PostgreSQL health check
- **Caddy**: HTTP response check
### Monitoring Commands
```bash
# View real-time logs
npm run docker:prod:logs
@@ -273,37 +289,41 @@ docker logs appointment-booking-caddy
### Common Issues
1. **Container won't start**
```bash
# Check container logs
docker logs <container-name>
# Check resource usage
docker stats
```
2. **Database connection issues**
```bash
# Test database connectivity
docker exec -it appointment-booking-postgres psql -U postgres -d appointment_booking
# Check database logs
docker logs appointment-booking-postgres
```
3. **HTTPS certificate issues**
```bash
# Check Caddy logs
docker logs appointment-booking-caddy
# Verify domain DNS
nslookup yourdomain.com
```
4. **Application not accessible**
```bash
# Check if all containers are running
docker ps
# Test internal connectivity
docker exec -it appointment-booking-caddy wget -q --spider http://app:3000/health
```
@@ -323,6 +343,7 @@ npm run docker:prod:up
## Backup and Restore
### Database Backup
```bash
# Create backup
docker exec appointment-booking-postgres pg_dump -U postgres appointment_booking > backup.sql
@@ -332,7 +353,8 @@ docker exec -i appointment-booking-postgres psql -U postgres appointment_booking
```
### Volume Backup
```bash
# Backup PostgreSQL data volume
docker run --rm -v appointment-booking-software_postgres_data:/data -v $(pwd):/backup alpine tar czf /backup/postgres-backup.tar.gz /data
```
```
+9 -6
View File
@@ -10,7 +10,6 @@
"license": "AGPL-3.0",
"dependencies": {
"@sveltejs/adapter-node": "^5.2.12",
"@types/dotenv": "^6.1.1",
"dotenv": "^16.5.0",
"dotenv-expand": "^12.0.2",
"drizzle-orm": "^0.40.0",
@@ -27,6 +26,7 @@
"@tailwindcss/vite": "^4.0.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/svelte": "^5.2.4",
"@types/dotenv": "^6.1.1",
"@types/node": "^22",
"drizzle-kit": "^0.30.2",
"eslint": "^9.18.0",
@@ -1886,9 +1886,9 @@
}
},
"node_modules/@sveltejs/kit": {
"version": "2.21.5",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.21.5.tgz",
"integrity": "sha512-P5m7yZtvD1Kx/Z6JcjgJtdMqef/tCGMDrd9B9S2q8j+FMnkeKTMxW1nidnjVzk4HEDRGf4IlBI94/niy6t3hLA==",
"version": "2.22.0",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.22.0.tgz",
"integrity": "sha512-DJm0UxVgzXq+1MUfiJK4Ridk7oIQsIets6JwHiEl97sI6nXScfXe+BeqNhzB7jQIVBb3BM51U4hNk8qQxRXBAA==",
"license": "MIT",
"dependencies": {
"@sveltejs/acorn-typescript": "^1.0.5",
@@ -1912,9 +1912,9 @@
"node": ">=18.13"
},
"peerDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0",
"svelte": "^4.0.0 || ^5.0.0-next.0",
"vite": "^5.0.3 || ^6.0.0"
"vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0"
}
},
"node_modules/@sveltejs/vite-plugin-svelte": {
@@ -2383,6 +2383,7 @@
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz",
"integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
@@ -2405,6 +2406,7 @@
"version": "22.15.32",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.32.tgz",
"integrity": "sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
@@ -6031,6 +6033,7 @@
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"devOptional": true,
"license": "MIT"
},
"node_modules/uri-js": {
+73 -73
View File
@@ -1,76 +1,76 @@
{
"name": "open-reception",
"private": true,
"version": "0.0.1",
"type": "module",
"description": "End-to-end encrypted appointment booking platform",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"format": "prettier --write .",
"lint": "prettier --check . && eslint .",
"test:unit": "vitest",
"test": "npm run test:unit -- --run && npm run test:e2e",
"test:e2e": "playwright test",
"db:push": "drizzle-kit push",
"db:migrate": "drizzle-kit migrate",
"db:studio": "drizzle-kit studio",
"docker:dev:up": "docker compose -f docker-compose.dev.yml up -d",
"docker:dev:down": "docker compose -f docker-compose.dev.yml down",
"docker:dev:logs": "docker compose -f docker-compose.dev.yml logs -f",
"docker:dev:clean": "docker compose -f docker-compose.dev.yml down -v --remove-orphans",
"docker:build": "docker build -t open-reception:latest .",
"docker:build:tag": "docker tag open-reception:latest open-reception:$npm_package_version",
"docker:push": "docker push open-reception:$npm_package_version && docker push open-reception:latest",
"docker:build-and-push": "npm run docker:build && npm run docker:build:tag && npm run docker:push",
"docker:prod:up": "docker compose -f docker-compose.prod.yml up -d",
"docker:prod:down": "docker compose -f docker-compose.prod.yml down",
"docker:prod:logs": "docker compose -f docker-compose.prod.yml logs -f",
"docker:prod:clean": "docker compose -f docker-compose.prod.yml down -v --remove-orphans"
},
"devDependencies": {
"@eslint/compat": "^1.2.5",
"@eslint/js": "^9.18.0",
"@playwright/test": "^1.49.1",
"@sveltejs/adapter-auto": "^6.0.0",
"@sveltejs/kit": "^2.16.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tailwindcss/typography": "^0.5.15",
"@tailwindcss/vite": "^4.0.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/svelte": "^5.2.4",
"@types/node": "^22",
"name": "open-reception",
"private": true,
"version": "0.0.1",
"type": "module",
"description": "End-to-end encrypted appointment booking platform",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"format": "prettier --write .",
"lint": "prettier --check . && eslint .",
"test:unit": "vitest",
"test": "npm run test:unit -- --run && npm run test:e2e",
"test:e2e": "playwright test",
"db:push": "drizzle-kit push",
"db:migrate": "drizzle-kit migrate",
"db:studio": "drizzle-kit studio",
"docker:dev:up": "docker compose -f docker-compose.dev.yml up -d",
"docker:dev:down": "docker compose -f docker-compose.dev.yml down",
"docker:dev:logs": "docker compose -f docker-compose.dev.yml logs -f",
"docker:dev:clean": "docker compose -f docker-compose.dev.yml down -v --remove-orphans",
"docker:build": "docker build -t open-reception:latest .",
"docker:build:tag": "docker tag open-reception:latest open-reception:$npm_package_version",
"docker:push": "docker push open-reception:$npm_package_version && docker push open-reception:latest",
"docker:build-and-push": "npm run docker:build && npm run docker:build:tag && npm run docker:push",
"docker:prod:up": "docker compose -f docker-compose.prod.yml up -d",
"docker:prod:down": "docker compose -f docker-compose.prod.yml down",
"docker:prod:logs": "docker compose -f docker-compose.prod.yml logs -f",
"docker:prod:clean": "docker compose -f docker-compose.prod.yml down -v --remove-orphans"
},
"devDependencies": {
"@eslint/compat": "^1.2.5",
"@eslint/js": "^9.18.0",
"@playwright/test": "^1.49.1",
"@sveltejs/adapter-auto": "^6.0.0",
"@sveltejs/kit": "^2.16.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tailwindcss/typography": "^0.5.15",
"@tailwindcss/vite": "^4.0.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/svelte": "^5.2.4",
"@types/node": "^22",
"@types/dotenv": "^6.1.1",
"drizzle-kit": "^0.30.2",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-svelte": "^3.0.0",
"globals": "^16.0.0",
"jsdom": "^26.0.0",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"tailwindcss": "^4.0.0",
"typescript": "^5.0.0",
"typescript-eslint": "^8.20.0",
"vite": "^6.2.6",
"vitest": "^3.2.3"
},
"dependencies": {
"@sveltejs/adapter-node": "^5.2.12",
"dotenv": "^16.5.0",
"dotenv-expand": "^12.0.2",
"drizzle-orm": "^0.40.0",
"postgres": "^3.4.5"
},
"engines": {
"node": ">=24.0.0"
},
"license": "AGPL-3.0"
"drizzle-kit": "^0.30.2",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-svelte": "^3.0.0",
"globals": "^16.0.0",
"jsdom": "^26.0.0",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"tailwindcss": "^4.0.0",
"typescript": "^5.0.0",
"typescript-eslint": "^8.20.0",
"vite": "^6.2.6",
"vitest": "^3.2.3"
},
"dependencies": {
"@sveltejs/adapter-node": "^5.2.12",
"dotenv": "^16.5.0",
"dotenv-expand": "^12.0.2",
"drizzle-orm": "^0.40.0",
"postgres": "^3.4.5"
},
"engines": {
"node": ">=24.0.0"
},
"license": "AGPL-3.0"
}
+9 -9
View File
@@ -1,15 +1,15 @@
<script>
import { onMount } from 'svelte';
let envVarsOkay = {};
onMount(async () => {
const response = await fetch('/api/env');
envVarsOkay = (await response.json()).envOkay ? "okay" : "not okay";
});
import { onMount } from "svelte";
let envVarsOkay = {};
onMount(async () => {
const response = await fetch("/api/env");
envVarsOkay = (await response.json()).envOkay ? "okay" : "not okay";
});
</script>
<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p>
<p>Environment configuration is {envVarsOkay}</p>
<p>Environment configuration is {envVarsOkay}</p>
+14 -16
View File
@@ -1,21 +1,19 @@
import { json } from '@sveltejs/kit';
import dotenv from 'dotenv';
import dotenvExpand from 'dotenv-expand'
import { json } from "@sveltejs/kit";
import dotenv from "dotenv";
import dotenvExpand from "dotenv-expand";
const cfg = dotenv.config()
dotenvExpand.expand(cfg)
const cfg = dotenv.config();
dotenvExpand.expand(cfg);
export async function GET() {
let envOkay = true;
if (process.env.NODE_ENV !== "production" && process.env.NODE_ENV !== "development")
envOkay = false;
if (!process.env.DATABASE_URL?.startsWith("postgres:")) envOkay = false;
let envOkay = true;
if (process.env.NODE_ENV !== "production" && process.env.NODE_ENV !== "development")
envOkay = false;
if (!process.env.DATABASE_URL?.startsWith("postgres:"))
envOkay = false;
console.warn("Environment is okay", envOkay);
console.warn("Environment is okay", envOkay)
return json({
envOkay
});
}
return json({
envOkay
});
}
+3 -3
View File
@@ -1,5 +1,5 @@
import { json } from '@sveltejs/kit';
import { json } from "@sveltejs/kit";
export async function GET() {
return json(true);
}
return json(true);
}
+10 -1
View File
@@ -1,9 +1,18 @@
import { describe, test, expect } from "vitest";
import { describe, test, expect, vi, beforeEach } from "vitest";
import "@testing-library/jest-dom/vitest";
import { render, screen } from "@testing-library/svelte";
import Page from "./+page.svelte";
describe("/+page.svelte", () => {
beforeEach(() => {
// Mock fetch to avoid the "Invalid URL" error
global.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ envOkay: true })
} as Response)
);
});
test("should render h1", () => {
render(Page);
expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument();
+2 -2
View File
@@ -1,5 +1,5 @@
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import adapter from "@sveltejs/adapter-node";
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
/** @type {import('@sveltejs/kit').Config} */
const config = {