Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
06002af
update ci tests to use pinned local services stack
owens1127 Apr 22, 2026
d193c6b
move services pin file to avoid remote path conflict
owens1127 Apr 22, 2026
2b10f99
use services make env in ci bootstrap script
owens1127 Apr 22, 2026
e91e812
use real postgres fixtures in status integration tests
owens1127 Apr 22, 2026
53a98bd
lazy-init database clients to reduce import-time coupling
owens1127 Apr 22, 2026
1349430
format
owens1127 Apr 22, 2026
bf4bc68
bump pinned services commit for ci schema parity
owens1127 Apr 22, 2026
7fc05bc
stabilize ci tests with local-db assumptions
owens1127 Apr 22, 2026
e663441
format player fixture test for ci checks
owens1127 Apr 22, 2026
ba96345
make dataset-dependent tests resilient for local ci data
owens1127 Apr 22, 2026
ba30b40
format pgcr service test for static checks
owens1127 Apr 22, 2026
18c8168
make integration tests tolerant of seeded local datasets
owens1127 Apr 22, 2026
292f4cd
stabilize instance service tests for local seeded db
owens1127 Apr 22, 2026
97c6ae5
remove remaining prod-data assumptions in service tests
owens1127 Apr 22, 2026
6ff7a95
stabilize player and clan tests for local ci data
owens1127 Apr 22, 2026
853a2c4
use discovered player ids in history and teammates route tests
owens1127 Apr 22, 2026
6a08dc0
make expectOk/expectErr only assert result type
owens1127 Apr 22, 2026
d60d12e
add deterministic fixtures for remaining brittle route tests
owens1127 Apr 22, 2026
d7c47bb
format reporting standing fixture tests for ci
owens1127 Apr 22, 2026
60632f1
make leaderboard search route tests data-tolerant in ci
owens1127 Apr 22, 2026
78654f5
fix profile and instance-standing test setup execution
owens1127 Apr 22, 2026
3b17814
set instance-standing fixture skull hashes to empty array
owens1127 Apr 22, 2026
291b137
test: add DB fixtures and drop vacuous skips
owens1127 Apr 22, 2026
0d1c75a
test: assert teammates payload and error codes
owens1127 Apr 22, 2026
739e0ea
test: extend review themes to leaderboards and service history
owens1127 Apr 23, 2026
fba24e7
fix(ci): repair leaderboard TS narrowing, PGCR gzip, instance_character
owens1127 Apr 23, 2026
19ae777
test: align history final-raid and feat list with CI seed
owens1127 Apr 23, 2026
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
2 changes: 2 additions & 0 deletions .github/raidhub-services-version.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
RAIDHUB_SERVICES_REPO=Raid-Hub/RaidHub-Services
RAIDHUB_SERVICES_COMMIT=858b76cf0314c1d5547a60011bf444eb63615e88
76 changes: 76 additions & 0 deletions .github/scripts/setup-local-services-ci.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
VERSIONS_FILE="${ROOT_DIR}/.github/raidhub-services-version.env"
SERVICES_DIR="${ROOT_DIR}/RaidHub-Services"

if [[ ! -f "${VERSIONS_FILE}" ]]; then
echo "Missing versions file at ${VERSIONS_FILE}"
exit 1
fi

set -a
source "${VERSIONS_FILE}"
set +a

if [[ -z "${RAIDHUB_SERVICES_REPO:-}" || -z "${RAIDHUB_SERVICES_COMMIT:-}" ]]; then
echo "RAIDHUB_SERVICES_REPO and RAIDHUB_SERVICES_COMMIT must be set in ${VERSIONS_FILE}"
exit 1
fi

if [[ ! "${RAIDHUB_SERVICES_COMMIT}" =~ ^[0-9a-f]{40}$ ]]; then
echo "RAIDHUB_SERVICES_COMMIT must be a full 40-character commit hash"
exit 1
fi

echo "Checking out ${RAIDHUB_SERVICES_REPO}@${RAIDHUB_SERVICES_COMMIT}"
rm -rf "${SERVICES_DIR}"
git init "${SERVICES_DIR}"
git -C "${SERVICES_DIR}" remote add origin "https://github.com/${RAIDHUB_SERVICES_REPO}.git"
git -C "${SERVICES_DIR}" fetch --depth=1 origin "${RAIDHUB_SERVICES_COMMIT}"
git -C "${SERVICES_DIR}" checkout --detach FETCH_HEAD

