Skip to content

Commit 1e76c91

Browse files
authored
Add rstest framework for unit tests (#32)
This PR introduces rstest as main unit testing framework. It's added as a development dependency to the project. maplit is added for its hashmap macro. * Core advantages of using rstest - can save on some boilerplate code - easier to add new test cases - #[once] Fixture to make an object initialise once and be shared between all tests *Disadvatages of using rstest Complex struct initialisation might not be possible in a test case. This can still be a no-issue because we can always write normal rust tests for anything that can't be done using rstest.
1 parent 6d084ac commit 1e76c91

File tree

4 files changed

+181
-141
lines changed

4 files changed

+181
-141
lines changed

server/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ ureq = "2.5.0"
5656
sha1_smol = { version = "1.0.0", features=["std"] }
5757
zip = { git = "https://github.com/zip-rs/zip" }
5858

59+
[dev-dependencies]
60+
maplit = "1.0.2"
61+
rstest = "0.15.0"
62+
5963
[package.metadata.parseable_ui]
6064
assets-url = "https://github.com/parseablehq/frontend/releases/download/v0.0.1/build.zip"
61-
assets-sha1 = "d9243bde7a98bdd8a19ed47cca2f12f9a5ce40ba"
65+
assets-sha1 = "d9243bde7a98bdd8a19ed47cca2f12f9a5ce40ba"

server/src/metadata.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,88 @@ fn parse_string(result: Result<Bytes, Error>) -> Result<String, Error> {
175175

176176
Ok(string)
177177
}
178+
179+
#[cfg(test)]
180+
mod tests {
181+
use super::*;
182+
use maplit::hashmap;
183+
use rstest::*;
184+
185+
#[rstest]
186+
#[case::zero(0, 0, 0)]
187+
#[case::some(1024, 512, 2048)]
188+
fn update_stats(#[case] size: u64, #[case] compressed_size: u64, #[case] prev_compressed: u64) {
189+
let mut stats = Stats {
190+
size,
191+
compressed_size,
192+
prev_compressed,
193+
};
194+
195+
stats.update(2056, 2000);
196+
197+
assert_eq!(
198+
stats,
199+
Stats {
200+
size: size + 2056,
201+
compressed_size: prev_compressed + 2000,
202+
prev_compressed
203+
}
204+
)
205+
}
206+
207+
fn clear_map() {
208+
STREAM_INFO.write().unwrap().clear();
209+
}
210+
211+
#[rstest]
212+
#[case::nonempty_string("Hello world")]
213+
#[case::empty_string("")]
214+
fn test_parse_string(#[case] string: String) {
215+
let bytes = Bytes::from(string);
216+
assert!(parse_string(Ok(bytes)).is_ok())
217+
}
218+
219+
#[test]
220+
fn test_bad_parse_string() {
221+
let bad: Vec<u8> = vec![195, 40];
222+
let bytes = Bytes::from(bad);
223+
assert!(parse_string(Ok(bytes)).is_err());
224+
}
225+
226+
#[rstest]
227+
#[case::stream_schema_alert("teststream", "schema", "alert_config")]
228+
#[case::stream_only("teststream", "", "")]
229+
fn test_add_stream(
230+
#[case] stream_name: String,
231+
#[case] schema: String,
232+
#[case] alert_config: String,
233+
) {
234+
clear_map();
235+
STREAM_INFO
236+
.add_stream(stream_name.clone(), schema.clone(), alert_config.clone())
237+
.unwrap();
238+
239+
let left = STREAM_INFO.read().unwrap().clone();
240+
let right = hashmap! {
241+
stream_name => LogStreamMetadata {
242+
schema: schema,
243+
alert_config: alert_config,
244+
..Default::default()
245+
}
246+
};
247+
assert_eq!(left, right);
248+
}
249+
250+
#[rstest]
251+
#[case::stream_only("teststream")]
252+
fn test_delete_stream(#[case] stream_name: String) {
253+
clear_map();
254+
STREAM_INFO
255+
.add_stream(stream_name.clone(), "".to_string(), "".to_string())
256+
.unwrap();
257+
258+
STREAM_INFO.delete_stream(&stream_name).unwrap();
259+
let map = STREAM_INFO.read().unwrap();
260+
assert!(!map.contains_key(&stream_name));
261+
}
262+
}

server/src/query.rs

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -111,29 +111,34 @@ impl Query {
111111

112112
#[cfg(test)]
113113
mod tests {
114-
use std::str::FromStr;
115-
116-
use serde_json::Value;
117-
118114
use super::Query;
115+
use rstest::*;
116+
use serde_json::Value;
117+
use std::str::FromStr;
119118

120-
#[test]
121-
fn query_parse_prefix() {
122-
let query = Value::from_str(
123-
r#"{
124-
"query": "SELECT * FROM stream_name",
125-
"startTime": "2022-10-15T10:00:00+00:00",
126-
"endTime": "2022-10-15T10:01:00+00:00"
127-
}"#,
128-
)
129-
.unwrap();
130-
119+
#[rstest]
120+
#[case(
121+
r#"{
122+
"query": "SELECT * FROM stream_name",
123+
"startTime": "2022-10-15T10:00:00+00:00",
124+
"endTime": "2022-10-15T10:01:00+00:00"
125+
}"#,
126+
&["stream_name/date=2022-10-15/hour=10/minute=00/"]
127+
)]
128+
#[case(
129+
r#"{
130+
"query": "SELECT * FROM stream_name",
131+
"startTime": "2022-10-15T10:00:00+00:00",
132+
"endTime": "2022-10-15T10:02:00+00:00"
133+
}"#,
134+
&["stream_name/date=2022-10-15/hour=10/minute=00/", "stream_name/date=2022-10-15/hour=10/minute=01/"]
135+
)]
136+
fn query_parse_prefix(#[case] prefix: &str, #[case] right: &[&str]) {
137+
let query = Value::from_str(prefix).unwrap();
131138
let query = Query::parse(query).unwrap();
132-
133139
assert_eq!(&query.stream_name, "stream_name");
134-
assert_eq!(
135-
query.get_prefixes(),
136-
vec!["stream_name/date=2022-10-15/hour=10/minute=00/".to_string()]
137-
);
140+
let prefixes = query.get_prefixes();
141+
let left = prefixes.iter().map(String::as_str).collect::<Vec<&str>>();
142+
assert_eq!(left.as_slice(), right);
138143
}
139144
}

server/src/utils.rs

Lines changed: 66 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ impl TimePeriod {
281281
#[cfg(test)]
282282
mod tests {
283283
use chrono::DateTime;
284+
use rstest::*;
284285

285286
use super::TimePeriod;
286287

@@ -292,125 +293,70 @@ mod tests {
292293
)
293294
}
294295

295-
#[test]
296-
fn prefix_generation_same_minute() {
297-
let time_period =
298-
time_period_from_str("2022-06-11T16:30:00+00:00", "2022-06-11T16:30:59+00:00");
299-
300-
assert_eq!(
301-
time_period.generate_prefixes("stream_name"),
302-
vec!["stream_name/date=2022-06-11/hour=16/minute=30/".to_string()]
303-
);
304-
}
305-
306-
#[test]
307-
fn prefix_generation_same_hour_different_minute() {
308-
let time_period =
309-
time_period_from_str("2022-06-11T16:57:00+00:00", "2022-06-11T16:59:00+00:00");
310-
311-
assert_eq!(
312-
time_period.generate_prefixes("stream_name"),
313-
vec![
314-
"stream_name/date=2022-06-11/hour=16/minute=57/".to_string(),
315-
"stream_name/date=2022-06-11/hour=16/minute=58/".to_string(),
316-
]
317-
);
318-
}
319-
320-
#[test]
321-
fn prefix_generation_same_hour_with_00_to_59_minute_block() {
322-
let time_period =
323-
time_period_from_str("2022-06-11T16:00:00+00:00", "2022-06-11T16:59:59+00:00");
324-
325-
assert_eq!(
326-
time_period.generate_prefixes("stream_name"),
327-
vec!["stream_name/date=2022-06-11/hour=16/".to_string(),]
328-
);
329-
}
330-
331-
#[test]
332-
fn prefix_generation_same_date_different_hours_coherent_minute() {
333-
let time_period =
334-
time_period_from_str("2022-06-11T15:00:00+00:00", "2022-06-11T17:00:00+00:00");
335-
336-
assert_eq!(
337-
time_period.generate_prefixes("stream_name"),
338-
vec![
339-
"stream_name/date=2022-06-11/hour=15/".to_string(),
340-
"stream_name/date=2022-06-11/hour=16/".to_string(),
341-
]
342-
);
343-
}
344-
345-
#[test]
346-
fn prefix_generation_same_date_different_hours_incoherent_minutes() {
347-
let time_period =
348-
time_period_from_str("2022-06-11T15:59:00+00:00", "2022-06-11T16:01:00+00:00");
349-
350-
assert_eq!(
351-
time_period.generate_prefixes("stream_name"),
352-
vec![
353-
"stream_name/date=2022-06-11/hour=15/minute=59/".to_string(),
354-
"stream_name/date=2022-06-11/hour=16/minute=00/".to_string(),
355-
]
356-
);
357-
}
358-
359-
#[test]
360-
fn prefix_generation_same_date_different_hours_whole_hours_between_incoherent_minutes() {
361-
let time_period =
362-
time_period_from_str("2022-06-11T15:59:00+00:00", "2022-06-11T17:01:00+00:00");
363-
364-
assert_eq!(
365-
time_period.generate_prefixes("stream_name"),
366-
vec![
367-
"stream_name/date=2022-06-11/hour=15/minute=59/".to_string(),
368-
"stream_name/date=2022-06-11/hour=16/".to_string(),
369-
"stream_name/date=2022-06-11/hour=17/minute=00/".to_string(),
370-
]
371-
);
372-
}
373-
374-
#[test]
375-
fn prefix_generation_different_date_coherent_hours_and_minutes() {
376-
let time_period =
377-
time_period_from_str("2022-06-11T00:00:00+00:00", "2022-06-13T00:00:00+00:00");
378-
379-
assert_eq!(
380-
time_period.generate_prefixes("stream_name"),
381-
vec![
382-
"stream_name/date=2022-06-11/".to_string(),
383-
"stream_name/date=2022-06-12/".to_string(),
384-
]
385-
);
386-
}
387-
388-
#[test]
389-
fn prefix_generation_different_date_incoherent_hours_coherent_minutes() {
390-
let time_period =
391-
time_period_from_str("2022-06-11T23:00:01+00:00", "2022-06-12T01:59:59+00:00");
392-
393-
assert_eq!(
394-
time_period.generate_prefixes("stream_name"),
395-
vec![
396-
"stream_name/date=2022-06-11/hour=23/".to_string(),
397-
"stream_name/date=2022-06-12/hour=00/".to_string(),
398-
"stream_name/date=2022-06-12/hour=01/".to_string(),
399-
]
400-
);
401-
}
402-
403-
#[test]
404-
fn prefix_generation_different_date_incoherent_hours_incoherent_minutes() {
405-
let time_period =
406-
time_period_from_str("2022-06-11T23:59:59+00:00", "2022-06-12T00:01:00+00:00");
407-
408-
assert_eq!(
409-
time_period.generate_prefixes("stream_name"),
410-
vec![
411-
"stream_name/date=2022-06-11/hour=23/minute=59/".to_string(),
412-
"stream_name/date=2022-06-12/hour=00/minute=00/".to_string(),
413-
]
414-
);
296+
#[rstest]
297+
#[case::same_minute(
298+
"2022-06-11T16:30:00+00:00", "2022-06-11T16:30:59+00:00",
299+
&["stream_name/date=2022-06-11/hour=16/minute=30/"]
300+
)]
301+
#[case::same_hour_different_minute(
302+
"2022-06-11T16:57:00+00:00", "2022-06-11T16:59:00+00:00",
303+
&[
304+
"stream_name/date=2022-06-11/hour=16/minute=57/",
305+
"stream_name/date=2022-06-11/hour=16/minute=58/"
306+
]
307+
)]
308+
#[case::same_hour_with_00_to_59_minute_block(
309+
"2022-06-11T16:00:00+00:00", "2022-06-11T16:59:59+00:00",
310+
&["stream_name/date=2022-06-11/hour=16/"]
311+
)]
312+
#[case::same_date_different_hours_coherent_minute(
313+
"2022-06-11T15:00:00+00:00", "2022-06-11T17:00:00+00:00",
314+
&[
315+
"stream_name/date=2022-06-11/hour=15/",
316+
"stream_name/date=2022-06-11/hour=16/"
317+
]
318+
)]
319+
#[case::same_date_different_hours_incoherent_minutes(
320+
"2022-06-11T15:59:00+00:00", "2022-06-11T16:01:00+00:00",
321+
&[
322+
"stream_name/date=2022-06-11/hour=15/minute=59/",
323+
"stream_name/date=2022-06-11/hour=16/minute=00/"
324+
]
325+
)]
326+
#[case::same_date_different_hours_whole_hours_between_incoherent_minutes(
327+
"2022-06-11T15:59:00+00:00", "2022-06-11T17:01:00+00:00",
328+
&[
329+
"stream_name/date=2022-06-11/hour=15/minute=59/",
330+
"stream_name/date=2022-06-11/hour=16/",
331+
"stream_name/date=2022-06-11/hour=17/minute=00/"
332+
]
333+
)]
334+
#[case::different_date_coherent_hours_and_minutes(
335+
"2022-06-11T00:00:00+00:00", "2022-06-13T00:00:00+00:00",
336+
&[
337+
"stream_name/date=2022-06-11/",
338+
"stream_name/date=2022-06-12/"
339+
]
340+
)]
341+
#[case::different_date_incoherent_hours_coherent_minutes(
342+
"2022-06-11T23:00:01+00:00", "2022-06-12T01:59:59+00:00",
343+
&[
344+
"stream_name/date=2022-06-11/hour=23/",
345+
"stream_name/date=2022-06-12/hour=00/",
346+
"stream_name/date=2022-06-12/hour=01/"
347+
]
348+
)]
349+
#[case::different_date_incoherent_hours_incoherent_minutes(
350+
"2022-06-11T23:59:59+00:00", "2022-06-12T00:01:00+00:00",
351+
&[
352+
"stream_name/date=2022-06-11/hour=23/minute=59/",
353+
"stream_name/date=2022-06-12/hour=00/minute=00/"
354+
]
355+
)]
356+
fn prefix_generation(#[case] start: &str, #[case] end: &str, #[case] right: &[&str]) {
357+
let time_period = time_period_from_str(start, end);
358+
let prefixes = time_period.generate_prefixes("stream_name");
359+
let left = prefixes.iter().map(String::as_str).collect::<Vec<&str>>();
360+
assert_eq!(left.as_slice(), right);
415361
}
416362
}

0 commit comments

Comments
 (0)