diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69811d8..b5155a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,21 @@ jobs: cache: maven - name: Maven Verify (Build + Tests + JaCoCo) - run: ./mvnw -B -T 1C -ntp verify + # -e fuer volle Stacktraces, --fail-at-end damit alle Module + # durchlaufen statt beim ersten Fehler abzubrechen. Kein -T 1C, + # bis die Backend-Tests stabil sind (parallele Modul-Ausfuehrung + # macht Surefire-Output beim Debuggen unleserlich). + run: ./mvnw -B -e -ntp --fail-at-end verify + + - name: Upload Surefire-Reports on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: surefire-reports-${{ github.run_number }} + path: | + **/target/surefire-reports/** + **/target/failsafe-reports/** + retention-days: 14 - name: Upload Backend-JAR (fuer E2E) uses: actions/upload-artifact@v4 @@ -177,19 +191,10 @@ jobs: --health-timeout=5s --health-retries=15 - keycloak: - image: quay.io/keycloak/keycloak:24.0 - env: - KEYCLOAK_ADMIN: admin - KEYCLOAK_ADMIN_PASSWORD: admin - KC_HEALTH_ENABLED: 'true' - ports: - - 8080:8080 - options: >- - --health-cmd="curl -fsS http://localhost:8080/health/ready || exit 1" - --health-interval=10s - --health-timeout=5s - --health-retries=30 + # Keycloak laeuft NICHT als services-Container: das Image braucht + # `start-dev` als Argument, und GHA-Services unterstuetzen weder + # `command` noch CMD-Override. Stattdessen `docker run` als Step + # (siehe "Start Keycloak"). env: CVM_SERVER_PORT: '8081' @@ -227,6 +232,16 @@ jobs: name: frontend-dist path: cvm-frontend/dist/cvm-frontend/browser + - name: Start Keycloak (start-dev) + run: | + set -euo pipefail + docker run -d --name keycloak \ + -p 8080:8080 \ + -e KEYCLOAK_ADMIN=admin \ + -e KEYCLOAK_ADMIN_PASSWORD=admin \ + -e KC_HEALTH_ENABLED=true \ + quay.io/keycloak/keycloak:24.0 start-dev + - name: Import Keycloak-Realm run: | set -euo pipefail @@ -237,6 +252,11 @@ jobs: break fi sleep 2 + if [ "$i" = "60" ]; then + echo "Keycloak nicht ready. Container-Log:" + docker logs keycloak | tail -100 + exit 1 + fi done TOKEN=$(curl -fsS -X POST \ -d "grant_type=password" \ @@ -301,6 +321,10 @@ jobs: working-directory: cvm-frontend run: npx playwright test + - name: Sammele Keycloak-Log + if: failure() + run: docker logs keycloak > keycloak.log 2>&1 || true + - name: Upload Playwright-Report if: failure() uses: actions/upload-artifact@v4 @@ -311,4 +335,5 @@ jobs: cvm-frontend/test-results/ backend.log frontend.log + keycloak.log retention-days: 14 diff --git a/cvm-persistence/src/test/java/com/ahs/cvm/persistence/AbstractPersistenceIntegrationsTest.java b/cvm-persistence/src/test/java/com/ahs/cvm/persistence/AbstractPersistenceIntegrationsTest.java index c6a53e8..080394e 100644 --- a/cvm-persistence/src/test/java/com/ahs/cvm/persistence/AbstractPersistenceIntegrationsTest.java +++ b/cvm-persistence/src/test/java/com/ahs/cvm/persistence/AbstractPersistenceIntegrationsTest.java @@ -1,6 +1,7 @@ package com.ahs.cvm.persistence; import com.ahs.cvm.persistence.support.DockerAvailability; +import java.util.UUID; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.test.context.DynamicPropertyRegistry; @@ -17,6 +18,15 @@ @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) public abstract class AbstractPersistenceIntegrationsTest { + /** + * Default-Mandant, der von Flyway-Migration V0023 als + * {@code is_default = TRUE} angelegt wird. Persistenz-Tests haben + * keine Aspect-Infrastruktur, die tenant_id automatisch fuellt - + * Fixtures setzen die Mandanten-Zuordnung deshalb explizit hierueber. + */ + protected static final UUID DEFAULT_TENANT_ID = + UUID.fromString("00000000-0000-0000-0000-000000000001"); + @SuppressWarnings("resource") static final PostgreSQLContainer POSTGRES = new PostgreSQLContainer<>( DockerImageName.parse("pgvector/pgvector:pg16") diff --git a/cvm-persistence/src/test/java/com/ahs/cvm/persistence/assessment/AssessmentImmutableTest.java b/cvm-persistence/src/test/java/com/ahs/cvm/persistence/assessment/AssessmentImmutableTest.java index efb5417..a0add6d 100644 --- a/cvm-persistence/src/test/java/com/ahs/cvm/persistence/assessment/AssessmentImmutableTest.java +++ b/cvm-persistence/src/test/java/com/ahs/cvm/persistence/assessment/AssessmentImmutableTest.java @@ -75,45 +75,53 @@ void assessmentSupersededErlaubt() { } private Assessment bereiteAssessmentVor() { - Product produkt = productRepository.save( - Product.builder().name("PortalCore-Test").key("pc-imm").build()); - ProductVersion pv = productVersionRepository.save( - ProductVersion.builder() - .product(produkt) - .version("1.0.0-imm") - .build()); - Environment env = environmentRepository.save( - Environment.builder() - .key("REF-IMM") - .name("Referenz Immutable") - .stage(EnvironmentStage.REF) - .build()); - Scan scan = scanRepository.save( - Scan.builder() - .productVersion(pv) - .sbomFormat("CycloneDX") - .sbomChecksum("sha256:imm") - .scannedAt(Instant.now()) - .build()); + Product produkt = productRepository.save(Product.builder() + .tenantId(DEFAULT_TENANT_ID) + .name("PortalCore-Test") + .key("pc-imm") + .build()); + ProductVersion pv = productVersionRepository.save(ProductVersion.builder() + .tenantId(DEFAULT_TENANT_ID) + .product(produkt) + .version("1.0.0-imm") + .build()); + Environment env = environmentRepository.save(Environment.builder() + .tenantId(DEFAULT_TENANT_ID) + .key("REF-IMM") + .name("Referenz Immutable") + .stage(EnvironmentStage.REF) + .build()); + Scan scan = scanRepository.save(Scan.builder() + .tenantId(DEFAULT_TENANT_ID) + .productVersion(pv) + .sbomFormat("CycloneDX") + .sbomChecksum("sha256:imm") + .scannedAt(Instant.now()) + .build()); Component comp = componentRepository.save( Component.builder() .purl("pkg:maven/org.test/imm@1.0") .name("imm") .version("1.0") .build()); - ComponentOccurrence occ = occurrenceRepository.save( - ComponentOccurrence.builder().scan(scan).component(comp).direct(true).build()); + ComponentOccurrence occ = occurrenceRepository.save(ComponentOccurrence.builder() + .tenantId(DEFAULT_TENANT_ID) + .scan(scan) + .component(comp) + .direct(true) + .build()); Cve cve = cveRepository.save( Cve.builder().cveId("CVE-2026-22610").source("NVD").build()); - Finding finding = findingRepository.save( - Finding.builder() - .scan(scan) - .componentOccurrence(occ) - .cve(cve) - .detectedAt(Instant.now()) - .build()); + Finding finding = findingRepository.save(Finding.builder() + .tenantId(DEFAULT_TENANT_ID) + .scan(scan) + .componentOccurrence(occ) + .cve(cve) + .detectedAt(Instant.now()) + .build()); Assessment assessment = Assessment.builder() + .tenantId(DEFAULT_TENANT_ID) .finding(finding) .productVersion(pv) .environment(env) diff --git a/cvm-persistence/src/test/java/com/ahs/cvm/persistence/product/ProductRepositoryIntegrationsTest.java b/cvm-persistence/src/test/java/com/ahs/cvm/persistence/product/ProductRepositoryIntegrationsTest.java index 4382c80..3c9a570 100644 --- a/cvm-persistence/src/test/java/com/ahs/cvm/persistence/product/ProductRepositoryIntegrationsTest.java +++ b/cvm-persistence/src/test/java/com/ahs/cvm/persistence/product/ProductRepositoryIntegrationsTest.java @@ -24,6 +24,7 @@ class ProductRepositoryIntegrationsTest extends AbstractPersistenceIntegrationsT @DisplayName("Product: save setzt id und createdAt via @PrePersist") void produktSpeichernSetztTechnischeFelder() { Product portalcore = Product.builder() + .tenantId(DEFAULT_TENANT_ID) .name("PortalCore-Test") .key("portalcore-test") .description("Referenz-Produkt") @@ -39,11 +40,15 @@ void produktSpeichernSetztTechnischeFelder() { @Test @DisplayName("ProductVersion: findByProductIdAndVersion liefert eindeutigen Treffer") void produktVersionLookup() { - Product produkt = productRepository.save( - Product.builder().name("SmileKH-Test").key("smile-test").build()); + Product produkt = productRepository.save(Product.builder() + .tenantId(DEFAULT_TENANT_ID) + .name("SmileKH-Test") + .key("smile-test") + .build()); ProductVersion version = productVersionRepository.save( ProductVersion.builder() + .tenantId(DEFAULT_TENANT_ID) .product(produkt) .version("1.14.2-test") .gitCommit("a3f9beef")