760b4a7c6c
#321 — `minstrel admin reset-password [-user admin] [-password X]`: loads config, updates BOTH password_hash (bcrypt) and subsonic_password (plaintext, for Subsonic t+s) so neither auth path is left stale; generates+prints a strong password when -password is omitted. Recovers a locked-out operator without DB surgery. Subcommand dispatch added to main.go (os.Args switch before flag-parse; server path untouched) plus a `minstrel migrate` subcommand exposing db.Migrate standalone. #339 — integration tests no longer truncate the dev DB: - deploy/initdb creates minstrel_test on a fresh compose volume; `make test-integration` ensures it idempotently and points MINSTREL_TEST_DATABASE_URL at minstrel_test. - docker-compose.yml + README updated. CI integration job (test-go.yml): the prior workflow only ran `go test -short -race` with no DB, so the entire integration suite silently t.Skip'd — "CI green" never covered API/db/scanner. New `integration` job runs the full `go test -race` against an ephemeral Postgres service, using the act_runner shared-daemon pattern: no published ports, discover the service container by job-name filter via the docker socket, reach it by bridge IP, hard exactly-one assertion + dev-compose-name reject (a wrong target would truncate real data), TCP-wait, `minstrel migrate`, then test. Fast `test` job kept as the quick gate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
110 lines
4.0 KiB
YAML
110 lines
4.0 KiB
YAML
name: test-go
|
|
|
|
# Go server: vet + golangci-lint + short race tests. Runs on push to
|
|
# dev/main and PRs to main, scoped to Go-side files only — web-only or
|
|
# Flutter-only diffs don't trigger this workflow.
|
|
#
|
|
# Two jobs: `test` (fast — vet + lint + `go test -short -race`, no DB) and
|
|
# `integration` (full `go test -race` against an ephemeral Postgres).
|
|
#
|
|
# Integration-job DB wiring follows the act_runner shared-daemon pattern:
|
|
# the runner's Docker daemon also runs the operator's dev compose stack,
|
|
# so service containers get NO published ports (collision) and no
|
|
# service-name DNS. We discover the service container by the job-scoped
|
|
# name filter via the mounted docker socket and reach it by bridge IP.
|
|
# The exactly-one assertion is a hard guard — pointing tests at the dev
|
|
# Postgres would truncate it (the disaster Fable #339 exists to prevent).
|
|
#
|
|
# `web/build/` has a committed placeholder index.html so go:embed succeeds
|
|
# without needing the SPA to be freshly built. Real builds happen in
|
|
# release.yml (container) and locally during dev.
|
|
|
|
on:
|
|
push:
|
|
branches: [dev, main]
|
|
paths:
|
|
- '**/*.go'
|
|
- 'go.mod'
|
|
- 'go.sum'
|
|
- 'sqlc.yaml'
|
|
- 'internal/**'
|
|
- 'cmd/**'
|
|
- '.forgejo/workflows/test-go.yml'
|
|
pull_request:
|
|
branches: [main]
|
|
paths:
|
|
- '**/*.go'
|
|
- 'go.mod'
|
|
- 'go.sum'
|
|
- 'sqlc.yaml'
|
|
- 'internal/**'
|
|
- 'cmd/**'
|
|
- '.forgejo/workflows/test-go.yml'
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: go-ci
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Toolchain versions
|
|
run: |
|
|
go version
|
|
golangci-lint --version
|
|
|
|
- name: go vet
|
|
run: go vet ./...
|
|
|
|
- name: golangci-lint
|
|
run: golangci-lint run ./...
|
|
|
|
- name: go test (short, race)
|
|
run: go test -short -race ./...
|
|
|
|
integration:
|
|
runs-on: go-ci
|
|
services:
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
env:
|
|
POSTGRES_USER: minstrel
|
|
POSTGRES_PASSWORD: minstrel
|
|
POSTGRES_DB: minstrel_test
|
|
# No `ports:` — the runner shares the operator's dev compose
|
|
# Docker daemon; publishing a fixed host port collides.
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Integration suite (discover service by bridge IP, migrate, test)
|
|
run: |
|
|
set -eux
|
|
# Discover THIS job's Postgres service container via the
|
|
# mounted docker socket. Scope by the job-name filter so the
|
|
# operator's dev compose `minstrel-postgres-*` (same image,
|
|
# same daemon) can never match. Require exactly one — abort
|
|
# loudly otherwise; a wrong target would truncate real data.
|
|
PG_LIST=$(docker ps --filter "name=integration" --filter "ancestor=postgres:16-alpine" --format '{{.ID}} {{.Names}}')
|
|
echo "candidates: ${PG_LIST:-<none>}"
|
|
PG_COUNT=$(printf '%s\n' "$PG_LIST" | grep -c . || true)
|
|
test "$PG_COUNT" = "1" || { echo "FATAL: expected exactly 1 postgres service container, got $PG_COUNT"; exit 1; }
|
|
PG_ID=$(printf '%s' "$PG_LIST" | awk '{print $1}')
|
|
PG_NAME=$(printf '%s' "$PG_LIST" | awk '{print $2}')
|
|
case "$PG_NAME" in
|
|
*minstrel-postgres*|*_postgres_*) echo "FATAL: matched the dev compose container ($PG_NAME), refusing"; exit 1 ;;
|
|
esac
|
|
PG_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$PG_ID")
|
|
test -n "$PG_IP"
|
|
export MINSTREL_TEST_DATABASE_URL="postgres://minstrel:minstrel@${PG_IP}:5432/minstrel_test?sslmode=disable"
|
|
|
|
# Wait for Postgres to accept TCP (no health-check dependency).
|
|
for i in $(seq 1 60); do (echo > "/dev/tcp/${PG_IP}/5432") 2>/dev/null && break; sleep 2; done
|
|
|
|
# Apply embedded migrations to the fresh test DB, then run the
|
|
# full suite (no -short → integration tests execute).
|
|
MINSTREL_DATABASE_URL="$MINSTREL_TEST_DATABASE_URL" go run ./cmd/minstrel migrate
|
|
go test -race ./...
|