Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
15 changes: 0 additions & 15 deletions .buildkite/pipeline.yml

This file was deleted.

1 change: 0 additions & 1 deletion .dockerignore

This file was deleted.

23 changes: 12 additions & 11 deletions .env.test
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
NEXT_PUBLIC_BASE_URL=http://localhost:3000
PGHOST=
PGPORT=5432
PGUSER=
PGDATABASE=postgres
PGPASSWORD=
PGHOST=127.0.0.1
PGPORT=54322
PGUSER=postgres
PGDATABASE=paradb_test
PGPASSWORD=postgres
SENTRY_DSN=sentryDsn
SENTRY_ENV=sentryEnv
PUBLIC_S3_BASE_URL=https://test.example.com
S3_ENDPOINT=
S3_ENDPOINT=http://127.0.0.1:54321/storage/v1/s3
S3_REGION=local
S3_ACCESS_KEY_ID=accesskeyneedstobeexactly32chars
S3_ACCESS_KEY_SECRET=12345678
S3_MAPS_BUCKET=paradb-maps-test
MEILISEARCH_HOST=https://
MEILISEARCH_KEY=123
SEARCH_IMPLEMENTATION=postgres
MEILISEARCH_HOST=unused
MEILISEARCH_KEY=unused
FLAGS_IMPLEMENTATION=local
FLAGS_EDGE_CONFIG=
FLAGS_EDGE_CONFIG_KEY=
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=
SUPABASE_SECRET_KEY=
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leave these keys out, they should be supplied by the user

SUPABASE_SECRET_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU
AXIOM_API_TOKEN=
AXIOM_DATASET=
NEXT_PUBLIC_AXIOM_API_TOKEN=
Expand Down
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Although you are taking on the persona of an intern developer, your skills are t

