omry_integration/
full_stack.rs1use crate::flora_stack::{FloraStack, FloraStackOptions};
4use anyhow::Result;
5use std::sync::Arc;
6use std::time::Duration;
7pub use test_app::App;
8use tracing::instrument;
9
10mod test_app {
12 use anyhow::{Context, Result};
13 use omry_service::OmryApi;
14 use omry_service::search::{SearchParameters, SearchResponse, SortBy};
15 use std::sync::Arc;
16 use tokio::time::{self, Duration};
17 use tracing::instrument;
18
19 use crate::bridge::ApiBridge;
20 use crate::flora_stack::{self, FloraStack};
21
22 #[derive(Debug, Clone)]
24 pub struct App {
25 api: OmryApi,
27 }
28
29 impl App {
30 #[must_use]
32 pub fn new(api: OmryApi) -> Self {
33 Self { api }
34 }
35
36 #[instrument]
44 pub async fn search(&self, query: String, page: Option<usize>) -> Result<SearchResponse> {
45 const BUCKETS: u32 = 5;
46 const QUERY_BY: &[&str] = &["title", "text-body"];
47
48 let query_by = QUERY_BY
49 .iter()
50 .map(std::string::ToString::to_string)
51 .collect::<Vec<_>>();
52
53 let sort_by = SortBy::relevance_and_recency(BUCKETS);
54 let page = page.unwrap_or(1);
55 let params = SearchParameters::new_query(query, query_by)
56 .num_typos(2)
57 .sort_by(sort_by)
58 .page(page);
59
60 ApiBridge::search(self, params.into()).await
61 }
62 }
63
64 impl ApiBridge for App {
65 fn api(&self) -> &OmryApi {
66 &self.api
67 }
68 }
69
70 async fn ping(api: &OmryApi) -> Result<String> {
71 let rx = api.ping().await?;
72 Ok(rx.await?)
73 }
74
75 #[instrument]
78 pub(super) async fn init_app(flora_stack: Arc<FloraStack>) -> Result<App> {
79 const MAX_CONN_ATTEMPTS: usize = 5;
80 const BACKOFF_MULTIPLIER: u32 = 2;
81
82 tokio::spawn(async move {
83 let mut attempts_so_far = 0;
84 let mut backoff_interval = Duration::from_secs(1);
85
86 loop {
95 match flora_stack::api_container(&flora_stack).await {
96 Ok(api) => match ping(&api).await {
97 Ok(_) => break Ok(App::new(api)),
98 Err(error) => {
99 tracing::warn!("error while initializing the RPC client: {error}");
100 }
101 },
102 Err(error) => {
103 tracing::warn!("error while creating the flora client: {error}");
104 }
105 }
106
107 if attempts_so_far == MAX_CONN_ATTEMPTS {
108 anyhow::bail!("couldn't succeed after {MAX_CONN_ATTEMPTS} attempts");
109 }
110
111 time::sleep(backoff_interval).await;
112 backoff_interval *= BACKOFF_MULTIPLIER;
113 attempts_so_far += 1;
114 }
115 })
116 .await
117 .context("Error initializing the test app")?
118 }
119}
120
121#[derive(Debug)]
123pub struct OmryFullStack {
124 backend: Arc<FloraStack>,
126}
127
128impl OmryFullStack {
129 #[instrument]
138 pub async fn new() -> Result<Self> {
139 let stack_options = FloraStackOptions {
140 search_sync_interval: Duration::from_secs(5),
141 ..Default::default()
142 };
143
144 let backend = Arc::new(FloraStack::new(Some(stack_options)).await?);
145 tracing::info!("successfully initialized the test bridge");
146 Ok(Self { backend })
147 }
148
149 #[must_use]
151 pub fn backend(&self) -> &Arc<FloraStack> {
152 &self.backend
153 }
154
155 pub async fn init_app(&self) -> Result<App> {
162 test_app::init_app(Arc::clone(self.backend())).await
163 }
164
165 pub async fn kill_flora_rpc(&mut self) -> Result<()> {
171 self.backend.stop().await?;
172 Ok(())
173 }
174}