(
cd "${SERVICES_DIR}"
make env
)

docker compose -f "${SERVICES_DIR}/docker-compose.yml" --env-file "${SERVICES_DIR}/.env" up -d postgres rabbitmq clickhouse prometheus

wait_for() {
local name="$1"
local cmd="$2"
local attempts="${3:-60}"
local sleep_seconds="${4:-2}"

for _ in $(seq 1 "${attempts}"); do
if eval "${cmd}" >/dev/null 2>&1; then
echo "${name} is ready"
return 0
fi
sleep "${sleep_seconds}"
done

echo "${name} did not become ready in time"
return 1
}

echo "Waiting for PostgreSQL..."
wait_for "PostgreSQL" "docker compose -f \"${SERVICES_DIR}/docker-compose.yml\" --env-file \"${SERVICES_DIR}/.env\" exec -T postgres pg_isready -U dev -d raidhub"

echo "Waiting for ClickHouse..."
wait_for "ClickHouse" "curl -fsS \"http://localhost:8123/ping\" | grep -q '^Ok\\.$'"

echo "Waiting for RabbitMQ API..."
wait_for "RabbitMQ API" "curl -fsS -u \"dev:password\" \"http://localhost:15672/api/overview\""

(
cd "${SERVICES_DIR}"
make migrate
make seed
)

if [[ -n "${GITHUB_ENV:-}" ]]; then
echo "RAIDHUB_SERVICES_DIR=${SERVICES_DIR}" >> "${GITHUB_ENV}"
fi
42 changes: 21 additions & 21 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,29 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- uses: actions/setup-go@v5
with:
go-version: "1.24"

- name: Bun Install
run: |
bun install --frozen-lockfile

- name: Install cloudflared
run: ./.github/scripts/install-cloudflared.sh

- name: Load SSH Key
uses: shimataro/ssh-key-action@v2
with:
key: ${{ secrets.VPS_SSH_PRIVATE_KEY }}
known_hosts: ${{ secrets.VPS_SSH_KNOWN_HOSTS }}

- name: Tunnel to VPS
env:
TUNNEL_SERVICE_TOKEN_ID: ${{ secrets.CF_GHA_CLIENT_ID }}
TUNNEL_SERVICE_TOKEN_SECRET: ${{ secrets.CF_GHA_CLIENT_SECRET }}
run: |
ssh -N -L 5432:localhost:5432 -L 8123:localhost:8123 -L 9090:localhost:9090 -L 15672:localhost:15672 -o ProxyCommand="cloudflared access ssh --hostname %h" root@ssh.raidhub.io &
- name: Start pinned local services stack
run: bash ./.github/scripts/setup-local-services-ci.sh

- name: Run Tests
env:
CLIENT_SECRET: "secret-token"
ADMIN_CLIENT_SECRET: "another-secret-token"
JWT_SECRET: "jwt-secret"
POSTGRES_USER: readonly
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_READONLY_PASSWORD }}
CLICKHOUSE_USER: ${{ secrets.CLICKHOUSE_READONLY_USER }}
CLICKHOUSE_PASSWORD: ${{ secrets.CLICKHOUSE_READONLY_PASSWORD }}
POSTGRES_USER: dev
POSTGRES_PASSWORD: password
CLICKHOUSE_USER: default
CLICKHOUSE_PASSWORD: ""
BUNGIE_API_KEY: ${{ secrets.BUNGIE_API_KEY }}
RABBIT_API_USER: ${{ secrets.RABBIT_API_USER }}
RABBIT_API_PASSWORD: ${{ secrets.RABBIT_API_PASSWORD }}
RABBIT_API_USER: dev
RABBIT_API_PASSWORD: password
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
Expand All @@ -57,6 +47,16 @@ jobs:
run: bun test --timeout=30000 --coverage
continue-on-error: true

- name: Dump services logs on failure
if: failure()
run: |
docker compose -f RaidHub-Services/docker-compose.yml --env-file RaidHub-Services/.env ps
docker compose -f RaidHub-Services/docker-compose.yml --env-file RaidHub-Services/.env logs --tail=200 postgres rabbitmq clickhouse

- name: Tear down local services stack
if: always()
run: docker compose -f RaidHub-Services/docker-compose.yml --env-file RaidHub-Services/.env down -v

- name: Upload coverage reports
uses: actions/upload-artifact@v4
with:
Expand Down
42 changes: 21 additions & 21 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- uses: actions/setup-go@v5
with:
go-version: "1.24"

