-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Zoe Spellman
committed
Feb 20, 2024
1 parent
ac506b2
commit 0243582
Showing
10 changed files
with
798 additions
and
14 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,4 +5,4 @@ pub mod rating; | |
pub mod user; | ||
|
||
mod common; | ||
mod pb; | ||
pub mod pb; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
Feature: User voting | ||
Background: | ||
Given a Snap named "chu-chu-garden" has already accumulated 5 votes and 3 upvotes | ||
|
||
Scenario: Amy upvotes a snap she hasn't voted for in the past | ||
When Amy casts an upvote | ||
Then the total number of votes increases | ||
And the total number of upvotes increases | ||
|
||
Rule: Votes that a user updates do not change the total vote count | ||
|
||
Scenario Outline: Sonic changes his vote between downvote and upvote because "chu-chu-garden" got better/worse | ||
Given Sonic originally voted <original> | ||
When Sonic changes his vote to <after> | ||
Then the total number of of upvotes <direction> | ||
But the total number of votes stays constant | ||
|
||
Examples: | ||
| original | after | direction | | ||
| upvote | downvote | decreases | | ||
| downvote | upvote | increases | | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
use std::str::FromStr; | ||
|
||
use cucumber::{given, then, when, Parameter, World}; | ||
use helpers::client_user::UserClient; | ||
use ratings::{ | ||
features::pb::user::{GetSnapVotesRequest, VoteRequest}, | ||
utils::Config, | ||
}; | ||
|
||
mod helpers; | ||
|
||
#[derive(Debug, Default)] | ||
struct AuthenticatedUser { | ||
token: String, | ||
} | ||
|
||
#[derive(Debug, Default, Copy, Clone, Parameter)] | ||
#[param(name = "vote-type", regex = "upvote|downvote")] | ||
enum VoteType { | ||
#[default] | ||
Upvote, | ||
Downvote, | ||
} | ||
|
||
impl FromStr for VoteType { | ||
type Err = String; | ||
|
||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
Ok(match s { | ||
"upvote" => Self::Upvote, | ||
"downvote" => Self::Downvote, | ||
_ => return Err(format!("invalid vote type {s}")), | ||
}) | ||
} | ||
} | ||
|
||
impl From<VoteType> for bool { | ||
fn from(value: VoteType) -> Self { | ||
match value { | ||
VoteType::Upvote => true, | ||
VoteType::Downvote => false, | ||
} | ||
} | ||
} | ||
|
||
impl From<VoteType> for u64 { | ||
fn from(value: VoteType) -> Self { | ||
bool::from(value) as u64 | ||
} | ||
} | ||
|
||
#[derive(Debug, Default, Copy, Clone, Parameter)] | ||
#[param(name = "direction", regex = "increases|decreases|stays constant")] | ||
enum Direction { | ||
#[default] | ||
Increase, | ||
Decrease, | ||
StaysConstant, | ||
} | ||
|
||
impl FromStr for Direction { | ||
type Err = String; | ||
|
||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
Ok(match s { | ||
"increases" => Self::Increase, | ||
"decreases" => Self::Decrease, | ||
"stays constant" => Self::StaysConstant, | ||
_ => return Err(format!("invalid vote count direction {s}")), | ||
}) | ||
} | ||
} | ||
|
||
impl Direction { | ||
fn check_and_apply(&self, current: &mut u64, new: u64) { | ||
match self { | ||
Direction::Decrease => { | ||
assert_eq!(new, *current - 1); | ||
*current -= 1; | ||
} | ||
Direction::Increase => { | ||
assert_eq!(new, *current + 1); | ||
*current += 1; | ||
} | ||
Direction::StaysConstant => assert_eq!(new, *current), | ||
}; | ||
} | ||
} | ||
|
||
#[derive(Debug, PartialEq, Eq)] | ||
struct Snap(String); | ||
|
||
impl Default for Snap { | ||
fn default() -> Self { | ||
Snap("93jv9vhsfbb8f7".to_string()) | ||
} | ||
} | ||
|
||
#[derive(Debug, World)] | ||
#[world(init = Self::new)] | ||
struct VotingWorld { | ||
user: AuthenticatedUser, | ||
client: UserClient, | ||
snap: Snap, | ||
votes: u64, | ||
upvotes: u64, | ||
} | ||
|
||
impl VotingWorld { | ||
async fn new() -> Self { | ||
let config = Config::load().expect("Could not load config"); | ||
let client = UserClient::new(&config.socket()); | ||
|
||
let id = helpers::data_faker::rnd_sha_256(); | ||
let user = AuthenticatedUser { | ||
token: client | ||
.authenticate(&id) | ||
.await | ||
.expect("could not authenticate user") | ||
.into_inner() | ||
.token, | ||
}; | ||
|
||
VotingWorld { | ||
user, | ||
client, | ||
snap: Default::default(), | ||
votes: 0, | ||
upvotes: 0, | ||
} | ||
} | ||
} | ||
|
||
#[given(expr = "a Snap named {string} has already accumulated {int} votes and {int} upvotes")] | ||
async fn seed_snap(world: &mut VotingWorld, _snap_name: String, votes: u64, upvotes: u64) { | ||
world.snap.0 = helpers::data_faker::rnd_id(); | ||
helpers::vote_generator::generate_votes(&world.snap.0, 1, true, upvotes, &world.client) | ||
.await | ||
.expect("could not generate votes"); | ||
helpers::vote_generator::generate_votes( | ||
&world.snap.0, | ||
1, | ||
false, | ||
votes - upvotes, | ||
&world.client, | ||
) | ||
.await | ||
.expect("could not generate votes"); | ||
|
||
world.votes = votes; | ||
world.upvotes = upvotes; | ||
} | ||
|
||
#[when(expr = "{word} casts a(n) {vote-type}")] | ||
#[given(expr = "{word} originally voted {vote-type}")] | ||
#[then(expr = "{word} changes his/her/their vote to {vote-type}")] | ||
async fn vote(world: &mut VotingWorld, _user_name: String, vote_type: VoteType) { | ||
let request = VoteRequest { | ||
snap_id: world.snap.0.clone(), | ||
snap_revision: 1, | ||
vote_up: vote_type.into(), | ||
}; | ||
|
||
world | ||
.client | ||
.vote(&world.user.token, request) | ||
.await | ||
.expect("could not cast vote"); | ||
} | ||
|
||
#[then(expr = "the total number of votes {direction}")] | ||
async fn check_vote(world: &mut VotingWorld, direction: Direction) { | ||
let votes = world | ||
.client | ||
.get_snap_votes( | ||
&world.user.token, | ||
GetSnapVotesRequest { | ||
snap_id: world.snap.0.clone(), | ||
}, | ||
) | ||
.await | ||
.expect("could not get snap votes") | ||
.into_inner(); | ||
panic!("{:?}", votes); | ||
|
||
let votes = votes.votes.len(); | ||
|
||
direction.check_and_apply(&mut world.votes, votes as u64); | ||
} | ||
|
||
#[then(expr = "The total number of upvotes {direction}")] | ||
async fn check_upvote(world: &mut VotingWorld, direction: Direction) { | ||
let upvotes: u64 = world | ||
.client | ||
.get_snap_votes( | ||
&world.user.token, | ||
GetSnapVotesRequest { | ||
snap_id: world.snap.0.clone(), | ||
}, | ||
) | ||
.await | ||
.expect("could not get snap votes") | ||
.into_inner() | ||
.votes | ||
.into_iter() | ||
.map(|v| v.vote_up as u64) | ||
.sum(); | ||
|
||
direction.check_and_apply(&mut world.upvotes, upvotes as u64); | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() { | ||
VotingWorld::run("tests/features/voting.feature").await; | ||
} |