lib

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

full_mode.rs (50511B)


      1 use radroots_replica_db::{ReplicaSql, export_manifest};
      2 use radroots_replica_db_schema::farm::{
      3     IFarmCreate, IFarmDelete, IFarmFindMany, IFarmFindOne, IFarmUpdate,
      4 };
      5 use radroots_replica_db_schema::farm_gcs_location::{
      6     IFarmGcsLocationCreate, IFarmGcsLocationDelete, IFarmGcsLocationFindMany,
      7     IFarmGcsLocationFindOne, IFarmGcsLocationUpdate,
      8 };
      9 use radroots_replica_db_schema::farm_member::{
     10     IFarmMemberCreate, IFarmMemberDelete, IFarmMemberFindMany, IFarmMemberFindOne,
     11     IFarmMemberUpdate,
     12 };
     13 use radroots_replica_db_schema::farm_member_claim::{
     14     IFarmMemberClaimCreate, IFarmMemberClaimDelete, IFarmMemberClaimFindMany,
     15     IFarmMemberClaimFindOne, IFarmMemberClaimUpdate,
     16 };
     17 use radroots_replica_db_schema::farm_tag::{
     18     IFarmTagCreate, IFarmTagDelete, IFarmTagFindMany, IFarmTagFindOne, IFarmTagUpdate,
     19 };
     20 use radroots_replica_db_schema::gcs_location::{
     21     GcsLocationFarmArgs, GcsLocationFindManyRel, GcsLocationPlotArgs, GcsLocationTradeProductArgs,
     22     IGcsLocationCreate, IGcsLocationDelete, IGcsLocationFindMany, IGcsLocationFindOne,
     23     IGcsLocationUpdate,
     24 };
     25 use radroots_replica_db_schema::log_error::{
     26     ILogErrorCreate, ILogErrorDelete, ILogErrorFindMany, ILogErrorFindOne, ILogErrorUpdate,
     27 };
     28 use radroots_replica_db_schema::media_image::{
     29     IMediaImageCreate, IMediaImageDelete, IMediaImageFindMany, IMediaImageFindOne,
     30     IMediaImageUpdate, MediaImageFindManyRel, MediaImageTradeProductArgs,
     31 };
     32 use radroots_replica_db_schema::nostr_event_head::{
     33     INostrEventHeadCreate, INostrEventHeadDelete, INostrEventHeadFindMany, INostrEventHeadFindOne,
     34     INostrEventHeadUpdate,
     35 };
     36 use radroots_replica_db_schema::nostr_profile::{
     37     INostrProfileCreate, INostrProfileDelete, INostrProfileFindMany, INostrProfileFindOne,
     38     INostrProfileUpdate, NostrProfileFindManyRel, NostrProfileRelayArgs,
     39 };
     40 use radroots_replica_db_schema::nostr_profile_relay::INostrProfileRelayRelation;
     41 use radroots_replica_db_schema::nostr_relay::{
     42     INostrRelayCreate, INostrRelayDelete, INostrRelayFindMany, INostrRelayFindOne,
     43     INostrRelayUpdate, NostrRelayFindManyRel, NostrRelayProfileArgs,
     44 };
     45 use radroots_replica_db_schema::plot::{
     46     IPlotCreate, IPlotDelete, IPlotFindMany, IPlotFindOne, IPlotUpdate,
     47 };
     48 use radroots_replica_db_schema::plot_gcs_location::{
     49     IPlotGcsLocationCreate, IPlotGcsLocationDelete, IPlotGcsLocationFindMany,
     50     IPlotGcsLocationFindOne, IPlotGcsLocationUpdate,
     51 };
     52 use radroots_replica_db_schema::plot_tag::{
     53     IPlotTagCreate, IPlotTagDelete, IPlotTagFindMany, IPlotTagFindOne, IPlotTagUpdate,
     54 };
     55 use radroots_replica_db_schema::trade_product::{
     56     ITradeProductCreate, ITradeProductDelete, ITradeProductFindMany, ITradeProductFindOne,
     57     ITradeProductUpdate,
     58 };
     59 use radroots_replica_db_schema::trade_product_location::ITradeProductLocationRelation;
     60 use radroots_replica_db_schema::trade_product_media::ITradeProductMediaRelation;
     61 use radroots_sql_core::{SqlError, SqliteExecutor};
     62 use radroots_types::types::IError;
     63 use serde::de::DeserializeOwned;
     64 use serde_json::json;
     65 
     66 fn parse_json<T: DeserializeOwned>(value: serde_json::Value) -> T {
     67     serde_json::from_value(value).expect("valid test payload")
     68 }
     69 
     70 fn hex64(ch: char) -> String {
     71     std::iter::repeat_n(ch, 64).collect()
     72 }
     73 
     74 fn assert_invalid_argument<T>(result: Result<T, IError<SqlError>>) {
     75     let err = match result {
     76         Ok(_) => panic!("invalid argument expected"),
     77         Err(err) => err,
     78     };
     79     assert!(matches!(err.err, SqlError::InvalidArgument(_)));
     80 }
     81 
     82 fn assert_not_found<T>(result: Result<T, IError<SqlError>>) {
     83     let err = match result {
     84         Ok(_) => panic!("not found expected"),
     85         Err(err) => err,
     86     };
     87     assert!(matches!(err.err, SqlError::NotFound(_)));
     88 }
     89 
     90 fn open_db() -> ReplicaSql<SqliteExecutor> {
     91     let exec = SqliteExecutor::open_memory().expect("open sqlite memory");
     92     let db = ReplicaSql::new(exec);
     93     db.migrate_up().expect("migrate up");
     94     db
     95 }
     96 
     97 #[test]
     98 fn full_mode_shaped_query_helpers_cover_cli_reads() {
     99     let db = open_db();
    100 
    101     let farm: IFarmCreate = parse_json(json!({
    102         "d_tag": "farm-a",
    103         "pubkey": hex64('a'),
    104         "name": "farm a"
    105     }));
    106     db.farm_create(&farm).expect("farm create");
    107 
    108     let gcs_location: IGcsLocationCreate = parse_json(json!({
    109         "d_tag": "gcs-a",
    110         "lat": 59.33,
    111         "lng": 18.06,
    112         "geohash": "u6sce4f",
    113         "point": "POINT(18.06 59.33)",
    114         "polygon": "POLYGON((18.06 59.33,18.07 59.33,18.07 59.34,18.06 59.34,18.06 59.33))",
    115         "label": "stockholm"
    116     }));
    117     let gcs_created = db
    118         .gcs_location_create(&gcs_location)
    119         .expect("gcs create")
    120         .result;
    121     let listing_addr = format!("30402:{}:listing-a", hex64('d'));
    122 
    123     let trade_product: ITradeProductCreate = parse_json(json!({
    124         "key": "product-a",
    125         "category": "coffee",
    126         "title": "coffee a",
    127         "summary": "washed lot",
    128         "process": "washed",
    129         "lot": "lot-a",
    130         "profile": "floral",
    131         "year": 2024,
    132         "qty_amt": 100,
    133         "qty_amt_exact": "100",
    134         "qty_unit": "kg",
    135         "qty_label": "bags",
    136         "qty_avail": 2,
    137         "price_amt": 7.5,
    138         "price_amt_exact": "7.5",
    139         "price_currency": "USD",
    140         "price_qty_amt": 1,
    141         "price_qty_amt_exact": "1",
    142         "price_qty_unit": "kg",
    143         "listing_addr": listing_addr.clone(),
    144         "primary_bin_id": "bin-a",
    145         "verified_primary_bin_id": "bin-a",
    146         "notes": "fresh coffee"
    147     }));
    148     let trade_product_created = db
    149         .trade_product_create(&trade_product)
    150         .expect("trade product create")
    151         .result;
    152 
    153     let product_location_rel: ITradeProductLocationRelation = parse_json(json!({
    154         "trade_product": { "id": trade_product_created.id },
    155         "gcs_location": { "id": gcs_created.id }
    156     }));
    157     db.trade_product_location_set(&product_location_rel)
    158         .expect("product location set");
    159 
    160     let nostr_event_head: INostrEventHeadCreate = parse_json(json!({
    161         "key": "state-a",
    162         "kind": 30023,
    163         "pubkey": hex64('d'),
    164         "d_tag": "listing-a",
    165         "last_event_id": hex64('e'),
    166         "last_created_at": 42,
    167         "content_hash": "hash-a"
    168     }));
    169     db.nostr_event_head_create(&nostr_event_head)
    170         .expect("nostr event state create");
    171 
    172     let rows = db
    173         .trade_product_search(&["coffee".to_owned()])
    174         .expect("trade product search");
    175     assert_eq!(rows.len(), 1);
    176     assert_eq!(rows[0].key, "product-a");
    177     assert_eq!(rows[0].listing_addr.as_deref(), Some(listing_addr.as_str()));
    178     assert_eq!(rows[0].primary_bin_id.as_deref(), Some("bin-a"));
    179     assert_eq!(rows[0].verified_primary_bin_id.as_deref(), Some("bin-a"));
    180     assert_eq!(rows[0].location_primary.as_deref(), Some("stockholm"));
    181 
    182     let lookup_rows = db
    183         .trade_product_lookup("product-a")
    184         .expect("trade product lookup");
    185     assert_eq!(lookup_rows.len(), 1);
    186     assert_eq!(lookup_rows[0].id, trade_product_created.id);
    187     assert_eq!(
    188         lookup_rows[0].listing_addr.as_deref(),
    189         Some(listing_addr.as_str())
    190     );
    191     assert_eq!(lookup_rows[0].primary_bin_id.as_deref(), Some("bin-a"));
    192     assert_eq!(
    193         lookup_rows[0].verified_primary_bin_id.as_deref(),
    194         Some("bin-a")
    195     );
    196 
    197     assert_eq!(
    198         db.trade_product_search(&[]).expect("empty search"),
    199         Vec::new()
    200     );
    201     assert_eq!(
    202         db.farm_unique_d_tag_by_pubkey(hex64('a').as_str())
    203             .expect("farm unique d tag"),
    204         Some("farm-a".to_owned())
    205     );
    206     assert_eq!(
    207         db.nostr_event_last_created_at()
    208             .expect("nostr event freshness"),
    209         Some(42)
    210     );
    211 }
    212 
    213 #[test]
    214 fn full_mode_crud_and_relation_paths() {
    215     let db = open_db();
    216 
    217     db.migrate_down().expect("migrate down");
    218     db.migrate_up().expect("migrate up again");
    219 
    220     let farm: IFarmCreate = parse_json(json!({
    221         "d_tag": "farm-a",
    222         "pubkey": hex64('a'),
    223         "name": "farm a"
    224     }));
    225     let farm_created = db.farm_create(&farm).expect("farm create").result;
    226 
    227     let gcs_location: IGcsLocationCreate = parse_json(json!({
    228         "d_tag": "gcs-a",
    229         "lat": 59.33,
    230         "lng": 18.06,
    231         "geohash": "u6sce4f",
    232         "point": "POINT(18.06 59.33)",
    233         "polygon": "POLYGON((18.06 59.33,18.07 59.33,18.07 59.34,18.06 59.34,18.06 59.33))"
    234     }));
    235     let gcs_created = db
    236         .gcs_location_create(&gcs_location)
    237         .expect("gcs create")
    238         .result;
    239 
    240     let plot: IPlotCreate = parse_json(json!({
    241         "d_tag": "plot-a",
    242         "farm_id": farm_created.id,
    243         "name": "plot a"
    244     }));
    245     let plot_created = db.plot_create(&plot).expect("plot create").result;
    246 
    247     let farm_gcs: IFarmGcsLocationCreate = parse_json(json!({
    248         "farm_id": farm_created.id,
    249         "gcs_location_id": gcs_created.id,
    250         "role": "primary"
    251     }));
    252     let farm_gcs_created = db
    253         .farm_gcs_location_create(&farm_gcs)
    254         .expect("farm gcs create")
    255         .result;
    256 
    257     let plot_gcs: IPlotGcsLocationCreate = parse_json(json!({
    258         "plot_id": plot_created.id,
    259         "gcs_location_id": gcs_created.id,
    260         "role": "primary"
    261     }));
    262     let plot_gcs_created = db
    263         .plot_gcs_location_create(&plot_gcs)
    264         .expect("plot gcs create")
    265         .result;
    266 
    267     let farm_tag: IFarmTagCreate = parse_json(json!({
    268         "farm_id": farm_created.id,
    269         "tag": "organic"
    270     }));
    271     let farm_tag_created = db
    272         .farm_tag_create(&farm_tag)
    273         .expect("farm tag create")
    274         .result;
    275 
    276     let plot_tag: IPlotTagCreate = parse_json(json!({
    277         "plot_id": plot_created.id,
    278         "tag": "north"
    279     }));
    280     let plot_tag_created = db
    281         .plot_tag_create(&plot_tag)
    282         .expect("plot tag create")
    283         .result;
    284 
    285     let farm_member: IFarmMemberCreate = parse_json(json!({
    286         "farm_id": farm_created.id,
    287         "member_pubkey": hex64('b'),
    288         "role": "owner"
    289     }));
    290     let farm_member_created = db
    291         .farm_member_create(&farm_member)
    292         .expect("farm member create")
    293         .result;
    294 
    295     let farm_member_claim: IFarmMemberClaimCreate = parse_json(json!({
    296         "member_pubkey": hex64('b'),
    297         "farm_pubkey": hex64('a')
    298     }));
    299     let farm_member_claim_created = db
    300         .farm_member_claim_create(&farm_member_claim)
    301         .expect("farm member claim create")
    302         .result;
    303 
    304     let log_error: ILogErrorCreate = parse_json(json!({
    305         "error": "panic",
    306         "message": "boom",
    307         "app_system": "studio",
    308         "app_version": "1.0.0",
    309         "nostr_pubkey": hex64('c')
    310     }));
    311     let log_error_created = db
    312         .log_error_create(&log_error)
    313         .expect("log error create")
    314         .result;
    315 
    316     let media_image: IMediaImageCreate = parse_json(json!({
    317         "file_path": "/img/a.jpg",
    318         "mime_type": "image/jpeg",
    319         "res_base": "https://cdn.example.com",
    320         "res_path": "img/a.jpg"
    321     }));
    322     let media_image_created = db
    323         .media_image_create(&media_image)
    324         .expect("media image create")
    325         .result;
    326 
    327     let nostr_profile: INostrProfileCreate = parse_json(json!({
    328         "public_key": hex64('d'),
    329         "profile_type": "farm",
    330         "name": "profile a"
    331     }));
    332     let nostr_profile_created = db
    333         .nostr_profile_create(&nostr_profile)
    334         .expect("nostr profile create")
    335         .result;
    336 
    337     let nostr_relay: INostrRelayCreate = parse_json(json!({
    338         "url": "wss://relay.example.com"
    339     }));
    340     let nostr_relay_created = db
    341         .nostr_relay_create(&nostr_relay)
    342         .expect("nostr relay create")
    343         .result;
    344 
    345     let nostr_event_head: INostrEventHeadCreate = parse_json(json!({
    346         "key": "state-a",
    347         "kind": 30023,
    348         "pubkey": hex64('d'),
    349         "d_tag": "listing-a",
    350         "last_event_id": hex64('e'),
    351         "last_created_at": 1,
    352         "content_hash": "hash-a"
    353     }));
    354     let nostr_event_head_created = db
    355         .nostr_event_head_create(&nostr_event_head)
    356         .expect("nostr event state create")
    357         .result;
    358 
    359     let trade_product: ITradeProductCreate = parse_json(json!({
    360         "key": "product-a",
    361         "category": "coffee",
    362         "title": "coffee a",
    363         "summary": "summary",
    364         "process": "washed",
    365         "lot": "lot-a",
    366         "profile": "floral",
    367         "year": 2024,
    368         "qty_amt": 100,
    369         "qty_amt_exact": "100",
    370         "qty_unit": "kg",
    371         "price_amt": 7.5,
    372         "price_amt_exact": "7.5",
    373         "price_currency": "USD",
    374         "price_qty_amt": 1,
    375         "price_qty_amt_exact": "1",
    376         "price_qty_unit": "kg"
    377     }));
    378     let trade_product_created = db
    379         .trade_product_create(&trade_product)
    380         .expect("trade product create")
    381         .result;
    382 
    383     let gcs_extra: IGcsLocationCreate = parse_json(json!({
    384         "d_tag": "gcs-b",
    385         "lat": 59.34,
    386         "lng": 18.07,
    387         "geohash": "u6sce4g",
    388         "point": "POINT(18.07 59.34)",
    389         "polygon": "POLYGON((18.07 59.34,18.08 59.34,18.08 59.35,18.07 59.35,18.07 59.34))"
    390     }));
    391     let _gcs_extra_created = db
    392         .gcs_location_create(&gcs_extra)
    393         .expect("gcs extra create")
    394         .result;
    395 
    396     let media_image_extra: IMediaImageCreate = parse_json(json!({
    397         "file_path": "/img/b.jpg",
    398         "mime_type": "image/jpeg",
    399         "res_base": "https://cdn.example.com",
    400         "res_path": "img/b.jpg"
    401     }));
    402     let _media_image_extra_created = db
    403         .media_image_create(&media_image_extra)
    404         .expect("media image extra create")
    405         .result;
    406 
    407     let nostr_profile_extra: INostrProfileCreate = parse_json(json!({
    408         "public_key": hex64('f'),
    409         "profile_type": "farm",
    410         "name": "profile c"
    411     }));
    412     let nostr_profile_extra_created = db
    413         .nostr_profile_create(&nostr_profile_extra)
    414         .expect("nostr profile extra create")
    415         .result;
    416 
    417     let nostr_relay_extra: INostrRelayCreate = parse_json(json!({
    418         "url": "wss://relay2.example.com"
    419     }));
    420     let nostr_relay_extra_created = db
    421         .nostr_relay_create(&nostr_relay_extra)
    422         .expect("nostr relay extra create")
    423         .result;
    424 
    425     let profile_relay_rel: INostrProfileRelayRelation = parse_json(json!({
    426         "nostr_profile": { "id": nostr_profile_created.id },
    427         "nostr_relay": { "id": nostr_relay_created.id }
    428     }));
    429     db.nostr_profile_relay_set(&profile_relay_rel)
    430         .expect("profile relay set");
    431 
    432     let profile_relay_rel_extra: INostrProfileRelayRelation = parse_json(json!({
    433         "nostr_profile": { "id": nostr_profile_extra_created.id },
    434         "nostr_relay": { "id": nostr_relay_extra_created.id }
    435     }));
    436     db.nostr_profile_relay_set(&profile_relay_rel_extra)
    437         .expect("profile relay extra set");
    438 
    439     let product_location_rel: ITradeProductLocationRelation = parse_json(json!({
    440         "trade_product": { "id": trade_product_created.id },
    441         "gcs_location": { "id": gcs_created.id }
    442     }));
    443     db.trade_product_location_set(&product_location_rel)
    444         .expect("product location set");
    445 
    446     let product_media_rel: ITradeProductMediaRelation = parse_json(json!({
    447         "trade_product": { "id": trade_product_created.id },
    448         "media_image": { "id": media_image_created.id }
    449     }));
    450     db.trade_product_media_set(&product_media_rel)
    451         .expect("product media set");
    452 
    453     let _: IFarmFindMany = parse_json(json!({ "filter": { "id": farm_created.id } }));
    454     let farm_find_many: IFarmFindMany = parse_json(json!({ "filter": { "id": farm_created.id } }));
    455     assert_eq!(
    456         db.farm_find_many(&farm_find_many)
    457             .expect("farm find many")
    458             .results
    459             .len(),
    460         1
    461     );
    462 
    463     let farm_find_one: IFarmFindOne = parse_json(json!({ "on": { "id": farm_created.id } }));
    464     assert!(
    465         db.farm_find_one(&farm_find_one)
    466             .expect("farm find one")
    467             .result
    468             .is_some()
    469     );
    470 
    471     let farm_update_on_alt: IFarmUpdate =
    472         parse_json(json!({ "on": { "d_tag": "farm-a" }, "fields": { "name": "farm a+" } }));
    473     assert_eq!(
    474         db.farm_update(&farm_update_on_alt)
    475             .expect("farm update alt")
    476             .result
    477             .name,
    478         "farm a+"
    479     );
    480     let farm_update_on_id: IFarmUpdate =
    481         parse_json(json!({ "on": { "id": farm_created.id }, "fields": { "name": "farm a++" } }));
    482     assert_eq!(
    483         db.farm_update(&farm_update_on_id)
    484             .expect("farm update id")
    485             .result
    486             .name,
    487         "farm a++"
    488     );
    489     let farm_update_empty: IFarmUpdate =
    490         parse_json(json!({ "on": { "id": farm_created.id }, "fields": {} }));
    491     assert_invalid_argument(db.farm_update(&farm_update_empty));
    492 
    493     let plot_find_many: IPlotFindMany = parse_json(json!({ "filter": { "id": plot_created.id } }));
    494     assert_eq!(
    495         db.plot_find_many(&plot_find_many)
    496             .expect("plot find many")
    497             .results
    498             .len(),
    499         1
    500     );
    501     let plot_find_one: IPlotFindOne = parse_json(json!({ "on": { "id": plot_created.id } }));
    502     assert!(
    503         db.plot_find_one(&plot_find_one)
    504             .expect("plot find one")
    505             .result
    506             .is_some()
    507     );
    508     let plot_update_alt: IPlotUpdate =
    509         parse_json(json!({ "on": { "d_tag": "plot-a" }, "fields": { "name": "plot a+" } }));
    510     assert_eq!(
    511         db.plot_update(&plot_update_alt)
    512             .expect("plot update alt")
    513             .result
    514             .name,
    515         "plot a+"
    516     );
    517     let plot_update_id: IPlotUpdate =
    518         parse_json(json!({ "on": { "id": plot_created.id }, "fields": { "name": "plot a++" } }));
    519     assert_eq!(
    520         db.plot_update(&plot_update_id)
    521             .expect("plot update id")
    522             .result
    523             .name,
    524         "plot a++"
    525     );
    526     let plot_update_empty: IPlotUpdate =
    527         parse_json(json!({ "on": { "id": plot_created.id }, "fields": {} }));
    528     assert_invalid_argument(db.plot_update(&plot_update_empty));
    529 
    530     for opts in [
    531         IGcsLocationFindMany::Rel {
    532             rel: GcsLocationFindManyRel::OnTradeProduct(GcsLocationTradeProductArgs {
    533                 id: trade_product_created.id.clone(),
    534             }),
    535         },
    536         IGcsLocationFindMany::Rel {
    537             rel: GcsLocationFindManyRel::OffTradeProduct(GcsLocationTradeProductArgs {
    538                 id: trade_product_created.id.clone(),
    539             }),
    540         },
    541         IGcsLocationFindMany::Rel {
    542             rel: GcsLocationFindManyRel::OnFarm(GcsLocationFarmArgs {
    543                 id: farm_created.id.clone(),
    544             }),
    545         },
    546         IGcsLocationFindMany::Rel {
    547             rel: GcsLocationFindManyRel::OffFarm(GcsLocationFarmArgs {
    548                 id: farm_created.id.clone(),
    549             }),
    550         },
    551         IGcsLocationFindMany::Rel {
    552             rel: GcsLocationFindManyRel::OnPlot(GcsLocationPlotArgs {
    553                 id: plot_created.id.clone(),
    554             }),
    555         },
    556         IGcsLocationFindMany::Rel {
    557             rel: GcsLocationFindManyRel::OffPlot(GcsLocationPlotArgs {
    558                 id: plot_created.id.clone(),
    559             }),
    560         },
    561     ] {
    562         let _ = db.gcs_location_find_many(&opts).expect("gcs rel find many");
    563     }
    564     let gcs_find_many_filter: IGcsLocationFindMany =
    565         parse_json(json!({ "filter": { "id": gcs_created.id } }));
    566     assert_eq!(
    567         db.gcs_location_find_many(&gcs_find_many_filter)
    568             .expect("gcs find many filter")
    569             .results
    570             .len(),
    571         1
    572     );
    573     let gcs_find_one_on: IGcsLocationFindOne =
    574         parse_json(json!({ "on": { "id": gcs_created.id } }));
    575     assert!(
    576         db.gcs_location_find_one(&gcs_find_one_on)
    577             .expect("gcs find one on")
    578             .result
    579             .is_some()
    580     );
    581     let gcs_find_one_rel: IGcsLocationFindOne =
    582         parse_json(json!({ "rel": { "on_farm": { "id": farm_created.id } } }));
    583     assert!(
    584         db.gcs_location_find_one(&gcs_find_one_rel)
    585             .expect("gcs find one rel")
    586             .result
    587             .is_some()
    588     );
    589     let gcs_update_alt: IGcsLocationUpdate =
    590         parse_json(json!({ "on": { "d_tag": "gcs-a" }, "fields": { "label": "gcs a+" } }));
    591     assert_eq!(
    592         db.gcs_location_update(&gcs_update_alt)
    593             .expect("gcs update alt")
    594             .result
    595             .label
    596             .as_deref(),
    597         Some("gcs a+")
    598     );
    599     let gcs_update_id: IGcsLocationUpdate =
    600         parse_json(json!({ "on": { "id": gcs_created.id }, "fields": { "label": "gcs a++" } }));
    601     assert_eq!(
    602         db.gcs_location_update(&gcs_update_id)
    603             .expect("gcs update id")
    604             .result
    605             .label
    606             .as_deref(),
    607         Some("gcs a++")
    608     );
    609     let gcs_update_empty: IGcsLocationUpdate =
    610         parse_json(json!({ "on": { "id": gcs_created.id }, "fields": {} }));
    611     assert_invalid_argument(db.gcs_location_update(&gcs_update_empty));
    612 
    613     let farm_gcs_find_many: IFarmGcsLocationFindMany =
    614         parse_json(json!({ "filter": { "id": farm_gcs_created.id } }));
    615     assert_eq!(
    616         db.farm_gcs_location_find_many(&farm_gcs_find_many)
    617             .expect("farm gcs find many")
    618             .results
    619             .len(),
    620         1
    621     );
    622     let farm_gcs_find_one: IFarmGcsLocationFindOne =
    623         parse_json(json!({ "on": { "id": farm_gcs_created.id } }));
    624     assert!(
    625         db.farm_gcs_location_find_one(&farm_gcs_find_one)
    626             .expect("farm gcs find one")
    627             .result
    628             .is_some()
    629     );
    630     let farm_gcs_update_alt: IFarmGcsLocationUpdate = parse_json(json!({
    631         "on": { "farm_id": farm_created.id },
    632         "fields": { "role": "secondary" }
    633     }));
    634     assert_eq!(
    635         db.farm_gcs_location_update(&farm_gcs_update_alt)
    636             .expect("farm gcs update")
    637             .result
    638             .role,
    639         "secondary"
    640     );
    641     let farm_gcs_update_id: IFarmGcsLocationUpdate = parse_json(
    642         json!({ "on": { "id": farm_gcs_created.id }, "fields": { "role": "tertiary" } }),
    643     );
    644     assert_eq!(
    645         db.farm_gcs_location_update(&farm_gcs_update_id)
    646             .expect("farm gcs update id")
    647             .result
    648             .role,
    649         "tertiary"
    650     );
    651     let farm_gcs_update_empty: IFarmGcsLocationUpdate =
    652         parse_json(json!({ "on": { "id": farm_gcs_created.id }, "fields": {} }));
    653     assert_invalid_argument(db.farm_gcs_location_update(&farm_gcs_update_empty));
    654 
    655     let plot_gcs_find_many: IPlotGcsLocationFindMany =
    656         parse_json(json!({ "filter": { "id": plot_gcs_created.id } }));
    657     assert_eq!(
    658         db.plot_gcs_location_find_many(&plot_gcs_find_many)
    659             .expect("plot gcs find many")
    660             .results
    661             .len(),
    662         1
    663     );
    664     let plot_gcs_find_one: IPlotGcsLocationFindOne =
    665         parse_json(json!({ "on": { "id": plot_gcs_created.id } }));
    666     assert!(
    667         db.plot_gcs_location_find_one(&plot_gcs_find_one)
    668             .expect("plot gcs find one")
    669             .result
    670             .is_some()
    671     );
    672     let plot_gcs_update_alt: IPlotGcsLocationUpdate = parse_json(json!({
    673         "on": { "plot_id": plot_created.id },
    674         "fields": { "role": "secondary" }
    675     }));
    676     assert_eq!(
    677         db.plot_gcs_location_update(&plot_gcs_update_alt)
    678             .expect("plot gcs update")
    679             .result
    680             .role,
    681         "secondary"
    682     );
    683     let plot_gcs_update_id: IPlotGcsLocationUpdate = parse_json(
    684         json!({ "on": { "id": plot_gcs_created.id }, "fields": { "role": "tertiary" } }),
    685     );
    686     assert_eq!(
    687         db.plot_gcs_location_update(&plot_gcs_update_id)
    688             .expect("plot gcs update id")
    689             .result
    690             .role,
    691         "tertiary"
    692     );
    693     let plot_gcs_update_empty: IPlotGcsLocationUpdate =
    694         parse_json(json!({ "on": { "id": plot_gcs_created.id }, "fields": {} }));
    695     assert_invalid_argument(db.plot_gcs_location_update(&plot_gcs_update_empty));
    696 
    697     let farm_tag_find_many: IFarmTagFindMany =
    698         parse_json(json!({ "filter": { "id": farm_tag_created.id } }));
    699     assert_eq!(
    700         db.farm_tag_find_many(&farm_tag_find_many)
    701             .expect("farm tag find many")
    702             .results
    703             .len(),
    704         1
    705     );
    706     let farm_tag_find_one: IFarmTagFindOne =
    707         parse_json(json!({ "on": { "id": farm_tag_created.id } }));
    708     assert!(
    709         db.farm_tag_find_one(&farm_tag_find_one)
    710             .expect("farm tag find one")
    711             .result
    712             .is_some()
    713     );
    714     let farm_tag_update_alt: IFarmTagUpdate = parse_json(
    715         json!({ "on": { "farm_id": farm_created.id }, "fields": { "tag": "biodynamic" } }),
    716     );
    717     assert_eq!(
    718         db.farm_tag_update(&farm_tag_update_alt)
    719             .expect("farm tag update")
    720             .result
    721             .tag,
    722         "biodynamic"
    723     );
    724     let farm_tag_update_id: IFarmTagUpdate = parse_json(
    725         json!({ "on": { "id": farm_tag_created.id }, "fields": { "tag": "regenerative" } }),
    726     );
    727     assert_eq!(
    728         db.farm_tag_update(&farm_tag_update_id)
    729             .expect("farm tag update id")
    730             .result
    731             .tag,
    732         "regenerative"
    733     );
    734     let farm_tag_update_empty: IFarmTagUpdate =
    735         parse_json(json!({ "on": { "id": farm_tag_created.id }, "fields": {} }));
    736     assert_invalid_argument(db.farm_tag_update(&farm_tag_update_empty));
    737 
    738     let plot_tag_find_many: IPlotTagFindMany =
    739         parse_json(json!({ "filter": { "id": plot_tag_created.id } }));
    740     assert_eq!(
    741         db.plot_tag_find_many(&plot_tag_find_many)
    742             .expect("plot tag find many")
    743             .results
    744             .len(),
    745         1
    746     );
    747     let plot_tag_find_one: IPlotTagFindOne =
    748         parse_json(json!({ "on": { "id": plot_tag_created.id } }));
    749     assert!(
    750         db.plot_tag_find_one(&plot_tag_find_one)
    751             .expect("plot tag find one")
    752             .result
    753             .is_some()
    754     );
    755     let plot_tag_update_alt: IPlotTagUpdate =
    756         parse_json(json!({ "on": { "plot_id": plot_created.id }, "fields": { "tag": "south" } }));
    757     assert_eq!(
    758         db.plot_tag_update(&plot_tag_update_alt)
    759             .expect("plot tag update")
    760             .result
    761             .tag,
    762         "south"
    763     );
    764     let plot_tag_update_id: IPlotTagUpdate =
    765         parse_json(json!({ "on": { "id": plot_tag_created.id }, "fields": { "tag": "east" } }));
    766     assert_eq!(
    767         db.plot_tag_update(&plot_tag_update_id)
    768             .expect("plot tag update id")
    769             .result
    770             .tag,
    771         "east"
    772     );
    773     let plot_tag_update_empty: IPlotTagUpdate =
    774         parse_json(json!({ "on": { "id": plot_tag_created.id }, "fields": {} }));
    775     assert_invalid_argument(db.plot_tag_update(&plot_tag_update_empty));
    776 
    777     let farm_member_find_many: IFarmMemberFindMany =
    778         parse_json(json!({ "filter": { "id": farm_member_created.id } }));
    779     assert_eq!(
    780         db.farm_member_find_many(&farm_member_find_many)
    781             .expect("farm member find many")
    782             .results
    783             .len(),
    784         1
    785     );
    786     let farm_member_find_one: IFarmMemberFindOne =
    787         parse_json(json!({ "on": { "id": farm_member_created.id } }));
    788     assert!(
    789         db.farm_member_find_one(&farm_member_find_one)
    790             .expect("farm member find one")
    791             .result
    792             .is_some()
    793     );
    794     let farm_member_update_alt: IFarmMemberUpdate = parse_json(json!({
    795         "on": { "member_pubkey": hex64('b') },
    796         "fields": { "role": "editor" }
    797     }));
    798     assert_eq!(
    799         db.farm_member_update(&farm_member_update_alt)
    800             .expect("farm member update")
    801             .result
    802             .role,
    803         "editor"
    804     );
    805     let farm_member_update_id: IFarmMemberUpdate = parse_json(
    806         json!({ "on": { "id": farm_member_created.id }, "fields": { "role": "admin" } }),
    807     );
    808     assert_eq!(
    809         db.farm_member_update(&farm_member_update_id)
    810             .expect("farm member update id")
    811             .result
    812             .role,
    813         "admin"
    814     );
    815     let farm_member_update_empty: IFarmMemberUpdate =
    816         parse_json(json!({ "on": { "id": farm_member_created.id }, "fields": {} }));
    817     assert_invalid_argument(db.farm_member_update(&farm_member_update_empty));
    818 
    819     let farm_member_claim_find_many: IFarmMemberClaimFindMany =
    820         parse_json(json!({ "filter": { "id": farm_member_claim_created.id } }));
    821     assert_eq!(
    822         db.farm_member_claim_find_many(&farm_member_claim_find_many)
    823             .expect("farm member claim find many")
    824             .results
    825             .len(),
    826         1
    827     );
    828     let farm_member_claim_find_one: IFarmMemberClaimFindOne =
    829         parse_json(json!({ "on": { "id": farm_member_claim_created.id } }));
    830     assert!(
    831         db.farm_member_claim_find_one(&farm_member_claim_find_one)
    832             .expect("farm member claim find one")
    833             .result
    834             .is_some()
    835     );
    836     let farm_member_claim_update_alt: IFarmMemberClaimUpdate = parse_json(json!({
    837         "on": { "member_pubkey": hex64('b') },
    838         "fields": { "farm_pubkey": hex64('f') }
    839     }));
    840     assert_eq!(
    841         db.farm_member_claim_update(&farm_member_claim_update_alt)
    842             .expect("farm member claim update")
    843             .result
    844             .farm_pubkey,
    845         hex64('f')
    846     );
    847     let farm_member_claim_update_id: IFarmMemberClaimUpdate = parse_json(json!({
    848         "on": { "id": farm_member_claim_created.id },
    849         "fields": { "farm_pubkey": hex64('g') }
    850     }));
    851     assert_eq!(
    852         db.farm_member_claim_update(&farm_member_claim_update_id)
    853             .expect("farm member claim update id")
    854             .result
    855             .farm_pubkey,
    856         hex64('g')
    857     );
    858     let farm_member_claim_update_empty: IFarmMemberClaimUpdate =
    859         parse_json(json!({ "on": { "id": farm_member_claim_created.id }, "fields": {} }));
    860     assert_invalid_argument(db.farm_member_claim_update(&farm_member_claim_update_empty));
    861 
    862     let log_error_find_many: ILogErrorFindMany =
    863         parse_json(json!({ "filter": { "id": log_error_created.id } }));
    864     assert_eq!(
    865         db.log_error_find_many(&log_error_find_many)
    866             .expect("log error find many")
    867             .results
    868             .len(),
    869         1
    870     );
    871     let log_error_find_one: ILogErrorFindOne =
    872         parse_json(json!({ "on": { "id": log_error_created.id } }));
    873     assert!(
    874         db.log_error_find_one(&log_error_find_one)
    875             .expect("log error find one")
    876             .result
    877             .is_some()
    878     );
    879     let log_error_update_alt: ILogErrorUpdate = parse_json(json!({
    880         "on": { "nostr_pubkey": hex64('c') },
    881         "fields": { "message": "boom+" }
    882     }));
    883     assert_eq!(
    884         db.log_error_update(&log_error_update_alt)
    885             .expect("log error update")
    886             .result
    887             .message,
    888         "boom+"
    889     );
    890     let log_error_update_id: ILogErrorUpdate = parse_json(
    891         json!({ "on": { "id": log_error_created.id }, "fields": { "message": "boom++" } }),
    892     );
    893     assert_eq!(
    894         db.log_error_update(&log_error_update_id)
    895             .expect("log error update id")
    896             .result
    897             .message,
    898         "boom++"
    899     );
    900     let log_error_update_empty: ILogErrorUpdate =
    901         parse_json(json!({ "on": { "id": log_error_created.id }, "fields": {} }));
    902     assert_invalid_argument(db.log_error_update(&log_error_update_empty));
    903 
    904     for opts in [
    905         IMediaImageFindMany::Rel {
    906             rel: MediaImageFindManyRel::OnTradeProduct(MediaImageTradeProductArgs {
    907                 id: trade_product_created.id.clone(),
    908             }),
    909         },
    910         IMediaImageFindMany::Rel {
    911             rel: MediaImageFindManyRel::OffTradeProduct(MediaImageTradeProductArgs {
    912                 id: trade_product_created.id.clone(),
    913             }),
    914         },
    915     ] {
    916         let _ = db
    917             .media_image_find_many(&opts)
    918             .expect("media image rel find many");
    919     }
    920     let media_image_find_many_filter: IMediaImageFindMany =
    921         parse_json(json!({ "filter": { "id": media_image_created.id } }));
    922     assert_eq!(
    923         db.media_image_find_many(&media_image_find_many_filter)
    924             .expect("media image find many filter")
    925             .results
    926             .len(),
    927         1
    928     );
    929     let media_image_find_one_on: IMediaImageFindOne =
    930         parse_json(json!({ "on": { "id": media_image_created.id } }));
    931     assert!(
    932         db.media_image_find_one(&media_image_find_one_on)
    933             .expect("media image find one")
    934             .result
    935             .is_some()
    936     );
    937     let media_image_find_one_rel: IMediaImageFindOne =
    938         parse_json(json!({ "rel": { "on_trade_product": { "id": trade_product_created.id } } }));
    939     assert!(
    940         db.media_image_find_one(&media_image_find_one_rel)
    941             .expect("media image find one rel")
    942             .result
    943             .is_some()
    944     );
    945     let media_image_update_alt: IMediaImageUpdate =
    946         parse_json(json!({ "on": { "file_path": "/img/a.jpg" }, "fields": { "label": "hero" } }));
    947     assert_eq!(
    948         db.media_image_update(&media_image_update_alt)
    949             .expect("media image update")
    950             .result
    951             .label
    952             .as_deref(),
    953         Some("hero")
    954     );
    955     let media_image_update_id: IMediaImageUpdate = parse_json(
    956         json!({ "on": { "id": media_image_created.id }, "fields": { "label": "hero+" } }),
    957     );
    958     assert_eq!(
    959         db.media_image_update(&media_image_update_id)
    960             .expect("media image update id")
    961             .result
    962             .label
    963             .as_deref(),
    964         Some("hero+")
    965     );
    966     let media_image_update_empty: IMediaImageUpdate =
    967         parse_json(json!({ "on": { "id": media_image_created.id }, "fields": {} }));
    968     assert_invalid_argument(db.media_image_update(&media_image_update_empty));
    969 
    970     for opts in [
    971         INostrProfileFindMany::Rel {
    972             rel: NostrProfileFindManyRel::OnRelay(NostrProfileRelayArgs {
    973                 id: nostr_relay_created.id.clone(),
    974             }),
    975         },
    976         INostrProfileFindMany::Rel {
    977             rel: NostrProfileFindManyRel::OffRelay(NostrProfileRelayArgs {
    978                 id: nostr_relay_created.id.clone(),
    979             }),
    980         },
    981     ] {
    982         let _ = db
    983             .nostr_profile_find_many(&opts)
    984             .expect("nostr profile rel find many");
    985     }
    986     let nostr_profile_find_many_filter: INostrProfileFindMany =
    987         parse_json(json!({ "filter": { "id": nostr_profile_created.id } }));
    988     assert_eq!(
    989         db.nostr_profile_find_many(&nostr_profile_find_many_filter)
    990             .expect("nostr profile find many filter")
    991             .results
    992             .len(),
    993         1
    994     );
    995     let nostr_profile_find_one_on: INostrProfileFindOne =
    996         parse_json(json!({ "on": { "id": nostr_profile_created.id } }));
    997     assert!(
    998         db.nostr_profile_find_one(&nostr_profile_find_one_on)
    999             .expect("nostr profile find one")
   1000             .result
   1001             .is_some()
   1002     );
   1003     let nostr_profile_find_one_rel: INostrProfileFindOne =
   1004         parse_json(json!({ "rel": { "on_relay": { "id": nostr_relay_created.id } } }));
   1005     assert!(
   1006         db.nostr_profile_find_one(&nostr_profile_find_one_rel)
   1007             .expect("nostr profile find one rel")
   1008             .result
   1009             .is_some()
   1010     );
   1011     let nostr_profile_update_alt: INostrProfileUpdate = parse_json(
   1012         json!({ "on": { "public_key": hex64('d') }, "fields": { "name": "profile b" } }),
   1013     );
   1014     assert_eq!(
   1015         db.nostr_profile_update(&nostr_profile_update_alt)
   1016             .expect("nostr profile update")
   1017             .result
   1018             .name,
   1019         "profile b"
   1020     );
   1021     let nostr_profile_update_id: INostrProfileUpdate = parse_json(
   1022         json!({ "on": { "id": nostr_profile_created.id }, "fields": { "name": "profile b+" } }),
   1023     );
   1024     assert_eq!(
   1025         db.nostr_profile_update(&nostr_profile_update_id)
   1026             .expect("nostr profile update id")
   1027             .result
   1028             .name,
   1029         "profile b+"
   1030     );
   1031     let nostr_profile_update_empty: INostrProfileUpdate =
   1032         parse_json(json!({ "on": { "id": nostr_profile_created.id }, "fields": {} }));
   1033     assert_invalid_argument(db.nostr_profile_update(&nostr_profile_update_empty));
   1034 
   1035     let nostr_event_head_find_many: INostrEventHeadFindMany =
   1036         parse_json(json!({ "filter": { "id": nostr_event_head_created.id } }));
   1037     assert_eq!(
   1038         db.nostr_event_head_find_many(&nostr_event_head_find_many)
   1039             .expect("nostr event state find many")
   1040             .results
   1041             .len(),
   1042         1
   1043     );
   1044     let nostr_event_head_find_one: INostrEventHeadFindOne =
   1045         parse_json(json!({ "on": { "id": nostr_event_head_created.id } }));
   1046     assert!(
   1047         db.nostr_event_head_find_one(&nostr_event_head_find_one)
   1048             .expect("nostr event state find one")
   1049             .result
   1050             .is_some()
   1051     );
   1052     let nostr_event_head_update_alt: INostrEventHeadUpdate =
   1053         parse_json(json!({ "on": { "key": "state-a" }, "fields": { "content_hash": "hash-b" } }));
   1054     assert_eq!(
   1055         db.nostr_event_head_update(&nostr_event_head_update_alt)
   1056             .expect("nostr event state update")
   1057             .result
   1058             .content_hash,
   1059         "hash-b"
   1060     );
   1061     let nostr_event_head_update_id: INostrEventHeadUpdate = parse_json(
   1062         json!({ "on": { "id": nostr_event_head_created.id }, "fields": { "content_hash": "hash-c" } }),
   1063     );
   1064     assert_eq!(
   1065         db.nostr_event_head_update(&nostr_event_head_update_id)
   1066             .expect("nostr event state update id")
   1067             .result
   1068             .content_hash,
   1069         "hash-c"
   1070     );
   1071     let nostr_event_head_update_empty: INostrEventHeadUpdate =
   1072         parse_json(json!({ "on": { "id": nostr_event_head_created.id }, "fields": {} }));
   1073     assert_invalid_argument(db.nostr_event_head_update(&nostr_event_head_update_empty));
   1074 
   1075     for opts in [
   1076         INostrRelayFindMany::Rel {
   1077             rel: NostrRelayFindManyRel::OnProfile(NostrRelayProfileArgs {
   1078                 public_key: hex64('d'),
   1079             }),
   1080         },
   1081         INostrRelayFindMany::Rel {
   1082             rel: NostrRelayFindManyRel::OffProfile(NostrRelayProfileArgs {
   1083                 public_key: hex64('d'),
   1084             }),
   1085         },
   1086     ] {
   1087         let _ = db
   1088             .nostr_relay_find_many(&opts)
   1089             .expect("nostr relay rel find many");
   1090     }
   1091     let nostr_relay_find_many_filter: INostrRelayFindMany =
   1092         parse_json(json!({ "filter": { "id": nostr_relay_created.id } }));
   1093     assert_eq!(
   1094         db.nostr_relay_find_many(&nostr_relay_find_many_filter)
   1095             .expect("nostr relay find many filter")
   1096             .results
   1097             .len(),
   1098         1
   1099     );
   1100     let nostr_relay_find_one_on: INostrRelayFindOne =
   1101         parse_json(json!({ "on": { "id": nostr_relay_created.id } }));
   1102     assert!(
   1103         db.nostr_relay_find_one(&nostr_relay_find_one_on)
   1104             .expect("nostr relay find one")
   1105             .result
   1106             .is_some()
   1107     );
   1108     let nostr_relay_find_one_rel: INostrRelayFindOne =
   1109         parse_json(json!({ "rel": { "on_profile": { "public_key": hex64('d') } } }));
   1110     assert!(
   1111         db.nostr_relay_find_one(&nostr_relay_find_one_rel)
   1112             .expect("nostr relay find one rel")
   1113             .result
   1114             .is_some()
   1115     );
   1116     let nostr_relay_update_alt: INostrRelayUpdate = parse_json(json!({
   1117         "on": { "url": "wss://relay.example.com" },
   1118         "fields": { "name": "relay a" }
   1119     }));
   1120     assert_eq!(
   1121         db.nostr_relay_update(&nostr_relay_update_alt)
   1122             .expect("nostr relay update")
   1123             .result
   1124             .name
   1125             .as_deref(),
   1126         Some("relay a")
   1127     );
   1128     let nostr_relay_update_id: INostrRelayUpdate = parse_json(
   1129         json!({ "on": { "id": nostr_relay_created.id }, "fields": { "name": "relay a+" } }),
   1130     );
   1131     assert_eq!(
   1132         db.nostr_relay_update(&nostr_relay_update_id)
   1133             .expect("nostr relay update id")
   1134             .result
   1135             .name
   1136             .as_deref(),
   1137         Some("relay a+")
   1138     );
   1139     let nostr_relay_update_empty: INostrRelayUpdate =
   1140         parse_json(json!({ "on": { "id": nostr_relay_created.id }, "fields": {} }));
   1141     assert_invalid_argument(db.nostr_relay_update(&nostr_relay_update_empty));
   1142 
   1143     let trade_product_find_many: ITradeProductFindMany =
   1144         parse_json(json!({ "filter": { "id": trade_product_created.id } }));
   1145     let trade_product_results = db
   1146         .trade_product_find_many(&trade_product_find_many)
   1147         .expect("trade product find many")
   1148         .results;
   1149     assert_eq!(trade_product_results.len(), 1);
   1150     assert_eq!(trade_product_results[0].listing_addr, None);
   1151     let trade_product_find_one: ITradeProductFindOne =
   1152         parse_json(json!({ "on": { "id": trade_product_created.id } }));
   1153     assert!(
   1154         db.trade_product_find_one(&trade_product_find_one)
   1155             .expect("trade product find one")
   1156             .result
   1157             .is_some()
   1158     );
   1159     let trade_product_update: ITradeProductUpdate = parse_json(
   1160         json!({ "on": { "id": trade_product_created.id }, "fields": { "title": "coffee b" } }),
   1161     );
   1162     assert_eq!(
   1163         db.trade_product_update(&trade_product_update)
   1164             .expect("trade product update")
   1165             .result
   1166             .title,
   1167         "coffee b"
   1168     );
   1169     let trade_product_update_empty: ITradeProductUpdate =
   1170         parse_json(json!({ "on": { "id": trade_product_created.id }, "fields": {} }));
   1171     assert_invalid_argument(db.trade_product_update(&trade_product_update_empty));
   1172 
   1173     let backup = db.backup_database().expect("backup");
   1174     let backup_json = db.backup_database_json().expect("backup json");
   1175     let _manifest = export_manifest(db.executor()).expect("export manifest");
   1176     db.restore_database(&backup).expect("restore backup");
   1177     db.restore_database_json(&backup_json)
   1178         .expect("restore backup json");
   1179 
   1180     let gcs_delete_rel_found: IGcsLocationDelete =
   1181         parse_json(json!({ "rel": { "off_trade_product": { "id": trade_product_created.id } } }));
   1182     db.gcs_location_delete(&gcs_delete_rel_found)
   1183         .expect("gcs rel delete found");
   1184 
   1185     let media_image_rel_delete_found: IMediaImageDelete =
   1186         parse_json(json!({ "rel": { "off_trade_product": { "id": trade_product_created.id } } }));
   1187     db.media_image_delete(&media_image_rel_delete_found)
   1188         .expect("media image rel delete found");
   1189 
   1190     let nostr_relay_rel_delete_found: INostrRelayDelete =
   1191         parse_json(json!({ "rel": { "on_profile": { "public_key": hex64('f') } } }));
   1192     db.nostr_relay_delete(&nostr_relay_rel_delete_found)
   1193         .expect("nostr relay rel delete found");
   1194 
   1195     let nostr_profile_rel_delete_found: INostrProfileDelete =
   1196         parse_json(json!({ "rel": { "off_relay": { "id": nostr_relay_created.id } } }));
   1197     db.nostr_profile_delete(&nostr_profile_rel_delete_found)
   1198         .expect("nostr profile rel delete found");
   1199 
   1200     db.trade_product_media_unset(&product_media_rel)
   1201         .expect("product media unset");
   1202     db.trade_product_location_unset(&product_location_rel)
   1203         .expect("product location unset");
   1204     db.nostr_profile_relay_unset(&profile_relay_rel)
   1205         .expect("profile relay unset");
   1206 
   1207     let trade_product_delete: ITradeProductDelete =
   1208         parse_json(json!({ "on": { "id": trade_product_created.id } }));
   1209     db.trade_product_delete(&trade_product_delete)
   1210         .expect("trade product delete");
   1211     let trade_product_delete_missing: ITradeProductDelete =
   1212         parse_json(json!({ "on": { "id": trade_product_created.id } }));
   1213     assert_not_found(db.trade_product_delete(&trade_product_delete_missing));
   1214 
   1215     let plot_gcs_delete: IPlotGcsLocationDelete =
   1216         parse_json(json!({ "on": { "plot_id": plot_created.id } }));
   1217     db.plot_gcs_location_delete(&plot_gcs_delete)
   1218         .expect("plot gcs delete");
   1219     let plot_gcs_delete_missing: IPlotGcsLocationDelete =
   1220         parse_json(json!({ "on": { "id": plot_gcs_created.id } }));
   1221     assert_not_found(db.plot_gcs_location_delete(&plot_gcs_delete_missing));
   1222 
   1223     let farm_gcs_delete: IFarmGcsLocationDelete =
   1224         parse_json(json!({ "on": { "farm_id": farm_created.id } }));
   1225     db.farm_gcs_location_delete(&farm_gcs_delete)
   1226         .expect("farm gcs delete");
   1227     let farm_gcs_delete_missing: IFarmGcsLocationDelete =
   1228         parse_json(json!({ "on": { "id": farm_gcs_created.id } }));
   1229     assert_not_found(db.farm_gcs_location_delete(&farm_gcs_delete_missing));
   1230 
   1231     let gcs_delete_on_non_primary: IGcsLocationDelete =
   1232         parse_json(json!({ "on": { "d_tag": "gcs-a" } }));
   1233     let _ = db.gcs_location_delete(&gcs_delete_on_non_primary);
   1234 
   1235     for payload in [
   1236         json!({ "rel": { "on_trade_product": { "id": trade_product_created.id } } }),
   1237         json!({ "rel": { "off_trade_product": { "id": trade_product_created.id } } }),
   1238         json!({ "rel": { "on_farm": { "id": farm_created.id } } }),
   1239         json!({ "rel": { "off_farm": { "id": farm_created.id } } }),
   1240         json!({ "rel": { "on_plot": { "id": plot_created.id } } }),
   1241         json!({ "rel": { "off_plot": { "id": plot_created.id } } }),
   1242     ] {
   1243         let opts: IGcsLocationDelete = parse_json(payload);
   1244         let _ = db.gcs_location_delete(&opts);
   1245     }
   1246     let gcs_delete_missing: IGcsLocationDelete =
   1247         parse_json(json!({ "on": { "id": gcs_created.id } }));
   1248     assert_not_found(db.gcs_location_delete(&gcs_delete_missing));
   1249 
   1250     let media_image_delete: IMediaImageDelete =
   1251         parse_json(json!({ "on": { "file_path": "/img/a.jpg" } }));
   1252     db.media_image_delete(&media_image_delete)
   1253         .expect("media image delete");
   1254     let media_image_delete_missing: IMediaImageDelete =
   1255         parse_json(json!({ "on": { "id": media_image_created.id } }));
   1256     assert_not_found(db.media_image_delete(&media_image_delete_missing));
   1257     for payload in [
   1258         json!({ "rel": { "on_trade_product": { "id": trade_product_created.id } } }),
   1259         json!({ "rel": { "off_trade_product": { "id": trade_product_created.id } } }),
   1260     ] {
   1261         let opts: IMediaImageDelete = parse_json(payload);
   1262         let _ = db.media_image_delete(&opts);
   1263     }
   1264 
   1265     let nostr_profile_delete: INostrProfileDelete =
   1266         parse_json(json!({ "on": { "public_key": hex64('d') } }));
   1267     db.nostr_profile_delete(&nostr_profile_delete)
   1268         .expect("nostr profile delete");
   1269     let nostr_profile_delete_missing: INostrProfileDelete =
   1270         parse_json(json!({ "on": { "id": nostr_profile_created.id } }));
   1271     assert_not_found(db.nostr_profile_delete(&nostr_profile_delete_missing));
   1272     for payload in [
   1273         json!({ "rel": { "on_relay": { "id": nostr_relay_created.id } } }),
   1274         json!({ "rel": { "off_relay": { "id": nostr_relay_created.id } } }),
   1275     ] {
   1276         let opts: INostrProfileDelete = parse_json(payload);
   1277         let _ = db.nostr_profile_delete(&opts);
   1278     }
   1279 
   1280     let nostr_relay_delete: INostrRelayDelete =
   1281         parse_json(json!({ "on": { "url": "wss://relay.example.com" } }));
   1282     db.nostr_relay_delete(&nostr_relay_delete)
   1283         .expect("nostr relay delete");
   1284     let nostr_relay_delete_missing: INostrRelayDelete =
   1285         parse_json(json!({ "on": { "id": nostr_relay_created.id } }));
   1286     assert_not_found(db.nostr_relay_delete(&nostr_relay_delete_missing));
   1287     for payload in [
   1288         json!({ "rel": { "on_profile": { "public_key": hex64('d') } } }),
   1289         json!({ "rel": { "off_profile": { "public_key": hex64('d') } } }),
   1290     ] {
   1291         let opts: INostrRelayDelete = parse_json(payload);
   1292         let _ = db.nostr_relay_delete(&opts);
   1293     }
   1294 
   1295     let nostr_event_head_delete: INostrEventHeadDelete =
   1296         parse_json(json!({ "on": { "key": "state-a" } }));
   1297     db.nostr_event_head_delete(&nostr_event_head_delete)
   1298         .expect("nostr event state delete");
   1299     let nostr_event_head_delete_missing: INostrEventHeadDelete =
   1300         parse_json(json!({ "on": { "id": nostr_event_head_created.id } }));
   1301     assert_not_found(db.nostr_event_head_delete(&nostr_event_head_delete_missing));
   1302 
   1303     let log_error_delete: ILogErrorDelete =
   1304         parse_json(json!({ "on": { "nostr_pubkey": hex64('c') } }));
   1305     db.log_error_delete(&log_error_delete)
   1306         .expect("log error delete");
   1307     let log_error_delete_missing: ILogErrorDelete =
   1308         parse_json(json!({ "on": { "id": log_error_created.id } }));
   1309     assert_not_found(db.log_error_delete(&log_error_delete_missing));
   1310 
   1311     let farm_member_claim_delete: IFarmMemberClaimDelete =
   1312         parse_json(json!({ "on": { "member_pubkey": hex64('b') } }));
   1313     db.farm_member_claim_delete(&farm_member_claim_delete)
   1314         .expect("farm member claim delete");
   1315     let farm_member_claim_delete_missing: IFarmMemberClaimDelete =
   1316         parse_json(json!({ "on": { "id": farm_member_claim_created.id } }));
   1317     assert_not_found(db.farm_member_claim_delete(&farm_member_claim_delete_missing));
   1318 
   1319     let farm_member_delete: IFarmMemberDelete =
   1320         parse_json(json!({ "on": { "member_pubkey": hex64('b') } }));
   1321     db.farm_member_delete(&farm_member_delete)
   1322         .expect("farm member delete");
   1323     let farm_member_delete_missing: IFarmMemberDelete =
   1324         parse_json(json!({ "on": { "id": farm_member_created.id } }));
   1325     assert_not_found(db.farm_member_delete(&farm_member_delete_missing));
   1326 
   1327     let plot_tag_delete: IPlotTagDelete = parse_json(json!({ "on": { "tag": "east" } }));
   1328     db.plot_tag_delete(&plot_tag_delete)
   1329         .expect("plot tag delete");
   1330     let plot_tag_delete_missing: IPlotTagDelete =
   1331         parse_json(json!({ "on": { "id": plot_tag_created.id } }));
   1332     assert_not_found(db.plot_tag_delete(&plot_tag_delete_missing));
   1333 
   1334     let farm_tag_delete: IFarmTagDelete = parse_json(json!({ "on": { "tag": "regenerative" } }));
   1335     db.farm_tag_delete(&farm_tag_delete)
   1336         .expect("farm tag delete");
   1337     let farm_tag_delete_missing: IFarmTagDelete =
   1338         parse_json(json!({ "on": { "id": farm_tag_created.id } }));
   1339     assert_not_found(db.farm_tag_delete(&farm_tag_delete_missing));
   1340 
   1341     let plot_delete: IPlotDelete = parse_json(json!({ "on": { "d_tag": "plot-a" } }));
   1342     db.plot_delete(&plot_delete).expect("plot delete");
   1343     let plot_delete_missing: IPlotDelete = parse_json(json!({ "on": { "id": plot_created.id } }));
   1344     assert_not_found(db.plot_delete(&plot_delete_missing));
   1345 
   1346     let farm_delete: IFarmDelete = parse_json(json!({ "on": { "d_tag": "farm-a" } }));
   1347     db.farm_delete(&farm_delete).expect("farm delete");
   1348     let farm_delete_missing: IFarmDelete = parse_json(json!({ "on": { "id": farm_created.id } }));
   1349     assert_not_found(db.farm_delete(&farm_delete_missing));
   1350 }