feat: complete Epic 1 production foundation
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn failed_consolidation_rolls_back_all_changes() {
|
||||
let _event_trigger_guard = EVENT_TRIGGER_TEST_LOCK.lock().await;
|
||||
let database_url = crank_test_support::postgres_schema_url("test_apply_rollback").await;
|
||||
let pool = sqlx::PgPool::connect(&database_url).await.unwrap();
|
||||
MigrationAuthority::apply(&pool).await.unwrap();
|
||||
sqlx::query(
|
||||
"insert into workspaces (id, slug, display_name, status, settings_json, created_at, updated_at)
|
||||
values ('rollback_preserved', 'rollback-preserved', 'Rollback Preserved', 'active', '{}'::jsonb, now(), now())",
|
||||
)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
sqlx::query(
|
||||
"drop table __crank_migrations, __crank_migration_legacy_audit,
|
||||
__crank_mcp_migrations, mcp_transport_sessions, __crank_ext_migrations",
|
||||
)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
let schema: String = sqlx::query_scalar("select current_schema()")
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
let failure_trigger = format!(
|
||||
"create function reject_story14_v2() returns event_trigger language plpgsql as $$
|
||||
begin
|
||||
if current_schema() = '{schema}' and current_query() like '%__crank_migrations%' then
|
||||
raise exception 'injected v2 ddl failure';
|
||||
end if;
|
||||
end $$;
|
||||
create event trigger reject_story14_v2 on ddl_command_start
|
||||
execute function reject_story14_v2();"
|
||||
);
|
||||
sqlx::raw_sql(sqlx::AssertSqlSafe(failure_trigger))
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
let error = MigrationAuthority::apply(&pool).await.unwrap_err();
|
||||
sqlx::raw_sql(
|
||||
"drop event trigger reject_story14_v2;
|
||||
drop function reject_story14_v2();",
|
||||
)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(error.code(), "apply_failed");
|
||||
for relation in [
|
||||
"__crank_migrations",
|
||||
"__crank_migration_legacy_audit",
|
||||
"__crank_mcp_migrations",
|
||||
"mcp_transport_sessions",
|
||||
"__crank_ext_migrations",
|
||||
] {
|
||||
let present: bool = sqlx::query_scalar(
|
||||
"select to_regclass(format('%I.%I', current_schema(), $1)) is not null",
|
||||
)
|
||||
.bind(relation)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!present, "{relation} must roll back with failed v2 DDL");
|
||||
}
|
||||
let preserved: String =
|
||||
sqlx::query_scalar("select display_name from workspaces where id = 'rollback_preserved'")
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(preserved, "Rollback Preserved");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn failed_request_trace_identity_migration_rolls_back_all_changes() {
|
||||
let _event_trigger_guard = EVENT_TRIGGER_TEST_LOCK.lock().await;
|
||||
let database_url =
|
||||
crank_test_support::postgres_schema_url("test_trace_identity_rollback").await;
|
||||
let pool = sqlx::PgPool::connect(&database_url).await.unwrap();
|
||||
MigrationAuthority::apply(&pool).await.unwrap();
|
||||
sqlx::raw_sql(
|
||||
"insert into operations
|
||||
(id, workspace_id, name, display_name, protocol, status, created_at, updated_at)
|
||||
values ('op_trace_rollback', 'ws_default', 'trace-rollback', 'Trace rollback',
|
||||
'rest', 'draft', now(), now());
|
||||
insert into invocation_logs
|
||||
(id, workspace_id, operation_id, source, level, status, tool_name, message,
|
||||
duration_ms, request_preview_json, response_preview_json, created_at)
|
||||
values ('trace_rollback_preserved', 'ws_default', 'op_trace_rollback', 'admin',
|
||||
'info', 'success', 'trace_rollback', 'safe preserved row', 1,
|
||||
'{}'::jsonb, '{}'::jsonb, now());",
|
||||
)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
remove_v4_schema(&pool).await;
|
||||
sqlx::query("delete from __crank_migrations where version = 3")
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
remove_v3_schema(&pool).await;
|
||||
let schema: String = sqlx::query_scalar("select current_schema()")
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
let failure_trigger = format!(
|
||||
"create function reject_story15_v3() returns event_trigger language plpgsql as $$
|
||||
begin
|
||||
if current_schema() = '{schema}' and current_query() like '%invocation_logs_workspace_trace_id_idx%' then
|
||||
raise exception 'injected v3 ddl failure';
|
||||
end if;
|
||||
end $$;
|
||||
create event trigger reject_story15_v3 on ddl_command_start
|
||||
execute function reject_story15_v3();"
|
||||
);
|
||||
sqlx::raw_sql(sqlx::AssertSqlSafe(failure_trigger))
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
let error = MigrationAuthority::apply(&pool).await.unwrap_err();
|
||||
sqlx::raw_sql(
|
||||
"drop event trigger reject_story15_v3;
|
||||
drop function reject_story15_v3();",
|
||||
)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(error.code(), "apply_failed");
|
||||
assert_eq!(error.version(), Some(3));
|
||||
let trace_column: bool = sqlx::query_scalar(
|
||||
"select exists (
|
||||
select 1 from information_schema.columns
|
||||
where table_schema = current_schema()
|
||||
and table_name = 'invocation_logs'
|
||||
and column_name = 'trace_id'
|
||||
)",
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!trace_column, "trace_id column must roll back with v3");
|
||||
for index in [
|
||||
"invocation_logs_workspace_request_id_idx",
|
||||
"invocation_logs_workspace_trace_id_idx",
|
||||
] {
|
||||
let present: bool = sqlx::query_scalar(
|
||||
"select to_regclass(format('%I.%I', current_schema(), $1)) is not null",
|
||||
)
|
||||
.bind(index)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!present, "{index} must roll back with failed v3 DDL");
|
||||
}
|
||||
let ledger_v3: i64 =
|
||||
sqlx::query_scalar("select count(*) from __crank_migrations where version = 3")
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(ledger_v3, 0, "failed v3 must not be recorded as applied");
|
||||
let preserved: String = sqlx::query_scalar(
|
||||
"select message from invocation_logs where id = 'trace_rollback_preserved'",
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(preserved, "safe preserved row");
|
||||
}
|
||||
Reference in New Issue
Block a user