This is a website that allows users to host custom maps and songs for a rhythm drumming game called "Paradiddle".
Custom maps consist of a zip file, which contains a .rlrr metadata file along with the audio tracks for the song. The audio tracks can either be the song itself, or the audio stems of the song that can allow the game to play the song without any drum track (as the player will be drumming along themselves).
The codebase uses Docker to run third-party services locally (Meilisearch for search, Minio for a local S3 instance), the local Supabase CLI for running the Supabase database locally, and the standad Next.js dev mode to run the backend and frontend locally.
The codebase uses the local Supabase CLI for running the Supabase database locally (which includes Postgres and Auth), and the standard Next.js dev mode to run the backend and frontend locally. S3 is used for blob storage (locally provided by Supabase Storage's S3-compatible API).

# Tech stack

Expand Down Expand Up @@ -36,9 +36,9 @@ The codebase uses Docker to run third-party services locally (Meilisearch for se

# Commands

- `bun dev` will start local services on Docker
- `bun dev:local` will start Next.js in dev mode with local env
- `bun supabase start` will start Supabase locally
- `bun next dev` will run Next.js in dev mode
- `bun test` will run the test suite
- `bun format` will format the codebase with Prettier
- `bun check` will typecheck and lint

Expand Down
14 changes: 0 additions & 14 deletions Dockerfile

This file was deleted.

20 changes: 0 additions & 20 deletions Dockerfile.dev

This file was deleted.

25 changes: 14 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,22 @@ git clone https://github.com/bitnimble/paradb.git
cd paradb
bun install

# Install and start postgres
sudo apt install postgresql
sudo service postgresql start
# Start local Supabase (requires Supabase CLI)
bun supabase start

# Create postgres user for yourself
sudo -u postgres createuser --interactive --pwprompt
# Copy the example env file and fill in any missing values
cp .env.test .env.localdev

# Edit .env to fill out your username and password!
# Start the dev server
bun dev:local
```

## Running tests

# Create db and instantiate schema
createdb paradb
db/init.sh
```
# Ensure local Supabase is running
bun supabase start

# Start server
bun dev
# Run tests
bun test
```
47 changes: 0 additions & 47 deletions docker/docker-compose.dev.yml

This file was deleted.

17 changes: 0 additions & 17 deletions docker/docker-compose.test.yml

This file was deleted.

35 changes: 0 additions & 35 deletions docker/docker-compose.yml

This file was deleted.

1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const createJestConfig = nextJest({ dir: './' });

const config = {
testMatch: ['**/tests/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'],
globalSetup: '<rootDir>/src/services/jest_global_setup.ts',
setupFilesAfterEnv: ['<rootDir>/src/services/jest_setup.ts'],
modulePaths: ['<rootDir>/src'],
};
Expand Down
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@
"dev:local": "dotenv -e .env.localdev -- bun next dev",
"dev:dev": "dotenv -e .env.dev -- bun next dev",
"dev:prod": "dotenv -e .env.prod -- bun next dev",
"build": "sudo docker compose -f docker/docker-compose.yml --env-file .env build",
"start": "sudo docker compose -f docker/docker-compose.yml --env-file .env up --build",
"check": "bun tsc && bun eslint .",
"lint": "eslint .",
"format": "prettier . --write",
"schema": "tools/update_schema.sh",
"test": "sudo docker compose -f docker/docker-compose.test.yml --env-file .env.test up --build --abort-on-container-exit"
"test": "dotenv -e .env.test -- bun jest --runInBand"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.958.0",
Expand Down
76 changes: 76 additions & 0 deletions src/services/jest_global_setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { loadEnvConfig } from '@next/env';
import * as fs from 'fs/promises';
import * as path from 'path';
import pg from 'pg';
import { DatabaseError } from 'pg-protocol';

const projectDir = process.cwd();
loadEnvConfig(projectDir);

/**
* Creates the test database and applies the schema.
* This runs once before the entire test suite, ensuring tests use a separate database
* from the local dev environment.
*/
export default async function globalSetup() {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot this function isn't actually deleting anything, and is still looking at the 'public' schema instead of a separate test one

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7fe04ee. Tests now use a separate paradb_test Postgres schema instead of public:

  • globalSetup creates the paradb_test schema, drops+recreates tables from the DDL files, seeds test data, and deletes leftover auth users
  • .env.test sets PGOPTIONS=-c search_path=paradb_test,public so all DB queries (both Jest and the dev server) route to the test schema
  • beforeEach truncates paradb_test.* tables specifically

Dev data in the public schema is untouched.

const testDb = process.env.PGDATABASE;
const host = process.env.PGHOST;
const port = process.env.PGPORT;
const user = process.env.PGUSER;
const password = process.env.PGPASSWORD;

if (!testDb || !host || !port || !user || !password) {
throw new Error('Missing required PG* environment variables for test setup');
}
if (!/^[a-zA-Z0-9_]+$/.test(testDb)) {
throw new Error(`Invalid test database name: ${testDb}`);
}

// Connect to the default `postgres` database to create the test database
const adminPool = new pg.Pool({
host,
port: Number(port),
database: 'postgres',
user,
password,
});

try {
const exists = await adminPool.query(`SELECT 1 FROM pg_database WHERE datname = $1`, [testDb]);
if (exists.rows.length === 0) {
await adminPool.query(`CREATE DATABASE "${testDb}"`);
}
} finally {
await adminPool.end();
}

// Connect to the test database and apply the schema
const testPool = new pg.Pool({
host,
port: Number(port),
database: testDb,
user,
password,
});

try {
const schemasDir = path.resolve(__dirname, '../../supabase/schemas');
// Apply schemas in order matching supabase config.toml
const schemaFiles = ['maps.sql', 'users.sql', 'favorites.sql', 'functions.sql', 'misc.sql'];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoiding recreating the test db or manually applying schema changes in a TS file. if possible, run a shell script that uses the supabase db cli to create it

for (const file of schemaFiles) {
const filePath = path.join(schemasDir, file);
try {
const sql = await fs.readFile(filePath, 'utf-8');
await testPool.query(sql);
} catch (e: unknown) {
// 42P07 = duplicate_table, 42710 = duplicate_object — schema already applied
if (e instanceof DatabaseError && (e.code === '42P07' || e.code === '42710')) {
continue;
}
console.warn(`Warning: failed to apply ${file}:`, e);
}
}
} finally {
await testPool.end();
}
}
6 changes: 3 additions & 3 deletions src/services/jest_setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ loadEnvConfig(projectDir);

async function initTestData() {
const { pool } = await getServerContext();
const initialDataSqlPath = path.resolve(__dirname, '../../db/fake_data.sql');
const initialDataSql = await fs.readFile(initialDataSqlPath).then((b) => b.toString());
const seedSqlPath = path.resolve(__dirname, '../../supabase/seed.sql');
Comment thread
bitnimble marked this conversation as resolved.
const seedSql = await fs.readFile(seedSqlPath).then((b) => b.toString());
await pool.query(`
TRUNCATE maps, difficulties, users, favorites CASCADE;
${initialDataSql}
${seedSql}
`);
}

Expand Down
5 changes: 0 additions & 5 deletions tools/docker/start.sh

This file was deleted.

Loading