Skip to content

Commit

Permalink
test: migrate chart tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Zoe Spellman committed Feb 21, 2024
1 parent 7110f4e commit 65e9b46
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 452 deletions.
136 changes: 126 additions & 10 deletions tests/chart.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,45 @@
use cucumber::{given, World};
#![cfg(test)]

use cucumber::{given, then, when, Parameter, World};
use futures::FutureExt;
use helpers::client::*;
use rand::{thread_rng, Rng};
use ratings::utils::{Config, Infrastructure};
use ratings::{
features::{
common::entities::{calculate_band, VoteSummary},
pb::chart::{Category, ChartData, Timeframe},
},
utils::{Config, Infrastructure},
};
use sqlx::Connection;
use strum::EnumString;

mod helpers;

#[derive(Copy, Clone, Debug, PartialEq, Eq, Parameter, EnumString)]
#[param(name = "category", regex = "Utilities|Development")]
pub enum TestCategory {
Utilities,
Development,
}

impl From<TestCategory> for Category {
fn from(value: TestCategory) -> Self {
match value {
TestCategory::Development => Self::Development,
TestCategory::Utilities => Self::Utilities,
}
}
}

#[derive(Debug, World)]
#[world(init = Self::new)]
struct ChartWorld {
token: String,
snap_ids: Vec<String>,
test_snap: String,
client: TestClient,
chart_data: Vec<ChartData>,
}

impl ChartWorld {
Expand All @@ -31,6 +57,7 @@ impl ChartWorld {
Self {
snap_ids: Vec::with_capacity(25),
test_snap: Default::default(),
chart_data: Vec::new(),
client,
token,
}
Expand All @@ -51,6 +78,8 @@ async fn set_test_snap(world: &mut ChartWorld, snap_id: String, votes: usize, up
.await
.expect("could not generate votes");

tracing::debug!("done generating upvotes");

helpers::vote_generator::generate_votes(
&world.test_snap,
1,
Expand All @@ -60,6 +89,8 @@ async fn set_test_snap(world: &mut ChartWorld, snap_id: String, votes: usize, up
)
.await
.expect("could not generate votes");

tracing::debug!("done generating downvotes");
}

#[given(
Expand All @@ -73,16 +104,28 @@ async fn generate_snaps(
min_upvote: usize,
max_upvote: usize,
) {
let mut rng = thread_rng();
let mut expected = Vec::with_capacity(25);

for i in 1..=num_snaps {
tracing::info!("starting snap {i} / {num_snaps}");

let (upvotes, votes) = {
let mut rng = thread_rng();

let upvotes = rng.gen_range(min_upvote..max_upvote);
let min_vote = Ord::max(upvotes, min_vote);
let votes = rng.gen_range(min_vote..=max_vote);
(upvotes, votes)
};

for _ in 0..=num_snaps {
let votes = rng.gen_range(min_vote..=max_vote);
let upvotes = rng.gen_range(min_upvote..max_upvote);
let id = helpers::data_faker::rnd_id();

helpers::vote_generator::generate_votes(&id, 1, true, upvotes as u64, &world.client)
.await
.expect("could not generate votes");

tracing::debug!("done generating upvotes ({i} / {num_snaps})");

helpers::vote_generator::generate_votes(
&id,
1,
Expand All @@ -93,13 +136,84 @@ async fn generate_snaps(
.await
.expect("could not generate votes");

world.snap_ids.push(id);
tracing::debug!("done generating downvotes ({i} / {num_snaps})");

let summary = VoteSummary {
snap_id: id,
total_votes: votes as i64,
positive_votes: upvotes as i64,
};

expected.push((calculate_band(&summary).0.unwrap(), summary.snap_id));
}

expected.sort_unstable_by(|(band1, _), (band2, _)| band1.partial_cmp(band2).unwrap().reverse());
world.snap_ids.extend(expected.drain(..).map(|(band, id)| {
tracing::debug!("id: {id}; band: {band}");
id
}));
}

#[when(expr = "the client fetches the top snaps")]
async fn get_chart(world: &mut ChartWorld) {
get_chart_internal(world, None).await;
}

#[when(expr = "the client fetches the top snaps for {category}")]
async fn get_chart_of_category(world: &mut ChartWorld, category: TestCategory) {
get_chart_internal(world, Some(category.into())).await;
}

async fn get_chart_internal(world: &mut ChartWorld, category: Option<Category>) {
world.chart_data = world
.client
.get_chart_of_category(Timeframe::Unspecified, category, &world.token)
.await
.expect("couldn't get chart")
.into_inner()
.ordered_chart_data;
}

#[then(expr = "the top {int} snaps are returned in the proper order")]
async fn chart_order(world: &mut ChartWorld, top: usize) {
assert_eq!(world.chart_data.len(), top);

assert!(world
.chart_data
.iter()
.zip(world.snap_ids.iter())
.all(|(data, id)| {
let left = &data
.rating
.as_ref()
.expect("no rating in chart data?")
.snap_id;

tracing::debug!("chart data: {data:?}, expected: {id}");

left == id
}))
}

/// Automatically clears and snaps with >= 75 votes, preventing them from interfering with tests
#[then(expr = "the top snap returned is the one with the ID {string}")]
async fn check_test_snap(world: &mut ChartWorld, snap_id: String) {
assert_eq!(
world.test_snap, snap_id,
"feature file and test snap definition got out of sync"
);

assert_eq!(
&world.chart_data[0].rating.as_ref().unwrap().snap_id,
&snap_id,
"top chart result is not test snap"
);
}

/// Automatically clears and snaps with >= TO_CLEAR votes, preventing them from interfering with tests
/// Being independent, while also not affecting other tests that require lower vote counts
async fn clear_db() {
const TO_CLEAR: usize = 3;

let config = Config::load().unwrap();
let infra = Infrastructure::new(&config).await.unwrap();
let mut conn = infra.repository().await.unwrap();
Expand All @@ -108,9 +222,10 @@ async fn clear_db() {

sqlx::query(
r#"DELETE FROM votes WHERE snap_id IN
(SELECT snap_id FROM votes GROUP BY snap_id HAVING COUNT(*) >= 75)
(SELECT snap_id FROM votes GROUP BY snap_id HAVING COUNT(*) >= $1)
"#,
)
.bind(TO_CLEAR as i64)
.execute(&mut *tx)
.await
.unwrap();
Expand All @@ -127,8 +242,9 @@ async fn clear_db() {
async fn main() {
ChartWorld::cucumber()
.before(|_, _, _, _| clear_db().boxed_local())
.repeat_skipped()
.repeat_failed()
.init_tracing()
.max_concurrent_scenarios(1)
.run_and_exit("tests/features/chart.feature")
.await
}
154 changes: 0 additions & 154 deletions tests/chart_tests/category.rs

This file was deleted.

Loading

0 comments on commit 65e9b46

Please sign in to comment.