omry_integration/
helpers.rs1use crate::db::TempDb;
3use omry_archiving::{Document, Marshal, Record, RecordParams, ToRecord};
4use std::env;
5use std::fs::{self, File};
6use std::io::prelude::*;
7use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, TcpListener, ToSocketAddrs};
8use std::path::{Path, PathBuf};
9use tempfile::tempdir;
10use tracing::Level;
11use tracing_subscriber::FmtSubscriber;
12
13const WORKSPACE_DIR_KEY: &str = "CARGO_WORKSPACE_DIR";
14const TEST_ARCHIVE_SUBPATH: &str = "test/data/example.bincode";
15const FIND_PORT_MAX_RETRIES: u8 = 5;
16
17fn free_port_from_socket_addr(
23 socket: impl ToSocketAddrs,
24 max_attempts: u8,
25) -> anyhow::Result<(TcpListener, u16)> {
26 let mut attempts_left = max_attempts;
27 let socket_addrs = socket.to_socket_addrs()?.collect::<Vec<_>>();
28
29 while attempts_left > 0 {
30 if let Ok(listener) = TcpListener::bind(socket_addrs.as_slice()) {
31 let port = listener.local_addr()?.port();
32 return Ok((listener, port));
33 }
34 attempts_left -= 1;
35 }
36
37 anyhow::bail!("Couldn't bind a listener to a port in {max_attempts} attempts.");
38}
39
40#[must_use]
49pub fn free_port_ipv4() -> (TcpListener, u16) {
50 let socket = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
51
52 #[allow(clippy::expect_used)]
55 free_port_from_socket_addr(socket, FIND_PORT_MAX_RETRIES).expect("Couldn't find a free port.")
56}
57
58#[must_use]
63pub fn free_port_ipv6() -> (TcpListener, u16) {
64 let socket = SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0);
65
66 #[allow(clippy::expect_used)]
69 free_port_from_socket_addr(socket, FIND_PORT_MAX_RETRIES).expect("Couldn't find a free port.")
70}
71
72fn make_test_archive_holder() -> anyhow::Result<Document> {
73 let (_, workspace_dir_path) = env::vars()
74 .find(|(k, _)| k == WORKSPACE_DIR_KEY)
75 .ok_or_else(|| anyhow::anyhow!("{WORKSPACE_DIR_KEY} is not set"))?;
76
77 let workspace_dir_path = fs::canonicalize(workspace_dir_path)?;
78 let test_archive_path = workspace_dir_path.join(TEST_ARCHIVE_SUBPATH);
79
80 let mut file = File::open(test_archive_path)?;
81 let mut buf = vec![];
82 file.read_to_end(&mut buf)?;
83
84 let archive_holder = Document::from_bytes(&buf)?;
85 Ok(archive_holder)
86}
87
88pub fn make_test_record_params() -> anyhow::Result<RecordParams> {
93 let document = make_test_archive_holder()?;
94 let url = "https://example.org/".to_string();
95 let title = "Example".to_string();
96 let client_datetime = "2024-10-11T13:49:46-05:00".to_string();
97 let language = Some("en".to_string());
98
99 Ok(RecordParams {
100 id: None,
101 url,
102 title,
103 language,
104 client_datetime,
105 document,
106 timestamp_flora: None,
107 })
108}
109
110pub fn make_test_record() -> anyhow::Result<Record> {
115 let incoming_record = Record::new(make_test_record_params()?);
116 Ok(incoming_record)
117}
118
119pub async fn make_test_db_records(how_many: usize) -> anyhow::Result<Vec<impl ToRecord>> {
129 let db = TempDb::new().await?;
130 let mut db_records = Vec::with_capacity(how_many);
131 for _ in 0..how_many {
132 let record = make_test_record()?;
133 let row_id = db.insert(record.try_into()?).await?;
134 let db_record = db.get_record(row_id).await?;
135 db_records.push(db_record);
136 }
137 Ok(db_records)
138}
139
140#[derive(Debug)]
142pub struct TempPathWithString {
143 path: PathBuf,
144 path_string: String,
145}
146
147impl TempPathWithString {
148 pub fn new() -> anyhow::Result<Self> {
155 let temp_path = tempdir()?.keep();
156
157 #[allow(clippy::unnecessary_debug_formatting)]
161 let temp_path_string = temp_path
162 .to_str()
163 .ok_or_else(|| anyhow::anyhow!("Couldn't get a path string from {temp_path:?}"))?
164 .to_string();
165
166 Ok(Self {
167 path: temp_path,
168 path_string: temp_path_string,
169 })
170 }
171
172 #[must_use]
174 pub fn as_path(&self) -> &Path {
175 &self.path
176 }
177
178 #[must_use]
180 pub fn as_str(&self) -> &str {
181 &self.path_string
182 }
183}
184
185pub fn init_tracing_subscriber() -> anyhow::Result<()> {
191 let subscriber = FmtSubscriber::builder()
192 .with_max_level(Level::INFO)
193 .finish();
194 tracing::subscriber::set_global_default(subscriber)?;
195
196 Ok(())
197}