diff --git a/.github/project.yml b/.github/project.yml index 420390ab..f8d7e9e4 100644 --- a/.github/project.yml +++ b/.github/project.yml @@ -1,6 +1,28 @@ name: api-sheriff -pages-reference: api-sheriff -sonar-project-key: cuioss_API-Sheriff +description: An API-Gateway focused on Security and a lightweight approach + release: current-version: 1.0.0 next-version: 1.1.0-SNAPSHOT + create-github-release: true + +maven-build: + java-versions: '["21","25"]' + java-version: '21' + enable-snapshot-deploy: true + maven-profiles-snapshot: 'release-snapshot,javadoc' + maven-profiles-release: 'release,javadoc' + npm-cache: false + +sonar: + project-key: cuioss_API-Sheriff + enabled: true + skip-on-dependabot: true + +pages: + reference: api-sheriff + deploy-at-release: true + +github-automation: + auto-merge-build-versions: true + auto-merge-build-timeout: 300 diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 4d40e60d..8de0af7b 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -8,8 +8,9 @@ on: tags: [ "*" ] workflow_dispatch: -# Declare default permissions as read only -permissions: read-all +# Declare default permissions as read only (principle of least privilege) +permissions: + contents: read # Prevent concurrent benchmark runs to avoid interference concurrency: @@ -18,7 +19,7 @@ concurrency: jobs: benchmark: - name: Run JMH Benchmarks + name: Run Integration Benchmarks runs-on: ubuntu-latest # Only run on merged PRs, not just closed ones if: github.event_name != 'pull_request' || github.event.pull_request.merged == true @@ -30,130 +31,96 @@ jobs: steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 + uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0 with: egress-policy: audit + - name: Create GitHub App token for deployment + id: app-token + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 + with: + app-id: ${{ secrets.RELEASE_APP_ID }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + repositories: ${{ github.event.repository.name }},cuioss.github.io + owner: cuioss + - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 # Fetch all history for proper versioning + persist-credentials: false # Prevent token from overriding app token during deploy - name: Set up JDK 21 - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: java-version: '21' distribution: 'temurin' cache: maven - - name: Build api-sheriff-library + - name: Build api-sheriff run: | - # Build library module first to ensure test artifact is available for benchmarking + # Build all modules first to ensure artifacts are available for benchmarking ./mvnw --no-transfer-progress clean install -DskipTests - - name: Run Micro Benchmarks + - name: Fetch Previous History + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: cuioss/cuioss.github.io + ref: main + path: previous-pages + sparse-checkout: | + api-sheriff/benchmarks/integration/history + continue-on-error: true + + - name: Prepare Historical Data for Benchmarks run: | - # Create directory for benchmark results - mkdir -p benchmark-results - - # Run micro benchmarks using configuration from pom.xml - ./mvnw --no-transfer-progress clean verify -pl benchmarking/benchmark-library -Pbenchmark - - # Move micro benchmark results to expected location for upload - if [ -d "benchmarking/benchmark-library/target/benchmark-results" ]; then - echo "Moving micro benchmark results to upload location..." - cp -v benchmarking/benchmark-library/target/benchmark-results/* benchmark-results/ || echo "Failed to copy micro benchmark results" - ls -la benchmark-results/ - else - echo "Warning: benchmarking/benchmark-library/target/benchmark-results directory not found!" - echo "Looking for result files:" - find benchmarking/benchmark-library -name "*.json" -type f - fi - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.7.1 - - - name: Run Integration Benchmarks + python3 benchmarks/scripts/benchmark-pages.py prepare-history \ + --previous-pages-dir previous-pages/api-sheriff/benchmarks \ + --output-dir "${GITHUB_WORKSPACE}/benchmark-history" + + - name: Run Integration Benchmarks with WRK run: | - # Run integration benchmarks with native image using profile that includes container startup - echo "๐Ÿš€ Running integration benchmarks with native Quarkus..." - ./mvnw --no-transfer-progress clean verify -pl benchmarking/benchmark-integration-quarkus -Pbenchmark-testing -DskipTests - - # Check what was actually created - echo "๐Ÿ“ Checking benchmark-integration-quarkus/target directory structure:" - ls -la benchmarking/benchmark-integration-quarkus/target/ || echo "Target directory not found" - - echo "๐Ÿ“ Checking benchmark-integration-quarkus/target/benchmark-results directory:" - ls -la benchmarking/benchmark-integration-quarkus/target/benchmark-results/ || echo "Benchmark results directory not found" - - echo "๐Ÿ” Finding all JSON files in benchmark-integration-quarkus:" - find benchmarking/benchmark-integration-quarkus -name "*.json" -type f | head -20 - - # Move integration benchmark results to expected location for upload - if [ -d "benchmarking/benchmark-integration-quarkus/target/benchmark-results" ]; then - echo "Moving integration benchmark results to upload location..." - # Copy ALL json files from benchmark-results directory - for file in benchmarking/benchmark-integration-quarkus/target/benchmark-results/*.json; do - if [ -f "$file" ]; then - echo "Copying: $(basename "$file")" - cp "$file" benchmark-results/ - fi - done - else - echo "โŒ Directory benchmarking/benchmark-integration-quarkus/target/benchmark-results does not exist!" - fi - - # Add timestamp to results - echo "{ \"timestamp\": \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\", \"commit\": \"${{ github.sha }}\" }" > benchmark-results/metadata.json - - # Verify that benchmark results were generated - echo "๐Ÿ“Š Final contents of benchmark-results directory:" - ls -la benchmark-results/ - echo "๐Ÿ” Searching for any benchmark-result files in entire workspace:" - find . -name "*benchmark-result*.json" -type f || echo "No benchmark result files found anywhere" + # Run WRK-based integration benchmarks with native image + echo "Running WRK integration benchmarks with native Quarkus..." + ./mvnw --no-transfer-progress clean verify -pl benchmarks -Pbenchmark \ + -Dbenchmark.history.dir="${GITHUB_WORKSPACE}/benchmark-history/integration" + + # Verify artifacts were generated + echo "Integration benchmark artifacts generated:" + ls -la benchmarks/target/benchmark-results/ + + - name: Assemble benchmark artifacts for deployment + run: | + python3 benchmarks/scripts/benchmark-pages.py assemble \ + --integration-results benchmarks/target/benchmark-results/gh-pages-ready \ + --previous-pages-dir previous-pages/api-sheriff/benchmarks \ + --output-dir gh-pages \ + --commit-sha "${{ github.sha }}" - name: Upload benchmark results - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: benchmark-results - path: benchmark-results/ + path: gh-pages/ retention-days: 90 # Keep results for 90 days - - name: Process All Benchmarks - run: | - # Get current date for badge timestamp in Berlin time - TIMESTAMP=$(TZ='Europe/Berlin' date +"%Y-%m-%d") - TIMESTAMP_WITH_TIME=$(TZ='Europe/Berlin' date +"%Y-%m-%d %H:%M %Z") - - # Create gh-pages directory - mkdir -p gh-pages - - # Process all benchmarks using orchestration script - bash benchmarking/scripts/process-all-benchmarks.sh \ - benchmark-results \ - benchmarking/doc/templates \ - gh-pages \ - "${{ github.sha }}" \ - "$TIMESTAMP" \ - "$TIMESTAMP_WITH_TIME" - - echo "๐Ÿ“Š Benchmark processing completed" - - # Display processing results for debugging - if [ -f "gh-pages/processing-results.json" ]; then - echo "๐Ÿ“‹ Processing Summary:" - jq -r ' - "๐Ÿ”ฌ Micro Benchmarks: " + .processing.micro.status + " - " + .processing.micro.message, - "๐Ÿ”— Integration Benchmarks: " + .processing.integration.status + " - " + .processing.integration.message, - "โŒ Errors: " + (.errors | length | tostring) - ' gh-pages/processing-results.json - fi + - name: Checkout cuioss.github.io for deployment + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: cuioss/cuioss.github.io + path: _pages-deploy + sparse-checkout: api-sheriff/benchmarks + token: ${{ steps.app-token.outputs.token }} - name: Deploy to cuioss.github.io - uses: JamesIves/github-pages-deploy-action@6c2d9db40f9296374acc17b90404b6e8864128c8 # v4.7.3 - with: - folder: gh-pages - repository-name: cuioss/cuioss.github.io - target-folder: api-sheriff/benchmarks - branch: main - token: ${{ secrets.PAGES_DEPLOY_TOKEN }} \ No newline at end of file + run: | + TARGET_DIR="_pages-deploy/api-sheriff/benchmarks" + rm -rf "$TARGET_DIR" + mkdir -p "$TARGET_DIR" + cp -r gh-pages/* "$TARGET_DIR/" + cd _pages-deploy + git config user.name "cuioss-release-bot[bot]" + git config user.email "cuioss-release-bot[bot]@users.noreply.github.com" + git add . + git diff --staged --quiet || git commit -m "Deploy benchmark results from ${{ github.sha }}" + git push diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml new file mode 100644 index 00000000..229bcc46 --- /dev/null +++ b/.github/workflows/claude.yml @@ -0,0 +1,52 @@ +name: Claude Code + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + issues: + types: [opened, assigned] + pull_request_review: + types: [submitted] + +# Declare default permissions as read only (principle of least privilege) +permissions: + contents: read + +jobs: + claude: + if: | + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || + (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + issues: read + id-token: write + actions: read # Required for Claude to read CI results on PRs + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 1 + - name: Set up JDK 21 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + java-version: '21' + distribution: 'temurin' + cache: maven + - name: Run Claude Code + id: claude + uses: anthropics/claude-code-action@220272d38887a1caed373da96a9ffdb0919c26cc # beta + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + + # This is an optional setting that allows Claude to read CI results on PRs + additional_permissions: | + actions: read + + allowed_tools: "Bash(./mvnw*),Bash(./mvnw -Ppre-commit clean verify -DskipTests),Bash(./mvnw clean verify),Bash(./mvnw clean install),Bash(./mvnw -Ppre-commit clean verify -DskipTests -pl api-sheriff),Bash(./mvnw -Ppre-commit clean verify -DskipTests -pl integration-tests),Bash(./mvnw -Ppre-commit clean verify -DskipTests -pl benchmarks),Bash(./mvnw clean install -pl api-sheriff),Bash(./mvnw clean install -pl integration-tests),Bash(./mvnw clean install -pl benchmarks),Bash(git*),Bash(find*),Bash(gh*),Bash(java*)" diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 00000000..2eee5027 --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,14 @@ +name: Dependabot Auto-merge + +on: + pull_request: + +permissions: + contents: read + +jobs: + auto-merge: + uses: cuioss/cuioss-organization/.github/workflows/reusable-dependabot-auto-merge.yml@8fce8938a4e92b4164650d083dbbf9c5601c6dfb # v0.6.2 + permissions: + contents: write + pull-requests: write diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 00000000..2daca4b4 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,15 @@ +name: Dependency Review + +on: + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + dependency-review: + uses: cuioss/cuioss-organization/.github/workflows/reusable-dependency-review.yml@8fce8938a4e92b4164650d083dbbf9c5601c6dfb # v0.6.2 + permissions: + contents: read + pull-requests: write diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 8d779e08..522c8287 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -1,38 +1,28 @@ +# Runs integration/E2E tests via Maven with report deployment to cuioss.github.io. name: Integration Tests on: - pull_request: - branches: [ "main" ] push: - tags: [ "*" ] + branches: [main, "feature/*", "fix/*", "chore/*", "dependabot/**"] + pull_request: + branches: [main] workflow_dispatch: +permissions: + contents: read + jobs: integration-tests: - runs-on: ubuntu-latest - - steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 - with: - egress-policy: audit - - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - name: Set up JDK 21 - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 - with: - java-version: '21' - distribution: 'temurin' - cache: maven - - - name: Build with Maven (Default Profile) - run: ./mvnw --no-transfer-progress clean install -Dmaven.test.skip=true - - - name: Run Integration Tests Distroless - run: ./mvnw --no-transfer-progress clean verify -Pintegration-tests -pl api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests - - # JFR profile disabled in CI - only available for local development - # The JFR native container has startup issues in GitHub Actions environment - # - name: Run Integration Tests ubi9-quarkus-micro-image / jfr - # run: ./mvnw --no-transfer-progress clean verify -Pjfr -pl api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests \ No newline at end of file + # Run on push events, OR on pull_request only if from a fork + # This prevents duplicate runs: push handles internal branches, PR handles forks + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + uses: cuioss/cuioss-organization/.github/workflows/reusable-maven-integration-tests.yml@8fce8938a4e92b4164650d083dbbf9c5601c6dfb # v0.6.2 + with: + report-name: integration-tests + maven-args-input: | + verify -Pintegration-tests -pl integration-tests -am + reports-folder: | + integration-tests/target/failsafe-reports + secrets: + RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }} + RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} diff --git a/.github/workflows/maven-release.yml b/.github/workflows/maven-release.yml deleted file mode 100644 index c412ea83..00000000 --- a/.github/workflows/maven-release.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Maven Release - -on: - pull_request: - types: [ closed ] - paths: - - '.github/project.yml' - workflow_dispatch: - -jobs: - release: - runs-on: ubuntu-latest - name: release - - steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 - with: - egress-policy: audit - - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false # otherwise, the validation used is the PA_TOKEN, instead of your personal access token. - fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository. - - - uses: radcortez/project-metadata-action@203f7ffba8db2669b2c9b4d4c2e90b186c588fa5 # 1.1 - name: Retrieve project metadata from '.github/project.yml' - id: metadata - with: - github-token: ${{secrets.GITHUB_TOKEN}} - metadata-file-path: '.github/project.yml' - local-file: true - - - name: Set up JDK 21 - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 - with: - java-version: '21' - distribution: 'temurin' - server-id: central - server-username: MAVEN_USERNAME - server-password: MAVEN_PASSWORD - gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} - gpg-passphrase: MAVEN_GPG_PASSPHRASE - cache: maven - - - name: Configure Git author - run: | - git config --local user.email "action@github.com" - git config --local user.name "Cuioss Robot Action" - - - name: Maven release ${{steps.metadata.outputs.current-version}} - run: | - git checkout -b release - ./mvnw -B --no-transfer-progress -Prelease release:clean release:prepare -DreleaseVersion=${{steps.metadata.outputs.current-version}} -DdevelopmentVersion=${{steps.metadata.outputs.next-version}} - ./mvnw -B --no-transfer-progress -Prelease,javadoc site:site site:stage - git checkout ${{vars.GITHUB_BASE_REF}} - git rebase release - ./mvnw -B --no-transfer-progress -Prelease release:perform -DskipTests - env: - MAVEN_USERNAME: ${{ secrets.OSS_SONATYPE_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.OSS_SONATYPE_PASSWORD }} - MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - - - name: Deploy Maven Site to cuioss.github.io -> ${{steps.metadata.outputs.pages-reference}}๐Ÿš€ - uses: JamesIves/github-pages-deploy-action@6c2d9db40f9296374acc17b90404b6e8864128c8 # v4.7.3 - with: - folder: target/site - repository-name: cuioss/cuioss.github.io - target-folder: ${{steps.metadata.outputs.pages-reference}} - branch: main - token: ${{ secrets.PAGES_DEPLOY_TOKEN }} - - - name: Push changes to ${{github.ref_name}} - uses: ad-m/github-push-action@d91a481090679876dfc4178fef17f286781251df # v0.8.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - branch: ${{github.ref_name}} - force: true - - - name: Push tag ${{steps.metadata.outputs.current-version}} - uses: ad-m/github-push-action@d91a481090679876dfc4178fef17f286781251df # v0.8.0 - with: - branch: ${{github.ref_name}} - github_token: ${{ secrets.GITHUB_TOKEN }} - tags: true - force: true \ No newline at end of file diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 48bfb51f..bac04657 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -1,109 +1,26 @@ -name: Master Build +# Configuration is read from .github/project.yml - no inputs needed! +# Path filtering is handled inside the reusable workflow via project.yml settings. +name: Maven Build on: push: - branches: [ "main", "feature/*" ] + branches: [main, "feature/*", "fix/*", "chore/*", "release/*", "dependabot/**"] pull_request: - branches: [ "main" ] + branches: [main] + workflow_dispatch: + +permissions: + contents: read jobs: build: - - runs-on: ubuntu-latest - strategy: - matrix: - version: [ 21,24 ] - - steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 - with: - egress-policy: audit - - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Set up JDK ${{ matrix.version }} - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 - with: - java-version: ${{ matrix.version }} - distribution: 'temurin' - cache: maven - - name: Build with Maven, Java ${{ matrix.version }} - run: ./mvnw --no-transfer-progress verify -Dmaven.compiler.release=${{ matrix.version }} - - sonar-build: - needs: build - runs-on: ubuntu-latest - - steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 - with: - egress-policy: audit - - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - fetch-depth: 0 - - - name: Set up JDK 21 for Sonar-build - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 - with: - java-version: '21' - distribution: 'temurin' - cache: maven - - - name: Cache SonarCloud packages - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 - with: - path: ~/.sonar/cache - key: ${{ runner.os }}-sonar - restore-keys: ${{ runner.os }}-sonar - - - uses: radcortez/project-metadata-action@203f7ffba8db2669b2c9b4d4c2e90b186c588fa5 # 1.1 - name: Retrieve project metadata from '.github/project.yml' - id: metadata - with: - github-token: ${{secrets.GITHUB_TOKEN}} - metadata-file-path: '.github/project.yml' - local-file: true - - - name: Build and analyze - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: ./mvnw -B --no-transfer-progress verify -Psonar -Dsonar.projectKey=${{steps.metadata.outputs.sonar-project-key}} sonar:sonar - - deploy-snapshot: - needs: sonar-build - if: github.ref == 'refs/heads/main' - runs-on: ubuntu-latest - steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 - with: - egress-policy: audit - - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Set up JDK 17 for snapshot release - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 - with: - java-version: '21' - distribution: 'temurin' - server-id: central - server-username: MAVEN_USERNAME - server-password: MAVEN_PASSWORD - gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} - gpg-passphrase: MAVEN_GPG_PASSPHRASE - cache: maven - - - name: Extract project version - id: project - run: echo ::set-output name=version::$(./mvnw --no-transfer-progress help:evaluate -Dexpression=project.version -q -DforceStdout) - - - name: Deploy Snapshot with Maven, version ${{ steps.project.outputs.version }} - if: ${{endsWith(steps.project.outputs.version, '-SNAPSHOT')}} - run: | - ./mvnw -B --no-transfer-progress -Prelease-snapshot,javadoc deploy -Dmaven.test.skip=true - env: - MAVEN_USERNAME: ${{ secrets.OSS_SONATYPE_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.OSS_SONATYPE_PASSWORD }} - MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} \ No newline at end of file + # Run on push events, OR on pull_request only if from a fork + # This prevents duplicate runs: push handles internal branches, PR handles forks + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + uses: cuioss/cuioss-organization/.github/workflows/reusable-maven-build.yml@8fce8938a4e92b4164650d083dbbf9c5601c6dfb # v0.6.2 + secrets: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + OSS_SONATYPE_USERNAME: ${{ secrets.OSS_SONATYPE_USERNAME }} + OSS_SONATYPE_PASSWORD: ${{ secrets.OSS_SONATYPE_PASSWORD }} + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..6e1f4cae --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,26 @@ +# Configuration is read from .github/project.yml - no inputs needed! +name: Release + +on: + pull_request: + types: [closed] + paths: + - '.github/project.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + release: + permissions: + contents: write + if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' + uses: cuioss/cuioss-organization/.github/workflows/reusable-maven-release.yml@8fce8938a4e92b4164650d083dbbf9c5601c6dfb # v0.6.2 + secrets: + RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }} + RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + OSS_SONATYPE_USERNAME: ${{ secrets.OSS_SONATYPE_USERNAME }} + OSS_SONATYPE_PASSWORD: ${{ secrets.OSS_SONATYPE_PASSWORD }} + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml new file mode 100644 index 00000000..9f0dd7a8 --- /dev/null +++ b/.github/workflows/scorecards.yml @@ -0,0 +1,23 @@ +name: Scorecard supply-chain security + +on: + branch_protection_rule: + schedule: + - cron: '20 7 * * 2' + push: + branches: [main] + +permissions: + contents: read + +jobs: + analysis: + uses: cuioss/cuioss-organization/.github/workflows/reusable-scorecards.yml@8fce8938a4e92b4164650d083dbbf9c5601c6dfb # v0.6.2 + permissions: + security-events: write + id-token: write + contents: read + actions: read + issues: read + pull-requests: read + checks: read diff --git a/.mvn/jvm.config b/.mvn/jvm.config new file mode 100644 index 00000000..81b88d81 --- /dev/null +++ b/.mvn/jvm.config @@ -0,0 +1 @@ +--enable-native-access=ALL-UNNAMED diff --git a/CLAUDE.md b/CLAUDE.md index ceee8dd4..5139be9b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,138 +1,129 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +This file provides guidance to Claude Code when working with this repository. ## Project Overview -API Sheriff is a security-focused API Gateway with a lightweight approach, currently in pre-1.0 development phase. The project follows CUI (CUIoss) standards and is built using Maven with Java 21+. +API Sheriff is a security-focused API Gateway with a lightweight approach, currently in pre-1.0 development. Built with Maven, Java 21+, and Quarkus 3.32.1. Follows CUI (CUIoss) standards. + +## Project Structure + +Multi-module Maven project: +- `api-sheriff/` โ€” Deployable Quarkus application (core library, CDI producers, REST endpoints, native executable) +- `integration-tests/` โ€” Integration test coordinator (Docker infrastructure, IT suites, scripts) +- `benchmarks/` โ€” WRK HTTP load testing benchmarks ## Build Commands -### Core Build Operations -- **Build project**: `./mvnw clean install` -- **Build without tests**: `./mvnw clean install -DskipTests` -- **Run tests only**: `./mvnw test` -- **Run single test**: `./mvnw test -Dtest=ClassName#methodName` +```bash +# Full build with tests +./mvnw clean install + +# Build without tests +./mvnw clean install -DskipTests + +# Single module +./mvnw clean install -pl + +# Single test +./mvnw test -Dtest=ClassName#methodName + +# Integration tests +./mvnw clean verify -Pintegration-tests -pl integration-tests -am + +# Integration benchmarks (WRK) +./mvnw clean verify -pl benchmarks -Pbenchmark + +# Build native executable +./mvnw clean install -Pnative -pl api-sheriff -am -DskipTests + +# Build production Docker image +docker build -f api-sheriff/src/main/docker/Dockerfile.native -t api-sheriff:latest api-sheriff/ +``` + +### Pre-Commit Process -### Quality and Pre-Commit Process -**CRITICAL**: Always run these commands before committing: +**CRITICAL** โ€” run before every commit: -1. **Quality verification** (fix all errors/warnings): +1. Quality verification (fix ALL errors/warnings): ```bash ./mvnw -Ppre-commit clean verify -DskipTests ``` -2. **Final verification** (must pass completely): +2. Full verification (must pass completely): ```bash ./mvnw clean install ``` -### CI/CD Commands -- **Sonar analysis**: `./mvnw verify -Psonar sonar:sonar` -- **Deploy snapshot**: `./mvnw -Prelease-snapshot,javadoc deploy` -- **Coverage report**: `./mvnw clean verify -Pcoverage` +## Pre-1.0 Rules (HIGHEST PRIORITY) -## Project Structure +- **NEVER deprecate code** โ€” remove it directly +- **NEVER add @Deprecated** โ€” delete unnecessary code immediately +- **NEVER enforce backward compatibility** โ€” make breaking changes freely +- **Clean APIs aggressively** โ€” remove unused methods, classes, patterns -``` -api-sheriff/ -โ”œโ”€โ”€ src/ -โ”‚ โ”œโ”€โ”€ main/ -โ”‚ โ”‚ โ””โ”€โ”€ java/ -โ”‚ โ”‚ โ””โ”€โ”€ de/cuioss/sheriff/api/ # Main source code -โ”‚ โ””โ”€โ”€ test/ -โ”‚ โ””โ”€โ”€ java/ -โ”‚ โ””โ”€โ”€ de/cuioss/sheriff/api/ # Test code -โ”œโ”€โ”€ doc/ -โ”‚ โ””โ”€โ”€ ai-rules.md # AI development guidelines -โ”œโ”€โ”€ pom.xml # Maven configuration -โ””โ”€โ”€ lombok.config # Lombok configuration -``` +## Code Standards -## Critical Development Rules +- Java 21+ features encouraged (records, sealed classes, pattern matching, text blocks) +- Lombok: `@Builder`, `@Value`, `@NonNull`, `@ToString`, `@EqualsAndHashCode` +- Prefer immutable objects, final fields, empty collections over null, Optional for nullable returns +- Indentation: 4 spaces, LF line endings, UTF-8 -### Pre-1.0 Project Rules (HIGHEST PRIORITY) -- **NEVER deprecate code** - Remove it directly -- **NEVER add @Deprecated annotations** - Delete unnecessary code immediately -- **NEVER enforce backward compatibility** - Make breaking changes freely -- **Clean APIs aggressively** - Remove unused methods, classes, and patterns -- **Focus on final API design** - Design for post-1.0 stability +### Logging -### CUI Standards Compliance +- Logger: `de.cuioss.tools.logging.CuiLogger` (private static final LOGGER) +- Format: always `%s` for substitution (NEVER `{}`, `%.2f`, `%d`) +- Structured: `de.cuioss.tools.logging.LogRecord` for INFO/WARN/ERROR +- Ranges: INFO (001-099), WARN (100-199), ERROR (200-299) +- Exception parameter always comes first +- Document in `doc/LogMessages.adoc` -#### Logging -- Use `de.cuioss.tools.logging.CuiLogger` (private static final LOGGER) -- Exception parameter always comes first in logging methods -- Use '%s' for string substitutions (not '{}' or '%d') -- Document all log messages in doc/LogMessages.adoc +### Testing -#### Testing -- Use JUnit 5 exclusively -- Follow AAA pattern (Arrange-Act-Assert) -- Minimum 80% code coverage required -- Use cui-test-generator for test data generation +- JUnit 5 exclusively, AAA pattern (Arrange-Act-Assert) +- Minimum 80% coverage +- CUI Test Generator for test data (`@GeneratorsSource` preferred) - **Forbidden**: Mockito, PowerMock, Hamcrest -#### Java Standards -- Java 21+ features encouraged (records, switch expressions, text blocks) -- Use Lombok annotations (@Builder, @Value, @NonNull, @UtilityClass) -- Prefer immutable objects and final fields -- Return empty collections instead of null -- Use Optional for nullable return values +### Javadoc -#### Documentation -- Every public API must have complete Javadoc -- Use AsciiDoc format (.adoc) for documentation -- Include @since tags with version information -- Document thread-safety considerations +- Every public/protected class and method documented +- Include `@since` tags, thread-safety notes, usage examples +- Every package must have `package-info.java` -## Dependency Management +## OpenRewrite Markers + +Markers like `/*~~(TODO: INFO needs LogRecord)~~>*/` indicate **actual bugs**: +- Fix placeholder/parameter mismatches, wrong format specifiers +- Create LogRecord constants for production INFO/WARN/ERROR logs +- Replace generic Exception/RuntimeException with specific types +- For test diagnostic logging: add `// cui-rewrite:disable CuiLogRecordPatternRecipe` suppression +- **Never commit code with markers present** -Current minimal dependencies: -- **lombok**: For boilerplate reduction -- **junit-jupiter-api**: For testing -- **Parent POM**: de.cuioss:cui-java-parent:1.1.4 +## Security -**CRITICAL**: Never add dependencies without explicit user approval +As a security-focused API Gateway: +- All changes must consider security implications +- Never expose sensitive data in logs +- Follow OWASP guidelines, validate all inputs/outputs +- Use secure defaults -## Integration with IntelliJ IDEA +## Dependency Management -The project is configured for IntelliJ IDEA with: -- MCP server integration for enhanced IDE features -- Maven wrapper for consistent builds -- EditorConfig for code formatting +- **Parent POM**: `de.cuioss:cui-java-parent:1.4.4` +- **CRITICAL**: Never add dependencies without explicit user approval -Use IntelliJ MCP tools when available: -- `mcp__jetbrains__get_file_problems` for code analysis -- `mcp__jetbrains__execute_terminal_command` for running commands -- `mcp__jetbrains__search_in_files_by_text` for efficient code search +## Git Workflow -## Security Considerations +All cuioss repositories have branch protection on `main`. Always: -As a security-focused API Gateway: -- All changes must consider security implications -- Never expose sensitive data in logs -- Follow OWASP security guidelines -- Validate all inputs and outputs -- Use secure defaults for all configurations - -## Common Tasks - -### Adding New Features -1. Research existing patterns in the codebase -2. Follow CUI standards for implementation -3. Write comprehensive tests (80%+ coverage) -4. Document public APIs with Javadoc -5. Run pre-commit checks before committing - -### Fixing Issues -1. Identify the issue using IDE diagnostics -2. Apply fix following existing code patterns -3. Add/update tests to prevent regression -4. Verify with `./mvnw clean install` - -### Refactoring -1. Ensure tests pass before starting -2. Make incremental changes -3. Run tests after each change -4. Use IDE refactoring tools when available -5. Clean up aggressively (pre-1.0 rule) \ No newline at end of file +1. Create feature branch: `git checkout -b ` +2. Commit and push: `git push -u origin ` +3. Create PR: `gh pr create --repo cuioss/API-Sheriff --head --base main --title "" --body "<body>"` +4. Wait for CI + Gemini review: `gh pr checks --watch` +5. **Handle Gemini review comments** โ€” fetch with `gh api repos/cuioss/API-Sheriff/pulls/<pr-number>/comments`: + - Valid and fixable: fix, commit, push, reply explaining, resolve + - Disagree/out of scope: reply explaining why, resolve + - Uncertain: **ask the user** + - Every comment MUST get a reply and MUST be resolved +6. Do **NOT** enable auto-merge unless explicitly instructed +7. Return to main: `git checkout main && git pull` diff --git a/README.adoc b/README.adoc index 6424dbc8..50926856 100644 --- a/README.adoc +++ b/README.adoc @@ -34,14 +34,14 @@ image:https://img.shields.io/endpoint?url=https://cuioss.github.io/api-sheriff/b image:https://img.shields.io/endpoint?url=https://cuioss.github.io/api-sheriff/benchmarks/badges/integration-performance-badge.json[Integration Performance,link=https://cuioss.github.io/api-sheriff/benchmarks/integration-index.html] image:https://img.shields.io/endpoint?url=https://cuioss.github.io/api-sheriff/benchmarks/badges/integration-trend-badge.json[Integration Trend,link=https://cuioss.github.io/api-sheriff/benchmarks/integration-performance-trends.html] -xref:benchmarking/doc/performance-scoring.adoc[Understand Performance Metrics] +xref:benchmarks/doc/performance-scoring.adoc[Understand Performance Metrics] https://cuioss.github.io/api-sheriff/about.html[Generated Documentation on github-pages] [.discrete] == What is it? -A template project demonstrating a multi-module Maven structure with Quarkus integration and comprehensive benchmarking. +A security-focused API Gateway with a lightweight approach, built with Quarkus. toc::[] @@ -51,43 +51,53 @@ toc::[] ---- <dependency> <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>api-sheriff-library</artifactId> + <artifactId>api-sheriff</artifactId> </dependency> ---- -== Additional Modules +== Modules -The project includes several additional modules that extend the core functionality: +=== API Sheriff -=== Quarkus Integration +The deployable Quarkus application module โ€” core library, CDI producers, REST endpoints, and native executable target. -The Quarkus Extension provides seamless integration into Quarkus applications. It includes: +=== Integration Tests -* CDI producers for easy dependency injection -* Configuration support via Quarkus properties -* Native image support for GraalVM compilation - -[source,xml] ----- -<dependency> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>api-sheriff-quarkus</artifactId> -</dependency> ----- +Integration test coordinator โ€” Docker infrastructure, native container builds, and IT suites with HTTPS support. === Performance Benchmarking -The project includes two complementary benchmarking modules: +Integration benchmarks using WRK HTTP load testing with containerized environments. + +xref:benchmarks/doc/performance-scoring.adoc[Understand Performance Metrics] -==== Micro-Benchmarks +== Deployment -The Benchmark Library provides in-memory performance measurements using JMH (Java Microbenchmark Harness). +=== Production Docker Image -==== Integration Benchmarks +Build the native executable and production Docker image: -The Quarkus Integration Benchmarks provides end-to-end performance testing using containerized environments. +[source,bash] +---- +# Build native executable +./mvnw clean install -Pnative -pl api-sheriff -am -DskipTests + +# Build production Docker image +docker build -f api-sheriff/src/main/docker/Dockerfile.native -t api-sheriff:latest api-sheriff/ +---- + +Run with TLS certificates mounted at runtime: + +[source,bash] +---- +docker run -p 8443:8443 \ + -v /path/to/certs:/certs:ro \ + -e QUARKUS_HTTP_SSL_CERTIFICATE_FILES=/certs/server.crt \ + -e QUARKUS_HTTP_SSL_CERTIFICATE_KEY_FILES=/certs/server.key \ + api-sheriff:latest +---- -xref:benchmarking/doc/performance-scoring.adoc[Understand Performance Metrics] +* Port `8443` โ€” All endpoints (`/api/health`, `/api/info`, `/q/health`, `/q/metrics`) == External Resources diff --git a/api-sheriff-library/README.adoc b/api-sheriff-library/README.adoc deleted file mode 100644 index 40cd2dac..00000000 --- a/api-sheriff-library/README.adoc +++ /dev/null @@ -1,155 +0,0 @@ -= API Sheriff Library - -The core library module providing lightweight, high-performance API gateway functionality with comprehensive security features and rate limiting capabilities. - -== Overview - -The API Sheriff Library is the foundational component of the API Sheriff project, providing essential security and rate limiting functionality for API gateways. It's designed to be framework-agnostic and can be integrated into any Java application. - -== Key Components - -=== ApiSheriff -The main gateway class that coordinates all security measures and API gateway features. - -[source,java] ----- -ApiGatewayConfig config = ApiGatewayConfig.builder() - .rateLimit(100) - .timeWindow(Duration.ofMinutes(1)) - .requestTimeout(Duration.ofSeconds(30)) - .corsEnabled(true) - .build(); - -ApiSheriff sheriff = new ApiSheriff(config); - -if (sheriff.isRequestAllowed("client-123", "/api/users")) { - // Process request -} ----- - -=== ApiGatewayConfig -Flexible configuration system using the builder pattern with comprehensive validation. - -[source,java] ----- -ApiGatewayConfig config = ApiGatewayConfig.builder() - .rateLimit(1000) - .timeWindow(Duration.ofMinutes(5)) - .requestTimeout(Duration.ofSeconds(60)) - .corsEnabled(false) - .customProperty("feature.auth", true) - .build(); ----- - -=== RateLimiter -Thread-safe rate limiter implementation using a sliding window approach for accurate rate limiting. - -[source,java] ----- -RateLimiter rateLimiter = new RateLimiter(100, Duration.ofMinutes(1).toMillis()); - -if (rateLimiter.isRequestAllowed("client-id")) { - // Process request -} - -// Check remaining requests -int remaining = rateLimiter.getRemainingRequests("client-id"); ----- - -== Features - -* **Thread-Safe**: All components are designed for concurrent access -* **High Performance**: Optimized for minimal latency and high throughput -* **Configurable**: Extensive configuration options with sensible defaults -* **Monitoring**: Built-in logging and metrics support -* **Validation**: Comprehensive input validation with meaningful error messages -* **Multi-tenant**: Per-client rate limiting and state management - -== Usage Patterns - -=== Basic Rate Limiting -[source,java] ----- -ApiSheriff sheriff = new ApiSheriff( - ApiGatewayConfig.builder() - .rateLimit(50) - .timeWindow(Duration.ofMinutes(1)) - .build() -); - -boolean allowed = sheriff.isRequestAllowed("client-123", "/api/endpoint"); ----- - -=== Configuration Validation -[source,java] ----- -Optional<String> validationError = sheriff.validateConfiguration(); -if (validationError.isPresent()) { - log.error("Configuration invalid: {}", validationError.get()); -} ----- - -=== Client State Management -[source,java] ----- -// Reset specific client -sheriff.resetClientState("problematic-client"); - -// Get rate limiter statistics -RateLimiter rateLimiter = sheriff.getRateLimiter(); -int trackedClients = rateLimiter.getTrackedClientCount(); ----- - -== Maven Coordinates - -[source,xml] ----- -<dependency> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>api-sheriff-library</artifactId> - <version>${api-sheriff.version}</version> -</dependency> ----- - -== Dependencies - -The library has minimal dependencies: - -* **Lombok**: For code generation and boilerplate reduction -* **CUI Tools**: For logging and utility functions -* **Jakarta JSON API**: For JSON processing (provided scope) - -== Thread Safety - -All public APIs are thread-safe and can be safely called from multiple threads concurrently. The implementation uses: - -* `ConcurrentHashMap` for client state management -* `AtomicInteger` for counters -* `synchronized` blocks for critical sections -* Immutable configuration objects - -== Performance Characteristics - -* **Memory**: Minimal memory footprint with efficient data structures -* **Latency**: Sub-millisecond request validation in most cases -* **Throughput**: Supports thousands of requests per second per instance -* **Scaling**: Linear scaling with client count and request volume - -== Testing - -The library includes comprehensive unit tests and performance benchmarks: - -* Unit tests with >90% code coverage -* Parameterized tests for edge cases -* Concurrent access testing -* Performance benchmarks with JMH - -== Integration - -The library can be integrated into various environments: - -* Standalone Java applications -* Spring Boot applications -* Quarkus applications (see api-sheriff-quarkus module) -* Jakarta EE applications -* Any framework supporting standard Java libraries \ No newline at end of file diff --git a/api-sheriff-library/src/test/java/de/cuioss/sheriff/api/test/TestDataGenerator.java b/api-sheriff-library/src/test/java/de/cuioss/sheriff/api/test/TestDataGenerator.java deleted file mode 100644 index 7a5d717e..00000000 --- a/api-sheriff-library/src/test/java/de/cuioss/sheriff/api/test/TestDataGenerator.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.cuioss.sheriff.api.test; - -import lombok.experimental.UtilityClass; - -/** - * Test data generator for API Sheriff tests - */ -@UtilityClass -public class TestDataGenerator { - - /** - * Generates a test API key - * @return test API key - */ - public static String generateApiKey() { - return "test-api-key-" + System.currentTimeMillis(); - } - - /** - * Generates a test endpoint URL - * @return test endpoint URL - */ - public static String generateEndpoint() { - return "https://api.example.com/v1/test-" + System.currentTimeMillis(); - } -} \ No newline at end of file diff --git a/api-sheriff-quarkus-parent/README.adoc b/api-sheriff-quarkus-parent/README.adoc deleted file mode 100644 index 1eca5b8b..00000000 --- a/api-sheriff-quarkus-parent/README.adoc +++ /dev/null @@ -1,273 +0,0 @@ -= API Sheriff Quarkus Extension - -Native Quarkus extension providing seamless integration of API Sheriff functionality into Quarkus applications with CDI support, configuration binding, and native image compatibility. - -== Overview - -The API Sheriff Quarkus Extension provides first-class integration between API Sheriff and Quarkus, leveraging Quarkus's dependency injection, configuration management, and build-time optimizations for optimal performance and ease of use. - -== Modules - -=== api-sheriff-quarkus -Runtime components including CDI producers and configuration binding. - -=== api-sheriff-quarkus-deployment -Build-time components for Quarkus native image compilation and dependency injection setup. - -=== api-sheriff-quarkus-integration-tests -Comprehensive integration tests for the Quarkus extension. - -== Features - -* **CDI Integration**: Automatic bean registration and dependency injection -* **Configuration Binding**: Native Quarkus configuration property support -* **Native Image Support**: Full GraalVM native image compatibility -* **Build-time Optimization**: Quarkus build-time processing for optimal performance -* **Health Checks**: Built-in health check support -* **Metrics Integration**: MicroProfile Metrics support -* **Hot Reload**: Development mode with hot reload support - -== Quick Start - -=== 1. Add Dependency - -[source,xml] ----- -<dependency> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>api-sheriff-quarkus</artifactId> - <version>${api-sheriff.version}</version> -</dependency> ----- - -=== 2. Configure Application - -Add to your `application.properties`: - -[source,properties] ----- -# Rate limiting configuration -api-sheriff.rate-limit=200 -api-sheriff.time-window=60 -api-sheriff.request-timeout=30 -api-sheriff.cors-enabled=true ----- - -=== 3. Inject and Use - -[source,java] ----- -@RestController -@ApplicationScoped -public class ApiGatewayResource { - - @Inject - ApiSheriff apiSheriff; - - @GET - @Path("/check/{clientId}") - public Response checkRequest(@PathParam("clientId") String clientId, - @QueryParam("endpoint") String endpoint) { - - if (apiSheriff.isRequestAllowed(clientId, endpoint)) { - return Response.ok("Request allowed").build(); - } else { - return Response.status(429).entity("Rate limit exceeded").build(); - } - } -} ----- - -== Configuration Properties - -All configuration properties are prefixed with `api-sheriff.`: - -[cols="1,1,2,1"] -|=== -|Property |Type |Description |Default - -|`rate-limit` -|Integer -|Maximum requests per client within time window -|100 - -|`time-window` -|Long -|Time window in seconds for rate limiting -|60 - -|`request-timeout` -|Long -|Request timeout in seconds -|30 - -|`cors-enabled` -|Boolean -|Enable CORS support -|false -|=== - -=== Environment Variables - -Configuration can also be provided via environment variables: - -[source,bash] ----- -API_SHERIFF_RATE_LIMIT=500 -API_SHERIFF_TIME_WINDOW=120 -API_SHERIFF_REQUEST_TIMEOUT=45 -API_SHERIFF_CORS_ENABLED=true ----- - -== Advanced Usage - -=== Custom Configuration - -[source,java] ----- -@ApplicationScoped -public class CustomApiSheriffProducer { - - @Produces - @Alternative - @Priority(1000) - public ApiGatewayConfig customConfig() { - return ApiGatewayConfig.builder() - .rateLimit(1000) - .timeWindow(Duration.ofMinutes(5)) - .customProperty("tenant.isolation", true) - .build(); - } -} ----- - -=== Integration with REST Endpoints - -[source,java] ----- -@ApplicationScoped -public class RateLimitingFilter implements ContainerRequestFilter { - - @Inject - ApiSheriff apiSheriff; - - @Override - public void filter(ContainerRequestContext context) throws IOException { - String clientId = extractClientId(context); - String endpoint = context.getUriInfo().getPath(); - - if (!apiSheriff.isRequestAllowed(clientId, endpoint)) { - context.abortWith( - Response.status(429) - .entity("Rate limit exceeded") - .build() - ); - } - } -} ----- - -=== Health Checks - -[source,java] ----- -@ApplicationScoped -public class ApiSheriffHealthCheck implements HealthCheck { - - @Inject - ApiSheriff apiSheriff; - - @Override - public HealthCheckResponse call() { - Optional<String> validationError = apiSheriff.validateConfiguration(); - - if (validationError.isEmpty()) { - return HealthCheckResponse.up("api-sheriff"); - } else { - return HealthCheckResponse.down("api-sheriff") - .withData("error", validationError.get()); - } - } -} ----- - -== Development Mode - -The extension supports Quarkus development mode with hot reload: - -[source,bash] ----- -./mvnw quarkus:dev ----- - -Configuration changes in `application.properties` will be automatically reloaded. - -== Native Image - -The extension is fully compatible with GraalVM native image compilation: - -[source,bash] ----- -./mvnw clean package -Pnative ----- - -All necessary reflection and resource configurations are automatically handled by the build processor. - -== Testing - -=== Unit Testing - -[source,java] ----- -@QuarkusTest -class ApiSheriffExtensionTest { - - @Inject - ApiSheriff apiSheriff; - - @Test - void shouldInjectApiSheriff() { - assertNotNull(apiSheriff); - assertTrue(apiSheriff.isRequestAllowed("test-client", "/api/test")); - } -} ----- - -=== Integration Testing - -[source,java] ----- -@QuarkusTest -class ApiGatewayIntegrationTest { - - @Test - void shouldRespectRateLimit() { - given() - .pathParam("clientId", "test-client") - .queryParam("endpoint", "/api/test") - .when() - .get("/check/{clientId}") - .then() - .statusCode(200); - } -} ----- - -== Performance - -The Quarkus extension is optimized for: - -* **Startup Time**: Build-time dependency injection setup -* **Memory Usage**: Minimal runtime overhead -* **Native Image**: Full GraalVM native image support -* **Hot Reload**: Fast development cycle iteration - -== Migration from Core Library - -If migrating from the core library to the Quarkus extension: - -1. Replace `api-sheriff-library` dependency with `api-sheriff-quarkus` -2. Remove manual `ApiSheriff` instantiation -3. Add `@Inject ApiSheriff` to your components -4. Move configuration to `application.properties` -5. Optional: Add health checks and metrics integration \ No newline at end of file diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-deployment/package-lock.json b/api-sheriff-quarkus-parent/api-sheriff-quarkus-deployment/package-lock.json deleted file mode 100644 index 8d382aea..00000000 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-deployment/package-lock.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "api-sheriff-quarkus-deployment", - "version": "1.0.0-SNAPSHOT", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "api-sheriff-quarkus-deployment", - "version": "1.0.0-SNAPSHOT" - } - } -} diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-deployment/package.json b/api-sheriff-quarkus-parent/api-sheriff-quarkus-deployment/package.json deleted file mode 100644 index b978044a..00000000 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-deployment/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "api-sheriff-quarkus-deployment", - "version": "1.0.0-SNAPSHOT", - "description": "Deployment module for Quarkus integration for API Sheriff", - "private": true, - "scripts": { - "test:ci-strict": "echo 'No JavaScript tests to run'", - "format:check": "echo 'No JavaScript formatting to check'", - "validate:css": "echo 'No CSS to validate'", - "lint": "echo 'No JavaScript to lint'" - } -} \ No newline at end of file diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-deployment/pom.xml b/api-sheriff-quarkus-parent/api-sheriff-quarkus-deployment/pom.xml deleted file mode 100644 index bdb23e0a..00000000 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-deployment/pom.xml +++ /dev/null @@ -1,229 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>api-sheriff-quarkus-parent</artifactId> - <version>1.0.0-SNAPSHOT</version> - <relativePath>../pom.xml</relativePath> - </parent> - - <artifactId>api-sheriff-quarkus-deployment</artifactId> - <packaging>jar</packaging> - <name>API Sheriff Quarkus Integration - Deployment</name> - <description>Deployment module for Quarkus integration for API Sheriff</description> - - <properties> - <maven.jar.plugin.automatic.module.name>de.cuioss.sheriff.api.quarkus.deployment</maven.jar.plugin.automatic.module.name> - - <!-- Sonar configuration for JavaScript coverage --> - <sonar.javascript.lcov.reportPaths>target/coverage/lcov.info</sonar.javascript.lcov.reportPaths> - <sonar.coverage.exclusions>**/*.test.js,**/test/**/*,**/mocks/**/*</sonar.coverage.exclusions> - <sonar.javascript.file.suffixes>.js</sonar.javascript.file.suffixes> - <sonar.javascript.coverage.overall_condition.branch>55</sonar.javascript.coverage.overall_condition.branch> - <sonar.javascript.coverage.new_condition.branch>55</sonar.javascript.coverage.new_condition.branch> - - <!-- JaCoCo configuration for Java code coverage --> - <sonar.coverage.jacoco.xmlReportPaths>${project.build.directory}/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths> - </properties> - - <dependencies> - <!-- Runtime module dependency --> - <dependency> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>api-sheriff-quarkus</artifactId> - </dependency> - - <!-- Quarkus deployment dependencies --> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-core-deployment</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-arc-deployment</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-micrometer-deployment</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-security-deployment</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-smallrye-health-deployment</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-vertx-http-deployment</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-scheduler-deployment</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-config-yaml-deployment</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-hibernate-validator-deployment</artifactId> - </dependency> - <!-- DevUI integration --> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-vertx-http-dev-ui-spi</artifactId> - </dependency> - - <!-- Test dependencies --> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-junit5-internal</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.jboss.logmanager</groupId> - <artifactId>jboss-logmanager</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <annotationProcessorPaths> - <path> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-extension-processor</artifactId> - </path> - </annotationProcessorPaths> - <!-- Use release instead of source/target to properly handle modules --> - <release>21</release> - </configuration> - </plugin> - <plugin> - <groupId>com.github.eirslett</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <configuration> - <nodeVersion>${frontend.node.version}</nodeVersion> - <npmVersion>${frontend.npm.version}</npmVersion> - <installDirectory>target</installDirectory> - </configuration> - <executions> - <execution> - <id>install-node-and-npm</id> - <goals> - <goal>install-node-and-npm</goal> - </goals> - </execution> - <execution> - <id>npm-install</id> - <goals> - <goal>npm</goal> - </goals> - <configuration> - <arguments>install</arguments> - </configuration> - </execution> - <execution> - <id>npm-test</id> - <goals> - <goal>npm</goal> - </goals> - <phase>test</phase> - <configuration> - <arguments>run test:ci-strict</arguments> - </configuration> - </execution> - <execution> - <id>npm-format-check</id> - <goals> - <goal>npm</goal> - </goals> - <phase>compile</phase> - <configuration> - <arguments>run format:check</arguments> - </configuration> - </execution> - <execution> - <id>npm-css-validate</id> - <goals> - <goal>npm</goal> - </goals> - <phase>compile</phase> - <configuration> - <arguments>run validate:css</arguments> - </configuration> - </execution> - <execution> - <id>npm-lint</id> - <goals> - <goal>npm</goal> - </goals> - <phase>compile</phase> - <configuration> - <arguments>run lint</arguments> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <!-- Override argLine to include JaCoCo agent --> - <argLine>@{argLine} -XX:+IgnoreUnrecognizedVMOptions -Djava.awt.headless=true</argLine> - </configuration> - </plugin> - <!-- Add JaCoCo for code coverage with Sonar --> - <plugin> - <groupId>org.jacoco</groupId> - <artifactId>jacoco-maven-plugin</artifactId> - <executions> - <execution> - <id>prepare-agent</id> - <goals> - <goal>prepare-agent</goal> - </goals> - <configuration> - <!-- Configure JaCoCo to work with deployment tests --> - <includes> - <include>de/cuioss/sheriff/api/quarkus/deployment/**</include> - </includes> - <!-- Ensure JaCoCo agent is properly attached to tests --> - <append>true</append> - <!-- Ensure destination file is set --> - <destFile>${project.build.directory}/jacoco.exec</destFile> - </configuration> - </execution> - <execution> - <id>report</id> - <phase>test</phase> - <goals> - <goal>report</goal> - </goals> - <configuration> - <outputDirectory>${project.build.directory}/site/jacoco</outputDirectory> - <formats> - <format>XML</format> - <format>HTML</format> - </formats> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> -</project> \ No newline at end of file diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-deployment/src/main/java/de/cuioss/sheriff/api/quarkus/deployment/ApiSheriffProcessor.java b/api-sheriff-quarkus-parent/api-sheriff-quarkus-deployment/src/main/java/de/cuioss/sheriff/api/quarkus/deployment/ApiSheriffProcessor.java deleted file mode 100644 index 65d9eaa3..00000000 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-deployment/src/main/java/de/cuioss/sheriff/api/quarkus/deployment/ApiSheriffProcessor.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * <p> - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p> - * https://www.apache.org/licenses/LICENSE-2.0 - * <p> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package de.cuioss.sheriff.api.quarkus.deployment; - -import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.arc.deployment.BeanContainerBuildItem; -import io.quarkus.deployment.annotations.BuildProducer; -import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.annotations.ExecutionTime; -import io.quarkus.deployment.annotations.Record; -import io.quarkus.deployment.builditem.CombinedIndexBuildItem; -import io.quarkus.deployment.builditem.FeatureBuildItem; -import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; - -import de.cuioss.sheriff.api.ApiSheriff; -import de.cuioss.sheriff.api.quarkus.ApiSheriffProducer; - -/** - * Quarkus build step processor for API Sheriff extension. - * This class configures the API Sheriff components for Quarkus applications during build time. - * - * <p>The processor handles:</p> - * <ul> - * <li>Feature registration for the API Sheriff extension</li> - * <li>CDI bean registration for automatic dependency injection</li> - * <li>Native image configuration for GraalVM compilation</li> - * <li>Reflection registration for runtime class access</li> - * </ul> - * - * <h2>Build Time Configuration:</h2> - * <p>This processor runs during Quarkus build time and prepares all necessary - * components for runtime. It ensures that API Sheriff classes are properly - * registered with CDI and configured for native image compilation.</p> - * - * <h2>Native Image Support:</h2> - * <p>The processor automatically registers classes that require reflection - * access during native image execution, ensuring that the API Sheriff - * functionality works correctly in both JVM and native modes.</p> - * - * @author API Sheriff Team - */ -public class ApiSheriffProcessor { - - private static final String FEATURE = "api-sheriff"; - - /** - * Registers the API Sheriff feature with Quarkus. - * This build step creates a feature item that identifies the API Sheriff - * extension in the Quarkus runtime. - * - * @return FeatureBuildItem for API Sheriff - */ - @BuildStep - FeatureBuildItem feature() { - return new FeatureBuildItem(FEATURE); - } - - /** - * Registers API Sheriff classes as CDI beans. - * This build step ensures that all API Sheriff components are available - * for dependency injection in the Quarkus application. - * - * @param additionalBeans producer for additional CDI beans - */ - @BuildStep - void addBeans(BuildProducer<AdditionalBeanBuildItem> additionalBeans) { - // Register only the producer class - it will handle bean creation - additionalBeans.produce(AdditionalBeanBuildItem.unremovableOf(ApiSheriffProducer.class)); - } - - /** - * Registers classes for reflection in native image. - * This build step ensures that API Sheriff classes that require reflection - * access are properly registered for GraalVM native image compilation. - * - * @param reflectiveClass producer for reflective class registration - * @param combinedIndex the combined index of all classes - */ - @BuildStep - void addReflectiveClasses(BuildProducer<ReflectiveClassBuildItem> reflectiveClass, - CombinedIndexBuildItem combinedIndex) { - - // Register main API Sheriff classes for reflection - reflectiveClass.produce(ReflectiveClassBuildItem.builder(ApiSheriff.class) - .constructors() - .methods() - .fields() - .build()); - - - // Register the producer class for CDI proxy generation - reflectiveClass.produce(ReflectiveClassBuildItem.builder(ApiSheriffProducer.class) - .constructors() - .methods() - .fields() - .build()); - } - - -} \ No newline at end of file diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/stop-integration-container.sh b/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/stop-integration-container.sh deleted file mode 100755 index fc818a32..00000000 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/stop-integration-container.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# Stop JWT Integration Tests Docker containers - -set -e - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" - -echo "๐Ÿ›‘ Stopping JWT Integration Tests Docker containers" - -cd "${PROJECT_DIR}" - -# Use the docker-compose.yml file (only file available) -COMPOSE_FILE="docker-compose.yml" -MODE="native" - -# Stop and remove containers -echo "๐Ÿ“ฆ Stopping Docker containers ($MODE mode)..." -docker compose -f "$COMPOSE_FILE" down - -# Optional: Clean up images and volumes -if [ "$1" = "--clean" ]; then - echo "๐Ÿงน Cleaning up Docker images and volumes..." - docker compose -f "$COMPOSE_FILE" down --volumes --rmi all -fi - -echo "โœ… JWT Integration Tests stopped successfully" - -# Show final status -if docker compose -f "$COMPOSE_FILE" ps | grep -q "Up"; then - echo "โš ๏ธ Some containers are still running:" - docker compose -f "$COMPOSE_FILE" ps -else - echo "โœ… All containers are stopped" -fi \ No newline at end of file diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/Dockerfile.native.distroless b/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/Dockerfile.native.distroless deleted file mode 100644 index eb9780e7..00000000 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/Dockerfile.native.distroless +++ /dev/null @@ -1,34 +0,0 @@ -# Simple Dockerfile for copying pre-built native executable -# This approach is much faster as it avoids rebuilding the native image in Docker -FROM quay.io/quarkus/quarkus-distroless-image:2.0 - -LABEL org.opencontainers.image.title="API Sheriff Integration Tests - Distroless" -LABEL org.opencontainers.image.description="Security-hardened Quarkus native application" -LABEL org.opencontainers.image.vendor="CUI" -LABEL org.opencontainers.image.version="1.0.0-SNAPSHOT" -LABEL security.scan.required="true" -LABEL security.distroless="true" -LABEL performance.jfr.enabled="false" - -WORKDIR /app - -# Copy pre-built native executable from Maven target directory -COPY --chmod=0755 api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/target/*-runner /app/application - -# Copy certificates and health check script -COPY --chmod=0755 api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/health-check.sh /app/health-check.sh -COPY --chmod=0644 api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost.crt /app/certificates/localhost.crt -COPY --chmod=0600 api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost.key /app/certificates/localhost.key -COPY --chmod=0644 api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost-truststore.p12 /app/certificates/localhost-truststore.p12 - -EXPOSE 8443 -HEALTHCHECK --interval=15s --timeout=5s --retries=3 --start-period=10s CMD ["/app/health-check.sh"] -USER nonroot - -# Optimized entrypoint for production -ENTRYPOINT ["/app/application", \ - "-Djavax.net.ssl.trustStore=/app/certificates/localhost-truststore.p12", \ - "-Djavax.net.ssl.trustStorePassword=localhost-trust", \ - "-Djavax.net.ssl.trustStoreType=PKCS12", \ - "-Djdk.tls.rejectClientInitiatedRenegotiation=true", \ - "-Djavax.net.ssl.sessionCacheSize=20480"] \ No newline at end of file diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/Dockerfile.native.jfr b/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/Dockerfile.native.jfr deleted file mode 100644 index 33a56f5f..00000000 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/Dockerfile.native.jfr +++ /dev/null @@ -1,36 +0,0 @@ -# Simple Dockerfile for copying pre-built native executable with JFR support -# This approach is much faster as it avoids rebuilding the native image in Docker -FROM quay.io/quarkus/ubi9-quarkus-micro-image:2.0 - -LABEL org.opencontainers.image.title="API Sheriff Integration Tests - JFR" -LABEL org.opencontainers.image.description="Quarkus native application with JFR profiling support" -LABEL org.opencontainers.image.vendor="CUI" -LABEL org.opencontainers.image.version="1.0.0-SNAPSHOT" -LABEL security.scan.required="true" -LABEL performance.jfr.enabled="true" - -WORKDIR /app - -# Copy pre-built native executable from Maven target directory -COPY --chmod=0755 api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/target/*-runner /app/application - -# Copy certificates and health check script -COPY --chmod=0755 api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/health-check.sh /app/health-check.sh -COPY --chmod=0644 api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost.crt /app/certificates/localhost.crt -COPY --chmod=0600 api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost.key /app/certificates/localhost.key -COPY --chmod=0644 api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost-truststore.p12 /app/certificates/localhost-truststore.p12 - -# Create JFR output directory with proper permissions -RUN mkdir -p /tmp/jfr-output && chmod 777 /tmp/jfr-output - -EXPOSE 8443 -HEALTHCHECK --interval=15s --timeout=8s --retries=3 --start-period=15s CMD ["/app/health-check.sh"] -USER 1001 - -# JFR-enabled entrypoint with profiling optimized for benchmarking -ENTRYPOINT ["/app/application", \ - "-Djavax.net.ssl.trustStore=/app/certificates/localhost-truststore.p12", \ - "-Djavax.net.ssl.trustStorePassword=localhost-trust", \ - "-Djavax.net.ssl.trustStoreType=PKCS12", \ - "-XX:+FlightRecorder", \ - "-XX:StartFlightRecording=filename=/tmp/jfr-output/api-sheriff-profile.jfr,dumponexit=true,duration=240s,settings=profile"] \ No newline at end of file diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost-truststore.p12 b/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost-truststore.p12 deleted file mode 100644 index 61d2e5e8..00000000 Binary files a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost-truststore.p12 and /dev/null differ diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost.crt b/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost.crt deleted file mode 100644 index 689c1407..00000000 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost.crt +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDtjCCAp6gAwIBAgIJAKJe1eKGCr7tMA0GCSqGSIb3DQEBDAUAMHMxCzAJBgNV -BAYTAkRFMQ8wDQYDVQQIEwZCZXJsaW4xDzANBgNVBAcTBkJlcmxpbjEQMA4GA1UE -ChMHQ1VJLUpXVDEcMBoGA1UECxMTSW50ZWdyYXRpb24gVGVzdGluZzESMBAGA1UE -AxMJbG9jYWxob3N0MB4XDTI1MDcwNDExMDM1M1oXDTI3MDcwNDExMDM1M1owczEL -MAkGA1UEBhMCREUxDzANBgNVBAgTBkJlcmxpbjEPMA0GA1UEBxMGQmVybGluMRAw -DgYDVQQKEwdDVUktSldUMRwwGgYDVQQLExNJbnRlZ3JhdGlvbiBUZXN0aW5nMRIw -EAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQDpWdNs0eTnFVFFA34eWjU3NzXPzEn0/GQTjC8jfyf766z5WCArwN7AfxIkBhSa -35JSfPdTB3p0LmWUyhm0843BSHD21ZQyICimDxyWSB5BL2DWwPf2qw2KhiLOn9Yc -SQrR4UakW0CF4OmIT98QLIVbsL7baHthmKn7pqNjvfe9Xhvws29bqdakxECwKLTg -vq6Nk1WobVz+phillM3the8wk83a0yBAYlOTVEebD3IXDhjMsC0Fyug0LmWCuX3w -bPcZdf9zjUbGt8h7k3PQ5OjkKlW5HTyRE20wmsOMZmGgQX+OnF5sT2ES6ebjG1lT -jsW3mfxpJyHb1nJXELedTjb7AgMBAAGjTTBLMB0GA1UdDgQWBBTe7icNOG1nJ8EI -mfDSYgfobZKkijAqBgNVHREEIzAhgglsb2NhbGhvc3SCCGtleWNsb2FrhwR/AAAB -hwQAAAAAMA0GCSqGSIb3DQEBDAUAA4IBAQBCaPx5x4LUhIpbfbd2qGlAgOgCbXRx -iDlaYtJTqD/I/d4CwReMADQgoNI+Ey0Whu890cNwIAU6E2uzXcvu5lof2NpebYyr -20mOCpwTKBNnztHZ+nbFBPDEhCe3uoqfRB1qeEOEtjL7KZwNlBLCK6TYNk6jTte3 -D/hCrEKsCxaQUmywKPF/yIQP+YOxZuCI90v/8Y5Qqj9uS2CwHTiSKTFm4JDPA1f1 -sCD/JsdALv1dtKKkO5pbumupyw0ySb0Z3O9D3Ufkg7D1/27iW2YL6fqcsJT6x8Ox -o4R7BzVGOmvHF6CIS3Rd/dvYkgNQDekOGbq+/Et42+T5tYHpdD3WUHTG ------END CERTIFICATE----- diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost.key b/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost.key deleted file mode 100644 index b73c44fe..00000000 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/localhost.key +++ /dev/null @@ -1,32 +0,0 @@ -Bag Attributes - friendlyName: localhost - localKeyID: 54 69 6D 65 20 31 37 35 31 36 32 37 30 33 33 35 31 31 -Key Attributes: <No Attributes> ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDpWdNs0eTnFVFF -A34eWjU3NzXPzEn0/GQTjC8jfyf766z5WCArwN7AfxIkBhSa35JSfPdTB3p0LmWU -yhm0843BSHD21ZQyICimDxyWSB5BL2DWwPf2qw2KhiLOn9YcSQrR4UakW0CF4OmI -T98QLIVbsL7baHthmKn7pqNjvfe9Xhvws29bqdakxECwKLTgvq6Nk1WobVz+phil -lM3the8wk83a0yBAYlOTVEebD3IXDhjMsC0Fyug0LmWCuX3wbPcZdf9zjUbGt8h7 -k3PQ5OjkKlW5HTyRE20wmsOMZmGgQX+OnF5sT2ES6ebjG1lTjsW3mfxpJyHb1nJX -ELedTjb7AgMBAAECggEAGA1FKIs937+nv8hLYI+Fuqo7Jq701IaLPiSN20fI9ENn -cc/uiPP5QbgXQ5VI3Gm86DmvOGSl74G0wLBQFAGik9CGrDp5au07o1odZTQLwkZC -4f/Dzy30WFnZDpkU9ZdlwRpKMLijHul+yKkK4dzk5f2CvpS3WujkZGbZonc7KM5v -BkNWoGBqxCRS8tzX1s7m+f9AOEj45Jz1f2wdd+JBp/MIg+5SBikGIsNAf7BvlpHn -fbKjcFvs3yaM9svDwCCc2ORuvAoAZ8wW3EXCbQQIn2ShCt72LqfP6utYAx0p84tC -wx5HYllDopetWrZ95lNmht8y2mmdR/FsHRI0/CmZAQKBgQD6mTSu1L9kCzb2IBYd -fDWL4nQs2HZDy8W8z5/pYimASNAgy3KAWAfja1800PJn1LwwShb1vnokyoCY+4qp -LMRp9jjVZHA+9ERv9x24BvboI8HJnkEei+1fLvEeHwCOyY2YHzwkw+J4BUK4QKFY -S6tWLbR/2vtlgSkupnuqad+8ewKBgQDuYXLRhBwWo3CZYhxEQg71/Z3hshgtL8Y+ -L2kEE0+kJN4SvcoiS8HtCZ/UynT9kCS7UCbMEM34FoPXd6e0XZ+lcu1w/Gbganpe -rln3QlBS3C2PzMpD7radAVl+rap0QbffKLHgRV+vQ7ixZ8dUkIllXyh2ZIqRW17G -m7A44hSngQKBgQDoA9LAD59HvA5d4CU5lVdqNPbE0oDkkhR4lG6EwOqVqFRyGIrh -gx+CklWqa84TDeZSezY4vesOhyJ7AWFG8njDdkD2aTB3SObYFx4/1Mri8MApsEiw -RHM4ThjVf3SfvsJG1pxzmZzi7FPyjXwUaLKwbk6QlaluOCBt3ZvfknigYwKBgCOQ -4oKkBVTTWc9otfLxMC4/grjTy4uiXx+UD0UOZImG/qMpMelgCDUHhJNJCZ9zTCeu -U7uKnlBve4hAUAM3HMSgmxCKeAbvnAZYWQ/tEvLp6tpTobH1AcX3F5Uw8AecboSb -G77sWtRZdErzwue5EObRBcZ0RcBeM4vKWsaB5LcBAoGBANNebcFUbwGwSFgVY0Ub -hwOfyoEHgrPDbKIwmLOOY2e6NP2kabo1tdrIaLPVWvw0j+bQFsgeoi6tnnpIKWJP -hLcQtB3iD3mk8mKHbr3APqHmK/l6MTKE0zh2ilKhFIVpUVQZKnZV+ktOFaCBjrhl -d3p5s0u8b+r3Xbt0j7+cgsI8 ------END PRIVATE KEY----- diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/resources/application.properties b/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/resources/application.properties deleted file mode 100644 index 80731a05..00000000 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/resources/application.properties +++ /dev/null @@ -1,108 +0,0 @@ -# JWT Integration Test Application Configuration - -# Application metadata -quarkus.application.name=api-sheriff-integration-tests -quarkus.application.version=1.0.0-SNAPSHOT - -# HTTPS Configuration for native image integration tests (using PEM certificates) -quarkus.http.ssl.certificate.files=/app/certificates/localhost.crt -quarkus.http.ssl.certificate.key-files=/app/certificates/localhost.key -quarkus.http.port=8080 -quarkus.http.ssl-port=8443 -# HTTPS Configuration - redirect HTTP requests to HTTPS for security -quarkus.http.insecure-requests=redirect - -# JWT Configuration for Integration Testing -# Default issuer for static key testing (disabled for Docker integration tests) -api.sheriff.issuers.default.issuer-identifier=https://test-auth.example.com -api.sheriff.issuers.default.enabled=false -api.sheriff.issuers.default.jwks.file-path=classpath:test-jwks.json - -# Keycloak issuer for well-known discovery testing (uses default mappers) -api.sheriff.issuers.keycloak.enabled=true -# IMPORTANT: issuer-identifier must match what Keycloak puts in tokens (Docker internal URL) -api.sheriff.issuers.keycloak.issuer-identifier=https://keycloak:8443/realms/benchmark -# JWKS URLs use internal Docker hostname for container-to-container communication -api.sheriff.issuers.keycloak.jwks.http.well-known-url=https://keycloak:8443/realms/benchmark/.well-known/openid-configuration -api.sheriff.issuers.keycloak.expected-client-id=benchmark-client -# Disable default mappers since benchmark realm uses protocol mappers directly -api.sheriff.issuers.keycloak.keycloak.mappers.default-roles.enabled=false -api.sheriff.issuers.keycloak.keycloak.mappers.default-groups.enabled=false -# Make subject claim optional for benchmark realm (Keycloak access tokens don't include 'sub' by default) -api.sheriff.issuers.keycloak.claim-sub-optional=true - -# Integration issuer for direct JWKS URL testing (uses protocol mappers) -api.sheriff.issuers.integration.enabled=true -# IMPORTANT: issuer-identifier must match what Keycloak puts in tokens (Docker internal URL) -api.sheriff.issuers.integration.issuer-identifier=https://keycloak:8443/realms/integration -# JWKS URLs use Docker service name for container-to-container communication -api.sheriff.issuers.integration.jwks.http.url=https://keycloak:8443/realms/integration/protocol/openid-connect/certs -api.sheriff.issuers.integration.expected-client-id=integration-client -# This issuer uses traditional protocol mappers (no default mappers needed) -# Disable default mappers since protocol mappers are configured in realm -api.sheriff.issuers.integration.keycloak.mappers.default-roles.enabled=false -api.sheriff.issuers.integration.keycloak.mappers.default-groups.enabled=false -# Subject claim is required since protocol mapper provides it -api.sheriff.issuers.integration.claim-sub-optional=false - - -# Health Checks -api.sheriff.health.enabled=true -api.sheriff.health.jwks.cache-seconds=30 -api.sheriff.health.jwks.timeout-seconds=5 - -# Security Provider Configuration -# Use default JCA providers for RSA operations - -# TLS Configuration for HTTPS endpoints with proper certificate validation -# Uses modern Quarkus TLS Registry for native image compatibility (research-based) -quarkus.ssl.native=true -quarkus.tls.default.trust-store.p12.path=/app/certificates/localhost-truststore.p12 -quarkus.tls.default.trust-store.p12.password=localhost-trust - -# Metrics Configuration -# Set faster collection interval for integration tests (2s instead of default 10s) -api.sheriff.metrics.collection.interval=2s - -# Cache Configuration for testing -# DISABLED for benchmark accuracy - set to 0 to force full validation on every request -api.sheriff.cache.access-token.max-size=0 -api.sheriff.cache.access-token.eviction-interval-seconds=10 - -# Logging -quarkus.log.level=INFO -quarkus.log.category."de.cuioss.sheriff".level=INFO - -# Development settings -quarkus.live-reload.instrumentation=false - -# Native Image Configuration -# Enable HTTPS protocol for JWT well-known discovery and JWKS endpoints -# TESTING: INFO logging level impact on performance (was DEBUG) -quarkus.native.additional-build-args=--enable-url-protocols=https,--enable-http,--enable-https,-O2 - -# JFR Configuration for Native Images (2024-2025 best practices) -quarkus.native.monitoring=jfr - -# TLS Performance Optimization -# Enable TLS 1.3 with 1.2 fallback for better handshake performance -quarkus.tls.protocols=TLSv1.3,TLSv1.2 -# Optimize cipher suites for performance (TLS 1.3 prioritized) -quarkus.tls.cipher-suites=TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 -# Enable ALPN for HTTP/2 support -quarkus.tls.alpn=true -# Include certificates in native image -quarkus.native.resources.includes=**/*.p12,**/*.crt,**/*.key -# Container memory limit removed for build - will be applied at runtime via Docker -# Build requires unrestricted memory, runtime testing uses Docker --memory=64m - -# REST Configuration (default: /) - -# Virtual Threads Configuration (VERIFIED: 24-30% improvement) -quarkus.virtual-threads.name-prefix=jwt-validation -quarkus.virtual-threads.shutdown-timeout=10s - -# REMOVED UNVERIFIED OPTIMIZATIONS FOR TESTING: -# - JFR monitoring: --enable-monitoring=jfr (needs verification) -# - Compiler optimization: -O2 (needs verification) -# - Memory runtime options: -m=256m (needs verification) diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus/pom.xml b/api-sheriff-quarkus-parent/api-sheriff-quarkus/pom.xml deleted file mode 100644 index d62316bb..00000000 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus/pom.xml +++ /dev/null @@ -1,203 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>api-sheriff-quarkus-parent</artifactId> - <version>1.0.0-SNAPSHOT</version> - <relativePath>../pom.xml</relativePath> - </parent> - - <artifactId>api-sheriff-quarkus</artifactId> - <packaging>jar</packaging> - <name>API Sheriff Quarkus Integration - Runtime</name> - <description>Runtime module for Quarkus integration for API Sheriff, providing configuration, producers, - and integration from SecurityEvents to Micrometer - </description> - - <properties> - <maven.jar.plugin.automatic.module.name>de.cuioss.sheriff.api.quarkus</maven.jar.plugin.automatic.module.name> - </properties> - - <dependencies> - <!-- Test dependencies --> - <dependency> - <groupId>io.rest-assured</groupId> - <artifactId>rest-assured</artifactId> - <scope>test</scope> - </dependency> - - <!-- Other test dependencies --> - <dependency> - <groupId>org.jboss.logmanager</groupId> - <artifactId>jboss-logmanager</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.jboss.weld</groupId> - <artifactId>weld-junit5</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>api-sheriff-library</artifactId> - <version>${project.version}</version> - <classifier>generators</classifier> - <scope>test</scope> - </dependency> - - <!-- Runtime-specific dependencies can be added here --> - <!-- All common dependencies are inherited from parent --> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-scheduler</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-config-yaml</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-hibernate-validator</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-smallrye-health</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-vertx-http</artifactId> - <optional>true</optional> - </dependency> - <dependency> - <groupId>jakarta.servlet</groupId> - <artifactId>jakarta.servlet-api</artifactId> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy</artifactId> - <optional>true</optional> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-jacoco</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.awaitility</groupId> - <artifactId>awaitility</artifactId> - <scope>test</scope> - </dependency> - <!-- JJWT dependencies. Reduced to testing --> - <dependency> - <groupId>io.jsonwebtoken</groupId> - <artifactId>jjwt-api</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>io.jsonwebtoken</groupId> - <artifactId>jjwt-impl</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>io.jsonwebtoken</groupId> - <artifactId>jjwt-jackson</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>de.cuioss.test</groupId> - <artifactId>cui-test-generator</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.easymock</groupId> - <artifactId>easymock</artifactId> - <version>${version.easymock}</version> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-maven-plugin</artifactId> - <!-- Use inherited configuration from pluginManagement --> - </plugin> - <plugin> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-extension-maven-plugin</artifactId> - <version>${version.quarkus}</version> - <executions> - <execution> - <phase>compile</phase> - <goals> - <goal>extension-descriptor</goal> - </goals> - <configuration> - <deployment>${project.groupId}:api-sheriff-quarkus-deployment:${project.version}</deployment> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <annotationProcessorPaths> - <path> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-extension-processor</artifactId> - </path> - </annotationProcessorPaths> - <!-- Use release instead of source/target to properly handle modules --> - <release>21</release> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <argLine>@{argLine} -XX:+IgnoreUnrecognizedVMOptions -Djava.awt.headless=true</argLine> - <systemPropertyVariables> - <quarkus.jacoco.reuse-data-file>true</quarkus.jacoco.reuse-data-file> - </systemPropertyVariables> - </configuration> - </plugin> - <!-- JaCoCo Plugin for test coverage --> - <plugin> - <groupId>org.jacoco</groupId> - <artifactId>jacoco-maven-plugin</artifactId> - <executions> - <execution> - <id>default-prepare-agent</id> - <goals> - <goal>prepare-agent</goal> - </goals> - </execution> - <execution> - <id>default-report</id> - <phase>test</phase> - <goals> - <goal>report</goal> - </goals> - <configuration> - <formats> - <format>XML</format> - <format>HTML</format> - </formats> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> -</project> \ No newline at end of file diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus/src/main/java/de/cuioss/sheriff/api/quarkus/ApiSheriffRecorder.java b/api-sheriff-quarkus-parent/api-sheriff-quarkus/src/main/java/de/cuioss/sheriff/api/quarkus/ApiSheriffRecorder.java deleted file mode 100644 index e69de29b..00000000 diff --git a/api-sheriff-quarkus-parent/pom.xml b/api-sheriff-quarkus-parent/pom.xml deleted file mode 100644 index c237529a..00000000 --- a/api-sheriff-quarkus-parent/pom.xml +++ /dev/null @@ -1,334 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>api-sheriff-parent</artifactId> - <version>1.0.0-SNAPSHOT</version> - <relativePath>../pom.xml</relativePath> - </parent> - - <artifactId>api-sheriff-quarkus-parent</artifactId> - <packaging>pom</packaging> - <name>API Sheriff Quarkus Integration Parent</name> - <description>Parent module for Quarkus integration for API Sheriff, providing configuration, producers, and integration from SecurityEvents to Micrometer</description> - - <modules> - <module>api-sheriff-quarkus</module> - <module>api-sheriff-quarkus-deployment</module> - <module>api-sheriff-quarkus-integration-tests</module> - </modules> - - <properties> - <!-- - Logging configuration: - - Use JBoss LogManager for tests (java.util.logging.manager=org.jboss.logmanager.LogManager) - - Configure logging.properties in each module's test resources - - Suppress JVM warnings with -XX:+IgnoreUnrecognizedVMOptions - --> - <junit-platform.version>1.13.1</junit-platform.version> - <version.weld-junit5>5.0.1.Final</version.weld-junit5> - <version.easymock>5.6.0</version.easymock> - <!-- Default empty argLine for JaCoCo compatibility - JaCoCo will override this when active --> - <argLine></argLine> - </properties> - - <dependencyManagement> - <dependencies> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-bom</artifactId> - <version>${version.quarkus}</version> - <type>pom</type> - <scope>import</scope> - </dependency> - <dependency> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>bom</artifactId> - <version>${project.version}</version> - <type>pom</type> - <scope>import</scope> - </dependency> - <!-- JUnit BOM is already imported by quarkus-bom --> - <!-- Explicitly override JUnit versions to ensure all components are compatible--> - <dependency> - <groupId>org.junit.platform</groupId> - <artifactId>junit-platform-commons</artifactId> - <version>${junit-platform.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.platform</groupId> - <artifactId>junit-platform-engine</artifactId> - <version>${junit-platform.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.junit.platform</groupId> - <artifactId>junit-platform-launcher</artifactId> - <version>${junit-platform.version}</version> - <scope>test</scope> - </dependency> - <!-- Additional dependencies --> - <dependency> - <groupId>org.jboss.weld</groupId> - <artifactId>weld-junit5</artifactId> - <version>${version.weld-junit5}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-extension-processor</artifactId> - <version>${version.quarkus}</version> - </dependency> - </dependencies> - </dependencyManagement> - - <dependencies> - <!-- Internal module dependencies --> - <dependency> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>api-sheriff-library</artifactId> - </dependency> - - <!-- Quarkus dependencies --> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-core</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-arc</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-micrometer</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-security</artifactId> - </dependency> - - <!-- Other dependencies --> - <dependency> - <groupId>org.projectlombok</groupId> - <artifactId>lombok</artifactId> - </dependency> - - <!-- Test dependencies --> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-junit5</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>de.cuioss.test</groupId> - <artifactId>cui-test-juli-logger</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <pluginManagement> - <plugins> - <plugin> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-maven-plugin</artifactId> - <version>${version.quarkus}</version> - <extensions>true</extensions> - <executions> - <execution> - <goals> - <goal>build</goal> - <goal>generate-code</goal> - <goal>generate-code-tests</goal> - </goals> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <!-- Use release instead of source/target to properly handle modules --> - <release>21</release> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <systemPropertyVariables> - <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> - <java.util.logging.config.file>${project.build.testOutputDirectory}/logging.properties</java.util.logging.config.file> - <maven.home>${maven.home}</maven.home> - <quarkus.test.arg-line>@{argLine}</quarkus.test.arg-line> - </systemPropertyVariables> - <useModulePath>false</useModulePath> - <useFile>false</useFile> - <trimStackTrace>false</trimStackTrace> - <enableAssertions>true</enableAssertions> - <!-- JaCoCo-compatible argLine (will be empty if JaCoCo not active) --> - <argLine>@{argLine} -XX:+IgnoreUnrecognizedVMOptions -Djava.awt.headless=true</argLine> - <!-- Stability configurations --> - <forkedProcessTimeoutInSeconds>0</forkedProcessTimeoutInSeconds> - <forkedProcessExitTimeoutInSeconds>60</forkedProcessExitTimeoutInSeconds> - <!-- Fix for class loading issues --> - <useSystemClassLoader>false</useSystemClassLoader> - <reuseForks>false</reuseForks> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-failsafe-plugin</artifactId> - <configuration> - <systemPropertyVariables> - <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> - <java.util.logging.config.file>${project.build.testOutputDirectory}/logging.properties</java.util.logging.config.file> - <maven.home>${maven.home}</maven.home> - <quarkus.test.arg-line>@{argLine}</quarkus.test.arg-line> - </systemPropertyVariables> - <useModulePath>false</useModulePath> - <useFile>false</useFile> - <trimStackTrace>false</trimStackTrace> - <enableAssertions>true</enableAssertions> - <argLine>@{argLine} -XX:+IgnoreUnrecognizedVMOptions -Djava.awt.headless=true</argLine> - <!-- Fix for class loading issues --> - <useSystemClassLoader>false</useSystemClassLoader> - <reuseForks>false</reuseForks> - <!-- Standardized IT naming patterns --> - <includes> - <include>**/*IT.java</include> - <include>**/*IntegrationTest.java</include> - </includes> - </configuration> - <executions> - <execution> - <goals> - <goal>integration-test</goal> - <goal>verify</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </pluginManagement> - <!-- No plugins in parent - all configurations inherited from pluginManagement --> - </build> - - <profiles> - <profile> - <id>coverage</id> - <build> - <pluginManagement> - <plugins> - <plugin> - <groupId>org.jacoco</groupId> - <artifactId>jacoco-maven-plugin</artifactId> - <executions> - <execution> - <id>prepare-agent</id> - <goals> - <goal>prepare-agent</goal> - </goals> - <configuration> - <append>true</append> - <destFile>${project.build.directory}/jacoco.exec</destFile> - </configuration> - </execution> - <execution> - <id>prepare-agent-integration</id> - <goals> - <goal>prepare-agent-integration</goal> - </goals> - <configuration> - <destFile>${project.build.directory}/jacoco-it.exec</destFile> - <append>true</append> - </configuration> - </execution> - <execution> - <id>report</id> - <phase>test</phase> - <goals> - <goal>report</goal> - </goals> - <configuration> - <outputDirectory>${project.build.directory}/site/jacoco</outputDirectory> - <formats> - <format>XML</format> - <format>HTML</format> - </formats> - </configuration> - </execution> - <execution> - <id>report-integration</id> - <phase>post-integration-test</phase> - <goals> - <goal>report-integration</goal> - </goals> - <configuration> - <outputDirectory>${project.build.directory}/site/jacoco-it</outputDirectory> - <formats> - <format>XML</format> - <format>HTML</format> - </formats> - </configuration> - </execution> - <execution> - <id>merge-results</id> - <phase>verify</phase> - <goals> - <goal>merge</goal> - </goals> - <configuration> - <fileSets> - <fileSet> - <directory>${project.build.directory}</directory> - <includes> - <include>*.exec</include> - </includes> - </fileSet> - </fileSets> - <destFile>${project.build.directory}/jacoco-merged.exec</destFile> - </configuration> - </execution> - <execution> - <id>report-merged</id> - <phase>verify</phase> - <goals> - <goal>report</goal> - </goals> - <configuration> - <dataFile>${project.build.directory}/jacoco-merged.exec</dataFile> - <outputDirectory>${project.build.directory}/site/jacoco-merged</outputDirectory> - <formats> - <format>XML</format> - <format>HTML</format> - </formats> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </pluginManagement> - <plugins> - <plugin> - <groupId>org.jacoco</groupId> - <artifactId>jacoco-maven-plugin</artifactId> - </plugin> - </plugins> - </build> - <properties> - <!-- Multiple XML report paths for Sonar to pick up coverage from all modules --> - <sonar.coverage.jacoco.xmlReportPaths> - ${project.build.directory}/site/jacoco/jacoco.xml, - api-sheriff-quarkus/target/site/jacoco/jacoco.xml, - api-sheriff-quarkus-deployment/target/site/jacoco/jacoco.xml, - ../target/site/jacoco/jacoco.xml - </sonar.coverage.jacoco.xmlReportPaths> - </properties> - </profile> - </profiles> -</project> \ No newline at end of file diff --git a/api-sheriff-library/pom.xml b/api-sheriff/pom.xml similarity index 64% rename from api-sheriff-library/pom.xml rename to api-sheriff/pom.xml index 4e19ccc1..aeaa4453 100644 --- a/api-sheriff-library/pom.xml +++ b/api-sheriff/pom.xml @@ -10,28 +10,17 @@ <relativePath>../pom.xml</relativePath> </parent> - <artifactId>api-sheriff-library</artifactId> + <artifactId>api-sheriff</artifactId> <packaging>jar</packaging> - <name>API Sheriff Library</name> - <description>Core library for API Sheriff gateway with security features</description> + <name>API Sheriff</name> + <description>Unified Quarkus application module for API Sheriff gateway with security features</description> <properties> <maven.jar.plugin.automatic.module.name>de.cuioss.sheriff.api</maven.jar.plugin.automatic.module.name> </properties> - <dependencyManagement> - <dependencies> - <dependency> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>bom</artifactId> - <version>${project.version}</version> - <type>pom</type> - <scope>import</scope> - </dependency> - </dependencies> - </dependencyManagement> - <dependencies> + <!-- Core library dependencies --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> @@ -40,98 +29,86 @@ <groupId>de.cuioss</groupId> <artifactId>cui-java-tools</artifactId> </dependency> - <dependency> - <groupId>jakarta.json</groupId> - <artifactId>jakarta.json-api</artifactId> - <scope>provided</scope> - </dependency> - <!-- Unit testing --> + <!-- Quarkus dependencies --> <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter</artifactId> - <scope>test</scope> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-core</artifactId> </dependency> - <!-- JJWT dependencies. Reduced to testing --> <dependency> - <groupId>io.jsonwebtoken</groupId> - <artifactId>jjwt-api</artifactId> - <scope>test</scope> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-arc</artifactId> </dependency> <dependency> - <groupId>io.jsonwebtoken</groupId> - <artifactId>jjwt-impl</artifactId> - <scope>test</scope> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-resteasy</artifactId> </dependency> <dependency> - <groupId>io.jsonwebtoken</groupId> - <artifactId>jjwt-jackson</artifactId> - <scope>test</scope> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-resteasy-jackson</artifactId> </dependency> - <!-- Test --> <dependency> - <groupId>de.cuioss.test</groupId> - <artifactId>cui-test-mockwebserver-junit5</artifactId> - <scope>test</scope> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-smallrye-health</artifactId> </dependency> - <!-- Implementation of jakarta.json-api--> - <!-- The concrete is up to the integration--> <dependency> - <groupId>org.eclipse.parsson</groupId> - <artifactId>parsson</artifactId> - <scope>test</scope> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-micrometer</artifactId> </dependency> <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter-params</artifactId> - <scope>test</scope> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-micrometer-registry-prometheus</artifactId> </dependency> + + <!-- Test dependencies --> <dependency> - <groupId>de.cuioss.test</groupId> - <artifactId>cui-test-value-objects</artifactId> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter</artifactId> <scope>test</scope> </dependency> <dependency> - <groupId>io.rest-assured</groupId> - <artifactId>rest-assured</artifactId> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-junit5</artifactId> <scope>test</scope> </dependency> <dependency> - <groupId>jakarta.servlet</groupId> - <artifactId>jakarta.servlet-api</artifactId> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-jacoco</artifactId> <scope>test</scope> </dependency> + <!-- CUI test dependencies --> <dependency> <groupId>de.cuioss.test</groupId> - <artifactId>cui-test-generator</artifactId> + <artifactId>cui-test-mockwebserver-junit5</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>de.cuioss.test</groupId> - <artifactId>cui-test-juli-logger</artifactId> - <scope>test</scope> - </dependency> - <!-- Simple implementation that is actually a bridge to juli logging. Used for test-containers slf4j --> - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>slf4j-jdk14</artifactId> + <artifactId>cui-test-value-objects</artifactId> <scope>test</scope> </dependency> - <!-- Bridge jakarta-commons logging to slf4j --> <dependency> - <groupId>org.slf4j</groupId> - <artifactId>jcl-over-slf4j</artifactId> + <groupId>de.cuioss.test</groupId> + <artifactId>cui-test-generator</artifactId> <scope>test</scope> </dependency> <dependency> - <groupId>org.awaitility</groupId> - <artifactId>awaitility</artifactId> + <groupId>de.cuioss.test</groupId> + <artifactId>cui-test-juli-logger</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> + <plugin> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>io.smallrye</groupId> + <artifactId>jandex-maven-plugin</artifactId> + </plugin> <!-- Create test-jar artifact with test utilities --> <plugin> <groupId>org.apache.maven.plugins</groupId> @@ -196,7 +173,42 @@ </execution> </executions> </plugin> + <!-- JaCoCo Plugin for test coverage --> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <executions> + <execution> + <id>default-prepare-agent</id> + <goals> + <goal>prepare-agent</goal> + </goals> + </execution> + <execution> + <id>default-report</id> + <phase>test</phase> + <goals> + <goal>report</goal> + </goals> + <configuration> + <formats> + <format>XML</format> + <format>HTML</format> + </formats> + </configuration> + </execution> + </executions> + </plugin> </plugins> </build> -</project> \ No newline at end of file + <profiles> + <profile> + <id>native</id> + <properties> + <quarkus.native.container-build>true</quarkus.native.container-build> + <quarkus.native.enabled>true</quarkus.native.enabled> + </properties> + </profile> + </profiles> +</project> diff --git a/api-sheriff/src/main/docker/Dockerfile.native b/api-sheriff/src/main/docker/Dockerfile.native new file mode 100644 index 00000000..9d913700 --- /dev/null +++ b/api-sheriff/src/main/docker/Dockerfile.native @@ -0,0 +1,25 @@ +# Production Dockerfile for API Sheriff native executable +# Distroless: no shell, no package manager โ€” minimal attack surface +# No baked-in certificates โ€” mount TLS certs at runtime via volumes or secrets +# Health probes via management interface (port 9000, plain HTTP) +FROM quay.io/quarkus/quarkus-distroless-image:2.0@sha256:0278fd70d9627df836295570da0fd22034ea664efc12a834a0532cea7556a87b + +LABEL org.opencontainers.image.title="API Sheriff" +LABEL org.opencontainers.image.description="Security-focused API Gateway โ€” Quarkus native executable" +LABEL org.opencontainers.image.version="1.0.0-SNAPSHOT" +LABEL org.opencontainers.image.vendor="CUIoss" +LABEL org.opencontainers.image.licenses="Apache-2.0" +LABEL org.opencontainers.image.source="https://github.com/cuioss/API-Sheriff" +LABEL org.opencontainers.image.base.name="quay.io/quarkus/quarkus-distroless-image:2.0" +LABEL security.scan.required="true" +LABEL security.distroless="true" + +WORKDIR /app + +# Copy pre-built native executable +COPY --chmod=0755 target/*-runner /app/application + +EXPOSE 8443 9000 +USER nonroot + +ENTRYPOINT ["/app/application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/api-sheriff/src/main/docker/Dockerfile.native.jfr b/api-sheriff/src/main/docker/Dockerfile.native.jfr new file mode 100644 index 00000000..14c49c67 --- /dev/null +++ b/api-sheriff/src/main/docker/Dockerfile.native.jfr @@ -0,0 +1,35 @@ +# JFR-enabled Dockerfile for API Sheriff native executable +# UBI micro base (has shell, needed for HEALTHCHECK and JFR output) +# No baked-in certificates โ€” mount TLS certs at runtime via volumes or secrets +FROM quay.io/quarkus/ubi9-quarkus-micro-image:2.0@sha256:7d6c45257b2c8a27b05ac544a1a713abc2d36371f01675fb4497fbb385ae2046 + +LABEL org.opencontainers.image.title="API Sheriff - JFR" +LABEL org.opencontainers.image.description="Security-focused API Gateway โ€” Quarkus native executable with JFR profiling" +LABEL org.opencontainers.image.version="1.0.0-SNAPSHOT" +LABEL org.opencontainers.image.vendor="CUIoss" +LABEL org.opencontainers.image.licenses="Apache-2.0" +LABEL org.opencontainers.image.source="https://github.com/cuioss/API-Sheriff" +LABEL org.opencontainers.image.base.name="quay.io/quarkus/ubi9-quarkus-micro-image:2.0" +LABEL security.scan.required="true" +LABEL performance.jfr.enabled="true" + +WORKDIR /app + +# Copy pre-built native executable +COPY --chown=1001:root --chmod=0755 target/*-runner /app/application + +# Create JFR output directory with proper permissions +RUN mkdir -p /tmp/jfr-output && chmod 777 /tmp/jfr-output + +EXPOSE 8443 9000 + +# Port probe health check using bash /dev/tcp (available in UBI micro) +HEALTHCHECK --interval=15s --timeout=5s --start-period=15s --retries=3 \ + CMD echo -n '' > /dev/tcp/127.0.0.1/8443 2>/dev/null || exit 1 + +USER 1001 + +# JFR-enabled entrypoint with profiling optimized for benchmarking +ENTRYPOINT ["/app/application", "-Dquarkus.http.host=0.0.0.0", \ + "-XX:+FlightRecorder", \ + "-XX:StartFlightRecording=filename=/tmp/jfr-output/api-sheriff-profile.jfr,dumponexit=true,duration=240s,settings=profile"] diff --git a/api-sheriff-library/src/main/java/de/cuioss/sheriff/api/ApiSheriff.java b/api-sheriff/src/main/java/de/cuioss/sheriff/api/ApiSheriff.java similarity index 75% rename from api-sheriff-library/src/main/java/de/cuioss/sheriff/api/ApiSheriff.java rename to api-sheriff/src/main/java/de/cuioss/sheriff/api/ApiSheriff.java index d07a2cb1..32ae830a 100644 --- a/api-sheriff-library/src/main/java/de/cuioss/sheriff/api/ApiSheriff.java +++ b/api-sheriff/src/main/java/de/cuioss/sheriff/api/ApiSheriff.java @@ -1,12 +1,12 @@ /* - * Copyright 2025 the original author or authors. - * <p> + * Copyright ยฉ 2025 CUI-OpenSource-Software (info@cuioss.de) + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * <p> - * https://www.apache.org/licenses/LICENSE-2.0 - * <p> + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,6 +15,7 @@ */ package de.cuioss.sheriff.api; +import io.quarkus.runtime.annotations.RegisterForReflection; import de.cuioss.tools.logging.CuiLogger; /** @@ -23,15 +24,16 @@ * * @author API Sheriff Team */ +@RegisterForReflection public class ApiSheriff { - private static final CuiLogger log = new CuiLogger(ApiSheriff.class); + private static final CuiLogger LOGGER = new CuiLogger(ApiSheriff.class); /** * Creates a new ApiSheriff instance. */ public ApiSheriff() { - log.debug("ApiSheriff initialized"); + LOGGER.debug("ApiSheriff initialized"); } /** @@ -42,4 +44,4 @@ public ApiSheriff() { public String getStatus() { return "API Sheriff is operational"; } -} \ No newline at end of file +} diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/java/de/cuioss/sheriff/api/integration/TestApplication.java b/api-sheriff/src/main/java/de/cuioss/sheriff/api/ApiSheriffApplication.java similarity index 70% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/java/de/cuioss/sheriff/api/integration/TestApplication.java rename to api-sheriff/src/main/java/de/cuioss/sheriff/api/ApiSheriffApplication.java index 9bf1a2f2..9885bc3a 100644 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/java/de/cuioss/sheriff/api/integration/TestApplication.java +++ b/api-sheriff/src/main/java/de/cuioss/sheriff/api/ApiSheriffApplication.java @@ -1,38 +1,39 @@ /* - * Copyright 2025 the original author or authors. - * <p> + * Copyright ยฉ 2025 CUI-OpenSource-Software (info@cuioss.de) + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * <p> - * https://www.apache.org/licenses/LICENSE-2.0 - * <p> + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package de.cuioss.sheriff.api.integration; +package de.cuioss.sheriff.api; import io.quarkus.runtime.Quarkus; import io.quarkus.runtime.QuarkusApplication; import io.quarkus.runtime.annotations.QuarkusMain; /** - * Test application for API Sheriff integration testing. + * Main entry point for the API Sheriff gateway application. * <p> - * This application provides endpoints for testing API Sheriff functionality, + * This application provides the security-focused API Gateway with REST endpoints, * health checks, and metrics in a containerized environment. * * @author API Sheriff Team + * @since 1.0 */ @QuarkusMain -public class TestApplication implements QuarkusApplication { +public class ApiSheriffApplication implements QuarkusApplication { @Override public int run(String... args) throws Exception { Quarkus.waitForExit(); return 0; } -} \ No newline at end of file +} diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/java/de/cuioss/sheriff/api/integration/TestResource.java b/api-sheriff/src/main/java/de/cuioss/sheriff/api/gateway/GatewayResource.java similarity index 58% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/java/de/cuioss/sheriff/api/integration/TestResource.java rename to api-sheriff/src/main/java/de/cuioss/sheriff/api/gateway/GatewayResource.java index 92a58ec3..d76f1034 100644 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/java/de/cuioss/sheriff/api/integration/TestResource.java +++ b/api-sheriff/src/main/java/de/cuioss/sheriff/api/gateway/GatewayResource.java @@ -1,19 +1,19 @@ /* - * Copyright 2025 the original author or authors. - * <p> + * Copyright ยฉ 2025 CUI-OpenSource-Software (info@cuioss.de) + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * <p> - * https://www.apache.org/licenses/LICENSE-2.0 - * <p> + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package de.cuioss.sheriff.api.integration; +package de.cuioss.sheriff.api.gateway; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -27,59 +27,53 @@ import de.cuioss.sheriff.api.ApiSheriff; /** - * REST resource for testing API Sheriff integration in Quarkus applications. + * REST resource for the API Sheriff gateway providing health and info endpoints. * * @author API Sheriff Team + * @since 1.0 */ -@Path("/test") +@Path("/api") @ApplicationScoped @RegisterForReflection @Produces(MediaType.APPLICATION_JSON) -public class TestResource { +public class GatewayResource { @Inject ApiSheriff apiSheriff; /** * Health check endpoint to verify that API Sheriff is properly configured. - * + * * @return Response indicating the health status of API Sheriff */ @GET @Path("/health") @Produces(MediaType.APPLICATION_JSON) public Response health() { - try { - // Verify that ApiSheriff is properly injected and configured - if (apiSheriff == null) { - return Response.status(Response.Status.SERVICE_UNAVAILABLE) + // Verify that ApiSheriff is properly injected and configured + if (apiSheriff == null) { + return Response.status(Response.Status.SERVICE_UNAVAILABLE) .entity("{\"status\":\"DOWN\",\"reason\":\"ApiSheriff not available\"}") .build(); - } + } - // Get status to verify it's working - String status = apiSheriff.getStatus(); - - return Response.ok("{\"status\":\"UP\",\"apiSheriff\":\"" + status + "\"}") - .build(); + // Get status to verify it's working + String status = apiSheriff.getStatus(); - } catch (Exception e) { - return Response.status(Response.Status.INTERNAL_SERVER_ERROR) - .entity("{\"status\":\"DOWN\",\"error\":\"" + e.getMessage() + "\"}") + return Response.ok("{\"status\":\"UP\",\"apiSheriff\":\"" + status + "\"}") .build(); - } } /** - * Test endpoint for basic API Sheriff functionality. - * - * @return Response with test information + * Info endpoint for basic API Sheriff status. + * + * @return Response with application information */ @GET @Path("/info") @Produces(MediaType.APPLICATION_JSON) public Response info() { - return Response.ok("{\"message\":\"API Sheriff Integration Test\",\"version\":\"1.0.0-SNAPSHOT\"}") - .build(); + return Response.ok("{\"message\":\"API Sheriff Gateway\",\"version\":\"1.0.0-SNAPSHOT\"}") + .build(); } -} \ No newline at end of file +} diff --git a/api-sheriff/src/main/java/de/cuioss/sheriff/api/gateway/package-info.java b/api-sheriff/src/main/java/de/cuioss/sheriff/api/gateway/package-info.java new file mode 100644 index 00000000..899e71e4 --- /dev/null +++ b/api-sheriff/src/main/java/de/cuioss/sheriff/api/gateway/package-info.java @@ -0,0 +1,25 @@ +/* + * Copyright ยฉ 2025 CUI-OpenSource-Software (info@cuioss.de) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Gateway REST endpoints for the API Sheriff application. + * <p> + * This package contains the JAX-RS resources that serve as the external API + * for the API Sheriff gateway, including health and info endpoints. + * + * @since 1.0 + */ +package de.cuioss.sheriff.api.gateway; diff --git a/api-sheriff-library/src/main/java/de/cuioss/sheriff/api/package-info.java b/api-sheriff/src/main/java/de/cuioss/sheriff/api/package-info.java similarity index 80% rename from api-sheriff-library/src/main/java/de/cuioss/sheriff/api/package-info.java rename to api-sheriff/src/main/java/de/cuioss/sheriff/api/package-info.java index 27b5a4f1..3bd1d8b9 100644 --- a/api-sheriff-library/src/main/java/de/cuioss/sheriff/api/package-info.java +++ b/api-sheriff/src/main/java/de/cuioss/sheriff/api/package-info.java @@ -1,12 +1,12 @@ /* - * Copyright 2025 the original author or authors. - * <p> + * Copyright ยฉ 2025 CUI-OpenSource-Software (info@cuioss.de) + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * <p> - * https://www.apache.org/licenses/LICENSE-2.0 - * <p> + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,9 +15,9 @@ */ /** * Main package for the API Sheriff library. - * + * * This package contains the core functionality of the API Sheriff project. - * + * * @author API Sheriff Team */ -package de.cuioss.sheriff.api; \ No newline at end of file +package de.cuioss.sheriff.api; diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus/src/main/java/de/cuioss/sheriff/api/quarkus/ApiSheriffProducer.java b/api-sheriff/src/main/java/de/cuioss/sheriff/api/quarkus/ApiSheriffProducer.java similarity index 77% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus/src/main/java/de/cuioss/sheriff/api/quarkus/ApiSheriffProducer.java rename to api-sheriff/src/main/java/de/cuioss/sheriff/api/quarkus/ApiSheriffProducer.java index c71ec965..82942a03 100644 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus/src/main/java/de/cuioss/sheriff/api/quarkus/ApiSheriffProducer.java +++ b/api-sheriff/src/main/java/de/cuioss/sheriff/api/quarkus/ApiSheriffProducer.java @@ -1,12 +1,12 @@ /* - * Copyright 2025 the original author or authors. - * <p> + * Copyright ยฉ 2025 CUI-OpenSource-Software (info@cuioss.de) + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * <p> - * https://www.apache.org/licenses/LICENSE-2.0 - * <p> + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,11 +15,13 @@ */ package de.cuioss.sheriff.api.quarkus; -import de.cuioss.sheriff.api.ApiSheriff; -import de.cuioss.tools.logging.CuiLogger; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Produces; +import io.quarkus.runtime.annotations.RegisterForReflection; +import de.cuioss.sheriff.api.ApiSheriff; +import de.cuioss.tools.logging.CuiLogger; + /** * CDI producer for API Sheriff components. * This class provides CDI integration for the API Sheriff library in Quarkus applications. @@ -27,9 +29,10 @@ * @author API Sheriff Team */ @ApplicationScoped +@RegisterForReflection public class ApiSheriffProducer { - private static final CuiLogger log = new CuiLogger(ApiSheriffProducer.class); + private static final CuiLogger LOGGER = new CuiLogger(ApiSheriffProducer.class); /** * Produces an application-scoped ApiSheriff instance. @@ -39,7 +42,7 @@ public class ApiSheriffProducer { @Produces @ApplicationScoped public ApiSheriff produceApiSheriff() { - log.debug("Creating ApiSheriff instance"); + LOGGER.debug("Creating ApiSheriff instance"); return new ApiSheriff(); } -} \ No newline at end of file +} diff --git a/api-sheriff-library/src/main/java/module-info.java b/api-sheriff/src/main/java/de/cuioss/sheriff/api/quarkus/package-info.java similarity index 79% rename from api-sheriff-library/src/main/java/module-info.java rename to api-sheriff/src/main/java/de/cuioss/sheriff/api/quarkus/package-info.java index cdf9b1ac..48653699 100644 --- a/api-sheriff-library/src/main/java/module-info.java +++ b/api-sheriff/src/main/java/de/cuioss/sheriff/api/quarkus/package-info.java @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -module de.cuioss.sheriff.api { - exports de.cuioss.sheriff.api; - - requires static lombok; - requires de.cuioss.java.tools; - requires java.logging; -} \ No newline at end of file +/** + * Quarkus CDI integration for the API Sheriff library. + * + * @author API Sheriff Team + */ +package de.cuioss.sheriff.api.quarkus; diff --git a/api-sheriff/src/main/resources/application.properties b/api-sheriff/src/main/resources/application.properties new file mode 100644 index 00000000..57b9a98b --- /dev/null +++ b/api-sheriff/src/main/resources/application.properties @@ -0,0 +1,43 @@ +# API Sheriff Application Configuration + +# Application metadata +quarkus.application.name=api-sheriff + +# HTTP/HTTPS Configuration +quarkus.http.port=8080 +quarkus.http.ssl-port=8443 +quarkus.http.insecure-requests=redirect + +# Management interface โ€” health/metrics on separate plain HTTP port +# Follows Keycloak pattern: application on 8443 (HTTPS), operations on 9000 (HTTP) +quarkus.management.enabled=true + +# TLS Configuration +quarkus.ssl.native=true +quarkus.tls.protocols=TLSv1.3,TLSv1.2 +quarkus.tls.cipher-suites=TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 +quarkus.tls.alpn=true + +# Native Image Configuration +quarkus.native.additional-build-args=--enable-url-protocols=https,--enable-http,--enable-https,-O2 +quarkus.native.resources.includes=**/*.p12,**/*.crt,**/*.key +quarkus.native.monitoring=jfr + +# Virtual Threads Configuration +quarkus.virtual-threads.name-prefix=jwt-validation + +# Logging +quarkus.log.level=INFO +quarkus.log.category."de.cuioss.sheriff".level=INFO + +# File logging (enabled in Docker via LOG_FILE_PATH env var) +quarkus.log.file.enable=true +quarkus.log.file.path=${LOG_FILE_PATH:/quarkus.log} +quarkus.log.file.level=INFO +quarkus.log.file.format=%d{yyyy-MM-dd HH:mm:ss,SSS z} %-5p [%c{3.}] (%t) %s%e%n +quarkus.log.file.rotation.max-file-size=100M +quarkus.log.file.rotation.max-backup-index=5 +%dev.quarkus.log.file.enable=false + +# Development settings +quarkus.live-reload.instrumentation=false diff --git a/api-sheriff-library/src/test/java/de/cuioss/sheriff/api/ApiSheriffTest.java b/api-sheriff/src/test/java/de/cuioss/sheriff/api/ApiSheriffTest.java similarity index 89% rename from api-sheriff-library/src/test/java/de/cuioss/sheriff/api/ApiSheriffTest.java rename to api-sheriff/src/test/java/de/cuioss/sheriff/api/ApiSheriffTest.java index 3c65feb0..1a647fa7 100644 --- a/api-sheriff-library/src/test/java/de/cuioss/sheriff/api/ApiSheriffTest.java +++ b/api-sheriff/src/test/java/de/cuioss/sheriff/api/ApiSheriffTest.java @@ -1,12 +1,12 @@ /* - * Copyright 2025 the original author or authors. - * <p> + * Copyright ยฉ 2025 CUI-OpenSource-Software (info@cuioss.de) + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * <p> - * https://www.apache.org/licenses/LICENSE-2.0 - * <p> + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -36,4 +36,4 @@ void shouldReturnStatus() { ApiSheriff sheriff = new ApiSheriff(); assertEquals("API Sheriff is operational", sheriff.getStatus()); } -} \ No newline at end of file +} diff --git a/api-sheriff/src/test/java/de/cuioss/sheriff/api/test/TestDataGenerator.java b/api-sheriff/src/test/java/de/cuioss/sheriff/api/test/TestDataGenerator.java new file mode 100644 index 00000000..756673ae --- /dev/null +++ b/api-sheriff/src/test/java/de/cuioss/sheriff/api/test/TestDataGenerator.java @@ -0,0 +1,41 @@ +/* + * Copyright ยฉ 2025 CUI-OpenSource-Software (info@cuioss.de) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.cuioss.sheriff.api.test; + +import lombok.experimental.UtilityClass; + +/** + * Test data generator for API Sheriff tests + */ +@UtilityClass +public class TestDataGenerator { + + /** + * Generates a test API key + * @return test API key + */ + public static String generateApiKey() { + return "test-api-key-" + System.currentTimeMillis(); + } + + /** + * Generates a test endpoint URL + * @return test endpoint URL + */ + public static String generateEndpoint() { + return "https://api.example.com/v1/test-" + System.currentTimeMillis(); + } +} diff --git a/benchmarking/README.adoc b/benchmarking/README.adoc deleted file mode 100644 index 0c64077f..00000000 --- a/benchmarking/README.adoc +++ /dev/null @@ -1,307 +0,0 @@ -= API Sheriff Benchmarking - -Comprehensive performance testing and benchmarking suite for API Sheriff components, including both standalone library benchmarks and integration benchmarks for various frameworks. - -== Overview - -The benchmarking module provides thorough performance testing capabilities to ensure API Sheriff meets performance requirements across different deployment scenarios. It includes both micro-benchmarks for individual components and integration benchmarks for real-world usage patterns. - -== Modules - -=== benchmark-library -Standalone JMH benchmarks for the core API Sheriff library components, focusing on: -now verify that the - -=== benchmark-integration-quarkus -Integration benchmarks testing API Sheriff within Quarkus applications, including: - -* CDI injection overhead -* Configuration property loading performance -* Native image performance characteristics -* Real-world usage pattern simulation - -== Benchmark Categories - -=== Core Library Benchmarks - -==== Rate Limiting Performance -- Single-threaded request processing -- Multi-threaded concurrent access -- Rate limit exceeded scenarios -- Client state management operations - -==== Configuration Benchmarks -- Configuration validation performance -- Custom property access -- Builder pattern overhead - -==== Memory Benchmarks -- Object allocation patterns -- Garbage collection impact -- Memory usage scaling with client count - -=== Integration Benchmarks - -==== CDI Performance -- Injection overhead comparison -- Proxy call performance -- Bean lookup costs - -==== Quarkus Specific -- Application startup impact -- Configuration loading performance -- Native image execution characteristics - -== Running Benchmarks - -=== Prerequisites - -[source,bash] ----- -# Ensure you have sufficient system resources -# Recommended: 8GB+ RAM, dedicated CPU cores -export MAVEN_OPTS="-Xmx4G -XX:+UseG1GC" ----- - -=== Library Benchmarks - -Run micro benchmarks using the benchmark profile: - -[source,bash] ----- -./mvnw clean verify -Pbenchmark -pl benchmarking/benchmark-library ----- - -With custom JMH options via system properties: - -[source,bash] ----- -./mvnw clean verify -Pbenchmark -pl benchmarking/benchmark-library \ - -Djmh.warmupIterations=5 \ - -Djmh.measurementIterations=10 \ - -Djmh.forks=1 \ - -Djmh.threads=4 \ - -Djmh.time=2s ----- - -For JFR profiling: - -[source,bash] ----- -./mvnw clean verify -Pbenchmark-jfr -pl benchmarking/benchmark-library ----- - -=== Integration Benchmarks - -Run integration benchmarks with native Quarkus: - -[source,bash] ----- -./mvnw clean verify -Pbenchmark-testing -pl benchmarking/benchmark-integration-quarkus ----- - -For JFR profiling with native image: - -[source,bash] ----- -./mvnw clean verify -Pbenchmark-jfr -pl benchmarking/benchmark-integration-quarkus ----- - -== Benchmark Configuration - -=== JMH Parameters - -Common JMH configuration options: - -[cols="1,2,1"] -|=== -|Parameter |Description |Default - -|Warmup Iterations -|Number of warmup iterations -|3 - -|Measurement Iterations -|Number of measurement iterations -|5 - -|Forks -|Number of benchmark forks -|1 - -|Threads -|Number of benchmark threads -|1 - -|Mode -|Benchmark mode (Throughput/AverageTime) -|Throughput -|=== - -=== Parameterized Tests - -Benchmarks support various parameters: - -==== Rate Limiting Parameters -- `rateLimit`: 100, 1000, 10000 requests -- `timeWindow`: 1, 5, 10 seconds -- `clientCount`: 1, 10, 100 concurrent clients - -==== Load Parameters -- `concurrentThreads`: 1, 4, 8, 16 threads -- `requestBurst`: 10, 100, 1000 requests per burst -- `endpointVariation`: Different endpoint patterns - -== Benchmark Results Interpretation - -=== Throughput Benchmarks - -[source] ----- -Benchmark Mode Cnt Score Error Units -BenchmarkRunner.benchmarkSingleThreaded thrpt 5 2834.567 ยฑ 45.123 ops/s -BenchmarkRunner.benchmarkConcurrent thrpt 5 8234.123 ยฑ 89.456 ops/s ----- - -**Score**: Operations per second (higher is better) -**Error**: Statistical margin of error -**Units**: Operations per time unit - -=== Memory Benchmarks - -[source] ----- -Benchmark Mode Cnt Score Error Units -BenchmarkRunner.memoryAllocation avgt 5 12.345 ยฑ 0.123 ns/op - -Secondary metrics: -ยทGC.alloc.rate avgt 5 234.567 ยฑ 5.678 MB/sec -ยทGC.count avgt 5 2.000 ยฑ 0.000 counts ----- - -== Performance Targets - -=== Core Library Targets -- **Throughput**: >5,000 ops/sec single-threaded -- **Latency**: <1ms average request processing -- **Memory**: <100MB heap for 10,000 concurrent clients -- **Scaling**: Linear scaling up to 16 threads - -=== Integration Targets -- **CDI Overhead**: <5% performance impact -- **Startup Time**: <2 seconds additional startup -- **Native Image**: >90% of JVM performance -- **Configuration**: <10ms configuration loading - -== Continuous Benchmarking - -=== CI Integration - -[source,yaml] ----- -# .github/workflows/benchmarks.yml -name: Performance Benchmarks -on: - pull_request: - branches: [ main ] - schedule: - - cron: '0 2 * * *' # Daily at 2 AM - -jobs: - benchmark: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Run Benchmarks - run: | - cd benchmarking - ./mvnw clean test -Pbenchmark ----- - -=== Performance Regression Detection - -Benchmarks include performance regression detection: - -[source,java] ----- -// Fail if performance drops below threshold -@Measurement(iterations = 5, time = 2) -@BenchmarkMode(Mode.Throughput) -@Fork(value = 1, jvmArgs = {"-Xmx2G"}) -public void benchmarkWithThreshold() { - // Test implementation - // CI will fail if throughput < baseline - 10% -} ----- - -== Custom Benchmarks - -=== Adding New Benchmarks - -[source,java] ----- -@Benchmark -@BenchmarkMode(Mode.AverageTime) -@OutputTimeUnit(TimeUnit.MICROSECONDS) -public long benchmarkCustomScenario() { - // Your benchmark implementation - return apiSheriff.customOperation(); -} ----- - -=== Benchmark Best Practices - -1. **Warm-up**: Always include adequate warm-up iterations -2. **Isolation**: Use separate JVM forks for reliable results -3. **Realistic Data**: Use representative test data -4. **Multiple Metrics**: Measure both throughput and latency -5. **Memory Profiling**: Include GC and allocation metrics -6. **Repeatability**: Ensure consistent test conditions - -== Analysis and Reporting - -=== Performance Reports - -Benchmarks generate comprehensive performance reports: - -- JSON results for automated analysis -- HTML reports with charts and graphs -- CSV data for spreadsheet analysis -- Performance trend tracking - -=== Profiling Integration - -Support for various profilers: - -[source,bash] ----- -# JProfiler integration -java -jar benchmarks.jar -prof jprofiler - -# Async profiler -java -jar benchmarks.jar -prof async - -# GC profiling -java -jar benchmarks.jar -prof gc ----- - -== Troubleshooting - -=== Common Issues - -**Out of Memory Errors** -[source,bash] ----- -export MAVEN_OPTS="-Xmx4G" -# Or reduce benchmark parameters ----- - -**Inconsistent Results** -- Ensure system is idle during benchmarks -- Use dedicated benchmark environment -- Increase measurement iterations - -**Native Image Issues** -- Verify GraalVM version compatibility -- Check reflection configuration -- Review native image build logs \ No newline at end of file diff --git a/benchmarking/benchmark-integration-quarkus/pom.xml b/benchmarking/benchmark-integration-quarkus/pom.xml deleted file mode 100644 index b926ec61..00000000 --- a/benchmarking/benchmark-integration-quarkus/pom.xml +++ /dev/null @@ -1,505 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>benchmarking</artifactId> - <version>1.0.0-SNAPSHOT</version> - <relativePath>../pom.xml</relativePath> - </parent> - - <artifactId>benchmark-integration-quarkus</artifactId> - <packaging>jar</packaging> - <name>API Sheriff Quarkus Integration Benchmarks</name> - <description>JMH benchmarks for Quarkus integration testing against live services</description> - - <properties> - <maven.jar.plugin.automatic.module.name>de.cuioss.sheriff.api.quarkus.benchmark</maven.jar.plugin.automatic.module.name> - <sonar.skip>true</sonar.skip> - - <!-- Default benchmark runner --> - <benchmark.runner>de.cuioss.sheriff.api.quarkus.benchmark.BenchmarkRunner</benchmark.runner> - - <!-- JMH Configuration Properties - Optimized for integration testing --> - <jmh.result.format>JSON</jmh.result.format> - <jmh.result.filePrefix>${benchmark.results.dir}/integration-benchmark-result</jmh.result.filePrefix> - <jmh.iterations>5</jmh.iterations> - <jmh.warmupIterations>1</jmh.warmupIterations> - <jmh.forks>2</jmh.forks> - <jmh.threads>24</jmh.threads> - <jmh.time>10s</jmh.time> - <jmh.warmupTime>1s</jmh.warmupTime> - - <!-- Integration test service URLs - aligned with docker-compose.yml ports --> - <integration.service.url>https://localhost:10443</integration.service.url> - <keycloak.url>https://localhost:1443</keycloak.url> - <quarkus.metrics.url>https://localhost:10443</quarkus.metrics.url> - </properties> - - <dependencies> - <!-- API Sheriff library dependency --> - <dependency> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>api-sheriff-library</artifactId> - <version>${project.version}</version> - </dependency> - - <!-- Quarkus dependencies for CDI --> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-core</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-arc</artifactId> - </dependency> - - <!-- JMH dependencies --> - <dependency> - <groupId>org.openjdk.jmh</groupId> - <artifactId>jmh-core</artifactId> - </dependency> - <dependency> - <groupId>org.openjdk.jmh</groupId> - <artifactId>jmh-generator-annprocess</artifactId> - </dependency> - - <!-- CUI utilities --> - <dependency> - <groupId>de.cuioss</groupId> - <artifactId>cui-java-tools</artifactId> - <scope>compile</scope> - </dependency> - - <!-- JSON processing --> - <dependency> - <groupId>com.google.code.gson</groupId> - <artifactId>gson</artifactId> - </dependency> - - <!-- HdrHistogram for accurate latency recording --> - <dependency> - <groupId>org.hdrhistogram</groupId> - <artifactId>HdrHistogram</artifactId> - </dependency> - - <!-- Lombok --> - <dependency> - <groupId>org.projectlombok</groupId> - <artifactId>lombok</artifactId> - <scope>provided</scope> - </dependency> - - <!-- Apache Commons IO for TeeOutputStream --> - <dependency> - <groupId>commons-io</groupId> - <artifactId>commons-io</artifactId> - </dependency> - - - <!-- JUnit for basic testing --> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter</artifactId> - <scope>test</scope> - </dependency> - - <!-- Awaitility for better async testing --> - <dependency> - <groupId>org.awaitility</groupId> - <artifactId>awaitility</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <plugins> - <!-- Configure surefire for unit tests --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <skipTests>false</skipTests> - <argLine>-XX:+IgnoreUnrecognizedVMOptions -Djava.awt.headless=true</argLine> - </configuration> - </plugin> - - <!-- Enable annotation processing for JMH --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <annotationProcessorPaths> - <path> - <groupId>org.openjdk.jmh</groupId> - <artifactId>jmh-generator-annprocess</artifactId> - <version>${version.jmh}</version> - </path> - <path> - <groupId>org.projectlombok</groupId> - <artifactId>lombok</artifactId> - <version>${version.lombok}</version> - </path> - </annotationProcessorPaths> - <release>21</release> - </configuration> - </plugin> - - <!-- Execute benchmark tests when skip.benchmark is false --> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>exec-maven-plugin</artifactId> - <executions> - <execution> - <id>create-benchmark-dir</id> - <phase>compile</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <skip>${skip.benchmark}</skip> - <executable>mkdir</executable> - <arguments> - <argument>-p</argument> - <argument>${benchmark.results.dir}</argument> - </arguments> - </configuration> - </execution> - <execution> - <id>run-benchmarks</id> - <phase>integration-test</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <skip>${skip.benchmark}</skip> - <executable>java</executable> - <arguments> - <argument>-Djava.util.logging.manager=java.util.logging.LogManager</argument> - <argument>-classpath</argument> - <argument>${project.build.outputDirectory}${path.separator}${project.build.directory}/dependency/*</argument> - <argument>-Djmh.result.format=${jmh.result.format}</argument> - <argument>-Djmh.result.filePrefix=${jmh.result.filePrefix}</argument> - <argument>-Djmh.iterations=${jmh.iterations}</argument> - <argument>-Djmh.warmupIterations=${jmh.warmupIterations}</argument> - <argument>-Djmh.forks=${jmh.forks}</argument> - <argument>-Djmh.threads=${jmh.threads}</argument> - <argument>-Djmh.time=${jmh.time}</argument> - <argument>-Djmh.warmupTime=${jmh.warmupTime}</argument> - <argument>-Dbenchmark.results.dir=${benchmark.results.dir}</argument> - <argument>-Dintegration.service.url=${integration.service.url}</argument> - <argument>-Dkeycloak.url=${keycloak.url}</argument> - <argument>-Dquarkus.metrics.url=${quarkus.metrics.url}</argument> - <argument>-Djava.util.logging.manager=java.util.logging.LogManager</argument> - <argument>-Djava.util.logging.config.file=src/main/resources/benchmark-logging.properties</argument> - <argument>-XX:+UnlockDiagnosticVMOptions</argument> - <argument>-XX:+DebugNonSafepoints</argument> - <argument>${benchmark.runner}</argument> - </arguments> - </configuration> - </execution> - </executions> - </plugin> - - <!-- Build classpath for benchmark execution --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-dependency-plugin</artifactId> - <executions> - <execution> - <id>copy-dependencies</id> - <phase>package</phase> - <goals> - <goal>copy-dependencies</goal> - </goals> - <configuration> - <outputDirectory>${project.build.directory}/dependency</outputDirectory> - <overWriteReleases>false</overWriteReleases> - <overWriteSnapshots>false</overWriteSnapshots> - <overWriteIfNewer>true</overWriteIfNewer> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - - <profiles> - <profile> - <id>benchmark-testing</id> - <properties> - <skip.benchmark>false</skip.benchmark> - <benchmark.results.dir>target/benchmark-results</benchmark.results.dir> - </properties> - <build> - <plugins> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>exec-maven-plugin</artifactId> - <executions> - <!-- Build integration tests (using smart native build script) --> - <execution> - <id>maven-build-integration-tests</id> - <phase>pre-integration-test</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <executable>../../mvnw</executable> - <arguments> - <argument>compile</argument> - <argument>exec:exec@build-native-if-needed</argument> - <argument>exec:exec@docker-build-distroless</argument> - <argument>-Pintegration-tests</argument> - <argument>-DskipTests</argument> - </arguments> - <workingDirectory>${project.basedir}/../../api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests</workingDirectory> - </configuration> - </execution> - <!-- Start integration test containers (keep running for benchmarks) --> - <execution> - <id>start-integration-containers</id> - <phase>pre-integration-test</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <executable>../../api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/start-integration-container.sh</executable> - <workingDirectory>${project.basedir}</workingDirectory> - </configuration> - </execution> - <!-- Metrics processing moved to BenchmarkRunner.java directly --> - <!-- Generate HTTP metrics from benchmark results --> - <execution> - <id>generate-http-metrics</id> - <phase>post-integration-test</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <executable>bash</executable> - <arguments> - <argument>-c</argument> - <argument>cd target && java -cp "classes:dependency/*" de.cuioss.sheriff.api.quarkus.benchmark.metrics.MetricsPostProcessor benchmark-results || echo "Comprehensive metrics generation failed - continuing"</argument> - </arguments> - <workingDirectory>${project.basedir}</workingDirectory> - </configuration> - </execution> - <!-- Stop integration test containers --> - <execution> - <id>stop-integration-containers</id> - <phase>post-integration-test</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <executable>../../api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/stop-integration-container.sh</executable> - <workingDirectory>${project.basedir}</workingDirectory> - <!-- Don't fail build if cleanup fails --> - <successCodes> - <successCode>0</successCode> - <successCode>1</successCode> - </successCodes> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - - <profile> - <id>rebuild-container</id> - <build> - <plugins> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>exec-maven-plugin</artifactId> - <executions> - <!-- Clean integration tests to force container rebuild --> - <execution> - <id>clean-integration-tests-for-rebuild</id> - <phase>clean</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <executable>../../mvnw</executable> - <arguments> - <argument>clean</argument> - </arguments> - <workingDirectory>${project.basedir}/../../api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests</workingDirectory> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - - <profile> - <id>benchmark-jfr</id> - <properties> - <skip.benchmark>false</skip.benchmark> - <benchmark.results.dir>target/benchmark-results</benchmark.results.dir> - <jfr.enabled>true</jfr.enabled> - </properties> - <build> - <plugins> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>exec-maven-plugin</artifactId> - <executions> - <!-- Create benchmark and JFR results directories --> - <execution> - <id>create-jfr-dirs</id> - <phase>pre-integration-test</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <executable>bash</executable> - <arguments> - <argument>-c</argument> - <argument>mkdir -p ${benchmark.results.dir}/jfr-recordings ${benchmark.results.dir}/jfr-reports</argument> - </arguments> - </configuration> - </execution> - <!-- Build JFR integration tests (without running containers) --> - <execution> - <id>maven-build-jfr-tests</id> - <phase>pre-integration-test</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <executable>bash</executable> - <arguments> - <argument>-c</argument> - <argument>cd ../../api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests && ../../mvnw clean compile quarkus:build -Pintegration-tests -DskipTests && DOCKERFILE=Dockerfile.native.jfr DOCKER_IMAGE_TAG=jfr DOCKER_BUILDKIT=1 docker compose build api-sheriff-integration-tests</argument> - </arguments> - <workingDirectory>${project.basedir}</workingDirectory> - </configuration> - </execution> - <!-- Start JFR integration test containers --> - <execution> - <id>start-jfr-containers</id> - <phase>pre-integration-test</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <executable>bash</executable> - <arguments> - <argument>-c</argument> - <argument>DOCKER_IMAGE_TAG=jfr DOCKERFILE=Dockerfile.native.jfr ../../api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/start-integration-container.sh</argument> - </arguments> - <workingDirectory>${project.basedir}</workingDirectory> - </configuration> - </execution> - <!-- Run benchmarks with JFR profiling enabled --> - <execution> - <id>run-benchmarks-jfr</id> - <phase>integration-test</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <executable>java</executable> - <arguments> - <argument>-Djava.util.logging.manager=java.util.logging.LogManager</argument> - <argument>-classpath</argument> - <argument>${project.build.outputDirectory}${path.separator}${project.build.directory}/dependency/*</argument> - <argument>-Djmh.result.format=${jmh.result.format}</argument> - <argument>-Djmh.result.filePrefix=${jmh.result.filePrefix}</argument> - <argument>-Djmh.iterations=${jmh.iterations}</argument> - <argument>-Djmh.warmupIterations=${jmh.warmupIterations}</argument> - <argument>-Djmh.forks=${jmh.forks}</argument> - <argument>-Djmh.threads=${jmh.threads}</argument> - <argument>-Djmh.time=${jmh.time}</argument> - <argument>-Djmh.warmupTime=${jmh.warmupTime}</argument> - <argument>-Dbenchmark.results.dir=${benchmark.results.dir}</argument> - <argument>-Dintegration.service.url=${integration.service.url}</argument> - <argument>-Dkeycloak.url=${keycloak.url}</argument> - <argument>-Dquarkus.metrics.url=${quarkus.metrics.url}</argument> - <argument>-Djava.util.logging.manager=java.util.logging.LogManager</argument> - <argument>-Djava.util.logging.config.file=src/main/resources/benchmark-logging.properties</argument> - <argument>-XX:+UnlockDiagnosticVMOptions</argument> - <argument>-XX:+DebugNonSafepoints</argument> - <argument>-XX:StartFlightRecording=settings=profile,filename=${benchmark.results.dir}/jfr-recordings/benchmark.jfr,dumponexit=true</argument> - <argument>${benchmark.runner}</argument> - </arguments> - </configuration> - </execution> - <!-- Verify JFR files from container volume mount --> - <execution> - <id>verify-jfr-files</id> - <phase>post-integration-test</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <executable>bash</executable> - <arguments> - <argument>-c</argument> - <argument>echo "JFR files in ${benchmark.results.dir}/jfr-recordings:" && ls -la ${benchmark.results.dir}/jfr-recordings/ || echo "No JFR directory found"</argument> - </arguments> - </configuration> - </execution> - <!-- Generate JFR reports --> - <execution> - <id>generate-jfr-reports</id> - <phase>post-integration-test</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <executable>bash</executable> - <arguments> - <argument>-c</argument> - <argument>for jfr in ${benchmark.results.dir}/jfr-recordings/*.jfr; do if [ -f "$jfr" ] && [ -s "$jfr" ]; then echo "Generating summary for $(basename "$jfr")"; jfr summary "$jfr" > "${benchmark.results.dir}/jfr-reports/$(basename "$jfr" .jfr)-summary.txt" || echo "Failed to generate summary for $(basename "$jfr")"; else echo "Skipping empty or non-existent file: $(basename "$jfr")"; fi; done</argument> - </arguments> - </configuration> - </execution> - <!-- Generate HTTP metrics from benchmark results (JFR profile) --> - <execution> - <id>generate-http-metrics-jfr</id> - <phase>post-integration-test</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <executable>bash</executable> - <arguments> - <argument>-c</argument> - <argument>cd target && java -cp "classes:dependency/*" de.cuioss.sheriff.api.quarkus.benchmark.metrics.MetricsPostProcessor benchmark-results || echo "Comprehensive metrics generation failed - continuing"</argument> - </arguments> - <workingDirectory>${project.basedir}</workingDirectory> - </configuration> - </execution> - <!-- Stop integration test containers --> - <execution> - <id>stop-jfr-containers</id> - <phase>post-integration-test</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <executable>../../api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/stop-integration-container.sh</executable> - <workingDirectory>${project.basedir}</workingDirectory> - <!-- Don't fail build if cleanup fails --> - <successCodes> - <successCode>0</successCode> - <successCode>1</successCode> - </successCodes> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - </profile> - </profiles> -</project> \ No newline at end of file diff --git a/benchmarking/benchmark-integration-quarkus/src/main/java/de/cuioss/sheriff/api/quarkus/benchmark/BenchmarkOptionsHelper.java b/benchmarking/benchmark-integration-quarkus/src/main/java/de/cuioss/sheriff/api/quarkus/benchmark/BenchmarkOptionsHelper.java deleted file mode 100644 index 337223c1..00000000 --- a/benchmarking/benchmark-integration-quarkus/src/main/java/de/cuioss/sheriff/api/quarkus/benchmark/BenchmarkOptionsHelper.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * <p> - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p> - * https://www.apache.org/licenses/LICENSE-2.0 - * <p> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package de.cuioss.sheriff.api.quarkus.benchmark; - -import org.openjdk.jmh.results.format.ResultFormatType; -import org.openjdk.jmh.runner.options.TimeValue; - -import java.io.File; -import java.util.concurrent.TimeUnit; - -/** - * Shared utility class for building JMH benchmark options. - * Consolidates common option parsing logic for benchmark runners. - * - * @author API Sheriff Team - */ -public final class BenchmarkOptionsHelper { - - private BenchmarkOptionsHelper() { - // Utility class - } - - /** - * Gets the result format from system property or defaults to JSON. - * - * @return the result format type - */ - public static ResultFormatType getResultFormat() { - String format = System.getProperty("jmh.result.format", "JSON"); - try { - return ResultFormatType.valueOf(format.toUpperCase()); - } catch (IllegalArgumentException e) { - return ResultFormatType.JSON; - } - } - - /** - * Gets the result file path from system property or returns the default. - * Ensures parent directory exists. - * - * @param defaultFileName the default file name to use if property not set - * @return the result file path - */ - public static String getResultFile(String defaultFileName) { - String filePrefix = System.getProperty("jmh.result.filePrefix"); - if (filePrefix != null && !filePrefix.isEmpty()) { - String resultFile = filePrefix + ".json"; - // Ensure parent directory exists - File file = new File(resultFile); - File parentDir = file.getParentFile(); - if (parentDir != null && !parentDir.exists()) { - parentDir.mkdirs(); - } - return resultFile; - } - return defaultFileName; - } - - /** - * Gets the measurement time from system property or uses the provided default. - * - * @param defaultTime the default time value (e.g., "2s", "5s") - * @return the parsed time value - */ - public static TimeValue getMeasurementTime(String defaultTime) { - String time = System.getProperty("jmh.time", defaultTime); - return parseTimeValue(time); - } - - /** - * Gets the warmup time from system property or uses the provided default. - * - * @param defaultTime the default time value (e.g., "2s", "3s") - * @return the parsed time value - */ - public static TimeValue getWarmupTime(String defaultTime) { - String time = System.getProperty("jmh.warmupTime", defaultTime); - return parseTimeValue(time); - } - - /** - * Gets the thread count from system property or uses the provided default. - * Supports "MAX" to use all available processors. - * - * @param defaultCount the default thread count - * @return the thread count - */ - public static int getThreadCount(int defaultCount) { - String threads = System.getProperty("jmh.threads"); - if (threads != null) { - if ("MAX".equalsIgnoreCase(threads)) { - return Runtime.getRuntime().availableProcessors(); - } - try { - return Integer.parseInt(threads); - } catch (NumberFormatException e) { - // Fall through to default - } - } - return defaultCount; - } - - /** - * Gets the number of forks from system property or uses the provided default. - * - * @param defaultForks the default number of forks - * @return the number of forks - */ - public static int getForks(int defaultForks) { - return Integer.parseInt(System.getProperty("jmh.forks", String.valueOf(defaultForks))); - } - - /** - * Gets the warmup iterations from system property or uses the provided default. - * - * @param defaultIterations the default number of warmup iterations - * @return the number of warmup iterations - */ - public static int getWarmupIterations(int defaultIterations) { - return Integer.parseInt(System.getProperty("jmh.warmupIterations", String.valueOf(defaultIterations))); - } - - /** - * Gets the measurement iterations from system property or uses the provided default. - * - * @param defaultIterations the default number of measurement iterations - * @return the number of measurement iterations - */ - public static int getMeasurementIterations(int defaultIterations) { - return Integer.parseInt(System.getProperty("jmh.measurementIterations", String.valueOf(defaultIterations))); - } - - /** - * Parse a time value string (e.g., "2s", "100ms", "1m"). - * - * @param timeStr the time string to parse - * @return the parsed TimeValue - */ - private static TimeValue parseTimeValue(String timeStr) { - if (timeStr == null || timeStr.isEmpty()) { - return TimeValue.seconds(1); - } - - timeStr = timeStr.trim().toLowerCase(); - - // Extract numeric part and unit - StringBuilder numPart = new StringBuilder(); - StringBuilder unitPart = new StringBuilder(); - boolean foundUnit = false; - - for (char c : timeStr.toCharArray()) { - if (Character.isDigit(c) || c == '.') { - if (!foundUnit) { - numPart.append(c); - } - } else { - foundUnit = true; - unitPart.append(c); - } - } - - try { - long value = Long.parseLong(numPart.toString()); - String unit = unitPart.toString(); - - switch (unit) { - case "ms": - case "milliseconds": - case "millis": - return TimeValue.milliseconds(value); - case "s": - case "sec": - case "seconds": - return TimeValue.seconds(value); - case "m": - case "min": - case "minutes": - return TimeValue.minutes(value); - default: - // Default to seconds if no unit or unknown unit - return TimeValue.seconds(value); - } - } catch (NumberFormatException e) { - // Default fallback - return TimeValue.seconds(1); - } - } -} \ No newline at end of file diff --git a/benchmarking/benchmark-integration-quarkus/src/main/java/de/cuioss/sheriff/api/quarkus/benchmark/BenchmarkRunner.java b/benchmarking/benchmark-integration-quarkus/src/main/java/de/cuioss/sheriff/api/quarkus/benchmark/BenchmarkRunner.java deleted file mode 100644 index 9687eca7..00000000 --- a/benchmarking/benchmark-integration-quarkus/src/main/java/de/cuioss/sheriff/api/quarkus/benchmark/BenchmarkRunner.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * <p> - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p> - * https://www.apache.org/licenses/LICENSE-2.0 - * <p> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package de.cuioss.sheriff.api.quarkus.benchmark; - -import java.util.Collection; -import java.util.concurrent.TimeUnit; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.results.RunResult; -import org.openjdk.jmh.runner.Runner; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; - -import de.cuioss.sheriff.api.ApiSheriff; - -/** - * JMH benchmark suite for API Sheriff Quarkus integration performance testing. - * - * @author API Sheriff Team - */ -@State(Scope.Benchmark) -@BenchmarkMode(Mode.Throughput) -@OutputTimeUnit(TimeUnit.SECONDS) -@Fork(value = 1, jvmArgs = {"-Xmx2G", "-server"}) -@Warmup(iterations = 3, time = 2, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS) -public class BenchmarkRunner { - - private ApiSheriff apiSheriff; - - @Setup(Level.Trial) - public void setup() { - // Simple placeholder implementation for benchmarking - apiSheriff = new ApiSheriff(); - } - - @Benchmark - public String benchmarkGetStatus() { - return apiSheriff.getStatus(); - } - - /** - * Main method to run the benchmarks. - * - * @param args command line arguments - * @throws Exception if benchmark execution fails - */ - public static void main(String[] args) throws Exception { - // Configure JMH options using helper for system property configuration - Options options = new OptionsBuilder() - .include(System.getProperty("jmh.include", "de\\.cuioss\\.sheriff\\.api\\.quarkus\\.benchmark\\..*")) - .forks(BenchmarkOptionsHelper.getForks(1)) - .warmupIterations(BenchmarkOptionsHelper.getWarmupIterations(1)) - .measurementIterations(BenchmarkOptionsHelper.getMeasurementIterations(2)) - .measurementTime(BenchmarkOptionsHelper.getMeasurementTime("5s")) - .warmupTime(BenchmarkOptionsHelper.getWarmupTime("1s")) - .threads(BenchmarkOptionsHelper.getThreadCount(10)) - .resultFormat(BenchmarkOptionsHelper.getResultFormat()) - .result(BenchmarkOptionsHelper.getResultFile(getBenchmarkResultsDir() + "/integration-benchmark-result.json")) - .build(); - - // Run the benchmarks - Collection<RunResult> results = new Runner(options).run(); - - // Check if benchmarks ran successfully - if (results.isEmpty()) { - throw new IllegalStateException("No benchmark results were produced"); - } - - System.out.println("Benchmark completed successfully with " + results.size() + " results"); - } - - /** - * Gets the benchmark results directory from system property or defaults to target/benchmark-results. - * - * @return the benchmark results directory path - */ - private static String getBenchmarkResultsDir() { - return System.getProperty("benchmark.results.dir", "target/benchmark-results"); - } -} \ No newline at end of file diff --git a/benchmarking/benchmark-library/pom.xml b/benchmarking/benchmark-library/pom.xml deleted file mode 100644 index 8b8c780a..00000000 --- a/benchmarking/benchmark-library/pom.xml +++ /dev/null @@ -1,307 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>benchmarking</artifactId> - <version>1.0.0-SNAPSHOT</version> - <relativePath>../pom.xml</relativePath> - </parent> - - <artifactId>benchmark-library</artifactId> - <packaging>jar</packaging> - <name>API Sheriff Benchmark Library</name> - <description>Benchmarking module for API Sheriff functionality</description> - - <properties> - <maven.jar.plugin.automatic.module.name>de.cuioss.sheriff.api.benchmark</maven.jar.plugin.automatic.module.name> - <sonar.skip>true</sonar.skip> - <version.mockwebserver3>5.0.0-alpha.12</version.mockwebserver3> - - <!-- Default benchmark runner --> - <benchmark.runner>de.cuioss.sheriff.api.benchmark.BenchmarkRunner</benchmark.runner> - - <!-- JMH Configuration Properties - Optimized for fast execution (<10 minutes) --> - <jmh.result.format>JSON</jmh.result.format> - <jmh.result.filePrefix>${benchmark.results.dir}/micro-benchmark-result</jmh.result.filePrefix> - <jmh.iterations>3</jmh.iterations> - <jmh.warmupIterations>1</jmh.warmupIterations> - <jmh.forks>1</jmh.forks> - <!-- Reduced from 200 to 100 threads to avoid contention and improve P99 latency --> - <jmh.threads>100</jmh.threads> - <jmh.time>4s</jmh.time> - <jmh.warmupTime>1s</jmh.warmupTime> - </properties> - - <dependencyManagement> - <dependencies> - <dependency> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>bom</artifactId> - <version>${project.version}</version> - <type>pom</type> - <scope>import</scope> - </dependency> - <dependency> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>api-sheriff-library</artifactId> - <version>${project.version}</version> - <classifier>generators</classifier> - </dependency> - <dependency> - <groupId>com.squareup.okhttp3</groupId> - <artifactId>mockwebserver3</artifactId> - <version>${version.mockwebserver3}</version> - </dependency> - <dependency> - <groupId>com.squareup.okhttp3</groupId> - <artifactId>okhttp</artifactId> - <version>${version.mockwebserver3}</version> - </dependency> - </dependencies> - </dependencyManagement> - - <dependencies> - <!-- Internal module dependencies --> - <dependency> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>api-sheriff-library</artifactId> - </dependency> - - <!-- Test dependencies from library module --> - <dependency> - <groupId>de.cuioss.sheriff.api</groupId> - <artifactId>api-sheriff-library</artifactId> - <version>${project.version}</version> - <classifier>generators</classifier> - <scope>compile</scope> - </dependency> - - <!-- Mock Web Server for HTTP testing --> - <dependency> - <groupId>com.squareup.okhttp3</groupId> - <artifactId>mockwebserver3</artifactId> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>com.squareup.okhttp3</groupId> - <artifactId>okhttp</artifactId> - <scope>compile</scope> - </dependency> - - <!-- Keycloak test integration --> - <dependency> - <groupId>de.cuioss.test</groupId> - <artifactId>cui-test-keycloak-integration</artifactId> - <scope>compile</scope> - <exclusions> - <exclusion> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - </exclusion> - </exclusions> - </dependency> - - <!-- Rest Assured for API testing --> - <dependency> - <groupId>io.rest-assured</groupId> - <artifactId>rest-assured</artifactId> - <scope>compile</scope> - </dependency> - - <!-- Test utilities --> - <dependency> - <groupId>de.cuioss.test</groupId> - <artifactId>cui-test-generator</artifactId> - <scope>compile</scope> - </dependency> - - <!-- JMH dependencies --> - <dependency> - <groupId>org.openjdk.jmh</groupId> - <artifactId>jmh-core</artifactId> - </dependency> - <dependency> - <groupId>org.openjdk.jmh</groupId> - <artifactId>jmh-generator-annprocess</artifactId> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-jupiter</artifactId> - <scope>test</scope> - </dependency> - - <!-- Other dependencies --> - <dependency> - <groupId>org.projectlombok</groupId> - <artifactId>lombok</artifactId> - </dependency> - - <!-- JJWT dependencies --> - <dependency> - <groupId>io.jsonwebtoken</groupId> - <artifactId>jjwt-api</artifactId> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>io.jsonwebtoken</groupId> - <artifactId>jjwt-impl</artifactId> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>io.jsonwebtoken</groupId> - <artifactId>jjwt-jackson</artifactId> - <scope>compile</scope> - </dependency> - - <!-- Implementation of jakarta.json-api --> - <dependency> - <groupId>org.eclipse.parsson</groupId> - <artifactId>parsson</artifactId> - <scope>compile</scope> - </dependency> - - <!-- HdrHistogram for accurate latency recording --> - <dependency> - <groupId>org.hdrhistogram</groupId> - <artifactId>HdrHistogram</artifactId> - <scope>compile</scope> - </dependency> - - <!-- Gson for JSON serialization --> - <dependency> - <groupId>com.google.code.gson</groupId> - <artifactId>gson</artifactId> - <scope>compile</scope> - </dependency> - - <!-- Awaitility for better async testing --> - <dependency> - <groupId>org.awaitility</groupId> - <artifactId>awaitility</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <plugins> - <!-- Skip unit tests by default --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <skipTests>${skipTests}</skipTests> - </configuration> - </plugin> - - <!-- Enable annotation processing for JMH --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <annotationProcessorPaths> - <path> - <groupId>org.openjdk.jmh</groupId> - <artifactId>jmh-generator-annprocess</artifactId> - <version>${version.jmh}</version> - </path> - </annotationProcessorPaths> - <release>21</release> - </configuration> - </plugin> - - <!-- Execute benchmark tests when skip.benchmark is false --> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>exec-maven-plugin</artifactId> - <executions> - <execution> - <id>create-benchmark-dir</id> - <phase>compile</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <skip>${skip.benchmark}</skip> - <executable>mkdir</executable> - <arguments> - <argument>-p</argument> - <argument>${benchmark.results.dir}</argument> - </arguments> - </configuration> - </execution> - <execution> - <id>run-benchmarks</id> - <phase>verify</phase> - <goals> - <goal>exec</goal> - </goals> - <configuration> - <skip>${skip.benchmark}</skip> - <executable>java</executable> - <arguments> - <argument>-classpath</argument> - <argument>${project.build.outputDirectory}${path.separator}${project.build.directory}/dependency/*</argument> - <argument>-Djmh.result.format=${jmh.result.format}</argument> - <argument>-Djmh.result.filePrefix=${jmh.result.filePrefix}</argument> - <argument>-Djmh.iterations=${jmh.iterations}</argument> - <argument>-Djmh.warmupIterations=${jmh.warmupIterations}</argument> - <argument>-Djmh.forks=${jmh.forks}</argument> - <argument>-Djmh.threads=${jmh.threads}</argument> - <argument>-Djmh.time=${jmh.time}</argument> - <argument>-Djmh.warmupTime=${jmh.warmupTime}</argument> - <argument>-Dbenchmark.results.dir=${benchmark.results.dir}</argument> - <argument>-Djava.util.logging.config.file=src/main/resources/benchmark-logging.properties</argument> - <argument>-XX:+UnlockDiagnosticVMOptions</argument> - <argument>-XX:+DebugNonSafepoints</argument> - <argument>${benchmark.runner}</argument> - </arguments> - </configuration> - </execution> - </executions> - </plugin> - - <!-- Build classpath for benchmark execution --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-dependency-plugin</artifactId> - <executions> - <execution> - <id>copy-dependencies</id> - <phase>package</phase> - <goals> - <goal>copy-dependencies</goal> - </goals> - <configuration> - <outputDirectory>${project.build.directory}/dependency</outputDirectory> - <overWriteReleases>false</overWriteReleases> - <overWriteSnapshots>false</overWriteSnapshots> - <overWriteIfNewer>true</overWriteIfNewer> - </configuration> - </execution> - </executions> - </plugin> - </plugins> - </build> - - <profiles> - <profile> - <id>benchmark</id> - <properties> - <skip.benchmark>false</skip.benchmark> - <benchmark.results.dir>target/benchmark-results</benchmark.results.dir> - </properties> - </profile> - <profile> - <id>benchmark-jfr</id> - <properties> - <skip.benchmark>false</skip.benchmark> - <benchmark.runner>de.cuioss.sheriff.api.benchmark.JfrBenchmarkRunner</benchmark.runner> - <benchmark.results.dir>target/benchmark-jfr-results</benchmark.results.dir> - </properties> - </profile> - </profiles> -</project> \ No newline at end of file diff --git a/benchmarking/benchmark-library/src/main/java/de/cuioss/sheriff/api/benchmark/BenchmarkOptionsHelper.java b/benchmarking/benchmark-library/src/main/java/de/cuioss/sheriff/api/benchmark/BenchmarkOptionsHelper.java deleted file mode 100644 index fe756509..00000000 --- a/benchmarking/benchmark-library/src/main/java/de/cuioss/sheriff/api/benchmark/BenchmarkOptionsHelper.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * <p> - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p> - * https://www.apache.org/licenses/LICENSE-2.0 - * <p> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package de.cuioss.sheriff.api.benchmark; - -import org.openjdk.jmh.results.format.ResultFormatType; -import org.openjdk.jmh.runner.options.TimeValue; - -import java.io.File; -import java.util.concurrent.TimeUnit; - -/** - * Shared utility class for building JMH benchmark options. - * Consolidates common option parsing logic for benchmark runners. - * - * @author API Sheriff Team - */ -public final class BenchmarkOptionsHelper { - - private BenchmarkOptionsHelper() { - // Utility class - } - - /** - * Gets the result format from system property or defaults to JSON. - * - * @return the result format type - */ - public static ResultFormatType getResultFormat() { - String format = System.getProperty("jmh.result.format", "JSON"); - try { - return ResultFormatType.valueOf(format.toUpperCase()); - } catch (IllegalArgumentException e) { - return ResultFormatType.JSON; - } - } - - /** - * Gets the result file path from system property or returns the default. - * Ensures parent directory exists. - * - * @param defaultFileName the default file name to use if property not set - * @return the result file path - */ - public static String getResultFile(String defaultFileName) { - String filePrefix = System.getProperty("jmh.result.filePrefix"); - if (filePrefix != null && !filePrefix.isEmpty()) { - String resultFile = filePrefix + ".json"; - // Ensure parent directory exists - File file = new File(resultFile); - File parentDir = file.getParentFile(); - if (parentDir != null && !parentDir.exists()) { - parentDir.mkdirs(); - } - return resultFile; - } - return defaultFileName; - } - - /** - * Gets the measurement time from system property or uses the provided default. - * - * @param defaultTime the default time value (e.g., "2s", "5s") - * @return the parsed time value - */ - public static TimeValue getMeasurementTime(String defaultTime) { - String time = System.getProperty("jmh.time", defaultTime); - return parseTimeValue(time); - } - - /** - * Gets the warmup time from system property or uses the provided default. - * - * @param defaultTime the default time value (e.g., "2s", "3s") - * @return the parsed time value - */ - public static TimeValue getWarmupTime(String defaultTime) { - String time = System.getProperty("jmh.warmupTime", defaultTime); - return parseTimeValue(time); - } - - /** - * Gets the thread count from system property or uses the provided default. - * Supports "MAX" to use all available processors. - * - * @param defaultCount the default thread count - * @return the thread count - */ - public static int getThreadCount(int defaultCount) { - String threads = System.getProperty("jmh.threads"); - if (threads != null) { - if ("MAX".equalsIgnoreCase(threads)) { - return Runtime.getRuntime().availableProcessors(); - } - try { - return Integer.parseInt(threads); - } catch (NumberFormatException e) { - // Fall through to default - } - } - return defaultCount; - } - - /** - * Gets the number of forks from system property or uses the provided default. - * - * @param defaultForks the default number of forks - * @return the number of forks - */ - public static int getForks(int defaultForks) { - return Integer.parseInt(System.getProperty("jmh.forks", String.valueOf(defaultForks))); - } - - /** - * Gets the warmup iterations from system property or uses the provided default. - * - * @param defaultIterations the default number of warmup iterations - * @return the number of warmup iterations - */ - public static int getWarmupIterations(int defaultIterations) { - return Integer.parseInt(System.getProperty("jmh.warmupIterations", String.valueOf(defaultIterations))); - } - - /** - * Gets the measurement iterations from system property or uses the provided default. - * - * @param defaultIterations the default number of measurement iterations - * @return the number of measurement iterations - */ - public static int getMeasurementIterations(int defaultIterations) { - return Integer.parseInt(System.getProperty("jmh.measurementIterations", String.valueOf(defaultIterations))); - } - - /** - * Parse a time value string (e.g., "2s", "100ms", "1m"). - * - * @param timeStr the time string to parse - * @return the parsed TimeValue - */ - private static TimeValue parseTimeValue(String timeStr) { - if (timeStr == null || timeStr.isEmpty()) { - return TimeValue.seconds(1); - } - - timeStr = timeStr.trim().toLowerCase(); - - // Extract numeric part and unit - StringBuilder numPart = new StringBuilder(); - StringBuilder unitPart = new StringBuilder(); - boolean foundUnit = false; - - for (char c : timeStr.toCharArray()) { - if (Character.isDigit(c) || c == '.') { - if (!foundUnit) { - numPart.append(c); - } - } else { - foundUnit = true; - unitPart.append(c); - } - } - - try { - long value = Long.parseLong(numPart.toString()); - String unit = unitPart.toString(); - - switch (unit) { - case "ms": - case "milliseconds": - case "millis": - return TimeValue.milliseconds(value); - case "s": - case "sec": - case "seconds": - return TimeValue.seconds(value); - case "m": - case "min": - case "minutes": - return TimeValue.minutes(value); - default: - // Default to seconds if no unit or unknown unit - return TimeValue.seconds(value); - } - } catch (NumberFormatException e) { - // Default fallback - return TimeValue.seconds(1); - } - } -} \ No newline at end of file diff --git a/benchmarking/benchmark-library/src/main/java/de/cuioss/sheriff/api/benchmark/BenchmarkRunner.java b/benchmarking/benchmark-library/src/main/java/de/cuioss/sheriff/api/benchmark/BenchmarkRunner.java deleted file mode 100644 index 84cb8019..00000000 --- a/benchmarking/benchmark-library/src/main/java/de/cuioss/sheriff/api/benchmark/BenchmarkRunner.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2025 the original author or authors. - * <p> - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * <p> - * https://www.apache.org/licenses/LICENSE-2.0 - * <p> - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package de.cuioss.sheriff.api.benchmark; - -import java.util.concurrent.TimeUnit; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Level; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.runner.Runner; -import org.openjdk.jmh.runner.RunnerException; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; - -import de.cuioss.sheriff.api.ApiSheriff; - -/** - * JMH benchmark suite for API Sheriff library performance testing. - * - * @author API Sheriff Team - */ -@State(Scope.Benchmark) -@BenchmarkMode(Mode.Throughput) -@OutputTimeUnit(TimeUnit.SECONDS) -@Fork(value = 1, jvmArgs = {"-Xmx2G", "-server"}) -@Warmup(iterations = 3, time = 2, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS) -public class BenchmarkRunner { - - private ApiSheriff apiSheriff; - - @Setup(Level.Trial) - public void setup() { - apiSheriff = new ApiSheriff(); - } - - @Benchmark - public String benchmarkGetStatus() { - return apiSheriff.getStatus(); - } - - public static void main(String[] args) throws RunnerException { - // Configure JMH options using helper for system property configuration - Options opt = new OptionsBuilder() - .include(System.getProperty("jmh.include", BenchmarkRunner.class.getSimpleName())) - .forks(BenchmarkOptionsHelper.getForks(1)) - .warmupIterations(BenchmarkOptionsHelper.getWarmupIterations(5)) - .measurementIterations(BenchmarkOptionsHelper.getMeasurementIterations(5)) - .measurementTime(BenchmarkOptionsHelper.getMeasurementTime("2s")) - .warmupTime(BenchmarkOptionsHelper.getWarmupTime("2s")) - .threads(BenchmarkOptionsHelper.getThreadCount(8)) - .resultFormat(BenchmarkOptionsHelper.getResultFormat()) - .result(BenchmarkOptionsHelper.getResultFile(getBenchmarkResultsDir() + "/micro-benchmark-result.json")) - .build(); - - new Runner(opt).run(); - } - - /** - * Gets the benchmark results directory from system property or defaults to target/benchmark-results. - * - * @return the benchmark results directory path - */ - private static String getBenchmarkResultsDir() { - return System.getProperty("benchmark.results.dir", "target/benchmark-results"); - } -} \ No newline at end of file diff --git a/benchmarking/doc/index-visualizer.html b/benchmarking/doc/index-visualizer.html deleted file mode 100644 index 7e26d42c..00000000 --- a/benchmarking/doc/index-visualizer.html +++ /dev/null @@ -1,107 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>JWT Micro Benchmark Results - - - - -
-
-

JWT Micro Benchmark Results

-

Unit-level performance testing of JWT validation components

- -
- -
- - -
-
- - - - - \ No newline at end of file diff --git a/benchmarking/doc/integration-benchmark-visualizer.html b/benchmarking/doc/integration-benchmark-visualizer.html deleted file mode 100644 index e3c70d09..00000000 --- a/benchmarking/doc/integration-benchmark-visualizer.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - JWT Integration Benchmark Results - - - - -
-
-

JWT Integration Benchmark Results

-

End-to-end performance testing with Keycloak and native Quarkus

- -
- -
-

Integration Test Environment

-
-
-
Test Type
-
Native Container
-
-
-
Services
-
Keycloak + Quarkus
-
-
-
Protocol
-
HTTPS/TLS
-
-
-
Benchmark Format
-
JMH Results
-
-
-
- -
- - -
-
- - - - - \ No newline at end of file diff --git a/benchmarking/doc/integration-index.html b/benchmarking/doc/integration-index.html deleted file mode 100644 index 7c33b0ae..00000000 --- a/benchmarking/doc/integration-index.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - JWT Integration Benchmark Results - - - - -
-
-

JWT Integration Benchmark Results

-

End-to-end performance testing with containerized Quarkus application and Keycloak

- -
- -
- - -
-
- - - - - \ No newline at end of file diff --git a/benchmarking/doc/integration-performance-trends.html b/benchmarking/doc/integration-performance-trends.html deleted file mode 100644 index ba41cf31..00000000 --- a/benchmarking/doc/integration-performance-trends.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - Integration Benchmark Performance Trends - - - - - -
-
-

Integration Benchmark Performance Trends

-

Performance metrics and trends for integration benchmarks over the last 10 runs

- -
- -
-

Loading integration performance data...

-
- - - - - - -
- - - - \ No newline at end of file diff --git a/benchmarking/doc/performance-run.json b/benchmarking/doc/performance-run.json deleted file mode 100644 index 26862c15..00000000 --- a/benchmarking/doc/performance-run.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "timestamp": "${TIMESTAMP}", - "commit": "${COMMIT_HASH}", - "performance": { - "score": ${PERFORMANCE_SCORE}, - "throughput": { - "value": ${THROUGHPUT_VALUE}, - "unit": "ops/s" - }, - "averageTime": { - "value": ${AVERAGE_TIME_SEC}, - "unit": "s" - }, - "errorResilience": { - "value": ${ERROR_RESILIENCE_VALUE}, - "unit": "ops/s" - } - }, - "rawMetrics": { - "throughputOpsPerSec": ${THROUGHPUT_OPS_PER_SEC}, - "avgTimeInMicros": ${AVG_TIME_MICROS}, - "errorResilienceOpsPerSec": ${ERROR_RESILIENCE_OPS_PER_SEC} - }, - "environment": { - "javaVersion": "${JAVA_VERSION}", - "jvmArgs": "${JVM_ARGS}", - "osName": "${OS_NAME}" - } -} \ No newline at end of file diff --git a/benchmarking/doc/performance-scoring.adoc b/benchmarking/doc/performance-scoring.adoc deleted file mode 100644 index 6dd0497a..00000000 --- a/benchmarking/doc/performance-scoring.adoc +++ /dev/null @@ -1,107 +0,0 @@ -= JWT Performance Scoring System -:source-highlighter: highlight.js - -== Overview - -A weighted performance score combining throughput, latency, and error resilience into a single metric. - -== Formula - -[source,text] ----- -Performance Score = (Throughput ร— 0.57) + (Latency_Inverted ร— 0.40) + (Error_Resilience ร— 0.03) - -Where: -- Throughput = Operations/second (concurrent load) -- Latency_Inverted = 1,000,000 รท Average_Time_Microseconds -- Error_Resilience = Operations/second (0% error scenario) ----- - -== Metrics - -=== Throughput (57% weight) - -* Measures: Token validations per second under concurrent load -* Configuration: `@BenchmarkMode(Mode.Throughput)` with `@Threads(Threads.MAX)` -* Target: >10,000 ops/sec (good), >50,000 ops/sec (excellent) - -=== Latency (40% weight) - -* Measures: Average single-threaded validation time -* Configuration: `@BenchmarkMode(Mode.AverageTime)` with `@Threads(1)` -* Target: <100ฮผs (good), <50ฮผs (excellent) - -=== Error Resilience (3% weight) - -* Measures: Performance stability with invalid tokens -* Validates error handling doesn't degrade performance - -== Score Interpretation - -[cols="1,1,2", options="header"] -|=== -|Score Range |Level |Description - -|> 40,000 -|Exceptional -|High-scale production ready - -|30,000-40,000 -|Excellent -|Most production scenarios - -|20,000-30,000 -|Good -|Typical applications - -|10,000-20,000 -|Moderate -|Low-medium load - -|< 10,000 -|Needs Improvement -|Optimization required -|=== - -== Example Calculation - -[source,text] ----- -Throughput: 45,000 ops/sec -Average Time: 80 microseconds -Error Resilience: 40,000 ops/sec - -Latency_Inverted = 1,000,000 รท 80 = 12,500 ops/sec -Score = (45,000 ร— 0.57) + (12,500 ร— 0.40) + (40,000 ร— 0.03) - = 25,650 + 5,000 + 1,200 = 31,850 ----- - -== Badge Format - -[source,text] ----- -Performance Score: 32000 (45k ops/s, 0.15ms) - โ†‘ โ†‘ โ†‘ - | | โ””โ”€ Average validation time - | โ””โ”€ Throughput (rounded) - โ””โ”€ Weighted score ----- - -== Usage - -* **Regression Detection**: Track performance over releases -* **Optimization Tracking**: Measure improvement impact -* **Capacity Planning**: Understand performance characteristics - -== Limitations - -* Environment dependent (hardware, JVM settings) -* Based on synthetic test tokens -* Single library performance only - -== Best Practices - -1. Focus on trends over absolute values -2. Use consistent test environments -3. Run multiple iterations for accuracy -4. Consider context when interpreting results \ No newline at end of file diff --git a/benchmarking/doc/performance-trends.html b/benchmarking/doc/performance-trends.html deleted file mode 100644 index 5202e8d7..00000000 --- a/benchmarking/doc/performance-trends.html +++ /dev/null @@ -1,248 +0,0 @@ - - - - - - JWT Validation Performance Trends - - - - - -
-
-

JWT Validation Performance Trends

-

Performance metrics and trends over the last 10 benchmark runs

- -
- -
-

Loading performance data...

-
- - - - - - - -
- - - - - diff --git a/benchmarking/doc/resources/common.css b/benchmarking/doc/resources/common.css deleted file mode 100644 index eb48eaf4..00000000 --- a/benchmarking/doc/resources/common.css +++ /dev/null @@ -1,387 +0,0 @@ -/* CUI JWT Benchmarking - Common Styles */ - -/* Base Styles */ -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; - margin: 0; - padding: 0; - background-color: #f8f9fa; - color: #343a40; -} - -/* Layout */ -.container { - max-width: 1400px; - margin: 0 auto; - background: white; - border-radius: 8px; - box-shadow: 0 2px 10px rgba(0,0,0,0.1); - padding: 30px; -} - -/* Typography */ -h1 { - color: #343a40; - margin-bottom: 10px; - font-size: 2.5rem; - font-weight: 300; -} - -h2 { - color: #495057; - font-size: 1.8rem; - font-weight: 400; - margin-bottom: 15px; -} - -h3 { - color: #495057; - font-size: 1.4rem; - font-weight: 500; - margin-bottom: 10px; -} - -.subtitle { - color: #6c757d; - margin-bottom: 20px; - font-size: 1.1rem; -} - -/* Header & Navigation */ -.header { - margin-bottom: 30px; - text-align: center; - padding: 20px; - background-color: #f8f9fa; - border-bottom: 1px solid #dee2e6; - border-radius: 8px 8px 0 0; - margin: -30px -30px 30px -30px; -} - -.navigation { - display: flex; - justify-content: center; - align-items: center; - gap: 10px; - margin: 15px 0; - flex-wrap: wrap; -} - -/* Badges & Buttons */ -.badge { - display: inline-block; - padding: 6px 12px; - margin: 3px; - border-radius: 4px; - font-size: 12px; - font-weight: 600; - text-decoration: none; - transition: all 0.2s; - border: none; - cursor: pointer; -} - -.badge:hover { - transform: translateY(-1px); - box-shadow: 0 2px 5px rgba(0,0,0,0.2); -} - -.badge-primary { - background-color: #007bff; - color: white; -} - -.badge-secondary { - background-color: #6c757d; - color: white; -} - -.badge-success { - background-color: #28a745; - color: white; -} - -.badge-info { - background-color: #17a2b8; - color: white; -} - -.badge-warning { - background-color: #ffc107; - color: #212529; -} - -.badge-current { - background-color: #343a40; - color: white; - cursor: default; -} - -/* Form Controls */ -.controls { - display: flex; - justify-content: center; - align-items: center; - gap: 20px; - margin-bottom: 30px; - padding: 20px; - background-color: #f8f9fa; - border-radius: 8px; -} - -.control-group { - display: flex; - flex-direction: column; - align-items: center; - gap: 8px; -} - -.control-label { - font-weight: 500; - color: #495057; - font-size: 0.9rem; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -select { - padding: 8px 16px; - border: 2px solid #dee2e6; - border-radius: 6px; - background: white; - font-size: 1rem; - min-width: 200px; - transition: border-color 0.2s; -} - -select:focus { - outline: none; - border-color: #007bff; -} - -/* Cards & Sections */ -.metric-card, .stat-card { - background: white; - padding: 20px; - border-radius: 6px; - text-align: center; - border-left: 4px solid #007bff; - box-shadow: 0 1px 3px rgba(0,0,0,0.1); -} - -.metric-value, .stat-value { - font-size: 2rem; - font-weight: 600; - color: #343a40; - margin-bottom: 5px; -} - -.metric-label, .stat-label { - font-size: 0.8rem; - color: #6c757d; - text-transform: uppercase; - letter-spacing: 0.5px; - margin-top: 5px; -} - -/* Grid Layouts */ -.metrics-grid, .summary-stats { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 20px; - margin: 20px 0; -} - -.charts-container { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 30px; - margin-top: 20px; -} - -/* Chart Styles */ -.chart-section { - background: #ffffff; - border: 1px solid #e9ecef; - border-radius: 8px; - padding: 20px; - box-shadow: 0 1px 3px rgba(0,0,0,0.1); -} - -.chart-title { - font-size: 1.2rem; - font-weight: 600; - color: #343a40; - margin-bottom: 15px; - text-align: center; - padding-bottom: 10px; - border-bottom: 2px solid #f8f9fa; -} - -.chart-container { - position: relative; - height: 400px; - width: 100%; -} - -/* Content Areas */ -.content { - flex: 1; - overflow: hidden; - position: relative; -} - -.content iframe { - width: 100%; - height: 100%; - border: none; - min-height: 600px; -} - -/* Info Boxes */ -.test-info { - text-align: center; - margin-bottom: 20px; - padding: 15px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - border-radius: 8px; -} - -.test-timestamp { - font-size: 0.9rem; - opacity: 0.9; -} - -.step-details { - margin-top: 20px; - padding: 15px; - background: #e3f2fd; - border-radius: 8px; - border-left: 4px solid #2196f3; -} - -.step-details h4 { - margin: 0 0 10px 0; - color: #1976d2; -} - -/* Legends */ -.percentile-legend { - display: flex; - justify-content: center; - gap: 20px; - margin-bottom: 15px; - font-size: 0.9rem; -} - -.legend-item { - display: flex; - align-items: center; - gap: 5px; -} - -.legend-color { - width: 16px; - height: 16px; - border-radius: 3px; -} - -/* Trend Indicators */ -.trend-indicator { - font-size: 0.9rem; - margin-top: 5px; - font-weight: 500; -} - -.trend-up { color: #28a745; } -.trend-down { color: #dc3545; } -.trend-stable { color: #6c757d; } - -/* Status Messages */ -.loading { - text-align: center; - padding: 40px; - color: #6c757d; -} - -.error { - background: #f8d7da; - color: #721c24; - border: 1px solid #f5c6cb; - border-radius: 4px; - padding: 20px; - margin: 20px 0; - text-align: center; -} - -.error h3 { - margin-top: 0; -} - -/* Footer */ -.footer { - margin-top: 40px; - padding-top: 20px; - border-top: 1px solid #dee2e6; - color: #6c757d; - font-size: 0.9rem; - text-align: center; -} - -/* Responsive Design */ -@media (max-width: 1200px) { - .container { - max-width: 95%; - padding: 20px; - } - - .charts-container { - grid-template-columns: 1fr; - } -} - -@media (max-width: 768px) { - .header { - padding: 15px; - } - - h1 { - font-size: 2rem; - } - - .navigation { - flex-direction: column; - gap: 8px; - } - - .controls { - flex-direction: column; - } - - .metrics-grid, .summary-stats { - grid-template-columns: 1fr; - } - - .badge { - font-size: 11px; - padding: 4px 8px; - } -} - -@media (max-width: 480px) { - .container { - padding: 15px; - margin: 10px; - border-radius: 6px; - } - - .header { - margin: -15px -15px 20px -15px; - } - - .percentile-legend { - flex-direction: column; - gap: 10px; - } -} \ No newline at end of file diff --git a/benchmarking/doc/resources/navigation.js b/benchmarking/doc/resources/navigation.js deleted file mode 100644 index c43f014a..00000000 --- a/benchmarking/doc/resources/navigation.js +++ /dev/null @@ -1,69 +0,0 @@ -// CUI JWT Benchmarking - Shared Navigation Component - -class BenchmarkNavigation { - constructor(currentPage = '') { - this.currentPage = currentPage; - this.pages = [ - { id: 'micro', title: 'Micro Benchmarks', file: 'index-visualizer.html', description: 'JMH unit-level performance testing' }, - { id: 'integration', title: 'Integration Benchmarks', file: 'integration-benchmark-visualizer.html', description: 'End-to-end containerized testing' }, - { id: 'step-metrics', title: 'Step Metrics', file: 'step-metrics-visualizer.html', description: 'Detailed step-by-step analysis' }, - { id: 'trends', title: 'Performance Trends', file: 'performance-trends.html', description: 'Historical performance tracking' } - ]; - } - - // Generate navigation HTML - generateNavigation() { - return this.pages.map(page => { - const isCurrent = page.id === this.currentPage; - const badgeClass = isCurrent ? 'badge-current' : 'badge-primary'; - const title = isCurrent ? `Current: ${page.description}` : page.description; - - return ` - ${page.title}${isCurrent ? ' (Current)' : ''} - `; - }).join('\n '); - } - - // Inject navigation into header - injectNavigation() { - const headerElement = document.querySelector('.header'); - if (headerElement) { - const existingNav = headerElement.querySelector('.navigation'); - if (existingNav) { - existingNav.innerHTML = this.generateNavigation(); - } else { - const navDiv = document.createElement('div'); - navDiv.className = 'navigation'; - navDiv.innerHTML = this.generateNavigation(); - headerElement.appendChild(navDiv); - } - } - } - - // Initialize navigation when DOM is loaded - init() { - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', () => this.injectNavigation()); - } else { - this.injectNavigation(); - } - } -} - -// Utility function to get current page type from filename or path -function getCurrentPageType() { - const path = window.location.pathname; - const filename = path.split('/').pop() || 'index-visualizer.html'; - - if (filename.includes('integration-benchmark-visualizer')) return 'integration'; - if (filename.includes('integration')) return 'integration'; - if (filename.includes('step-metrics')) return 'step-metrics'; - if (filename.includes('performance-trends')) return 'trends'; - if (filename.includes('index-visualizer')) return 'micro'; - return 'micro'; // default -} - -// Auto-initialize navigation -const currentPage = getCurrentPageType(); -const navigation = new BenchmarkNavigation(currentPage); -navigation.init(); \ No newline at end of file diff --git a/benchmarking/doc/step-metrics-visualizer.html b/benchmarking/doc/step-metrics-visualizer.html deleted file mode 100644 index 2c89aa8c..00000000 --- a/benchmarking/doc/step-metrics-visualizer.html +++ /dev/null @@ -1,476 +0,0 @@ - - - - - - JWT Validation Step Metrics Visualizer - - - - -
-
-

JWT Validation Step Metrics

-

Interactive visualization of performance metrics for each validation step

- -
- -
-

Loading metrics data...

-
- - - - - -
- - - - - \ No newline at end of file diff --git a/benchmarking/doc/templates/index-visualizer.html b/benchmarking/doc/templates/index-visualizer.html deleted file mode 100644 index 7e26d42c..00000000 --- a/benchmarking/doc/templates/index-visualizer.html +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - JWT Micro Benchmark Results - - - - -
-
-

JWT Micro Benchmark Results

-

Unit-level performance testing of JWT validation components

- -
- -
- - -
-
- - - - - \ No newline at end of file diff --git a/benchmarking/doc/templates/integration-benchmark-visualizer.html b/benchmarking/doc/templates/integration-benchmark-visualizer.html deleted file mode 100644 index e3c70d09..00000000 --- a/benchmarking/doc/templates/integration-benchmark-visualizer.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - JWT Integration Benchmark Results - - - - -
-
-

JWT Integration Benchmark Results

-

End-to-end performance testing with Keycloak and native Quarkus

- -
- -
-

Integration Test Environment

-
-
-
Test Type
-
Native Container
-
-
-
Services
-
Keycloak + Quarkus
-
-
-
Protocol
-
HTTPS/TLS
-
-
-
Benchmark Format
-
JMH Results
-
-
-
- -
- - -
-
- - - - - \ No newline at end of file diff --git a/benchmarking/doc/templates/integration-index.html b/benchmarking/doc/templates/integration-index.html deleted file mode 100644 index 7c33b0ae..00000000 --- a/benchmarking/doc/templates/integration-index.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - JWT Integration Benchmark Results - - - - -
-
-

JWT Integration Benchmark Results

-

End-to-end performance testing with containerized Quarkus application and Keycloak

- -
- -
- - -
-
- - - - - \ No newline at end of file diff --git a/benchmarking/doc/templates/integration-performance-trends.html b/benchmarking/doc/templates/integration-performance-trends.html deleted file mode 100644 index ba41cf31..00000000 --- a/benchmarking/doc/templates/integration-performance-trends.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - Integration Benchmark Performance Trends - - - - - -
-
-

Integration Benchmark Performance Trends

-

Performance metrics and trends for integration benchmarks over the last 10 runs

- -
- -
-

Loading integration performance data...

-
- - - - - - -
- - - - \ No newline at end of file diff --git a/benchmarking/doc/templates/performance-run.json b/benchmarking/doc/templates/performance-run.json deleted file mode 100644 index 26862c15..00000000 --- a/benchmarking/doc/templates/performance-run.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "timestamp": "${TIMESTAMP}", - "commit": "${COMMIT_HASH}", - "performance": { - "score": ${PERFORMANCE_SCORE}, - "throughput": { - "value": ${THROUGHPUT_VALUE}, - "unit": "ops/s" - }, - "averageTime": { - "value": ${AVERAGE_TIME_SEC}, - "unit": "s" - }, - "errorResilience": { - "value": ${ERROR_RESILIENCE_VALUE}, - "unit": "ops/s" - } - }, - "rawMetrics": { - "throughputOpsPerSec": ${THROUGHPUT_OPS_PER_SEC}, - "avgTimeInMicros": ${AVG_TIME_MICROS}, - "errorResilienceOpsPerSec": ${ERROR_RESILIENCE_OPS_PER_SEC} - }, - "environment": { - "javaVersion": "${JAVA_VERSION}", - "jvmArgs": "${JVM_ARGS}", - "osName": "${OS_NAME}" - } -} \ No newline at end of file diff --git a/benchmarking/doc/templates/performance-trends.html b/benchmarking/doc/templates/performance-trends.html deleted file mode 100644 index 5202e8d7..00000000 --- a/benchmarking/doc/templates/performance-trends.html +++ /dev/null @@ -1,248 +0,0 @@ - - - - - - JWT Validation Performance Trends - - - - - -
-
-

JWT Validation Performance Trends

-

Performance metrics and trends over the last 10 benchmark runs

- -
- -
-

Loading performance data...

-
- - - - - - - -
- - - - - diff --git a/benchmarking/doc/templates/resources/common.css b/benchmarking/doc/templates/resources/common.css deleted file mode 100644 index eb48eaf4..00000000 --- a/benchmarking/doc/templates/resources/common.css +++ /dev/null @@ -1,387 +0,0 @@ -/* CUI JWT Benchmarking - Common Styles */ - -/* Base Styles */ -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; - margin: 0; - padding: 0; - background-color: #f8f9fa; - color: #343a40; -} - -/* Layout */ -.container { - max-width: 1400px; - margin: 0 auto; - background: white; - border-radius: 8px; - box-shadow: 0 2px 10px rgba(0,0,0,0.1); - padding: 30px; -} - -/* Typography */ -h1 { - color: #343a40; - margin-bottom: 10px; - font-size: 2.5rem; - font-weight: 300; -} - -h2 { - color: #495057; - font-size: 1.8rem; - font-weight: 400; - margin-bottom: 15px; -} - -h3 { - color: #495057; - font-size: 1.4rem; - font-weight: 500; - margin-bottom: 10px; -} - -.subtitle { - color: #6c757d; - margin-bottom: 20px; - font-size: 1.1rem; -} - -/* Header & Navigation */ -.header { - margin-bottom: 30px; - text-align: center; - padding: 20px; - background-color: #f8f9fa; - border-bottom: 1px solid #dee2e6; - border-radius: 8px 8px 0 0; - margin: -30px -30px 30px -30px; -} - -.navigation { - display: flex; - justify-content: center; - align-items: center; - gap: 10px; - margin: 15px 0; - flex-wrap: wrap; -} - -/* Badges & Buttons */ -.badge { - display: inline-block; - padding: 6px 12px; - margin: 3px; - border-radius: 4px; - font-size: 12px; - font-weight: 600; - text-decoration: none; - transition: all 0.2s; - border: none; - cursor: pointer; -} - -.badge:hover { - transform: translateY(-1px); - box-shadow: 0 2px 5px rgba(0,0,0,0.2); -} - -.badge-primary { - background-color: #007bff; - color: white; -} - -.badge-secondary { - background-color: #6c757d; - color: white; -} - -.badge-success { - background-color: #28a745; - color: white; -} - -.badge-info { - background-color: #17a2b8; - color: white; -} - -.badge-warning { - background-color: #ffc107; - color: #212529; -} - -.badge-current { - background-color: #343a40; - color: white; - cursor: default; -} - -/* Form Controls */ -.controls { - display: flex; - justify-content: center; - align-items: center; - gap: 20px; - margin-bottom: 30px; - padding: 20px; - background-color: #f8f9fa; - border-radius: 8px; -} - -.control-group { - display: flex; - flex-direction: column; - align-items: center; - gap: 8px; -} - -.control-label { - font-weight: 500; - color: #495057; - font-size: 0.9rem; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -select { - padding: 8px 16px; - border: 2px solid #dee2e6; - border-radius: 6px; - background: white; - font-size: 1rem; - min-width: 200px; - transition: border-color 0.2s; -} - -select:focus { - outline: none; - border-color: #007bff; -} - -/* Cards & Sections */ -.metric-card, .stat-card { - background: white; - padding: 20px; - border-radius: 6px; - text-align: center; - border-left: 4px solid #007bff; - box-shadow: 0 1px 3px rgba(0,0,0,0.1); -} - -.metric-value, .stat-value { - font-size: 2rem; - font-weight: 600; - color: #343a40; - margin-bottom: 5px; -} - -.metric-label, .stat-label { - font-size: 0.8rem; - color: #6c757d; - text-transform: uppercase; - letter-spacing: 0.5px; - margin-top: 5px; -} - -/* Grid Layouts */ -.metrics-grid, .summary-stats { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 20px; - margin: 20px 0; -} - -.charts-container { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 30px; - margin-top: 20px; -} - -/* Chart Styles */ -.chart-section { - background: #ffffff; - border: 1px solid #e9ecef; - border-radius: 8px; - padding: 20px; - box-shadow: 0 1px 3px rgba(0,0,0,0.1); -} - -.chart-title { - font-size: 1.2rem; - font-weight: 600; - color: #343a40; - margin-bottom: 15px; - text-align: center; - padding-bottom: 10px; - border-bottom: 2px solid #f8f9fa; -} - -.chart-container { - position: relative; - height: 400px; - width: 100%; -} - -/* Content Areas */ -.content { - flex: 1; - overflow: hidden; - position: relative; -} - -.content iframe { - width: 100%; - height: 100%; - border: none; - min-height: 600px; -} - -/* Info Boxes */ -.test-info { - text-align: center; - margin-bottom: 20px; - padding: 15px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - border-radius: 8px; -} - -.test-timestamp { - font-size: 0.9rem; - opacity: 0.9; -} - -.step-details { - margin-top: 20px; - padding: 15px; - background: #e3f2fd; - border-radius: 8px; - border-left: 4px solid #2196f3; -} - -.step-details h4 { - margin: 0 0 10px 0; - color: #1976d2; -} - -/* Legends */ -.percentile-legend { - display: flex; - justify-content: center; - gap: 20px; - margin-bottom: 15px; - font-size: 0.9rem; -} - -.legend-item { - display: flex; - align-items: center; - gap: 5px; -} - -.legend-color { - width: 16px; - height: 16px; - border-radius: 3px; -} - -/* Trend Indicators */ -.trend-indicator { - font-size: 0.9rem; - margin-top: 5px; - font-weight: 500; -} - -.trend-up { color: #28a745; } -.trend-down { color: #dc3545; } -.trend-stable { color: #6c757d; } - -/* Status Messages */ -.loading { - text-align: center; - padding: 40px; - color: #6c757d; -} - -.error { - background: #f8d7da; - color: #721c24; - border: 1px solid #f5c6cb; - border-radius: 4px; - padding: 20px; - margin: 20px 0; - text-align: center; -} - -.error h3 { - margin-top: 0; -} - -/* Footer */ -.footer { - margin-top: 40px; - padding-top: 20px; - border-top: 1px solid #dee2e6; - color: #6c757d; - font-size: 0.9rem; - text-align: center; -} - -/* Responsive Design */ -@media (max-width: 1200px) { - .container { - max-width: 95%; - padding: 20px; - } - - .charts-container { - grid-template-columns: 1fr; - } -} - -@media (max-width: 768px) { - .header { - padding: 15px; - } - - h1 { - font-size: 2rem; - } - - .navigation { - flex-direction: column; - gap: 8px; - } - - .controls { - flex-direction: column; - } - - .metrics-grid, .summary-stats { - grid-template-columns: 1fr; - } - - .badge { - font-size: 11px; - padding: 4px 8px; - } -} - -@media (max-width: 480px) { - .container { - padding: 15px; - margin: 10px; - border-radius: 6px; - } - - .header { - margin: -15px -15px 20px -15px; - } - - .percentile-legend { - flex-direction: column; - gap: 10px; - } -} \ No newline at end of file diff --git a/benchmarking/doc/templates/resources/navigation.js b/benchmarking/doc/templates/resources/navigation.js deleted file mode 100644 index c43f014a..00000000 --- a/benchmarking/doc/templates/resources/navigation.js +++ /dev/null @@ -1,69 +0,0 @@ -// CUI JWT Benchmarking - Shared Navigation Component - -class BenchmarkNavigation { - constructor(currentPage = '') { - this.currentPage = currentPage; - this.pages = [ - { id: 'micro', title: 'Micro Benchmarks', file: 'index-visualizer.html', description: 'JMH unit-level performance testing' }, - { id: 'integration', title: 'Integration Benchmarks', file: 'integration-benchmark-visualizer.html', description: 'End-to-end containerized testing' }, - { id: 'step-metrics', title: 'Step Metrics', file: 'step-metrics-visualizer.html', description: 'Detailed step-by-step analysis' }, - { id: 'trends', title: 'Performance Trends', file: 'performance-trends.html', description: 'Historical performance tracking' } - ]; - } - - // Generate navigation HTML - generateNavigation() { - return this.pages.map(page => { - const isCurrent = page.id === this.currentPage; - const badgeClass = isCurrent ? 'badge-current' : 'badge-primary'; - const title = isCurrent ? `Current: ${page.description}` : page.description; - - return ` - ${page.title}${isCurrent ? ' (Current)' : ''} - `; - }).join('\n '); - } - - // Inject navigation into header - injectNavigation() { - const headerElement = document.querySelector('.header'); - if (headerElement) { - const existingNav = headerElement.querySelector('.navigation'); - if (existingNav) { - existingNav.innerHTML = this.generateNavigation(); - } else { - const navDiv = document.createElement('div'); - navDiv.className = 'navigation'; - navDiv.innerHTML = this.generateNavigation(); - headerElement.appendChild(navDiv); - } - } - } - - // Initialize navigation when DOM is loaded - init() { - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', () => this.injectNavigation()); - } else { - this.injectNavigation(); - } - } -} - -// Utility function to get current page type from filename or path -function getCurrentPageType() { - const path = window.location.pathname; - const filename = path.split('/').pop() || 'index-visualizer.html'; - - if (filename.includes('integration-benchmark-visualizer')) return 'integration'; - if (filename.includes('integration')) return 'integration'; - if (filename.includes('step-metrics')) return 'step-metrics'; - if (filename.includes('performance-trends')) return 'trends'; - if (filename.includes('index-visualizer')) return 'micro'; - return 'micro'; // default -} - -// Auto-initialize navigation -const currentPage = getCurrentPageType(); -const navigation = new BenchmarkNavigation(currentPage); -navigation.init(); \ No newline at end of file diff --git a/benchmarking/doc/templates/step-metrics-visualizer.html b/benchmarking/doc/templates/step-metrics-visualizer.html deleted file mode 100644 index 2c89aa8c..00000000 --- a/benchmarking/doc/templates/step-metrics-visualizer.html +++ /dev/null @@ -1,476 +0,0 @@ - - - - - - JWT Validation Step Metrics Visualizer - - - - -
-
-

JWT Validation Step Metrics

-

Interactive visualization of performance metrics for each validation step

- -
- -
-

Loading metrics data...

-
- - - - - -
- - - - - \ No newline at end of file diff --git a/benchmarking/pom.xml b/benchmarking/pom.xml deleted file mode 100644 index 8325242c..00000000 --- a/benchmarking/pom.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - 4.0.0 - - de.cuioss.sheriff.api - api-sheriff-parent - 1.0.0-SNAPSHOT - ../pom.xml - - - benchmarking - pom - API Sheriff Benchmarking Parent - Parent module for all benchmarking related modules including library and integration benchmarks - - - benchmark-library - benchmark-integration-quarkus - - - - de.cuioss.sheriff.api.benchmarking - - 1.37 - - 2.10.1 - 2.1.12 - 2.15.1 - - true - - target/benchmark-results - - - - - - - io.quarkus - quarkus-bom - ${version.quarkus} - pom - import - - - - - org.openjdk.jmh - jmh-core - ${version.jmh} - - - org.openjdk.jmh - jmh-generator-annprocess - ${version.jmh} - provided - - - - - com.google.code.gson - gson - ${version.gson} - - - org.hdrhistogram - HdrHistogram - ${version.hdrhistogram} - - - commons-io - commons-io - ${version.commons-io} - - - - - - - - - - - - - - - benchmark - - false - - - - \ No newline at end of file diff --git a/benchmarking/scripts/create-performance-tracking.sh b/benchmarking/scripts/create-performance-tracking.sh deleted file mode 100755 index 765cc10d..00000000 --- a/benchmarking/scripts/create-performance-tracking.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash -# Create performance tracking data from benchmark results -# Usage: create-performance-tracking.sh - -set -e - -JMH_RESULT_FILE="$1" -TEMPLATES_DIR="$2" -OUTPUT_DIR="$3" -COMMIT_HASH="$4" - -if [ ! -f "$JMH_RESULT_FILE" ]; then - echo "Error: JMH result file not found: $JMH_RESULT_FILE" - exit 1 -fi - -echo "Creating performance tracking data..." - -# Load utility libraries -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -source "$SCRIPT_DIR/lib/metrics-utils.sh" - -# Get environment info -eval $(get_environment_info) - -# Create performance tracking directory -mkdir -p "$OUTPUT_DIR/data/tracking" -mkdir -p "$OUTPUT_DIR/badges" - -# Run the unified performance badge script to get metrics and capture in a metrics file -METRICS_FILE="$OUTPUT_DIR/data/tracking/metrics-temp.sh" -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -# Detect benchmark type -BENCHMARK_TYPE=$(detect_benchmark_type "$JMH_RESULT_FILE") - -bash "$SCRIPT_DIR/create-unified-performance-badge.sh" "$BENCHMARK_TYPE" "$JMH_RESULT_FILE" "$OUTPUT_DIR/badges" > "$METRICS_FILE.log" 2>&1 - -# Read the badge output immediately after script execution -BADGE_OUTPUT_CONTENT=$(cat "$METRICS_FILE.log") - -# Extract metrics from badge script output and create a sourceable metrics file -{ - echo "# Performance metrics extracted from create-unified-performance-badge.sh" - echo "$BADGE_OUTPUT_CONTENT" | grep "PERFORMANCE_SCORE=" | head -1 || echo "PERFORMANCE_SCORE=0" - echo "$BADGE_OUTPUT_CONTENT" | grep "THROUGHPUT_OPS_PER_SEC=" | head -1 || echo "THROUGHPUT_OPS_PER_SEC=0" - echo "$BADGE_OUTPUT_CONTENT" | grep "AVERAGE_TIME_SEC=" | head -1 || echo "AVERAGE_TIME_SEC=0" - echo "$BADGE_OUTPUT_CONTENT" | grep "THROUGHPUT_DISPLAY=" | head -1 || echo "THROUGHPUT_DISPLAY=0" - echo "$BADGE_OUTPUT_CONTENT" | grep "ERROR_RESILIENCE_OPS_PER_SEC=" | head -1 || echo "ERROR_RESILIENCE_OPS_PER_SEC=0" - echo "$BADGE_OUTPUT_CONTENT" | grep "AVG_TIME_MICROS=" | head -1 || echo "AVG_TIME_MICROS=0" -} > "$METRICS_FILE" - -# Source the metrics file for more robust parsing -source "$METRICS_FILE" || { - echo "Error: Failed to source metrics from create-unified-performance-badge.sh" - exit 1 -} - -# Clean up temporary files -rm -f "$METRICS_FILE" "$METRICS_FILE.log" - -if [ -z "$PERFORMANCE_SCORE" ] || [ "$PERFORMANCE_SCORE" = "0" ]; then - echo "Warning: Could not extract valid performance metrics for tracking" - exit 1 -fi - -# Create performance run JSON from template using envsubst for safe variable substitution -export TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" -export COMMIT_HASH="$COMMIT_HASH" -export PERFORMANCE_SCORE="$PERFORMANCE_SCORE" -export THROUGHPUT_VALUE="$THROUGHPUT_OPS_PER_SEC" -export AVERAGE_TIME_SEC="$AVERAGE_TIME_SEC" -export THROUGHPUT_DISPLAY="$THROUGHPUT_DISPLAY" -export ERROR_RESILIENCE_VALUE="$ERROR_RESILIENCE_OPS_PER_SEC" -export THROUGHPUT_OPS_PER_SEC="$THROUGHPUT_OPS_PER_SEC" -export AVG_TIME_MICROS="$AVG_TIME_MICROS" -export ERROR_RESILIENCE_OPS_PER_SEC="$ERROR_RESILIENCE_OPS_PER_SEC" -export JAVA_VERSION="$JAVA_VERSION" -export JVM_ARGS="$JVM_ARGS_VALUE" -export OS_NAME="$OS_NAME" - -TIMESTAMP_FILE=$(date -u +"%Y%m%d-%H%M%S") -envsubst < "$TEMPLATES_DIR/performance-run.json" > "$OUTPUT_DIR/data/tracking/performance-$TIMESTAMP_FILE.json" - -echo "Created performance tracking file: data/tracking/performance-$TIMESTAMP_FILE.json" - -# Export metrics for use by calling script -echo "PERF_SCORE=$PERFORMANCE_SCORE" -echo "PERF_THROUGHPUT=$THROUGHPUT_DISPLAY" -echo "PERF_LATENCY=$AVERAGE_TIME_SEC" -echo "PERF_RESILIENCE=$ERROR_RESILIENCE_OPS_PER_SEC" diff --git a/benchmarking/scripts/create-unified-performance-badge.sh b/benchmarking/scripts/create-unified-performance-badge.sh deleted file mode 100755 index d7fbcd9a..00000000 --- a/benchmarking/scripts/create-unified-performance-badge.sh +++ /dev/null @@ -1,165 +0,0 @@ -#!/bin/bash -# Unified Performance Badge Creation Script -# Handles both micro-benchmarks and integration benchmarks -# Usage: create-unified-performance-badge.sh [commit-hash] [timestamp] [timestamp-with-time] - -set -e - -# Load utility libraries -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -source "$SCRIPT_DIR/lib/badge-utils.sh" -source "$SCRIPT_DIR/lib/metrics-utils.sh" - -BENCHMARK_TYPE="$1" # "micro" or "integration" -RESULT_FILE="$2" -OUTPUT_DIR="$3" -COMMIT_HASH="${4:-unknown}" -TIMESTAMP="${5:-$(date +"%Y-%m-%d")}" -TIMESTAMP_WITH_TIME="${6:-$(date +"%Y-%m-%d %H:%M %Z")}" - -if [ ! -f "$RESULT_FILE" ]; then - echo "Error: Result file not found: $RESULT_FILE" - exit 1 -fi - -# Create output directory -mkdir -p "$OUTPUT_DIR" - -echo "Creating unified performance badge for $BENCHMARK_TYPE benchmarks..." - - -# Process micro benchmarks -process_micro_benchmarks() { - echo "Processing micro-benchmark results..." - - # Extract throughput data - local throughput_entry=$(jq -r '.[] | select(.benchmark == "de.cuioss.jwt.validation.benchmark.standard.SimpleCoreValidationBenchmark.measureThroughput")' "$RESULT_FILE" 2>/dev/null) - local throughput_score=$(echo "$throughput_entry" | jq -r '.primaryMetric.score' 2>/dev/null || echo "0") - local throughput_unit=$(echo "$throughput_entry" | jq -r '.primaryMetric.scoreUnit' 2>/dev/null || echo "") - - # Extract average time data - local avg_time_entry=$(jq -r '.[] | select(.benchmark == "de.cuioss.jwt.validation.benchmark.standard.SimpleCoreValidationBenchmark.measureAverageTime")' "$RESULT_FILE" 2>/dev/null) - local avg_time_score=$(echo "$avg_time_entry" | jq -r '.primaryMetric.score' 2>/dev/null || echo "0") - local avg_time_unit=$(echo "$avg_time_entry" | jq -r '.primaryMetric.scoreUnit' 2>/dev/null || echo "") - - # Extract error resilience data - local error_resilience_entry=$(jq -r '.[] | select(.benchmark == "de.cuioss.jwt.validation.benchmark.standard.SimpleErrorLoadBenchmark.validateMixedTokens0")' "$RESULT_FILE" 2>/dev/null) - local error_resilience_score=$(echo "$error_resilience_entry" | jq -r '.primaryMetric.score' 2>/dev/null || echo "0") - local error_resilience_unit=$(echo "$error_resilience_entry" | jq -r '.primaryMetric.scoreUnit' 2>/dev/null || echo "") - - # Convert units - local throughput_ops_per_sec=$(convert_to_ops_per_sec "$throughput_score" "$throughput_unit") - local avg_time_ms=$(convert_to_milliseconds "$avg_time_score" "$avg_time_unit") - local error_resilience_ops_per_sec=$(convert_to_ops_per_sec "$error_resilience_score" "$error_resilience_unit") - - # Calculate performance score - local performance_score=$(calculate_performance_score "$throughput_ops_per_sec" "$avg_time_ms" "$error_resilience_ops_per_sec") - - # Format for display - local formatted_score=$(printf "%.0f" "$performance_score") - local throughput_display=$(format_throughput_display "$throughput_ops_per_sec") - local formatted_avg_time_ms=$(printf "%.0f" "$avg_time_ms") - - # Create performance badge - create_badge "Performance Score" "${formatted_score} (${throughput_display} ops/s, ${formatted_avg_time_ms}ms)" "brightgreen" "performance-badge.json" - - echo "Created micro-benchmark performance badge: Score=$formatted_score (Throughput=${throughput_display} ops/s, AvgTime=${formatted_avg_time_ms}ms)" - - # Export metrics for performance tracking - echo "PERFORMANCE_SCORE=$formatted_score" - echo "THROUGHPUT_OPS_PER_SEC=$throughput_ops_per_sec" - echo "AVERAGE_TIME_SEC=$(echo "scale=6; $avg_time_ms / 1000" | bc -l)" - echo "THROUGHPUT_DISPLAY=$throughput_display" - echo "ERROR_RESILIENCE_OPS_PER_SEC=$error_resilience_ops_per_sec" - echo "AVG_TIME_MICROS=$(echo "scale=0; $avg_time_ms * 1000" | bc -l)" -} - -# Process integration benchmarks -process_integration_benchmarks() { - echo "Processing integration benchmark results..." - - # Calculate average integration throughput (handle both ops/s and ops/ms) - local avg_throughput=$(jq -r ' - [.[] | select(.benchmark and .mode == "thrpt")] | - if length > 0 then - map( - if .primaryMetric.scoreUnit == "ops/ms" then - .primaryMetric.score * 1000 # Convert ops/ms to ops/s - else - .primaryMetric.score - end - ) | add / length | . * 100 | round / 100 - else - 0 - end - ' "$RESULT_FILE") - - # Calculate average integration latency (convert to milliseconds) - local avg_latency_ms=$(jq -r ' - [.[] | select(.benchmark and (.mode == "avgt" or .primaryMetric.scoreUnit == "ms/op"))] | - if length > 0 then - (map(.primaryMetric.score) | add / length | . * 1000 | round / 1000) - else - 0 - end - ' "$RESULT_FILE") - - # Calculate integration performance score - local integration_score=$(calculate_performance_score "$avg_throughput" "$avg_latency_ms" "0") - - # Format for display - local formatted_score=$(printf "%.0f" "$integration_score") - local throughput_display=$(format_throughput_display "$avg_throughput") - local formatted_latency_ms=$(printf "%.0f" "$avg_latency_ms") - - # Determine badge color - local badge_color=$(get_performance_badge_color "$integration_score") - - # Create integration performance badge (different name from micro benchmarks) - create_badge "Integration Performance" "${formatted_score} (${throughput_display} ops/s, ${formatted_latency_ms}ms)" "$badge_color" "integration-performance-badge.json" - - # Create additional integration-specific badges - local throughput_color=$(get_throughput_badge_color "$avg_throughput") - local latency_color=$(get_latency_badge_color "$avg_latency_ms") - - create_badge "Throughput" "${throughput_display} ops/s" "$throughput_color" "throughput-badge.json" - create_badge "Latency" "${formatted_latency_ms}ms" "$latency_color" "latency-badge.json" - - echo "Created integration benchmark badges: Score=$formatted_score (Throughput=${throughput_display} ops/s, Latency=${formatted_latency_ms}ms)" - - # Export metrics for performance tracking - echo "PERFORMANCE_SCORE=$formatted_score" - echo "THROUGHPUT_OPS_PER_SEC=$avg_throughput" - echo "AVERAGE_TIME_SEC=$(echo "scale=6; $avg_latency_ms / 1000" | bc -l)" - echo "THROUGHPUT_DISPLAY=$throughput_display" - echo "ERROR_RESILIENCE_OPS_PER_SEC=0" - echo "AVG_TIME_MICROS=$(echo "scale=0; $avg_latency_ms * 1000" | bc -l)" -} - -# Create simple badges for both types -create_simple_badges() { - echo "Creating simple informational badges..." - - # Create combined badge for all benchmarks - create_badge "JWT Benchmarks" "Updated $TIMESTAMP" "brightgreen" "all-benchmarks.json" - - # Create last benchmark run badge with time - create_badge "Last Benchmark Run" "$TIMESTAMP_WITH_TIME" "blue" "last-run-badge.json" - - echo "Simple badges created successfully" -} - -# Main processing logic -if [ "$BENCHMARK_TYPE" = "micro" ]; then - process_micro_benchmarks -elif [ "$BENCHMARK_TYPE" = "integration" ]; then - process_integration_benchmarks -else - echo "Error: Invalid benchmark type. Use 'micro' or 'integration'" - exit 1 -fi - -# Always create simple badges -create_simple_badges - -echo "Badge creation completed successfully" \ No newline at end of file diff --git a/benchmarking/scripts/create-unified-trend-badge.sh b/benchmarking/scripts/create-unified-trend-badge.sh deleted file mode 100755 index b5591d92..00000000 --- a/benchmarking/scripts/create-unified-trend-badge.sh +++ /dev/null @@ -1,136 +0,0 @@ -#!/bin/bash -# Unified Trend Badge Creation Script -# Handles both micro-benchmarks and integration benchmarks -# Usage: create-unified-trend-badge.sh - -set -e - -BENCHMARK_TYPE="$1" # "micro" or "integration" -TRACKING_FILE="$2" -OUTPUT_DIR="$3" - -if [ ! -f "$TRACKING_FILE" ]; then - echo "Error: Tracking file not found: $TRACKING_FILE" - # Create placeholder badge - mkdir -p "$OUTPUT_DIR" - echo "{\"schemaVersion\":1,\"label\":\"Performance Trend\",\"message\":\"โ†’ No Data\",\"color\":\"lightgrey\"}" > "$OUTPUT_DIR/trend-badge.json" - echo "Created placeholder trend badge: no tracking data" - exit 0 -fi - -# Create output directory -mkdir -p "$OUTPUT_DIR" - -echo "Calculating performance trends for $BENCHMARK_TYPE benchmarks..." - -# Common trend calculation function -calculate_trend() { - local scores="$1" - local score_count="$2" - - if [ "$score_count" -ge 2 ]; then - # Calculate simple trend (percentage change from first to last in the dataset) - local first_score=$(echo "$scores" | head -1) - local last_score=$(echo "$scores" | tail -1) - - # Handle division by zero - local percent_change - if [ "$(echo "$first_score == 0" | bc -l)" -eq 1 ]; then - if [ "$(echo "$last_score == 0" | bc -l)" -eq 1 ]; then - percent_change="0" - else - percent_change="99999" # Treat as massive improvement - fi - else - percent_change=$(echo "scale=2; (($last_score - $first_score) / $first_score) * 100" | bc -l) - fi - - # Determine trend direction and color - local trend_direction="stable" - local trend_color="lightgrey" - local trend_symbol="โ†’" - - if [ $(echo "$percent_change > 2" | bc -l) -eq 1 ]; then - trend_direction="improving" - trend_color="brightgreen" - trend_symbol="โ†—" - elif [ $(echo "$percent_change < -2" | bc -l) -eq 1 ]; then - trend_direction="declining" - trend_color="orange" - trend_symbol="โ†˜" - fi - - local abs_change=$(echo "$percent_change" | sed 's/-//') - local formatted_change=$(printf "%.1f" $abs_change) - - # Create trend badge with benchmark type context - local label="Performance Trend" - local badge_file="trend-badge.json" - if [ "$BENCHMARK_TYPE" = "integration" ]; then - label="Integration Performance Trend" - badge_file="integration-trend-badge.json" - fi - - echo "{\"schemaVersion\":1,\"label\":\"$label\",\"message\":\"$trend_symbol $formatted_change% ($trend_direction)\",\"color\":\"$trend_color\"}" > "$OUTPUT_DIR/$badge_file" - - echo "Created trend badge: $trend_direction ($formatted_change%)" - else - # Not enough data for trend - local label="Performance Trend" - local badge_file="trend-badge.json" - if [ "$BENCHMARK_TYPE" = "integration" ]; then - label="Integration Performance Trend" - badge_file="integration-trend-badge.json" - fi - - echo "{\"schemaVersion\":1,\"label\":\"$label\",\"message\":\"โ†’ Insufficient Data\",\"color\":\"lightgrey\"}" > "$OUTPUT_DIR/$badge_file" - echo "Created trend badge: insufficient data" - fi -} - -# Extract performance scores based on benchmark type -if [ "$BENCHMARK_TYPE" = "micro" ]; then - # Extract scores from micro-benchmark tracking file - SCORES=$(jq -r '.runs[].performance.score' "$TRACKING_FILE" 2>/dev/null | tail -10) - SCORE_COUNT=$(echo "$SCORES" | wc -l) - - if [ "$SCORE_COUNT" -eq 0 ]; then - # Try alternative structure - SCORES=$(jq -r '.runs[].score' "$TRACKING_FILE" 2>/dev/null | tail -10) - SCORE_COUNT=$(echo "$SCORES" | wc -l) - fi - -elif [ "$BENCHMARK_TYPE" = "integration" ]; then - # Extract scores from integration benchmark tracking file - SCORES=$(jq -r '.runs[].integration.performanceScore' "$TRACKING_FILE" 2>/dev/null | tail -10) - SCORE_COUNT=$(echo "$SCORES" | wc -l) - - if [ "$SCORE_COUNT" -eq 0 ]; then - # Try alternative structure - SCORES=$(jq -r '.runs[].performance.score' "$TRACKING_FILE" 2>/dev/null | tail -10) - SCORE_COUNT=$(echo "$SCORES" | wc -l) - fi - -else - echo "Error: Invalid benchmark type. Use 'micro' or 'integration'" - exit 1 -fi - -# Validate extracted scores -if [ "$SCORE_COUNT" -eq 0 ] || [ -z "$SCORES" ]; then - echo "Warning: No performance scores found in tracking file" - label="Performance Trend" - badge_file="trend-badge.json" - if [ "$BENCHMARK_TYPE" = "integration" ]; then - label="Integration Performance Trend" - badge_file="integration-trend-badge.json" - fi - echo "{\"schemaVersion\":1,\"label\":\"$label\",\"message\":\"โ†’ No Data\",\"color\":\"lightgrey\"}" > "$OUTPUT_DIR/$badge_file" - echo "Created placeholder trend badge: no performance data" - exit 0 -fi - -# Calculate and create trend badge -calculate_trend "$SCORES" "$SCORE_COUNT" - -echo "Trend badge creation completed successfully" \ No newline at end of file diff --git a/benchmarking/scripts/lib/badge-utils.sh b/benchmarking/scripts/lib/badge-utils.sh deleted file mode 100644 index 664ce985..00000000 --- a/benchmarking/scripts/lib/badge-utils.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash -# Common badge creation utilities -# Usage: source lib/badge-utils.sh - -# Common badge creation function -create_badge() { - local label="$1" - local message="$2" - local color="$3" - local filename="$4" - local output_dir="${5:-$OUTPUT_DIR}" - - echo "{\"schemaVersion\":1,\"label\":\"$label\",\"message\":\"$message\",\"color\":\"$color\"}" > "$output_dir/$filename" -} - -# Format throughput for display -format_throughput_display() { - local throughput="$1" - - if [ $(echo "$throughput >= 1000" | bc -l) -eq 1 ]; then - throughput_k=$(echo "scale=1; $throughput / 1000" | bc -l) - echo "${throughput_k}k" - else - printf "%.0f" "$throughput" - fi -} - -# Determine badge color based on score -get_performance_badge_color() { - local score="$1" - - if (( $(echo "$score >= 50" | bc -l) )); then - echo "green" - elif (( $(echo "$score >= 25" | bc -l) )); then - echo "yellow" - elif (( $(echo "$score >= 10" | bc -l) )); then - echo "orange" - else - echo "red" - fi -} - -# Get throughput badge color -get_throughput_badge_color() { - local throughput="$1" - - if (( $(echo "$throughput >= 1000" | bc -l) )); then - echo "green" - elif (( $(echo "$throughput >= 500" | bc -l) )); then - echo "yellow" - elif (( $(echo "$throughput >= 100" | bc -l) )); then - echo "orange" - else - echo "red" - fi -} - -# Get latency badge color (lower is better) -get_latency_badge_color() { - local latency_ms="$1" - - if (( $(echo "$latency_ms <= 10" | bc -l) )); then - echo "green" - elif (( $(echo "$latency_ms <= 25" | bc -l) )); then - echo "yellow" - elif (( $(echo "$latency_ms <= 50" | bc -l) )); then - echo "orange" - else - echo "red" - fi -} \ No newline at end of file diff --git a/benchmarking/scripts/lib/metrics-utils.sh b/benchmarking/scripts/lib/metrics-utils.sh deleted file mode 100644 index dbcbc4f6..00000000 --- a/benchmarking/scripts/lib/metrics-utils.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/bash -# Common metric calculation utilities -# Usage: source lib/metrics-utils.sh - -# Convert various units to operations per second -convert_to_ops_per_sec() { - local value="$1" - local unit="$2" - - if [[ "$unit" == "ops/s" ]]; then - echo "$value" - elif [[ "$unit" == "ops/ms" ]]; then - # Convert ops/ms to ops/s by multiplying by 1000 - echo "scale=2; $value * 1000" | bc -l - elif [[ "$unit" == "s/op" ]]; then - if [ "$(echo "$value == 0" | bc -l)" -eq 1 ]; then - echo "0" - else - echo "scale=2; 1 / $value" | bc -l - fi - elif [[ "$unit" == "ms/op" ]]; then - if [ "$(echo "$value == 0" | bc -l)" -eq 1 ]; then - echo "0" - else - echo "scale=2; 1000 / $value" | bc -l - fi - elif [[ "$unit" == "us/op" ]]; then - if [ "$(echo "$value == 0" | bc -l)" -eq 1 ]; then - echo "0" - else - echo "scale=2; 1000000 / $value" | bc -l - fi - else - echo "0" - fi -} - -# Convert various units to milliseconds -convert_to_milliseconds() { - local value="$1" - local unit="$2" - - if [[ "$unit" == "ms/op" ]]; then - echo "$value" - elif [[ "$unit" == "us/op" ]]; then - echo "scale=3; $value / 1000" | bc -l - elif [[ "$unit" == "s/op" ]]; then - echo "scale=3; $value * 1000" | bc -l - else - echo "0" - fi -} - -# Calculate weighted performance score -calculate_performance_score() { - local throughput="$1" - local latency_ms="$2" - local error_resilience="$3" - - # Convert latency to operations per second equivalent - local latency_ops_per_sec - if [ "$(echo "$latency_ms == 0" | bc -l)" -eq 1 ]; then - latency_ops_per_sec="0" - else - latency_ops_per_sec=$(echo "scale=2; 1000 / $latency_ms" | bc -l) - fi - - # Calculate weighted score - if [ "$error_resilience" != "0" ]; then - # Enhanced formula: (throughput * 0.57) + (latency * 0.40) + (error_resilience * 0.03) - echo "scale=2; ($throughput * 0.57) + ($latency_ops_per_sec * 0.40) + ($error_resilience * 0.03)" | bc -l - else - # Fallback to original formula: (throughput * 0.6) + (latency * 0.4) - echo "scale=2; ($throughput * 0.6) + ($latency_ops_per_sec * 0.4)" | bc -l - fi -} - -# Detect benchmark type from JMH result file -detect_benchmark_type() { - local result_file="$1" - - # Check for API-Sheriff benchmark patterns - if grep -q "de\\.cuioss\\.sheriff\\.api\\.quarkus\\.benchmark" "$result_file"; then - echo "integration" - elif grep -q "de\\.cuioss\\.sheriff\\.api\\.benchmark" "$result_file"; then - echo "micro" - # Legacy cui-jwt patterns - elif grep -q "JwtHealthBenchmark\|JwtValidationBenchmark\|JwtEchoBenchmark" "$result_file"; then - echo "integration" - elif grep -q "SimpleCoreValidationBenchmark\|SimpleErrorLoadBenchmark" "$result_file"; then - echo "micro" - else - echo "integration" # Default to integration - fi -} - -# Extract environment information -get_environment_info() { - local java_version=$(java -version 2>&1 | head -n 1 | cut -d'"' -f2 2>/dev/null || echo "unknown") - local os_name="$(uname -s)" - local jvm_args_value="default" - - # Capture actual JVM arguments if available from environment - if [ -n "$MAVEN_OPTS" ]; then - jvm_args_value="$MAVEN_OPTS" - elif [ -n "$JAVA_OPTS" ]; then - jvm_args_value="$JAVA_OPTS" - fi - - echo "JAVA_VERSION=$java_version" - echo "OS_NAME=$os_name" - echo "JVM_ARGS_VALUE=$jvm_args_value" -} \ No newline at end of file diff --git a/benchmarking/scripts/prepare-github-pages.sh b/benchmarking/scripts/prepare-github-pages.sh deleted file mode 100755 index f219faba..00000000 --- a/benchmarking/scripts/prepare-github-pages.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/bash -# Prepare GitHub Pages structure and copy benchmark results -# Usage: prepare-github-pages.sh - -set -e - -BENCHMARK_RESULTS_DIR="$1" -TEMPLATES_DIR="$2" -OUTPUT_DIR="$3" - -echo "Preparing unified GitHub Pages structure..." - -# Create directories -mkdir -p "$OUTPUT_DIR" -mkdir -p "$OUTPUT_DIR/data" -mkdir -p "$OUTPUT_DIR/resources" -mkdir -p "$OUTPUT_DIR/badges" - -# Copy all benchmark results to data directory -if [ -d "$BENCHMARK_RESULTS_DIR" ]; then - cp -r "$BENCHMARK_RESULTS_DIR"/* "$OUTPUT_DIR/data"/ - echo "Copied benchmark results to data directory" -else - echo "Warning: Benchmark results directory not found: $BENCHMARK_RESULTS_DIR" -fi - -# Find and copy the main JMH result file -if [ -f "jmh-result.json" ]; then - echo "Using jmh-result.json from project root" - cp jmh-result.json "$OUTPUT_DIR/data/jmh-result.json" -else - # Find the result file in benchmark-results directory - echo "Looking for JMH result files in benchmark-results directory" - if [ -d "$BENCHMARK_RESULTS_DIR" ]; then - find "$BENCHMARK_RESULTS_DIR" -name "jmh-result*.json" -type f -exec cp {} "$OUTPUT_DIR/data/jmh-result.json" \; 2>/dev/null || true - find "$BENCHMARK_RESULTS_DIR" -name "micro-benchmark-result.json" -type f -exec cp {} "$OUTPUT_DIR/data/jmh-result.json" \; 2>/dev/null || true - fi -fi - -# Verify benchmark result file exists -if [ ! -f "$OUTPUT_DIR/data/jmh-result.json" ]; then - echo "ERROR: No benchmark result file found!" - exit 1 -fi - -# Copy all page templates to root directory using original names -cp "$TEMPLATES_DIR/index-visualizer.html" "$OUTPUT_DIR/index-visualizer.html" -echo "Copied micro benchmarks template" - -if [ -f "$TEMPLATES_DIR/integration-index.html" ]; then - cp "$TEMPLATES_DIR/integration-index.html" "$OUTPUT_DIR/integration-index.html" - echo "Copied integration benchmarks template" -fi - -if [ -f "$TEMPLATES_DIR/step-metrics-visualizer.html" ]; then - cp "$TEMPLATES_DIR/step-metrics-visualizer.html" "$OUTPUT_DIR/step-metrics-visualizer.html" - echo "Copied step metrics visualizer template" -fi - -if [ -f "$TEMPLATES_DIR/performance-trends.html" ]; then - cp "$TEMPLATES_DIR/performance-trends.html" "$OUTPUT_DIR/performance-trends.html" - echo "Copied performance trends template" -fi - -if [ -f "$TEMPLATES_DIR/integration-performance-trends.html" ]; then - cp "$TEMPLATES_DIR/integration-performance-trends.html" "$OUTPUT_DIR/integration-performance-trends.html" - echo "Copied integration performance trends template" -fi - -# Copy shared resources -if [ -d "$TEMPLATES_DIR/resources" ]; then - cp -r "$TEMPLATES_DIR/resources"/* "$OUTPUT_DIR/resources"/ - echo "Copied shared resources (CSS, JS)" -fi - -# Ensure jwt-validation-metrics.json is available for step metrics -if [ -f "$TEMPLATES_DIR/data/jwt-validation-metrics.json" ]; then - cp "$TEMPLATES_DIR/data/jwt-validation-metrics.json" "$OUTPUT_DIR/data/" - echo "Copied JWT validation step metrics" -fi - -echo "โœ… Unified GitHub Pages structure prepared successfully" -echo "๐Ÿ“ All pages are now in same directory with shared navigation" -echo "๐Ÿ“Š Data files are in data/ subdirectory" -echo "๐ŸŽจ Shared resources are in resources/ subdirectory" \ No newline at end of file diff --git a/benchmarking/scripts/prepare-step-metrics.sh b/benchmarking/scripts/prepare-step-metrics.sh deleted file mode 100755 index a44150fc..00000000 --- a/benchmarking/scripts/prepare-step-metrics.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -# Prepare step metrics visualization (integrated into prepare-github-pages.sh) -# Usage: prepare-step-metrics.sh - -set -e - -BENCHMARK_RESULTS_DIR="$1" -TEMPLATES_DIR="$2" -OUTPUT_DIR="$3" - -echo "Preparing step metrics visualization..." - -# Create data directory -mkdir -p "$OUTPUT_DIR/data" - -# Copy step metrics data if it exists in benchmark results -if [ -f "$BENCHMARK_RESULTS_DIR/jwt-validation-metrics.json" ]; then - cp "$BENCHMARK_RESULTS_DIR/jwt-validation-metrics.json" "$OUTPUT_DIR/data/" - echo "Copied jwt-validation-metrics.json to data directory" -else - echo "Warning: jwt-validation-metrics.json not found in $BENCHMARK_RESULTS_DIR" -fi - -# Copy from templates data directory if available -if [ -f "$TEMPLATES_DIR/data/jwt-validation-metrics.json" ]; then - cp "$TEMPLATES_DIR/data/jwt-validation-metrics.json" "$OUTPUT_DIR/data/" - echo "Copied jwt-validation-metrics.json from templates data directory" -fi - -echo "Step metrics data prepared successfully" \ No newline at end of file diff --git a/benchmarking/scripts/process-all-benchmarks.sh b/benchmarking/scripts/process-all-benchmarks.sh deleted file mode 100755 index 2f9f3358..00000000 --- a/benchmarking/scripts/process-all-benchmarks.sh +++ /dev/null @@ -1,132 +0,0 @@ -#!/bin/bash -# Master orchestration script for processing all benchmark types -# Usage: process-all-benchmarks.sh - -set -e - -BENCHMARK_RESULTS_DIR="$1" -TEMPLATES_DIR="$2" -OUTPUT_DIR="$3" -COMMIT_HASH="$4" -TIMESTAMP="$5" -TIMESTAMP_WITH_TIME="$6" - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -echo "๐Ÿš€ Processing all benchmark results..." -echo "๐Ÿ“ Benchmark Results: $BENCHMARK_RESULTS_DIR" -echo "๐Ÿ“‹ Templates: $TEMPLATES_DIR" -echo "๐Ÿ“ฆ Output: $OUTPUT_DIR" -echo "๐Ÿ”— Commit: $COMMIT_HASH" -echo "๐Ÿ“… Timestamp: $TIMESTAMP" - -# Initialize processing results -RESULTS_FILE="$OUTPUT_DIR/processing-results.json" -mkdir -p "$OUTPUT_DIR" - -# Create initial results structure -cat > "$RESULTS_FILE" << EOF -{ - "timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")", - "commit": "$COMMIT_HASH", - "processing": { - "micro": { "status": "not_found", "message": "No micro benchmark results" }, - "integration": { "status": "not_found", "message": "No integration benchmark results" } - }, - "errors": [] -} -EOF - -# Create header for badge markdown -echo "## Benchmark Results ($TIMESTAMP)" > "$OUTPUT_DIR/badge-markdown.txt" - -# Prepare basic GitHub Pages structure -echo "๐Ÿ“‹ Preparing GitHub Pages structure..." -bash "$SCRIPT_DIR/prepare-github-pages.sh" "$BENCHMARK_RESULTS_DIR" "$TEMPLATES_DIR" "$OUTPUT_DIR" - -# Process micro benchmarks -echo "๐Ÿ”ฌ Checking for micro benchmark results..." -if [ -f "$BENCHMARK_RESULTS_DIR/micro-benchmark-result.json" ]; then - echo "โœ… Found micro benchmark results, processing..." - - # Copy for legacy compatibility - cp "$BENCHMARK_RESULTS_DIR/micro-benchmark-result.json" "$BENCHMARK_RESULTS_DIR/jmh-result.json" - - # Process micro benchmarks - if bash "$SCRIPT_DIR/process-micro-benchmarks.sh" "$BENCHMARK_RESULTS_DIR/jmh-result.json" "$TEMPLATES_DIR" "$OUTPUT_DIR" "$COMMIT_HASH" "$TIMESTAMP" "$TIMESTAMP_WITH_TIME"; then - # Update results file with success - jq '.processing.micro = {"status": "success", "message": "Micro benchmarks processed successfully"}' "$RESULTS_FILE" > "${RESULTS_FILE}.tmp" && mv "${RESULTS_FILE}.tmp" "$RESULTS_FILE" - echo "โœ… Micro benchmarks processed successfully" - else - # Update results file with error - jq '.processing.micro = {"status": "error", "message": "Failed to process micro benchmarks"} | .errors += ["Micro benchmark processing failed"]' "$RESULTS_FILE" > "${RESULTS_FILE}.tmp" && mv "${RESULTS_FILE}.tmp" "$RESULTS_FILE" - echo "โŒ Failed to process micro benchmarks" - fi -else - echo "โš ๏ธ No micro benchmark results found" - # Create fallback badges for micro benchmarks - mkdir -p "$OUTPUT_DIR/badges" - echo "{\"schemaVersion\":1,\"label\":\"Performance Score\",\"message\":\"No Data\",\"color\":\"red\"}" > "$OUTPUT_DIR/badges/performance-badge.json" - echo "{\"schemaVersion\":1,\"label\":\"Performance Trend\",\"message\":\"โ†’ No Data\",\"color\":\"lightgrey\"}" > "$OUTPUT_DIR/badges/trend-badge.json" -fi - -# Process integration benchmarks -echo "๐Ÿ”— Checking for integration benchmark results..." -# Check for combined file first (preferred approach), then fallback to separate files -if [ -f "$BENCHMARK_RESULTS_DIR/integration-benchmark-result.json" ]; then - echo "โœ… Found combined integration benchmark results, processing..." - - # Process the combined JMH benchmark file for integration tests - if bash "$SCRIPT_DIR/process-integration-jmh-benchmarks.sh" "$BENCHMARK_RESULTS_DIR/integration-benchmark-result.json" "$OUTPUT_DIR" "$COMMIT_HASH" "$TIMESTAMP"; then - # Update results file with success - jq '.processing.integration = {"status": "success", "message": "Integration benchmarks processed successfully"}' "$RESULTS_FILE" > "${RESULTS_FILE}.tmp" && mv "${RESULTS_FILE}.tmp" "$RESULTS_FILE" - echo "โœ… Integration benchmarks processed successfully" - - # Add integration benchmark link to main page - echo "- [Integration Benchmark Results](integration-index.html)" >> "$OUTPUT_DIR/badge-markdown.txt" - else - # Update results file with error - jq '.processing.integration = {"status": "error", "message": "Failed to process integration benchmarks"} | .errors += ["Integration benchmark processing failed"]' "$RESULTS_FILE" > "${RESULTS_FILE}.tmp" && mv "${RESULTS_FILE}.tmp" "$RESULTS_FILE" - echo "โŒ Failed to process integration benchmarks" - fi -elif [ -f "$BENCHMARK_RESULTS_DIR/health-check-results.json" ] && [ -f "$BENCHMARK_RESULTS_DIR/jwt-validation-results.json" ]; then - echo "โœ… Found separate integration benchmark results, processing..." - - # Process integration benchmarks with separate files - if bash "$SCRIPT_DIR/process-integration-benchmarks.sh" "$BENCHMARK_RESULTS_DIR/health-check-results.json" "$BENCHMARK_RESULTS_DIR/jwt-validation-results.json" "$OUTPUT_DIR" "$COMMIT_HASH"; then - # Update results file with success - jq '.processing.integration = {"status": "success", "message": "Integration benchmarks processed successfully"}' "$RESULTS_FILE" > "${RESULTS_FILE}.tmp" && mv "${RESULTS_FILE}.tmp" "$RESULTS_FILE" - echo "โœ… Integration benchmarks processed successfully" - - # Add integration benchmark link to main page - echo "- [Integration Benchmark Results](integration-index.html)" >> "$OUTPUT_DIR/badge-markdown.txt" - else - # Update results file with error - jq '.processing.integration = {"status": "error", "message": "Failed to process integration benchmarks"} | .errors += ["Integration benchmark processing failed"]' "$RESULTS_FILE" > "${RESULTS_FILE}.tmp" && mv "${RESULTS_FILE}.tmp" "$RESULTS_FILE" - echo "โŒ Failed to process integration benchmarks" - fi -else - echo "โš ๏ธ No integration benchmark results found" - # Create fallback badges for integration benchmarks - mkdir -p "$OUTPUT_DIR/badges" - echo "{\"schemaVersion\":1,\"label\":\"Integration Performance\",\"message\":\"No Data\",\"color\":\"red\"}" > "$OUTPUT_DIR/badges/integration-performance-badge.json" - echo "{\"schemaVersion\":1,\"label\":\"Integration Trend\",\"message\":\"โ†’ No Data\",\"color\":\"lightgrey\"}" > "$OUTPUT_DIR/badges/integration-trend-badge.json" -fi - -# Final status report -echo "" -echo "๐Ÿ“Š Processing Summary:" -MICRO_STATUS=$(jq -r '.processing.micro.status' "$RESULTS_FILE") -INTEGRATION_STATUS=$(jq -r '.processing.integration.status' "$RESULTS_FILE") -ERROR_COUNT=$(jq '.errors | length' "$RESULTS_FILE") - -echo " ๐Ÿ”ฌ Micro Benchmarks: $MICRO_STATUS" -echo " ๐Ÿ”— Integration Benchmarks: $INTEGRATION_STATUS" -echo " โŒ Errors: $ERROR_COUNT" - -if [ "$ERROR_COUNT" -gt 0 ]; then - echo "โš ๏ธ Some processing steps failed. Check $RESULTS_FILE for details." - exit 1 -else - echo "โœ… All benchmark processing completed successfully!" -fi \ No newline at end of file diff --git a/benchmarking/scripts/process-integration-benchmarks.sh b/benchmarking/scripts/process-integration-benchmarks.sh deleted file mode 100755 index 5ae4b808..00000000 --- a/benchmarking/scripts/process-integration-benchmarks.sh +++ /dev/null @@ -1,146 +0,0 @@ -#!/bin/bash -# Process integration benchmark results and create badges/visualization -# Usage: process-integration-benchmarks.sh - -set -e - -HEALTH_RESULTS="$1" -JWT_RESULTS="$2" -OUTPUT_DIR="$3" -COMMIT_HASH="$4" - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -echo "๐Ÿ”— Processing integration benchmark results..." -echo "๐Ÿ’š Health Check Results: $HEALTH_RESULTS" -echo "๐Ÿ” JWT Validation Results: $JWT_RESULTS" - -# Validate input files -if [ ! -f "$HEALTH_RESULTS" ]; then - echo "โŒ Error: Health check results not found: $HEALTH_RESULTS" - exit 1 -fi - -if [ ! -f "$JWT_RESULTS" ]; then - echo "โŒ Error: JWT validation results not found: $JWT_RESULTS" - exit 1 -fi - -# Ensure output directories exist -mkdir -p "$OUTPUT_DIR/badges" -mkdir -p "$OUTPUT_DIR/data" - -# Create combined integration results file for compatibility -echo "๐Ÿ”„ Merging integration benchmark results..." -INTEGRATION_RESULT="$OUTPUT_DIR/data/integration-result.json" - -jq -n --slurpfile health "$HEALTH_RESULTS" --slurpfile jwt "$JWT_RESULTS" ' - { - "timestamp": (now | strftime("%Y-%m-%dT%H:%M:%SZ")), - "commit": "'$COMMIT_HASH'", - "health_check": $health[0], - "jwt_validation": $jwt[0], - "jwt_overhead_ms": ($jwt[0].latency_p95_ms - $health[0].latency_p95_ms) - }' > "$INTEGRATION_RESULT" - -echo "โœ… Integration results merged successfully" - -# Copy integration template to unified structure -TEMPLATES_DIR="$(dirname "$SCRIPT_DIR")/doc/templates" -if [ -f "$TEMPLATES_DIR/integration-index.html" ]; then - cp "$TEMPLATES_DIR/integration-index.html" "$OUTPUT_DIR/integration-index.html" - echo "๐Ÿ“‹ Integration template copied" -else - echo "โš ๏ธ Integration template not found at: $TEMPLATES_DIR/integration-index.html" -fi - -# Extract metrics from integration results -echo "๐Ÿ“Š Extracting integration metrics..." - -HEALTH_LATENCY=$(jq -r '.health_check.latency_p95_ms // 0' "$INTEGRATION_RESULT") -JWT_LATENCY=$(jq -r '.jwt_validation.latency_p95_ms // 0' "$INTEGRATION_RESULT") -JWT_THROUGHPUT=$(jq -r '.jwt_validation.throughput_rps // 0' "$INTEGRATION_RESULT") -JWT_OVERHEAD=$(jq -r '.jwt_overhead_ms // 0' "$INTEGRATION_RESULT") - -echo "Integration Performance Metrics:" -echo " Health Check P95: ${HEALTH_LATENCY}ms" -echo " JWT Validation P95: ${JWT_LATENCY}ms" -echo " JWT Throughput: ${JWT_THROUGHPUT} ops/s" -echo " JWT Overhead: ${JWT_OVERHEAD}ms" - -# Validate extracted metrics -if [ "$HEALTH_LATENCY" = "null" ] || [ "$HEALTH_LATENCY" = "" ]; then HEALTH_LATENCY="0"; fi -if [ "$JWT_LATENCY" = "null" ] || [ "$JWT_LATENCY" = "" ]; then JWT_LATENCY="0"; fi -if [ "$JWT_THROUGHPUT" = "null" ] || [ "$JWT_THROUGHPUT" = "" ]; then JWT_THROUGHPUT="0"; fi -if [ "$JWT_OVERHEAD" = "null" ] || [ "$JWT_OVERHEAD" = "" ]; then JWT_OVERHEAD="0"; fi - -# Create integration performance badges with smart color coding -echo "๐Ÿ† Creating integration performance badges..." - -# JWT P95 Latency Badge -JWT_LATENCY_COLOR="red" -if (( $(echo "$JWT_LATENCY <= 50" | bc -l 2>/dev/null || echo "0") )); then - JWT_LATENCY_COLOR="green" -elif (( $(echo "$JWT_LATENCY <= 100" | bc -l 2>/dev/null || echo "0") )); then - JWT_LATENCY_COLOR="yellow" -elif (( $(echo "$JWT_LATENCY <= 200" | bc -l 2>/dev/null || echo "0") )); then - JWT_LATENCY_COLOR="orange" -fi - -echo "{\"schemaVersion\":1,\"label\":\"JWT P95 Latency\",\"message\":\"${JWT_LATENCY}ms\",\"color\":\"$JWT_LATENCY_COLOR\"}" > "$OUTPUT_DIR/badges/integration-performance-badge.json" - -# JWT Throughput Badge -JWT_THROUGHPUT_COLOR="red" -if (( $(echo "$JWT_THROUGHPUT >= 1000" | bc -l 2>/dev/null || echo "0") )); then - JWT_THROUGHPUT_COLOR="green" -elif (( $(echo "$JWT_THROUGHPUT >= 500" | bc -l 2>/dev/null || echo "0") )); then - JWT_THROUGHPUT_COLOR="yellow" -elif (( $(echo "$JWT_THROUGHPUT >= 100" | bc -l 2>/dev/null || echo "0") )); then - JWT_THROUGHPUT_COLOR="orange" -fi - -# Format throughput for display -if (( $(echo "$JWT_THROUGHPUT >= 1000" | bc -l 2>/dev/null || echo "0") )); then - JWT_THROUGHPUT_DISPLAY=$(echo "scale=1; $JWT_THROUGHPUT / 1000" | bc -l)k -else - JWT_THROUGHPUT_DISPLAY=$(printf "%.0f" "$JWT_THROUGHPUT" 2>/dev/null || echo "$JWT_THROUGHPUT") -fi - -echo "{\"schemaVersion\":1,\"label\":\"JWT Throughput\",\"message\":\"${JWT_THROUGHPUT_DISPLAY} ops/s\",\"color\":\"$JWT_THROUGHPUT_COLOR\"}" > "$OUTPUT_DIR/badges/integration-throughput-badge.json" - -# JWT Overhead Badge -JWT_OVERHEAD_COLOR="red" -if (( $(echo "$JWT_OVERHEAD <= 10" | bc -l 2>/dev/null || echo "0") )); then - JWT_OVERHEAD_COLOR="green" -elif (( $(echo "$JWT_OVERHEAD <= 25" | bc -l 2>/dev/null || echo "0") )); then - JWT_OVERHEAD_COLOR="yellow" -elif (( $(echo "$JWT_OVERHEAD <= 50" | bc -l 2>/dev/null || echo "0") )); then - JWT_OVERHEAD_COLOR="orange" -fi - -echo "{\"schemaVersion\":1,\"label\":\"JWT Overhead\",\"message\":\"${JWT_OVERHEAD}ms\",\"color\":\"$JWT_OVERHEAD_COLOR\"}" > "$OUTPUT_DIR/badges/integration-overhead-badge.json" - -echo "โœ… Integration badges created successfully" - -# Create integration processing results -INTEGRATION_RESULTS_FILE="$OUTPUT_DIR/integration-processing-results.json" -cat > "$INTEGRATION_RESULTS_FILE" << EOF -{ - "timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")", - "commit": "$COMMIT_HASH", - "integration_benchmarks": { - "health_check_latency_p95_ms": "$HEALTH_LATENCY", - "jwt_validation_latency_p95_ms": "$JWT_LATENCY", - "jwt_throughput_rps": "$JWT_THROUGHPUT", - "jwt_overhead_ms": "$JWT_OVERHEAD", - "health_results_file": "$HEALTH_RESULTS", - "jwt_results_file": "$JWT_RESULTS", - "combined_results_file": "$INTEGRATION_RESULT", - "status": "success" - } -} -EOF - -echo "โœ… Integration benchmark processing completed successfully" -echo "๐Ÿ“„ Results saved to: $INTEGRATION_RESULTS_FILE" -echo "๐Ÿ“„ Combined results available at: $INTEGRATION_RESULT" \ No newline at end of file diff --git a/benchmarking/scripts/process-integration-jmh-benchmarks.sh b/benchmarking/scripts/process-integration-jmh-benchmarks.sh deleted file mode 100755 index 65b41fd0..00000000 --- a/benchmarking/scripts/process-integration-jmh-benchmarks.sh +++ /dev/null @@ -1,170 +0,0 @@ -#!/bin/bash -# Process integration JMH benchmark results -# Usage: process-integration-jmh-benchmarks.sh - -set -e - -INTEGRATION_JMH_RESULTS="$1" -OUTPUT_DIR="$2" -COMMIT_HASH="$3" -TIMESTAMP="$4" - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -TEMPLATES_DIR="$(dirname "$SCRIPT_DIR")/doc/templates" - -echo "๐Ÿ”— Processing integration JMH benchmark results..." -echo "๐Ÿ“Š Integration JMH Results: $INTEGRATION_JMH_RESULTS" -echo "๐Ÿ“ฆ Output Directory: $OUTPUT_DIR" - -# Validate input file -if [ ! -f "$INTEGRATION_JMH_RESULTS" ]; then - echo "โŒ Error: Integration JMH results not found: $INTEGRATION_JMH_RESULTS" - exit 1 -fi - -# Ensure output directories exist -mkdir -p "$OUTPUT_DIR/data" -mkdir -p "$OUTPUT_DIR/badges" - -# Copy the integration benchmark results to the data directory -echo "๐Ÿ“‹ Copying integration benchmark results..." -cp "$INTEGRATION_JMH_RESULTS" "$OUTPUT_DIR/data/integration-benchmark-result.json" - -# Also copy as jmh-result.json for fallback compatibility -cp "$INTEGRATION_JMH_RESULTS" "$OUTPUT_DIR/data/jmh-result.json" - -# Copy the integration visualizer template -echo "๐Ÿ“„ Copying integration visualizer template..." -if [ -f "$TEMPLATES_DIR/integration-benchmark-visualizer.html" ]; then - cp "$TEMPLATES_DIR/integration-benchmark-visualizer.html" "$OUTPUT_DIR/integration-benchmark-visualizer.html" -else - echo "โš ๏ธ Warning: Integration visualizer template not found, using fallback" - # Create a minimal fallback if template doesn't exist - cat > "$OUTPUT_DIR/integration-benchmark-visualizer.html" << 'EOF' - - - - Integration Benchmark Results - - - -

Redirecting to benchmark results...

- - -EOF -fi - -# Create performance tracking data for integration benchmarks -echo "๐Ÿ“Š Creating integration performance tracking..." -TRACKING_OUTPUT=$(bash "$SCRIPT_DIR/create-performance-tracking.sh" "$OUTPUT_DIR/data/integration-benchmark-result.json" "$TEMPLATES_DIR" "$OUTPUT_DIR" "$COMMIT_HASH" 2>&1) -echo "$TRACKING_OUTPUT" - -# Extract metrics from tracking script output -PERF_SCORE=$(echo "$TRACKING_OUTPUT" | grep "PERF_SCORE=" | cut -d'=' -f2 || echo "0") -PERF_THROUGHPUT=$(echo "$TRACKING_OUTPUT" | grep "PERF_THROUGHPUT=" | cut -d'=' -f2 || echo "0") -PERF_LATENCY=$(echo "$TRACKING_OUTPUT" | grep "PERF_LATENCY=" | cut -d'=' -f2 || echo "0") -PERF_RESILIENCE=$(echo "$TRACKING_OUTPUT" | grep "PERF_RESILIENCE=" | cut -d'=' -f2 || echo "0") - -echo "๐ŸŽฏ Extracted Integration Performance Metrics:" -echo " Score: $PERF_SCORE" -echo " Throughput: $PERF_THROUGHPUT" -echo " Latency: $PERF_LATENCY" -echo " Resilience: $PERF_RESILIENCE" - -# Create integration performance badge with actual score and details -if [ -n "$PERF_SCORE" ] && [ "$PERF_SCORE" != "0" ] && [ "$PERF_SCORE" != "" ]; then - # Format latency for display (convert to ms if needed) - LATENCY_MS=$(echo "$PERF_LATENCY * 1000" | bc -l | xargs printf "%.0f") - - # Format the badge message with score, throughput, and latency (like micro benchmarks) - BADGE_MESSAGE="${PERF_SCORE} (${PERF_THROUGHPUT} ops/s, ${LATENCY_MS}ms)" - - # Determine color based on score - SCORE_VALUE=$(echo "$PERF_SCORE" | sed 's/[^0-9.]//g') - if (( $(echo "$SCORE_VALUE >= 5000" | bc -l) )); then - BADGE_COLOR="brightgreen" - elif (( $(echo "$SCORE_VALUE >= 1000" | bc -l) )); then - BADGE_COLOR="green" - elif (( $(echo "$SCORE_VALUE >= 500" | bc -l) )); then - BADGE_COLOR="yellow" - else - BADGE_COLOR="orange" - fi - echo "{\"schemaVersion\":1,\"label\":\"Integration Performance\",\"message\":\"$BADGE_MESSAGE\",\"color\":\"$BADGE_COLOR\"}" > "$OUTPUT_DIR/badges/integration-performance-badge.json" -else - echo "{\"schemaVersion\":1,\"label\":\"Integration Performance\",\"message\":\"No Data\",\"color\":\"red\"}" > "$OUTPUT_DIR/badges/integration-performance-badge.json" -fi - -# Update integration trends using unified tracking system -if [ -n "$PERF_SCORE" ] && [ "$PERF_SCORE" != "0" ] && [ "$PERF_SCORE" != "" ]; then - echo "๐Ÿ“ˆ Updating integration performance trends..." - - # Use unified tracking system for integration benchmarks - # Create a separate tracking file for integration benchmarks - INTEGRATION_TRACKING_DIR="$OUTPUT_DIR" - if bash "$SCRIPT_DIR/update-integration-performance-trends.sh" "$TEMPLATES_DIR" "$INTEGRATION_TRACKING_DIR" "$COMMIT_HASH" "$PERF_SCORE" "$PERF_THROUGHPUT" "$PERF_LATENCY" "$PERF_RESILIENCE"; then - echo "โœ… Integration performance trends updated successfully" - else - echo "โš ๏ธ Failed to update integration performance trends, using fallback..." - # Fallback to old method - TRENDS_FILE="$OUTPUT_DIR/data/integration-trends.json" - if [ -f "$TRENDS_FILE" ]; then - # Read existing trends - PREV_SCORE=$(jq -r '.latest_score // "0"' "$TRENDS_FILE" 2>/dev/null || echo "0") - PREV_SCORE_VALUE=$(echo "$PREV_SCORE" | sed 's/[^0-9.]//g') - SCORE_VALUE=$(echo "$PERF_SCORE" | sed 's/[^0-9.]//g') - - # Calculate trend - if (( $(echo "$SCORE_VALUE > $PREV_SCORE_VALUE + 1" | bc -l) )); then - TREND_SYMBOL="โ†—" - TREND_COLOR="green" - elif (( $(echo "$SCORE_VALUE < $PREV_SCORE_VALUE - 1" | bc -l) )); then - TREND_SYMBOL="โ†˜" - TREND_COLOR="orange" - else - TREND_SYMBOL="โ†’" - TREND_COLOR="blue" - fi - else - TREND_SYMBOL="โ†’" - TREND_COLOR="blue" - fi - - # Update trends file - cat > "$TRENDS_FILE" << EOF -{ - "latest_score": "$PERF_SCORE", - "latest_throughput": "$PERF_THROUGHPUT", - "latest_latency": "$PERF_LATENCY", - "latest_resilience": "$PERF_RESILIENCE", - "updated": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" -} -EOF - - # Create trend badge - TREND_MESSAGE="${TREND_SYMBOL} ${SCORE_VALUE}%" - echo "{\"schemaVersion\":1,\"label\":\"Integration Trend\",\"message\":\"$TREND_MESSAGE\",\"color\":\"$TREND_COLOR\"}" > "$OUTPUT_DIR/badges/integration-trend-badge.json" - fi -else - echo "{\"schemaVersion\":1,\"label\":\"Integration Trend\",\"message\":\"โ†’ No Data\",\"color\":\"lightgrey\"}" > "$OUTPUT_DIR/badges/integration-trend-badge.json" -fi - -# Create metadata file -cat > "$OUTPUT_DIR/data/integration-metadata.json" << EOF -{ - "timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")", - "commit": "$COMMIT_HASH", - "generated_at": "$TIMESTAMP", - "metrics": { - "performance_score": "$PERF_SCORE", - "throughput": "$PERF_THROUGHPUT", - "latency": "$PERF_LATENCY", - "resilience": "$PERF_RESILIENCE" - } -} -EOF - -echo "โœ… Integration JMH benchmarks processed successfully" -echo " - Performance Score: $PERF_SCORE" -echo " - Throughput: $PERF_THROUGHPUT" -echo " - Latency: $PERF_LATENCY" \ No newline at end of file diff --git a/benchmarking/scripts/process-micro-benchmarks.sh b/benchmarking/scripts/process-micro-benchmarks.sh deleted file mode 100755 index 3de6edd7..00000000 --- a/benchmarking/scripts/process-micro-benchmarks.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/bin/bash -# Process micro benchmark results and create badges/tracking -# Usage: process-micro-benchmarks.sh - -set -e - -JMH_RESULT_FILE="$1" -TEMPLATES_DIR="$2" -OUTPUT_DIR="$3" -COMMIT_HASH="$4" -TIMESTAMP="$5" -TIMESTAMP_WITH_TIME="$6" - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -echo "๐Ÿ”ฌ Processing micro benchmark results..." -echo "๐Ÿ“„ JMH Result File: $JMH_RESULT_FILE" - -if [ ! -f "$JMH_RESULT_FILE" ]; then - echo "โŒ Error: JMH result file not found: $JMH_RESULT_FILE" - exit 1 -fi - -# Ensure output directories exist -mkdir -p "$OUTPUT_DIR/badges" - -# Copy JMH result to output directory for visualization -cp "$JMH_RESULT_FILE" "$OUTPUT_DIR/data/jmh-result.json" - -# Load utility libraries and detect benchmark type -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -source "$SCRIPT_DIR/lib/metrics-utils.sh" - -echo "๐Ÿ” Detecting benchmark type..." -BENCHMARK_TYPE=$(detect_benchmark_type "$JMH_RESULT_FILE") - -if [ "$BENCHMARK_TYPE" = "integration" ]; then - echo " โœ“ Detected integration benchmarks" -else - echo " โœ“ Detected micro benchmarks" -fi - -# Create performance badge using unified script (includes simple badges) -echo "๐Ÿ† Creating performance badge for $BENCHMARK_TYPE benchmarks..." -if ! bash "$SCRIPT_DIR/create-unified-performance-badge.sh" "$BENCHMARK_TYPE" "$OUTPUT_DIR/data/jmh-result.json" "$OUTPUT_DIR/badges" "$COMMIT_HASH" "$TIMESTAMP" "$TIMESTAMP_WITH_TIME"; then - echo "โš ๏ธ Failed to create performance badge, creating fallback..." - echo "{\"schemaVersion\":1,\"label\":\"Performance Score\",\"message\":\"Processing Error\",\"color\":\"red\"}" > "$OUTPUT_DIR/badges/performance-badge.json" -fi - -# Performance Tracking System -echo "๐Ÿ“ˆ Setting up performance tracking..." - -# Create performance tracking data -TRACKING_OUTPUT=$(bash "$SCRIPT_DIR/create-performance-tracking.sh" "$OUTPUT_DIR/data/jmh-result.json" "$TEMPLATES_DIR" "$OUTPUT_DIR" "$COMMIT_HASH" 2>&1) -echo "$TRACKING_OUTPUT" - -# Extract metrics from tracking script output -PERF_SCORE=$(echo "$TRACKING_OUTPUT" | grep "PERF_SCORE=" | cut -d'=' -f2 || echo "0") -PERF_THROUGHPUT=$(echo "$TRACKING_OUTPUT" | grep "PERF_THROUGHPUT=" | cut -d'=' -f2 || echo "0") -PERF_LATENCY=$(echo "$TRACKING_OUTPUT" | grep "PERF_LATENCY=" | cut -d'=' -f2 || echo "0") -PERF_RESILIENCE=$(echo "$TRACKING_OUTPUT" | grep "PERF_RESILIENCE=" | cut -d'=' -f2 || echo "0") - -echo "๐Ÿ“Š Extracted Performance Metrics:" -echo " Score: $PERF_SCORE" -echo " Throughput: $PERF_THROUGHPUT" -echo " Latency: $PERF_LATENCY" -echo " Resilience: $PERF_RESILIENCE" - -# Update consolidated tracking and create trend badge -if [ -n "$PERF_SCORE" ] && [ "$PERF_SCORE" != "0" ] && [ "$PERF_SCORE" != "" ]; then - echo "๐Ÿ“ˆ Updating performance trends..." - if bash "$SCRIPT_DIR/update-performance-trends.sh" "$TEMPLATES_DIR" "$OUTPUT_DIR" "$COMMIT_HASH" "$PERF_SCORE" "$PERF_THROUGHPUT" "$PERF_LATENCY" "$PERF_RESILIENCE"; then - echo "โœ… Performance trends updated successfully" - else - echo "โš ๏ธ Failed to update performance trends, creating fallback badge..." - echo "{\"schemaVersion\":1,\"label\":\"Performance Trend\",\"message\":\"โ†’ No Data\",\"color\":\"lightgrey\"}" > "$OUTPUT_DIR/badges/trend-badge.json" - fi -else - echo "โš ๏ธ Could not extract valid performance metrics for trend tracking" - echo "{\"schemaVersion\":1,\"label\":\"Performance Trend\",\"message\":\"โ†’ No Data\",\"color\":\"lightgrey\"}" > "$OUTPUT_DIR/badges/trend-badge.json" -fi - -# Create micro benchmark processing results -MICRO_RESULTS_FILE="$OUTPUT_DIR/micro-processing-results.json" -cat > "$MICRO_RESULTS_FILE" << EOF -{ - "timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")", - "commit": "$COMMIT_HASH", - "micro_benchmarks": { - "performance_score": "$PERF_SCORE", - "throughput": "$PERF_THROUGHPUT", - "latency": "$PERF_LATENCY", - "resilience": "$PERF_RESILIENCE", - "jmh_result_file": "$JMH_RESULT_FILE", - "status": "success" - } -} -EOF - -echo "โœ… Micro benchmark processing completed successfully" -echo "๐Ÿ“„ Results saved to: $MICRO_RESULTS_FILE" \ No newline at end of file diff --git a/benchmarking/scripts/serve-local.js b/benchmarking/scripts/serve-local.js deleted file mode 100755 index c31e3597..00000000 --- a/benchmarking/scripts/serve-local.js +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env node -/** - * Simple HTTP server for local testing of benchmark templates - * Usage: node serve-local.js [port] - */ - -const http = require('http'); -const fs = require('fs'); -const path = require('path'); -const url = require('url'); - -const PORT = process.argv[2] || 8080; -const TEMPLATES_DIR = path.join(__dirname, '..', 'doc', 'templates'); - -// MIME types -const MIME_TYPES = { - '.html': 'text/html', - '.js': 'application/javascript', - '.json': 'application/json', - '.css': 'text/css', - '.png': 'image/png', - '.jpg': 'image/jpeg', - '.gif': 'image/gif', - '.svg': 'image/svg+xml', - '.ico': 'image/x-icon' -}; - -// Create server -const server = http.createServer((req, res) => { - let pathname = url.parse(req.url).pathname; - - // Default to index-visualizer.html - if (pathname === '/') { - pathname = '/index-visualizer.html'; - } - - const filePath = path.join(TEMPLATES_DIR, pathname); - const ext = path.extname(filePath); - - // Check if file exists - fs.stat(filePath, (err, stats) => { - if (err || !stats.isFile()) { - res.writeHead(404, { 'Content-Type': 'text/plain' }); - res.end('404 Not Found'); - return; - } - - // Serve the file - const mimeType = MIME_TYPES[ext] || 'application/octet-stream'; - res.writeHead(200, { - 'Content-Type': mimeType, - 'Access-Control-Allow-Origin': '*' // Allow CORS for local testing - }); - - fs.createReadStream(filePath).pipe(res); - }); -}); - -// Start server -server.listen(PORT, () => { - console.log('๐Ÿš€ Starting local HTTP server for benchmark templates'); - console.log(`๐Ÿ“ Serving from: ${TEMPLATES_DIR}`); - console.log(`๐ŸŒ URL: http://localhost:${PORT}`); - console.log(''); - console.log('Available pages:'); - console.log(` - http://localhost:${PORT}/index-visualizer.html (Micro Benchmarks)`); - console.log(` - http://localhost:${PORT}/integration-index.html (Integration Tests)`); - console.log(` - http://localhost:${PORT}/step-metrics-visualizer.html (Step Metrics)`); - console.log(` - http://localhost:${PORT}/performance-trends.html (Performance Trends)`); - console.log(''); - console.log('Press Ctrl+C to stop the server'); -}); \ No newline at end of file diff --git a/benchmarking/scripts/serve-local.sh b/benchmarking/scripts/serve-local.sh deleted file mode 100755 index 4a1f4cb5..00000000 --- a/benchmarking/scripts/serve-local.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# Simple HTTP server for local testing of benchmark templates -# Usage: ./serve-local.sh [port] - -PORT="${1:-8080}" -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -TEMPLATES_DIR="$(cd "$SCRIPT_DIR/../doc/templates" && pwd)" - -echo "๐Ÿš€ Starting local HTTP server for benchmark templates" -echo "๐Ÿ“ Serving from: $TEMPLATES_DIR" -echo "๐ŸŒ URL: http://localhost:$PORT" -echo "" -echo "Available pages:" -echo " - http://localhost:$PORT/index-visualizer.html (Micro Benchmarks)" -echo " - http://localhost:$PORT/integration-index.html (Integration Tests)" -echo " - http://localhost:$PORT/step-metrics-visualizer.html (Step Metrics)" -echo " - http://localhost:$PORT/performance-trends.html (Performance Trends)" -echo "" -echo "Press Ctrl+C to stop the server" -echo "" - -# Change to templates directory -cd "$TEMPLATES_DIR" - -# Try Python 3 first, then Python 2 -if command -v python3 &> /dev/null; then - python3 -m http.server "$PORT" -elif command -v python &> /dev/null; then - python -m SimpleHTTPServer "$PORT" -else - echo "โŒ Error: Python is not installed. Please install Python to use this script." - echo "Alternative: Use any other HTTP server like 'npx http-server' or 'php -S localhost:$PORT'" - exit 1 -fi \ No newline at end of file diff --git a/benchmarking/scripts/update-integration-performance-trends.sh b/benchmarking/scripts/update-integration-performance-trends.sh deleted file mode 100755 index a865203e..00000000 --- a/benchmarking/scripts/update-integration-performance-trends.sh +++ /dev/null @@ -1,128 +0,0 @@ -#!/bin/bash -# Update consolidated integration performance tracking and calculate trends -# Usage: update-integration-performance-trends.sh - -set -e - -TEMPLATES_DIR="$1" -OUTPUT_DIR="$2" -COMMIT_HASH="$3" -CURRENT_SCORE="$4" -CURRENT_THROUGHPUT="$5" -CURRENT_LATENCY="$6" -CURRENT_RESILIENCE="$7" - -echo "Updating integration performance tracking..." - -# Ensure output directories exist -mkdir -p "$OUTPUT_DIR/data" -mkdir -p "$OUTPUT_DIR/badges" - -# Use separate tracking file for integration benchmarks -TRACKING_FILE="$OUTPUT_DIR/data/integration-performance-tracking.json" -# Try to download from the deployed GitHub Pages URL -GITHUB_PAGES_URL="https://cuioss.github.io/cui-jwt/benchmarks/data/integration-performance-tracking.json" - -# Check if we already have a local tracking file with data -if [ -f "$TRACKING_FILE" ]; then - LOCAL_RUN_COUNT=$(jq '.runs | length' "$TRACKING_FILE" 2>/dev/null || echo "0") - if [ "$LOCAL_RUN_COUNT" -gt "0" ]; then - echo "Using existing local tracking file with $LOCAL_RUN_COUNT runs" - else - # Local file exists but is empty, try to download from GitHub Pages - echo "Local tracking file is empty, checking GitHub Pages..." - TEMP_FILE=$(mktemp) - if curl -f -s "$GITHUB_PAGES_URL" -o "$TEMP_FILE" 2>/dev/null; then - REMOTE_RUN_COUNT=$(jq '.runs | length' "$TEMP_FILE" 2>/dev/null || echo "0") - if [ "$REMOTE_RUN_COUNT" -gt "0" ]; then - echo "Downloaded existing performance tracking file with $REMOTE_RUN_COUNT runs" - mv "$TEMP_FILE" "$TRACKING_FILE" - else - echo "Remote tracking file is also empty, starting fresh" - rm -f "$TEMP_FILE" - echo '{"runs":[]}' > "$TRACKING_FILE" - fi - else - echo "No remote tracking file found, starting fresh" - rm -f "$TEMP_FILE" - echo '{"runs":[]}' > "$TRACKING_FILE" - fi - fi -else - # No local file, try to download from GitHub Pages - echo "No local tracking file, downloading from GitHub Pages..." - if curl -f -s "$GITHUB_PAGES_URL" -o "$TRACKING_FILE" 2>/dev/null; then - REMOTE_RUN_COUNT=$(jq '.runs | length' "$TRACKING_FILE" 2>/dev/null || echo "0") - echo "Downloaded existing performance tracking file with $REMOTE_RUN_COUNT runs" - if [ "$REMOTE_RUN_COUNT" -eq "0" ]; then - echo "Warning: Remote tracking file is empty" - fi - else - echo "No remote tracking file found, starting fresh" - echo '{"runs":[]}' > "$TRACKING_FILE" - fi -fi - -# Convert formatted values to raw numbers -# Remove 'k' suffix and multiply by 1000 if present -if [[ "$CURRENT_THROUGHPUT" == *"k" ]]; then - CURRENT_THROUGHPUT=$(echo "$CURRENT_THROUGHPUT" | sed 's/k$//' | awk '{print $1 * 1000}') -fi -if [[ "$CURRENT_RESILIENCE" == *"k" ]]; then - CURRENT_RESILIENCE=$(echo "$CURRENT_RESILIENCE" | sed 's/k$//' | awk '{print $1 * 1000}') -fi - -# Ensure values are numeric (default to 0 if not) -CURRENT_SCORE=$(echo "$CURRENT_SCORE" | grep -o '[0-9.]*' | head -1 || echo "0") -CURRENT_THROUGHPUT=$(echo "$CURRENT_THROUGHPUT" | grep -o '[0-9.]*' | head -1 || echo "0") -CURRENT_LATENCY=$(echo "$CURRENT_LATENCY" | grep -o '[0-9.]*' | head -1 || echo "0") -CURRENT_RESILIENCE=$(echo "$CURRENT_RESILIENCE" | grep -o '[0-9.]*' | head -1 || echo "0") - -# Add current run to tracking data -CURRENT_RUN=$(cat </dev/null; then - # Add new run and keep only last 10 runs - jq --argjson newrun "$CURRENT_RUN" '.runs += [$newrun] | .runs = (.runs | sort_by(.timestamp) | .[-10:])' "$TRACKING_FILE" > "$TRACKING_FILE.tmp" && mv "$TRACKING_FILE.tmp" "$TRACKING_FILE" -else - echo "Warning: Invalid JSON in tracking file, resetting to default structure." - echo '{"runs":[]}' > "$TRACKING_FILE" - jq --argjson newrun "$CURRENT_RUN" '.runs += [$newrun] | .runs = (.runs | sort_by(.timestamp) | .[-10:])' "$TRACKING_FILE" > "$TRACKING_FILE.tmp" && mv "$TRACKING_FILE.tmp" "$TRACKING_FILE" -fi - -# Log the final state for debugging -FINAL_RUN_COUNT=$(jq '.runs | length' "$TRACKING_FILE" 2>/dev/null || echo "0") -echo "Performance tracking file now contains $FINAL_RUN_COUNT runs" - -# Verify the file is actually written and contains data -if [ -f "$TRACKING_FILE" ]; then - echo "โœ… Performance tracking file exists at: $TRACKING_FILE" - FILE_SIZE=$(wc -c < "$TRACKING_FILE") - echo " File size: $FILE_SIZE bytes" - if [ "$FINAL_RUN_COUNT" -eq "0" ]; then - echo "โš ๏ธ WARNING: Tracking file exists but contains no runs!" - fi -else - echo "โŒ ERROR: Performance tracking file was not created!" -fi - -# Calculate trends and create trend badge using unified script for integration -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -bash "$SCRIPT_DIR/create-unified-trend-badge.sh" integration "$TRACKING_FILE" "$OUTPUT_DIR/badges" - -# Integration performance trends template is copied by prepare-github-pages.sh script - -echo "Integration performance tracking updated successfully" diff --git a/benchmarking/scripts/update-performance-trends.sh b/benchmarking/scripts/update-performance-trends.sh deleted file mode 100755 index c7b77af0..00000000 --- a/benchmarking/scripts/update-performance-trends.sh +++ /dev/null @@ -1,128 +0,0 @@ -#!/bin/bash -# Update consolidated performance tracking and calculate trends -# Usage: update-performance-trends.sh - -set -e - -TEMPLATES_DIR="$1" -OUTPUT_DIR="$2" -COMMIT_HASH="$3" -CURRENT_SCORE="$4" -CURRENT_THROUGHPUT="$5" -CURRENT_LATENCY="$6" -CURRENT_RESILIENCE="$7" - -echo "Updating consolidated performance tracking..." - -# Ensure output directories exist -mkdir -p "$OUTPUT_DIR/data" -mkdir -p "$OUTPUT_DIR/badges" - -# Download existing tracking file if it exists -TRACKING_FILE="$OUTPUT_DIR/data/performance-tracking.json" -# Try to download from the deployed GitHub Pages URL -GITHUB_PAGES_URL="https://cuioss.github.io/cui-jwt/benchmarks/data/performance-tracking.json" - -# Check if we already have a local tracking file with data -if [ -f "$TRACKING_FILE" ]; then - LOCAL_RUN_COUNT=$(jq '.runs | length' "$TRACKING_FILE" 2>/dev/null || echo "0") - if [ "$LOCAL_RUN_COUNT" -gt "0" ]; then - echo "Using existing local tracking file with $LOCAL_RUN_COUNT runs" - else - # Local file exists but is empty, try to download from GitHub Pages - echo "Local tracking file is empty, checking GitHub Pages..." - TEMP_FILE=$(mktemp) - if curl -f -s "$GITHUB_PAGES_URL" -o "$TEMP_FILE" 2>/dev/null; then - REMOTE_RUN_COUNT=$(jq '.runs | length' "$TEMP_FILE" 2>/dev/null || echo "0") - if [ "$REMOTE_RUN_COUNT" -gt "0" ]; then - echo "Downloaded existing performance tracking file with $REMOTE_RUN_COUNT runs" - mv "$TEMP_FILE" "$TRACKING_FILE" - else - echo "Remote tracking file is also empty, starting fresh" - rm -f "$TEMP_FILE" - echo '{"runs":[]}' > "$TRACKING_FILE" - fi - else - echo "No remote tracking file found, starting fresh" - rm -f "$TEMP_FILE" - echo '{"runs":[]}' > "$TRACKING_FILE" - fi - fi -else - # No local file, try to download from GitHub Pages - echo "No local tracking file, downloading from GitHub Pages..." - if curl -f -s "$GITHUB_PAGES_URL" -o "$TRACKING_FILE" 2>/dev/null; then - REMOTE_RUN_COUNT=$(jq '.runs | length' "$TRACKING_FILE" 2>/dev/null || echo "0") - echo "Downloaded existing performance tracking file with $REMOTE_RUN_COUNT runs" - if [ "$REMOTE_RUN_COUNT" -eq "0" ]; then - echo "Warning: Remote tracking file is empty" - fi - else - echo "No remote tracking file found, starting fresh" - echo '{"runs":[]}' > "$TRACKING_FILE" - fi -fi - -# Convert formatted values to raw numbers -# Remove 'k' suffix and multiply by 1000 if present -if [[ "$CURRENT_THROUGHPUT" == *"k" ]]; then - CURRENT_THROUGHPUT=$(echo "$CURRENT_THROUGHPUT" | sed 's/k$//' | awk '{print $1 * 1000}') -fi -if [[ "$CURRENT_RESILIENCE" == *"k" ]]; then - CURRENT_RESILIENCE=$(echo "$CURRENT_RESILIENCE" | sed 's/k$//' | awk '{print $1 * 1000}') -fi - -# Ensure values are numeric (default to 0 if not) -CURRENT_SCORE=$(echo "$CURRENT_SCORE" | grep -o '[0-9.]*' | head -1 || echo "0") -CURRENT_THROUGHPUT=$(echo "$CURRENT_THROUGHPUT" | grep -o '[0-9.]*' | head -1 || echo "0") -CURRENT_LATENCY=$(echo "$CURRENT_LATENCY" | grep -o '[0-9.]*' | head -1 || echo "0") -CURRENT_RESILIENCE=$(echo "$CURRENT_RESILIENCE" | grep -o '[0-9.]*' | head -1 || echo "0") - -# Add current run to tracking data -CURRENT_RUN=$(cat </dev/null; then - # Add new run and keep only last 10 runs - jq --argjson newrun "$CURRENT_RUN" '.runs += [$newrun] | .runs = (.runs | sort_by(.timestamp) | .[-10:])' "$TRACKING_FILE" > "$TRACKING_FILE.tmp" && mv "$TRACKING_FILE.tmp" "$TRACKING_FILE" -else - echo "Warning: Invalid JSON in tracking file, resetting to default structure." - echo '{"runs":[]}' > "$TRACKING_FILE" - jq --argjson newrun "$CURRENT_RUN" '.runs += [$newrun] | .runs = (.runs | sort_by(.timestamp) | .[-10:])' "$TRACKING_FILE" > "$TRACKING_FILE.tmp" && mv "$TRACKING_FILE.tmp" "$TRACKING_FILE" -fi - -# Log the final state for debugging -FINAL_RUN_COUNT=$(jq '.runs | length' "$TRACKING_FILE" 2>/dev/null || echo "0") -echo "Performance tracking file now contains $FINAL_RUN_COUNT runs" - -# Verify the file is actually written and contains data -if [ -f "$TRACKING_FILE" ]; then - echo "โœ… Performance tracking file exists at: $TRACKING_FILE" - FILE_SIZE=$(wc -c < "$TRACKING_FILE") - echo " File size: $FILE_SIZE bytes" - if [ "$FINAL_RUN_COUNT" -eq "0" ]; then - echo "โš ๏ธ WARNING: Tracking file exists but contains no runs!" - fi -else - echo "โŒ ERROR: Performance tracking file was not created!" -fi - -# Calculate trends and create trend badge using unified script -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -bash "$SCRIPT_DIR/create-unified-trend-badge.sh" micro "$TRACKING_FILE" "$OUTPUT_DIR/badges" - -# Performance trends template is copied by prepare-github-pages.sh script - -echo "Performance tracking updated successfully" diff --git a/benchmarking/templates/data/badges/performance-badge.json b/benchmarking/templates/data/badges/performance-badge.json deleted file mode 100644 index 07d72db2..00000000 --- a/benchmarking/templates/data/badges/performance-badge.json +++ /dev/null @@ -1 +0,0 @@ -{"schemaVersion":1,"label":"Performance Score","message":"52580 (85.5k ops/s, 1ms)","color":"brightgreen"} \ No newline at end of file diff --git a/benchmarking/templates/data/badges/trend-badge.json b/benchmarking/templates/data/badges/trend-badge.json deleted file mode 100644 index cf460fd1..00000000 --- a/benchmarking/templates/data/badges/trend-badge.json +++ /dev/null @@ -1 +0,0 @@ -{"schemaVersion":1,"label":"Performance Trend","message":"โ†— 8.4% (improving)","color":"brightgreen"} \ No newline at end of file diff --git a/benchmarking/templates/data/integration-metrics.json b/benchmarking/templates/data/integration-metrics.json deleted file mode 100644 index ec61c1d5..00000000 --- a/benchmarking/templates/data/integration-metrics.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "timestamp": "2025-08-01T14:30:45.123456Z", - "commit": "abc123456789abcdef123456789abcdef12345678", - "integration": { - "totalBenchmarks": 4, - "throughputBenchmarks": 2, - "avgThroughput": 5894.267573456785, - "avgLatency": 0.010649616666666111, - "performanceScore": 89.45, - "customData": { - "containerType": "native-quarkus", - "keycloakVersion": "26.0.4", - "testEnvironment": "containerized", - "jwtOverheadMs": 9.565, - "healthCheckP95Ms": 6.52, - "jwtValidationP95Ms": 17.40 - } - } -} \ No newline at end of file diff --git a/benchmarking/templates/data/integration-result.json b/benchmarking/templates/data/integration-result.json deleted file mode 100644 index 211096c6..00000000 --- a/benchmarking/templates/data/integration-result.json +++ /dev/null @@ -1,222 +0,0 @@ -[ - { - "jmhVersion": "1.37", - "benchmark": "de.cuioss.jwt.quarkus.benchmark.integration.HealthCheckBenchmark.measureHealthCheck", - "mode": "thrpt", - "threads": 50, - "forks": 1, - "jvm": "/opt/java/openjdk/bin/java", - "jvmArgs": [ - "-Dquarkus.http.port=8080", - "-Djava.util.logging.manager=org.jboss.logmanager.LogManager" - ], - "jdkVersion": "21.0.7", - "vmName": "OpenJDK 64-Bit Server VM", - "vmVersion": "21.0.7+6-LTS", - "warmupIterations": 2, - "warmupTime": "3 s", - "warmupBatchSize": 1, - "measurementIterations": 3, - "measurementTime": "5 s", - "measurementBatchSize": 1, - "primaryMetric": { - "score": 8542.856234567891, - "scoreError": 234.567891234567, - "scoreConfidence": [ - 8308.288343333324, - 8777.424125802458 - ], - "scorePercentiles": { - "0.0": 8234.567891234567, - "50.0": 8542.856234567891, - "90.0": 8851.144577901235, - "95.0": 8928.289749567902, - "99.0": 9005.434921234569, - "99.9": 9043.007507067902, - "99.99": 9050.580092901235, - "99.999": 9053.366385817902, - "99.9999": 9054.759532276235, - "100.0": 9056.152678734568 - }, - "scoreUnit": "ops/s", - "rawData": [ - [ - 8234.567891234567, - 8542.856234567891, - 8851.144577901235 - ] - ] - }, - "secondaryMetrics": {}, - "customData": { - "containerType": "native-quarkus", - "testType": "health-check", - "description": "Health check endpoint performance baseline" - } - }, - { - "jmhVersion": "1.37", - "benchmark": "de.cuioss.jwt.quarkus.benchmark.integration.JwtValidationBenchmark.measureJwtValidation", - "mode": "thrpt", - "threads": 50, - "forks": 1, - "jvm": "/opt/java/openjdk/bin/java", - "jvmArgs": [ - "-Dquarkus.http.port=8080", - "-Djava.util.logging.manager=org.jboss.logmanager.LogManager" - ], - "jdkVersion": "21.0.7", - "vmName": "OpenJDK 64-Bit Server VM", - "vmVersion": "21.0.7+6-LTS", - "warmupIterations": 2, - "warmupTime": "3 s", - "warmupBatchSize": 1, - "measurementIterations": 3, - "measurementTime": "5 s", - "measurementBatchSize": 1, - "primaryMetric": { - "score": 3245.678912345678, - "scoreError": 123.456789123456, - "scoreConfidence": [ - 3122.222123222222, - 3369.135701469134 - ], - "scorePercentiles": { - "0.0": 3098.765432109876, - "50.0": 3245.678912345678, - "90.0": 3392.59239258148, - "95.0": 3443.827160493827, - "99.0": 3495.061928406174, - "99.9": 3520.679312362347, - "99.99": 3533.48800934032, - "99.999": 3540.391851829307, - "99.9999": 3543.844273073801, - "100.0": 3547.296694318294 - }, - "scoreUnit": "ops/s", - "rawData": [ - [ - 3098.765432109876, - 3245.678912345678, - 3392.59239258148 - ] - ] - }, - "secondaryMetrics": {}, - "customData": { - "containerType": "native-quarkus", - "testType": "jwt-validation", - "description": "JWT validation with Keycloak integration" - } - }, - { - "jmhVersion": "1.37", - "benchmark": "de.cuioss.jwt.quarkus.benchmark.integration.HealthCheckBenchmark.measureHealthCheckLatency", - "mode": "avgt", - "threads": 50, - "forks": 1, - "jvm": "/opt/java/openjdk/bin/java", - "jvmArgs": [ - "-Dquarkus.http.port=8080", - "-Djava.util.logging.manager=org.jboss.logmanager.LogManager" - ], - "jdkVersion": "21.0.7", - "vmName": "OpenJDK 64-Bit Server VM", - "vmVersion": "21.0.7+6-LTS", - "warmupIterations": 2, - "warmupTime": "3 s", - "warmupBatchSize": 1, - "measurementIterations": 3, - "measurementTime": "5 s", - "measurementBatchSize": 1, - "primaryMetric": { - "score": 5.867123456789012, - "scoreError": 0.234567891234567, - "scoreConfidence": [ - 5.632555565554445, - 6.101691347923579 - ], - "scorePercentiles": { - "0.0": 5.432109876543209, - "50.0": 5.867123456789012, - "90.0": 6.302137037034815, - "95.0": 6.519643827157716, - "99.0": 6.737150617280618, - "99.9": 6.845904012342069, - "99.99": 6.90028071987227, - "99.999": 6.927469574307375, - "99.9999": 6.941064001524928, - "100.0": 6.95465842874248 - }, - "scoreUnit": "ms/op", - "rawData": [ - [ - 5.432109876543209, - 5.867123456789012, - 6.302137037034815 - ] - ] - }, - "secondaryMetrics": {}, - "customData": { - "containerType": "native-quarkus", - "testType": "health-check-latency", - "description": "Health check endpoint latency baseline" - } - }, - { - "jmhVersion": "1.37", - "benchmark": "de.cuioss.jwt.quarkus.benchmark.integration.JwtValidationBenchmark.measureJwtValidationLatency", - "mode": "avgt", - "threads": 50, - "forks": 1, - "jvm": "/opt/java/openjdk/bin/java", - "jvmArgs": [ - "-Dquarkus.http.port=8080", - "-Djava.util.logging.manager=org.jboss.logmanager.LogManager" - ], - "jdkVersion": "21.0.7", - "vmName": "OpenJDK 64-Bit Server VM", - "vmVersion": "21.0.7+6-LTS", - "warmupIterations": 2, - "warmupTime": "3 s", - "warmupBatchSize": 1, - "measurementIterations": 3, - "measurementTime": "5 s", - "measurementBatchSize": 1, - "primaryMetric": { - "score": 15.432109876543209, - "scoreError": 0.876543210987654, - "scoreConfidence": [ - 14.555566665555555, - 16.308653087530863 - ], - "scorePercentiles": { - "0.0": 14.123456789012345, - "50.0": 15.432109876543209, - "90.0": 16.740762964074073, - "95.0": 17.39508950783395, - "99.0": 18.049416051593827, - "99.9": 18.376579323473765, - "99.99": 18.540160959413734, - "99.999": 18.621951777383719, - "99.9999": 18.662847186368711, - "100.0": 18.703742595353703 - }, - "scoreUnit": "ms/op", - "rawData": [ - [ - 14.123456789012345, - 15.432109876543209, - 16.740762964074073 - ] - ] - }, - "secondaryMetrics": {}, - "customData": { - "containerType": "native-quarkus", - "testType": "jwt-validation-latency", - "description": "JWT validation latency with Keycloak integration" - } - } -] \ No newline at end of file diff --git a/benchmarking/templates/data/jmh-result.json b/benchmarking/templates/data/jmh-result.json deleted file mode 100644 index 551607cf..00000000 --- a/benchmarking/templates/data/jmh-result.json +++ /dev/null @@ -1,259 +0,0 @@ -[ - { - "jmhVersion" : "1.37", - "benchmark" : "de.cuioss.jwt.validation.benchmark.standard.SimpleCoreValidationBenchmark.measureThroughput", - "mode" : "thrpt", - "threads" : 100, - "forks" : 1, - "jvm" : "/Users/oliver/.sdkman/candidates/java/21.0.7-tem/bin/java", - "jvmArgs" : [ - "-Djava.util.logging.config.file=src/main/resources/benchmark-logging.properties", - "-Dbenchmark.results.dir=target/benchmark-results" - ], - "jdkVersion" : "21.0.7", - "vmName" : "OpenJDK 64-Bit Server VM", - "vmVersion" : "21.0.7+6-LTS", - "warmupIterations" : 1, - "warmupTime" : "1 s", - "warmupBatchSize" : 1, - "measurementIterations" : 3, - "measurementTime" : "4 s", - "measurementBatchSize" : 1, - "primaryMetric" : { - "score" : 85537.63015022938, - "scoreError" : 254883.1704119681, - "scoreConfidence" : [ - -169345.54026173873, - 340420.80056219746 - ], - "scorePercentiles" : { - "0.0" : 73495.27601820591, - "50.0" : 82262.2860058696, - "90.0" : 100855.32842661263, - "95.0" : 100855.32842661263, - "99.0" : 100855.32842661263, - "99.9" : 100855.32842661263, - "99.99" : 100855.32842661263, - "99.999" : 100855.32842661263, - "99.9999" : 100855.32842661263, - "100.0" : 100855.32842661263 - }, - "scoreUnit" : "ops/s", - "rawData" : [ - [ - 73495.27601820591, - 82262.2860058696, - 100855.32842661263 - ] - ] - }, - "secondaryMetrics" : { - } - }, - { - "jmhVersion" : "1.37", - "benchmark" : "de.cuioss.jwt.validation.benchmark.standard.SimpleErrorLoadBenchmark.validateMixedTokens0", - "mode" : "thrpt", - "threads" : 100, - "forks" : 1, - "jvm" : "/Users/oliver/.sdkman/candidates/java/21.0.7-tem/bin/java", - "jvmArgs" : [ - "-Djava.util.logging.config.file=src/main/resources/benchmark-logging.properties", - "-Dbenchmark.results.dir=target/benchmark-results" - ], - "jdkVersion" : "21.0.7", - "vmName" : "OpenJDK 64-Bit Server VM", - "vmVersion" : "21.0.7+6-LTS", - "warmupIterations" : 1, - "warmupTime" : "1 s", - "warmupBatchSize" : 1, - "measurementIterations" : 3, - "measurementTime" : "4 s", - "measurementBatchSize" : 1, - "primaryMetric" : { - "score" : 113075.3659554129, - "scoreError" : 429927.7193989502, - "scoreConfidence" : [ - -316852.3534435373, - 543003.0853543631 - ], - "scorePercentiles" : { - "0.0" : 87679.28318017177, - "50.0" : 117310.21366746748, - "90.0" : 134236.6010185994, - "95.0" : 134236.6010185994, - "99.0" : 134236.6010185994, - "99.9" : 134236.6010185994, - "99.99" : 134236.6010185994, - "99.999" : 134236.6010185994, - "99.9999" : 134236.6010185994, - "100.0" : 134236.6010185994 - }, - "scoreUnit" : "ops/s", - "rawData" : [ - [ - 87679.28318017177, - 117310.21366746748, - 134236.6010185994 - ] - ] - }, - "secondaryMetrics" : { - } - }, - { - "jmhVersion" : "1.37", - "benchmark" : "de.cuioss.jwt.validation.benchmark.standard.SimpleErrorLoadBenchmark.validateMixedTokens50", - "mode" : "thrpt", - "threads" : 100, - "forks" : 1, - "jvm" : "/Users/oliver/.sdkman/candidates/java/21.0.7-tem/bin/java", - "jvmArgs" : [ - "-Djava.util.logging.config.file=src/main/resources/benchmark-logging.properties", - "-Dbenchmark.results.dir=target/benchmark-results" - ], - "jdkVersion" : "21.0.7", - "vmName" : "OpenJDK 64-Bit Server VM", - "vmVersion" : "21.0.7+6-LTS", - "warmupIterations" : 1, - "warmupTime" : "1 s", - "warmupBatchSize" : 1, - "measurementIterations" : 3, - "measurementTime" : "4 s", - "measurementBatchSize" : 1, - "primaryMetric" : { - "score" : 181228.33898605246, - "scoreError" : 572634.4137451742, - "scoreConfidence" : [ - -391406.07475912175, - 753862.7527312266 - ], - "scorePercentiles" : { - "0.0" : 147500.59042516802, - "50.0" : 186601.46421029576, - "90.0" : 209582.96232269364, - "95.0" : 209582.96232269364, - "99.0" : 209582.96232269364, - "99.9" : 209582.96232269364, - "99.99" : 209582.96232269364, - "99.999" : 209582.96232269364, - "99.9999" : 209582.96232269364, - "100.0" : 209582.96232269364 - }, - "scoreUnit" : "ops/s", - "rawData" : [ - [ - 147500.59042516802, - 186601.46421029576, - 209582.96232269364 - ] - ] - }, - "secondaryMetrics" : { - } - }, - { - "jmhVersion" : "1.37", - "benchmark" : "de.cuioss.jwt.validation.benchmark.standard.SimpleCoreValidationBenchmark.measureAverageTime", - "mode" : "avgt", - "threads" : 100, - "forks" : 1, - "jvm" : "/Users/oliver/.sdkman/candidates/java/21.0.7-tem/bin/java", - "jvmArgs" : [ - "-Djava.util.logging.config.file=src/main/resources/benchmark-logging.properties", - "-Dbenchmark.results.dir=target/benchmark-results" - ], - "jdkVersion" : "21.0.7", - "vmName" : "OpenJDK 64-Bit Server VM", - "vmVersion" : "21.0.7+6-LTS", - "warmupIterations" : 1, - "warmupTime" : "1 s", - "warmupBatchSize" : 1, - "measurementIterations" : 3, - "measurementTime" : "4 s", - "measurementBatchSize" : 1, - "primaryMetric" : { - "score" : 928.6525007153555, - "scoreError" : 3218.660182413916, - "scoreConfidence" : [ - -2290.0076816985606, - 4147.312683129272 - ], - "scorePercentiles" : { - "0.0" : 808.4349352738636, - "50.0" : 846.3295311978617, - "90.0" : 1131.1930356743412, - "95.0" : 1131.1930356743412, - "99.0" : 1131.1930356743412, - "99.9" : 1131.1930356743412, - "99.99" : 1131.1930356743412, - "99.999" : 1131.1930356743412, - "99.9999" : 1131.1930356743412, - "100.0" : 1131.1930356743412 - }, - "scoreUnit" : "us/op", - "rawData" : [ - [ - 1131.1930356743412, - 808.4349352738636, - 846.3295311978617 - ] - ] - }, - "secondaryMetrics" : { - } - }, - { - "jmhVersion" : "1.37", - "benchmark" : "de.cuioss.jwt.validation.benchmark.standard.SimpleCoreValidationBenchmark.measureConcurrentValidation", - "mode" : "avgt", - "threads" : 100, - "forks" : 1, - "jvm" : "/Users/oliver/.sdkman/candidates/java/21.0.7-tem/bin/java", - "jvmArgs" : [ - "-Djava.util.logging.config.file=src/main/resources/benchmark-logging.properties", - "-Dbenchmark.results.dir=target/benchmark-results" - ], - "jdkVersion" : "21.0.7", - "vmName" : "OpenJDK 64-Bit Server VM", - "vmVersion" : "21.0.7+6-LTS", - "warmupIterations" : 1, - "warmupTime" : "1 s", - "warmupBatchSize" : 1, - "measurementIterations" : 3, - "measurementTime" : "4 s", - "measurementBatchSize" : 1, - "primaryMetric" : { - "score" : 904.1837804561357, - "scoreError" : 2009.986557383793, - "scoreConfidence" : [ - -1105.8027769276573, - 2914.170337839929 - ], - "scorePercentiles" : { - "0.0" : 835.4446265863261, - "50.0" : 845.8466213683903, - "90.0" : 1031.2600934136908, - "95.0" : 1031.2600934136908, - "99.0" : 1031.2600934136908, - "99.9" : 1031.2600934136908, - "99.99" : 1031.2600934136908, - "99.999" : 1031.2600934136908, - "99.9999" : 1031.2600934136908, - "100.0" : 1031.2600934136908 - }, - "scoreUnit" : "us/op", - "rawData" : [ - [ - 1031.2600934136908, - 845.8466213683903, - 835.4446265863261 - ] - ] - }, - "secondaryMetrics" : { - } - } -] - - diff --git a/benchmarking/templates/data/jwt-validation-metrics.json b/benchmarking/templates/data/jwt-validation-metrics.json deleted file mode 100644 index 7d71f4e2..00000000 --- a/benchmarking/templates/data/jwt-validation-metrics.json +++ /dev/null @@ -1,357 +0,0 @@ -{ - "measureThroughput": { - "timestamp": "2025-08-01T07:33:36.504762Z", - "steps": { - "token_parsing": { - "sample_count": 1024, - "p50_us": 3.8, - "p95_us": 4.9, - "p99_us": 5.9 - }, - "header_validation": { - "sample_count": 1024, - "p50_us": 0.3, - "p95_us": 1.2, - "p99_us": 1.9 - }, - "claims_validation": { - "sample_count": 1024, - "p50_us": 1.9, - "p95_us": 3.3, - "p99_us": 3.7 - }, - "cache_lookup": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.3, - "p99_us": 0.4 - }, - "issuer_config_resolution": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.2, - "p99_us": 0.3 - }, - "signature_validation": { - "sample_count": 1024, - "p50_us": 53, - "p95_us": 70, - "p99_us": 24864 - }, - "token_format_check": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.2, - "p99_us": 0.3 - }, - "token_building": { - "sample_count": 1024, - "p50_us": 4.6, - "p95_us": 8.8, - "p99_us": 12 - }, - "complete_validation": { - "sample_count": 1024, - "p50_us": 67, - "p95_us": 92, - "p99_us": 40819 - }, - "issuer_extraction": { - "sample_count": 1024, - "p50_us": 0.2, - "p95_us": 0.5, - "p99_us": 1 - }, - "cache_store": { - "sample_count": 1024, - "p50_us": 0.4, - "p95_us": 0.7, - "p99_us": 1.1 - } - } - }, - "validateMixedTokens0": { - "timestamp": "2025-08-01T07:34:12.407553Z", - "steps": { - "issuer_config_resolution": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.2, - "p99_us": 0.4 - }, - "token_building": { - "sample_count": 1024, - "p50_us": 2.2, - "p95_us": 3.7, - "p99_us": 4.9 - }, - "token_parsing": { - "sample_count": 1024, - "p50_us": 3.5, - "p95_us": 4.8, - "p99_us": 6.1 - }, - "issuer_extraction": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.2, - "p99_us": 0.3 - }, - "cache_store": { - "sample_count": 1024, - "p50_us": 0.2, - "p95_us": 0.4, - "p99_us": 0.6 - }, - "cache_lookup": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.2, - "p99_us": 0.3 - }, - "header_validation": { - "sample_count": 1024, - "p50_us": 0.2, - "p95_us": 0.4, - "p99_us": 0.5 - }, - "token_format_check": { - "sample_count": 1024, - "p50_us": 0.1, - "p95_us": 0.3, - "p99_us": 0.4 - }, - "signature_validation": { - "sample_count": 1024, - "p50_us": 52, - "p95_us": 69, - "p99_us": 608 - }, - "complete_validation": { - "sample_count": 1024, - "p50_us": 60, - "p95_us": 81, - "p99_us": 864 - }, - "claims_validation": { - "sample_count": 1024, - "p50_us": 0.7, - "p95_us": 1.2, - "p99_us": 1.7 - } - } - }, - "validateMixedTokens50": { - "timestamp": "2025-08-01T07:34:47.800593Z", - "steps": { - "signature_validation": { - "sample_count": 1024, - "p50_us": 46, - "p95_us": 69, - "p99_us": 91 - }, - "issuer_config_resolution": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 2.5, - "p99_us": 3.2 - }, - "token_format_check": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.2, - "p99_us": 0.3 - }, - "token_parsing": { - "sample_count": 1024, - "p50_us": 3.5, - "p95_us": 6.1, - "p99_us": 6.8 - }, - "claims_validation": { - "sample_count": 1024, - "p50_us": 1.7, - "p95_us": 3.3, - "p99_us": 4.1 - }, - "token_building": { - "sample_count": 1024, - "p50_us": 2.4, - "p95_us": 4.1, - "p99_us": 5 - }, - "complete_validation": { - "sample_count": 1024, - "p50_us": 35, - "p95_us": 84, - "p99_us": 104 - }, - "issuer_extraction": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.2, - "p99_us": 0.4 - }, - "cache_lookup": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.2, - "p99_us": 0.4 - }, - "header_validation": { - "sample_count": 1024, - "p50_us": 0.2, - "p95_us": 0.4, - "p99_us": 0.5 - }, - "cache_store": { - "sample_count": 1024, - "p50_us": 0.4, - "p95_us": 0.7, - "p99_us": 0.9 - } - } - }, - "measureAverageTime": { - "timestamp": "2025-08-01T07:35:22.250463Z", - "steps": { - "token_format_check": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.2, - "p99_us": 0.3 - }, - "token_building": { - "sample_count": 1024, - "p50_us": 3.2, - "p95_us": 4.6, - "p99_us": 7 - }, - "cache_lookup": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.3, - "p99_us": 0.4 - }, - "cache_store": { - "sample_count": 1024, - "p50_us": 0.1, - "p95_us": 0.4, - "p99_us": 0.6 - }, - "signature_validation": { - "sample_count": 1024, - "p50_us": 68, - "p95_us": 83, - "p99_us": 8645 - }, - "issuer_extraction": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.2, - "p99_us": 0.3 - }, - "issuer_config_resolution": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.2, - "p99_us": 0.3 - }, - "claims_validation": { - "sample_count": 1024, - "p50_us": 1, - "p95_us": 1.4, - "p99_us": 1.8 - }, - "header_validation": { - "sample_count": 1024, - "p50_us": 0.2, - "p95_us": 0.4, - "p99_us": 0.5 - }, - "token_parsing": { - "sample_count": 1024, - "p50_us": 4.3, - "p95_us": 5.3, - "p99_us": 7.3 - }, - "complete_validation": { - "sample_count": 1024, - "p50_us": 78, - "p95_us": 107, - "p99_us": 18042 - } - } - }, - "measureConcurrentValidation": { - "timestamp": "2025-08-01T07:35:57.926454Z", - "steps": { - "issuer_extraction": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.2, - "p99_us": 0.3 - }, - "token_building": { - "sample_count": 1024, - "p50_us": 3, - "p95_us": 4, - "p99_us": 5.3 - }, - "complete_validation": { - "sample_count": 1024, - "p50_us": 63, - "p95_us": 90, - "p99_us": 10203 - }, - "cache_store": { - "sample_count": 1024, - "p50_us": 0.1, - "p95_us": 0.4, - "p99_us": 0.5 - }, - "issuer_config_resolution": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.2, - "p99_us": 0.3 - }, - "token_format_check": { - "sample_count": 1024, - "p50_us": 0.1, - "p95_us": 0.2, - "p99_us": 0.3 - }, - "claims_validation": { - "sample_count": 1024, - "p50_us": 0.9, - "p95_us": 1.1, - "p99_us": 1.5 - }, - "header_validation": { - "sample_count": 1024, - "p50_us": 0.2, - "p95_us": 0.4, - "p99_us": 0.5 - }, - "cache_lookup": { - "sample_count": 1024, - "p50_us": 0, - "p95_us": 0.2, - "p99_us": 0.3 - }, - "signature_validation": { - "sample_count": 1024, - "p50_us": 54, - "p95_us": 74, - "p99_us": 10190 - }, - "token_parsing": { - "sample_count": 1024, - "p50_us": 4.3, - "p95_us": 6.1, - "p99_us": 7.3 - } - } - } -} \ No newline at end of file diff --git a/benchmarking/templates/data/performance-tracking.json b/benchmarking/templates/data/performance-tracking.json deleted file mode 100644 index 754236af..00000000 --- a/benchmarking/templates/data/performance-tracking.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "runs": [ - { - "timestamp": "2025-07-25T10:15:30Z", - "commit": "a1b2c3d4e5f6789012345678901234567890abcd", - "performance": { - "score": 48520, - "throughput": {"value": 79800, "unit": "ops/s"}, - "averageTime": {"value": 0.001256, "unit": "s"}, - "errorResilience": {"value": 105200, "unit": "ops/s"} - } - }, - { - "timestamp": "2025-07-26T14:22:15Z", - "commit": "b2c3d4e5f6789012345678901234567890abcde1", - "performance": { - "score": 49850, - "throughput": {"value": 82100, "unit": "ops/s"}, - "averageTime": {"value": 0.001218, "unit": "s"}, - "errorResilience": {"value": 108500, "unit": "ops/s"} - } - }, - { - "timestamp": "2025-07-27T09:45:22Z", - "commit": "c3d4e5f6789012345678901234567890abcde12f", - "performance": { - "score": 47200, - "throughput": {"value": 77500, "unit": "ops/s"}, - "averageTime": {"value": 0.001289, "unit": "s"}, - "errorResilience": {"value": 101800, "unit": "ops/s"} - } - }, - { - "timestamp": "2025-07-28T16:33:44Z", - "commit": "d4e5f6789012345678901234567890abcde12f34", - "performance": { - "score": 51200, - "throughput": {"value": 84300, "unit": "ops/s"}, - "averageTime": {"value": 0.001186, "unit": "s"}, - "errorResilience": {"value": 112000, "unit": "ops/s"} - } - }, - { - "timestamp": "2025-07-29T11:28:17Z", - "commit": "e5f6789012345678901234567890abcde12f3456", - "performance": { - "score": 50100, - "throughput": {"value": 82800, "unit": "ops/s"}, - "averageTime": {"value": 0.001207, "unit": "s"}, - "errorResilience": {"value": 109300, "unit": "ops/s"} - } - }, - { - "timestamp": "2025-07-30T13:17:58Z", - "commit": "f6789012345678901234567890abcde12f345678", - "performance": { - "score": 52800, - "throughput": {"value": 87200, "unit": "ops/s"}, - "averageTime": {"value": 0.001147, "unit": "s"}, - "errorResilience": {"value": 115600, "unit": "ops/s"} - } - }, - { - "timestamp": "2025-07-31T15:42:33Z", - "commit": "6789012345678901234567890abcde12f3456789", - "performance": { - "score": 51900, - "throughput": {"value": 85700, "unit": "ops/s"}, - "averageTime": {"value": 0.001167, "unit": "s"}, - "errorResilience": {"value": 113400, "unit": "ops/s"} - } - }, - { - "timestamp": "2025-08-01T07:30:12Z", - "commit": "789012345678901234567890abcde12f3456789a", - "performance": { - "score": 52580, - "throughput": {"value": 85538, "unit": "ops/s"}, - "averageTime": {"value": 0.001169, "unit": "s"}, - "errorResilience": {"value": 113075, "unit": "ops/s"} - } - } - ] -} \ No newline at end of file diff --git a/benchmarking/templates/data/tracking/performance-20250801-073012.json b/benchmarking/templates/data/tracking/performance-20250801-073012.json deleted file mode 100644 index 639b8a73..00000000 --- a/benchmarking/templates/data/tracking/performance-20250801-073012.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "timestamp": "2025-08-01T07:30:12Z", - "commit": "789012345678901234567890abcde12f3456789a", - "performance": { - "score": 52580, - "throughput": { - "value": 85538, - "unit": "ops/s" - }, - "averageTime": { - "value": 0.001169, - "unit": "s" - }, - "errorResilience": { - "value": 113075, - "unit": "ops/s" - } - }, - "rawMetrics": { - "throughputOpsPerSec": 85538, - "avgTimeInMicros": 1169, - "errorResilienceOpsPerSec": 113075 - }, - "environment": { - "javaVersion": "21.0.7", - "jvmArgs": "-Djava.util.logging.config.file=src/main/resources/benchmark-logging.properties -Dbenchmark.results.dir=target/benchmark-results", - "osName": "Linux" - } -} \ No newline at end of file diff --git a/benchmarking/templates/index-visualizer.html b/benchmarking/templates/index-visualizer.html deleted file mode 100644 index 7e26d42c..00000000 --- a/benchmarking/templates/index-visualizer.html +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - JWT Micro Benchmark Results - - - - -
-
-

JWT Micro Benchmark Results

-

Unit-level performance testing of JWT validation components

- -
- -
- - -
-
- - - - - \ No newline at end of file diff --git a/benchmarking/templates/integration-benchmark-visualizer.html b/benchmarking/templates/integration-benchmark-visualizer.html deleted file mode 100644 index e3c70d09..00000000 --- a/benchmarking/templates/integration-benchmark-visualizer.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - JWT Integration Benchmark Results - - - - -
-
-

JWT Integration Benchmark Results

-

End-to-end performance testing with Keycloak and native Quarkus

- -
- -
-

Integration Test Environment

-
-
-
Test Type
-
Native Container
-
-
-
Services
-
Keycloak + Quarkus
-
-
-
Protocol
-
HTTPS/TLS
-
-
-
Benchmark Format
-
JMH Results
-
-
-
- -
- - -
-
- - - - - \ No newline at end of file diff --git a/benchmarking/templates/integration-index.html b/benchmarking/templates/integration-index.html deleted file mode 100644 index 7c33b0ae..00000000 --- a/benchmarking/templates/integration-index.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - JWT Integration Benchmark Results - - - - -
-
-

JWT Integration Benchmark Results

-

End-to-end performance testing with containerized Quarkus application and Keycloak

- -
- -
- - -
-
- - - - - \ No newline at end of file diff --git a/benchmarking/templates/integration-performance-trends.html b/benchmarking/templates/integration-performance-trends.html deleted file mode 100644 index ba41cf31..00000000 --- a/benchmarking/templates/integration-performance-trends.html +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - Integration Benchmark Performance Trends - - - - - -
-
-

Integration Benchmark Performance Trends

-

Performance metrics and trends for integration benchmarks over the last 10 runs

- -
- -
-

Loading integration performance data...

-
- - - - - - -
- - - - \ No newline at end of file diff --git a/benchmarking/templates/performance-run.json b/benchmarking/templates/performance-run.json deleted file mode 100644 index 26862c15..00000000 --- a/benchmarking/templates/performance-run.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "timestamp": "${TIMESTAMP}", - "commit": "${COMMIT_HASH}", - "performance": { - "score": ${PERFORMANCE_SCORE}, - "throughput": { - "value": ${THROUGHPUT_VALUE}, - "unit": "ops/s" - }, - "averageTime": { - "value": ${AVERAGE_TIME_SEC}, - "unit": "s" - }, - "errorResilience": { - "value": ${ERROR_RESILIENCE_VALUE}, - "unit": "ops/s" - } - }, - "rawMetrics": { - "throughputOpsPerSec": ${THROUGHPUT_OPS_PER_SEC}, - "avgTimeInMicros": ${AVG_TIME_MICROS}, - "errorResilienceOpsPerSec": ${ERROR_RESILIENCE_OPS_PER_SEC} - }, - "environment": { - "javaVersion": "${JAVA_VERSION}", - "jvmArgs": "${JVM_ARGS}", - "osName": "${OS_NAME}" - } -} \ No newline at end of file diff --git a/benchmarking/templates/performance-trends.html b/benchmarking/templates/performance-trends.html deleted file mode 100644 index 5202e8d7..00000000 --- a/benchmarking/templates/performance-trends.html +++ /dev/null @@ -1,248 +0,0 @@ - - - - - - JWT Validation Performance Trends - - - - - -
-
-

JWT Validation Performance Trends

-

Performance metrics and trends over the last 10 benchmark runs

- -
- -
-

Loading performance data...

-
- - - - - - - -
- - - - - diff --git a/benchmarking/templates/resources/common.css b/benchmarking/templates/resources/common.css deleted file mode 100644 index eb48eaf4..00000000 --- a/benchmarking/templates/resources/common.css +++ /dev/null @@ -1,387 +0,0 @@ -/* CUI JWT Benchmarking - Common Styles */ - -/* Base Styles */ -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; - margin: 0; - padding: 0; - background-color: #f8f9fa; - color: #343a40; -} - -/* Layout */ -.container { - max-width: 1400px; - margin: 0 auto; - background: white; - border-radius: 8px; - box-shadow: 0 2px 10px rgba(0,0,0,0.1); - padding: 30px; -} - -/* Typography */ -h1 { - color: #343a40; - margin-bottom: 10px; - font-size: 2.5rem; - font-weight: 300; -} - -h2 { - color: #495057; - font-size: 1.8rem; - font-weight: 400; - margin-bottom: 15px; -} - -h3 { - color: #495057; - font-size: 1.4rem; - font-weight: 500; - margin-bottom: 10px; -} - -.subtitle { - color: #6c757d; - margin-bottom: 20px; - font-size: 1.1rem; -} - -/* Header & Navigation */ -.header { - margin-bottom: 30px; - text-align: center; - padding: 20px; - background-color: #f8f9fa; - border-bottom: 1px solid #dee2e6; - border-radius: 8px 8px 0 0; - margin: -30px -30px 30px -30px; -} - -.navigation { - display: flex; - justify-content: center; - align-items: center; - gap: 10px; - margin: 15px 0; - flex-wrap: wrap; -} - -/* Badges & Buttons */ -.badge { - display: inline-block; - padding: 6px 12px; - margin: 3px; - border-radius: 4px; - font-size: 12px; - font-weight: 600; - text-decoration: none; - transition: all 0.2s; - border: none; - cursor: pointer; -} - -.badge:hover { - transform: translateY(-1px); - box-shadow: 0 2px 5px rgba(0,0,0,0.2); -} - -.badge-primary { - background-color: #007bff; - color: white; -} - -.badge-secondary { - background-color: #6c757d; - color: white; -} - -.badge-success { - background-color: #28a745; - color: white; -} - -.badge-info { - background-color: #17a2b8; - color: white; -} - -.badge-warning { - background-color: #ffc107; - color: #212529; -} - -.badge-current { - background-color: #343a40; - color: white; - cursor: default; -} - -/* Form Controls */ -.controls { - display: flex; - justify-content: center; - align-items: center; - gap: 20px; - margin-bottom: 30px; - padding: 20px; - background-color: #f8f9fa; - border-radius: 8px; -} - -.control-group { - display: flex; - flex-direction: column; - align-items: center; - gap: 8px; -} - -.control-label { - font-weight: 500; - color: #495057; - font-size: 0.9rem; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -select { - padding: 8px 16px; - border: 2px solid #dee2e6; - border-radius: 6px; - background: white; - font-size: 1rem; - min-width: 200px; - transition: border-color 0.2s; -} - -select:focus { - outline: none; - border-color: #007bff; -} - -/* Cards & Sections */ -.metric-card, .stat-card { - background: white; - padding: 20px; - border-radius: 6px; - text-align: center; - border-left: 4px solid #007bff; - box-shadow: 0 1px 3px rgba(0,0,0,0.1); -} - -.metric-value, .stat-value { - font-size: 2rem; - font-weight: 600; - color: #343a40; - margin-bottom: 5px; -} - -.metric-label, .stat-label { - font-size: 0.8rem; - color: #6c757d; - text-transform: uppercase; - letter-spacing: 0.5px; - margin-top: 5px; -} - -/* Grid Layouts */ -.metrics-grid, .summary-stats { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 20px; - margin: 20px 0; -} - -.charts-container { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 30px; - margin-top: 20px; -} - -/* Chart Styles */ -.chart-section { - background: #ffffff; - border: 1px solid #e9ecef; - border-radius: 8px; - padding: 20px; - box-shadow: 0 1px 3px rgba(0,0,0,0.1); -} - -.chart-title { - font-size: 1.2rem; - font-weight: 600; - color: #343a40; - margin-bottom: 15px; - text-align: center; - padding-bottom: 10px; - border-bottom: 2px solid #f8f9fa; -} - -.chart-container { - position: relative; - height: 400px; - width: 100%; -} - -/* Content Areas */ -.content { - flex: 1; - overflow: hidden; - position: relative; -} - -.content iframe { - width: 100%; - height: 100%; - border: none; - min-height: 600px; -} - -/* Info Boxes */ -.test-info { - text-align: center; - margin-bottom: 20px; - padding: 15px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - border-radius: 8px; -} - -.test-timestamp { - font-size: 0.9rem; - opacity: 0.9; -} - -.step-details { - margin-top: 20px; - padding: 15px; - background: #e3f2fd; - border-radius: 8px; - border-left: 4px solid #2196f3; -} - -.step-details h4 { - margin: 0 0 10px 0; - color: #1976d2; -} - -/* Legends */ -.percentile-legend { - display: flex; - justify-content: center; - gap: 20px; - margin-bottom: 15px; - font-size: 0.9rem; -} - -.legend-item { - display: flex; - align-items: center; - gap: 5px; -} - -.legend-color { - width: 16px; - height: 16px; - border-radius: 3px; -} - -/* Trend Indicators */ -.trend-indicator { - font-size: 0.9rem; - margin-top: 5px; - font-weight: 500; -} - -.trend-up { color: #28a745; } -.trend-down { color: #dc3545; } -.trend-stable { color: #6c757d; } - -/* Status Messages */ -.loading { - text-align: center; - padding: 40px; - color: #6c757d; -} - -.error { - background: #f8d7da; - color: #721c24; - border: 1px solid #f5c6cb; - border-radius: 4px; - padding: 20px; - margin: 20px 0; - text-align: center; -} - -.error h3 { - margin-top: 0; -} - -/* Footer */ -.footer { - margin-top: 40px; - padding-top: 20px; - border-top: 1px solid #dee2e6; - color: #6c757d; - font-size: 0.9rem; - text-align: center; -} - -/* Responsive Design */ -@media (max-width: 1200px) { - .container { - max-width: 95%; - padding: 20px; - } - - .charts-container { - grid-template-columns: 1fr; - } -} - -@media (max-width: 768px) { - .header { - padding: 15px; - } - - h1 { - font-size: 2rem; - } - - .navigation { - flex-direction: column; - gap: 8px; - } - - .controls { - flex-direction: column; - } - - .metrics-grid, .summary-stats { - grid-template-columns: 1fr; - } - - .badge { - font-size: 11px; - padding: 4px 8px; - } -} - -@media (max-width: 480px) { - .container { - padding: 15px; - margin: 10px; - border-radius: 6px; - } - - .header { - margin: -15px -15px 20px -15px; - } - - .percentile-legend { - flex-direction: column; - gap: 10px; - } -} \ No newline at end of file diff --git a/benchmarking/templates/resources/navigation.js b/benchmarking/templates/resources/navigation.js deleted file mode 100644 index c43f014a..00000000 --- a/benchmarking/templates/resources/navigation.js +++ /dev/null @@ -1,69 +0,0 @@ -// CUI JWT Benchmarking - Shared Navigation Component - -class BenchmarkNavigation { - constructor(currentPage = '') { - this.currentPage = currentPage; - this.pages = [ - { id: 'micro', title: 'Micro Benchmarks', file: 'index-visualizer.html', description: 'JMH unit-level performance testing' }, - { id: 'integration', title: 'Integration Benchmarks', file: 'integration-benchmark-visualizer.html', description: 'End-to-end containerized testing' }, - { id: 'step-metrics', title: 'Step Metrics', file: 'step-metrics-visualizer.html', description: 'Detailed step-by-step analysis' }, - { id: 'trends', title: 'Performance Trends', file: 'performance-trends.html', description: 'Historical performance tracking' } - ]; - } - - // Generate navigation HTML - generateNavigation() { - return this.pages.map(page => { - const isCurrent = page.id === this.currentPage; - const badgeClass = isCurrent ? 'badge-current' : 'badge-primary'; - const title = isCurrent ? `Current: ${page.description}` : page.description; - - return ` - ${page.title}${isCurrent ? ' (Current)' : ''} - `; - }).join('\n '); - } - - // Inject navigation into header - injectNavigation() { - const headerElement = document.querySelector('.header'); - if (headerElement) { - const existingNav = headerElement.querySelector('.navigation'); - if (existingNav) { - existingNav.innerHTML = this.generateNavigation(); - } else { - const navDiv = document.createElement('div'); - navDiv.className = 'navigation'; - navDiv.innerHTML = this.generateNavigation(); - headerElement.appendChild(navDiv); - } - } - } - - // Initialize navigation when DOM is loaded - init() { - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', () => this.injectNavigation()); - } else { - this.injectNavigation(); - } - } -} - -// Utility function to get current page type from filename or path -function getCurrentPageType() { - const path = window.location.pathname; - const filename = path.split('/').pop() || 'index-visualizer.html'; - - if (filename.includes('integration-benchmark-visualizer')) return 'integration'; - if (filename.includes('integration')) return 'integration'; - if (filename.includes('step-metrics')) return 'step-metrics'; - if (filename.includes('performance-trends')) return 'trends'; - if (filename.includes('index-visualizer')) return 'micro'; - return 'micro'; // default -} - -// Auto-initialize navigation -const currentPage = getCurrentPageType(); -const navigation = new BenchmarkNavigation(currentPage); -navigation.init(); \ No newline at end of file diff --git a/benchmarking/templates/step-metrics-visualizer.html b/benchmarking/templates/step-metrics-visualizer.html deleted file mode 100644 index 2c89aa8c..00000000 --- a/benchmarking/templates/step-metrics-visualizer.html +++ /dev/null @@ -1,476 +0,0 @@ - - - - - - JWT Validation Step Metrics Visualizer - - - - -
-
-

JWT Validation Step Metrics

-

Interactive visualization of performance metrics for each validation step

- -
- -
-

Loading metrics data...

-
- - - - - -
- - - - - \ No newline at end of file diff --git a/benchmarks/Dockerfile.wrk b/benchmarks/Dockerfile.wrk new file mode 100644 index 00000000..317e37a6 --- /dev/null +++ b/benchmarks/Dockerfile.wrk @@ -0,0 +1,5 @@ +FROM alpine:3.22 +RUN apk add --no-cache wrk +COPY benchmarks/src/main/resources/wrk-scripts/*.lua /scripts/ +ENTRYPOINT ["wrk"] +CMD ["--help"] diff --git a/benchmarks/README.adoc b/benchmarks/README.adoc new file mode 100644 index 00000000..70efcd9e --- /dev/null +++ b/benchmarks/README.adoc @@ -0,0 +1,21 @@ += API Sheriff Integration Benchmarks + +WRK-based HTTP benchmarks for API Sheriff endpoints with integration test infrastructure. + +== Running Benchmarks + +[source,bash] +---- +./mvnw clean verify -pl benchmarks -Pbenchmark +---- + +For quick runs (shorter duration, assumes containers already running): + +[source,bash] +---- +./mvnw clean verify -pl benchmarks -Pbenchmark -Pquick +---- + +== Performance Scoring + +See the https://github.com/cuioss/OAuthSheriff/blob/main/benchmarking/doc/performance-scoring.adoc[JWT Performance Scoring System] documentation in the OAuthSheriff repository. diff --git a/benchmarks/doc/performance-scoring.adoc b/benchmarks/doc/performance-scoring.adoc new file mode 100644 index 00000000..5ac6c41b --- /dev/null +++ b/benchmarks/doc/performance-scoring.adoc @@ -0,0 +1,5 @@ += JWT Performance Scoring System + +This document is maintained in the OAuthSheriff repository. + +See: https://github.com/cuioss/OAuthSheriff/blob/main/benchmarking/doc/performance-scoring.adoc diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml new file mode 100644 index 00000000..0731d721 --- /dev/null +++ b/benchmarks/pom.xml @@ -0,0 +1,369 @@ + + + 4.0.0 + + + de.cuioss.sheriff.api + api-sheriff-parent + 1.0.0-SNAPSHOT + ../pom.xml + + + benchmarks + API Sheriff Integration Benchmarks + WRK-based HTTP benchmarks for API Sheriff endpoints with integration test infrastructure + + + de.cuioss.sheriff.api.benchmark.integration + + true + + + false + + + ${project.basedir}/../integration-tests/scripts + + + ${project.basedir}/../integration-tests + + + https://localhost:10443 + http://localhost:19000 + https://localhost:1443 + http://localhost:19000 + + + 60s + 5 + 50 + 2s + true + ${project.basedir}/src/main/resources/wrk-scripts + ${project.build.directory}/benchmark-results + ${wrk.results.dir}/wrk + + + ${wrk.results.dir}/history + 100 + + + true + + + + + + de.cuioss.sheriff.oauth + benchmarking-common + ${version.oauth-sheriff} + + + + + de.cuioss + cui-java-tools + + + + + org.junit.jupiter + junit-jupiter + test + + + + + org.awaitility + awaitility + test + + + + + + + + org.codehaus.mojo + exec-maven-plugin + + ${skip.benchmark} + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + package + + copy-dependencies + + + ${project.build.directory}/dependency + false + false + true + runtime + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + -XX:+IgnoreUnrecognizedVMOptions -Djava.awt.headless=true + + + + + + org.codehaus.mojo + exec-maven-plugin + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + + + + + benchmark + + false + + + + + org.codehaus.mojo + exec-maven-plugin + + + + maven-build-integration-tests + pre-integration-test + + exec + + + ${skip.container.lifecycle} + ../mvnw + + compile + exec:exec@build-native-if-needed + exec:exec@docker-build-distroless + -Pintegration-tests + -DskipTests + + ${project.basedir}/../integration-tests + + + + + + start-integration-containers + pre-integration-test + + exec + + + ${skip.container.lifecycle} + ${integration.scripts.dir}/start-integration-container.sh + ${project.basedir} + + ${wrk.results.dir} + + + + + + + create-benchmark-dirs + pre-integration-test + + exec + + + mkdir + + -p + ${wrk.results.dir} + ${wrk.output.dir} + + + + + + + pre-benchmark-health-check + integration-test + + exec + + + bash + + ${wrk.script.dir}/pre-benchmark-health-check.sh + + + ${integration.service.url} + ${integration.management.url} + http://localhost:9090 + https://localhost:1090 + + + + + + + run-wrk-health-live-benchmark + integration-test + + exec + + + bash + + ${wrk.script.dir}/health_live_benchmark.sh + + ${wrk.output.dir}/wrk-health-live-results.txt + 240000 + + ${wrk.threads} + ${wrk.connections} + ${wrk.duration} + ${wrk.timeout} + ${integration.compose.dir} + + + + + + + run-wrk-api-health-benchmark + integration-test + + exec + + + bash + + ${wrk.script.dir}/api_health_benchmark.sh + + ${wrk.output.dir}/wrk-api-health-results.txt + 240000 + + ${wrk.threads} + ${wrk.connections} + ${wrk.duration} + ${wrk.timeout} + ${integration.compose.dir} + + + + + + + process-wrk-results + post-integration-test + + java + + + de.cuioss.sheriff.api.wrk.benchmark.WrkResultPostProcessor + + ${wrk.results.dir} + + + + quarkus.metrics.url + ${quarkus.metrics.url} + + + prometheus.url + http://localhost:9090 + + + + + + + + dump-keycloak-logs + post-integration-test + + exec + + + ${skip.container.lifecycle} + ${integration.scripts.dir}/dump-keycloak-logs.sh + + ${wrk.results.dir} + + ${project.basedir} + + + + + stop-integration-containers + post-integration-test + + exec + + + ${skip.container.lifecycle} + ${integration.scripts.dir}/stop-integration-container.sh + ${project.basedir} + + + 0 + 1 + + + + + + + clean-integration-tests-for-rebuild + clean + + exec + + + ${skip.container.lifecycle} + ./mvnw + + clean + -pl + api-sheriff,integration-tests + + ${project.basedir}/.. + + + + + + + + + + + quick + + 30s + true + + + + diff --git a/benchmarks/scripts/benchmark-pages.py b/benchmarks/scripts/benchmark-pages.py new file mode 100644 index 00000000..69122763 --- /dev/null +++ b/benchmarks/scripts/benchmark-pages.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +"""Benchmark CI pipeline helper for GitHub Pages deployment. + +Manages benchmark history and assembles deployment artifacts for cuioss.github.io. + +Subcommands: + prepare-history Copy fetched history to working directories for Maven trend calculation. + assemble Merge history, enforce retention, and combine artifacts for deployment. + +Usage in CI: + # Before Maven benchmark runs: + python3 benchmarks/scripts/benchmark-pages.py prepare-history \ + --previous-pages-dir previous-pages/api-sheriff/benchmarks \ + --output-dir "$GITHUB_WORKSPACE/benchmark-history" + + # After all Maven benchmark runs: + python3 benchmarks/scripts/benchmark-pages.py assemble \ + --integration-results benchmarks/target/benchmark-results/gh-pages-ready \ + --previous-pages-dir previous-pages/api-sheriff/benchmarks \ + --output-dir gh-pages \ + --commit-sha "$COMMIT_SHA" +""" + +import argparse +import json +import shutil +import sys +from datetime import datetime, timezone +from pathlib import Path + +# Badge files produced by each benchmark type and their names in the root badges/ directory. +_BADGE_MAPPING = { + "integration": { + "integration-performance-badge.json": "integration-performance-badge.json", + "integration-trend-badge.json": "integration-trend-badge.json", + "last-run-badge.json": "integration-last-run-badge.json", + }, +} + +_BENCHMARK_TYPES = ("integration",) +_DEFAULT_MAX_HISTORY = 10 + + +def _copy_json_files(src_dir: Path, dst_dir: Path, *, skip_existing: bool = False) -> int: + """Copy all .json files from src to dst. Returns count of files copied.""" + dst_dir.mkdir(parents=True, exist_ok=True) + copied = 0 + for f in src_dir.glob("*.json"): + dst = dst_dir / f.name + if skip_existing and dst.exists(): + continue + shutil.copy2(f, dst) + copied += 1 + return copied + + +def _enforce_retention(history_dir: Path, max_files: int) -> int: + """Keep only the newest max_files JSON files (sorted by name descending). Returns count removed.""" + if not history_dir.is_dir(): + return 0 + files = sorted(history_dir.glob("*.json"), reverse=True) + removed = 0 + for old_file in files[max_files:]: + old_file.unlink() + removed += 1 + return removed + + +def prepare_history(args: argparse.Namespace) -> None: + """Copy previously deployed history files to working directories for Maven trend calculation.""" + previous_dir = Path(args.previous_pages_dir) + output_dir = Path(args.output_dir) + + if not previous_dir.is_dir(): + print(f"No previous pages directory found at {previous_dir}, skipping history preparation") + return + + for benchmark_type in _BENCHMARK_TYPES: + history_src = previous_dir / benchmark_type / "history" + if not history_src.is_dir(): + continue + count = _copy_json_files(history_src, output_dir / benchmark_type) + print(f"Prepared {count} {benchmark_type} history files") + + +def assemble(args: argparse.Namespace) -> None: + """Merge history, enforce retention, and combine artifacts for deployment. + + Note: History archiving (current run -> history/) is handled by Maven's + HistoricalDataManager.archiveCurrentRun() during the benchmark verify phase. + This function only merges previously deployed history and enforces retention. + """ + now = datetime.now(timezone.utc) + output_dir = Path(args.output_dir) + previous_dir = Path(args.previous_pages_dir) if args.previous_pages_dir else None + commit_sha = args.commit_sha + max_history = args.max_history + + # Collect configured benchmark modules + modules: list[tuple[str, Path]] = [] + if args.micro_results: + modules.append(("micro", Path(args.micro_results))) + if args.integration_results: + modules.append(("integration", Path(args.integration_results))) + + if not modules: + print("Error: at least one of --micro-results or --integration-results is required", + file=sys.stderr) + sys.exit(1) + + # 1. Merge previous history (skip files that already exist to avoid overwriting current run) + if previous_dir and previous_dir.is_dir(): + for name, results_dir in modules: + prev_history = previous_dir / name / "history" + if not prev_history.is_dir(): + continue + history_dir = results_dir / "history" + count = _copy_json_files(prev_history, history_dir, skip_existing=True) + print(f"Merged {count} previous {name} history files") + + # 2. Enforce retention policy per module + for name, results_dir in modules: + removed = _enforce_retention(results_dir / "history", max_history) + if removed: + print(f"Removed {removed} old {name} history files (retention: {max_history})") + + # 3. Combine into output directory + output_dir.mkdir(parents=True, exist_ok=True) + badges_dir = output_dir / "badges" + badges_dir.mkdir(exist_ok=True) + + for name, results_dir in modules: + if not results_dir.is_dir(): + print(f"Warning: {name} results not found at {results_dir}, skipping") + continue + + # Copy full module output into type subdirectory + shutil.copytree(results_dir, output_dir / name, dirs_exist_ok=True) + print(f"Copied {name} benchmark artifacts to {output_dir / name}") + + # Promote badges to root badges/ directory with mapped names + module_badges = results_dir / "badges" + if module_badges.is_dir() and name in _BADGE_MAPPING: + for src_name, dst_name in _BADGE_MAPPING[name].items(): + src = module_badges / src_name + if src.is_file(): + shutil.copy2(src, badges_dir / dst_name) + + # 4. Write deployment metadata + metadata = { + "timestamp": now.strftime("%Y-%m-%dT%H:%M:%SZ"), + "commit": commit_sha, + } + (output_dir / "metadata.json").write_text(json.dumps(metadata, indent=2) + "\n") + + # Summary + print(f"\nAssembled deployment artifacts in {output_dir}/") + for entry in sorted(output_dir.iterdir()): + kind = "dir" if entry.is_dir() else "file" + print(f" {entry.name}/ " if entry.is_dir() else f" {entry.name}") + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Benchmark CI pipeline helper for GitHub Pages deployment.", + ) + subparsers = parser.add_subparsers(dest="command", required=True) + + # prepare-history + prep = subparsers.add_parser( + "prepare-history", + help="Copy fetched history to working directories for Maven trend calculation.", + ) + prep.add_argument( + "--previous-pages-dir", required=True, + help="Path to previously deployed pages (e.g. previous-pages/api-sheriff/benchmarks)", + ) + prep.add_argument( + "--output-dir", required=True, + help="Output directory for history files (passed to Maven as benchmark.history.dir parent)", + ) + + # assemble + asm = subparsers.add_parser( + "assemble", + help="Merge history, enforce retention, and combine all benchmark artifacts for deployment.", + ) + asm.add_argument( + "--micro-results", + help="Path to micro benchmark gh-pages-ready directory", + ) + asm.add_argument( + "--integration-results", + help="Path to integration benchmark gh-pages-ready directory", + ) + asm.add_argument( + "--previous-pages-dir", + help="Path to previously deployed pages for history merging", + ) + asm.add_argument( + "--output-dir", required=True, + help="Output directory for combined deployment artifacts", + ) + asm.add_argument( + "--commit-sha", required=True, + help="Git commit SHA for metadata and history archive naming", + ) + asm.add_argument( + "--max-history", type=int, default=_DEFAULT_MAX_HISTORY, + help=f"Maximum number of history entries to retain (default: {_DEFAULT_MAX_HISTORY})", + ) + + args = parser.parse_args() + + command_handlers = { + "prepare-history": prepare_history, + "assemble": assemble, + } + command_handlers[args.command](args) + + +if __name__ == "__main__": + main() diff --git a/benchmarks/src/main/java/de/cuioss/sheriff/api/wrk/benchmark/WrkResultPostProcessor.java b/benchmarks/src/main/java/de/cuioss/sheriff/api/wrk/benchmark/WrkResultPostProcessor.java new file mode 100644 index 00000000..2eca8917 --- /dev/null +++ b/benchmarks/src/main/java/de/cuioss/sheriff/api/wrk/benchmark/WrkResultPostProcessor.java @@ -0,0 +1,360 @@ +/* + * Copyright ยฉ 2025 CUI-OpenSource-Software (info@cuioss.de) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.cuioss.sheriff.api.wrk.benchmark; + +import de.cuioss.benchmarking.common.config.BenchmarkType; +import de.cuioss.benchmarking.common.converter.WrkBenchmarkConverter; +import de.cuioss.benchmarking.common.metrics.PrometheusMetricsManager; +import de.cuioss.benchmarking.common.model.BenchmarkData; +import de.cuioss.benchmarking.common.output.OutputDirectoryStructure; +import de.cuioss.benchmarking.common.report.GitHubPagesGenerator; +import de.cuioss.benchmarking.common.report.ReportGenerator; +import de.cuioss.tools.logging.CuiLogger; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static de.cuioss.benchmarking.common.util.BenchmarkingLogMessages.ERROR; +import static de.cuioss.benchmarking.common.util.BenchmarkingLogMessages.INFO; + +/** + * Post-processor for WRK benchmark results. + *

+ * This class converts WRK output files to the central BenchmarkData format + * and uses the unified report generation infrastructure from cui-benchmarking-common. + */ +public class WrkResultPostProcessor { + + private static final CuiLogger LOGGER = new CuiLogger(WrkResultPostProcessor.class); + + // File naming constants + public static final String WRK_OUTPUT_FILE_SUFFIX = "-results.txt"; + + private final WrkBenchmarkConverter converter = new WrkBenchmarkConverter(); + private final ReportGenerator reportGenerator = new ReportGenerator(); + private final GitHubPagesGenerator gitHubPagesGenerator = new GitHubPagesGenerator(); + private final PrometheusMetricsManager prometheusMetricsManager = new PrometheusMetricsManager(); + + // Map to store benchmark metadata (name -> timestamps) + private final Map benchmarkMetadataMap = new HashMap<>(); + + /** + * Holds metadata for a benchmark execution. + */ + private record BenchmarkMetadata(String name, Instant startTime, Instant endTime) { + } + + /** + * Main entry point for processing WRK benchmark results. + * Usage: + * - args[0]=inputDir, args[1]=outputDir (optional) + * + * @param args Command line arguments + */ + public static void main(String[] args) { + if (args.length == 0) { + LOGGER.error(ERROR.WRK_USAGE_ERROR); + System.exit(1); + } + + try { + Path inputDir = Path.of(args[0]); + WrkResultPostProcessor processor = new WrkResultPostProcessor(); + + // Normal processing mode + Path outputDir = args.length > 1 ? + Path.of(args[1]) : + inputDir.getParent().resolve("benchmark-results"); + + processor.process(inputDir, outputDir); + + LOGGER.info(INFO.RESULTS_AVAILABLE, outputDir); + + } catch (IOException e) { + LOGGER.error(e, ERROR.WRK_PROCESSOR_FAILED); + System.exit(1); + } + } + + /** + * Processes WRK output files and generates reports. + * + * @param inputDir Directory containing WRK output files + * @param outputDir Directory to write reports to + * @throws IOException if processing fails + */ + public void process(Path inputDir, Path outputDir) throws IOException { + LOGGER.info(INFO.WRK_PROCESSING_START); + + // Parse all WRK result files to extract metadata + parseBenchmarkMetadata(inputDir); + + // Validate input directory + if (!Files.exists(inputDir)) { + throw new IllegalArgumentException("Input directory does not exist: " + inputDir); + } + + if (!Files.isDirectory(inputDir)) { + throw new IllegalArgumentException("Input path is not a directory: " + inputDir); + } + + // Check for WRK output files in wrk subdirectory + Path wrkDir = inputDir.resolve("wrk"); + boolean hasWrkFiles = false; + if (Files.exists(wrkDir)) { + try (Stream files = Files.list(wrkDir)) { + hasWrkFiles = files.anyMatch(p -> p.getFileName().toString().endsWith(".txt")); + } + } + + if (!hasWrkFiles) { + LOGGER.error(ERROR.NO_WRK_FILES, wrkDir); + } + + // Convert WRK output to BenchmarkData from wrk subdirectory + BenchmarkData benchmarkData; + if (!Files.exists(wrkDir)) { + LOGGER.error(ERROR.WRK_DIR_NOT_EXIST, wrkDir); + benchmarkData = BenchmarkData.builder() + .metadata(BenchmarkData.Metadata.builder() + .reportVersion("2.0") + .timestamp(Instant.now().toString()) + .displayTimestamp(Instant.now().toString()) + .benchmarkType("Integration Performance") + .build()) + .overview(BenchmarkData.Overview.builder() + .throughput("0 ops/s") + .latency("0ms") + .throughputBenchmarkName("N/A") + .latencyBenchmarkName("N/A") + .performanceScore(0) + .performanceGrade("F") + .performanceGradeClass("grade-f") + .build()) + .benchmarks(List.of()) + .build(); + } else { + benchmarkData = converter.convert(wrkDir); + } + + if (benchmarkData.getBenchmarks() == null || benchmarkData.getBenchmarks().isEmpty()) { + LOGGER.error(ERROR.NO_BENCHMARK_DATA); + } + + // Generate reports using new OutputDirectoryStructure (no duplication) + Files.createDirectories(outputDir); + + // Create OutputDirectoryStructure for organized file generation + OutputDirectoryStructure structure = new OutputDirectoryStructure(outputDir); + structure.ensureDirectories(); + + // Generate HTML reports directly to gh-pages-ready directory + String deploymentPath = structure.getDeploymentDir().toString(); + reportGenerator.generateIndexPage(benchmarkData, BenchmarkType.INTEGRATION, deploymentPath); + reportGenerator.generateTrendsPage(deploymentPath); + reportGenerator.copySupportFiles(deploymentPath); + + // Collect real-time Prometheus metrics for the benchmark execution + collectPrometheusMetrics(benchmarkData, structure); + + // Generate GitHub Pages deployment-specific assets (404.html, robots.txt, sitemap.xml) + gitHubPagesGenerator.generateDeploymentAssets(structure); + } + + /** + * Collect real-time metrics from Prometheus for the benchmark execution. + * + * @param benchmarkData The benchmark data containing benchmark names + * @param structure The output directory structure + */ + private void collectPrometheusMetrics(BenchmarkData benchmarkData, OutputDirectoryStructure structure) { + if (benchmarkMetadataMap.isEmpty()) { + LOGGER.error(ERROR.NO_PROMETHEUS_METADATA); + return; + } + + if (benchmarkData.getBenchmarks() != null) { + for (BenchmarkData.Benchmark benchmark : benchmarkData.getBenchmarks()) { + BenchmarkMetadata metadata = findMetadataForBenchmark(benchmark.getName()); + + if (metadata == null) { + LOGGER.error(ERROR.NO_METADATA_FOR_BENCHMARK, benchmark.getName()); + continue; + } + + prometheusMetricsManager.collectMetricsForWrkBenchmark( + metadata.name, + metadata.startTime, + metadata.endTime, + structure.getBenchmarkResultsDir().toString() + ); + } + } + + copyPrometheusMetricsToDeployment(structure); + } + + /** + * Copies Prometheus metrics from the raw prometheus directory to the deployment data directory. + * + * @param structure The output directory structure + */ + private void copyPrometheusMetricsToDeployment(OutputDirectoryStructure structure) { + try { + Path prometheusRawDir = structure.getPrometheusRawDir(); + Path deploymentDataDir = structure.getDataDir(); + + if (!Files.exists(prometheusRawDir)) { + return; + } + + try (Stream files = Files.list(prometheusRawDir)) { + files.filter(file -> file.getFileName().toString().endsWith(".json")) + .forEach(sourceFile -> { + try { + Path targetFile = deploymentDataDir.resolve(sourceFile.getFileName()); + Files.copy(sourceFile, targetFile, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + LOGGER.error(e, ERROR.FAILED_COPY_PROMETHEUS, sourceFile); + } + }); + } + } catch (IOException e) { + LOGGER.error(e, ERROR.FAILED_COPY_PROMETHEUS_DIR); + } + } + + /** + * Parse benchmark metadata from WRK result files. + * + * @param inputDir The directory containing benchmark results + * @throws IOException if reading files fails + */ + private void parseBenchmarkMetadata(Path inputDir) throws IOException { + Path wrkDir = inputDir.resolve("wrk"); + if (!Files.exists(wrkDir)) { + return; + } + + try (Stream files = Files.list(wrkDir)) { + List wrkFiles = files + .filter(p -> p.getFileName().toString().endsWith(WRK_OUTPUT_FILE_SUFFIX)) + .toList(); + + if (wrkFiles.isEmpty()) { + String message = "CRITICAL: No WRK result files (*%s) found in %s. Ensure benchmarks were run successfully." + .formatted(WRK_OUTPUT_FILE_SUFFIX, inputDir); + LOGGER.error(ERROR.NO_WRK_FILES, inputDir); + throw new IllegalStateException(message); + } + + for (Path wrkFile : wrkFiles) { + parseSingleBenchmarkMetadata(wrkFile); + } + + if (benchmarkMetadataMap.isEmpty()) { + String message = "CRITICAL: No valid benchmark metadata found in any result files"; + LOGGER.error(ERROR.NO_BENCHMARK_DATA); + throw new IllegalStateException(message); + } + } + } + + /** + * Parse metadata from a single WRK result file. + * + * @param resultFile Path to the WRK result file + */ + private void parseSingleBenchmarkMetadata(Path resultFile) { + try { + List lines = Files.readAllLines(resultFile); + String benchmarkName = null; + Instant startTime = null; + Instant endTime = null; + boolean inMetadata = false; + + for (String line : lines) { + if ("=== BENCHMARK METADATA ===".equals(line)) { + inMetadata = true; + } else if ("=== WRK OUTPUT ===".equals(line)) { + inMetadata = false; + } else if (inMetadata || line.startsWith("end_time:")) { + if (line.startsWith("benchmark_name: ")) { + benchmarkName = line.substring(16).trim(); + } else if (line.startsWith("start_time: ")) { + long epochSeconds = Long.parseLong(line.substring(12).trim()); + startTime = Instant.ofEpochSecond(epochSeconds); + } else if (line.startsWith("end_time: ")) { + long epochSeconds = Long.parseLong(line.substring(10).trim()); + endTime = Instant.ofEpochSecond(epochSeconds); + } + } + } + + if (benchmarkName == null || startTime == null || endTime == null) { + LOGGER.error(ERROR.INCOMPLETE_METADATA, resultFile, benchmarkName, startTime, endTime); + return; + } + + BenchmarkMetadata metadata = new BenchmarkMetadata(benchmarkName, startTime, endTime); + benchmarkMetadataMap.put(benchmarkName, metadata); + + } catch (IOException | NumberFormatException e) { + LOGGER.error(ERROR.FAILED_PARSE_METADATA, resultFile, e.getMessage()); + } + } + + /** + * Find metadata for a benchmark based on its result file name. + * + * @param benchmarkFileName The benchmark file name from BenchmarkData + * @return The matching metadata or null if not found + */ + private BenchmarkMetadata findMetadataForBenchmark(String benchmarkFileName) { + if (benchmarkMetadataMap.containsKey(benchmarkFileName)) { + return benchmarkMetadataMap.get(benchmarkFileName); + } + + String baseName = benchmarkFileName; + if (baseName.endsWith(".txt")) { + baseName = baseName.substring(0, baseName.length() - 4); + } + if (baseName.endsWith("-results")) { + baseName = baseName.substring(0, baseName.length() - 8); + } + + for (Map.Entry entry : benchmarkMetadataMap.entrySet()) { + if (entry.getKey().equals(baseName) || + entry.getKey().contains(baseName) || + baseName.contains(entry.getKey())) { + return entry.getValue(); + } + } + + if (benchmarkMetadataMap.size() == 1) { + return benchmarkMetadataMap.values().iterator().next(); + } + + return null; + } +} diff --git a/benchmarks/src/main/resources/wrk-scripts/api_health_benchmark.sh b/benchmarks/src/main/resources/wrk-scripts/api_health_benchmark.sh new file mode 100755 index 00000000..70361340 --- /dev/null +++ b/benchmarks/src/main/resources/wrk-scripts/api_health_benchmark.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Benchmark runner for /api/health endpoint +set -e + +WRK_THREADS="${WRK_THREADS:-5}" +WRK_CONNECTIONS="${WRK_CONNECTIONS:-50}" +WRK_DURATION="${WRK_DURATION:-60s}" +WRK_TIMEOUT="${WRK_TIMEOUT:-2s}" +COMPOSE_DIR="${COMPOSE_DIR:?COMPOSE_DIR must be set}" + +BENCHMARK_NAME="gatewayHealth" +TARGET_URL="https://api-sheriff:8443/api/health" + +echo "=== BENCHMARK METADATA ===" +echo "benchmark_name: ${BENCHMARK_NAME}" +echo "target_url: ${TARGET_URL}" +echo "threads: ${WRK_THREADS}" +echo "connections: ${WRK_CONNECTIONS}" +echo "duration: ${WRK_DURATION}" +echo "start_time: $(date +%s)" +echo "=== WRK OUTPUT ===" + +cd "${COMPOSE_DIR}" +docker compose run --rm wrk \ + -t"${WRK_THREADS}" \ + -c"${WRK_CONNECTIONS}" \ + -d"${WRK_DURATION}" \ + --timeout "${WRK_TIMEOUT}" \ + --latency \ + -s /scripts/api_health_check.lua \ + "${TARGET_URL}" + +echo "end_time: $(date +%s)" +echo "=== BENCHMARK COMPLETE ===" diff --git a/benchmarks/src/main/resources/wrk-scripts/api_health_check.lua b/benchmarks/src/main/resources/wrk-scripts/api_health_check.lua new file mode 100644 index 00000000..2993d4a9 --- /dev/null +++ b/benchmarks/src/main/resources/wrk-scripts/api_health_check.lua @@ -0,0 +1,27 @@ +-- api_health_check.lua +-- WRK Lua script for benchmarking /api/health endpoint +-- Sends GET requests with JSON Accept header and validates 200 response + +local error_count = 0 +local success_count = 0 +local non_200_count = 0 + +wrk.method = "GET" +wrk.headers["Accept"] = "application/json" + +function response(status, headers, body) + if status == 200 then + success_count = success_count + 1 + else + non_200_count = non_200_count + 1 + end +end + +function done(summary, latency, requests) + io.write("--- Lua Script Summary ---\n") + io.write(string.format("Successful requests (200): %d\n", success_count)) + io.write(string.format("Non-200 responses: %d\n", non_200_count)) + io.write(string.format("Socket errors: connect=%d, read=%d, write=%d, timeout=%d\n", + summary.errors.connect, summary.errors.read, + summary.errors.write, summary.errors.timeout)) +end diff --git a/benchmarks/src/main/resources/wrk-scripts/health_live_benchmark.sh b/benchmarks/src/main/resources/wrk-scripts/health_live_benchmark.sh new file mode 100755 index 00000000..999e3711 --- /dev/null +++ b/benchmarks/src/main/resources/wrk-scripts/health_live_benchmark.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Benchmark runner for /q/health/live endpoint +set -e + +WRK_THREADS="${WRK_THREADS:-5}" +WRK_CONNECTIONS="${WRK_CONNECTIONS:-50}" +WRK_DURATION="${WRK_DURATION:-60s}" +WRK_TIMEOUT="${WRK_TIMEOUT:-2s}" +COMPOSE_DIR="${COMPOSE_DIR:?COMPOSE_DIR must be set}" + +BENCHMARK_NAME="healthLiveCheck" +TARGET_URL="http://api-sheriff:9000/q/health/live" + +echo "=== BENCHMARK METADATA ===" +echo "benchmark_name: ${BENCHMARK_NAME}" +echo "target_url: ${TARGET_URL}" +echo "threads: ${WRK_THREADS}" +echo "connections: ${WRK_CONNECTIONS}" +echo "duration: ${WRK_DURATION}" +echo "start_time: $(date +%s)" +echo "=== WRK OUTPUT ===" + +cd "${COMPOSE_DIR}" +docker compose run --rm wrk \ + -t"${WRK_THREADS}" \ + -c"${WRK_CONNECTIONS}" \ + -d"${WRK_DURATION}" \ + --timeout "${WRK_TIMEOUT}" \ + --latency \ + -s /scripts/health_live_check.lua \ + "${TARGET_URL}" + +echo "end_time: $(date +%s)" +echo "=== BENCHMARK COMPLETE ===" diff --git a/benchmarks/src/main/resources/wrk-scripts/health_live_check.lua b/benchmarks/src/main/resources/wrk-scripts/health_live_check.lua new file mode 100644 index 00000000..200c92c2 --- /dev/null +++ b/benchmarks/src/main/resources/wrk-scripts/health_live_check.lua @@ -0,0 +1,27 @@ +-- health_live_check.lua +-- WRK Lua script for benchmarking /q/health/live endpoint +-- Sends GET requests with JSON Accept header and tracks errors + +local error_count = 0 +local success_count = 0 +local non_200_count = 0 + +wrk.method = "GET" +wrk.headers["Accept"] = "application/json" + +function response(status, headers, body) + if status == 200 then + success_count = success_count + 1 + else + non_200_count = non_200_count + 1 + end +end + +function done(summary, latency, requests) + io.write("--- Lua Script Summary ---\n") + io.write(string.format("Successful requests (200): %d\n", success_count)) + io.write(string.format("Non-200 responses: %d\n", non_200_count)) + io.write(string.format("Socket errors: connect=%d, read=%d, write=%d, timeout=%d\n", + summary.errors.connect, summary.errors.read, + summary.errors.write, summary.errors.timeout)) +end diff --git a/benchmarks/src/main/resources/wrk-scripts/pre-benchmark-health-check.sh b/benchmarks/src/main/resources/wrk-scripts/pre-benchmark-health-check.sh new file mode 100755 index 00000000..0e47ea9e --- /dev/null +++ b/benchmarks/src/main/resources/wrk-scripts/pre-benchmark-health-check.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Pre-flight check: verify Quarkus, Prometheus, and Keycloak are reachable before benchmarks start +set -e + +INTEGRATION_SERVICE_URL="${INTEGRATION_SERVICE_URL:?INTEGRATION_SERVICE_URL must be set}" +MANAGEMENT_URL="${MANAGEMENT_URL:?MANAGEMENT_URL must be set}" +PROMETHEUS_URL="${PROMETHEUS_URL:?PROMETHEUS_URL must be set}" +KEYCLOAK_URL="${KEYCLOAK_URL:?KEYCLOAK_URL must be set}" + +MAX_RETRIES=30 +RETRY_INTERVAL=2 + +check_service() { + local name="$1" + local url="$2" + local retries=0 + + echo "Checking ${name} at ${url}..." + while [ $retries -lt $MAX_RETRIES ]; do + if curl -k -s -o /dev/null -w "%{http_code}" "$url" | grep -q "200"; then + echo "${name} is ready." + return 0 + fi + retries=$((retries + 1)) + echo "Waiting for ${name}... (attempt ${retries}/${MAX_RETRIES})" + sleep $RETRY_INTERVAL + done + + echo "ERROR: ${name} at ${url} did not become ready within $((MAX_RETRIES * RETRY_INTERVAL))s" + return 1 +} + +echo "=== Pre-Benchmark Health Check ===" + +check_service "Quarkus (health/live)" "${MANAGEMENT_URL}/q/health/live" +check_service "Prometheus" "${PROMETHEUS_URL}/-/ready" +check_service "Keycloak" "${KEYCLOAK_URL}/health/ready" + +echo "=== All services are ready. Proceeding with benchmarks. ===" diff --git a/benchmarks/src/test/java/de/cuioss/sheriff/api/wrk/benchmark/GatewayEndpointBenchmarkTest.java b/benchmarks/src/test/java/de/cuioss/sheriff/api/wrk/benchmark/GatewayEndpointBenchmarkTest.java new file mode 100644 index 00000000..122d3e98 --- /dev/null +++ b/benchmarks/src/test/java/de/cuioss/sheriff/api/wrk/benchmark/GatewayEndpointBenchmarkTest.java @@ -0,0 +1,162 @@ +/* + * Copyright ยฉ 2025 CUI-OpenSource-Software (info@cuioss.de) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.cuioss.sheriff.api.wrk.benchmark; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.*; + + +/** + * Tests WRK output parsing for the {@code /q/health/live} and {@code /api/health} + * gateway endpoints. Uses synthetic WRK output to validate the parsing pipeline + * without requiring running containers. + * + * @author API Sheriff Team + * @since 1.0 + */ +class GatewayEndpointBenchmarkTest { + + @TempDir + Path tempDir; + + private WrkResultPostProcessor processor; + + @BeforeEach + void setUp() { + processor = new WrkResultPostProcessor(); + } + + @Test + void parseHealthLiveBenchmark() throws Exception { + // Arrange โ€” synthetic WRK output for /q/health/live endpoint + String wrkOutput = """ + === BENCHMARK METADATA === + benchmark_name: healthLiveCheck + start_time: 1700000000 + start_time_iso: 2023-11-14T22:13:20Z + === WRK OUTPUT === + + Running 30s test @ https://api-sheriff:8443/q/health/live + 4 threads and 50 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 650.00us 300.00us 8.00ms 92.00% + Req/Sec 7.50k 800.00 12.00k 70.00% + Latency Distribution + 50% 600.00us + 75% 0.85ms + 90% 1.10ms + 99% 2.50ms + 450000 requests in 30.00s, 120.00MB read + Requests/sec: 15000.00 + Transfer/sec: 4.00MB + + === BENCHMARK COMPLETE === + end_time: 1700000030 + end_time_iso: 2023-11-14T22:13:50Z + duration_seconds: 30 + """; + + Path wrkDir = tempDir.resolve("wrk"); + Files.createDirectories(wrkDir); + Files.writeString(wrkDir.resolve("wrk-health-live-results.txt"), wrkOutput); + + // Act + Path outputDir = tempDir.resolve("output"); + processor.process(tempDir, outputDir); + + // Assert โ€” verify JSON structure and percentile ordering + Path jsonFile = outputDir.resolve("gh-pages-ready/data/benchmark-data.json"); + assertTrue(Files.exists(jsonFile), "Benchmark data JSON should be created"); + + JsonObject json = JsonParser.parseString(Files.readString(jsonFile)).getAsJsonObject(); + JsonObject benchmark = json.getAsJsonArray("benchmarks").get(0).getAsJsonObject(); + + assertEquals("healthLiveCheck", benchmark.get("name").getAsString()); + assertTrue(benchmark.has("score"), "Benchmark should have score"); + assertTrue(benchmark.has("scoreUnit"), "Benchmark should have scoreUnit"); + + JsonObject percentiles = benchmark.getAsJsonObject("percentiles"); + assertTrue(percentiles.get("50.0").getAsDouble() <= percentiles.get("75.0").getAsDouble(), + "P50 should be <= P75"); + assertTrue(percentiles.get("75.0").getAsDouble() <= percentiles.get("90.0").getAsDouble(), + "P75 should be <= P90"); + assertTrue(percentiles.get("90.0").getAsDouble() <= percentiles.get("99.0").getAsDouble(), + "P90 should be <= P99"); + } + + @Test + void parseApiHealthBenchmark() throws Exception { + // Arrange โ€” synthetic WRK output for /api/health endpoint + String wrkOutput = """ + === BENCHMARK METADATA === + benchmark_name: gatewayHealth + start_time: 1700000000 + start_time_iso: 2023-11-14T22:13:20Z + === WRK OUTPUT === + + Running 30s test @ https://api-sheriff:8443/api/health + 4 threads and 50 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 700.00us 350.00us 9.00ms 91.00% + Req/Sec 7.00k 750.00 11.00k 72.00% + Latency Distribution + 50% 650.00us + 75% 0.90ms + 90% 1.20ms + 99% 2.80ms + 420000 requests in 30.00s, 110.00MB read + Requests/sec: 14000.00 + Transfer/sec: 3.67MB + + === BENCHMARK COMPLETE === + end_time: 1700000030 + end_time_iso: 2023-11-14T22:13:50Z + duration_seconds: 30 + """; + + Path wrkDir = tempDir.resolve("wrk"); + Files.createDirectories(wrkDir); + Files.writeString(wrkDir.resolve("wrk-api-health-results.txt"), wrkOutput); + + // Act + Path outputDir = tempDir.resolve("output"); + processor.process(tempDir, outputDir); + + // Assert โ€” verify parsing succeeds with different latency values + Path jsonFile = outputDir.resolve("gh-pages-ready/data/benchmark-data.json"); + assertTrue(Files.exists(jsonFile), "Benchmark data JSON should be created"); + + JsonObject json = JsonParser.parseString(Files.readString(jsonFile)).getAsJsonObject(); + JsonObject benchmark = json.getAsJsonArray("benchmarks").get(0).getAsJsonObject(); + + assertEquals("gatewayHealth", benchmark.get("name").getAsString()); + assertFalse(benchmark.get("score").getAsString().isEmpty(), "Score should not be empty"); + + JsonObject percentiles = benchmark.getAsJsonObject("percentiles"); + double p50 = percentiles.get("50.0").getAsDouble(); + double p99 = percentiles.get("99.0").getAsDouble(); + assertTrue(p50 > 0, "P50 should be positive"); + assertTrue(p50 <= p99, "P50 should be <= P99"); + } +} diff --git a/benchmarks/src/test/java/de/cuioss/sheriff/api/wrk/benchmark/WrkResultPostProcessorTest.java b/benchmarks/src/test/java/de/cuioss/sheriff/api/wrk/benchmark/WrkResultPostProcessorTest.java new file mode 100644 index 00000000..e6bd2353 --- /dev/null +++ b/benchmarks/src/test/java/de/cuioss/sheriff/api/wrk/benchmark/WrkResultPostProcessorTest.java @@ -0,0 +1,269 @@ +/* + * Copyright ยฉ 2025 CUI-OpenSource-Software (info@cuioss.de) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.cuioss.sheriff.api.wrk.benchmark; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Unit tests for {@link WrkResultPostProcessor}. + * Tests that the processor correctly parses WRK output format, + * not specific values that change with each benchmark run. + */ +class WrkResultPostProcessorTest { + + @TempDir + Path tempDir; + + private WrkResultPostProcessor processor; + + @BeforeEach + void setUp() { + processor = new WrkResultPostProcessor(); + } + + @Test + void comprehensiveStructureGeneration() throws Exception { + // Copy real benchmark outputs to temp directory wrk subdirectory + Path wrkDir = tempDir.resolve("wrk"); + Files.createDirectories(wrkDir); + Path healthSource = Path.of("src/test/resources/wrk-health-results.txt"); + Path apiHealthSource = Path.of("src/test/resources/wrk-api-health-results.txt"); + Files.copy(healthSource, wrkDir.resolve("wrk-health-results.txt")); + Files.copy(apiHealthSource, wrkDir.resolve("wrk-api-health-results.txt")); + + // Process results + Path outputDir = tempDir.resolve("output"); + processor.process(tempDir, outputDir); + + // Verify comprehensive structure matching JMH benchmarks + verifyComprehensiveStructure(); + } + + @Test + void parseHealthLiveOutput() throws Exception { + Path wrkDir = tempDir.resolve("wrk"); + Files.createDirectories(wrkDir); + Path sourceFile = Path.of("src/test/resources/wrk-health-results.txt"); + Path targetFile = wrkDir.resolve("wrk-health-results.txt"); + Files.copy(sourceFile, targetFile); + + Path outputDir = tempDir.resolve("output"); + processor.process(tempDir, outputDir); + + Path jsonFile = outputDir.resolve("gh-pages-ready/data/benchmark-data.json"); + assertTrue(Files.exists(jsonFile), "Benchmark data file should be created"); + + String jsonContent = Files.readString(jsonFile); + JsonObject json = JsonParser.parseString(jsonContent).getAsJsonObject(); + + assertTrue(json.has("metadata")); + JsonObject metadata = json.getAsJsonObject("metadata"); + assertTrue(metadata.has("timestamp")); + assertTrue(metadata.has("displayTimestamp")); + assertEquals("Integration Performance", metadata.get("benchmarkType").getAsString()); + assertEquals("2.0", metadata.get("reportVersion").getAsString()); + + assertTrue(json.has("benchmarks")); + JsonObject healthBenchmark = json.getAsJsonArray("benchmarks").get(0).getAsJsonObject(); + assertEquals("healthLiveCheck", healthBenchmark.get("name").getAsString()); + assertTrue(healthBenchmark.has("mode")); + assertTrue(healthBenchmark.has("score")); + assertTrue(healthBenchmark.has("scoreUnit")); + + JsonObject percentiles = healthBenchmark.getAsJsonObject("percentiles"); + assertTrue(percentiles.has("50.0")); + assertTrue(percentiles.has("75.0")); + assertTrue(percentiles.has("90.0")); + assertTrue(percentiles.has("99.0")); + + double p50 = percentiles.get("50.0").getAsDouble(); + double p75 = percentiles.get("75.0").getAsDouble(); + double p90 = percentiles.get("90.0").getAsDouble(); + double p99 = percentiles.get("99.0").getAsDouble(); + + assertTrue(p50 <= p75, "P50 should be <= P75"); + assertTrue(p75 <= p90, "P75 should be <= P90"); + assertTrue(p90 <= p99, "P90 should be <= P99"); + } + + @Test + void parseApiHealthOutput() throws Exception { + Path wrkDir = tempDir.resolve("wrk"); + Files.createDirectories(wrkDir); + Path sourceFile = Path.of("src/test/resources/wrk-api-health-results.txt"); + Path targetFile = wrkDir.resolve("wrk-api-health-results.txt"); + Files.copy(sourceFile, targetFile); + + Path outputDir = tempDir.resolve("output"); + processor.process(tempDir, outputDir); + + Path jsonFile = outputDir.resolve("gh-pages-ready/data/benchmark-data.json"); + String jsonContent = Files.readString(jsonFile); + JsonObject json = JsonParser.parseString(jsonContent).getAsJsonObject(); + + JsonObject apiHealthBenchmark = null; + var benchmarks = json.getAsJsonArray("benchmarks"); + for (int i = 0; i < benchmarks.size(); i++) { + var bench = benchmarks.get(i).getAsJsonObject(); + String benchName = bench.get("name").getAsString(); + if ("gatewayHealth".equals(benchName)) { + apiHealthBenchmark = bench; + break; + } + } + + assertNotNull(apiHealthBenchmark, "Gateway Health benchmark should be present"); + assertTrue(apiHealthBenchmark.has("mode")); + assertTrue(apiHealthBenchmark.has("score")); + assertTrue(apiHealthBenchmark.has("scoreUnit")); + + JsonObject percentiles = apiHealthBenchmark.getAsJsonObject("percentiles"); + assertTrue(percentiles.has("50.0")); + assertTrue(percentiles.has("75.0")); + assertTrue(percentiles.has("90.0")); + assertTrue(percentiles.has("99.0")); + + double p50 = percentiles.get("50.0").getAsDouble(); + double p75 = percentiles.get("75.0").getAsDouble(); + double p90 = percentiles.get("90.0").getAsDouble(); + double p99 = percentiles.get("99.0").getAsDouble(); + + assertTrue(p50 <= p75, "P50 should be <= P75"); + assertTrue(p75 <= p90, "P75 should be <= P90"); + assertTrue(p90 <= p99, "P90 should be <= P99"); + } + + @Test + void missingFileHandling() throws Exception { + Path wrkDir = tempDir.resolve("wrk"); + Files.createDirectories(wrkDir); + Path outputDir = tempDir.resolve("output"); + assertThrows(IllegalStateException.class, () -> processor.process(tempDir, outputDir)); + + Path jsonFile = outputDir.resolve("gh-pages-ready/data/benchmark-data.json"); + assertFalse(Files.exists(jsonFile), "JSON should not be created with missing inputs"); + } + + @Test + void parseRealWrkFormatVariations() throws Exception { + String wrkOutput = """ + === BENCHMARK METADATA === + benchmark_name: format-test + start_time: 1700000000 + start_time_iso: 2023-11-14T22:13:20Z + === WRK OUTPUT === + + Running 10s test @ https://localhost:10443/test + 4 threads and 20 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 849.00us 500.00us 10.00ms 90.00% + Req/Sec 5.00k 1.00k 10.00k 75.00% + Latency Distribution + 50% 805.00us + 75% 1.08ms + 90% 1.54ms + 99% 4.01ms + 100000 requests in 10.00s, 50.00MB read + Requests/sec: 10000.00 + Transfer/sec: 5.00MB + + === BENCHMARK COMPLETE === + end_time: 1700000010 + end_time_iso: 2023-11-14T22:13:30Z + duration_seconds: 10 + """; + + Path wrkDir = tempDir.resolve("wrk"); + Files.createDirectories(wrkDir); + Path testFile = wrkDir.resolve("wrk-format-test-results.txt"); + Files.writeString(testFile, wrkOutput); + + Path outputDir = tempDir.resolve("output"); + processor.process(tempDir, outputDir); + + Path jsonFile = outputDir.resolve("gh-pages-ready/data/benchmark-data.json"); + JsonObject json = JsonParser.parseString(Files.readString(jsonFile)).getAsJsonObject(); + JsonObject benchmark = json.getAsJsonArray("benchmarks").get(0).getAsJsonObject(); + + assertTrue(benchmark.has("name")); + assertTrue(benchmark.has("mode")); + assertTrue(benchmark.has("score")); + assertTrue(benchmark.has("scoreUnit")); + + JsonObject percentiles = benchmark.getAsJsonObject("percentiles"); + double p50 = percentiles.get("50.0").getAsDouble(); + assertTrue(p50 > 0, "P50 should be positive"); + } + + /** + * Verify that WRK generates the same comprehensive structure as JMH benchmarks + */ + private void verifyComprehensiveStructure() throws IOException { + Path outputDir = tempDir.resolve("output"); + Path ghPagesDir = outputDir.resolve("gh-pages-ready"); + assertTrue(Files.exists(ghPagesDir), "GitHub Pages directory should be created"); + + assertTrue(Files.exists(ghPagesDir.resolve("index.html")), "index.html should be created"); + assertTrue(Files.exists(ghPagesDir.resolve("trends.html")), "trends.html should be created"); + + assertTrue(Files.exists(ghPagesDir.resolve("report-styles.css")), "CSS file should exist"); + assertTrue(Files.exists(ghPagesDir.resolve("data-loader.js")), "JavaScript file should exist"); + + Path badgesDir = ghPagesDir.resolve("badges"); + assertTrue(Files.exists(badgesDir), "Badges directory should be created"); + + Path dataFile = ghPagesDir.resolve("data/benchmark-data.json"); + assertTrue(Files.exists(dataFile), "benchmark-data.json should be created"); + + String jsonContent = Files.readString(dataFile); + JsonObject json = JsonParser.parseString(jsonContent).getAsJsonObject(); + + assertTrue(json.has("metadata"), "Should have metadata section"); + JsonObject metadata = json.getAsJsonObject("metadata"); + assertTrue(metadata.has("timestamp"), "Should have timestamp"); + assertTrue(metadata.has("displayTimestamp"), "Should have displayTimestamp"); + assertTrue(metadata.has("benchmarkType"), "Should have benchmarkType"); + assertTrue(metadata.has("reportVersion"), "Should have reportVersion"); + + assertTrue(json.has("overview"), "Should have overview section"); + JsonObject overview = json.getAsJsonObject("overview"); + assertTrue(overview.has("performanceScore"), "Should have performance score"); + assertTrue(overview.has("performanceGrade"), "Should have performance grade"); + + assertTrue(json.has("benchmarks"), "Should have benchmarks array"); + assertTrue(json.getAsJsonArray("benchmarks").size() > 0, "Should have at least one benchmark"); + + assertTrue(json.has("chartData"), "Should have chart data section"); + assertTrue(json.has("trends"), "Should have trends section"); + + JsonObject benchmark = json.getAsJsonArray("benchmarks").get(0).getAsJsonObject(); + assertTrue(benchmark.has("name"), "Benchmark should have name"); + assertTrue(benchmark.has("mode"), "Benchmark should have mode"); + assertTrue(benchmark.has("score"), "Benchmark should have score"); + assertTrue(benchmark.has("scoreUnit"), "Benchmark should have scoreUnit"); + assertTrue(benchmark.has("percentiles"), "Benchmark should have percentiles"); + } +} diff --git a/benchmarks/src/test/resources/wrk-api-health-results.txt b/benchmarks/src/test/resources/wrk-api-health-results.txt new file mode 100644 index 00000000..78bca017 --- /dev/null +++ b/benchmarks/src/test/resources/wrk-api-health-results.txt @@ -0,0 +1,24 @@ +=== BENCHMARK METADATA === +benchmark_name: gatewayHealth +start_time: 1758797251 +start_time_iso: 2025-09-25T12:47:31+02:00 +=== WRK OUTPUT === + +Running 5s test @ https://api-sheriff:8443/api/health + 4 threads and 20 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 4.33ms 6.30ms 42.53ms 87.28% + Req/Sec 2.16k 283.91 2.54k 85.00% + Latency Distribution + 50% 1.73ms + 75% 2.97ms + 90% 14.05ms + 99% 28.27ms + 43069 requests in 5.01s, 11.13MB read +Requests/sec: 8592.89 +Transfer/sec: 2.22MB + +=== BENCHMARK COMPLETE === +end_time: 1758797257 +end_time_iso: 2025-09-25T12:47:37+02:00 +duration_seconds: 6 diff --git a/benchmarks/src/test/resources/wrk-health-results.txt b/benchmarks/src/test/resources/wrk-health-results.txt new file mode 100644 index 00000000..ef074289 --- /dev/null +++ b/benchmarks/src/test/resources/wrk-health-results.txt @@ -0,0 +1,24 @@ +=== BENCHMARK METADATA === +benchmark_name: healthLiveCheck +start_time: 1758797205 +start_time_iso: 2025-09-25T12:46:45+02:00 +=== WRK OUTPUT === + +Running 5s test @ https://api-sheriff:8443/q/health/live + 4 threads and 20 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 4.51ms 6.70ms 41.04ms 87.50% + Req/Sec 2.14k 387.08 2.60k 89.00% + Latency Distribution + 50% 1.69ms + 75% 3.18ms + 90% 14.60ms + 99% 29.99ms + 42606 requests in 5.01s, 11.01MB read +Requests/sec: 8511.49 +Transfer/sec: 2.20MB + +=== BENCHMARK COMPLETE === +end_time: 1758797211 +end_time_iso: 2025-09-25T12:46:51+02:00 +duration_seconds: 6 diff --git a/bom/pom.xml b/bom/pom.xml deleted file mode 100644 index 1c416487..00000000 --- a/bom/pom.xml +++ /dev/null @@ -1,83 +0,0 @@ - - - 4.0.0 - - de.cuioss.sheriff.api - api-sheriff-parent - 1.0.0-SNAPSHOT - ../pom.xml - - - bom - pom - API Sheriff BOM - Bill of Materials (BOM) for API Sheriff modules - - 0.12.6 - - - - - - de.cuioss - java-ee-orthogonal - ${version.cui.parent} - pom - import - - - de.cuioss - java-ee-10-bom - ${version.cui.parent} - pom - import - - - - io.jsonwebtoken - jjwt-api - ${version.jjwt} - test - - - io.jsonwebtoken - jjwt-impl - test - ${version.jjwt} - - - io.jsonwebtoken - jjwt-jackson - ${version.jjwt} - test - - - - - de.cuioss.sheriff.api - api-sheriff-library - ${project.version} - - - de.cuioss.sheriff.api - api-sheriff-library - ${project.version} - test-jar - generators - test - - - de.cuioss.sheriff.api - benchmarking - ${project.version} - - - de.cuioss.sheriff.api - api-sheriff-quarkus - ${project.version} - - - - \ No newline at end of file diff --git a/doc/ai-rules.md b/doc/ai-rules.md deleted file mode 100644 index 145779c3..00000000 --- a/doc/ai-rules.md +++ /dev/null @@ -1,369 +0,0 @@ -# AI Development Guidelines - -This file provides guidance to AI tools (IntelliJ Junie, Claude Code, GitHub Copilot, etc.) when working with code in CUI projects. - -## Configuration - -**Standards Base URL**: Configure this based on your development environment: -- **Remote (gitingest)**: `https://gitingest.com/github.com/cuioss/cui-llm-rules/tree/main` -- **Local checkout**: Use relative path to your local standards directory (e.g., `../cui-llm-rules`) - -Replace `{STANDARDS_BASE_URL}` in all references below with your chosen base URL. - -## Context Hierarchy and Priority Framework -**Critical for AI System Decision Making** - -When conflicting information exists, AI systems must follow this priority order: - -1. **Core Process Rules** (CRITICAL - highest priority, non-negotiable) -2. **Project-Specific Context** (CLAUDE.md, .github/copilot-instructions.md, local config) -3. **Standards References** (adaptable based on context and requirements) -4. **General Guidelines** (lowest priority, may be overridden by higher levels) - -### Context-Aware Response Patterns -- **New Project Context**: Emphasize architecture decisions and initial setup standards -- **Legacy Code Context**: Prioritize compatibility, incremental changes, and migration paths -- **Testing Context**: Focus on coverage requirements and quality standards -- **Documentation Context**: Emphasize clarity, completeness, and AsciiDoc standards -- **Security Context**: Apply strictest security standards without compromise - -## Core Process Rules (CRITICAL - READ FIRST) -**Reference**: `{STANDARDS_BASE_URL}/standards/process/general.adoc` - -These rules govern ALL development activities: - -### ๐Ÿšจ PRE-1.0 PROJECT RULE (HIGHEST PRIORITY) -**This project is PRE-1.0 and therefore:** -- **NEVER deprecate code** - Remove it directly if not needed -- **NEVER add transitional comments** like "TODO: Remove in v2.0" -- **NEVER enforce backward compatibility** - Make breaking changes freely -- **NEVER add @Deprecated annotations** - Delete unnecessary code immediately -- **Clean APIs aggressively** - Remove unused methods, classes, and patterns -- **Focus on final API design** - Design for post-1.0 stability, not pre-1.0 transitions - -### General Process Rules -1. **If in doubt, ask the user** - Never make assumptions -2. **Always research topics** - Use available tools (WebSearch, WebFetch, etc.) to find the most recent best practices -3. **Never guess or be creative** - If you cannot find best practices, ask the user -4. **Do not proliferate documents** - Always use context-relevant documents, never create without user approval -5. **Never add dependencies without approval** - Always ask before adding any dependency - -## AI Safety and Validation Framework -**Mandatory for All AI-Generated Content** - -### Safety Constraints (NON-NEGOTIABLE) -- **Never bypass security measures**: AI must not suggest workarounds for security controls -- **Preserve data integrity**: All changes must maintain data consistency -- **Respect privacy**: No exposure of sensitive data in logs or outputs -- **Maintain auditability**: All AI-generated changes must be traceable -- **Follow standards hierarchy**: Respect the context priority framework above - -### Validation Requirements -Before any code implementation: -1. **Standards Compliance Check**: Verify output matches CUI standards -2. **Build Verification**: Ensure generated code compiles and passes pre-commit checks -3. **Security Review**: Check for security anti-patterns and vulnerabilities -4. **Documentation Sync**: Verify documentation reflects any code changes -5. **Test Coverage**: Ensure adequate test coverage for new functionality - -### Error Recovery Patterns -- **Standards Violation**: Provide specific correction guidance with reference links -- **Build Failures**: Include diagnostic steps and reference Build Commands Template -- **Test Failures**: Guide through debugging using Testing Standards -- **Integration Issues**: Escalate to human review with detailed context - -## Task Completion Standards (MANDATORY) -**Reference**: `{STANDARDS_BASE_URL}/standards/process/task-completion-standards.adoc` - -### Pre-Commit Checklist -Execute in sequence before ANY commit: - -1. **Quality Verification**: `./mvnw -Ppre-commit clean verify -DskipTests` - - Fix ALL errors and warnings (mandatory) - - Address code quality, formatting, and linting issues - -2. **Final Verification**: `./mvnw clean install` - - Must complete without errors or warnings - - All tests must pass - - Tasks are complete ONLY after this succeeds - -3. **Run Integration Tests**: `./mvnw clean verify -Pintegration-tests -pl api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests` - - Ensure all integration tests pass - - Verify against the latest standards - -4. **Documentation**: Ensure all changes are documented - - Update Javadoc for public APIs - - Update AsciiDoc documentation if necessary - -5. **Documentation**: Update if changes affect APIs, features, or configuration - -6. **Commit Message**: Follow Git Commit Standards - -### Quality Requirements -- New code requires appropriate test coverage -- Existing tests must continue to pass -- Documentation must be updated for API/feature changes -- Link commits to relevant issues or tasks - -## Build Commands Template -Common Maven commands for CUI projects: -- Build project: `./mvnw clean install` -- Build Single Module: `./mvnw clean install -pl ` -- Run tests: `./mvnw test` -- Run single test: `./mvnw test -Dtest=ClassName#methodName` -- Clean-Up Code: `./mvnw -Ppre-commit clean install -DskipTests` -> Check the console after running the command and fix all errors and warnings, verify until they are all corrected - -## Standards Overview -**Base Reference**: `{STANDARDS_BASE_URL}/standards` - -## Java Standards -**References**: -- Java Code Standards: `{STANDARDS_BASE_URL}/standards/java` -- DSL-Style Constants: `{STANDARDS_BASE_URL}/standards/java/dsl-style-constants.adoc` - -### Language Standards -- Use latest Java LTS version features (Java 17+ minimum) -- Use records for data carriers and DTOs -- Use switch expressions over classic switch statements -- Use text blocks for multi-line strings -- Use var for local variables with obvious types -- Use sealed classes for restricted hierarchies -- Pattern matching in instanceof and switch -- Stream API for complex data transformations -- Use proper access modifiers (prefer package-private over public) -- Mark classes final unless designed for inheritance -- Prefer composition over inheritance -- Return empty collections instead of null -- Use Optional for nullable return values -- Never catch or throw generic Exception or RuntimeException - always use specific exception types -- Use DSL-style nested constants for logging messages -- Follow builder pattern for complex object creation -- Implement fluent interfaces where appropriate -- Use method references over lambdas when possible -- Keep lambda expressions short and clear -- Avoid side effects in streams -- Use immutable objects when possible -- Make fields final by default -- Use enum instead of constants for fixed sets -- Prefer immutable collections (List.of(), Set.of()) -- Avoid magic numbers, use named constants -- Use StringBuilder for string concatenation in loops -- Override toString() for debugging -- Implement equals() and hashCode() together -- Use @Override annotation consistently -- Avoid premature optimization -- See Lombok Usage section for annotation patterns - -### Lombok Usage -**Reference**: `{STANDARDS_BASE_URL}/standards/java/java-code-standards.adoc` -- Use `@Builder` for complex object creation -- Use `@Value` for immutable objects -- Use `@NonNull` for required parameters -- Use `@ToString` and `@EqualsAndHashCode` for value objects -- Use `@UtilityClass` for utility classes -- Make proper use of `lombok.config` settings - -## Logging Standards -**References**: -- Logging Core Standards: `{STANDARDS_BASE_URL}/standards/logging` -- Logging Implementation Guide: `{STANDARDS_BASE_URL}/standards/logging/implementation-guide.adoc` -- Logging Testing Guide: `{STANDARDS_BASE_URL}/standards/logging/testing-guide.adoc` -- Use `de.cuioss.tools.logging.CuiLogger` (private static final LOGGER) -- Logger must be private static final with constant name 'LOGGER' -- Module/artifact: cui-java-tools -- Exception parameter always comes first in logging methods -- Use '%s' for string substitutions (not '{}' or '%d') -- Use `de.cuioss.tools.logging.LogRecord` for template logging -- Follow logging level ranges: INFO (001-99), WARN (100-199), ERROR (200-299), FATAL (300-399) -- All log messages must be documented in doc/LogMessages.adoc -- No log4j, slf4j, System.out, or System.err usage - -## Testing Standards -**References**: -- Testing Core Standards: `{STANDARDS_BASE_URL}/standards/testing` -- Quality Standards: `{STANDARDS_BASE_URL}/standards/testing/quality-standards.adoc` -- CUI Test Generator Guide: `https://gitingest.com/github.com/cuioss/cui-test-generator` (separate repository) -- Use JUnit 5 (`@Test`, `@DisplayName`, `@Nested`) -- Follow AAA pattern (Arrange-Act-Assert) -- One logical assertion per test -- Tests must be independent and not rely on execution order -- Minimum 80% line and branch coverage -- Use Maven profile `-Pcoverage` for coverage verification -- All public APIs must be tested -- Use cui-test-juli-logger for logger testing with `@EnableTestLogger` -- Use assertLogMessagePresentContaining for testing log messages -- Critical paths must have 100% coverage -- Forbidden: Mockito, PowerMock, Hamcrest - use CUI alternatives - -### CUI Test Generator Usage -**Reference**: `https://gitingest.com/github.com/cuioss/cui-test-generator` (separate repository) -- Mandatory for all test data generation in CUI projects -- Primary framework for creating test objects and data -- Provides type-safe, consistent test data generation -- Use cui-test-value-objects for value object contract testing -- Integrates with parameterized tests via @GeneratorsSource -- See Parameterized Tests Standards for annotation hierarchy - -### Parameterized Tests Standards -**Reference**: `{STANDARDS_BASE_URL}/standards/testing/quality-standards.adoc#parameterized-tests-best-practices` -- **Mandatory** for 3+ similar test variants -- Annotation hierarchy (preferred order): - 1. `@GeneratorsSource` - Most preferred for complex objects - 2. `@CompositeTypeGeneratorSource` - For multiple related types - 3. `@CsvSource` - Standard choice for simple data - 4. `@ValueSource` - Single parameter variations - 5. `@MethodSource` - Last resort only -- Use `@ParameterizedTest` with `@DisplayName` -- Consolidate duplicate test methods -- Provide clear test data and expected outcomes -- Document why @MethodSource if used - -## Documentation Standards -**References**: -- General Documentation: `{STANDARDS_BASE_URL}/standards/documentation` -- Javadoc Standards: `{STANDARDS_BASE_URL}/standards/documentation/javadoc-standards.adoc` -- AsciiDoc Standards: `{STANDARDS_BASE_URL}/standards/documentation/asciidoc-standards.adoc` -- README Structure: `{STANDARDS_BASE_URL}/standards/documentation/readme-structure.adoc` - -### Javadoc Standards -- Every public and protected class/interface must be documented -- Include clear purpose statement in class documentation -- Document all public methods with parameters, returns, and exceptions -- Include `@since` tag with version information -- Document thread-safety considerations -- Include usage examples for complex classes and methods -- Every package must have package-info.java -- Use `{@link}` for references to classes, methods, and fields -- Document Builder classes with complete usage examples - -## CDI and Quarkus Standards -**References**: -- CDI Development Patterns: `{STANDARDS_BASE_URL}/standards/cdi-quarkus` -- Quarkus Testing Standards: `{STANDARDS_BASE_URL}/standards/cdi-quarkus/testing-standards.adoc` -- Container Standards: `{STANDARDS_BASE_URL}/standards/cdi-quarkus/container-standards.adoc` -- Use constructor injection (mandatory over field injection) -- Single constructor rule: No `@Inject` needed for single constructors -- Use `final` fields for injected dependencies -- Use `@ApplicationScoped` for stateless services -- Use `@QuarkusTest` for CDI context testing -- Use `@QuarkusIntegrationTest` for packaged app testing -- Container: Use Quarkus distroless base image (91.9MB) -- HTTPS required for all integration tests -- OWASP Docker Top 10 compliance mandatory - -## CSS Standards -**Reference**: `{STANDARDS_BASE_URL}/standards/css` -- Use CSS custom properties (variables) for theming -- Follow BEM methodology for class naming -- Use Stylelint for code quality enforcement -- Use Prettier for consistent formatting -- Mobile-first responsive design approach -- Semantic HTML with accessible CSS patterns -- Performance optimization: minimize CSS bundle size -- Support for modern browsers (last 2 versions) - -## JavaScript Standards -**Reference**: `{STANDARDS_BASE_URL}/standards/javascript` -- Use ES6+ modern JavaScript features -- Use ESLint with strict configuration -- Use Prettier for code formatting -- Use Jest for unit testing framework -- Follow functional programming patterns when appropriate -- Use JSDoc for comprehensive documentation -- Use Lit components for web components (Quarkus DevUI context) -- Maven integration via frontend-maven-plugin -- Cypress for E2E testing - -### General Documentation Standards -- Use AsciiDoc format with `.adoc` extension -- Include proper document header with TOC and section numbering -- Use `:source-highlighter: highlight.js` attribute -- Use `xref:` syntax for cross-references (not `<<>>`) -- Blank lines required before all lists -- Consistent heading hierarchy -- Update main README when adding new documents -- Reference AsciiDoc standards from all relevant documents - -## Process Standards -**Reference**: `{STANDARDS_BASE_URL}/standards/process` -- Follow standardized git commit message format -- Use structured refactoring process for code improvements -- Complete task completion standards for quality assurance -- Maintain Javadoc error resolution process -- Follow Java test maintenance procedures -- Implement logger maintenance standards compliance -- If in doubt, ask the user - never guess or be creative -- Always research topics using available tools - -## Requirements Standards -**References**: -- Requirements Documents: `{STANDARDS_BASE_URL}/standards/requirements` -- Specification Documents: `{STANDARDS_BASE_URL}/standards/requirements/specification-documents.adoc` -- New Project Guide: `{STANDARDS_BASE_URL}/standards/requirements/new-project-guide.adoc` -- All requirements must be traceable to specifications -- Requirements must be specific, measurable, achievable, relevant, time-bound -- Maintain consistent documentation structure across projects -- Link implemented specifications to actual implementation code -- Use standard directory structure: doc/Requirements.adoc, doc/Specification.adoc -- Update specifications when implementation is complete -- See Documentation Standards for AsciiDoc formatting rules - -## AI Tool Specific Instructions - -### For IntelliJ Junie -- Always check for pre-commit profile availability -- Use proper Maven module selection for focused builds -- Leverage IDE integration for testing and debugging -- **Context Management**: Use module-level context awareness for focused assistance -- **Incremental Guidance**: Apply progressive disclosure of standards based on task complexity - -### For Claude Code (Agentic Coding) -- **CLAUDE.md Integration**: Auto-generate and maintain project-specific CLAUDE.md files -- **Permission Management**: Configure safe tool allowlists using `/permissions` command -- **Custom Slash Commands**: Create CUI-specific workflow commands in `.claude/commands/` -- **MCP Integration**: Leverage Model Context Protocol for enhanced capabilities -- Use todo lists for complex multi-step tasks -- Batch tool calls for parallel operations -- Always run lint and typecheck commands after code changes -- **Context Window Optimization**: Prioritize recent and relevant context for token efficiency - -### For GitHub Copilot -- **Repository Instructions**: Maintain `.github/copilot-instructions.md` for repo-specific guidance -- **Setup Steps**: Configure `copilot-setup-steps.yml` for consistent development environments -- **Task Scoping**: Optimize issue descriptions as effective AI prompts -- **Review Iteration**: Structure PR comments for effective AI collaboration using batch reviews -- Context-aware suggestions based on CUI standards -- Follow established patterns in the codebase -- Respect existing code style and architecture - -### For All AI Tools -- **Standards Hierarchy**: Follow the Context Hierarchy and Priority Framework -- **Safety First**: Apply AI Safety and Validation Framework for all outputs -- Always refer to CUI standards documentation before making changes -- Validate against existing patterns in the codebase -- Run tests after any code modifications -- Follow the pre-commit process for code quality -- Document any new public APIs according to Javadoc standards -- Use gitingest.com links for accessing CUI standards repository content -- **Feedback Integration**: Learn from user corrections and adapt instruction effectiveness -- **Escalation Protocol**: When in doubt, ask the user rather than making assumptions - -## Performance and Context Optimization -**Guidelines for Efficient AI Interactions** - -### Context Window Management -- **Prioritize Recent Context**: Weight recent files and conversations higher in decision making -- **Dynamic Context Selection**: Load only relevant standards for current task context -- **Token Economy**: Balance instruction comprehensiveness with context window efficiency -- **Incremental Loading**: Request additional context only when needed for task completion - -### Iterative Improvement Patterns -- **Pattern Recognition**: Identify and codify successful interaction patterns within CUI projects -- **Continuous Calibration**: Adjust instruction effectiveness based on build outcomes and user feedback -- **Workflow Optimization**: Streamline common development workflows through custom commands and templates - -### Integration with Development Workflows -- **CI/CD Awareness**: Understand and respect automated build and deployment processes -- **Quality Gate Integration**: Ensure all outputs pass automated quality checks -- **Collaborative Development**: Optimize for effective human-AI pair programming -- **Knowledge Contribution**: Help maintain and improve team knowledge base and documentation diff --git a/integration-tests/docker-compose.jfr.yml b/integration-tests/docker-compose.jfr.yml new file mode 100644 index 00000000..6a4a29ac --- /dev/null +++ b/integration-tests/docker-compose.jfr.yml @@ -0,0 +1,13 @@ +# Docker Compose override for JFR profiling +# Usage: docker compose -f docker-compose.yml -f docker-compose.jfr.yml up -d + +services: + api-sheriff: + image: "api-sheriff:jfr" + build: + context: ../api-sheriff + dockerfile: src/main/docker/Dockerfile.native.jfr + volumes: + - ./src/main/docker/certificates:/app/certificates:ro + - ${LOG_TARGET_DIR:-./target}:/logs:rw + - ./target/jfr-recordings:/tmp/jfr-output:rw diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/docker-compose.yml b/integration-tests/docker-compose.yml similarity index 53% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/docker-compose.yml rename to integration-tests/docker-compose.yml index 5f6d3c5c..26f3bc26 100644 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/docker-compose.yml +++ b/integration-tests/docker-compose.yml @@ -1,11 +1,11 @@ -# JWT Integration Tests - Production-Grade Configuration +# API Sheriff Integration Tests - Production-Grade Configuration # Purpose: Test with production-equivalent settings to catch issues early services: keycloak: # NOTE: Dependabot cannot automatically update this image version. # Manual updates required. Check: https://quay.io/keycloak/keycloak - image: quay.io/keycloak/keycloak:26.2.5 + image: quay.io/keycloak/keycloak:26.5.4@sha256:ae8efb0d218d8921334b03a2dbee7069a0b868240691c50a3ffc9f42fabba8b4 command: - start-dev - --import-realm @@ -31,29 +31,29 @@ services: - KC_HOSTNAME=localhost # External hostname for Docker container well-known discovery networks: - - jwt-integration + - api-sheriff - api-sheriff-integration-tests: - # Use separate Dockerfiles for different variants - # For distroless: DOCKERFILE=Dockerfile.native.distroless DOCKER_IMAGE_TAG=distroless - # For JFR: DOCKERFILE=Dockerfile.native.jfr DOCKER_IMAGE_TAG=jfr - image: "api-sheriff-integration-tests:${DOCKER_IMAGE_TAG:-distroless}" + api-sheriff: + image: "api-sheriff:distroless" build: - context: ../.. - dockerfile: api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/${DOCKERFILE:-Dockerfile.native.distroless} + context: ../api-sheriff + dockerfile: src/main/docker/Dockerfile.native cache_from: - quay.io/quarkus/quarkus-distroless-image:2.0 - - registry.access.redhat.com/ubi8/ubi-minimal:8.10 - - quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21 + - quay.io/quarkus/ubi9-quarkus-micro-image:2.0 ports: - "10443:8443" # External test port for integration tests + - "19000:9000" # Management interface (health/metrics, plain HTTP) environment: - # JVM SSL Session Cache Optimization for TLS Performance - # JFR configuration handled by image variant (distroless vs JFR-enabled) - - JAVA_OPTS_APPEND=-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager -Djdk.tls.rejectClientInitiatedRenegotiation=true -Djavax.net.ssl.sessionCacheSize=20480 -Djavax.net.ssl.sessionTimeout=300 -Djsse.enableSNIExtension=true -Djdk.tls.maxHandshakeMessageSize=65536 -Djdk.tls.maxCertificateChainLength=10 -Djdk.tls.useExtendedMasterSecret=true - - QUARKUS_LOG_LEVEL=INFO + # File logging to mounted target directory + - LOG_FILE_PATH=/logs/quarkus.log + # Certificate paths (runtime override) + - QUARKUS_HTTP_SSL_CERTIFICATE_FILES=/app/certificates/localhost.crt + - QUARKUS_HTTP_SSL_CERTIFICATE_KEY_FILES=/app/certificates/localhost.key + - QUARKUS_TLS_DEFAULT_TRUST__STORE_P12_PATH=/app/certificates/localhost-truststore.p12 + - QUARKUS_TLS_DEFAULT_TRUST__STORE_P12_PASSWORD=localhost-trust depends_on: - keycloak @@ -61,8 +61,8 @@ services: volumes: # Read-only certificate mount (production pattern) - ./src/main/docker/certificates:/app/certificates:ro - # JFR output volume - mount directly to benchmark target directory - - ../quarkus-integration-jmh/target/benchmark-results/jfr-recordings:/tmp/jfr-output:rw + # Mount target directory for log files (defaults to ./target) + - ${LOG_TARGET_DIR:-./target}:/logs:rw # OWASP Security hardening (production-grade) security_opt: @@ -87,27 +87,38 @@ services: memory: 256M cpus: '1.0' - # Health check using internal script (optimized for fast native startup) - healthcheck: - test: ["CMD", "/app/health-check.sh"] - interval: 15s - timeout: 5s - retries: 3 - start_period: 10s + # Health check: startup script probes management interface (port 9000, plain HTTP) # Network isolation (production pattern) networks: - - jwt-integration + - api-sheriff # Production restart policy restart: unless-stopped + prometheus: + image: prom/prometheus:v3.6.0 + ports: + - "9090:9090" + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.retention.time=1h' + - '--web.enable-lifecycle' + networks: + - api-sheriff + + wrk: + build: + context: .. + dockerfile: benchmarks/Dockerfile.wrk + networks: + - api-sheriff + profiles: + - benchmark + restart: "no" + networks: - jwt-integration: + api-sheriff: driver: bridge - # Control inter-container communication (production setting) - internal: false - driver_opts: - com.docker.network.bridge.enable_icc: "true" - com.docker.network.bridge.enable_ip_masquerade: "true" - com.docker.network.driver.mtu: "1500" diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/pom.xml b/integration-tests/pom.xml similarity index 74% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/pom.xml rename to integration-tests/pom.xml index 64bce7f3..cf430aa5 100644 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -5,25 +5,21 @@ 4.0.0 de.cuioss.sheriff.api - api-sheriff-quarkus-parent + api-sheriff-parent 1.0.0-SNAPSHOT ../pom.xml - api-sheriff-quarkus-integration-tests + integration-tests jar - API Sheriff Quarkus Integration Tests - Integration tests for the API Sheriff Quarkus extension including native container testing with HTTPS support + API Sheriff Integration Tests + Integration test coordinator for API Sheriff โ€” builds native containers and runs IT suites - de.cuioss.sheriff.api.quarkus.integration.tests - + de.cuioss.sheriff.api.integration.tests + true - - false - false - 10443 @@ -32,48 +28,32 @@ - + de.cuioss.sheriff.api - api-sheriff-quarkus + api-sheriff - - + + de.cuioss.sheriff.api - api-sheriff-quarkus-deployment + api-sheriff ${project.version} + generators test - - - io.quarkus - quarkus-resteasy - - - io.quarkus - quarkus-resteasy-jackson - + io.quarkus - quarkus-smallrye-health - - - io.quarkus - quarkus-micrometer-registry-prometheus + quarkus-junit5 + test - - - - de.cuioss.sheriff.api - api-sheriff-library - ${project.version} - generators + de.cuioss.test + cui-test-juli-logger test - io.rest-assured rest-assured @@ -89,18 +69,22 @@ awaitility test - - - - io.quarkus - quarkus-vertx-http-dev-ui-tests - test - - + + + io.quarkus + quarkus-maven-plugin + + + default + none + + + + org.codehaus.mojo @@ -131,33 +115,14 @@ - + integration-tests - false - - - true - true - - - - io.quarkus - quarkus-maven-plugin - - - - default - none - - - - org.apache.maven.plugins @@ -184,7 +149,6 @@ ${test.https.port} org.jboss.logmanager.LogManager - false ${skipITs} @@ -197,7 +161,7 @@ org.codehaus.mojo exec-maven-plugin - + build-native-if-needed pre-integration-test @@ -222,11 +186,9 @@ compose build - api-sheriff-integration-tests + api-sheriff - Dockerfile.native.distroless - distroless 1 @@ -241,6 +203,25 @@ ./scripts/start-integration-container.sh ${project.basedir} + + + ${project.build.directory} + + + + + + dump-keycloak-logs + post-integration-test + + exec + + + ./scripts/dump-keycloak-logs.sh + + target + + ${project.basedir} @@ -260,30 +241,15 @@ - + jfr - false - - - true - - jfr - true - - - - io.quarkus - quarkus-maven-plugin - - - org.apache.maven.plugins @@ -310,7 +276,6 @@ ${test.https.port} org.jboss.logmanager.LogManager - false ${skipITs} @@ -323,6 +288,18 @@ org.codehaus.mojo exec-maven-plugin + + + build-native-if-needed + pre-integration-test + + exec + + + ./scripts/build-native-if-needed.sh + ${project.basedir} + + docker-build-jfr @@ -335,12 +312,14 @@ ${project.basedir} compose + -f + docker-compose.yml + -f + docker-compose.jfr.yml build - api-sheriff-integration-tests + api-sheriff - Dockerfile.native.jfr - jfr 1 @@ -355,6 +334,25 @@ ./scripts/start-integration-container.sh ${project.basedir} + + + ${project.build.directory} + + + + + + dump-keycloak-logs + post-integration-test + + exec + + + ./scripts/dump-keycloak-logs.sh + + target + + ${project.basedir} @@ -375,4 +373,4 @@ - \ No newline at end of file + diff --git a/integration-tests/prometheus.yml b/integration-tests/prometheus.yml new file mode 100644 index 00000000..44aa3fd2 --- /dev/null +++ b/integration-tests/prometheus.yml @@ -0,0 +1,10 @@ +global: + scrape_interval: 2s + evaluation_interval: 2s + +scrape_configs: + - job_name: 'quarkus-benchmark' + static_configs: + - targets: ['api-sheriff:9000'] + metrics_path: '/q/metrics' + scheme: http diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/benchmark-with-monitoring.sh b/integration-tests/scripts/benchmark-with-monitoring.sh similarity index 97% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/benchmark-with-monitoring.sh rename to integration-tests/scripts/benchmark-with-monitoring.sh index 439fad9e..b4fe2a78 100755 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/benchmark-with-monitoring.sh +++ b/integration-tests/scripts/benchmark-with-monitoring.sh @@ -5,8 +5,8 @@ set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" -RESULTS_DIR="$PROJECT_ROOT/api-sheriff-quarkus-parent/quarkus-integration-jmh/target/monitoring-results" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +RESULTS_DIR="$PROJECT_ROOT/integration-tests/target/monitoring-results" TIMESTAMP=$(date +%Y%m%d-%H%M%S) echo "๐Ÿ” API Sheriff Validation Benchmark with Comprehensive Monitoring" @@ -360,11 +360,11 @@ trap cleanup INT TERM echo "๐Ÿ”จ Step 1: Building integration tests (~6 seconds)..." cd "$PROJECT_ROOT" -$MAVEN_CMD clean install -pl api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests -q +$MAVEN_CMD clean install -pl api-sheriff -q echo "๐Ÿ”จ Step 2: Force clean rebuild of native executable and container (~90 seconds)..." # Ensure clean rebuild includes updated application.properties -$MAVEN_CMD clean package -pl api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests -Pintegration-tests -q +$MAVEN_CMD clean package -pl api-sheriff -Pnative -q # Force Docker to rebuild container with updated configuration docker system prune -f --volumes >/dev/null 2>&1 || true @@ -375,7 +375,7 @@ echo "" # Start benchmark with JFR recording START_TIME=$(date +%s) -$MAVEN_CMD verify -pl api-sheriff-quarkus-parent/quarkus-integration-jmh -Pbenchmark > "$BENCHMARK_LOG" 2>&1 & +$MAVEN_CMD verify -pl benchmarks -Pbenchmark > "$BENCHMARK_LOG" 2>&1 & BENCHMARK_PID=$! # Wait for containers to start diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/build-native-if-needed.sh b/integration-tests/scripts/build-native-if-needed.sh similarity index 55% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/build-native-if-needed.sh rename to integration-tests/scripts/build-native-if-needed.sh index 72425e71..27e8c04e 100755 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/build-native-if-needed.sh +++ b/integration-tests/scripts/build-native-if-needed.sh @@ -2,25 +2,26 @@ set -e # Script to build native executable if it doesn't exist +# Targets the api-sheriff module (the deployable application) SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +ROOT_DIR="$(cd "${PROJECT_DIR}/.." && pwd)" +APP_TARGET_DIR="${ROOT_DIR}/api-sheriff/target" -# Check if native executable exists -RUNNER_FILE=$(find "${PROJECT_DIR}/target" -name "*-runner" -type f 2>/dev/null | head -n 1) +# Check if native executable exists in api-sheriff target +RUNNER_FILE=$(find "${APP_TARGET_DIR}" -name "*-runner" -type f 2>/dev/null | head -n 1) if [[ -z "$RUNNER_FILE" ]]; then echo "๐Ÿ”จ Native executable not found, building it now..." echo "This will take approximately 2 minutes..." - - # Build native executable using Quarkus Maven plugin - # Find the root directory with mvnw - ROOT_DIR=$(cd "${PROJECT_DIR}/../.." && pwd) + + # Build native executable with full lifecycle (ensures resources/augmentation) cd "${ROOT_DIR}" - ./mvnw --no-transfer-progress -Pintegration-tests quarkus:build -pl api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests - + ./mvnw --no-transfer-progress clean package -Pnative -pl api-sheriff -DskipTests + # Verify it was created - RUNNER_FILE=$(find "${PROJECT_DIR}/target" -name "*-runner" -type f 2>/dev/null | head -n 1) + RUNNER_FILE=$(find "${APP_TARGET_DIR}" -name "*-runner" -type f 2>/dev/null | head -n 1) if [[ -z "$RUNNER_FILE" ]]; then echo "โŒ Failed to build native executable" exit 1 @@ -28,4 +29,4 @@ if [[ -z "$RUNNER_FILE" ]]; then echo "โœ… Native executable built: $(basename "$RUNNER_FILE")" else echo "โœ… Native executable already exists: $(basename "$RUNNER_FILE")" -fi \ No newline at end of file +fi diff --git a/integration-tests/scripts/dump-keycloak-logs.sh b/integration-tests/scripts/dump-keycloak-logs.sh new file mode 100755 index 00000000..ed908389 --- /dev/null +++ b/integration-tests/scripts/dump-keycloak-logs.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# Keycloak Container Log Dumping Script +# Usage: ./dump-keycloak-logs.sh +# Example: ./dump-keycloak-logs.sh target +# +# Note: Quarkus logs are written directly to target/quarkus.log via file logging + +set -euo pipefail + +# Configuration +KEYCLOAK_CONTAINER_NAME="integration-tests-keycloak-1" +TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") +KEYCLOAK_LOG_FILENAME="keycloak-logs-${TIMESTAMP}.txt" + +# Parameter validation +if [ $# -ne 1 ]; then + echo "โŒ Error: Target directory parameter required" + echo "Usage: $0 " + echo "Example: $0 target" + exit 1 +fi + +TARGET_DIR="$1" + +# Create target directory if it doesn't exist +if [ ! -d "$TARGET_DIR" ]; then + echo "๐Ÿ“ Creating target directory: $TARGET_DIR" + mkdir -p "$TARGET_DIR" +fi + +# Resolve absolute path for clarity +TARGET_ABS_PATH=$(cd "$TARGET_DIR" && pwd) +KEYCLOAK_LOG_FILE_PATH="${TARGET_ABS_PATH}/${KEYCLOAK_LOG_FILENAME}" + +echo "๐Ÿš€ Dumping Keycloak container logs..." +echo "๐Ÿ“ฆ Keycloak container: $KEYCLOAK_CONTAINER_NAME" +echo "๐Ÿ“ Output file: $KEYCLOAK_LOG_FILE_PATH" + +# Check if container exists and is running +if ! docker ps --format "{{.Names}}" | grep -q "^${KEYCLOAK_CONTAINER_NAME}$"; then + if docker ps -a --format "{{.Names}}" | grep -q "^${KEYCLOAK_CONTAINER_NAME}$"; then + echo "โš ๏ธ Warning: Container $KEYCLOAK_CONTAINER_NAME exists but is not running" + echo "๐Ÿ“‹ Attempting to dump logs from stopped container..." + else + echo "โŒ Error: Container $KEYCLOAK_CONTAINER_NAME not found" + echo "๐Ÿ” Available containers:" + docker ps -a --format "table {{.Names}}\t{{.Status}}" + exit 1 + fi +else + echo "โœ… Container is running" +fi + +# Dump logs +echo "๐Ÿ“ฅ Dumping Keycloak logs..." +if docker logs "$KEYCLOAK_CONTAINER_NAME" > "$KEYCLOAK_LOG_FILE_PATH" 2>&1; then + LOG_SIZE=$(wc -l < "$KEYCLOAK_LOG_FILE_PATH") + FILE_SIZE=$(du -h "$KEYCLOAK_LOG_FILE_PATH" | cut -f1) + echo "โœ… Successfully dumped $LOG_SIZE lines ($FILE_SIZE)" + echo "๐Ÿ“ Full path: $KEYCLOAK_LOG_FILE_PATH" + echo "๐ŸŽ‰ Keycloak logs successfully dumped!" + exit 0 +else + echo "โŒ Failed to dump logs from container: $KEYCLOAK_CONTAINER_NAME" + exit 1 +fi diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/start-integration-container.sh b/integration-tests/scripts/start-integration-container.sh similarity index 70% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/start-integration-container.sh rename to integration-tests/scripts/start-integration-container.sh index 6b80fe38..6f371daf 100755 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/start-integration-container.sh +++ b/integration-tests/scripts/start-integration-container.sh @@ -1,11 +1,12 @@ #!/bin/bash -# Start JWT Integration Tests using Docker Compose +# Start API Sheriff Integration Tests using Docker Compose set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" -ROOT_DIR="$(dirname "$(dirname "$PROJECT_DIR")")" +ROOT_DIR="$(dirname "$PROJECT_DIR")" +APP_TARGET_DIR="${ROOT_DIR}/api-sheriff/target" echo "๐Ÿš€ Starting API Sheriff Integration Tests with Docker Compose" echo "Project directory: ${PROJECT_DIR}" @@ -14,21 +15,19 @@ echo "Root directory: ${ROOT_DIR}" cd "${PROJECT_DIR}" # Check build approach - Native executable + Docker copy vs Docker build -RUNNER_FILE=$(find target/ -name "*-runner" -type f 2>/dev/null | head -n 1) +RUNNER_FILE=$(find "${APP_TARGET_DIR}" -name "*-runner" -type f 2>/dev/null | head -n 1) # Detect image type - prefer JFR if available, fallback to distroless -JFR_IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "^api-sheriff-integration-tests:jfr$" || true) -DISTROLESS_IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "^api-sheriff-integration-tests:distroless$" || true) +JFR_IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "^api-sheriff:jfr$" || true) +DISTROLESS_IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "^api-sheriff:distroless$" || true) if [[ -n "$JFR_IMAGE" ]]; then AVAILABLE_IMAGE="$JFR_IMAGE" IMAGE_TYPE="jfr" - export DOCKER_IMAGE_TAG="jfr" - export DOCKERFILE="Dockerfile.native.jfr" + COMPOSE_CMD="docker compose -f docker-compose.yml -f docker-compose.jfr.yml" elif [[ -n "$DISTROLESS_IMAGE" ]]; then AVAILABLE_IMAGE="$DISTROLESS_IMAGE" IMAGE_TYPE="distroless" - export DOCKER_IMAGE_TAG="distroless" - export DOCKERFILE="Dockerfile.native.distroless" + COMPOSE_CMD="docker compose -f docker-compose.yml" else AVAILABLE_IMAGE="" IMAGE_TYPE="none" @@ -39,25 +38,28 @@ IMAGE_EXISTS=$([ ! -z "$AVAILABLE_IMAGE" ] && echo "true" || echo "false") if [[ -n "$RUNNER_FILE" ]] && [[ "$IMAGE_EXISTS" == "true" ]]; then echo "๐Ÿ“ฆ Using Maven-built native executable: $(basename "$RUNNER_FILE")" echo "๐Ÿณ Docker image: $AVAILABLE_IMAGE ($IMAGE_TYPE mode)" - COMPOSE_FILE="docker-compose.yml" MODE="native (Maven-built + Docker copy) - $IMAGE_TYPE" elif [[ "$IMAGE_EXISTS" == "true" ]]; then echo "๐Ÿ“ฆ Using Docker-built native image: $AVAILABLE_IMAGE ($IMAGE_TYPE mode)" - COMPOSE_FILE="docker-compose.yml" MODE="native (Docker-built) - $IMAGE_TYPE" else echo "โŒ Neither native executable nor Docker image found" - echo "Expected: target/*-runner file and api-sheriff-integration-tests image" + echo "Expected: api-sheriff/target/*-runner file and api-sheriff image" echo "Available images:" docker images | grep api-sheriff || echo " No api-sheriff images found" - echo "Run: mvnw verify -Pintegration-tests -pl api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests" + echo "Run: mvnw verify -Pintegration-tests -pl integration-tests -am" exit 1 fi +# Set LOG_TARGET_DIR to project's target directory for Quarkus file logging +export LOG_TARGET_DIR="${LOG_TARGET_DIR:-${PROJECT_DIR}/target}" +mkdir -p "${LOG_TARGET_DIR}" +echo "๐Ÿ“ Quarkus logs will be written to: ${LOG_TARGET_DIR}/quarkus.log" + # Start with Docker Compose (includes Keycloak) echo "๐Ÿณ Starting Docker containers (Quarkus $MODE + Keycloak)..." -(cd "${PROJECT_DIR}" && docker compose -f "$COMPOSE_FILE" up -d) +(cd "${PROJECT_DIR}" && $COMPOSE_CMD up -d) # Wait for Keycloak to be ready first echo "โณ Waiting for Keycloak to be ready..." @@ -79,7 +81,7 @@ done echo "โณ Waiting for Quarkus service to be ready..." START_TIME=$(date +%s) for i in {1..30}; do - if curl -k -s https://localhost:10443/q/health/live > /dev/null 2>&1; then + if curl -sf http://localhost:19000/q/health/live > /dev/null 2>&1; then END_TIME=$(date +%s) TOTAL_TIME=$((END_TIME - START_TIME)) echo "โœ… Quarkus service is ready!" @@ -88,7 +90,7 @@ for i in {1..30}; do fi if [ $i -eq 30 ]; then echo "โŒ Quarkus service failed to start within 30 seconds" - echo "Check logs with: docker compose logs api-sheriff-integration-tests" + echo "Check logs with: docker compose logs api-sheriff" exit 1 fi echo "โณ Waiting for Quarkus... (attempt $i/30)" @@ -96,13 +98,13 @@ for i in {1..30}; do done # Extract native startup time from logs -NATIVE_STARTUP=$(docker compose logs api-sheriff-integration-tests 2>/dev/null | grep "started in" | sed -n 's/.*started in \([0-9.]*\)s.*/\1/p' | tail -1) +NATIVE_STARTUP=$(docker compose logs api-sheriff 2>/dev/null | grep "started in" | sed -n 's/.*started in \([0-9.]*\)s.*/\1/p' | tail -1) if [ ! -z "$NATIVE_STARTUP" ]; then echo "โšก Native app startup: ${NATIVE_STARTUP}s (application only)" fi # Show actual image size -IMAGE_SIZE=$(docker images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}" | grep api-sheriff-integration-tests | awk '{print $2}' | head -1) +IMAGE_SIZE=$(docker images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}" | grep "api-sheriff:" | grep -v integration | awk '{print $2}' | head -1) if [ ! -z "$IMAGE_SIZE" ]; then echo "๐Ÿ“ฆ Image size: ${IMAGE_SIZE} (native image)" fi @@ -111,12 +113,12 @@ echo "" echo "๐ŸŽ‰ API Sheriff Integration Benchmark Environment is running!" echo "" echo "๐Ÿ“ฑ Application URLs:" -echo " ๐Ÿ” Health Check: https://localhost:10443/q/health" -echo " ๐Ÿ“Š Metrics: https://localhost:10443/q/metrics" +echo " ๐Ÿ” Health Check: http://localhost:19000/q/health" +echo " ๐Ÿ“Š Metrics: http://localhost:19000/q/metrics" echo " ๐Ÿ”‘ Keycloak: https://localhost:1443/auth" echo "" echo "๐Ÿงช Quick test commands:" -echo " curl -k https://localhost:10443/q/health/live" +echo " curl -sf http://localhost:19000/q/health/live" echo " curl -k https://localhost:1090/health/ready" echo "" echo "๐Ÿ›‘ To stop: ./scripts/stop-integration-container.sh" diff --git a/integration-tests/scripts/stop-integration-container.sh b/integration-tests/scripts/stop-integration-container.sh new file mode 100755 index 00000000..e91601b1 --- /dev/null +++ b/integration-tests/scripts/stop-integration-container.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Stop API Sheriff Integration Tests Docker containers + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" + +echo "๐Ÿ›‘ Stopping API Sheriff Integration Tests Docker containers" + +cd "${PROJECT_DIR}" + +# Detect if JFR image is running to use matching compose files +JFR_RUNNING=$(docker ps --format "{{.Image}}" | grep "^api-sheriff:jfr$" || true) + +if [[ -n "$JFR_RUNNING" ]]; then + COMPOSE_CMD="docker compose -f docker-compose.yml -f docker-compose.jfr.yml" + MODE="jfr" +else + COMPOSE_CMD="docker compose -f docker-compose.yml" + MODE="distroless" +fi + +# Stop and remove containers +echo "๐Ÿ“ฆ Stopping Docker containers ($MODE mode)..." +$COMPOSE_CMD down + +# Optional: Clean up images and volumes +if [ "$1" = "--clean" ]; then + echo "๐Ÿงน Cleaning up Docker images and volumes..." + $COMPOSE_CMD down --volumes --rmi all +fi + +echo "โœ… API Sheriff Integration Tests stopped successfully" + +# Show final status +if $COMPOSE_CMD ps | grep -q "Up"; then + echo "โš ๏ธ Some containers are still running:" + $COMPOSE_CMD ps +else + echo "โœ… All containers are stopped" +fi diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/verify-environment.sh b/integration-tests/scripts/verify-environment.sh similarity index 100% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/scripts/verify-environment.sh rename to integration-tests/scripts/verify-environment.sh diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/generate-certificates.sh b/integration-tests/src/main/docker/certificates/generate-certificates.sh similarity index 83% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/generate-certificates.sh rename to integration-tests/src/main/docker/certificates/generate-certificates.sh index 63b23ced..d175565b 100755 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/generate-certificates.sh +++ b/integration-tests/src/main/docker/certificates/generate-certificates.sh @@ -1,17 +1,17 @@ #!/bin/bash -# Script to generate PEM certificates for JWT Quarkus integration testing +# Script to generate PEM certificates for API Sheriff integration testing # Generates passwordless localhost certificates for secure container usage set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" CERT_DIR="${SCRIPT_DIR}" -CERT_DNAME="CN=localhost, OU=Integration Testing, O=CUI-JWT, L=Berlin, ST=Berlin, C=DE" +CERT_DNAME="CN=localhost, OU=Integration Testing, O=API-Sheriff, L=Berlin, ST=Berlin, C=DE" CERT_VALIDITY=730 TEMP_KEYSTORE="${CERT_DIR}/temp-keystore.p12" TEMP_PASSWORD="temp-$(date +%s)" -echo "Generating PEM certificates for JWT integration testing..." +echo "Generating PEM certificates for API Sheriff integration testing..." echo "Certificate directory: ${CERT_DIR}" # Clean up existing certificates @@ -25,7 +25,7 @@ keytool -genkeypair \ -keysize 2048 \ -validity ${CERT_VALIDITY} \ -dname "${CERT_DNAME}" \ - -ext san=dns:localhost,dns:keycloak,ip:127.0.0.1,ip:0.0.0.0 \ + -ext san=dns:localhost,dns:keycloak,dns:api-sheriff,ip:127.0.0.1,ip:0.0.0.0 \ -keystore "${TEMP_KEYSTORE}" \ -storetype PKCS12 \ -storepass "${TEMP_PASSWORD}" \ @@ -64,6 +64,6 @@ echo " - localhost.key: Private key in PEM format (restricted access)" echo "" echo "Certificate valid for ${CERT_VALIDITY} days (2 years)" echo "Subject: ${CERT_DNAME}" -echo "SAN: dns:localhost,dns:keycloak,ip:127.0.0.1,ip:0.0.0.0" +echo "SAN: dns:localhost,dns:keycloak,dns:api-sheriff,ip:127.0.0.1,ip:0.0.0.0" echo "" echo "Security: No passwords required - file permissions provide security" \ No newline at end of file diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/generate-truststore.sh b/integration-tests/src/main/docker/certificates/generate-truststore.sh similarity index 88% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/generate-truststore.sh rename to integration-tests/src/main/docker/certificates/generate-truststore.sh index 49059143..64caad56 100755 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/certificates/generate-truststore.sh +++ b/integration-tests/src/main/docker/certificates/generate-truststore.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Script to generate Java truststore for JWT Quarkus integration testing +# Script to generate Java truststore for API Sheriff integration testing # Creates truststore with localhost certificate for proper TLS validation set -e @@ -9,7 +9,7 @@ CERT_DIR="${SCRIPT_DIR}" TRUSTSTORE_FILE="${CERT_DIR}/localhost-truststore.p12" TRUSTSTORE_PASSWORD="localhost-trust" -echo "Generating Java truststore for JWT integration testing..." +echo "Generating Java truststore for API Sheriff integration testing..." echo "Certificate directory: ${CERT_DIR}" # Check if certificate exists diff --git a/integration-tests/src/main/docker/certificates/localhost-truststore.p12 b/integration-tests/src/main/docker/certificates/localhost-truststore.p12 new file mode 100644 index 00000000..741f870a Binary files /dev/null and b/integration-tests/src/main/docker/certificates/localhost-truststore.p12 differ diff --git a/integration-tests/src/main/docker/certificates/localhost.crt b/integration-tests/src/main/docker/certificates/localhost.crt new file mode 100644 index 00000000..fe906f4c --- /dev/null +++ b/integration-tests/src/main/docker/certificates/localhost.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDyzCCArOgAwIBAgIJAIn+s76rgt1zMA0GCSqGSIb3DQEBDAUAMHcxCzAJBgNV +BAYTAkRFMQ8wDQYDVQQIEwZCZXJsaW4xDzANBgNVBAcTBkJlcmxpbjEUMBIGA1UE +ChMLQVBJLVNoZXJpZmYxHDAaBgNVBAsTE0ludGVncmF0aW9uIFRlc3RpbmcxEjAQ +BgNVBAMTCWxvY2FsaG9zdDAeFw0yNjAzMDQyMDI2NTJaFw0yODAzMDMyMDI2NTJa +MHcxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZCZXJsaW4xDzANBgNVBAcTBkJlcmxp +bjEUMBIGA1UEChMLQVBJLVNoZXJpZmYxHDAaBgNVBAsTE0ludGVncmF0aW9uIFRl +c3RpbmcxEjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBALsV2R8NOjmWnJAshFzbUUtpG4Ow5da41h7qSRfXLbCe8tOioTod +kLMtYrC6TTJv0tPIflUqPxQ4qyjkQs0IH6C2pWffXc+Fl6ZeRDYl6Dkdt4/Dy/k7 +ybytZ5DjL3r5n13/IOvi+kWqPQYbppnecCyskASS7XPSGIOcgpAuOxb3DmfBsE6D +NXjbSq9ouwpHjzNQjpMhzFjUTGYjLVvPQxdQkUwsiffKzrbgGmUH9ZS1y1Aj5TdP +b8zc3DlOyPNvgDDAGq3BEkXwtH5TUAigeOFI/dOTwai7jGlTBxnxdnY9UnrXZT0L +ecOBTJYbnL/zqYE8YbGxVXnmVDAqducphrECAwEAAaNaMFgwHQYDVR0OBBYEFFu6 +h0rtSLTmhhQiljH84cA102srMDcGA1UdEQQwMC6CCWxvY2FsaG9zdIIIa2V5Y2xv +YWuCC2FwaS1zaGVyaWZmhwR/AAABhwQAAAAAMA0GCSqGSIb3DQEBDAUAA4IBAQBC +Jv4FL8/9UPtyc6yxQOKTTBC+rPzzjYposhGVRQG+j4zLUEYYYP+qW+L54Uj/+iYe +iPvBYutYT5Z3sKnGtvnJR6EVn4Ot0E5m3mPFIIJLKwpyQZ8jv6SJ6zofcB4j+8HP +pbvuv+V6or6t3j4U4SMgd4D/qFZbRh5ksnoyxshnvSpMltAmVhtX1QUO5P/M427c +U3NbrZjw5DijNy5M5/mpPd3UfftvnxbwiakUpk5qs42hmi89moywkYVB5yWdfleA +IavDpmVSvJdzHtNgweNhJ0qKBvuIFCsrJiN7yK+VaC926+RR5QlTxWFVomg8Ln26 +4XirSlJCS1o6EmhmcM4k +-----END CERTIFICATE----- diff --git a/integration-tests/src/main/docker/certificates/localhost.key b/integration-tests/src/main/docker/certificates/localhost.key new file mode 100644 index 00000000..0f37923e --- /dev/null +++ b/integration-tests/src/main/docker/certificates/localhost.key @@ -0,0 +1,32 @@ +Bag Attributes + friendlyName: localhost + localKeyID: 54 69 6D 65 20 31 37 37 32 36 35 36 30 31 32 39 39 35 +Key Attributes: +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7FdkfDTo5lpyQ +LIRc21FLaRuDsOXWuNYe6kkX1y2wnvLToqE6HZCzLWKwuk0yb9LTyH5VKj8UOKso +5ELNCB+gtqVn313PhZemXkQ2Jeg5HbePw8v5O8m8rWeQ4y96+Z9d/yDr4vpFqj0G +G6aZ3nAsrJAEku1z0hiDnIKQLjsW9w5nwbBOgzV420qvaLsKR48zUI6TIcxY1Exm +Iy1bz0MXUJFMLIn3ys624BplB/WUtctQI+U3T2/M3Nw5Tsjzb4AwwBqtwRJF8LR+ +U1AIoHjhSP3Tk8Gou4xpUwcZ8XZ2PVJ612U9C3nDgUyWG5y/86mBPGGxsVV55lQw +KnbnKYaxAgMBAAECggEAK8IctmlY3srmREeRTwnCPj3voFICNEKn3HPGlGOxTWAk +IlGl3cq4A1zAel8c+sjSelQpDmDy/5cQm6AmKsA1a5kE36KMVeNkmNZwJG0h36ge +fOsaPKmaEj5J3MbynlQgDTOFHv9IM/6xRso1YcR4Hs7e4Z7/GKnBr5juJeyWSBSr +B0V6rb0pZZbTJ5V5rgQX8luWEgVNXZikkdsbBhfRdgNLzB/dil3fV7PxoxZUkvQl +6cem0HRnoB/nOmIVq8BfaCm+kSCe6JBZMrP62eLSaiJr2sVUyxZatEcGRWe0MGmB +3WxSwxtPzM2A+cNLqgIoubAGsVb4x3Jv5Iqc6HRa9QKBgQDqICCwTHa7inv9dxd7 +jp27KclnDQtnGwe4Lscqe8jtlaZOfXF5FAR7HCdZeNe1gJb+RYc3y6BdWJYz4WjV +yzYrwcUKy0XlGd3CkanndDYkoEVEYtdLYG2zJmZuaiZazLROUAs9p0uALAHfrWod +Lv5W+wE3o0qizt+QvnMTDrC6XQKBgQDMkJosqoLDwrAjFOSkuggUN/aqcUjnf/Eg +JIKDLKqouofZKRylJEQFPmMsf9GL7q6e6lyrrpdikzal95JyGfkwN7L1FRGF/dAn +ns8Ivs6aZbvQ1bN0hIhSNI28aAf407JneXvM8YGVOjPwI17m/g/38D0JaKLcKYSF +oIt9MjAAZQKBgQDRIgOxF4xTt79LyQ6b2ugAYLI8MAXIgvehSX+07j+sIiodIKa0 +3fGmup5XqL8erQ+zcA36BBVqbJA3JZmMp+nqqjrFipATtshFXfJkAoW+r79P9+6S +sT8scRe8d2ttXj+NWKjB2OdzRVwjHneUO/8LWUlcFqu7xIkldUm7czrgLQKBgQCC +R42I6McC+ZjHnuTG7Bt9FO8pOaCenuLn/5iHBgliD/m4mfA+VExlsofirTy9C7N2 +Tffa9wQ+qVbieDtyI4yJ4s88OsDAPxc0RHXCMhLOTgBBTfhOOqG/CO1DklWRaPFz +6PCpYx2N9lVhmIU2Q5PT6dJVjag64s4ddFOh1jav6QKBgBy33Sr6pGcVG9nzES2p +MWCClDkSMAoNnHS5OmLwOkGvldwhyzZ4rL0mVWqjPCpMbUoIXTBjy0OaUJLIQ9Nj +si7ZSa4ChR6Tf7gDF/Ea5IM5mVacs41TSX16aFCmpyW+C6HAdeoJxvslTm0N4Gg6 +ysPybT9r313v2eY81F17lpgL +-----END PRIVATE KEY----- diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/health-check.sh b/integration-tests/src/main/docker/health-check.sh similarity index 100% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/health-check.sh rename to integration-tests/src/main/docker/health-check.sh diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/keycloak/benchmark-realm.json b/integration-tests/src/main/docker/keycloak/benchmark-realm.json similarity index 100% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/keycloak/benchmark-realm.json rename to integration-tests/src/main/docker/keycloak/benchmark-realm.json diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/keycloak/integration-realm.json b/integration-tests/src/main/docker/keycloak/integration-realm.json similarity index 100% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/docker/keycloak/integration-realm.json rename to integration-tests/src/main/docker/keycloak/integration-realm.json diff --git a/integration-tests/src/main/resources/application.properties b/integration-tests/src/main/resources/application.properties new file mode 100644 index 00000000..927e29a3 --- /dev/null +++ b/integration-tests/src/main/resources/application.properties @@ -0,0 +1,3 @@ +# Integration Test Configuration +# Runtime configuration is provided via docker-compose.yml environment variables. +# This file is kept for classpath resource references only. diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/resources/test-jwks.json b/integration-tests/src/main/resources/test-jwks.json similarity index 100% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/resources/test-jwks.json rename to integration-tests/src/main/resources/test-jwks.json diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/resources/test-public-key.pem b/integration-tests/src/main/resources/test-public-key.pem similarity index 100% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/main/resources/test-public-key.pem rename to integration-tests/src/main/resources/test-public-key.pem diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/test/java/de/cuioss/sheriff/api/integration/ApiSheriffIntegrationIT.java b/integration-tests/src/test/java/de/cuioss/sheriff/api/integration/ApiSheriffIntegrationIT.java similarity index 67% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/test/java/de/cuioss/sheriff/api/integration/ApiSheriffIntegrationIT.java rename to integration-tests/src/test/java/de/cuioss/sheriff/api/integration/ApiSheriffIntegrationIT.java index ffde8f57..f2ece8e8 100644 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/test/java/de/cuioss/sheriff/api/integration/ApiSheriffIntegrationIT.java +++ b/integration-tests/src/test/java/de/cuioss/sheriff/api/integration/ApiSheriffIntegrationIT.java @@ -1,12 +1,12 @@ /* - * Copyright 2025 the original author or authors. - *

+ * Copyright ยฉ 2025 CUI-OpenSource-Software (info@cuioss.de) + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - *

- * https://www.apache.org/licenses/LICENSE-2.0 - *

+ * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -28,18 +28,18 @@ * * @author API Sheriff Team */ -public class ApiSheriffIntegrationIT extends BaseIntegrationTest { +class ApiSheriffIntegrationIT extends BaseIntegrationTest { /** * Test that the health endpoint returns a successful response, * indicating that API Sheriff is properly configured. */ @Test - public void testApiSheriffHealthEndpoint() { + void apiSheriffHealthEndpoint() { given() - .when() - .get("/test/health") - .then() + .when() + .get("/api/health") + .then() .statusCode(200) .contentType("application/json") .body("status", is("UP")) @@ -50,39 +50,41 @@ public void testApiSheriffHealthEndpoint() { * Test that the info endpoint returns expected information. */ @Test - public void testInfoEndpoint() { + void infoEndpoint() { given() - .when() - .get("/test/info") - .then() + .when() + .get("/api/info") + .then() .statusCode(200) .contentType("application/json") - .body("message", containsString("API Sheriff Integration Test")) + .body("message", containsString("API Sheriff Gateway")) .body("version", containsString("1.0.0-SNAPSHOT")); } /** - * Test that the Quarkus health check endpoint is available. + * Test that the Quarkus health check endpoint is available on the management interface. */ @Test - public void testQuarkusHealthEndpoint() { + void quarkusHealthEndpoint() { given() - .when() + .baseUri(managementBaseUri()) + .when() .get("/q/health") - .then() + .then() .statusCode(200) .contentType("application/json"); } /** - * Test that metrics endpoint is available (from Micrometer integration). + * Test that metrics endpoint is available on the management interface. */ @Test - public void testMetricsEndpoint() { + void metricsEndpoint() { given() - .when() + .baseUri(managementBaseUri()) + .when() .get("/q/metrics") - .then() + .then() .statusCode(200); } } \ No newline at end of file diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/test/java/de/cuioss/sheriff/api/integration/BaseIntegrationTest.java b/integration-tests/src/test/java/de/cuioss/sheriff/api/integration/BaseIntegrationTest.java similarity index 68% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/test/java/de/cuioss/sheriff/api/integration/BaseIntegrationTest.java rename to integration-tests/src/test/java/de/cuioss/sheriff/api/integration/BaseIntegrationTest.java index 474c3be6..92530ddb 100644 --- a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/test/java/de/cuioss/sheriff/api/integration/BaseIntegrationTest.java +++ b/integration-tests/src/test/java/de/cuioss/sheriff/api/integration/BaseIntegrationTest.java @@ -1,12 +1,12 @@ /* - * Copyright 2025 the original author or authors. - *

+ * Copyright ยฉ 2025 CUI-OpenSource-Software (info@cuioss.de) + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - *

- * https://www.apache.org/licenses/LICENSE-2.0 - *

+ * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -28,6 +28,7 @@ public abstract class BaseIntegrationTest { private static final String DEFAULT_TEST_PORT = "10443"; + private static final String DEFAULT_MANAGEMENT_PORT = "19000"; @BeforeAll static void setUpBaseIntegrationTest() { @@ -39,6 +40,19 @@ static void setUpBaseIntegrationTest() { String testPort = System.getProperty("test.https.port", DEFAULT_TEST_PORT); RestAssured.port = Integer.parseInt(testPort); + // cui-rewrite:disable CuiLoggerStandardsRecipe System.out.println("Integration tests configured for HTTPS port: " + testPort); } + + /** + * Returns the management interface base URI (plain HTTP, port 19000). + * Health and metrics endpoints are served on the management port + * when {@code quarkus.management.enabled=true}. + * + * @return management base URI for health/metrics endpoints + */ + static String managementBaseUri() { + String managementPort = System.getProperty("test.management.port", DEFAULT_MANAGEMENT_PORT); + return "http://localhost:" + managementPort; + } } \ No newline at end of file diff --git a/api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/test/resources/keys/test_public_key.pem b/integration-tests/src/test/resources/keys/test_public_key.pem similarity index 100% rename from api-sheriff-quarkus-parent/api-sheriff-quarkus-integration-tests/src/test/resources/keys/test_public_key.pem rename to integration-tests/src/test/resources/keys/test_public_key.pem diff --git a/pom.xml b/pom.xml index 825e0e3e..39126fb9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,8 +6,8 @@ de.cuioss cui-java-parent - 1.1.4 - + 1.4.4 + de.cuioss.sheriff.api api-sheriff-parent @@ -21,10 +21,9 @@ https://github.com/cuioss/api-sheriff/ - bom - api-sheriff-library - api-sheriff-quarkus-parent - benchmarking + api-sheriff + integration-tests + benchmarks https://github.com/cuioss/api-sheriff/ @@ -42,38 +41,268 @@ - - 2.5.1 - - - 1.37 - 3.23.3 - - - 1.15.1 - 3.2.0 - - - v20.19.2 - 10.5.0 + 21 + 3.32.1 + 0.4.1 + + + + + + io.quarkus + quarkus-bom + ${version.quarkus} + pom + import + + + de.cuioss + java-ee-orthogonal + ${version.cui.parent} + pom + import + + + de.cuioss + java-ee-10-bom + ${version.cui.parent} + pom + import + + + + de.cuioss.sheriff.api + api-sheriff + ${project.version} + + + de.cuioss.sheriff.api + api-sheriff + ${project.version} + test-jar + generators + test + + + + org.junit.platform + junit-platform-commons + ${version.junit.jupiter} + test + + + org.junit.platform + junit-platform-engine + ${version.junit.jupiter} + test + + + org.junit.platform + junit-platform-launcher + ${version.junit.jupiter} + test + + + + - - org.codehaus.mojo - exec-maven-plugin - ${exec.maven.plugin.version} + io.quarkus + quarkus-maven-plugin + ${version.quarkus} + true + + + + build + generate-code + generate-code-tests + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 21 + - - com.github.eirslett - frontend-maven-plugin - ${frontend.maven.plugin.version} + org.apache.maven.plugins + maven-surefire-plugin + + + org.jboss.logmanager.LogManager + ${project.build.testOutputDirectory}/logging.properties + ${maven.home} + @{argLine} + + false + false + false + true + + @{argLine} -XX:+IgnoreUnrecognizedVMOptions -Djava.awt.headless=true + + 0 + 60 + + false + false + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + org.jboss.logmanager.LogManager + ${project.build.testOutputDirectory}/logging.properties + ${maven.home} + @{argLine} + + false + false + false + true + @{argLine} -XX:+IgnoreUnrecognizedVMOptions -Djava.awt.headless=true + + false + false + + + **/*IT.java + **/*IntegrationTest.java + + + + + + integration-test + verify + + + - \ No newline at end of file + + + + coverage + + + + + org.jacoco + jacoco-maven-plugin + + + prepare-agent + + prepare-agent + + + true + ${project.build.directory}/jacoco.exec + + + + prepare-agent-integration + + prepare-agent-integration + + + ${project.build.directory}/jacoco-it.exec + true + + + + report + test + + report + + + ${project.build.directory}/site/jacoco + + XML + HTML + + + + + report-integration + post-integration-test + + report-integration + + + ${project.build.directory}/site/jacoco-it + + XML + HTML + + + + + merge-results + verify + + merge + + + + + ${project.build.directory} + + *.exec + + + + ${project.build.directory}/jacoco-merged.exec + + + + report-merged + verify + + report + + + ${project.build.directory}/jacoco-merged.exec + ${project.build.directory}/site/jacoco-merged + + XML + HTML + + + + + + + + + + org.jacoco + jacoco-maven-plugin + + + + + + ${project.build.directory}/site/jacoco/jacoco.xml, + api-sheriff/target/site/jacoco/jacoco.xml, + ../target/site/jacoco/jacoco.xml + + + + +