package com.osglab.account.config import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldNotContain import java.nio.file.Files import java.nio.file.Path class DeploymentConsistencyTest : FunSpec({ val root = Path.of(System.getProperty("user.dir")) test("OpenAPI documents every mounted API route") { val openApi = root.read("docs/openapi.yaml") val documentedPaths = Regex("""(?m)^ (/[^:]+):\s*$""") .findAll(openApi) .map { it.groupValues[1] } .toSet() documentedPaths shouldBe EXPECTED_PUBLIC_PATHS } test("OpenAPI defines admin pagination and response contracts") { val openApi = root.read("docs/openapi.yaml") val sessionSchema = openApi .substringAfter(" AdminSessionState:") .substringBefore(" AdminLoginResponse:") sessionSchema shouldNotContain "csrfToken" openApi shouldContain "schema: { \$ref: \"#/components/schemas/AdminOverview\" }" openApi shouldContain "schema: { \$ref: \"#/components/schemas/AdminReferralOverview\" }" openApi shouldContain "schema: { \$ref: \"#/components/schemas/AdminUserPage\" }" openApi shouldContain "schema: { \$ref: \"#/components/schemas/AdminUserDetail\" }" openApi shouldContain "schema: { \$ref: \"#/components/schemas/AdminLedgerPage\" }" openApi shouldContain "schema: { \$ref: \"#/components/schemas/AdminAuditPage\" }" openApi shouldContain "pendingBindings" openApi shouldContain "ineligibleBindings" openApi shouldContain "chargedCredits" openApi shouldContain "referralCode" } test("provider defaults and Apple integrity contract stay production compatible") { val providerConfigurations = listOf( root.read("src/main/kotlin/com/osglab/account/config/AppConfig.kt"), root.read("src/main/resources/application.yaml"), root.read(".env.example"), root.read("compose.yaml"), ) providerConfigurations.forEach { configuration -> configuration shouldContain "wss://openspeech.bytedance.com/api/v3/sauc/bigmodel" configuration shouldNotContain "bigmodel_async" } val openApi = root.read("docs/openapi.yaml") openApi shouldContain "osg-app-attest-v1" openApi shouldContain "purpose=apple-sign-in" openApi shouldContain "identity_token_sha256=" openApi shouldContain "authorization_code_sha256=" openApi shouldContain "nonce_sha256=" openApi shouldContain "including the final line feed" openApi shouldContain "unpadded Base64URL" } test("production Compose reuses private MySQL and hardens the application container") { val compose = root.read("compose.yaml") compose shouldContain "127.0.0.1:\${ACCOUNT_BIND_PORT:-18080}:8080" compose shouldContain "ghcr.io/hkgood/osg-account-server" compose shouldContain "pull_policy: always" compose shouldContain "external: true" compose shouldContain "account-egress:" compose shouldContain "user: \"10001:10001\"" compose shouldContain "read_only: true" compose shouldContain "cap_drop:" compose shouldContain "no-new-privileges:true" compose shouldNotContain "image: mysql" compose shouldNotContain "3306:3306" compose shouldNotContain "0.0.0.0:" } test("admin bootstrap is one-time and runtime database grants stay explicit") { val compose = root.read("compose.yaml") val privileges = root.read("docs/mysql-minimum-privileges.sql") compose shouldContain "ADMIN_BOOTSTRAP_ENABLED: \${ADMIN_BOOTSTRAP_ENABLED:-false}" privileges shouldContain "GRANT SELECT ON osg_account.admin_operators" privileges shouldContain "GRANT INSERT, UPDATE ON osg_account.admin_operators" privileges shouldContain "GRANT SELECT ON osg_account.admin_sessions" privileges shouldContain "GRANT INSERT, UPDATE, DELETE ON osg_account.admin_sessions" privileges shouldContain "GRANT SELECT ON osg_account.gateway_grant_scopes" privileges shouldContain "GRANT INSERT ON osg_account.gateway_grant_scopes" privileges shouldContain "GRANT SELECT ON osg_account.gateway_refresh_tokens" privileges shouldContain "GRANT INSERT, UPDATE ON osg_account.gateway_refresh_tokens" privileges shouldContain "GRANT INSERT ON osg_account.admin_audit_log" privileges shouldContain "GRANT INSERT ON osg_account.admin_credit_grants" privileges shouldNotContain "UPDATE ON osg_account.admin_audit_log" privileges shouldNotContain "DELETE ON osg_account.admin_credit_grants" } test("container image remains non-root and read-only compatible") { val dockerfile = root.read("Dockerfile") val build = root.read("build.gradle.kts") dockerfile shouldContain "USER 10001:10001" dockerfile shouldContain "ENV HOME=/tmp" dockerfile shouldContain "http://127.0.0.1:8080/health/ready" dockerfile shouldNotContain "ENTRYPOINT [\"sh\"" dockerfile shouldNotContain "jansi.tmpdir" build shouldContain "configurations.configureEach" build shouldContain "exclude(group = \"org.fusesource.jansi\", module = \"jansi\")" build shouldContain "tasks.register(\"verifyRuntimeClasspath\")" } test("OpenResty proxies HTTP WebSocket invitations and both AASA paths safely") { val openResty = root.read("deploy/openresty-account.conf") openResty shouldContain "proxy_set_header Upgrade \$http_upgrade;" openResty shouldContain "proxy_set_header Connection \$connection_upgrade;" openResty shouldContain "location = /.well-known/apple-app-site-association" openResty shouldContain "location = /apple-app-site-association" openResty shouldContain "location ^~ /i/" Regex("""location \^~ /i/ \{\s+access_log off;""").containsMatchIn(openResty) shouldBe true openResty shouldNotContain "alias /www/wwwroot/osglab.com/apple-app-site-association" openResty shouldNotContain "unsafe-inline" } test("CI definition is singular and leaves MySQL lifecycle to Testcontainers") { val ci = root.read(".github/workflows/ci.yml") Regex("""(?m)^name: CI$""").findAll(ci).count() shouldBe 1 Regex("""(?m)^jobs:$""").findAll(ci).count() shouldBe 1 ci shouldContain "docker compose -f compose.yaml config --quiet" ci shouldContain "./gradlew --no-daemon clean test" ci shouldContain "./gradlew --no-daemon buildFatJar" ci shouldContain "docker/build-push-action@v6" ci shouldContain "packages: write" ci shouldNotContain "3306:3306" ci shouldNotContain "TEST_DB_" } test("AASA has one runtime template and no deploy-time identifier placeholder") { val aasa = root.read("src/main/resources/invite/apple-app-site-association.json") aasa shouldContain "\"{{APPLE_APP_ID}}\"" aasa shouldContain "\"/i/*\"" Files.exists(root.resolve("deploy/apple-app-site-association")) shouldBe false } }) private fun Path.read(relativePath: String): String = Files.readString(resolve(relativePath)) private val EXPECTED_PUBLIC_PATHS = setOf( "/health", "/health/live", "/health/ready", "/v1/auth/apple", "/v1/auth/refresh", "/v1/auth/logout", "/v1/account", "/v1/apple/events", "/v1/credits/balance", "/v1/credits/ledger", "/v1/credits/rates", "/v1/referrals", "/v1/referrals/me", "/v1/referrals/code", "/v1/referrals/redeem", "/v1/referrals/bind", "/v1/referrals/campaigns", "/v1/integrity/challenges", "/v1/integrity/attest", "/v1/integrity/assert", "/v1/gateway/catalog", "/v1/gateway/grants", "/v1/gateway/grants/refresh", "/v1/gateway/grants/{grantId}", "/v1/gateway/llm/{capability}", "/v1/gateway/asr", "/v1/gateway/asr/sessions", "/v1/gateway/asr/sessions/{sessionId}/stream", "/v1/admin/auth/session", "/v1/admin/auth/login", "/v1/admin/auth/logout", "/v1/admin/overview", "/v1/admin/referrals", "/v1/admin/users", "/v1/admin/users/{userId}", "/v1/admin/users/{userId}/ledger", "/v1/admin/credits/grants", "/v1/admin/operators/summary", "/v1/admin/operators", "/v1/admin/operators/{operatorId}/enable", "/v1/admin/operators/{operatorId}/disable", "/v1/admin/operators/{operatorId}/unlock", "/v1/admin/operators/{operatorId}/credentials/reset", "/v1/admin/operators/{operatorId}/sessions/revoke", "/v1/admin/audit", "/.well-known/apple-app-site-association", "/apple-app-site-association", "/i/{code}", )