- name: Download code coverage report
uses: dawidd6/action-download-artifact@v3
Expand All @@ -26,45 +29,42 @@ jobs:
workflow_conclusion: success
name: test-coverage.info

- name: Install cloudflared
run: ./.github/scripts/install-cloudflared.sh

- name: Load SSH Key
uses: shimataro/ssh-key-action@v2
with:
key: ${{ secrets.VPS_SSH_PRIVATE_KEY }}
known_hosts: ${{ secrets.VPS_SSH_KNOWN_HOSTS }}

- name: Install dependencies
run: |
bun install --frozen-lockfile

- name: Tunnel to VPS
env:
TUNNEL_SERVICE_TOKEN_ID: ${{ secrets.CF_GHA_CLIENT_ID }}
TUNNEL_SERVICE_TOKEN_SECRET: ${{ secrets.CF_GHA_CLIENT_SECRET }}
run: |
ssh -N -L 5432:localhost:5432 -L 8123:localhost:8123 -L 9090:localhost:9090 -L 15672:localhost:15672 -o ProxyCommand="cloudflared access ssh --hostname %h" root@ssh.raidhub.io &
- name: Start pinned local services stack
run: bash ./.github/scripts/setup-local-services-ci.sh

- name: Run Tests
env:
CLIENT_SECRET: "secret-token"
ADMIN_CLIENT_SECRET: "another-secret-token"
JWT_SECRET: "jwt-secret"
POSTGRES_USER: readonly
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_READONLY_PASSWORD }}
CLICKHOUSE_USER: ${{ secrets.CLICKHOUSE_READONLY_USER }}
CLICKHOUSE_PASSWORD: ${{ secrets.CLICKHOUSE_READONLY_PASSWORD }}
POSTGRES_USER: dev
POSTGRES_PASSWORD: password
CLICKHOUSE_USER: default
CLICKHOUSE_PASSWORD: ""
BUNGIE_API_KEY: ${{ secrets.BUNGIE_API_KEY }}
RABBIT_API_USER: ${{ secrets.RABBIT_API_USER }}
RABBIT_API_PASSWORD: ${{ secrets.RABBIT_API_PASSWORD }}
RABBIT_API_USER: dev
RABBIT_API_PASSWORD: password
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
R2_BUCKET: ${{ secrets.R2_BUCKET }}

run: bun test --coverage --bail=5 --timeout=20000

- name: Dump services logs on failure
if: failure()
run: |
docker compose -f RaidHub-Services/docker-compose.yml --env-file RaidHub-Services/.env ps
docker compose -f RaidHub-Services/docker-compose.yml --env-file RaidHub-Services/.env logs --tail=200 postgres rabbitmq clickhouse

- name: Tear down local services stack
if: always()
run: docker compose -f RaidHub-Services/docker-compose.yml --env-file RaidHub-Services/.env down -v

