Files
crank/crates/crank-registry/tests/integration/migrations/artifact_metadata.rs
T
bsodfather bc03c33387
CI / Rust Checks (push) Failing after 4m6s
CI / UI Checks (push) Has been skipped
CI / Frontend E2E (push) Has been skipped
CI / Community Image Smoke (push) Has been skipped
CI / Deploy (push) Has been skipped
fix(openapi): harden story 2.1 production lifecycle
2026-08-29 00:48:22 +03:00

219 lines
9.1 KiB
Rust

use super::*;
#[tokio::test]
async fn healthy_v11_upgrades_to_v13_without_rewriting_prior_ledger() {
let database_url = crank_test_support::postgres_schema_url("test_v11_to_v12_artifacts").await;
let pool = sqlx::PgPool::connect(&database_url).await.unwrap();
MigrationAuthority::apply(&pool).await.unwrap();
let prior = sqlx::query(
"select version, name, checksum, applied_at
from __crank_migrations where version <= 11 order by version",
)
.fetch_all(&pool)
.await
.unwrap()
.into_iter()
.map(|row| {
(
row.get::<i64, _>("version"),
row.get::<String, _>("name"),
row.get::<String, _>("checksum"),
row.get::<time::OffsetDateTime, _>("applied_at"),
)
})
.collect::<Vec<_>>();
remove_v12_schema(&pool).await;
assert_eq!(
MigrationAuthority::preflight(&pool).await.unwrap(),
MigrationPreflight::MigrationRequired {
current: 11,
target: 13,
}
);
MigrationAuthority::apply(&pool).await.unwrap();
let after = sqlx::query(
"select version, name, checksum, applied_at
from __crank_migrations where version <= 11 order by version",
)
.fetch_all(&pool)
.await
.unwrap()
.into_iter()
.map(|row| {
(
row.get::<i64, _>("version"),
row.get::<String, _>("name"),
row.get::<String, _>("checksum"),
row.get::<time::OffsetDateTime, _>("applied_at"),
)
})
.collect::<Vec<_>>();
assert_eq!(after, prior);
let v13_applied_at: time::OffsetDateTime =
sqlx::query_scalar("select applied_at from __crank_migrations where version = 13")
.fetch_one(&pool)
.await
.unwrap();
MigrationAuthority::apply(&pool).await.unwrap();
let replayed_at: time::OffsetDateTime =
sqlx::query_scalar("select applied_at from __crank_migrations where version = 13")
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(replayed_at, v13_applied_at);
}
#[tokio::test]
async fn v12_guard_still_rejects_column_constraint_index_and_relation_drift() {
let database_url = crank_test_support::postgres_schema_url("test_v12_artifact_drift").await;
let pool = sqlx::PgPool::connect(&database_url).await.unwrap();
MigrationAuthority::apply(&pool).await.unwrap();
for (drift, restore) in [
(
"alter table artifact_blobs add column unexpected text null;",
"alter table artifact_blobs drop column unexpected;",
),
(
"create index unrelated_idx on artifact_sources(source_id) include (mime_type);",
"drop index unrelated_idx;",
),
(
"alter table artifact_blobs alter column storage_lifecycle set default 'unavailable';",
"alter table artifact_blobs alter column storage_lifecycle set default 'available';",
),
(
"alter table artifact_sources alter column mime_type drop not null;",
"alter table artifact_sources alter column mime_type set not null;",
),
(
"alter table artifact_blobs drop constraint artifact_blobs_size_check;
alter table artifact_blobs add constraint artifact_blobs_size_check
check (size_bytes between 1 and 262145);",
"alter table artifact_blobs drop constraint artifact_blobs_size_check;
alter table artifact_blobs add constraint artifact_blobs_size_check
check (size_bytes between 1 and 262144);",
),
(
"drop index artifact_sources_workspace_created_idx;
create index artifact_sources_workspace_created_idx
on artifact_sources(workspace_id, source_id, created_at);",
"drop index artifact_sources_workspace_created_idx;
create index artifact_sources_workspace_created_idx
on artifact_sources(workspace_id, created_at, source_id);",
),
(
"alter table artifact_sources alter column created_at type timestamptz(3);",
"alter table artifact_sources alter column created_at type timestamptz;",
),
(
"alter table artifact_sources enable row level security;",
"alter table artifact_sources disable row level security;",
),
(
"create policy unexpected_policy on artifact_sources using (true);",
"drop policy unexpected_policy on artifact_sources;",
),
(
"create function unrelated_trigger_fn() returns trigger language plpgsql as $$
begin return new; end
$$;
create trigger unexpected_trigger before insert on artifact_sources
for each row execute function unrelated_trigger_fn();",
"drop trigger unexpected_trigger on artifact_sources;
drop function unrelated_trigger_fn();",
),
(
"alter table artifact_sources set unlogged;",
"alter table artifact_sources set logged;",
),
(
"alter table artifact_sources drop constraint artifact_sources_id_check;
alter table artifact_sources add constraint artifact_sources_id_check
check (source_id ~ '^src_[a-zA-Z0-9_-]{1,128}$');",
"alter table artifact_sources drop constraint artifact_sources_id_check;
alter table artifact_sources add constraint artifact_sources_id_check
check (source_id ~ '^src_[A-Za-z0-9_-]{1,128}$');",
),
(
"alter table artifact_blobs drop constraint artifact_blobs_claim_shape_check;
alter table artifact_blobs add constraint artifact_blobs_claim_shape_check check (
claim_token is null and
(claim_expires_at is null or claim_token is not null) and
claim_expires_at is not null
);",
"alter table artifact_blobs drop constraint artifact_blobs_claim_shape_check;
alter table artifact_blobs add constraint artifact_blobs_claim_shape_check check (
(claim_token is null and claim_expires_at is null)
or (claim_token is not null and claim_expires_at is not null)
);",
),
] {
sqlx::raw_sql(drift).execute(&pool).await.unwrap();
let error = MigrationAuthority::preflight(&pool).await.unwrap_err();
assert_eq!(error.code(), "partial_sequence", "drift: {drift}");
assert_eq!(error.version(), Some(12), "drift: {drift}");
sqlx::raw_sql(restore).execute(&pool).await.unwrap();
assert_eq!(
MigrationAuthority::preflight(&pool).await.unwrap(),
MigrationPreflight::Current { version: 13 },
"restore: {restore}"
);
}
sqlx::query("alter table artifact_sources rename to artifact_sources_table")
.execute(&pool)
.await
.unwrap();
sqlx::query("create view artifact_sources as select * from artifact_sources_table")
.execute(&pool)
.await
.unwrap();
let error = MigrationAuthority::preflight(&pool).await.unwrap_err();
assert_eq!(error.code(), "partial_sequence");
assert_eq!(error.version(), Some(12));
}
#[tokio::test]
async fn v13_guard_rejects_wrong_cleanup_index_order_and_predicate() {
let database_url =
crank_test_support::postgres_schema_url("test_v13_cleanup_index_drift").await;
let pool = sqlx::PgPool::connect(&database_url).await.unwrap();
MigrationAuthority::apply(&pool).await.unwrap();
for (drift, restore) in [
(
"drop index artifact_sources_blob_lifecycle_detached_idx;
create index artifact_sources_blob_lifecycle_detached_idx
on artifact_sources(lifecycle, blob_digest, detached_at);",
"drop index artifact_sources_blob_lifecycle_detached_idx;
create index artifact_sources_blob_lifecycle_detached_idx
on artifact_sources(blob_digest, lifecycle, detached_at);",
),
(
"drop index artifact_sources_openapi_dangling_idx;
create index artifact_sources_openapi_dangling_idx
on artifact_sources(created_at, workspace_id, source_id)
where lifecycle = 'active';",
"drop index artifact_sources_openapi_dangling_idx;
create index artifact_sources_openapi_dangling_idx
on artifact_sources(created_at, workspace_id, source_id)
where lifecycle = 'active' and left(source_id, 12) = 'src_openapi_';",
),
] {
sqlx::raw_sql(drift).execute(&pool).await.unwrap();
let error = MigrationAuthority::preflight(&pool).await.unwrap_err();
assert_eq!(error.code(), "partial_sequence", "drift: {drift}");
assert_eq!(error.version(), Some(13), "drift: {drift}");
sqlx::raw_sql(restore).execute(&pool).await.unwrap();
assert_eq!(
MigrationAuthority::preflight(&pool).await.unwrap(),
MigrationPreflight::Current { version: 13 },
"restore: {restore}"
);
}
}