mirror of
https://github.com/iflytek/skillhub.git
synced 2026-08-27 11:14:59 +00:00
Merge pull request #653 from iflytek/fix/identity-migration-version-collision
fix(db): resolve identity migration version collision
This commit is contained in:
commit
4d462b3f48
11 changed files with 36 additions and 20 deletions
|
|
@ -38,7 +38,7 @@ They did not connect to or modify the shared test-environment database.
|
|||
|
||||
| Scenario | Observable result |
|
||||
|---|---|
|
||||
| Fresh migration | Flyway V44 applied successfully and created `identity_provider_state` |
|
||||
| Fresh migration | Flyway V45 applied successfully and created `identity_provider_state` after the reserved V44 compliance index |
|
||||
| Fixed GitHub authority vector | `oauth2-github`, `https://github.com`, fingerprint `b2a93d58465e3de9e8b6cd127ba18425ae0f80c49c85f18f76086832923ca619`, state `READY` |
|
||||
| Concurrent first pin | Two application instances converged to one READY row with the same fingerprint; no unique-constraint error |
|
||||
| Legacy OAuth binding | Existing `identity_binding` row remained byte-for-byte equivalent while the provider moved through first pin to READY |
|
||||
|
|
@ -48,8 +48,8 @@ They did not connect to or modify the shared test-environment database.
|
|||
| Stale READY mismatch window | Recovery returned 409, persisted `AUTHORITY_MISMATCH`, retained the pinned authority/fingerprint, and wrote no recovery audit |
|
||||
| Transaction rollback | A forced audit insert failure returned 500; the provider state update rolled back and no audit record was added |
|
||||
| Unknown provider routes | Authorization and callback routes returned 403 without an upstream redirect |
|
||||
| V43 to V44 upgrade | A database initialized by `v0.2.15` upgraded successfully and retained its legacy OAuth binding |
|
||||
| Mixed-version and rollback | Current and `v0.2.15` servers were simultaneously healthy against the V44 database; the old provider endpoint returned 200 |
|
||||
| V43 to V45 upgrade | A database initialized by `v0.2.15` upgraded successfully through reserved V44 and retained its legacy OAuth binding |
|
||||
| Mixed-version and rollback | Current and `v0.2.15` servers were simultaneously healthy against the V45 database; the old provider endpoint returned 200 |
|
||||
| Redis session compatibility | A local session created by `v0.2.15` was accepted by the current server for the same user |
|
||||
|
||||
## Remaining integration gate
|
||||
|
|
@ -60,7 +60,7 @@ the merge commit, deploy them to the shared test environment, and verify:
|
|||
1. health, login catalog, and local-password login through the configured test
|
||||
domain;
|
||||
2. unknown provider authorization/callback rejection;
|
||||
3. V44 migration and READY provider state in the shared database;
|
||||
3. reserved V44 compatibility migration, V45 identity migration, and READY provider state in the shared database;
|
||||
4. existing Redis sessions and OAuth bindings;
|
||||
5. recovery authorization and audit behavior;
|
||||
6. logs contain no credentials or unexpected identity errors.
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ run_test() {
|
|||
}
|
||||
|
||||
run_test IdentityBindingV2MigrationPostgresTest
|
||||
run_test IdentityBindingV2ContractPostgresTest 46
|
||||
run_test IdentityBindingV2ContractPostgresTest 47
|
||||
run_test UserProfileFieldSourceMigrationPostgresTest
|
||||
run_test IdentityBindingV2PostgresIntegrationTest 47
|
||||
run_test IdentityProfileProvisioningPostgresIntegrationTest 47
|
||||
run_test IdentityBindingV2PostgresIntegrationTest 48
|
||||
run_test IdentityProfileProvisioningPostgresIntegrationTest 48
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
CREATE INDEX IF NOT EXISTS idx_skill_version_compliance_mappings
|
||||
ON skill_version
|
||||
USING GIN ((parsed_metadata_json -> 'frontmatter' -> 'x-astron-compliance'));
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
-- Binding V2 contract gate.
|
||||
--
|
||||
-- Deploy this migration only after every pre-Binding-V2 application instance
|
||||
-- has exited. Unlike the V45 expand migration, this gate rejects transactions
|
||||
-- has exited. Unlike the V46 expand migration, this gate rejects transactions
|
||||
-- that leave an ACTIVE binding without exactly one ACTIVE primary subject.
|
||||
|
||||
DO $$
|
||||
|
|
@ -37,7 +37,7 @@ class IdentityBindingV2ContractPostgresTest {
|
|||
.schemas(PREFLIGHT_SCHEMA)
|
||||
.defaultSchema(PREFLIGHT_SCHEMA)
|
||||
.createSchemas(true)
|
||||
.target(MigrationVersion.fromVersion("45"))
|
||||
.target(MigrationVersion.fromVersion("46"))
|
||||
.load()
|
||||
.migrate();
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ class IdentityBindingV2ContractPostgresTest {
|
|||
WHERE success = TRUE
|
||||
ORDER BY installed_rank DESC
|
||||
LIMIT 1
|
||||
""")).isEqualTo("45");
|
||||
""")).isEqualTo("46");
|
||||
assertThat(singleLong(
|
||||
statement,
|
||||
"""
|
||||
|
|
@ -126,7 +126,7 @@ class IdentityBindingV2ContractPostgresTest {
|
|||
database.username(),
|
||||
database.password())
|
||||
.locations("classpath:db/migration")
|
||||
.target(MigrationVersion.fromVersion("46"))
|
||||
.target(MigrationVersion.fromVersion("47"))
|
||||
.load()
|
||||
.migrate();
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class IdentityBindingV2MigrationPostgresTest {
|
|||
Flyway.configure()
|
||||
.dataSource(url, username, password)
|
||||
.locations("classpath:db/migration")
|
||||
.target(MigrationVersion.fromVersion("44"))
|
||||
.target(MigrationVersion.fromVersion("45"))
|
||||
.load()
|
||||
.migrate();
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ class IdentityBindingV2MigrationPostgresTest {
|
|||
Flyway.configure()
|
||||
.dataSource(url, username, password)
|
||||
.locations("classpath:db/migration")
|
||||
.target(MigrationVersion.fromVersion("45"))
|
||||
.target(MigrationVersion.fromVersion("46"))
|
||||
.load()
|
||||
.migrate();
|
||||
|
||||
|
|
@ -237,7 +237,7 @@ class IdentityBindingV2MigrationPostgresTest {
|
|||
.schemas(PREFLIGHT_SCHEMA)
|
||||
.defaultSchema(PREFLIGHT_SCHEMA)
|
||||
.createSchemas(true)
|
||||
.target(MigrationVersion.fromVersion("44"))
|
||||
.target(MigrationVersion.fromVersion("45"))
|
||||
.load()
|
||||
.migrate();
|
||||
|
||||
|
|
@ -315,7 +315,7 @@ class IdentityBindingV2MigrationPostgresTest {
|
|||
.schemas(PREFLIGHT_SCHEMA)
|
||||
.defaultSchema(PREFLIGHT_SCHEMA)
|
||||
.createSchemas(true)
|
||||
.target(MigrationVersion.fromVersion("45"))
|
||||
.target(MigrationVersion.fromVersion("46"))
|
||||
.load()
|
||||
.migrate());
|
||||
|
||||
|
|
@ -343,7 +343,7 @@ class IdentityBindingV2MigrationPostgresTest {
|
|||
WHERE success = TRUE
|
||||
ORDER BY installed_rank DESC
|
||||
LIMIT 1
|
||||
""")).isEqualTo("44");
|
||||
""")).isEqualTo("45");
|
||||
assertThat(singleLong(
|
||||
statement,
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class UserProfileFieldSourceMigrationPostgresTest {
|
|||
.schemas(SCHEMA)
|
||||
.defaultSchema(SCHEMA)
|
||||
.createSchemas(true)
|
||||
.target(MigrationVersion.fromVersion("46"))
|
||||
.target(MigrationVersion.fromVersion("47"))
|
||||
.load()
|
||||
.migrate();
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ class UserProfileFieldSourceMigrationPostgresTest {
|
|||
.schemas(SCHEMA)
|
||||
.defaultSchema(SCHEMA)
|
||||
.createSchemas(true)
|
||||
.target(MigrationVersion.fromVersion("47"))
|
||||
.target(MigrationVersion.fromVersion("48"))
|
||||
.load()
|
||||
.migrate();
|
||||
|
||||
|
|
|
|||
|
|
@ -102,11 +102,24 @@ class FlywayMigrationGuardrailTest {
|
|||
assertThat(migration).contains("bad_namespace.slug <> 'global'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void complianceIndexMigration_mustMatchReservedV44() throws IOException {
|
||||
String migration = Files.readString(
|
||||
migrationPath(
|
||||
"V44__skill_version_compliance_index.sql"));
|
||||
|
||||
assertThat(migration).isEqualTo("""
|
||||
CREATE INDEX IF NOT EXISTS idx_skill_version_compliance_mappings
|
||||
ON skill_version
|
||||
USING GIN ((parsed_metadata_json -> 'frontmatter' -> 'x-astron-compliance'));
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void identityBindingContractGate_mustRemainDeferred() throws IOException {
|
||||
String migration = Files.readString(
|
||||
migrationPath(
|
||||
"V46__identity_binding_v2_contract_gate.sql"));
|
||||
"V47__identity_binding_v2_contract_gate.sql"));
|
||||
|
||||
assertThat(migration)
|
||||
.contains("CREATE CONSTRAINT TRIGGER")
|
||||
|
|
@ -120,7 +133,7 @@ class FlywayMigrationGuardrailTest {
|
|||
throws IOException {
|
||||
String migration = Files.readString(
|
||||
migrationPath(
|
||||
"V47__user_profile_field_source.sql"));
|
||||
"V48__user_profile_field_source.sql"));
|
||||
|
||||
assertThat(migration)
|
||||
.contains("LEGACY_LOCAL")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue