use bevy::prelude::*;
use bevy_persistence_database::{
commit_sync, Guid, persistence_plugin::PersistencePlugins, PersistentQuery,
DatabaseConnection, db::connection::DatabaseConnectionResource,
};
use crate::common::*;
use crate::common::CountingDbConnection;
use bevy_persistence_database_derive::db_matrix_test;
use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
fn test_cached_query_system(mut query1: PersistentQuery<&Health>, mut query2: PersistentQuery<&Health>) {
let _ = query1.ensure_loaded();
let _ = query2.ensure_loaded();
}
fn test_force_refresh_system(mut query: PersistentQuery<&Health>) {
query = query.force_refresh();
let _ = query.ensure_loaded();
}
#[db_matrix_test]
fn test_persistent_query_caching() {
let (db, _container) = setup();
let mut app = App::new();
app.add_plugins(PersistencePlugins::new(db.clone()));
app.world_mut().spawn(Health { value: 100 });
app.update();
commit_sync(&mut app, db.clone(), TEST_STORE).expect("Initial commit failed");
let mut app2 = App::new();
app2.add_plugins(PersistencePlugins::new(db.clone()));
let query_count = Arc::new(AtomicUsize::new(0));
let counting = Arc::new(CountingDbConnection::new(db.clone(), query_count.clone())) as Arc<dyn DatabaseConnection>;
app2.insert_resource(DatabaseConnectionResource(counting));
app2.add_systems(bevy::prelude::Update, test_cached_query_system);
app2.update();
assert_eq!(query_count.load(Ordering::SeqCst), 1, "Database should only be queried once due to caching");
app2.add_systems(bevy::prelude::Update, test_force_refresh_system);
app2.update();
assert_eq!(query_count.load(Ordering::SeqCst), 2, "Force refresh should bypass cache and query again");
}
#[derive(bevy::prelude::Resource, Default)]
struct TestState {
entity: Option<bevy::prelude::Entity>,
mutated: bool,
}
fn system_load_and_capture(
mut pq: PersistentQuery<&Health>,
mut state: bevy::prelude::ResMut<TestState>,
) {
pq.ensure_loaded();
for (e, _) in pq.iter() {
if state.entity.is_none() {
state.entity = Some(e);
break;
}
}
}
fn system_mutate_once(
mut q: bevy::prelude::Query<&mut Health>,
mut state: bevy::prelude::ResMut<TestState>,
) {
if state.mutated {
return;
}
if let Some(e) = state.entity {
if let Ok(mut health) = q.get_mut(e) {
health.value = 123;
state.mutated = true;
}
}
}
fn system_second_load(
pq: PersistentQuery<&Health>,
q_guid: bevy::prelude::Query<&Guid>,
state: bevy::prelude::Res<TestState>,
) {
if let Some(e) = state.entity {
if let Ok(g) = q_guid.get(e) {
let _ = pq
.filter(Guid::key_field().eq(g.id()))
.ensure_loaded();
}
}
}
#[db_matrix_test]
fn test_entity_not_overwritten_on_second_query_without_refresh() {
let (db, _container) = setup();
let mut app1 = App::new();
app1.add_plugins(PersistencePlugins::new(db.clone()));
let _e = app1.world_mut().spawn(Health { value: 100 }).id();
app1.update();
commit_sync(&mut app1, db.clone(), TEST_STORE).expect("commit failed");
let mut app2 = App::new();
app2.add_plugins(PersistencePlugins::new(db.clone()));
app2.insert_resource(TestState::default());
app2.add_systems(bevy::prelude::Update, system_load_and_capture);
app2.update();
app2.add_systems(bevy::prelude::Update, system_mutate_once);
app2.update();
app2.add_systems(bevy::prelude::Update, system_second_load);
app2.update();
let state = app2.world().resource::<TestState>();
let entity = state.entity.expect("Entity should be captured");
let health = app2.world().get::<Health>(entity).expect("Health not found");
assert_eq!(health.value, 123, "Local mutation should be preserved");
}
#[derive(bevy::prelude::Resource, Clone)]
struct TestKey(String);
fn force_refresh_system(query: PersistentQuery<&Health>, key: bevy::prelude::Res<TestKey>) {
let _ = query
.filter(Guid::key_field().eq(key.0.as_str()))
.force_refresh()
.ensure_loaded();
}
#[db_matrix_test]
fn test_force_refresh_overwrites() {
let (db, _container) = setup();
let mut app1 = App::new();
app1.add_plugins(PersistencePlugins::new(db.clone()));
let _e = app1.world_mut().spawn(Health { value: 100 }).id();
app1.update();
commit_sync(&mut app1, db.clone(), TEST_STORE).expect("commit failed");
let mut app2 = App::new();
app2.add_plugins(PersistencePlugins::new(db.clone()));
fn load(mut pq: PersistentQuery<&Health, With<Health>>) { let _ = pq.ensure_loaded(); }
app2.add_systems(bevy::prelude::Update, load);
app2.update();
let (e, g) = {
let mut q = app2.world_mut().query::<(bevy::prelude::Entity, &Guid)>();
let first = q.iter(&app2.world()).next().expect("no entity loaded");
(first.0, first.1.id().to_string())
};
app2.world_mut().get_mut::<Health>(e).unwrap().value = 123;
app2.update();
app2.insert_resource(TestKey(g.clone()));
app2.add_systems(bevy::prelude::Update, force_refresh_system);
app2.update();
assert_eq!(app2.world().get::<Health>(e).unwrap().value, 100);
}