Skip to content

Commit

Permalink
test: authentication tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Zoe Spellman committed Feb 21, 2024
1 parent f9598ed commit 09dfd5e
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 332 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,7 @@ cucumber = { version = "0.20.2", features = ["libtest", "tracing"] }
[[test]]
name = "voting"
harness = false

[[test]]
name = "authentication"
harness = false
102 changes: 102 additions & 0 deletions tests/authentication.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use cucumber::{given, then, when, World};

use helpers::client::*;
use ratings::utils::{Config, Infrastructure};
use sqlx::Row;
use tonic::{Code, Status};

mod helpers;

#[derive(Clone, Debug, Default, World)]
struct AuthenticationWorld {
client_hash: String,
client: Option<TestClient>,
tokens: Vec<String>,
auth_error: Option<Status>,
}

#[given(expr = "a valid client hash")]
fn generate_hash(world: &mut AuthenticationWorld) {
world.client_hash = helpers::data_faker::rnd_sha_256();
}

#[given(expr = "a bad client with the hash {word}")]
fn with_hash(world: &mut AuthenticationWorld, hash: String) {
world.client_hash = hash;
}

#[when(expr = "the client attempts to authenticate")]
#[when(expr = "that client authenticates a second time")]
#[given(expr = "an authenticated client")]
async fn authenticate(world: &mut AuthenticationWorld) {
let config = Config::load().expect("Could not load config");

world.client = Some(TestClient::new(config.socket()));

match world
.client
.as_ref()
.unwrap()
.authenticate(&world.client_hash)
.await
{
Ok(resp) => world.tokens.push(resp.into_inner().token),
Err(err) => world.auth_error = Some(err),
}
}

#[then(expr = "the authentication is rejected")]
fn check_rejected(world: &mut AuthenticationWorld) {
assert!(world.auth_error.is_some());

let err = world.auth_error.as_ref().unwrap();

assert_eq!(err.code(), Code::InvalidArgument);
}

#[then(expr = "the returned token is valid")]
#[then(expr = "both tokens are valid")]
fn verify_token(world: &mut AuthenticationWorld) {
assert!(
world.auth_error.is_none(),
"needed clean exit, instead got status {:?}",
world.auth_error
);

let config = Config::load().expect("Could not load config");

for token in world.tokens.iter() {
helpers::assert::assert_token_is_valid(token, &config.jwt_secret);
}
}

#[then(expr = "the hash is only in the database once")]
async fn no_double_auth(world: &mut AuthenticationWorld) {
// In other test scenarios we might do this when we init the world, but
// given authentication only needs this once this is fine
let config = Config::load().expect("Could not load config");
let infra = Infrastructure::new(&config)
.await
.expect("Could not init DB");

// User still registered
let row = sqlx::query("SELECT COUNT(*) FROM users WHERE client_hash = $1")
.bind(&world.client_hash)
.fetch_one(&mut *infra.repository().await.expect("could not connect to DB"))
.await
.unwrap();

let count: i64 = row.try_get("count").expect("Failed to get count");

// Only appears in db once
assert_eq!(count, 1);
}

#[tokio::main]
async fn main() {
AuthenticationWorld::cucumber()
.repeat_skipped()
.init_tracing()
.run_and_exit("tests/features/user/authentication.feature")
.await
}
24 changes: 24 additions & 0 deletions tests/features/user/authentication.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Feature: User authentication

Scenario: The Snap Store tries to authenticate
Given a valid client hash
When the client attempts to authenticate
Then the returned token is valid

Rule: Client hashes must be exactly 64 characters long
Scenario Outline: Eggman tries to directly rate a Snap with an unofficial client with an improper hardcoded "hash"
Given a bad client with the hash <hash>
When the client attempts to authenticate
Then the authentication is rejected

Examples:
| hash |
| notarealhash |
| abcdefghijkabcdefghijkabcdefghijkabcdefghijkabcdefghijkabcdefghijk |

Scenario: Charmy's client authenticates twice
Given a valid client hash
Given an authenticated client
When that client authenticates a second time
Then both tokens are valid
And the hash is only in the database once
File renamed without changes.
150 changes: 0 additions & 150 deletions tests/user_tests/category.rs

This file was deleted.

42 changes: 0 additions & 42 deletions tests/user_tests/reject_invalid_register_test.rs

This file was deleted.

Loading

0 comments on commit 09dfd5e

Please sign in to comment.