services_hardening_state_catalog.rs (20705B)
1 #![forbid(unsafe_code)] 2 3 use std::error::Error; 4 5 use myc::{ 6 MYC_MIGRATION_CATALOG_SHA256, MYC_STATE_BASE_SCHEMA_VERSION, MYC_STATE_SCHEMA_CATALOG_SHA256, 7 MYC_STATE_SCHEMA_VERSION, MYC_STATE_SCHEMA_VERSION_1_OBJECT_COUNT, 8 MYC_STATE_SCHEMA_VERSION_1_SHA256, MYC_STATE_SCHEMA_VERSION_2_MIGRATION_SHA256, 9 MYC_STATE_SCHEMA_VERSION_2_OBJECT_COUNT, MYC_STATE_SCHEMA_VERSION_2_SHA256, 10 MYC_STATE_SCHEMA_VERSION_3_MIGRATION_SHA256, MYC_STATE_SCHEMA_VERSION_3_OBJECT_COUNT, 11 MYC_STATE_SCHEMA_VERSION_3_SHA256, MYC_STATE_SCHEMA_VERSION_4_MIGRATION_SHA256, 12 MYC_STATE_SCHEMA_VERSION_4_OBJECT_COUNT, MYC_STATE_SCHEMA_VERSION_4_SHA256, 13 MYC_STATE_SCHEMA_VERSION_5_MIGRATION_SHA256, MYC_STATE_SCHEMA_VERSION_5_OBJECT_COUNT, 14 MYC_STATE_SCHEMA_VERSION_5_SHA256, MYC_STATE_SCHEMA_VERSION_6_MIGRATION_SHA256, 15 MYC_STATE_SCHEMA_VERSION_6_OBJECT_COUNT, MYC_STATE_SCHEMA_VERSION_6_SHA256, 16 MYC_STATE_SCHEMA_VERSION_7_MIGRATION_SHA256, MYC_STATE_SCHEMA_VERSION_7_OBJECT_COUNT, 17 MYC_STATE_SCHEMA_VERSION_7_SHA256, MYC_STATE_SCHEMA_VERSION_8_MIGRATION_SHA256, 18 MYC_STATE_SCHEMA_VERSION_8_OBJECT_COUNT, MYC_STATE_SCHEMA_VERSION_8_SHA256, 19 MYC_STATE_SCHEMA_VERSION_9_MIGRATION_SHA256, MYC_STATE_SCHEMA_VERSION_9_OBJECT_COUNT, 20 MYC_STATE_SCHEMA_VERSION_9_SHA256, MYC_STATE_SCHEMA_VERSION_10_MIGRATION_SHA256, 21 MYC_STATE_SCHEMA_VERSION_10_OBJECT_COUNT, MYC_STATE_SCHEMA_VERSION_10_SHA256, 22 MYC_STATE_SCHEMA_VERSION_11_MIGRATION_SHA256, MYC_STATE_SCHEMA_VERSION_11_OBJECT_COUNT, 23 MYC_STATE_SCHEMA_VERSION_11_SHA256, MYC_STATE_SCHEMA_VERSION_12_MIGRATION_SHA256, 24 MYC_STATE_SCHEMA_VERSION_12_OBJECT_COUNT, MYC_STATE_SCHEMA_VERSION_12_SHA256, 25 MycStateCatalogErrorKind, myc_migration_catalog, myc_schema_catalog, 26 validate_myc_state_catalogs, 27 }; 28 use radroots_service_sqlite::{ 29 MigrationCatalog, MigrationChecksum, MigrationDescriptor, SchemaCatalog, SchemaDigest, 30 SchemaObject, SchemaObjectKind, SchemaVersionCatalog, 31 }; 32 33 const CATALOG_SOURCE: &str = include_str!("../src/state_catalog.rs"); 34 const LIB_SOURCE: &str = include_str!("../src/lib.rs"); 35 const MANIFEST: &str = include_str!("../Cargo.toml"); 36 37 #[test] 38 fn schema_v1_through_v12_and_all_migrations_have_exact_literal_identities() { 39 let migrations = myc_migration_catalog().expect("Myc migration catalog"); 40 let schema = myc_schema_catalog().expect("Myc schema catalog"); 41 42 assert_eq!(MYC_STATE_BASE_SCHEMA_VERSION, 1); 43 assert_eq!(MYC_STATE_SCHEMA_VERSION, 12); 44 assert_eq!(migrations.descriptors().len(), 11); 45 let metadata = &migrations.descriptors()[0]; 46 assert_eq!(metadata.target_version(), 2); 47 assert_eq!(metadata.name().as_str(), "create_myc_state_metadata"); 48 assert_eq!( 49 metadata.checksum().as_bytes(), 50 &MYC_STATE_SCHEMA_VERSION_2_MIGRATION_SHA256 51 ); 52 let request = &migrations.descriptors()[1]; 53 assert_eq!(request.target_version(), 3); 54 assert_eq!(request.name().as_str(), "create_nip46_request_admission"); 55 assert_eq!( 56 request.checksum().as_bytes(), 57 &MYC_STATE_SCHEMA_VERSION_3_MIGRATION_SHA256 58 ); 59 let connection = &migrations.descriptors()[2]; 60 assert_eq!(connection.target_version(), 4); 61 assert_eq!( 62 connection.name().as_str(), 63 "create_connection_authorization_state" 64 ); 65 assert_eq!( 66 connection.checksum().as_bytes(), 67 &MYC_STATE_SCHEMA_VERSION_4_MIGRATION_SHA256 68 ); 69 let governance = &migrations.descriptors()[3]; 70 assert_eq!(governance.target_version(), 5); 71 assert_eq!( 72 governance.name().as_str(), 73 "create_bounded_governance_state" 74 ); 75 assert_eq!( 76 governance.checksum().as_bytes(), 77 &MYC_STATE_SCHEMA_VERSION_5_MIGRATION_SHA256 78 ); 79 let delivery = &migrations.descriptors()[4]; 80 assert_eq!(delivery.target_version(), 6); 81 assert_eq!(delivery.name().as_str(), "create_delivery_evidence_state"); 82 assert_eq!( 83 delivery.checksum().as_bytes(), 84 &MYC_STATE_SCHEMA_VERSION_6_MIGRATION_SHA256 85 ); 86 let discovery = &migrations.descriptors()[5]; 87 assert_eq!(discovery.target_version(), 7); 88 assert_eq!(discovery.name().as_str(), "create_discovery_desired_state"); 89 assert_eq!( 90 discovery.checksum().as_bytes(), 91 &MYC_STATE_SCHEMA_VERSION_7_MIGRATION_SHA256 92 ); 93 let completion = &migrations.descriptors()[6]; 94 assert_eq!(completion.target_version(), 8); 95 assert_eq!( 96 completion.name().as_str(), 97 "create_nip46_operation_completion" 98 ); 99 assert_eq!( 100 completion.checksum().as_bytes(), 101 &MYC_STATE_SCHEMA_VERSION_8_MIGRATION_SHA256 102 ); 103 let response = &migrations.descriptors()[7]; 104 assert_eq!(response.target_version(), 9); 105 assert_eq!(response.name().as_str(), "create_nip46_atomic_response"); 106 assert_eq!( 107 response.checksum().as_bytes(), 108 &MYC_STATE_SCHEMA_VERSION_9_MIGRATION_SHA256 109 ); 110 let configuration = &migrations.descriptors()[8]; 111 assert_eq!(configuration.target_version(), 10); 112 assert_eq!( 113 configuration.name().as_str(), 114 "create_configuration_binding_history" 115 ); 116 assert_eq!( 117 configuration.checksum().as_bytes(), 118 &MYC_STATE_SCHEMA_VERSION_10_MIGRATION_SHA256 119 ); 120 let admin_operations = &migrations.descriptors()[9]; 121 assert_eq!(admin_operations.target_version(), 11); 122 assert_eq!( 123 admin_operations.name().as_str(), 124 "create_admin_operation_journal" 125 ); 126 assert_eq!( 127 admin_operations.checksum().as_bytes(), 128 &MYC_STATE_SCHEMA_VERSION_11_MIGRATION_SHA256 129 ); 130 let pending_responses = &migrations.descriptors()[10]; 131 assert_eq!(pending_responses.target_version(), 12); 132 assert_eq!( 133 pending_responses.name().as_str(), 134 "create_nip46_pending_response_authority" 135 ); 136 assert_eq!( 137 pending_responses.checksum().as_bytes(), 138 &MYC_STATE_SCHEMA_VERSION_12_MIGRATION_SHA256 139 ); 140 assert_eq!(migrations.current_version(), 12); 141 assert_eq!( 142 migrations.digest().as_bytes(), 143 &MYC_MIGRATION_CATALOG_SHA256 144 ); 145 146 assert_eq!(schema.versions().len(), 12); 147 assert_eq!(schema.versions()[0].version(), 1); 148 assert_eq!( 149 schema.versions()[0].object_count(), 150 MYC_STATE_SCHEMA_VERSION_1_OBJECT_COUNT 151 ); 152 assert_eq!( 153 schema.versions()[0].digest().as_bytes(), 154 &MYC_STATE_SCHEMA_VERSION_1_SHA256 155 ); 156 assert_eq!(schema.versions()[1].version(), 2); 157 assert_eq!( 158 schema.versions()[1].object_count(), 159 MYC_STATE_SCHEMA_VERSION_2_OBJECT_COUNT 160 ); 161 assert_eq!(schema.versions()[1].object_count(), 9); 162 assert_eq!( 163 schema.versions()[1].digest().as_bytes(), 164 &MYC_STATE_SCHEMA_VERSION_2_SHA256 165 ); 166 assert_eq!(schema.versions()[2].version(), 3); 167 assert_eq!( 168 schema.versions()[2].object_count(), 169 MYC_STATE_SCHEMA_VERSION_3_OBJECT_COUNT 170 ); 171 assert_eq!(schema.versions()[2].object_count(), 13); 172 assert_eq!( 173 schema.versions()[2].digest().as_bytes(), 174 &MYC_STATE_SCHEMA_VERSION_3_SHA256 175 ); 176 assert_eq!(schema.versions()[3].version(), 4); 177 assert_eq!( 178 schema.versions()[3].object_count(), 179 MYC_STATE_SCHEMA_VERSION_4_OBJECT_COUNT 180 ); 181 assert_eq!(schema.versions()[3].object_count(), 25); 182 assert_eq!( 183 schema.versions()[3].digest().as_bytes(), 184 &MYC_STATE_SCHEMA_VERSION_4_SHA256 185 ); 186 assert_eq!(schema.versions()[4].version(), 5); 187 assert_eq!( 188 schema.versions()[4].object_count(), 189 MYC_STATE_SCHEMA_VERSION_5_OBJECT_COUNT 190 ); 191 assert_eq!(schema.versions()[4].object_count(), 34); 192 assert_eq!( 193 schema.versions()[4].digest().as_bytes(), 194 &MYC_STATE_SCHEMA_VERSION_5_SHA256 195 ); 196 assert_eq!(schema.versions()[5].version(), 6); 197 assert_eq!( 198 schema.versions()[5].object_count(), 199 MYC_STATE_SCHEMA_VERSION_6_OBJECT_COUNT 200 ); 201 assert_eq!(schema.versions()[5].object_count(), 43); 202 assert_eq!( 203 schema.versions()[5].digest().as_bytes(), 204 &MYC_STATE_SCHEMA_VERSION_6_SHA256 205 ); 206 assert_eq!(schema.versions()[6].version(), 7); 207 assert_eq!( 208 schema.versions()[6].object_count(), 209 MYC_STATE_SCHEMA_VERSION_7_OBJECT_COUNT 210 ); 211 assert_eq!(schema.versions()[6].object_count(), 53); 212 assert_eq!( 213 schema.versions()[6].digest().as_bytes(), 214 &MYC_STATE_SCHEMA_VERSION_7_SHA256 215 ); 216 assert_eq!(schema.versions()[7].version(), 8); 217 assert_eq!( 218 schema.versions()[7].object_count(), 219 MYC_STATE_SCHEMA_VERSION_8_OBJECT_COUNT 220 ); 221 assert_eq!(schema.versions()[7].object_count(), 56); 222 assert_eq!( 223 schema.versions()[7].digest().as_bytes(), 224 &MYC_STATE_SCHEMA_VERSION_8_SHA256 225 ); 226 assert_eq!(schema.versions()[8].version(), 9); 227 assert_eq!( 228 schema.versions()[8].object_count(), 229 MYC_STATE_SCHEMA_VERSION_9_OBJECT_COUNT 230 ); 231 assert_eq!(schema.versions()[8].object_count(), 59); 232 assert_eq!( 233 schema.versions()[8].digest().as_bytes(), 234 &MYC_STATE_SCHEMA_VERSION_9_SHA256 235 ); 236 assert_eq!(schema.versions()[9].version(), 10); 237 assert_eq!( 238 schema.versions()[9].object_count(), 239 MYC_STATE_SCHEMA_VERSION_10_OBJECT_COUNT 240 ); 241 assert_eq!(schema.versions()[9].object_count(), 63); 242 assert_eq!( 243 schema.versions()[9].digest().as_bytes(), 244 &MYC_STATE_SCHEMA_VERSION_10_SHA256 245 ); 246 assert_eq!(schema.versions()[10].version(), 11); 247 assert_eq!( 248 schema.versions()[10].object_count(), 249 MYC_STATE_SCHEMA_VERSION_11_OBJECT_COUNT 250 ); 251 assert_eq!(schema.versions()[10].object_count(), 65); 252 assert_eq!( 253 schema.versions()[10].digest().as_bytes(), 254 &MYC_STATE_SCHEMA_VERSION_11_SHA256 255 ); 256 assert_eq!(schema.versions()[11].version(), 12); 257 assert_eq!( 258 schema.versions()[11].object_count(), 259 MYC_STATE_SCHEMA_VERSION_12_OBJECT_COUNT 260 ); 261 assert_eq!(schema.versions()[11].object_count(), 70); 262 assert_eq!( 263 schema.versions()[11].digest().as_bytes(), 264 &MYC_STATE_SCHEMA_VERSION_12_SHA256 265 ); 266 assert_eq!(schema.digest().as_bytes(), &MYC_STATE_SCHEMA_CATALOG_SHA256); 267 assert_eq!(schema.migration_catalog_digest(), migrations.digest()); 268 validate_myc_state_catalogs(&migrations, &schema).expect("exact catalogs"); 269 270 assert_eq!( 271 hex::encode(MYC_STATE_SCHEMA_VERSION_2_MIGRATION_SHA256), 272 "c5eb978bda1bc70c70bd9bd44d8cba70cb2857d26a9dce74961f4b781e710037" 273 ); 274 assert_eq!( 275 hex::encode(MYC_MIGRATION_CATALOG_SHA256), 276 "b1d6458245e6dfc4661aa6146b8ce8ab55f7c29bf36099d261c07f5f5167561d" 277 ); 278 assert_eq!( 279 hex::encode(MYC_STATE_SCHEMA_VERSION_1_SHA256), 280 "94dc66fbca601679615c055229dc0db6119f5bd92b04390c67f698a036fa78ae" 281 ); 282 assert_eq!( 283 hex::encode(MYC_STATE_SCHEMA_VERSION_2_SHA256), 284 "94739b5a34ca8ed130b946d092731b59bf1548fe541d9a9269a33d0aed1ea09e" 285 ); 286 assert_eq!( 287 hex::encode(MYC_STATE_SCHEMA_CATALOG_SHA256), 288 "b52721d85c1eb8cddc28256c21179a10d5f32bcb33e9d2a6bb8fe49ecbc49a68" 289 ); 290 assert_eq!( 291 hex::encode(MYC_STATE_SCHEMA_VERSION_3_MIGRATION_SHA256), 292 "753165136b3dace0091d782f33f6b10ca1a2823314158d80a4775e0af628edf9" 293 ); 294 assert_eq!( 295 hex::encode(MYC_STATE_SCHEMA_VERSION_3_SHA256), 296 "572fe6a4d36c0476ec40536f48028e1558488fb8abeba0a34ba69b4b7080ba08" 297 ); 298 assert_eq!( 299 hex::encode(MYC_STATE_SCHEMA_VERSION_4_MIGRATION_SHA256), 300 "939c0ed07cd15cc0c794bf6c2af7192261409305c2876f3be363e8e4fe4a1abb" 301 ); 302 assert_eq!( 303 hex::encode(MYC_STATE_SCHEMA_VERSION_4_SHA256), 304 "479863d37d91e6c269fa3573db6c2e767cdd3b24a93ab482d774bcec0218c174" 305 ); 306 assert_eq!( 307 hex::encode(MYC_STATE_SCHEMA_VERSION_5_MIGRATION_SHA256), 308 "0e004fcb5d0bc7c951b16f4334533d10efcb18a826f624de0922d86dc8504b33" 309 ); 310 assert_eq!( 311 hex::encode(MYC_STATE_SCHEMA_VERSION_5_SHA256), 312 "fe89d4af7de4eded78a3f062dc9d300f06cf0f4cfca5fa5cffc93af1146f21c5" 313 ); 314 assert_eq!( 315 hex::encode(MYC_STATE_SCHEMA_VERSION_6_MIGRATION_SHA256), 316 "4649b9afd03fc07f89fe184f027925675a0265951ea7cf75e06a4312c55a82bd" 317 ); 318 assert_eq!( 319 hex::encode(MYC_STATE_SCHEMA_VERSION_6_SHA256), 320 "557273306e68e9306dc1d7c7009e1cb7c9d15b8d52e07db7d0bd51e83f054492" 321 ); 322 assert_eq!( 323 hex::encode(MYC_STATE_SCHEMA_VERSION_7_MIGRATION_SHA256), 324 "6097c40776a57dd4bddc04e652987217d6f5991f2d5e94d322d1208619254e6c" 325 ); 326 assert_eq!( 327 hex::encode(MYC_STATE_SCHEMA_VERSION_7_SHA256), 328 "3b3528911a293499d9721da71b6ad07a5e723e97b6ebf5b40a19b9058958bf79" 329 ); 330 assert_eq!( 331 hex::encode(MYC_STATE_SCHEMA_VERSION_8_MIGRATION_SHA256), 332 "b81f980c91acd98b91ecd5cb248e4027c9f47c1dc8d0134a61af5c38238315bc" 333 ); 334 assert_eq!( 335 hex::encode(MYC_STATE_SCHEMA_VERSION_8_SHA256), 336 "50158cb093ed70b3d5783762b18d21b7809665c89fde925d872365909d28f06e" 337 ); 338 assert_eq!( 339 hex::encode(MYC_STATE_SCHEMA_VERSION_9_MIGRATION_SHA256), 340 "fdb039d472cd62e7da46c40a9786c3a9a7ec55f2ae7db689d0f855fbbf8a9566" 341 ); 342 assert_eq!( 343 hex::encode(MYC_STATE_SCHEMA_VERSION_9_SHA256), 344 "eee76f4ff0bd2dc2c061ae384e00de16c5f5efc4b6edd0ac7f73cad83a991ff7" 345 ); 346 assert_eq!( 347 hex::encode(MYC_STATE_SCHEMA_VERSION_10_MIGRATION_SHA256), 348 "28423ebb59f4b26223307b74a7e12905ca4818c01a655203ee8d63c911f44489" 349 ); 350 assert_eq!( 351 hex::encode(MYC_STATE_SCHEMA_VERSION_10_SHA256), 352 "b77ed23afa39ff45dda250faa1ebfc0587c34462658fc49fb7b2fbc8909ba303" 353 ); 354 assert_eq!( 355 hex::encode(MYC_STATE_SCHEMA_VERSION_11_MIGRATION_SHA256), 356 "16385368aa4ee40ea7002a0ab42645bc68b546a4ba6afdfede565fe026576c96" 357 ); 358 assert_eq!( 359 hex::encode(MYC_STATE_SCHEMA_VERSION_11_SHA256), 360 "0de7fe176ea7da6030424addc29b9a91363e88b04dc78ddab480fd0f0af94e5a" 361 ); 362 assert_eq!( 363 hex::encode(MYC_STATE_SCHEMA_VERSION_12_MIGRATION_SHA256), 364 "388ee51de599f37b7bb1956cebd518461f3eb193535ffe6bb9eac2d82bbe3e37" 365 ); 366 assert_eq!( 367 hex::encode(MYC_STATE_SCHEMA_VERSION_12_SHA256), 368 "d893a23ba68e463489ad7ef1e6a80ea4e88089381c73decc3243a4ce6f64da8f" 369 ); 370 } 371 372 #[test] 373 fn independent_validator_rejects_migration_or_schema_drift() { 374 const SQL: &str = "CREATE TABLE unexpected (value INTEGER NOT NULL) STRICT"; 375 let migration = 376 MigrationDescriptor::sql(2, "unexpected_schema", SQL, MigrationChecksum::for_sql(SQL)) 377 .expect("valid drift fixture"); 378 let migrations = MigrationCatalog::new([migration]).expect("drift migration catalog"); 379 let expected_schema = myc_schema_catalog().expect("expected schema"); 380 assert_eq!( 381 validate_myc_state_catalogs(&migrations, &expected_schema) 382 .expect_err("migration drift") 383 .kind(), 384 MycStateCatalogErrorKind::CatalogMismatch 385 ); 386 387 let expected_migrations = myc_migration_catalog().expect("expected migrations"); 388 let v1 = SchemaVersionCatalog::new( 389 1, 390 [], 391 SchemaDigest::from_bytes(MYC_STATE_SCHEMA_VERSION_1_SHA256), 392 ) 393 .expect("schema v1"); 394 let object_digest = 395 SchemaObject::computed_digest(SchemaObjectKind::Table, "unexpected", "unexpected", SQL) 396 .expect("object digest"); 397 let object = SchemaObject::new( 398 SchemaObjectKind::Table, 399 "unexpected", 400 "unexpected", 401 SQL, 402 object_digest, 403 ) 404 .expect("schema object"); 405 let snapshot_digest = 406 SchemaVersionCatalog::computed_digest(2, [object.clone()]).expect("snapshot digest"); 407 let v2 = SchemaVersionCatalog::new(2, [object.clone()], snapshot_digest).expect("schema v2"); 408 let v3_digest = 409 SchemaVersionCatalog::computed_digest(3, [object.clone()]).expect("schema-v3 digest"); 410 let v3 = SchemaVersionCatalog::new(3, [object.clone()], v3_digest).expect("schema v3"); 411 let v4_digest = 412 SchemaVersionCatalog::computed_digest(4, [object.clone()]).expect("schema-v4 digest"); 413 let v4 = SchemaVersionCatalog::new(4, [object.clone()], v4_digest).expect("schema v4"); 414 let v5_digest = 415 SchemaVersionCatalog::computed_digest(5, [object.clone()]).expect("schema-v5 digest"); 416 let v5 = SchemaVersionCatalog::new(5, [object.clone()], v5_digest).expect("schema v5"); 417 let v6_digest = 418 SchemaVersionCatalog::computed_digest(6, [object.clone()]).expect("schema-v6 digest"); 419 let v6 = SchemaVersionCatalog::new(6, [object.clone()], v6_digest).expect("schema v6"); 420 let v7_digest = 421 SchemaVersionCatalog::computed_digest(7, [object.clone()]).expect("schema-v7 digest"); 422 let v7 = SchemaVersionCatalog::new(7, [object.clone()], v7_digest).expect("schema v7"); 423 let v8_digest = 424 SchemaVersionCatalog::computed_digest(8, [object.clone()]).expect("schema-v8 digest"); 425 let v8 = SchemaVersionCatalog::new(8, [object.clone()], v8_digest).expect("schema v8"); 426 let v9_digest = 427 SchemaVersionCatalog::computed_digest(9, [object.clone()]).expect("schema-v9 digest"); 428 let v9 = SchemaVersionCatalog::new(9, [object.clone()], v9_digest).expect("schema v9"); 429 let v10_digest = 430 SchemaVersionCatalog::computed_digest(10, [object.clone()]).expect("schema-v10 digest"); 431 let v10 = SchemaVersionCatalog::new(10, [object.clone()], v10_digest).expect("schema v10"); 432 let v11_digest = 433 SchemaVersionCatalog::computed_digest(11, [object.clone()]).expect("schema-v11 digest"); 434 let v11 = SchemaVersionCatalog::new(11, [object.clone()], v11_digest).expect("schema v11"); 435 let v12_digest = 436 SchemaVersionCatalog::computed_digest(12, [object.clone()]).expect("schema-v12 digest"); 437 let v12 = SchemaVersionCatalog::new(12, [object], v12_digest).expect("schema v12"); 438 let schema = SchemaCatalog::new( 439 &expected_migrations, 440 [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12], 441 ) 442 .expect("drift schema catalog"); 443 assert_eq!( 444 validate_myc_state_catalogs(&expected_migrations, &schema) 445 .expect_err("schema drift") 446 .kind(), 447 MycStateCatalogErrorKind::CatalogMismatch 448 ); 449 } 450 451 #[test] 452 fn catalog_errors_are_stable_source_free_and_redacted() { 453 const SQL: &str = "CREATE TABLE secret_table (value INTEGER NOT NULL) STRICT"; 454 let migration = 455 MigrationDescriptor::sql(2, "secret_schema", SQL, MigrationChecksum::for_sql(SQL)) 456 .expect("migration"); 457 let migrations = MigrationCatalog::new([migration]).expect("migration catalog"); 458 let expected_schema = myc_schema_catalog().expect("expected schema"); 459 let error = validate_myc_state_catalogs(&migrations, &expected_schema).expect_err("mismatch"); 460 461 assert_eq!(error.kind(), MycStateCatalogErrorKind::CatalogMismatch); 462 assert_eq!(error.code(), "state_catalog_mismatch"); 463 assert!(Error::source(&error).is_none()); 464 let rendered = format!("{error} {error:?}"); 465 assert!(!rendered.contains("secret")); 466 assert!(!rendered.contains(&hex::encode(MigrationChecksum::for_sql(SQL).as_bytes()))); 467 } 468 469 #[test] 470 fn catalog_source_is_pure_pinned_and_uses_only_the_shared_authority() { 471 assert!(MANIFEST.contains( 472 "radroots_service_sqlite = { git = \"https://github.com/radrootslabs/lib\", rev = \"055096853fca95e15d0f813d33a14aca13be3881\", version = \"=0.1.0-alpha\" }" 473 )); 474 assert!(LIB_SOURCE.contains("mod state_catalog;")); 475 assert!(!LIB_SOURCE.contains("pub mod state_catalog;")); 476 assert!(CATALOG_SOURCE.contains("MigrationDescriptor::sql(")); 477 assert!(CATALOG_SOURCE.contains("MigrationChecksum::from_bytes(")); 478 assert!(CATALOG_SOURCE.contains("SchemaDigest::from_bytes(")); 479 assert!(CATALOG_SOURCE.contains("FROM publication_outbox;")); 480 assert!( 481 CATALOG_SOURCE.contains("INSERT INTO delivery_targets SELECT * FROM publication_targets;") 482 ); 483 assert!( 484 CATALOG_SOURCE 485 .contains("INSERT INTO delivery_attempts SELECT * FROM publication_attempts;") 486 ); 487 assert!(CATALOG_SOURCE.contains("delivery_jobs_guard_insert")); 488 assert!(!CATALOG_SOURCE.contains("computed_digest")); 489 assert!(!CATALOG_SOURCE.contains("ALTER TABLE")); 490 for forbidden in [ 491 "sqlx::", 492 "rusqlite", 493 "libsqlite3_sys", 494 "raw_sql", 495 "std::fs", 496 "std::path", 497 "SqliteConnection", 498 "Transaction", 499 ] { 500 assert!( 501 !CATALOG_SOURCE.contains(forbidden), 502 "found forbidden catalog authority `{forbidden}`" 503 ); 504 } 505 }