chore(merge): 整合最新 main 与统一认证变更
ci / verify (pull_request) Successful in 12m18s

合并远端生产 CI、依赖与 API 文档改动,保留统一认证和 SSF 能力。由于远端生产基线已占用 0062,将尚未发布的身份迁移整理为 0063 至 0068 的最终增量 Schema,移除仅用于开发期草稿升级的破坏性迁移步骤。

验证:go test ./...。其余生产门禁在合并提交后继续执行。
This commit is contained in:
2026-07-17 19:06:37 +08:00
100 changed files with 9673 additions and 1799 deletions
+43 -51
View File
@@ -14,42 +14,41 @@ import (
"github.com/jackc/pgx/v5/pgxpool"
)
func TestSecurityEventIdempotencyRepairMigrationExists(t *testing.T) {
payload, err := os.ReadFile("../../migrations/0064_security_event_connection_idempotency_repair.sql")
func TestSecurityEventSchemaMigrationsDefineCurrentLifecycle(t *testing.T) {
streamPayload, err := os.ReadFile("../../migrations/0063_oidc_security_events.sql")
if err != nil {
t.Fatalf("read repair migration: %v", err)
t.Fatalf("read security event stream migration: %v", err)
}
sql := string(payload)
streamSQL := string(streamPayload)
for _, statement := range []string{
"CREATE TABLE IF NOT EXISTS gateway_security_event_connection_idempotency",
"CREATE INDEX IF NOT EXISTS idx_gateway_security_event_connection_idempotency_created",
} {
if !strings.Contains(sql, statement) {
t.Fatalf("repair migration is missing %q", statement)
}
}
}
func TestSecurityEventVerificationStateRepairMigrationExists(t *testing.T) {
payload, err := os.ReadFile("../../migrations/0065_security_event_verification_state_repair.sql")
if err != nil {
t.Fatalf("read verification state repair migration: %v", err)
}
sql := string(payload)
for _, statement := range []string{
"ADD COLUMN IF NOT EXISTS stream_status",
"ADD COLUMN IF NOT EXISTS created_at",
"CREATE TABLE IF NOT EXISTS gateway_security_event_stream_state",
"CREATE TABLE IF NOT EXISTS gateway_security_event_verification_challenges",
"CREATE INDEX IF NOT EXISTS idx_gateway_security_event_challenges_expiry",
} {
if !strings.Contains(sql, statement) {
t.Fatalf("verification state repair migration is missing %q", statement)
if !strings.Contains(streamSQL, statement) {
t.Fatalf("security event stream migration is missing %q", statement)
}
}
connectionPayload, err := os.ReadFile("../../migrations/0064_security_event_connections.sql")
if err != nil {
t.Fatalf("read security event connection migration: %v", err)
}
connectionSQL := string(connectionPayload)
for _, statement := range []string{
"CREATE TABLE IF NOT EXISTS gateway_security_event_connections",
"management_client_id text",
"management_credential_ref text",
"CREATE TABLE IF NOT EXISTS gateway_security_event_connection_idempotency",
} {
if !strings.Contains(connectionSQL, statement) {
t.Fatalf("security event connection migration is missing %q", statement)
}
}
}
func TestIdentityConfigurationRevisionMigrationDefinesSecretReferencesAndSingleActiveRevision(t *testing.T) {
payload, err := os.ReadFile("../../migrations/0067_identity_configuration_revisions.sql")
payload, err := os.ReadFile("../../migrations/0065_identity_configuration_revisions.sql")
if err != nil {
t.Fatal(err)
}
@@ -58,6 +57,7 @@ func TestIdentityConfigurationRevisionMigrationDefinesSecretReferencesAndSingleA
"CREATE TABLE IF NOT EXISTS gateway_identity_configuration_revisions",
"machine_credential_ref text",
"session_encryption_key_ref text",
"security_event_configuration_url text",
"WHERE state = 'active'",
"CHECK (state IN ('draft','validated','active','superseded','failed'))",
} {
@@ -73,7 +73,7 @@ func TestIdentityConfigurationRevisionMigrationDefinesSecretReferencesAndSingleA
}
func TestIdentityOnboardingExchangeMigrationStoresOnlySecretReferences(t *testing.T) {
payload, err := os.ReadFile("../../migrations/0068_identity_onboarding_exchanges.sql")
payload, err := os.ReadFile("../../migrations/0066_identity_onboarding_exchanges.sql")
if err != nil {
t.Fatal(err)
}
@@ -81,7 +81,8 @@ func TestIdentityOnboardingExchangeMigrationStoresOnlySecretReferences(t *testin
for _, required := range []string{
"create table if not exists gateway_identity_onboarding_exchanges",
"exchange_token_ref text not null",
"security_event_configuration_url text",
"cleanup_status text not null default 'none'",
"security_event_credential_handoff_unsafe",
} {
if !strings.Contains(content, required) {
t.Fatalf("identity onboarding migration is missing %q", required)
@@ -94,8 +95,8 @@ func TestIdentityOnboardingExchangeMigrationStoresOnlySecretReferences(t *testin
}
}
func TestIdentityPairingCancellationMigrationSeparatesCleanupLifecycle(t *testing.T) {
payload, err := os.ReadFile("../../migrations/0069_identity_pairing_cancellation.sql")
func TestIdentityOnboardingMigrationDefinesCancellationLifecycle(t *testing.T) {
payload, err := os.ReadFile("../../migrations/0066_identity_onboarding_exchanges.sql")
if err != nil {
t.Fatal(err)
}
@@ -106,9 +107,6 @@ func TestIdentityPairingCancellationMigrationSeparatesCleanupLifecycle(t *testin
"cleanup_completed_at",
"idx_gateway_identity_onboarding_cleanup",
"last_error_category",
"set last_error_category = 'pairing_step_failed'",
"set local lock_timeout = '10s'",
"set local statement_timeout = '60s'",
} {
if !strings.Contains(content, required) {
t.Fatalf("identity pairing cancellation migration is missing %q", required)
@@ -121,14 +119,14 @@ func TestIdentityPairingCancellationMigrationSeparatesCleanupLifecycle(t *testin
}
}
func TestIdentityPairingErrorCategoryUpgradeMigrationExists(t *testing.T) {
payload, err := os.ReadFile("../../migrations/0074_identity_pairing_error_categories.sql")
func TestIdentityOnboardingMigrationDefinesFinalErrorCategories(t *testing.T) {
payload, err := os.ReadFile("../../migrations/0066_identity_onboarding_exchanges.sql")
if err != nil {
t.Fatal(err)
}
content := strings.ToLower(string(payload))
for _, required := range []string{
"drop constraint if exists gateway_identity_onboarding_error_category_check",
"gateway_identity_onboarding_error_category_check",
"security_event_credential_handoff_unsafe",
"security_event_connection_binding_missing",
"security_event_connection_binding_unavailable",
@@ -142,18 +140,14 @@ func TestIdentityPairingErrorCategoryUpgradeMigrationExists(t *testing.T) {
}
}
func TestIdentityPairingErrorCategoryUpgradeMigrationExecutesAgainstCurrentSchema(t *testing.T) {
func TestIdentityPairingErrorCategoriesExecuteAgainstCurrentSchema(t *testing.T) {
pool := newIdentityMigrationPostgresTestSchema(t)
ctx := context.Background()
for _, migration := range []string{
"../../migrations/0067_identity_configuration_revisions.sql",
"../../migrations/0068_identity_onboarding_exchanges.sql",
"../../migrations/0069_identity_pairing_cancellation.sql",
"../../migrations/0070_identity_secret_cleanup_queue.sql",
"../../migrations/0071_identity_pairing_start_reservation.sql",
"../../migrations/0072_identity_secret_cleanup_claim_lifecycle.sql",
"../../migrations/0073_identity_pairing_start_reservation_upgrade.sql",
"../../migrations/0074_identity_pairing_error_categories.sql",
"../../migrations/0065_identity_configuration_revisions.sql",
"../../migrations/0066_identity_onboarding_exchanges.sql",
"../../migrations/0067_identity_secret_cleanup_queue.sql",
"../../migrations/0068_identity_pairing_start_reservation.sql",
} {
applyIdentityMigrationTestFile(t, ctx, pool, migration)
}
@@ -193,7 +187,7 @@ SET last_error_category='credential_secret_leaked' WHERE id=$1::uuid`, pairingID
}
func TestIdentitySecretCleanupQueueMigrationStoresOnlyReferences(t *testing.T) {
payload, err := os.ReadFile("../../migrations/0070_identity_secret_cleanup_queue.sql")
payload, err := os.ReadFile("../../migrations/0067_identity_secret_cleanup_queue.sql")
if err != nil {
t.Fatal(err)
}
@@ -219,11 +213,11 @@ func TestIdentitySecretCleanupQueueMigrationStoresOnlyReferences(t *testing.T) {
}
}
func TestIdentityPairingCancellationMigrationExecutesAgainstLegacyData(t *testing.T) {
func TestIdentityPairingCancellationLifecycleRejectsPartialStates(t *testing.T) {
pool := newIdentityMigrationPostgresTestSchema(t)
ctx := context.Background()
applyIdentityMigrationTestFile(t, ctx, pool, "../../migrations/0067_identity_configuration_revisions.sql")
applyIdentityMigrationTestFile(t, ctx, pool, "../../migrations/0068_identity_onboarding_exchanges.sql")
applyIdentityMigrationTestFile(t, ctx, pool, "../../migrations/0065_identity_configuration_revisions.sql")
applyIdentityMigrationTestFile(t, ctx, pool, "../../migrations/0066_identity_onboarding_exchanges.sql")
revisionID, pairingID := uuid.NewString(), uuid.NewString()
if _, err := pool.Exec(ctx, `
@@ -236,13 +230,11 @@ INSERT INTO gateway_identity_configuration_revisions (
if _, err := pool.Exec(ctx, `
INSERT INTO gateway_identity_onboarding_exchanges (
id,revision_id,remote_exchange_id,exchange_token_ref,status,remote_version,expires_at,last_error_category
) VALUES ($1::uuid,$2::uuid,$3::uuid,$4,'credentials_saved',4,now() + interval '30 minutes','secret_token_abcdef')`,
) VALUES ($1::uuid,$2::uuid,$3::uuid,$4,'credentials_saved',4,now() + interval '30 minutes','pairing_step_failed')`,
pairingID, revisionID, uuid.NewString(), "identity-exchange-"+pairingID); err != nil {
t.Fatalf("seed legacy onboarding exchange: %v", err)
}
applyIdentityMigrationTestFile(t, ctx, pool, "../../migrations/0069_identity_pairing_cancellation.sql")
var status, cleanupStatus, errorCategory string
var cancelledAt, cleanupCompletedAt *time.Time
if err := pool.QueryRow(ctx, `