use crate::common::*;
use bevy::prelude::*;
use bevy_persistence_database::bevy::components::Guid;
use bevy_persistence_database::bevy::plugins::persistence_plugin::CommitStatus;
use bevy_persistence_database::core::db::{MockDatabaseConnection, PersistenceError};
use bevy_persistence_database::core::persist::Persist;
use bevy_persistence_database::core::session::{PersistenceSession, commit_sync};
use bevy_persistence_database_derive::{db_matrix_test, persist};
use serde_json;
#[persist(component)]
#[derive(Debug, Clone, PartialEq)]
struct RoutedComp {
value: i32,
}
#[db_matrix_test]
fn commit_uses_provided_connection() {
let (db, _container) = setup();
let mut app = setup_test_app(db.clone(), None);
app.world_mut()
.resource_mut::<PersistenceSession>()
.register_component::<RoutedComp>();
let entity = app.world_mut().spawn(RoutedComp { value: 1 }).id();
app.update();
commit_sync(&mut app, db.clone(), TEST_STORE)
.expect("commit should succeed with provided connection");
let guid = app
.world()
.get::<Guid>(entity)
.expect("Guid should be attached after commit");
let stored = run_async(db.fetch_component(TEST_STORE, guid.id(), RoutedComp::name()))
.expect("fetch should succeed")
.expect("component should exist in database");
let fetched: RoutedComp = serde_json::from_value(stored).expect("deserialize RoutedComp");
assert_eq!(fetched.value, 1);
}
#[db_matrix_test]
fn commits_can_switch_connections_between_calls() {
let (db_a, _guard_a) = setup();
let (db_b, _guard_b) = setup();
let mut app = setup_test_app(db_a.clone(), None);
app.world_mut()
.resource_mut::<PersistenceSession>()
.register_component::<RoutedComp>();
let e1 = app.world_mut().spawn(RoutedComp { value: 1 }).id();
app.update();
commit_sync(&mut app, db_a.clone(), TEST_STORE).expect("first commit should succeed on db_a");
let guid1 = app
.world()
.get::<Guid>(e1)
.expect("guid for first entity")
.id()
.to_string();
let stored_a = run_async(db_a.fetch_component(TEST_STORE, &guid1, RoutedComp::name()))
.expect("fetch from db_a should succeed")
.expect("entity should exist in db_a");
let fetched_a: RoutedComp = serde_json::from_value(stored_a).expect("deserialize from db_a");
assert_eq!(fetched_a.value, 1);
let missing_in_b = run_async(db_b.fetch_component(TEST_STORE, &guid1, RoutedComp::name()))
.expect("fetch from db_b should succeed");
assert!(
missing_in_b.is_none(),
"first commit should not write to db_b"
);
let e2 = app.world_mut().spawn(RoutedComp { value: 2 }).id();
app.update();
commit_sync(&mut app, db_b.clone(), TEST_STORE).expect("second commit should succeed on db_b");
let guid2 = app
.world()
.get::<Guid>(e2)
.expect("guid for second entity")
.id()
.to_string();
let stored_b = run_async(db_b.fetch_component(TEST_STORE, &guid2, RoutedComp::name()))
.expect("fetch from db_b should succeed")
.expect("second entity should exist in db_b");
let fetched_b: RoutedComp = serde_json::from_value(stored_b).expect("deserialize from db_b");
assert_eq!(fetched_b.value, 2);
let missing_in_a = run_async(db_a.fetch_component(TEST_STORE, &guid2, RoutedComp::name()))
.expect("fetch from db_a should succeed");
assert!(
missing_in_a.is_none(),
"second commit should not write to db_a"
);
}
#[test]
fn commit_failure_propagates_error_and_resets_status() {
let mut mock_db = MockDatabaseConnection::new();
mock_db.expect_document_key_field().return_const("_key");
mock_db
.expect_execute_transaction()
.times(1)
.returning(|_| Box::pin(async { Err(PersistenceError::new("boom")) }));
let db = std::sync::Arc::new(mock_db);
let mut app = setup_test_app(db.clone(), None);
app.world_mut()
.resource_mut::<PersistenceSession>()
.register_component::<RoutedComp>();
app.world_mut().spawn(RoutedComp { value: 9 });
app.update();
let res = commit_sync(&mut app, db.clone(), TEST_STORE);
assert!(res.is_err(), "commit should surface backend error");
assert_eq!(
*app.world().resource::<CommitStatus>(),
CommitStatus::Idle,
"status should return to Idle after failure"
);
}