use std::str::FromStr; use std::sync::Arc; use std::sync::atomic::{AtomicI64, Ordering}; use std::time::Duration; use tokio::select; use shiny_application::{Application, CancellationSignalFactory, CreateInfrastructureConfigurationError, ConfigurationFactory, Module}; use shiny_application::controllers::Controllers; use shiny_common::pointer_utils::ToArc; use shiny_common::context::Context; use shiny_common::error::ServiceError; use shiny_configuration::Configuration; use shiny_configuration::configuration_provider::json5_provider::Json5Provider; use shiny_jobs::Job; #[tokio::test] async fn test_application() { let counter = AtomicI64::new(0).arc(); let mut application = Application::new(counter.clone(), TestModule); application.set_infrastructure_configuration_factory(TestableConfigurationFactory(r#"{ jobs: { hello_world: { schedule: "PT1S", }, } }"#.to_string()).arc()); application.set_cancellation_signal_factory(TimeoutCancellationToken(Duration::from_secs(3)).arc()); application.set_dynamic_configuration_factory(TestableConfigurationFactory( r#"{}"#.to_string() ).arc()); select! { _ = application.run() => { } _ = tokio::time::sleep(Duration::from_secs(5)) => { panic!("application is not finished after 7 seconds") } } let counter = counter.load(Ordering::SeqCst); assert!(counter >= 2 && counter <= 3, "counter = {counter}") } struct TestModule; impl Module for TestModule { type Clients = Arc; fn create(&self, client: Self::Clients) -> Controllers { let mut controllers = Controllers::new(); controllers.jobs().add("hello_world", JobExample(client.clone()).arc()); controllers } } struct JobExample(Arc); #[async_trait::async_trait] impl Job for JobExample { async fn execute(&self, _: &mut Context) -> Result<(), ServiceError> { self.0.fetch_add(1, Ordering::SeqCst); Ok(()) } } struct TimeoutCancellationToken(Duration); #[async_trait::async_trait] impl CancellationSignalFactory for TimeoutCancellationToken { async fn cancellation_signal(&self) -> std::io::Result<()> { tokio::time::sleep(self.0).await; Ok(()) } } struct TestableConfigurationFactory(String); impl ConfigurationFactory for TestableConfigurationFactory { fn configuration(&self) -> Result { let root_provider = Json5Provider::from_str(&self.0).unwrap(); Ok( Configuration::builder(root_provider) .build()? ) } }