Skip to content

Auth rate limiting, 409, Organisation #830

Auth rate limiting, 409, Organisation

Auth rate limiting, 409, Organisation #830

name: Build on Pull Request
on:
pull_request:
branches:
- "**"
env:
DOCKER_HUB_ORGANIZATION: ${{ vars.DOCKER_HUB_ORGANIZATION }}
# ---------------------------------------------------------------------------
# compile — compiles everything once, packages the JAR, uploads classes
# test — 3-way matrix downloads compiled output and runs a shard of tests
#
# Wall-clock target:
# compile ~10 min (parallel with setup of test shards)
# tests ~8 min (3 shards in parallel after compile finishes)
# total ~18 min (vs ~27 min single-job)
# ---------------------------------------------------------------------------
jobs:
# --------------------------------------------------------------------------
# Job 1: compile
# --------------------------------------------------------------------------
compile:
runs-on: ubuntu-latest
if: github.repository == 'OpenBankProject/OBP-API'
steps:
- uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: "11"
distribution: "adopt"
cache: maven # caches ~/.m2/repository keyed on pom.xml hash
- name: Setup production props
run: |
cp obp-api/src/main/resources/props/sample.props.template \
obp-api/src/main/resources/props/production.default.props
- name: Compile and install (skip test execution)
run: |
# -DskipTests — compile test sources but do NOT run them
# Test classes must be in target/test-classes for the test shards
MAVEN_OPTS="-Xmx3G -Xss2m -XX:MaxMetaspaceSize=1G" \
mvn clean install -T 4 -Pprod -DskipTests
- name: Upload compiled output
uses: actions/upload-artifact@v4
with:
name: compiled-output
retention-days: 1
# Upload full target dirs — test shards download and run surefire:test
# without recompiling (surefire:test goal bypasses compile lifecycle)
path: |
obp-api/target/
obp-commons/target/
- name: Save .jar artifact
run: mkdir -p ./pull && cp obp-api/target/obp-api.jar ./pull/
- uses: actions/upload-artifact@v4
with:
name: ${{ github.sha }}
path: pull/
# --------------------------------------------------------------------------
# Job 2: test (4-way matrix)
#
# Shard assignment (based on actual clean-run timings):
# Shard 1 ~258s v4_0_0(258)
# Shard 2 ~267s v6_0_0(122) v5_0_0(42) v3_0_0(39) v2_1_0(35) v2_2_0(12) …
# Shard 3 ~252s v1_2_1(137) ResourceDocs(67) berlin(34) util(12) …
# Shard 4 ~232s v5_1_0(79) v3_1_0(65) http4sbridge(52) v7_0_0(45) … + catch-all
# --------------------------------------------------------------------------
test:
needs: compile
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- shard: 1
name: "v4 only"
# ~258s of test work
test_filter: >-
code.api.v4_0_0
- shard: 2
name: "v6 + v5_0 + v3_0 + v2 + small"
# ~267s of test work
test_filter: >-
code.api.v6_0_0
code.api.v5_0_0
code.api.v3_0_0
code.api.v2_1_0
code.api.v2_2_0
code.api.v2_0_0
code.api.v1_4_0
code.api.v1_3_0
code.api.UKOpenBanking
code.atms
code.branches
code.products
code.crm
code.accountHolder
code.entitlement
code.bankaccountcreation
code.bankconnectors
code.container
- shard: 3
name: "v1_2_1 + ResourceDocs + berlin + util + small"
# ~252s of test work
test_filter: >-
code.api.v1_2_1
code.api.ResourceDocs1_4_0
code.api.util
code.api.berlin
code.management
code.metrics
code.model
code.views
code.usercustomerlinks
code.customer
code.errormessages
- shard: 4
name: "v5_1 + v3_1 + http4sbridge + v7 + code.api + util + connector"
# ~232s of test work + catch-all for any new packages
# Root-level code.api tests use class-name prefix matching (lowercase classes)
test_filter: >-
code.api.v5_1_0
code.api.v3_1_0
code.api.http4sbridge
code.api.v7_0_0
code.api.Authentication
code.api.dauthTest
code.api.DirectLoginTest
code.api.gateWayloginTest
code.api.OBPRestHelperTest
code.util
code.connector
services:
redis:
image: redis
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: "11"
distribution: "adopt"
cache: maven
- name: Download compiled output
uses: actions/download-artifact@v4
with:
name: compiled-output
- name: Touch artifact files (prevent Zinc recompilation)
run: |
# actions/download-artifact preserves original compile-job timestamps.
# actions/checkout gives source files the current (later) time.
# Zinc sees sources newer than classes → full recompile (~215 s wasted).
# Touching everything in target/ makes all artifact files appear
# just-downloaded (current time) → newer than sources → Zinc skips.
find obp-api/target obp-commons/target -type f -exec touch {} + 2>/dev/null || true
echo "Touched $(find obp-api/target obp-commons/target -type f 2>/dev/null | wc -l) files"
- name: Install local artifacts into Maven repo
run: |
# The compile runner's ~/.m2 is discarded after that job completes.
# Install the two local multi-module artifacts so scalatest:test can
# resolve com.tesobe:* without hitting remote repos.
#
# 1. Parent POM — obp-commons' pom.xml declares obp-parent as its
# <parent>; Maven fetches it when reading transitive deps.
mvn install:install-file \
-Dfile=pom.xml \
-DgroupId=com.tesobe \
-DartifactId=obp-parent \
-Dversion=1.10.1 \
-Dpackaging=pom \
-DgeneratePom=false
# 2. obp-commons JAR with its full POM (lists compile deps inherited
# by obp-api at test classpath resolution time).
mvn install:install-file \
-Dfile=obp-commons/target/obp-commons-1.10.1.jar \
-DpomFile=obp-commons/pom.xml
- name: Setup props
run: |
cp obp-api/src/main/resources/props/sample.props.template \
obp-api/src/main/resources/props/production.default.props
echo connector=star > obp-api/src/main/resources/props/test.default.props
echo starConnector_supported_types=mapped,internal >> obp-api/src/main/resources/props/test.default.props
echo hostname=http://localhost:8016 >> obp-api/src/main/resources/props/test.default.props
echo tests.port=8016 >> obp-api/src/main/resources/props/test.default.props
echo End of minimum settings >> obp-api/src/main/resources/props/test.default.props
echo payments_enabled=false >> obp-api/src/main/resources/props/test.default.props
echo importer_secret=change_me >> obp-api/src/main/resources/props/test.default.props
echo messageQueue.updateBankAccountsTransaction=false >> obp-api/src/main/resources/props/test.default.props
echo messageQueue.createBankAccounts=false >> obp-api/src/main/resources/props/test.default.props
echo allow_sandbox_account_creation=true >> obp-api/src/main/resources/props/test.default.props
echo allow_sandbox_data_import=true >> obp-api/src/main/resources/props/test.default.props
echo sandbox_data_import_secret=change_me >> obp-api/src/main/resources/props/test.default.props
echo allow_account_deletion=true >> obp-api/src/main/resources/props/test.default.props
echo allowed_internal_redirect_urls = /,/oauth/authorize >> obp-api/src/main/resources/props/test.default.props
echo transactionRequests_enabled=true >> obp-api/src/main/resources/props/test.default.props
echo transactionRequests_supported_types=SEPA,SANDBOX_TAN,FREE_FORM,COUNTERPARTY,ACCOUNT,SIMPLE >> obp-api/src/main/resources/props/test.default.props
echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
echo openredirects.hostname.whitlelist=http://127.0.0.1,http://localhost >> obp-api/src/main/resources/props/test.default.props
echo remotedata.secret = foobarbaz >> obp-api/src/main/resources/props/test.default.props
echo allow_public_views=true >> obp-api/src/main/resources/props/test.default.props
echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
echo ACCOUNT_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
echo SEPA_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
echo FREE_FORM_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
echo COUNTERPARTY_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
echo SEPA_CREDIT_TRANSFERS_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
echo allow_oauth2_login=true >> obp-api/src/main/resources/props/test.default.props
echo oauth2.jwk_set.url=https://www.googleapis.com/oauth2/v3/certs >> obp-api/src/main/resources/props/test.default.props
echo ResetPasswordUrlEnabled=true >> obp-api/src/main/resources/props/test.default.props
echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props
echo hikari.maximumPoolSize=20 >> obp-api/src/main/resources/props/test.default.props
echo write_metrics=false >> obp-api/src/main/resources/props/test.default.props
- name: Run tests — shard ${{ matrix.shard }} (${{ matrix.name }})
run: |
# wildcardSuites requires comma-separated package prefixes (-w per entry).
# The YAML >- scalar collapses newlines to spaces, so we convert here.
FILTER=$(echo "${{ matrix.test_filter }}" | tr ' ' ',')
# Shard 4 is the catch-all: append any test package not explicitly
# assigned to shards 1–3, so new packages are never silently skipped.
if [ "${{ matrix.shard }}" = "4" ]; then
SHARD1="code.api.v4_0_0"
SHARD2="code.api.v6_0_0 code.api.v5_0_0 code.api.v3_0_0 code.api.v2_1_0 \
code.api.v2_2_0 code.api.v2_0_0 code.api.v1_4_0 code.api.v1_3_0 \
code.api.UKOpenBanking code.atms code.branches code.products code.crm \
code.accountHolder code.entitlement code.bankaccountcreation \
code.bankconnectors code.container"
SHARD3="code.api.v1_2_1 code.api.ResourceDocs1_4_0 \
code.api.util code.api.berlin code.management code.metrics \
code.model code.views code.usercustomerlinks code.customer \
code.errormessages"
ASSIGNED="$SHARD1 $SHARD2 $SHARD3 ${{ matrix.test_filter }}"
# Discover all packages that contain at least one .scala test file
ALL_PKGS=$(find obp-api/src/test/scala obp-commons/src/test/scala \
-name "*.scala" 2>/dev/null \
| sed 's|.*/test/scala/||; s|/[^/]*\.scala$||; s|/|.|g' \
| sort -u)
EXTRAS=""
for pkg in $ALL_PKGS; do
covered=false
for prefix in $ASSIGNED; do
if [[ "$pkg" == "$prefix" || "$pkg" == "$prefix."* || "$prefix" == "$pkg."* ]]; then
covered=true; break
fi
done
[ "$covered" = "false" ] && EXTRAS="$EXTRAS,$pkg"
done
[ -n "$EXTRAS" ] && echo "Catch-all extras added to shard 4:$EXTRAS"
FILTER="${FILTER}${EXTRAS}"
fi
MAVEN_OPTS="-Xmx3G -Xss2m -XX:MaxMetaspaceSize=1G" \
mvn test \
-DwildcardSuites="$FILTER" \
> maven-build-shard${{ matrix.shard }}.log 2>&1
- name: Report failing tests — shard ${{ matrix.shard }}
if: always()
run: |
echo "Checking shard ${{ matrix.shard }} log for failing tests..."
if [ ! -f maven-build-shard${{ matrix.shard }}.log ]; then
echo "No build log found."; exit 0
fi
echo "=== RECOMPILATION CHECK ==="
if grep -c "Compiling " maven-build-shard${{ matrix.shard }}.log > /dev/null 2>&1; then
echo "WARNING: Scala recompilation occurred on this shard:"
grep "Compiling " maven-build-shard${{ matrix.shard }}.log | head -10
else
echo "OK: no recompilation (Zinc used pre-compiled classes)"
fi
echo ""
echo "=== BRIDGE / UNCAUGHT EXCEPTIONS ==="
grep -n "\[BRIDGE\] Exception\|Uncaught exception in dispatch\|requestScopeProxy=" \
maven-build-shard${{ matrix.shard }}.log | head -200 || true
echo ""
echo "=== FAILING TEST SCENARIOS (with 30 lines context) ==="
if grep -C 30 -n "\*\*\* FAILED \*\*\*" maven-build-shard${{ matrix.shard }}.log; then
echo "Failing tests detected in shard ${{ matrix.shard }}."
exit 1
else
echo "No failing tests detected in shard ${{ matrix.shard }}."
fi
- name: Upload Maven build log — shard ${{ matrix.shard }}
if: always()
uses: actions/upload-artifact@v4
with:
name: maven-build-log-shard${{ matrix.shard }}
if-no-files-found: ignore
path: maven-build-shard${{ matrix.shard }}.log
- name: Upload test reports — shard ${{ matrix.shard }}
if: always()
uses: actions/upload-artifact@v4
with:
name: test-reports-shard${{ matrix.shard }}
if-no-files-found: ignore
path: |
obp-api/target/surefire-reports/**
obp-commons/target/surefire-reports/**
**/target/scalatest-reports/**
**/target/site/surefire-report.html
**/target/site/surefire-report/*
# --------------------------------------------------------------------------
# Job 3: report — http4s v7 vs Lift per-test speed table
# --------------------------------------------------------------------------
report:
needs: test
runs-on: ubuntu-latest
if: always()
steps:
- uses: actions/checkout@v4
- name: Download test reports — all shards
uses: actions/download-artifact@v4
with:
pattern: test-reports-shard*
path: all-reports
merge-multiple: true
- name: http4s v7 vs Lift — per-test speed
run: python3 .github/scripts/test_speed_report.py all-reports