Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,17 @@ homik/
See [backend/README.md](backend/README.md) to run the API locally.
Frontend setup instructions coming once scaffolded.

## Running tests

Tests run against a local Postgres container. You'll need Docker installed and the daemon running. The container binds to port 5432.

```bash
./scripts/run-tests.sh # starts the container if needed, then runs the full suite
```

The container stays running after the script finishes — useful when iterating on tests. Stop it when you're done for the day:

```bash
docker compose down
```

4 changes: 3 additions & 1 deletion backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
# Use the asyncpg driver (required for async FastAPI)
# Use ssl=require (not sslmode=require) for asyncpg compatibility with Neon
DATABASE_URL=postgresql+asyncpg://user:password@host/dbname?ssl=require
TEST_DATABASE_URL=postgresql+asyncpg://user:password@host/test_dbname?ssl=require
# For local development with Docker Compose: postgresql+asyncpg://postgres:postgres@localhost:5432/homik_test
# For Neon: postgresql+asyncpg://user:password@host/test_dbname?ssl=require
TEST_DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/homik_test

# ── Auth (FastAPI Users) ─────────────────────────────────────────────
# Generate with: python -c "import secrets; print(secrets.token_hex(32))"
Expand Down
19 changes: 19 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
services:
db:
image: postgres:16
environment:
POSTGRES_DB: homik_test
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Minor: Port conflict consideration

The hardcoded 5432:5432 mapping will conflict if the developer already has a Postgres instance running locally on the default port.

This is acceptable for a simple local dev setup, but you could consider:

  1. Using a non-standard port like "5433:5432" to avoid conflicts
  2. Documenting in README.md that port 5432 must be free
  3. Adding a check in run-tests.sh to fail early with a helpful message if the port is in use

For a portfolio project targeting simplicity, option 2 (just documenting it) is probably sufficient. The current approach is fine — just something to be aware of if you run into "port already allocated" errors.

volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -q"]
interval: 5s
timeout: 5s
retries: 5

volumes:
postgres_data:
11 changes: 11 additions & 0 deletions docs/til.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@

## Developer Tooling

- **`until docker compose exec -T db pg_isready -U postgres -q; do sleep 1; done`** waits for Postgres to accept connections before running tests. `-T` disables TTY (teletypewriter) allocation — by default `docker compose exec` expects an interactive terminal session; `-T` tells it there's no human attached, which is required in scripts and CI. Without the wait, pytest starts before the container is ready and connections fail immediately.
- **`set -uo pipefail` without `-e` for lint scripts.** `-e` exits immediately on any non-zero exit code, which would kill the script before capturing lint output — lint failures are the expected case. Drop `-e` and capture output with `|| true` so both tools always run regardless of result.
- **`uv run` via a subshell, not a direct binary path.** Running `(cd backend && uv run ruff check .)` ensures `uv` resolves the correct project virtualenv without hardcoding `.venv/bin/ruff` or assuming anything about PATH. Works identically on any machine with `uv` installed.
- **`gh pr edit --body "$(cat docs/pr-description.md)"`** updates the PR description from a local file. Combine with a shell function (not an alias) so the `$()` expands at call time, not at definition time: `update-pr() { gh pr edit --body "$(cat docs/pr-description.md)"; }` in `~/.zshrc`.
Expand Down Expand Up @@ -101,6 +102,16 @@

## Session Log

### 2026-06-18 — Local Docker Postgres for tests

Built: `docker-compose.yml`, `run-tests.sh` updated to start and wait for the container automatically.

Key learnings:
- `pg_isready` in an `until` loop — reliable readiness wait; `-T` flag required because scripts have no interactive terminal
- Container left running between runs by design — stopping per run re-incurs the startup cost and defeats the speed gain

---

### 2026-06-16 — Integration test suite + test tooling (PR: feat/test)

Built: 43 integration tests across all resources, `run-tests.sh`, `/fix-test` skill, batch PATCH autoflush bug fix.
Expand Down
14 changes: 13 additions & 1 deletion scripts/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@
# Assumptions:
# - Run from the project root (not from backend/)
# - `uv` is installed and on PATH (https://docs.astral.sh/uv/)
# - TEST_DATABASE_URL is set in backend/.env
# - `docker` is installed and the Docker daemon is running
# - TEST_DATABASE_URL in backend/.env points at the local Docker container
# (postgresql+asyncpg://postgres:postgres@localhost:5432/homik_test)
#
# Database setup:
# Tests run against a local Postgres container defined in docker-compose.yml.
# This script starts the container automatically if it isn't already running.
# To start it manually: docker compose up -d db
# To stop it: docker compose down
#
# Usage: chmod +x scripts/run-tests.sh && ./scripts/run-tests.sh
# Shortcut: alias run-tests='./scripts/run-tests.sh'
Expand All @@ -31,6 +39,10 @@ if [ ! -d "$BACKEND_DIR" ]; then
exit 1
fi

echo "Starting local test database..."
docker compose up -d db
until docker compose exec -T db pg_isready -U postgres -q; do sleep 1; done

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nice pattern: until loop with pg_isready

This is the right way to wait for Postgres — pg_isready checks if the server is actually accepting connections, not just that the container started.

The -T flag on docker compose exec is critical here: without it, Docker expects an interactive terminal and the command fails in non-interactive contexts (scripts, CI). Good catch documenting this in til.md.

One minor enhancement if you want to prevent infinite hangs: add a timeout counter:

WAIT_TIME=0
until docker compose exec -T db pg_isready -U postgres -q; do
  sleep 1
  WAIT_TIME=$((WAIT_TIME + 1))
  if [ $WAIT_TIME -gt 30 ]; then
    echo "Error: Database failed to start after 30 seconds"
    exit 1
  fi
done

But for local dev, the current infinite loop is fine — if Docker/Postgres is broken, you want to know immediately anyway.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

decided to skip, easy to spot when testing locally


echo "Test run: $TIMESTAMP" > "$OUTPUT"
(cd "$BACKEND_DIR" && uv run pytest -v 2>&1 | tee -a "../$OUTPUT") || true

Expand Down