update grid4 & heroledger models
This commit is contained in:
117
heromodels/tests/grid4_models.rs
Normal file
117
heromodels/tests/grid4_models.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
use serde_json;
|
||||
|
||||
use heromodels::models::grid4::{
|
||||
ComputeSlice, DeviceInfo, Node, NodeCapacity, PricingPolicy, Reservation, ReservationStatus,
|
||||
SLAPolicy, StorageDevice, StorageSlice,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn build_and_serde_roundtrip_compute_storage_slices() {
|
||||
let pricing = PricingPolicy::new()
|
||||
.marketplace_year_discounts(vec![20, 30, 40])
|
||||
.volume_discounts(vec![5, 10, 15])
|
||||
.build();
|
||||
|
||||
let sla = SLAPolicy::new()
|
||||
.sla_uptime(99)
|
||||
.sla_bandwidth_mbit(1000)
|
||||
.sla_penalty(150)
|
||||
.build();
|
||||
|
||||
let cs = ComputeSlice::new()
|
||||
.nodeid(42)
|
||||
.slice_id(1)
|
||||
.mem_gb(16.0)
|
||||
.storage_gb(200.0)
|
||||
.passmark(5000)
|
||||
.vcores(8)
|
||||
.cpu_oversubscription(2)
|
||||
.storage_oversubscription(1)
|
||||
.price_range(vec![0.5, 2.0])
|
||||
.gpus(1)
|
||||
.price_cc(1.25)
|
||||
.pricing_policy(pricing.clone())
|
||||
.sla_policy(sla.clone());
|
||||
|
||||
let ss = StorageSlice::new()
|
||||
.nodeid(42)
|
||||
.slice_id(2)
|
||||
.price_cc(0.15)
|
||||
.pricing_policy(pricing)
|
||||
.sla_policy(sla);
|
||||
|
||||
// serde roundtrip compute slice
|
||||
let s = serde_json::to_string(&cs).expect("serialize compute slice");
|
||||
let cs2: ComputeSlice = serde_json::from_str(&s).expect("deserialize compute slice");
|
||||
assert_eq!(cs, cs2);
|
||||
|
||||
// serde roundtrip storage slice
|
||||
let s2 = serde_json::to_string(&ss).expect("serialize storage slice");
|
||||
let ss2: StorageSlice = serde_json::from_str(&s2).expect("deserialize storage slice");
|
||||
assert_eq!(ss, ss2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_and_serde_roundtrip_node() {
|
||||
let dev = DeviceInfo {
|
||||
vendor: "AcmeVendor".into(),
|
||||
storage: vec![StorageDevice { id: "sda".into(), size_gb: 512.0, description: "NVMe".into() }],
|
||||
memory: vec![],
|
||||
cpu: vec![],
|
||||
gpu: vec![],
|
||||
network: vec![],
|
||||
};
|
||||
|
||||
let cap = NodeCapacity { storage_gb: 2048.0, mem_gb: 128.0, mem_gb_gpu: 24.0, passmark: 12000, vcores: 32 };
|
||||
|
||||
let cs = ComputeSlice::new().nodeid(1).slice_id(1).mem_gb(8.0).storage_gb(100.0).passmark(2500).vcores(4);
|
||||
let ss = StorageSlice::new().nodeid(1).slice_id(2).price_cc(0.2);
|
||||
|
||||
let node = Node::new()
|
||||
.nodegroupid(7)
|
||||
.uptime(99)
|
||||
.add_compute_slice(cs)
|
||||
.add_storage_slice(ss)
|
||||
.devices(dev)
|
||||
.country("NL")
|
||||
.capacity(cap)
|
||||
.provisiontime(1710000000)
|
||||
.pubkey("node_pubkey")
|
||||
.signature_node("sig_node")
|
||||
.signature_farmer("sig_farmer");
|
||||
|
||||
let s = serde_json::to_string(&node).expect("serialize node");
|
||||
let node2: Node = serde_json::from_str(&s).expect("deserialize node");
|
||||
|
||||
assert_eq!(node.nodegroupid, node2.nodegroupid);
|
||||
assert_eq!(node.uptime, node2.uptime);
|
||||
assert_eq!(node.country, node2.country);
|
||||
assert_eq!(node.pubkey, node2.pubkey);
|
||||
assert_eq!(node.signature_node, node2.signature_node);
|
||||
assert_eq!(node.signature_farmer, node2.signature_farmer);
|
||||
assert_eq!(node.computeslices.len(), node2.computeslices.len());
|
||||
assert_eq!(node.storageslices.len(), node2.storageslices.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_and_serde_roundtrip_reservation() {
|
||||
let reservation = Reservation::new()
|
||||
.customer_id(1234)
|
||||
.add_compute_slice(11)
|
||||
.add_storage_slice(22)
|
||||
.status(ReservationStatus::Confirmed)
|
||||
.obligation(true)
|
||||
.start_date(1_710_000_000)
|
||||
.end_date(1_720_000_000);
|
||||
|
||||
let s = serde_json::to_string(&reservation).expect("serialize reservation");
|
||||
let reservation2: Reservation = serde_json::from_str(&s).expect("deserialize reservation");
|
||||
|
||||
assert_eq!(reservation.customer_id, reservation2.customer_id);
|
||||
assert_eq!(reservation.status, reservation2.status);
|
||||
assert_eq!(reservation.obligation, reservation2.obligation);
|
||||
assert_eq!(reservation.start_date, reservation2.start_date);
|
||||
assert_eq!(reservation.end_date, reservation2.end_date);
|
||||
assert_eq!(reservation.compute_slices, reservation2.compute_slices);
|
||||
assert_eq!(reservation.storage_slices, reservation2.storage_slices);
|
||||
}
|
82
heromodels/tests/grid4_ourdb.rs
Normal file
82
heromodels/tests/grid4_ourdb.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
use heromodels::db::hero::OurDB;
|
||||
use heromodels::db::{Collection, Db};
|
||||
use heromodels::models::grid4::node::node_index::{country, nodegroupid, pubkey};
|
||||
use heromodels::models::grid4::node::{ComputeSlice, DeviceInfo, Node};
|
||||
use heromodels_core::Model;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn create_test_db() -> Arc<OurDB> {
|
||||
let ts = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let path = format!("/tmp/grid4_node_test_{}", ts);
|
||||
let _ = std::fs::remove_dir_all(&path);
|
||||
Arc::new(OurDB::new(path, true).expect("create OurDB"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grid4_node_basic_roundtrip_and_indexes() {
|
||||
let db = create_test_db();
|
||||
let nodes = db.collection::<Node>().expect("open node collection");
|
||||
|
||||
// Clean any leftover
|
||||
if let Ok(existing) = nodes.get_all() {
|
||||
for n in existing {
|
||||
let _ = nodes.delete_by_id(n.get_id());
|
||||
}
|
||||
}
|
||||
|
||||
// Build a node with some compute slices and device info
|
||||
let cs = ComputeSlice::new()
|
||||
.nodeid(1)
|
||||
.slice_id(1)
|
||||
.mem_gb(32.0)
|
||||
.storage_gb(512.0)
|
||||
.passmark(5000)
|
||||
.vcores(16)
|
||||
.gpus(1)
|
||||
.price_cc(0.25);
|
||||
|
||||
let dev = DeviceInfo {
|
||||
vendor: "ACME".into(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let n = Node::new()
|
||||
.nodegroupid(42)
|
||||
.uptime(99)
|
||||
.add_compute_slice(cs)
|
||||
.devices(dev)
|
||||
.country("BE")
|
||||
.pubkey("PUB_NODE_1")
|
||||
.build();
|
||||
|
||||
let (id, stored) = nodes.set(&n).expect("store node");
|
||||
assert!(id > 0);
|
||||
assert_eq!(stored.country, "BE");
|
||||
|
||||
// get by id
|
||||
let fetched = nodes.get_by_id(id).expect("get by id").expect("exists");
|
||||
assert_eq!(fetched.pubkey, "PUB_NODE_1");
|
||||
|
||||
// query by top-level indexes
|
||||
let by_country = nodes.get::<country, _>("BE").expect("query country");
|
||||
assert_eq!(by_country.len(), 1);
|
||||
assert_eq!(by_country[0].get_id(), id);
|
||||
|
||||
let by_group = nodes.get::<nodegroupid, _>(&42).expect("query group");
|
||||
assert_eq!(by_group.len(), 1);
|
||||
|
||||
let by_pubkey = nodes.get::<pubkey, _>("PUB_NODE_1").expect("query pubkey");
|
||||
assert_eq!(by_pubkey.len(), 1);
|
||||
|
||||
// update
|
||||
let updated = fetched.clone().country("NL");
|
||||
let (_, back) = nodes.set(&updated).expect("update node");
|
||||
assert_eq!(back.country, "NL");
|
||||
|
||||
// delete
|
||||
nodes.delete_by_id(id).expect("delete");
|
||||
assert!(nodes.get_by_id(id).expect("get after delete").is_none());
|
||||
}
|
125
heromodels/tests/grid4_postgres.rs
Normal file
125
heromodels/tests/grid4_postgres.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
use heromodels::db::postgres::{Config, Postgres};
|
||||
use heromodels::db::{Collection, Db};
|
||||
use heromodels::models::grid4::node::node_index::{country, nodegroupid, pubkey};
|
||||
use heromodels::models::grid4::node::{ComputeSlice, DeviceInfo, Node};
|
||||
use heromodels_core::Model;
|
||||
|
||||
// Requires local Postgres (user=postgres password=test123 host=localhost port=5432)
|
||||
// Run with: cargo test -p heromodels --test grid4_postgres -- --ignored
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn grid4_node_postgres_roundtrip_like_example() {
|
||||
let db = Postgres::new(
|
||||
Config::new()
|
||||
.user(Some("postgres".into()))
|
||||
.password(Some("test123".into()))
|
||||
.host(Some("localhost".into()))
|
||||
.port(Some(5432)),
|
||||
)
|
||||
.expect("can connect to Postgres");
|
||||
|
||||
let nodes = db.collection::<Node>().expect("open node collection");
|
||||
|
||||
// Clean existing
|
||||
if let Ok(existing) = nodes.get_all() {
|
||||
for n in existing {
|
||||
let _ = nodes.delete_by_id(n.get_id());
|
||||
}
|
||||
}
|
||||
|
||||
// Build and store multiple nodes via builder and then persist via collection.set(), like examples
|
||||
let cs1 = ComputeSlice::new()
|
||||
.nodeid(10)
|
||||
.slice_id(1)
|
||||
.mem_gb(32.0)
|
||||
.storage_gb(512.0)
|
||||
.passmark(5000)
|
||||
.vcores(16)
|
||||
.gpus(1)
|
||||
.price_cc(0.25);
|
||||
let cs2 = ComputeSlice::new()
|
||||
.nodeid(10)
|
||||
.slice_id(2)
|
||||
.mem_gb(64.0)
|
||||
.storage_gb(2048.0)
|
||||
.passmark(7000)
|
||||
.vcores(24)
|
||||
.gpus(2)
|
||||
.price_cc(0.50);
|
||||
let cs3 = ComputeSlice::new()
|
||||
.nodeid(11)
|
||||
.slice_id(1)
|
||||
.mem_gb(16.0)
|
||||
.storage_gb(256.0)
|
||||
.passmark(3000)
|
||||
.vcores(8)
|
||||
.gpus(0)
|
||||
.price_cc(0.10);
|
||||
|
||||
let dev = DeviceInfo { vendor: "ACME".into(), ..Default::default() };
|
||||
|
||||
let n1 = Node::new()
|
||||
.nodegroupid(99)
|
||||
.uptime(97)
|
||||
.add_compute_slice(cs1)
|
||||
.devices(dev.clone())
|
||||
.country("BE")
|
||||
.pubkey("PG_NODE_1")
|
||||
.build();
|
||||
let n2 = Node::new()
|
||||
.nodegroupid(99)
|
||||
.uptime(96)
|
||||
.add_compute_slice(cs2)
|
||||
.devices(dev.clone())
|
||||
.country("NL")
|
||||
.pubkey("PG_NODE_2")
|
||||
.build();
|
||||
let n3 = Node::new()
|
||||
.nodegroupid(7)
|
||||
.uptime(95)
|
||||
.add_compute_slice(cs3)
|
||||
.devices(dev)
|
||||
.country("BE")
|
||||
.pubkey("PG_NODE_3")
|
||||
.build();
|
||||
|
||||
let (id1, s1) = nodes.set(&n1).expect("store n1");
|
||||
let (id2, s2) = nodes.set(&n2).expect("store n2");
|
||||
let (id3, s3) = nodes.set(&n3).expect("store n3");
|
||||
assert!(id1 > 0 && id2 > 0 && id3 > 0);
|
||||
|
||||
// Query by top-level indexes similar to the example style
|
||||
let be_nodes = nodes.get::<country, _>("BE").expect("by country");
|
||||
assert_eq!(be_nodes.len(), 2);
|
||||
|
||||
let grp_99 = nodes.get::<nodegroupid, _>(&99).expect("by group");
|
||||
assert_eq!(grp_99.len(), 2);
|
||||
|
||||
let by_key = nodes.get::<pubkey, _>("PG_NODE_2").expect("by pubkey");
|
||||
assert_eq!(by_key.len(), 1);
|
||||
assert_eq!(by_key[0].get_id(), id2);
|
||||
|
||||
// Update: change country of n1
|
||||
let updated = s1.clone().country("DE");
|
||||
let (_, back) = nodes.set(&updated).expect("update n1");
|
||||
assert_eq!(back.country, "DE");
|
||||
|
||||
// Cardinality after update
|
||||
let de_nodes = nodes.get::<country, _>("DE").expect("by country DE");
|
||||
assert_eq!(de_nodes.len(), 1);
|
||||
|
||||
// Delete by id and by index
|
||||
nodes.delete_by_id(id2).expect("delete n2 by id");
|
||||
assert!(nodes.get_by_id(id2).unwrap().is_none());
|
||||
|
||||
nodes.delete::<pubkey, _>("PG_NODE_3").expect("delete n3 by pubkey");
|
||||
assert!(nodes.get_by_id(id3).unwrap().is_none());
|
||||
|
||||
// Remaining should be updated n1 only; verify via targeted queries
|
||||
let de_nodes = nodes.get::<country, _>("DE").expect("country DE after deletes");
|
||||
assert_eq!(de_nodes.len(), 1);
|
||||
assert_eq!(de_nodes[0].get_id(), id1);
|
||||
let by_key = nodes.get::<pubkey, _>("PG_NODE_1").expect("by pubkey PG_NODE_1");
|
||||
assert_eq!(by_key.len(), 1);
|
||||
assert_eq!(by_key[0].get_id(), id1);
|
||||
}
|
97
heromodels/tests/heroledger_postgres.rs
Normal file
97
heromodels/tests/heroledger_postgres.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
use heromodels::db::postgres::{Config, Postgres};
|
||||
use heromodels::db::{Collection, Db};
|
||||
use heromodels::models::heroledger::user::user_index::username;
|
||||
use heromodels::models::heroledger::user::User;
|
||||
use heromodels_core::Model;
|
||||
|
||||
// NOTE: Requires a local Postgres running with user=postgres password=test123 host=localhost port=5432
|
||||
// Marked ignored by default. Run with: cargo test -p heromodels --test heroledger_postgres -- --ignored
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn heroledger_user_postgres_roundtrip() {
|
||||
// Connect
|
||||
let db = Postgres::new(
|
||||
Config::new()
|
||||
.user(Some("postgres".into()))
|
||||
.password(Some("test123".into()))
|
||||
.host(Some("localhost".into()))
|
||||
.port(Some(5432)),
|
||||
)
|
||||
.expect("can connect to Postgres");
|
||||
|
||||
// Open collection (will create table and indexes for top-level fields)
|
||||
let users = db.collection::<User>().expect("can open user collection");
|
||||
|
||||
// Clean slate
|
||||
if let Ok(existing) = users.get_all() {
|
||||
for u in existing {
|
||||
let _ = users.delete_by_id(u.get_id());
|
||||
}
|
||||
}
|
||||
|
||||
// Unique suffix to avoid collisions with any pre-existing rows
|
||||
let uniq = format!("{}", std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos());
|
||||
let alice = format!("alice_{}", uniq);
|
||||
let bob = format!("bob_{}", uniq);
|
||||
let carol = format!("carol_{}", uniq);
|
||||
|
||||
// Build and store multiple users
|
||||
let u1 = User::new(0)
|
||||
.username(&alice)
|
||||
.pubkey("PUBKEY_A")
|
||||
.add_email("alice@example.com")
|
||||
.build();
|
||||
let u2 = User::new(0)
|
||||
.username(&bob)
|
||||
.pubkey("PUBKEY_B")
|
||||
.add_email("bob@example.com")
|
||||
.build();
|
||||
let u3 = User::new(0)
|
||||
.username(&carol)
|
||||
.pubkey("PUBKEY_C")
|
||||
.add_email("carol@example.com")
|
||||
.build();
|
||||
|
||||
let (id1, db_u1) = users.set(&u1).expect("store u1");
|
||||
let (id2, db_u2) = users.set(&u2).expect("store u2");
|
||||
let (id3, db_u3) = users.set(&u3).expect("store u3");
|
||||
assert!(id1 > 0 && id2 > 0 && id3 > 0);
|
||||
|
||||
// Fetch by id
|
||||
assert_eq!(users.get_by_id(id1).unwrap().unwrap().username, alice);
|
||||
assert_eq!(users.get_by_id(id2).unwrap().unwrap().username, bob);
|
||||
assert_eq!(users.get_by_id(id3).unwrap().unwrap().username, carol);
|
||||
|
||||
// Fetch by index (top-level username)
|
||||
let by_username = users.get::<username, _>(&alice).expect("by username");
|
||||
assert_eq!(by_username.len(), 1);
|
||||
assert_eq!(by_username[0].get_id(), id1);
|
||||
|
||||
// Update one
|
||||
let updated = db_u1.clone().add_email("work@alice.example");
|
||||
let (id1b, updated_back) = users.set(&updated).expect("update alice");
|
||||
assert_eq!(id1b, id1);
|
||||
assert!(updated_back.email.len() >= 2);
|
||||
|
||||
// Targeted queries to avoid legacy rows in the same table
|
||||
// Verify three users exist via index queries
|
||||
assert_eq!(users.get::<username, _>(&alice).unwrap().len(), 1);
|
||||
assert_eq!(users.get::<username, _>(&bob).unwrap().len(), 1);
|
||||
assert_eq!(users.get::<username, _>(&carol).unwrap().len(), 1);
|
||||
|
||||
// Delete by id
|
||||
users.delete_by_id(id2).expect("delete bob by id");
|
||||
assert!(users.get_by_id(id2).unwrap().is_none());
|
||||
|
||||
// Delete by index (username)
|
||||
users.delete::<username, _>(&carol).expect("delete carol by username");
|
||||
assert!(users.get_by_id(id3).unwrap().is_none());
|
||||
|
||||
// Remaining should be just alice; verify via index
|
||||
let remain = users.get::<username, _>(&alice).expect("get alice after delete");
|
||||
assert_eq!(remain.len(), 1);
|
||||
assert_eq!(remain[0].get_id(), id1);
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
use heromodels::db::Collection;
|
||||
use heromodels::db::Db;
|
||||
use heromodels::db::hero::OurDB;
|
||||
use heromodels::models::biz::{BusinessType, Company, CompanyStatus, Payment, PaymentStatus};
|
||||
use heromodels_core::Model;
|
||||
@@ -197,12 +198,18 @@ fn test_payment_database_persistence() {
|
||||
);
|
||||
|
||||
// Save payment
|
||||
let (payment_id, saved_payment) = db.set(&payment).expect("Failed to save payment");
|
||||
let (payment_id, saved_payment) = db
|
||||
.collection::<Payment>()
|
||||
.expect("open payment collection")
|
||||
.set(&payment)
|
||||
.expect("Failed to save payment");
|
||||
assert!(payment_id > 0);
|
||||
assert_eq!(saved_payment.payment_intent_id, "pi_db_test");
|
||||
|
||||
// Retrieve payment
|
||||
let retrieved_payment: Payment = db
|
||||
.collection::<Payment>()
|
||||
.expect("open payment collection")
|
||||
.get_by_id(payment_id)
|
||||
.expect("Failed to get payment")
|
||||
.unwrap();
|
||||
@@ -224,20 +231,34 @@ fn test_payment_status_transitions() {
|
||||
1360.0,
|
||||
);
|
||||
|
||||
let (payment_id, mut payment) = db.set(&payment).expect("Failed to save payment");
|
||||
let (payment_id, mut payment) = db
|
||||
.collection::<Payment>()
|
||||
.expect("open payment collection")
|
||||
.set(&payment)
|
||||
.expect("Failed to save payment");
|
||||
|
||||
// Test pending -> completed
|
||||
payment = payment.complete_payment(Some("cus_transition_test".to_string()));
|
||||
let (_, mut payment) = db.set(&payment).expect("Failed to update payment");
|
||||
let (_, mut payment) = db
|
||||
.collection::<Payment>()
|
||||
.expect("open payment collection")
|
||||
.set(&payment)
|
||||
.expect("Failed to update payment");
|
||||
assert!(payment.is_completed());
|
||||
|
||||
// Test completed -> refunded
|
||||
payment = payment.refund_payment();
|
||||
let (_, payment) = db.set(&payment).expect("Failed to update payment");
|
||||
let (_, payment) = db
|
||||
.collection::<Payment>()
|
||||
.expect("open payment collection")
|
||||
.set(&payment)
|
||||
.expect("Failed to update payment");
|
||||
assert!(payment.is_refunded());
|
||||
|
||||
// Verify final state in database
|
||||
let final_payment: Payment = db
|
||||
.collection::<Payment>()
|
||||
.expect("open payment collection")
|
||||
.get_by_id(payment_id)
|
||||
.expect("Failed to get payment")
|
||||
.unwrap();
|
||||
@@ -270,15 +291,18 @@ fn test_company_payment_integration() {
|
||||
let db = create_test_db();
|
||||
|
||||
// Create company with default PendingPayment status
|
||||
let company = Company::new(
|
||||
"Integration Test Corp".to_string(),
|
||||
"ITC-001".to_string(),
|
||||
chrono::Utc::now().timestamp(),
|
||||
)
|
||||
.email("test@integration.com".to_string())
|
||||
.business_type(BusinessType::Starter);
|
||||
let company = Company::new()
|
||||
.name("Integration Test Corp")
|
||||
.registration_number("ITC-001")
|
||||
.incorporation_date(chrono::Utc::now().timestamp())
|
||||
.email("test@integration.com")
|
||||
.business_type(BusinessType::Starter);
|
||||
|
||||
let (company_id, company) = db.set(&company).expect("Failed to save company");
|
||||
let (company_id, company) = db
|
||||
.collection::<Company>()
|
||||
.expect("open company collection")
|
||||
.set(&company)
|
||||
.expect("Failed to save company");
|
||||
assert_eq!(company.status, CompanyStatus::PendingPayment);
|
||||
|
||||
// Create payment for the company
|
||||
@@ -291,18 +315,28 @@ fn test_company_payment_integration() {
|
||||
305.0,
|
||||
);
|
||||
|
||||
let (_payment_id, payment) = db.set(&payment).expect("Failed to save payment");
|
||||
let (_payment_id, payment) = db
|
||||
.collection::<Payment>()
|
||||
.expect("open payment collection")
|
||||
.set(&payment)
|
||||
.expect("Failed to save payment");
|
||||
assert_eq!(payment.company_id, company_id);
|
||||
|
||||
// Complete payment
|
||||
let completed_payment = payment.complete_payment(Some("cus_integration_test".to_string()));
|
||||
let (_, completed_payment) = db
|
||||
.collection::<Payment>()
|
||||
.expect("open payment collection")
|
||||
.set(&completed_payment)
|
||||
.expect("Failed to update payment");
|
||||
|
||||
// Update company status to Active
|
||||
let active_company = company.status(CompanyStatus::Active);
|
||||
let (_, active_company) = db.set(&active_company).expect("Failed to update company");
|
||||
let (_, active_company) = db
|
||||
.collection::<Company>()
|
||||
.expect("open company collection")
|
||||
.set(&active_company)
|
||||
.expect("Failed to update company");
|
||||
|
||||
// Verify final states
|
||||
assert!(completed_payment.is_completed());
|
||||
|
Reference in New Issue
Block a user