Add TestDefault trait

This commit is contained in:
fluo10 2025-06-22 11:29:44 +09:00
parent 3a2f53dd46
commit 3356684530
15 changed files with 241 additions and 174 deletions

View file

@ -2,13 +2,15 @@ mod multi_address;
mod peer; mod peer;
pub use multi_address::{ pub use multi_address::{
ActiveModel as MultiAddressActiveModel, ActiveModel as ActiveMultiAddressModel,
Column as MultiAddressColumn,
Model as MultiAddressModel, Model as MultiAddressModel,
Entity as MultiAddressEntity, Entity as MultiAddressEntity,
}; };
pub use peer::{ pub use peer::{
ActiveModel as PeerActiveModel, ActiveModel as ActivePeerModel,
Column as PeerColumn,
Model as PeerModel, Model as PeerModel,
Entity as PeerEntity, Entity as PeerEntity,
}; };

View file

@ -41,14 +41,15 @@ impl ActiveModel {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::global::get_or_init_test_cache_database;
use super::*; use super::*;
use libp2p::identity; use libp2p::identity;
use crate::global::GLOBAL;
#[tokio::test] #[tokio::test]
async fn check_insert_node() { async fn check_insert_node() {
let db = crate::global::get_or_init_temporary_main_database().await; let db = get_or_init_test_cache_database().await;
ActiveModel{ ActiveModel{
peer_id: Set(identity::Keypair::generate_ed25519().public().to_peer_id().to_string()), peer_id: Set(identity::Keypair::generate_ed25519().public().to_peer_id().to_string()),

View file

@ -37,14 +37,15 @@ impl ActiveModel {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::global::get_or_init_test_cache_database;
use super::*; use super::*;
use libp2p::identity; use libp2p::identity;
use crate::global::GLOBAL;
#[tokio::test] #[tokio::test]
async fn check_insert_node() { async fn check_insert_node() {
let db = crate::global::get_or_init_temporary_main_database().await; let db = get_or_init_test_cache_database().await;
ActiveModel{ ActiveModel{
peer_id: Set(identity::Keypair::generate_ed25519().public().to_peer_id().to_string()), peer_id: Set(identity::Keypair::generate_ed25519().public().to_peer_id().to_string()),

View file

@ -2,7 +2,7 @@ use std::path::PathBuf;
#[cfg(feature="desktop")] #[cfg(feature="desktop")]
use clap::Args; use clap::Args;
use crate::config::{ConfigError, PartialConfig}; use crate::{config::{ConfigError, PartialConfig}};
use libp2p::mdns::Config; use libp2p::mdns::Config;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -10,14 +10,7 @@ static DATA_DATABASE_NAME: &str = "data.sqlite";
static CACHE_DATABASE_NAME: &str = "cache.sqlite"; static CACHE_DATABASE_NAME: &str = "cache.sqlite";
#[cfg(any(test, feature="test"))] #[cfg(any(test, feature="test"))]
static TEST_DATA_DATABASE_PATH: std::sync::LazyLock<tempfile::TempPath> = std::sync::LazyLock::new(|| { use crate::tests::{GlobalTestDefault, TestDefault};
let mut temp_path = tempfile::NamedTempFile::new().unwrap().into_temp_path();
temp_path.disable_cleanup(true);
println!("{}", temp_path.as_os_str().to_str().unwrap());
temp_path
});
#[derive(Debug)] #[derive(Debug)]
pub struct StorageConfig { pub struct StorageConfig {
@ -26,11 +19,6 @@ pub struct StorageConfig {
} }
impl StorageConfig { impl StorageConfig {
#[cfg(any(test, feature="test"))]
pub fn new_test() -> Self {
let mut temp_path = tempfile::NamedTempFile::new().unwrap().into_temp_path().keep().unwrap();
Self { data_directory: temp_path.clone(), cache_directory: temp_path }
}
pub fn get_data_database_path(&self) -> PathBuf{ pub fn get_data_database_path(&self) -> PathBuf{
self.data_directory.join(DATA_DATABASE_NAME) self.data_directory.join(DATA_DATABASE_NAME)
} }
@ -39,6 +27,14 @@ impl StorageConfig {
} }
} }
#[cfg(any(test, feature="test"))]
impl TestDefault for StorageConfig {
fn test_default() -> Self {
let temp_path = tempfile::NamedTempFile::new().unwrap().into_temp_path().keep().unwrap();
Self { data_directory: temp_path.clone(), cache_directory: temp_path }
}
}
impl TryFrom<PartialStorageConfig> for StorageConfig { impl TryFrom<PartialStorageConfig> for StorageConfig {
type Error = ConfigError; type Error = ConfigError;

View file

@ -42,14 +42,15 @@ impl ActiveModel {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::global::get_or_init_test_data_database;
use super::*; use super::*;
use libp2p::identity; use libp2p::identity;
use crate::global::GLOBAL;
#[tokio::test] #[tokio::test]
async fn check_insert_node() { async fn check_insert_node() {
let db = crate::global::get_or_init_temporary_main_database().await; let db = get_or_init_test_data_database().await;
ActiveModel{ ActiveModel{
peer_id: Set(identity::Keypair::generate_ed25519().public().to_peer_id().to_string()), peer_id: Set(identity::Keypair::generate_ed25519().public().to_peer_id().to_string()),

View file

@ -35,14 +35,15 @@ impl ActiveModel {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::global::get_or_init_test_data_database;
use super::*; use super::*;
use uuid::{Timestamp, Uuid}; use uuid::{Timestamp, Uuid};
use crate::global::get_or_init_temporary_main_database;
#[tokio::test] #[tokio::test]
async fn check_insert_record_deletion() { async fn check_insert_record_deletion() {
let db = get_or_init_temporary_main_database().await; let db = get_or_init_test_data_database().await;
assert!(ActiveModel{ assert!(ActiveModel{
table_name: Set("test_table".to_string()), table_name: Set("test_table".to_string()),

View file

@ -1,6 +1,15 @@
use crate::{config::StorageConfig, error::Error, global::SimpleGlobal}; use crate::{config::{P2pConfig, StorageConfig}, error::Error, global::GlobalConstant};
use tokio::sync::OnceCell;
use uuid::fmt::Simple;
pub static STORAGE_CONFIG: SimpleGlobal<StorageConfig> = SimpleGlobal::const_new(); pub static STORAGE_CONFIG: GlobalConstant<StorageConfig> = GlobalConstant::const_new(stringify!(STORAGE_CONFIG));
pub static P2P_CONFIG: GlobalConstant<P2pConfig> = GlobalConstant::const_new(stringify!(P2P_CONFIG));
#[cfg(test)]
mod tests {
use crate::global::{config::P2P_CONFIG, STORAGE_CONFIG};
#[test]
fn test_global_constant_names() {
assert_eq!(STORAGE_CONFIG.name, stringify!(STORAGE_CONFIG));
assert_eq!(P2P_CONFIG.name, stringify!(P2P_CONFIG));
}
}

View file

@ -5,35 +5,51 @@ use sea_orm_migration::MigratorTrait;
use crate::error::Error; use crate::error::Error;
use tokio::sync::OnceCell; use tokio::sync::OnceCell;
static DATA_DATABASE_CONNECTION: GlobalDatabaseConnection = GlobalDatabaseConnection::const_new(); pub static DATA_DATABASE_CONNECTION: GlobalDatabaseConnection = GlobalDatabaseConnection::const_new(stringify!(DATA_DATABASE_CONNECTION));
static CACHE_DATABASE_CONNECTION: GlobalDatabaseConnection = GlobalDatabaseConnection::const_new(); pub static CACHE_DATABASE_CONNECTION: GlobalDatabaseConnection = GlobalDatabaseConnection::const_new(stringify!(CACHE_DATABASE_CONNECTION));
struct GlobalDatabaseConnection { pub struct GlobalDatabaseConnection {
name: &'static str,
inner: OnceCell<DatabaseConnection> inner: OnceCell<DatabaseConnection>
} }
impl GlobalDatabaseConnection { impl GlobalDatabaseConnection {
pub const fn const_new() -> Self { pub const fn const_new(name: &'static str) -> Self {
Self { Self {
name: name,
inner: OnceCell::const_new() inner: OnceCell::const_new()
} }
} }
pub fn get(&'static self) -> Option<&'static DatabaseConnection> { pub fn get(&'static self) -> &'static DatabaseConnection {
self.inner.get() self.inner.get().expect(&format!("{} is uninitialized!", self.name))
} }
pub fn get_and_unwrap(&'static self) -> &'static DatabaseConnection { pub async fn get_or_init<T, U>(&'static self, path: T, _: U) -> &'static DatabaseConnection
self.get().expect(&format!("{} is uninitialized!", &stringify!(self)))
}
pub async fn get_or_try_init<T, U>(&'static self, path: T, _: U) -> Result<&'static DatabaseConnection, Error>
where where
T: AsRef<Path>, T: AsRef<Path>,
U: MigratorTrait U: MigratorTrait
{ {
let url = "sqlite://".to_string() + path.as_ref().to_str().unwrap() + "?mode=rwc"; let url = "sqlite://".to_string() + path.as_ref().to_str().unwrap() + "?mode=rwc";
Ok(self.inner.get_or_try_init(|| async { self.inner.get_or_try_init(|| async {
let db = Database::connect(&url).await?; let db = Database::connect(&url).await?;
U::up(&db, None).await?; U::up(&db, None).await?;
Ok::<DatabaseConnection, DbErr>(db) Ok::<DatabaseConnection, DbErr>(db)
}).await?) }).await.expect(&format!("Fail to initialize {}!", self.name))
} }
} }
#[cfg(test)]
pub use tests::*;
#[cfg(test)]
mod tests {
use super::*;
use crate::{cache::migration::CacheMigrator, data::migration::DataMigrator, global::STORAGE_CONFIG, tests::GlobalTestDefault};
pub async fn get_or_init_test_data_database() -> &'static DatabaseConnection{
DATA_DATABASE_CONNECTION.get_or_init(STORAGE_CONFIG.get_or_init_test_default().await.get_data_database_path(), DataMigrator).await
}
pub async fn get_or_init_test_cache_database() -> &'static DatabaseConnection{
CACHE_DATABASE_CONNECTION.get_or_init(STORAGE_CONFIG.get_or_init_test_default().await.get_cache_database_path(), CacheMigrator).await
}
}

View file

@ -1,14 +1,16 @@
use std::{any::type_name, collections::HashMap, net::{IpAddr, Ipv4Addr}, path::{Path, PathBuf}, sync::LazyLock}; use std::{any::type_name, collections::HashMap, net::{IpAddr, Ipv4Addr}, path::{Path, PathBuf}, sync::LazyLock};
use crate::{config::{P2pConfig, PartialP2pConfig, StorageConfig}, error::Error}; use crate::{config::{P2pConfig, PartialP2pConfig, StorageConfig}, error::Error };
#[cfg(any(test, feature="test"))]
use crate::tests::{GlobalTestDefault, TestDefault};
use futures::StreamExt; use futures::StreamExt;
use libp2p::{swarm::SwarmEvent, Multiaddr, PeerId}; use libp2p::{swarm::SwarmEvent, Multiaddr, PeerId};
use sea_orm::{prelude::*, Database}; use sea_orm::{prelude::*, Database};
use sea_orm_migration::MigratorTrait; use sea_orm_migration::MigratorTrait;
use tokio::sync::{OnceCell, RwLock}; use tokio::sync::{OnceCell, RwLock, RwLockReadGuard, RwLockWriteGuard};
mod peers; mod peers;
pub use peers::GlobalPeers; pub use peers::PEERS;
mod config; mod config;
pub use config::STORAGE_CONFIG; pub use config::STORAGE_CONFIG;
mod database_connection; mod database_connection;
@ -37,13 +39,17 @@ fn uninitialized_message<T>(var: T) -> String {
format!("{} is uninitialized!", &stringify!(var)) format!("{} is uninitialized!", &stringify!(var))
} }
struct SimpleGlobal<T> { pub struct GlobalConstant<T> {
pub name: &'static str,
inner: OnceCell<T> inner: OnceCell<T>
} }
impl<T> SimpleGlobal<T> { impl<T> GlobalConstant<T> {
pub const fn const_new() -> Self { pub const fn const_new(name: &'static str ) -> Self {
Self{inner: OnceCell::const_new()} Self{
name: name,
inner: OnceCell::const_new()
}
} }
pub async fn get_or_init(&'static self, source: T) -> &'static T { pub async fn get_or_init(&'static self, source: T) -> &'static T {
self.inner.get_or_init(|| async { self.inner.get_or_init(|| async {
@ -58,46 +64,40 @@ impl<T> SimpleGlobal<T> {
} }
} }
#[cfg(any(test, feature="test"))]
impl<T> GlobalTestDefault<T> for GlobalConstant<T>
where
T: TestDefault + 'static
{
async fn get_or_init_test_default(&'static self) -> &'static T {
self.get_or_init(T::test_default()).await
}
}
struct GlobalRwLock<T> { struct GlobalRwLock<T> {
pub name: &'static str,
inner: OnceCell<RwLock<T>> inner: OnceCell<RwLock<T>>
} }
impl<T> GlobalRwLock<T> { impl<T> GlobalRwLock<T> {
pub const fn const_new() -> Self { pub const fn const_new(name: &'static str) -> Self {
Self{inner: OnceCell::const_new()} Self{
name: name,
inner: OnceCell::const_new()
}
} }
async fn write(&'static self) -> tokio::sync::RwLockWriteGuard<'_ ,T> { pub fn get(&'static self) -> &'static RwLock<T> {
self.get_peers_once_cell().get().expect(UNINITIALIZED_MESSAGE).write().await self.inner.get().expect(&format!("{} is uninitialized", self.name))
} }
async fn read(&'static self) -> RwLockReadGuard<'_, T> { pub async fn write(&'static self) -> RwLockWriteGuard<'_ ,T> {
self.get_peers_once_cell().get().expect(UNINITIALIZED_MESSAGE).read().await self.get().write().await
}
pub async fn read(&'static self) -> RwLockReadGuard<'_, T> {
self.get().read().await
} }
}
#[cfg(test)]
pub struct TestGlobal {
pub storage_config: &'static StorageConfig,
pub data_database_connection: &'static DatabaseConnection,
pub cache_database_connection: &'static DatabaseConnection,
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{cache::migration::CacheMigrator, data::migration::DataMigrator};
use super::*;
static TEST_DATA_DIRECTORY: LazyLock<PathBuf> = todo!();
static TEST_DATA_DATABASE_PATH: LazyLock<PathBuf> = todo!();
static TEST_CACHE_DIRECTORY: LazyLock<PathBuf> = todo!();
static TEST_CACHE_DATABASE_PATH: LazyLock<PathBuf> = todo!();
static TEST_STORAGE_CONFIG: LazyLock<StorageConfig> = todo!();
pub async fn get_or_try_init_test() -> TestGlobal {
TestGlobal {
storage_config: get_or_init_storage_config(StorageConfig{data_directory: TEST_DATA_DIRECTORY.clone(), cache_directory: TEST_CACHE_DIRECTORY.clone()}).await,
data_database_connection: get_or_try_init_data_database_connection(&*TEST_DATA_DATABASE_PATH, DataMigrator ).await.unwrap(),
cache_database_connection: get_or_try_init_cache_database_connection(&*TEST_CACHE_DATABASE_PATH, CacheMigrator).await.unwrap(),
}
}
} }

View file

@ -5,14 +5,7 @@ use tokio::sync::{OnceCell, RwLock, RwLockReadGuard};
use crate::cache::entity::PeerModel; use crate::cache::entity::PeerModel;
static UNINITIALIZED_MESSAGE: &str = "Global peer set uninitialized!"; use super::GlobalRwLock;
pub trait GlobalPeers {
fn get_peers_once_cell(&'static self) -> &OnceCell<RwLock<HashSet<PeerModel>>>; pub static PEERS: GlobalRwLock<HashSet<PeerModel>> = GlobalRwLock::const_new(stringify!(PEERS));
async fn write_peers(&'static self) -> tokio::sync::RwLockWriteGuard<'_ ,HashSet<PeerModel>> {
self.get_peers_once_cell().get().expect(UNINITIALIZED_MESSAGE).write().await
}
async fn read_peers(&'static self) -> RwLockReadGuard<'_, HashSet<PeerModel>> {
self.get_peers_once_cell().get().expect(UNINITIALIZED_MESSAGE).read().await
}
}

View file

@ -1,6 +1,7 @@
use libp2p::{ identity::Keypair, mdns, ping, swarm}; use libp2p::{ identity::Keypair, mdns, ping, swarm};
use sea_orm::{ActiveModelTrait, ActiveValue::Set, ColumnTrait, EntityTrait, QueryFilter};
use crate::{error::Error, global::GlobalPeers}; use crate::{cache::entity::{ActivePeerModel, PeerColumn, PeerEntity}, error::Error, global::{CACHE_DATABASE_CONNECTION, PEERS}};
#[derive(swarm::NetworkBehaviour)] #[derive(swarm::NetworkBehaviour)]
#[behaviour(to_swarm = "Event")] #[behaviour(to_swarm = "Event")]
@ -29,20 +30,23 @@ pub enum Event {
} }
impl Event { impl Event {
pub async fn run<T>(self, global: &T) pub async fn run(&self)
where
T: GlobalPeers
{ {
match self { match self {
Self::Mdns(x) => { Self::Mdns(x) => {
match x { match x {
mdns::Event::Discovered(e) => { mdns::Event::Discovered(e) => {
for peer in e { for peer in e.iter() {
global.write_peers().await; match PeerEntity::find().filter(PeerColumn::PeerId.contains(&peer.0.to_string())).one(CACHE_DATABASE_CONNECTION.get()).await {
peers.insert(peer.0, peer.1); Ok(_) => {}
Err(_) => {
ActivePeerModel{
peer_id: Set(peer.0.to_string()),
..ActivePeerModel::new()
}.insert(CACHE_DATABASE_CONNECTION.get()).await;
}
}
} }
let peers = global.read_peers().await;
println!("Peers: {peers:?}");
}, },
_ => {}, _ => {},
} }

View file

@ -20,6 +20,14 @@ pub static TEST_DATABASE_PATH: std::sync::LazyLock<PathBuf> = std::sync::LazyLoc
TEST_DIR_PATH.join("lazy-supplements.sqlite") TEST_DIR_PATH.join("lazy-supplements.sqlite")
}); });
pub trait TestDefault {
fn test_default() -> Self;
}
pub trait GlobalTestDefault<T: 'static> {
async fn get_or_init_test_default(&'static self) -> &'static T;
}
pub fn test_cbor_serialize_deserialize<T>(src: T) pub fn test_cbor_serialize_deserialize<T>(src: T)
where T: DeserializeOwned + Serialize + PartialEq + std::fmt::Debug where T: DeserializeOwned + Serialize + PartialEq + std::fmt::Debug
{ {

View file

@ -1,77 +0,0 @@
use std::path::PathBuf;
use clap::Args;
use lazy_supplements_core::config::{ConfigError, PartialConfig};
use libp2p::mdns::Config;
use serde::{Deserialize, Serialize};
pub struct DesktopConfig {
pub data_directory: PathBuf,
pub data_database: PathBuf,
pub cache_directory: PathBuf,
pub cache_database: PathBuf,
}
impl TryFrom<PartialDesktopConfig> for DesktopConfig {
type Error = ConfigError;
fn try_from(value: PartialDesktopConfig) -> Result<Self, Self::Error> {
Ok(Self {
data_directory: value.data_directory.ok_or(ConfigError::MissingConfig("data_directory".to_string()))?,
data_database: value.data_database.ok_or(ConfigError::MissingConfig("data_database".to_string()))?,
cache_directory: value.cache_directory.ok_or(ConfigError::MissingConfig("cache_directory".to_string()))?,
cache_database: value.cache_database.ok_or(ConfigError::MissingConfig("cache_database".to_string()))?,
})
}
}
#[derive(Args, Clone, Debug, Deserialize, Serialize)]
pub struct PartialDesktopConfig {
#[arg(long)]
pub data_directory: Option<PathBuf>,
#[arg(long)]
pub data_database: Option<PathBuf>,
#[arg(long)]
pub cache_directory: Option<PathBuf>,
#[arg(long)]
pub cache_database: Option<PathBuf>,
}
impl From<DesktopConfig> for PartialDesktopConfig {
fn from(config: DesktopConfig) -> PartialDesktopConfig {
Self {
data_database: Some(config.data_database),
data_directory: Some(config.data_directory),
cache_database: Some(config.cache_database),
cache_directory: Some(config.cache_directory),
}
}
}
impl PartialConfig<DesktopConfig> for PartialDesktopConfig {
fn empty() -> Self {
Self{
data_database: None,
cache_database: None,
data_directory: None,
cache_directory: None,
}
}
fn default() -> Self {
todo!()
}
fn merge(&mut self, other: Self) {
if let Some(x) = other.data_directory {
self.data_directory = Some(x);
}
if let Some(x) = other.data_database {
self.data_database = Some(x);
}
if let Some(x) = other.cache_directory {
self.cache_directory = Some(x);
}
if let Some(x) = other.cache_database {
self.cache_database = Some(x);
}
}
}

View file

@ -33,7 +33,7 @@ impl From<UnixConfig> for PartialUnixConfig {
} }
} }
impl PartialConfig<UnixConfig> for PartialUnixConfig { impl PartialConfig for PartialUnixConfig {
fn empty() -> Self { fn empty() -> Self {
Self { socket_path: None } Self { socket_path: None }
} }

View file

@ -35,4 +35,116 @@ pub static DEFAULT_PARTIAL_CORE_CONFIG: LazyLock<PartialCoreConfig> = LazyLock::
listen_ips: Some(DEFAULT_LISTEN_IPS.to_vec()), listen_ips: Some(DEFAULT_LISTEN_IPS.to_vec()),
port: Some(0), port: Some(0),
} }
}); });
pub struct Global {
pub p2p_config: OnceCell<P2pConfig>,
pub main_database: OnceCell<DatabaseConnection>,
pub cache_database: OnceCell<DatabaseConnection>,
pub peers: OnceCell<RwLock<HashMap<PeerId, Multiaddr>>>,
}
impl Global {
pub fn get_p2p_config(&self) -> Option<&P2pConfig> {
self.p2p_config.get()
}
pub async fn get_or_init_p2p_config(&self, config: P2pConfig) -> &P2pConfig {
self.p2p_config.get_or_init(|| async {config}).await
}
pub async fn get_or_init_peers(&self) -> &RwLock<HashMap<PeerId, Multiaddr>> {
self.peers.get_or_init(|| async {
RwLock::new(HashMap::new())
}).await
}
pub async fn read_peers(&self) -> tokio::sync::RwLockReadGuard<'_, HashMap<PeerId, Multiaddr>>{
self.get_or_init_peers().await.read().await
}
pub async fn write_peers(&self) -> tokio::sync::RwLockWriteGuard<'_, HashMap<PeerId, Multiaddr>>{
self.get_or_init_peers().await.write().await
}
pub async fn launch_swarm(&self) -> Result<(), Error> {
let mut swarm = self.get_p2p_config().unwrap().clone().try_into_swarm().await?;
loop{
let swarm_event = swarm.select_next_some().await;
tokio::spawn(async move{
match swarm_event {
SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {address:?}"),
SwarmEvent::Behaviour(event) => {
println!("{event:?}");
event.run().await;
},
_ => {}
}
});
}
}
}
impl GlobalDatabase for Global {
fn get_main_database(&self) -> Option<&DatabaseConnection> {
self.main_database.get()
}
async fn get_or_try_init_main_database<T, U>(&self, path: T, _: U) -> Result<&DatabaseConnection, Error>
where
T: AsRef<Path>,
U: MigratorTrait,
{
let url = "sqlite://".to_string() + path.as_ref().to_str().unwrap() + "?mode=rwc";
Ok(self.main_database.get_or_try_init(|| async {
let db = Database::connect(&url).await?;
U::up(&db, None).await?;
Ok::<DatabaseConnection, DbErr>(db)
}).await?)
}
fn get_cache_database(&self) -> Option<&DatabaseConnection> {
self.cache_database.get()
}
async fn get_or_try_init_cache_database<T, U>(&self, path: T, _: U) -> Result<&DatabaseConnection, Error>
where
T: AsRef<Path>,
U: MigratorTrait,
{
let url = "sqlite://".to_string() + path.as_ref().to_str().unwrap() + "?mode=rwc";
Ok(self.cache_database.get_or_try_init(|| async {
let db = Database::connect(&url).await?;
U::up(&db, None).await?;
Ok::<DatabaseConnection, DbErr>(db)
}).await?)
}
}
#[cfg(test)]
pub use tests::{get_or_init_temporary_main_database, get_or_init_temporary_cache_database};
#[cfg(test)]
pub mod tests {
use std::sync::LazyLock;
use sea_orm_migration::MigratorTrait;
use crate::{global::GLOBAL, cache::migration::CacheMigrator, data::migration::MainMigrator};
use super::*;
pub async fn get_or_init_temporary_main_database() -> &'static DatabaseConnection {
GLOBAL.get_or_try_init_temporary_main_database(MainMigrator).await.unwrap()
}
pub async fn get_or_init_temporary_cache_database() -> &'static DatabaseConnection {
GLOBAL.get_or_try_init_temporary_cache_database(CacheMigrator).await.unwrap()
}
#[tokio::test]
async fn connect_main_database () {
let db = get_or_init_temporary_main_database().await;
assert!(db.ping().await.is_ok());
}
#[tokio::test]
async fn connect_cache_database () {
let db = get_or_init_temporary_cache_database().await;
assert!(db.ping().await.is_ok());
}
}