rhi

Coordinated trade for connected markets
git clone https://radroots.dev/git/rhi.git
Log | Files | Refs | README | LICENSE

services_hardening_state_repository_topology.rs (9464B)


      1 #![forbid(unsafe_code)]
      2 
      3 use rhi::{
      4     RHI_STATE_REPOSITORY_CONTRACT_VERSION, RHI_STATE_REPOSITORY_COUNT, RhiStateRepositoryKind,
      5     RhiStateRepositoryWriteClass, rhi_state_repository_descriptors,
      6 };
      7 use serde_json::json;
      8 
      9 const CONTRACT: &str =
     10     include_str!("../contracts/services_hardening/state_repository_topology.v1.json");
     11 const LIB_SOURCE: &str = include_str!("../src/lib.rs");
     12 const HOST_SOURCE: &str = include_str!("../src/state_host.rs");
     13 const REPOSITORY_SOURCE: &str = include_str!("../src/state_repository.rs");
     14 
     15 #[test]
     16 fn machine_contract_and_typed_descriptor_inventory_are_exact() {
     17     let contract: serde_json::Value = serde_json::from_str(CONTRACT).expect("repository contract");
     18     assert_eq!(contract["schema"], "radroots.rhi.state-repository-topology");
     19     assert_eq!(contract["schema_version"], 1);
     20     assert_eq!(
     21         contract["contract_version"],
     22         RHI_STATE_REPOSITORY_CONTRACT_VERSION
     23     );
     24     assert_eq!(contract["repository_count"], RHI_STATE_REPOSITORY_COUNT);
     25     assert_eq!(contract["construction"], "sealed_to_open_rhi_state_host");
     26     assert_eq!(contract["raw_sqlite_authority_exposed"], false);
     27     assert_eq!(
     28         contract["deferred_behavior"],
     29         json!([
     30             "later_schema_migrations",
     31             "remaining_repository_crud",
     32             "backup_restore_and_recovery",
     33             "network_io",
     34             "task_supervision"
     35         ])
     36     );
     37     assert_eq!(
     38         contract
     39             .as_object()
     40             .expect("contract object")
     41             .keys()
     42             .map(String::as_str)
     43             .collect::<std::collections::BTreeSet<_>>(),
     44         [
     45             "schema",
     46             "schema_version",
     47             "contract_version",
     48             "repository_count",
     49             "construction",
     50             "raw_sqlite_authority_exposed",
     51             "repositories",
     52             "deferred_behavior",
     53         ]
     54         .into_iter()
     55         .collect()
     56     );
     57 
     58     let descriptors = rhi_state_repository_descriptors();
     59     let actual = descriptors
     60         .iter()
     61         .map(|descriptor| {
     62             json!({
     63                 "kind": descriptor.code(),
     64                 "backing_table": descriptor.backing_table(),
     65                 "write_class": descriptor.write_class().code(),
     66             })
     67         })
     68         .collect::<Vec<_>>();
     69     assert_eq!(
     70         contract["repositories"]
     71             .as_array()
     72             .expect("repository array"),
     73         &actual
     74     );
     75     assert_eq!(descriptors.len(), RHI_STATE_REPOSITORY_COUNT);
     76     for repository in contract["repositories"]
     77         .as_array()
     78         .expect("repository array")
     79     {
     80         assert_eq!(
     81             repository
     82                 .as_object()
     83                 .expect("repository object")
     84                 .keys()
     85                 .map(String::as_str)
     86                 .collect::<std::collections::BTreeSet<_>>(),
     87             ["kind", "backing_table", "write_class"]
     88                 .into_iter()
     89                 .collect()
     90         );
     91     }
     92 }
     93 
     94 #[test]
     95 fn repository_kinds_are_closed_ordered_and_cross_bound() {
     96     use RhiStateRepositoryKind as Kind;
     97     use RhiStateRepositoryWriteClass as Write;
     98 
     99     let expected = [
    100         (
    101             Kind::Source,
    102             "source",
    103             "evidence_reconciliation_sources",
    104             Write::AppendOnly,
    105         ),
    106         (
    107             Kind::SourceCursor,
    108             "source_cursor",
    109             "relay_checkpoints",
    110             Write::CompareAndSwap,
    111         ),
    112         (
    113             Kind::SourceCompletion,
    114             "source_completion",
    115             "evidence_reconciliation_sources",
    116             Write::AppendOnly,
    117         ),
    118         (
    119             Kind::SignedEvent,
    120             "signed_event",
    121             "nostr_events",
    122             Write::AppendOnly,
    123         ),
    124         (
    125             Kind::Mutation,
    126             "mutation",
    127             "trade_mutations",
    128             Write::AppendOnly,
    129         ),
    130         (
    131             Kind::Provenance,
    132             "provenance",
    133             "relay_observations",
    134             Write::AppendOnly,
    135         ),
    136         (
    137             Kind::DirtyTrade,
    138             "dirty_trade",
    139             "trade_dirty_generations",
    140             Write::CompareAndSwap,
    141         ),
    142         (
    143             Kind::ReconciliationJob,
    144             "reconciliation_job",
    145             "reconciliation_jobs",
    146             Write::CompareAndSwap,
    147         ),
    148         (
    149             Kind::ReconciliationAttempt,
    150             "reconciliation_attempt",
    151             "evidence_reconciliations",
    152             Write::AppendOnly,
    153         ),
    154         (
    155             Kind::EvidenceManifest,
    156             "evidence_manifest",
    157             "evidence_manifests",
    158             Write::Immutable,
    159         ),
    160         (
    161             Kind::Projection,
    162             "projection",
    163             "trade_projections",
    164             Write::Immutable,
    165         ),
    166         (
    167             Kind::Report,
    168             "report",
    169             "attestation_reports",
    170             Write::Immutable,
    171         ),
    172         (
    173             Kind::Supersession,
    174             "supersession",
    175             "attestation_reports",
    176             Write::AppendOnly,
    177         ),
    178         (
    179             Kind::SignedAttestationEvent,
    180             "signed_attestation_event",
    181             "signed_attestation_events",
    182             Write::Immutable,
    183         ),
    184         (
    185             Kind::PublicationOutbox,
    186             "publication_outbox",
    187             "publication_outbox",
    188             Write::CompareAndSwap,
    189         ),
    190         (
    191             Kind::PublicationTarget,
    192             "publication_target",
    193             "publication_targets",
    194             Write::CompareAndSwap,
    195         ),
    196         (
    197             Kind::PublicationAttempt,
    198             "publication_attempt",
    199             "publication_attempts",
    200             Write::AppendOnly,
    201         ),
    202         (
    203             Kind::DesiredPresence,
    204             "desired_presence",
    205             "presence_desired_state",
    206             Write::CompareAndSwap,
    207         ),
    208         (
    209             Kind::PresenceOutbox,
    210             "presence_outbox",
    211             "presence_outbox",
    212             Write::CompareAndSwap,
    213         ),
    214         (
    215             Kind::PresenceTarget,
    216             "presence_target",
    217             "presence_targets",
    218             Write::CompareAndSwap,
    219         ),
    220         (
    221             Kind::PresenceAttempt,
    222             "presence_attempt",
    223             "presence_attempts",
    224             Write::AppendOnly,
    225         ),
    226     ];
    227     for (descriptor, (kind, code, table, write_class)) in
    228         rhi_state_repository_descriptors().iter().zip(expected)
    229     {
    230         assert_eq!(descriptor.kind(), kind);
    231         assert_eq!(descriptor.code(), code);
    232         assert_eq!(descriptor.backing_table(), table);
    233         assert_eq!(descriptor.write_class(), write_class);
    234     }
    235 }
    236 
    237 #[test]
    238 fn capabilities_are_private_sealed_and_defer_unowned_behavior() {
    239     assert!(LIB_SOURCE.contains("mod state_repository;"));
    240     assert!(!LIB_SOURCE.contains("pub mod state_repository;"));
    241     assert!(HOST_SOURCE.contains("pub const fn repositories(&self) -> RhiStateRepositories<'_>"));
    242     for required in [
    243         "pub const fn sources(&self) -> RhiSourceRepository<'host>",
    244         "pub const fn source_cursors(&self) -> RhiSourceCursorRepository<'host>",
    245         "pub const fn source_completions(&self) -> RhiSourceCompletionRepository<'host>",
    246         "pub const fn signed_events(&self) -> RhiSignedEventRepository<'host>",
    247         "pub const fn mutations(&self) -> RhiMutationRepository<'host>",
    248         "pub const fn provenance(&self) -> RhiProvenanceRepository<'host>",
    249         "pub const fn dirty_trades(&self) -> RhiDirtyTradeRepository<'host>",
    250         "pub const fn reconciliation_jobs(&self) -> RhiReconciliationJobRepository<'host>",
    251         "pub const fn reconciliation_attempts(&self) -> RhiReconciliationAttemptRepository<'host>",
    252         "pub const fn evidence_manifests(&self) -> RhiEvidenceManifestRepository<'host>",
    253         "pub const fn projections(&self) -> RhiProjectionRepository<'host>",
    254         "pub const fn reports(&self) -> RhiReportRepository<'host>",
    255         "pub const fn supersessions(&self) -> RhiSupersessionRepository<'host>",
    256         "pub const fn signed_attestation_events(&self) -> RhiSignedAttestationEventRepository<'host>",
    257         "pub const fn publication_outbox(&self) -> RhiPublicationOutboxRepository<'host>",
    258         "pub const fn publication_targets(&self) -> RhiPublicationTargetRepository<'host>",
    259         "pub const fn publication_attempts(&self) -> RhiPublicationAttemptRepository<'host>",
    260         "pub const fn desired_presence(&self) -> RhiDesiredPresenceRepository<'host>",
    261         "pub const fn presence_outbox(&self) -> RhiPresenceOutboxRepository<'host>",
    262         "pub const fn presence_targets(&self) -> RhiPresenceTargetRepository<'host>",
    263         "pub const fn presence_attempts(&self) -> RhiPresenceAttemptRepository<'host>",
    264     ] {
    265         assert!(REPOSITORY_SOURCE.contains(required), "missing {required}");
    266     }
    267     for forbidden in [
    268         "SqlitePool",
    269         "SqliteConnection",
    270         "ServiceSqliteTransaction",
    271         "sqlx::",
    272         "rusqlite::",
    273         "CREATE TABLE",
    274         "INSERT INTO",
    275         "UPDATE ",
    276         "DELETE FROM",
    277         "std::fs",
    278         "std::path",
    279         "std::env",
    280         "tokio::",
    281         "nostr::",
    282         "Deref",
    283         "AsRef",
    284     ] {
    285         assert!(
    286             !REPOSITORY_SOURCE.contains(forbidden),
    287             "premature or escaping repository authority {forbidden}"
    288         );
    289     }
    290 }