mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
ci: consolidate E2E into pipeline and gate image builds on test suite
This commit is contained in:
@@ -216,3 +216,243 @@ jobs:
|
||||
echo "dependency-review=${{ needs.dependency-review.result }}"
|
||||
echo "detect-changes=${{ needs.detect-changes.result }}"
|
||||
echo "checks=${{ needs.checks.result }}"
|
||||
|
||||
e2e-setup:
|
||||
name: Detect E2E Suites
|
||||
runs-on: ubuntu-latest
|
||||
needs: [quality-gate]
|
||||
if: needs.quality-gate.result == 'success'
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
matrix: ${{ steps.build-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Detect path changes per suite
|
||||
id: changes
|
||||
if: github.event_name != 'workflow_dispatch'
|
||||
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4
|
||||
with:
|
||||
filters: |
|
||||
workflow-changed:
|
||||
- '.github/workflows/ci.yml'
|
||||
- '.github/workflows/e2e.yml'
|
||||
- '.github/workflows/e2e-runner.yml'
|
||||
- 'Dockerfile'
|
||||
- 'docker-compose*.yml'
|
||||
- 'docker/**'
|
||||
app-smoke:
|
||||
- 'server/src/**'
|
||||
- 'server/test/app-smoke.e2e-spec.ts'
|
||||
- 'server/src/db/schema/**'
|
||||
- 'packages/types/src/**'
|
||||
- 'scripts/test/**'
|
||||
- 'scripts/db/**'
|
||||
- '.github/workflows/e2e-runner.yml'
|
||||
auth-session-security:
|
||||
- 'server/src/modules/auth/**'
|
||||
- 'server/src/modules/user/**'
|
||||
- 'server/src/modules/role/**'
|
||||
- 'server/src/common/guards/**'
|
||||
- 'server/src/common/decorators/**'
|
||||
- 'server/src/common/types/**'
|
||||
- 'server/src/db/schema/**'
|
||||
- 'server/test/auth-session-security.e2e-spec.ts'
|
||||
- 'server/test/e2e/app-harness.ts'
|
||||
- 'packages/types/src/auth.ts'
|
||||
- 'packages/types/src/permissions.ts'
|
||||
- 'scripts/test/**'
|
||||
- 'scripts/db/**'
|
||||
- '.github/workflows/e2e-runner.yml'
|
||||
auth-recovery-oidc-logout:
|
||||
- 'server/src/modules/auth/**'
|
||||
- 'server/src/modules/user/**'
|
||||
- 'server/src/modules/email/**'
|
||||
- 'server/src/modules/metadata/**'
|
||||
- 'server/src/db/schema/**'
|
||||
- 'server/test/auth-recovery-oidc-logout.e2e-spec.ts'
|
||||
- 'server/test/e2e/app-harness.ts'
|
||||
- 'packages/types/src/auth.ts'
|
||||
- 'scripts/test/**'
|
||||
- 'scripts/db/**'
|
||||
- '.github/workflows/e2e-runner.yml'
|
||||
authorization-matrix:
|
||||
- 'server/src/**'
|
||||
- 'server/test/authorization-matrix.e2e-spec.ts'
|
||||
- 'server/test/e2e/authorization-matrix/**'
|
||||
- 'packages/types/src/**'
|
||||
- 'scripts/test/**'
|
||||
- 'scripts/db/**'
|
||||
- '.github/workflows/e2e-runner.yml'
|
||||
email-lifecycle:
|
||||
- 'server/src/modules/email/**'
|
||||
- 'server/src/modules/book/**'
|
||||
- 'server/src/modules/library/**'
|
||||
- 'server/src/modules/auth/**'
|
||||
- 'server/src/modules/cover/**'
|
||||
- 'server/src/modules/scanner/**'
|
||||
- 'server/src/modules/seed/**'
|
||||
- 'server/src/db/schema/**'
|
||||
- 'packages/types/src/**'
|
||||
- 'server/test/email-lifecycle.e2e-spec.ts'
|
||||
- 'server/test/e2e/email/**'
|
||||
- 'server/test/e2e/**'
|
||||
- 'scripts/test/**'
|
||||
- 'scripts/db/**'
|
||||
- 'client/src/features/email/**'
|
||||
- 'client/src/router/index.ts'
|
||||
- 'client/src/features/settings/SettingsHeader.vue'
|
||||
- 'client/src/views/HomeView.vue'
|
||||
- 'client/src/views/CollectionView.vue'
|
||||
- 'client/src/views/LensView.vue'
|
||||
- 'client/src/features/book/components/BookCoverCard.vue'
|
||||
- 'client/src/features/book/components/BookListRow.vue'
|
||||
metadata-write:
|
||||
- 'server/src/modules/book/**'
|
||||
- 'server/src/modules/library/**'
|
||||
- 'server/src/modules/file-write/**'
|
||||
- 'server/src/modules/metadata/**'
|
||||
- 'server/src/modules/app-settings/**'
|
||||
- 'server/test/metadata-write.e2e-spec.ts'
|
||||
- 'server/test/e2e/metadata-write/**'
|
||||
- 'server/src/db/schema/**'
|
||||
- 'packages/types/src/file-write.ts'
|
||||
- 'scripts/test/**'
|
||||
- 'scripts/db/**'
|
||||
opds-auth-catalog:
|
||||
- 'server/src/modules/opds/**'
|
||||
- 'server/src/modules/book/**'
|
||||
- 'server/src/modules/library/**'
|
||||
- 'server/src/modules/collection/**'
|
||||
- 'server/src/modules/lens/**'
|
||||
- 'server/src/modules/user/**'
|
||||
- 'server/src/modules/auth/**'
|
||||
- 'server/src/modules/app-settings/**'
|
||||
- 'server/test/opds-auth-catalog.e2e-spec.ts'
|
||||
- 'server/test/e2e/opds/**'
|
||||
- 'server/test/e2e/**'
|
||||
- 'server/src/db/schema/**'
|
||||
- 'packages/types/src/**'
|
||||
- 'scripts/test/**'
|
||||
- 'scripts/db/**'
|
||||
reader-state-isolation:
|
||||
- 'server/src/modules/bookmark/**'
|
||||
- 'server/src/modules/annotation/**'
|
||||
- 'server/src/modules/reader-preferences/**'
|
||||
- 'server/src/modules/reading-session/**'
|
||||
- 'server/src/modules/book/**'
|
||||
- 'server/src/modules/library/**'
|
||||
- 'server/src/modules/scanner/**'
|
||||
- 'server/src/modules/user-book-status/**'
|
||||
- 'server/src/scripts/db-prepare-e2e.ts'
|
||||
- 'server/test/reader-state-isolation.e2e-spec.ts'
|
||||
- 'server/test/e2e/reader-state-isolation/**'
|
||||
- 'server/src/db/schema/**'
|
||||
- 'packages/types/src/reader-settings.ts'
|
||||
- 'scripts/test/**'
|
||||
- 'scripts/db/**'
|
||||
scanner-file-operations:
|
||||
- 'server/src/modules/scanner/**'
|
||||
- 'server/test/scanner-file-operations.e2e-spec.ts'
|
||||
- 'server/test/e2e/scanner/**'
|
||||
- 'server/src/db/schema/**'
|
||||
- 'scripts/test/**'
|
||||
- 'scripts/db/**'
|
||||
- '.github/workflows/e2e-runner.yml'
|
||||
scanner-scenarios:
|
||||
- 'server/src/modules/scanner/**'
|
||||
- 'server/test/scanner-scenarios.e2e-spec.ts'
|
||||
- 'server/test/e2e/scanner/**'
|
||||
- 'server/src/db/schema/**'
|
||||
- 'scripts/test/**'
|
||||
- 'scripts/db/**'
|
||||
- '.github/workflows/e2e-runner.yml'
|
||||
staging-ingest-finalize:
|
||||
- 'server/src/modules/staging/**'
|
||||
- 'server/src/modules/upload/**'
|
||||
- 'server/src/modules/metadata/**'
|
||||
- 'server/test/staging-ingest-finalize.e2e-spec.ts'
|
||||
- 'server/test/e2e/staging/**'
|
||||
- 'server/src/db/schema/**'
|
||||
- 'scripts/test/**'
|
||||
- 'scripts/db/**'
|
||||
users-admin-lifecycle:
|
||||
- 'server/src/modules/user/**'
|
||||
- 'server/src/modules/auth/**'
|
||||
- 'server/src/common/guards/**'
|
||||
- 'server/src/common/decorators/**'
|
||||
- 'server/src/common/types/**'
|
||||
- 'server/src/db/schema/**'
|
||||
- 'server/test/users-admin-lifecycle.e2e-spec.ts'
|
||||
- 'server/test/e2e/users-admin-lifecycle/**'
|
||||
- 'packages/types/src/auth.ts'
|
||||
- 'packages/types/src/permissions.ts'
|
||||
- 'scripts/test/**'
|
||||
- 'scripts/db/**'
|
||||
- '.github/workflows/e2e-runner.yml'
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Build suite matrix
|
||||
id: build-matrix
|
||||
shell: bash
|
||||
run: |
|
||||
SUITES_DEF=$(node scripts/test/list-suites-json.mjs)
|
||||
|
||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
||||
MATRIX="$SUITES_DEF"
|
||||
elif [ "${{ steps.changes.outputs.workflow-changed }}" == "true" ]; then
|
||||
MATRIX="$SUITES_DEF"
|
||||
else
|
||||
CHANGED='${{ steps.changes.outputs.changes }}'
|
||||
if [ -z "$CHANGED" ] || [ "$CHANGED" == "[]" ]; then
|
||||
MATRIX="[]"
|
||||
else
|
||||
MATRIX=$(echo "$SUITES_DEF" | jq --argjson changed "$CHANGED" \
|
||||
'[.[] | select(.id as $id | $changed | index($id) != null)]')
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "matrix=$(echo "$MATRIX" | jq -c .)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
e2e:
|
||||
needs: [e2e-setup]
|
||||
if: needs.e2e-setup.outputs.matrix != '[]'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
suite: ${{ fromJSON(needs.e2e-setup.outputs.matrix) }}
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
uses: ./.github/workflows/e2e-runner.yml
|
||||
with:
|
||||
suite-id: ${{ matrix.suite.id }}
|
||||
check-name: "E2E - ${{ matrix.suite.name }}"
|
||||
artifact-name: "e2e-${{ matrix.suite.id }}-report"
|
||||
job-timeout-minutes: ${{ matrix.suite.timeout }}
|
||||
|
||||
ci-gate:
|
||||
name: CI Gate
|
||||
runs-on: ubuntu-latest
|
||||
needs: [quality-gate, e2e-setup, e2e]
|
||||
timeout-minutes: 5
|
||||
if: always()
|
||||
steps:
|
||||
- name: Fail if any job failed or was cancelled
|
||||
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
|
||||
run: |
|
||||
echo "One or more CI jobs failed or were cancelled."
|
||||
exit 1
|
||||
|
||||
- name: CI complete
|
||||
run: |
|
||||
echo "quality-gate=${{ needs.quality-gate.result }}"
|
||||
echo "e2e-setup=${{ needs.e2e-setup.result }}"
|
||||
echo "e2e=${{ needs.e2e.result }}"
|
||||
|
||||
@@ -2,6 +2,7 @@ name: Container Image
|
||||
run-name: ${{ github.event_name == 'workflow_run' && format('Image - {0} ({1})', github.event.workflow_run.head_commit.message, github.event.workflow_run.head_branch) || format('Image - {0}', github.ref_name) }}
|
||||
|
||||
on:
|
||||
# Break-glass: bypasses CI/E2E gate. Use only when CI is broken and a hotfix image is urgently needed.
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: ["CI"]
|
||||
@@ -85,7 +86,7 @@ jobs:
|
||||
set -euo pipefail
|
||||
echo "Checking CI status for commit ${COMMIT_SHA}..."
|
||||
success_count=$(gh api --paginate \
|
||||
"repos/${GH_REPO}/actions/workflows/ci.yml/runs?head_sha=${COMMIT_SHA}&status=completed" \
|
||||
"repos/${GH_REPO}/actions/workflows/ci.yml/runs?head_sha=${COMMIT_SHA}&status=completed&event=push&branch=main" \
|
||||
--jq '[.workflow_runs[] | select(.conclusion == "success")] | length' \
|
||||
| awk '{sum += $1} END {print sum}')
|
||||
if [[ "${success_count:-0}" -lt 1 ]]; then
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
name: E2E - App Smoke
|
||||
run-name: ${{ format('E2E - App Smoke - {0}', github.ref_name) }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "server/src/**"
|
||||
- "server/test/app-smoke.e2e-spec.ts"
|
||||
- "server/src/db/schema/**"
|
||||
- "packages/types/src/**"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- ".github/workflows/e2e-runner.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "server/src/**"
|
||||
- "server/test/app-smoke.e2e-spec.ts"
|
||||
- "server/src/db/schema/**"
|
||||
- "packages/types/src/**"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- ".github/workflows/e2e-runner.yml"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
app-smoke:
|
||||
uses: ./.github/workflows/e2e-runner.yml
|
||||
with:
|
||||
suite-id: app-smoke
|
||||
check-name: E2E - App Smoke
|
||||
artifact-name: e2e-app-smoke-report
|
||||
job-timeout-minutes: 30
|
||||
@@ -1,48 +0,0 @@
|
||||
name: E2E - Auth Recovery OIDC Logout
|
||||
run-name: ${{ format('E2E - Auth Recovery OIDC Logout - {0}', github.ref_name) }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "server/src/modules/auth/**"
|
||||
- "server/src/modules/user/**"
|
||||
- "server/src/modules/email/**"
|
||||
- "server/src/modules/metadata/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "server/test/auth-recovery-oidc-logout.e2e-spec.ts"
|
||||
- "server/test/e2e/app-harness.ts"
|
||||
- "packages/types/src/auth.ts"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- ".github/workflows/e2e-runner.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "server/src/modules/auth/**"
|
||||
- "server/src/modules/user/**"
|
||||
- "server/src/modules/email/**"
|
||||
- "server/src/modules/metadata/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "server/test/auth-recovery-oidc-logout.e2e-spec.ts"
|
||||
- "server/test/e2e/app-harness.ts"
|
||||
- "packages/types/src/auth.ts"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- ".github/workflows/e2e-runner.yml"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
auth-recovery-oidc-logout:
|
||||
uses: ./.github/workflows/e2e-runner.yml
|
||||
with:
|
||||
suite-id: auth-recovery-oidc-logout
|
||||
check-name: E2E - Auth Recovery OIDC Logout
|
||||
artifact-name: e2e-auth-recovery-oidc-logout-report
|
||||
job-timeout-minutes: 50
|
||||
@@ -1,54 +0,0 @@
|
||||
name: E2E - Auth Session Security
|
||||
run-name: ${{ format('E2E - Auth Session Security - {0}', github.ref_name) }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "server/src/modules/auth/**"
|
||||
- "server/src/modules/user/**"
|
||||
- "server/src/modules/role/**"
|
||||
- "server/src/common/guards/**"
|
||||
- "server/src/common/decorators/**"
|
||||
- "server/src/common/types/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "server/test/auth-session-security.e2e-spec.ts"
|
||||
- "server/test/e2e/app-harness.ts"
|
||||
- "packages/types/src/auth.ts"
|
||||
- "packages/types/src/permissions.ts"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- ".github/workflows/e2e-runner.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "server/src/modules/auth/**"
|
||||
- "server/src/modules/user/**"
|
||||
- "server/src/modules/role/**"
|
||||
- "server/src/common/guards/**"
|
||||
- "server/src/common/decorators/**"
|
||||
- "server/src/common/types/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "server/test/auth-session-security.e2e-spec.ts"
|
||||
- "server/test/e2e/app-harness.ts"
|
||||
- "packages/types/src/auth.ts"
|
||||
- "packages/types/src/permissions.ts"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- ".github/workflows/e2e-runner.yml"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
auth-session-security:
|
||||
uses: ./.github/workflows/e2e-runner.yml
|
||||
with:
|
||||
suite-id: auth-session-security
|
||||
check-name: E2E - Auth Session Security
|
||||
artifact-name: e2e-auth-session-security-report
|
||||
job-timeout-minutes: 50
|
||||
@@ -1,42 +0,0 @@
|
||||
name: E2E - Authorization Matrix
|
||||
run-name: ${{ format('E2E - Authorization Matrix - {0}', github.ref_name) }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "server/src/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "server/test/authorization-matrix.e2e-spec.ts"
|
||||
- "server/test/e2e/authorization-matrix/**"
|
||||
- "packages/types/src/**"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- ".github/workflows/e2e-runner.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "server/src/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "server/test/authorization-matrix.e2e-spec.ts"
|
||||
- "server/test/e2e/authorization-matrix/**"
|
||||
- "packages/types/src/**"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- ".github/workflows/e2e-runner.yml"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
authorization-matrix:
|
||||
uses: ./.github/workflows/e2e-runner.yml
|
||||
with:
|
||||
suite-id: authorization-matrix
|
||||
check-name: E2E - Authorization Matrix
|
||||
artifact-name: e2e-authorization-matrix-report
|
||||
job-timeout-minutes: 60
|
||||
@@ -1,70 +0,0 @@
|
||||
name: E2E - Email Lifecycle
|
||||
run-name: ${{ format('E2E - Email Lifecycle - {0}', github.ref_name) }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "server/src/modules/email/**"
|
||||
- "server/src/modules/book/**"
|
||||
- "server/src/modules/library/**"
|
||||
- "server/src/modules/auth/**"
|
||||
- "server/src/modules/cover/**"
|
||||
- "server/src/modules/scanner/**"
|
||||
- "server/src/modules/seed/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "packages/types/src/**"
|
||||
- "server/test/email-lifecycle.e2e-spec.ts"
|
||||
- "server/test/e2e/email/**"
|
||||
- "server/test/e2e/**"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- "client/src/features/email/**"
|
||||
- "client/src/router/index.ts"
|
||||
- "client/src/features/settings/SettingsHeader.vue"
|
||||
- "client/src/views/HomeView.vue"
|
||||
- "client/src/views/CollectionView.vue"
|
||||
- "client/src/views/LensView.vue"
|
||||
- "client/src/features/book/components/BookCoverCard.vue"
|
||||
- "client/src/features/book/components/BookListRow.vue"
|
||||
pull_request:
|
||||
paths:
|
||||
- "server/src/modules/email/**"
|
||||
- "server/src/modules/book/**"
|
||||
- "server/src/modules/library/**"
|
||||
- "server/src/modules/auth/**"
|
||||
- "server/src/modules/cover/**"
|
||||
- "server/src/modules/scanner/**"
|
||||
- "server/src/modules/seed/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "packages/types/src/**"
|
||||
- "server/test/email-lifecycle.e2e-spec.ts"
|
||||
- "server/test/e2e/email/**"
|
||||
- "server/test/e2e/**"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- "client/src/features/email/**"
|
||||
- "client/src/router/index.ts"
|
||||
- "client/src/features/settings/SettingsHeader.vue"
|
||||
- "client/src/views/HomeView.vue"
|
||||
- "client/src/views/CollectionView.vue"
|
||||
- "client/src/views/LensView.vue"
|
||||
- "client/src/features/book/components/BookCoverCard.vue"
|
||||
- "client/src/features/book/components/BookListRow.vue"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
email-lifecycle:
|
||||
uses: ./.github/workflows/e2e-runner.yml
|
||||
with:
|
||||
suite-id: email-lifecycle
|
||||
check-name: E2E - Email Lifecycle
|
||||
artifact-name: e2e-email-lifecycle-report
|
||||
job-timeout-minutes: 45
|
||||
@@ -1,55 +0,0 @@
|
||||
name: E2E - Metadata Write
|
||||
run-name: ${{ format('E2E - Metadata Write - {0}', github.ref_name) }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
vitest-args:
|
||||
description: 'Optional vitest args (examples: --shard=1/2, --testNamePattern="concurrency hardening")'
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
push:
|
||||
paths:
|
||||
- "server/src/modules/book/**"
|
||||
- "server/src/modules/library/**"
|
||||
- "server/src/modules/file-write/**"
|
||||
- "server/src/modules/metadata/**"
|
||||
- "server/src/modules/app-settings/**"
|
||||
- "server/test/metadata-write.e2e-spec.ts"
|
||||
- "server/test/e2e/metadata-write/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "packages/types/src/file-write.ts"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "server/src/modules/book/**"
|
||||
- "server/src/modules/library/**"
|
||||
- "server/src/modules/file-write/**"
|
||||
- "server/src/modules/metadata/**"
|
||||
- "server/src/modules/app-settings/**"
|
||||
- "server/test/metadata-write.e2e-spec.ts"
|
||||
- "server/test/e2e/metadata-write/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "packages/types/src/file-write.ts"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
metadata-write:
|
||||
uses: ./.github/workflows/e2e-runner.yml
|
||||
with:
|
||||
suite-id: metadata-write
|
||||
check-name: E2E - Metadata Write
|
||||
artifact-name: e2e-metadata-write-report
|
||||
job-timeout-minutes: 60
|
||||
vitest-args: ${{ github.event_name == 'workflow_dispatch' && inputs.vitest-args || '' }}
|
||||
@@ -1,56 +0,0 @@
|
||||
name: E2E - OPDS Auth Catalog
|
||||
run-name: ${{ format('E2E - OPDS Auth Catalog - {0}', github.ref_name) }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "server/src/modules/opds/**"
|
||||
- "server/src/modules/book/**"
|
||||
- "server/src/modules/library/**"
|
||||
- "server/src/modules/collection/**"
|
||||
- "server/src/modules/lens/**"
|
||||
- "server/src/modules/user/**"
|
||||
- "server/src/modules/auth/**"
|
||||
- "server/src/modules/app-settings/**"
|
||||
- "server/test/opds-auth-catalog.e2e-spec.ts"
|
||||
- "server/test/e2e/opds/**"
|
||||
- "server/test/e2e/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "packages/types/src/**"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "server/src/modules/opds/**"
|
||||
- "server/src/modules/book/**"
|
||||
- "server/src/modules/library/**"
|
||||
- "server/src/modules/collection/**"
|
||||
- "server/src/modules/lens/**"
|
||||
- "server/src/modules/user/**"
|
||||
- "server/src/modules/auth/**"
|
||||
- "server/src/modules/app-settings/**"
|
||||
- "server/test/opds-auth-catalog.e2e-spec.ts"
|
||||
- "server/test/e2e/opds/**"
|
||||
- "server/test/e2e/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "packages/types/src/**"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
opds-auth-catalog:
|
||||
uses: ./.github/workflows/e2e-runner.yml
|
||||
with:
|
||||
suite-id: opds-auth-catalog
|
||||
check-name: E2E - OPDS Auth Catalog
|
||||
artifact-name: e2e-opds-auth-catalog-report
|
||||
job-timeout-minutes: 45
|
||||
@@ -1,56 +0,0 @@
|
||||
name: E2E - Reader State Isolation
|
||||
run-name: ${{ format('E2E - Reader State Isolation - {0}', github.ref_name) }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "server/src/modules/bookmark/**"
|
||||
- "server/src/modules/annotation/**"
|
||||
- "server/src/modules/reader-preferences/**"
|
||||
- "server/src/modules/reading-session/**"
|
||||
- "server/src/modules/book/**"
|
||||
- "server/src/modules/library/**"
|
||||
- "server/src/modules/scanner/**"
|
||||
- "server/src/modules/user-book-status/**"
|
||||
- "server/src/scripts/db-prepare-e2e.ts"
|
||||
- "server/test/reader-state-isolation.e2e-spec.ts"
|
||||
- "server/test/e2e/reader-state-isolation/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "packages/types/src/reader-settings.ts"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "server/src/modules/bookmark/**"
|
||||
- "server/src/modules/annotation/**"
|
||||
- "server/src/modules/reader-preferences/**"
|
||||
- "server/src/modules/reading-session/**"
|
||||
- "server/src/modules/book/**"
|
||||
- "server/src/modules/library/**"
|
||||
- "server/src/modules/scanner/**"
|
||||
- "server/src/modules/user-book-status/**"
|
||||
- "server/src/scripts/db-prepare-e2e.ts"
|
||||
- "server/test/reader-state-isolation.e2e-spec.ts"
|
||||
- "server/test/e2e/reader-state-isolation/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "packages/types/src/reader-settings.ts"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
reader-state-isolation:
|
||||
uses: ./.github/workflows/e2e-runner.yml
|
||||
with:
|
||||
suite-id: reader-state-isolation
|
||||
check-name: E2E - Reader State Isolation
|
||||
artifact-name: e2e-reader-state-isolation-report
|
||||
job-timeout-minutes: 45
|
||||
@@ -1,42 +0,0 @@
|
||||
name: E2E - Scanner File Operations
|
||||
run-name: ${{ format('E2E - Scanner File Operations - {0}', github.ref_name) }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 4 * * *"
|
||||
push:
|
||||
paths:
|
||||
- "server/src/modules/scanner/**"
|
||||
- "server/test/scanner-file-operations.e2e-spec.ts"
|
||||
- "server/test/e2e/scanner/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- ".github/workflows/e2e-runner.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "server/src/modules/scanner/**"
|
||||
- "server/test/scanner-file-operations.e2e-spec.ts"
|
||||
- "server/test/e2e/scanner/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- ".github/workflows/e2e-runner.yml"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
scanner-file-operations:
|
||||
uses: ./.github/workflows/e2e-runner.yml
|
||||
with:
|
||||
suite-id: scanner-file-operations
|
||||
check-name: E2E - Scanner File Operations
|
||||
artifact-name: e2e-scanner-file-operations-report
|
||||
job-timeout-minutes: 60
|
||||
@@ -1,40 +0,0 @@
|
||||
name: E2E - Scanner Scenarios
|
||||
run-name: ${{ format('E2E - Scanner Scenarios - {0}', github.ref_name) }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "server/src/modules/scanner/**"
|
||||
- "server/test/scanner-scenarios.e2e-spec.ts"
|
||||
- "server/test/e2e/scanner/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- ".github/workflows/e2e-runner.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "server/src/modules/scanner/**"
|
||||
- "server/test/scanner-scenarios.e2e-spec.ts"
|
||||
- "server/test/e2e/scanner/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- ".github/workflows/e2e-runner.yml"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
scanner-scenarios:
|
||||
uses: ./.github/workflows/e2e-runner.yml
|
||||
with:
|
||||
suite-id: scanner-scenarios
|
||||
check-name: E2E - Scanner Scenarios
|
||||
artifact-name: e2e-scanner-scenarios-report
|
||||
job-timeout-minutes: 45
|
||||
@@ -1,42 +0,0 @@
|
||||
name: E2E - Staging Ingest Finalize
|
||||
run-name: ${{ format('E2E - Staging Ingest Finalize - {0}', github.ref_name) }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "server/src/modules/staging/**"
|
||||
- "server/src/modules/upload/**"
|
||||
- "server/src/modules/metadata/**"
|
||||
- "server/test/staging-ingest-finalize.e2e-spec.ts"
|
||||
- "server/test/e2e/staging/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "server/src/modules/staging/**"
|
||||
- "server/src/modules/upload/**"
|
||||
- "server/src/modules/metadata/**"
|
||||
- "server/test/staging-ingest-finalize.e2e-spec.ts"
|
||||
- "server/test/e2e/staging/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
staging-ingest-finalize:
|
||||
uses: ./.github/workflows/e2e-runner.yml
|
||||
with:
|
||||
suite-id: staging-ingest-finalize
|
||||
check-name: E2E - Staging Ingest Finalize
|
||||
artifact-name: e2e-staging-ingest-finalize-report
|
||||
job-timeout-minutes: 45
|
||||
@@ -1,52 +0,0 @@
|
||||
name: E2E - Users Admin Lifecycle
|
||||
run-name: ${{ format('E2E - Users Admin Lifecycle - {0}', github.ref_name) }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "server/src/modules/user/**"
|
||||
- "server/src/modules/auth/**"
|
||||
- "server/src/common/guards/**"
|
||||
- "server/src/common/decorators/**"
|
||||
- "server/src/common/types/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "server/test/users-admin-lifecycle.e2e-spec.ts"
|
||||
- "server/test/e2e/users-admin-lifecycle/**"
|
||||
- "packages/types/src/auth.ts"
|
||||
- "packages/types/src/permissions.ts"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- ".github/workflows/e2e-runner.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "server/src/modules/user/**"
|
||||
- "server/src/modules/auth/**"
|
||||
- "server/src/common/guards/**"
|
||||
- "server/src/common/decorators/**"
|
||||
- "server/src/common/types/**"
|
||||
- "server/src/db/schema/**"
|
||||
- "server/test/users-admin-lifecycle.e2e-spec.ts"
|
||||
- "server/test/e2e/users-admin-lifecycle/**"
|
||||
- "packages/types/src/auth.ts"
|
||||
- "packages/types/src/permissions.ts"
|
||||
- "scripts/test/**"
|
||||
- "scripts/db/**"
|
||||
- ".github/workflows/e2e-runner.yml"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
users-admin-lifecycle:
|
||||
uses: ./.github/workflows/e2e-runner.yml
|
||||
with:
|
||||
suite-id: users-admin-lifecycle
|
||||
check-name: E2E - Users Admin Lifecycle
|
||||
artifact-name: e2e-users-admin-lifecycle-report
|
||||
job-timeout-minutes: 50
|
||||
@@ -0,0 +1,57 @@
|
||||
name: E2E (Manual)
|
||||
run-name: E2E (dispatch) - ${{ github.ref_name }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
vitest-args:
|
||||
description: 'Optional vitest args forwarded to all suites (e.g. --testNamePattern="some test")'
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
|
||||
env:
|
||||
NODE_VERSION: "24"
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
name: Build Suite Matrix
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
matrix: ${{ steps.build.outputs.matrix }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Build matrix
|
||||
id: build
|
||||
shell: bash
|
||||
run: echo "matrix=$(node scripts/test/list-suites-json.mjs)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
e2e:
|
||||
needs: setup
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
suite: ${{ fromJSON(needs.setup.outputs.matrix) }}
|
||||
uses: ./.github/workflows/e2e-runner.yml
|
||||
with:
|
||||
suite-id: ${{ matrix.suite.id }}
|
||||
check-name: "E2E - ${{ matrix.suite.name }}"
|
||||
artifact-name: "e2e-${{ matrix.suite.id }}-report"
|
||||
job-timeout-minutes: ${{ matrix.suite.timeout }}
|
||||
vitest-args: ${{ inputs.vitest-args || '' }}
|
||||
@@ -1,8 +1,8 @@
|
||||
# ProjectX
|
||||
|
||||
A book and library management app with Kobo device support. Multi-user, self-hostable.
|
||||
[](https://github.com/neonsolstice/projectx/actions/workflows/ci.yml)
|
||||
|
||||
**Tech stack:** NestJS 11 (Fastify) + Vue 3 + PostgreSQL + Drizzle ORM
|
||||
A self-hostable book and library management app with Kobo device support. Organize your epub, pdf, and cbz collections, track reading progress, and sync with your Kobo.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ const TEST_RESULTS_DIR = "../test-results/server";
|
||||
export const E2E_SUITES = Object.freeze({
|
||||
"app-smoke": {
|
||||
id: "app-smoke",
|
||||
name: "App Smoke",
|
||||
timeout: 30,
|
||||
description: "App smoke suite",
|
||||
vitestTarget: "test/app-smoke.e2e-spec.ts",
|
||||
junitOutput: `${TEST_RESULTS_DIR}/app-smoke-e2e-junit.xml`,
|
||||
@@ -11,6 +13,8 @@ export const E2E_SUITES = Object.freeze({
|
||||
},
|
||||
"scanner-scenarios": {
|
||||
id: "scanner-scenarios",
|
||||
name: "Scanner Scenarios",
|
||||
timeout: 45,
|
||||
description: "Scanner scenario matrix",
|
||||
vitestTarget: "test/scanner-scenarios.e2e-spec.ts",
|
||||
junitOutput: `${TEST_RESULTS_DIR}/scanner-scenarios-e2e-junit.xml`,
|
||||
@@ -19,6 +23,8 @@ export const E2E_SUITES = Object.freeze({
|
||||
},
|
||||
"scanner-file-operations": {
|
||||
id: "scanner-file-operations",
|
||||
name: "Scanner File Operations",
|
||||
timeout: 60,
|
||||
description: "Scanner file operation matrix",
|
||||
vitestTarget: "test/scanner-file-operations.e2e-spec.ts",
|
||||
junitOutput: `${TEST_RESULTS_DIR}/scanner-file-operations-e2e-junit.xml`,
|
||||
@@ -27,6 +33,8 @@ export const E2E_SUITES = Object.freeze({
|
||||
},
|
||||
"auth-session-security": {
|
||||
id: "auth-session-security",
|
||||
name: "Auth Session Security",
|
||||
timeout: 50,
|
||||
description: "Auth session security suite",
|
||||
vitestTarget: "test/auth-session-security.e2e-spec.ts",
|
||||
junitOutput: `${TEST_RESULTS_DIR}/auth-session-security-e2e-junit.xml`,
|
||||
@@ -35,6 +43,8 @@ export const E2E_SUITES = Object.freeze({
|
||||
},
|
||||
"auth-recovery-oidc-logout": {
|
||||
id: "auth-recovery-oidc-logout",
|
||||
name: "Auth Recovery OIDC Logout",
|
||||
timeout: 50,
|
||||
description: "Auth recovery and OIDC logout suite",
|
||||
vitestTarget: "test/auth-recovery-oidc-logout.e2e-spec.ts",
|
||||
junitOutput: `${TEST_RESULTS_DIR}/auth-recovery-oidc-logout-e2e-junit.xml`,
|
||||
@@ -43,6 +53,8 @@ export const E2E_SUITES = Object.freeze({
|
||||
},
|
||||
"staging-ingest-finalize": {
|
||||
id: "staging-ingest-finalize",
|
||||
name: "Staging Ingest Finalize",
|
||||
timeout: 45,
|
||||
description: "Staging ingest and finalize suite",
|
||||
vitestTarget: "test/staging-ingest-finalize.e2e-spec.ts",
|
||||
junitOutput: `${TEST_RESULTS_DIR}/staging-ingest-finalize-e2e-junit.xml`,
|
||||
@@ -51,6 +63,8 @@ export const E2E_SUITES = Object.freeze({
|
||||
},
|
||||
"metadata-write": {
|
||||
id: "metadata-write",
|
||||
name: "Metadata Write",
|
||||
timeout: 60,
|
||||
description: "Metadata write operations suite",
|
||||
vitestTarget: "test/metadata-write.e2e-spec.ts",
|
||||
junitOutput: `${TEST_RESULTS_DIR}/metadata-write-e2e-junit.xml`,
|
||||
@@ -59,6 +73,8 @@ export const E2E_SUITES = Object.freeze({
|
||||
},
|
||||
"authorization-matrix": {
|
||||
id: "authorization-matrix",
|
||||
name: "Authorization Matrix",
|
||||
timeout: 60,
|
||||
description: "Authorization matrix suite",
|
||||
vitestTarget: "test/authorization-matrix.e2e-spec.ts",
|
||||
junitOutput: `${TEST_RESULTS_DIR}/authorization-matrix-e2e-junit.xml`,
|
||||
@@ -67,6 +83,8 @@ export const E2E_SUITES = Object.freeze({
|
||||
},
|
||||
"opds-auth-catalog": {
|
||||
id: "opds-auth-catalog",
|
||||
name: "OPDS Auth Catalog",
|
||||
timeout: 45,
|
||||
description: "OPDS auth and catalog suite",
|
||||
vitestTarget: "test/opds-auth-catalog.e2e-spec.ts",
|
||||
junitOutput: `${TEST_RESULTS_DIR}/opds-auth-catalog-e2e-junit.xml`,
|
||||
@@ -75,6 +93,8 @@ export const E2E_SUITES = Object.freeze({
|
||||
},
|
||||
"email-lifecycle": {
|
||||
id: "email-lifecycle",
|
||||
name: "Email Lifecycle",
|
||||
timeout: 45,
|
||||
description: "Email lifecycle suite",
|
||||
vitestTarget: "test/email-lifecycle.e2e-spec.ts",
|
||||
junitOutput: `${TEST_RESULTS_DIR}/email-lifecycle-e2e-junit.xml`,
|
||||
@@ -83,6 +103,8 @@ export const E2E_SUITES = Object.freeze({
|
||||
},
|
||||
"reader-state-isolation": {
|
||||
id: "reader-state-isolation",
|
||||
name: "Reader State Isolation",
|
||||
timeout: 45,
|
||||
description: "Reader state isolation suite",
|
||||
vitestTarget: "test/reader-state-isolation.e2e-spec.ts",
|
||||
junitOutput: `${TEST_RESULTS_DIR}/reader-state-isolation-e2e-junit.xml`,
|
||||
@@ -91,6 +113,8 @@ export const E2E_SUITES = Object.freeze({
|
||||
},
|
||||
"users-admin-lifecycle": {
|
||||
id: "users-admin-lifecycle",
|
||||
name: "Users Admin Lifecycle",
|
||||
timeout: 50,
|
||||
description: "Users admin lifecycle suite",
|
||||
vitestTarget: "test/users-admin-lifecycle.e2e-spec.ts",
|
||||
junitOutput: `${TEST_RESULTS_DIR}/users-admin-lifecycle-e2e-junit.xml`,
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { listE2ESuites } from "./e2e-suites.mjs";
|
||||
|
||||
process.stdout.write(
|
||||
JSON.stringify(listE2ESuites().map(({ id, name, timeout }) => ({ id, name, timeout })))
|
||||
);
|
||||
@@ -87,14 +87,14 @@ export class UserBookStatusService {
|
||||
|
||||
private toReadStatus(value: string): ReadStatus {
|
||||
if (!isReadStatus(value)) {
|
||||
throw new InternalServerErrorException(`Invalid read status value: ${value}`);
|
||||
throw new InternalServerErrorException(`Invalid read status value: ${String(value)}`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private toReadStatusSource(value: string): 'auto' | 'manual' {
|
||||
if (!isReadStatusSource(value)) {
|
||||
throw new InternalServerErrorException(`Invalid read status source value: ${value}`);
|
||||
throw new InternalServerErrorException(`Invalid read status source value: ${String(value)}`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -440,7 +440,7 @@ describe('Authorization matrix (e2e)', () => {
|
||||
const message = extractMessage(response);
|
||||
if (response.statusCode === 403 && message.includes('Missing permission:')) {
|
||||
failures.push({
|
||||
route: `${permission} -> ${probe.method} ${probe.path}`,
|
||||
route: `${String(permission)} -> ${probe.method} ${probe.path}`,
|
||||
status: response.statusCode,
|
||||
reason: 'permission allow-path still denied by PermissionGuard',
|
||||
message,
|
||||
|
||||
Reference in New Issue
Block a user