Skip to content

Commit

Permalink
Validate username (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
tbsklg authored Sep 2, 2024
1 parent a1a14b4 commit 195a29c
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 10 deletions.
88 changes: 88 additions & 0 deletions cli-client/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ config = "0.14.0"
serde_json = "1.0"
openssl = { version = "0.10", features = ["vendored"] }
async-trait = "0.1.81"
comfy-table = "7.1.1"

[dev-dependencies]
assert_cmd = "2.0.16"
Expand Down
17 changes: 16 additions & 1 deletion cli-client/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use clap::{Parser, Subcommand};
#[derive(Subcommand, Clone, Debug)]
pub enum Command {
#[command(about = "Add a strike", alias = "s")]
Strike { name: String },
Strike {
#[arg(help = "Name of the tarnished", value_parser = parse_username)]
name: String,
},
#[command(about = "List all strikes")]
Ls,
#[command(about = "Clear strikes", alias = "c")]
Expand All @@ -26,3 +29,15 @@ pub struct Cli {
#[command(subcommand)]
pub command: Option<Command>,
}

fn parse_username(s: &str) -> Result<String, Box<dyn std::error::Error + Send + Sync + 'static>> {
if s.is_empty() {
return Err("Username cannot be empty".into());
}

if s.len() > 20 {
return Err("Username cannot be longer than 20 characters".into());
}

Ok(s.to_lowercase())
}
3 changes: 2 additions & 1 deletion cli-client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use strikes::clients::local_client::LocalClient;
use strikes::clients::remote_client::RemoteClient;
use strikes::configuration::{get_configuration, Settings};
use strikes::output::{print_as_table, print_strikes};
use strikes::tarnished::Tarnished;

#[tokio::main]
async fn main() {
Expand All @@ -18,7 +19,7 @@ async fn main() {
Err(err) => eprintln!("Failed to add strike: {}", err),
},
Command::Ls => match client.get_tarnished().await {
Ok(tarnished) => print_as_table(tarnished),
Ok(tarnished) => print_as_table(Tarnished::sort_desc_by_strike(tarnished)),
Err(err) => eprintln!("Failed to get strikes: {}", err),
},
Command::Clear => {
Expand Down
9 changes: 7 additions & 2 deletions cli-client/src/output.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
use crate::tarnished::Tarnished;
use comfy_table::Table;

pub fn print_as_table(tarnished: Vec<Tarnished>) {
if tarnished.is_empty() {
println!("No one has been tarnished yet!");
return;
}

println!("{0: <10} | {1: <10} |", "Tarnished", "Strikes");
let mut table = Table::new();
table.set_header(vec!["Tarnished", "Strikes"]);

for tarnished in tarnished {
println!("{0: <10} | {1: <10} |", tarnished.name, tarnished.strikes);
table.add_row(vec![tarnished.name, tarnished.strikes.to_string()]);
}

println!("{table}");
}

pub fn print_strikes(name: &str, strikes: u8) {
Expand Down
45 changes: 39 additions & 6 deletions cli-client/tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,13 @@ fn it_should_list_strikes_in_descending_order() -> Result<(), Box<dyn std::error

cmd.arg("--config-path").arg(config_file.path()).arg("ls");

let expected_output = "Tarnished | Strikes |\n\
heinz | 2 |\n\
guenther | 1 |\n";
let expected_output = "+-----------+---------+\n\
| Tarnished | Strikes |\n\
+=====================+\n\
| heinz | 2 |\n\
|-----------+---------|\n\
| guenther | 1 |\n\
+-----------+---------+\n";

cmd.assert().success().stdout(expected_output);

Expand Down Expand Up @@ -86,9 +90,13 @@ fn it_should_clear_all_strikes() -> Result<(), Box<dyn std::error::Error>> {

let mut cmd = Command::cargo_bin("strikes")?;
cmd.arg("--config-path").arg(config_file.path()).arg("ls");
cmd.assert()
.success()
.stdout("Tarnished | Strikes |\nguenther | 1 |\n");
let expected_output = "+-----------+---------+\n\
| Tarnished | Strikes |\n\
+=====================+\n\
| guenther | 1 |\n\
+-----------+---------+\n";

cmd.assert().success().stdout(expected_output);

let mut cmd = Command::cargo_bin("strikes")?;
cmd.arg("--config-path")
Expand All @@ -106,3 +114,28 @@ fn it_should_clear_all_strikes() -> Result<(), Box<dyn std::error::Error>> {

Ok(())
}

#[test]
fn it_should_reject_usernames_longer_than_20_characters() -> Result<(), Box<dyn std::error::Error>>
{
let db_file = assert_fs::NamedTempFile::new("./tests/fixtures/db.json")?;
let config_file = assert_fs::NamedTempFile::new("./tests/fixtures/configuration.yaml")?;
config_file.write_str(
format!(
"{{\"local\": {{\"db_path\": \"{}\"}}}}",
db_file.path().to_str().unwrap()
)
.as_str(),
)?;

let mut cmd = Command::cargo_bin("strikes")?;
cmd.arg("--config-path")
.arg(config_file.path())
.arg("strike")
.arg("guentherguentherguenther");
cmd.assert().failure().stderr(predicate::str::contains(
"Username cannot be longer than 20 characters",
));

Ok(())
}
7 changes: 7 additions & 0 deletions infrastructure/lambdas/src/put_strike.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ pub async fn function_handler(request: Request) -> Result<Response<Body>, Error>

match user {
Some(username) => {
if username.is_empty() || username.len() > 20 {
return Ok(Response::builder()
.status(400)
.body(Body::Text("Invalid username".to_string()))
.expect("Failed to render response"));
}

let strike_count = increment_strikes(username, "Strikes", &client).await?;
Ok(Response::builder()
.status(200)
Expand Down

0 comments on commit 195a29c

Please sign in to comment.