lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

migration_delivery_facts_tests.rs (5290B)


      1 use super::{
      2     tests::{connection, establish_runtime_version, pragma},
      3     *,
      4 };
      5 
      6 const FAILING_V16: &str = concat!(
      7     include_str!("migration/runtime/0016_authored_delivery_facts.up.sql"),
      8     "\nINSERT INTO missing_fixture_table VALUES (1);"
      9 );
     10 
     11 fn plan(current: u32, fail: bool) -> MigrationPlan {
     12     MigrationPlan {
     13         database: RUNTIME_DATABASE,
     14         application_id: RUNTIME_APPLICATION_ID,
     15         set_application_id_sql: SET_RUNTIME_APPLICATION_ID,
     16         minimum_version: runtime::MINIMUM_VERSION,
     17         current_version: current,
     18         steps: runtime::MIGRATIONS
     19             .iter()
     20             .take(current as usize)
     21             .map(|step| MigrationStep {
     22                 version: step.version(),
     23                 sql: if fail && step.version() == 16 {
     24                     FAILING_V16
     25                 } else {
     26                     runtime::migration_sql(step.version()).unwrap()
     27                 },
     28                 owned_objects: step.owned_objects(),
     29             })
     30             .collect(),
     31     }
     32 }
     33 
     34 #[tokio::test]
     35 async fn delivery_fact_upgrade_preserves_legacy_snapshots_receipts_and_cancelled_stop() {
     36     let mut connection = connection().await;
     37     establish_runtime_version(&mut connection, 15).await;
     38     let old = super::signed_facts_tests::seed(&mut connection).await;
     39     let bytes: Vec<u8> =
     40         sqlx::query_scalar("SELECT snapshot FROM radroots_runtime_authored_delivery_plans")
     41             .fetch_one(&mut connection)
     42             .await
     43             .unwrap();
     44     let mut wire: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
     45     wire["state"] = serde_json::json!("cancelled");
     46     wire.as_object_mut().unwrap().remove("delivery_facts");
     47     wire.as_object_mut()
     48         .unwrap()
     49         .remove("stop_requested_at_unix_ms");
     50     let historical = serde_json::to_vec(&wire).unwrap();
     51     sqlx::query(
     52         "UPDATE radroots_runtime_authored_delivery_plans SET state = 'cancelled', snapshot = ?",
     53     )
     54     .bind(&historical)
     55     .execute(&mut connection)
     56     .await
     57     .unwrap();
     58     assert!(matches!(
     59         migrate(&mut connection, OpenMode::ReadOnly, &plan(16, false)).await,
     60         Err(Error::SchemaMigrationRequired {
     61             actual: 15,
     62             current: 16,
     63             ..
     64         })
     65     ));
     66     assert_eq!(
     67         migrate(
     68             &mut connection,
     69             OpenMode::ReadWriteExisting,
     70             &plan(16, false)
     71         )
     72         .await
     73         .unwrap()
     74         .applied(),
     75         1
     76     );
     77     assert_eq!(pragma(&mut connection, "user_version").await, 16);
     78     super::signed_facts_tests::retained(&mut connection, &old).await;
     79     let (actual, stop): (Vec<u8>, Option<i64>) = sqlx::query_as(
     80         "SELECT snapshot, stop_requested_at_unix_ms FROM radroots_runtime_authored_delivery_plans",
     81     )
     82     .fetch_one(&mut connection)
     83     .await
     84     .unwrap();
     85     assert_eq!(actual, historical);
     86     assert_eq!(stop, Some(10));
     87     let decoded: radroots_storage::authored_delivery::AuthoredDeliveryPlan =
     88         serde_json::from_slice(&actual).unwrap();
     89     assert_eq!(decoded.stop_requested_at_unix_ms(), Some(10));
     90     assert!(decoded.delivery_facts().is_empty());
     91     for mode in [OpenMode::ReadOnly, OpenMode::ReadWriteExisting] {
     92         assert!(matches!(
     93             migrate(&mut connection, mode, &plan(15, false)).await,
     94             Err(Error::SchemaTooNew {
     95                 actual: 16,
     96                 supported: 15,
     97                 ..
     98             })
     99         ));
    100     }
    101     connection.close().await.unwrap();
    102 }
    103 
    104 #[tokio::test]
    105 async fn failed_delivery_fact_migration_rolls_back_every_new_schema_object() {
    106     let mut connection = connection().await;
    107     establish_runtime_version(&mut connection, 15).await;
    108     let old = super::signed_facts_tests::seed(&mut connection).await;
    109     assert!(
    110         migrate(
    111             &mut connection,
    112             OpenMode::ReadWriteExisting,
    113             &plan(16, true)
    114         )
    115         .await
    116         .is_err()
    117     );
    118     assert_eq!(pragma(&mut connection, "user_version").await, 15);
    119     super::signed_facts_tests::retained(&mut connection, &old).await;
    120     assert!(
    121         sqlx::query(
    122             "SELECT stop_requested_at_unix_ms FROM radroots_runtime_authored_delivery_plans"
    123         )
    124         .fetch_all(&mut connection)
    125         .await
    126         .is_err()
    127     );
    128     assert_eq!(sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM sqlite_schema WHERE name LIKE 'radroots_runtime_authored_delivery_facts%' OR name = 'radroots_runtime_authored_delivery_stop_guard'").fetch_one(&mut connection).await.unwrap(), 0);
    129     migrate_runtime(&mut connection, OpenMode::ReadWriteExisting)
    130         .await
    131         .unwrap();
    132     super::signed_facts_tests::retained(&mut connection, &old).await;
    133     connection.close().await.unwrap();
    134 }
    135 
    136 #[test]
    137 fn delivery_fact_decision_binds_unchanged_historical_migrations_and_exact_successor() {
    138     let decision: serde_json::Value = serde_json::from_str(include_str!(
    139         "../../../contracts/architecture/decisions/authored_delivery_facts.v1.json"
    140     ))
    141     .unwrap();
    142     let migration = runtime::MIGRATIONS[15];
    143     assert_eq!(decision["migration"]["version"], migration.version());
    144     assert_eq!(decision["migration"]["name"], migration.name());
    145     assert_eq!(decision["migration"]["sha256"], migration.up_sha256());
    146 }