# Compares two code coverage files and generates report as a comment
- name: Generate Code Coverage report
id: code-coverage
Expand Down
39 changes: 24 additions & 15 deletions src/integrations/clickhouse/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { LogFields, Logger } from "@/lib/utils/logging"
import type { ClickHouseClient } from "@clickhouse/client"
import {
ClickHouseLogLevel,
ErrorLogParams,
Expand All @@ -11,66 +12,74 @@
class ClickhouseLogger implements ClickhouseLoggingInterface {
private logger = new Logger("CLICKHOUSE")

trace({ module, message, args }: LogParams) {
this.logger.debug("CLICKHOUSE_TRACE", {
module,
message,
...(args ? { args: args as LogFields } : {})

Check warning on line 19 in src/integrations/clickhouse/client.ts

View workflow job for this annotation

GitHub Actions / test

15-19 lines are not covered with tests
})
}
debug({ module, message, args }: LogParams) {
this.logger.debug("CLICKHOUSE_DEBUG", {
module,
message,
...(args ? { args: args as LogFields } : {})

Check warning on line 26 in src/integrations/clickhouse/client.ts

View workflow job for this annotation

GitHub Actions / test

22-26 lines are not covered with tests
})
}
info({ module, message, args }: LogParams) {
this.logger.info("CLICKHOUSE_INFO", {
module,
message,
...(args ? { args: args as LogFields } : {})

Check warning on line 33 in src/integrations/clickhouse/client.ts

View workflow job for this annotation

GitHub Actions / test

29-33 lines are not covered with tests
})
}
warn({ module, message, args, err }: WarnLogParams) {
this.logger.warn("CLICKHOUSE_WARN", err ?? null, {
module,
message,
...(args ? { args: args as LogFields } : {})

Check warning on line 40 in src/integrations/clickhouse/client.ts

View workflow job for this annotation

GitHub Actions / test

36-40 lines are not covered with tests
})
}
error({ module, message, args, err }: ErrorLogParams) {
if (!err) {
// ClickHouse error without Error object - create one
const error = new Error(message || "ClickHouse error")
this.logger.error("CLICKHOUSE_ERROR", error, {
module,
message,
...(args ? { args: args as LogFields } : {})
})
} else {
this.logger.error("CLICKHOUSE_ERROR", err, {
module,
message,
...(args ? { args: args as LogFields } : {})

Check warning on line 56 in src/integrations/clickhouse/client.ts

View workflow job for this annotation

GitHub Actions / test

43-56 lines are not covered with tests
})
}
}
}

export const clickhouse = createClient({
username: process.env.CLICKHOUSE_USER,
password: process.env.CLICKHOUSE_PASSWORD,
application: process.env.PROD ? "RaidHub-API-Prod" : "RaidHub-API-Dev",
database: process.env.CLICKHOUSE_DATABASE ?? "default",
request_timeout: 5000,
log: {
LoggerClass: ClickhouseLogger,
level:
process.env.NODE_ENV === "test"
? ClickHouseLogLevel.OFF
: process.env.PROD
? ClickHouseLogLevel.WARN
: ClickHouseLogLevel.DEBUG
let clickhouseClient: ClickHouseClient | null = null

export const getClickhouseClient = () => {
if (!clickhouseClient) {
clickhouseClient = createClient({
username: process.env.CLICKHOUSE_USER,
password: process.env.CLICKHOUSE_PASSWORD,
application: process.env.PROD ? "RaidHub-API-Prod" : "RaidHub-API-Dev",
database: process.env.CLICKHOUSE_DATABASE ?? "default",
request_timeout: 5000,
log: {
LoggerClass: ClickhouseLogger,
level:
process.env.NODE_ENV === "test"
? ClickHouseLogLevel.OFF
: process.env.PROD
? ClickHouseLogLevel.WARN
: ClickHouseLogLevel.DEBUG
}
})
}
})

return clickhouseClient
}
66 changes: 47 additions & 19 deletions src/integrations/postgres/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,50 @@

configurePostgresParsers()

export const pgReader = createReader({
user: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
database: "raidhub",
min: process.env.PROD ? 5 : 1,
max: process.env.PROD ? 150 : 10,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 10000
})

export const pgAdmin = createTransactional({
user: process.env.POSTGRES_WRITABLE_USER,
password: process.env.POSTGRES_WRITABLE_PASSWORD,
database: "raidhub",
min: process.env.PROD ? 2 : 1,
max: process.env.PROD ? 15 : 3,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 10000
})
let pgReaderClient: ReturnType<typeof createReader> | null = null
let pgAdminClient: ReturnType<typeof createTransactional> | null = null

const getPgReader = () => {
if (!pgReaderClient) {
pgReaderClient = createReader({
user: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
database: "raidhub",
min: process.env.PROD ? 5 : 1,
max: process.env.PROD ? 150 : 10,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 10000
})
}

return pgReaderClient
}

const getPgAdmin = () => {
if (!pgAdminClient) {
pgAdminClient = createTransactional({
user: process.env.POSTGRES_WRITABLE_USER,
password: process.env.POSTGRES_WRITABLE_PASSWORD,
database: "raidhub",
min: process.env.PROD ? 2 : 1,
max: process.env.PROD ? 15 : 3,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 10000
})
}

Check warning on line 38 in src/integrations/postgres/index.ts

View workflow job for this annotation

GitHub Actions / test

26-38 lines are not covered with tests
return pgAdminClient
}

export const pgReader: ReturnType<typeof createReader> = {
queryRow: (sql, options) => getPgReader().queryRow(sql, options),
queryRows: (sql, options) => getPgReader().queryRows(sql, options),
prepare: sql => getPgReader().prepare(sql)
}

export const pgAdmin: ReturnType<typeof createTransactional> = {
queryRow: (sql, options) => getPgAdmin().queryRow(sql, options),
queryRows: (sql, options) => getPgAdmin().queryRows(sql, options),
prepare: sql => getPgAdmin().prepare(sql),
transaction: callback => getPgAdmin().transaction(callback)
}
Loading
Loading