Change CLI structure from `brewlog {verb}-{entity}` to `brewlog {entity} {verb}`:
- brewlog add-roaster → brewlog roaster add
- brewlog list-bags → brewlog bag list
- brewlog create-token → brewlog token create
- etc.
Each entity module now owns its subcommand enum and dispatch logic,
simplifying main.rs and improving discoverability via `brewlog {entity} --help`.
- Add RoasterCommands, RoastCommands, BagCommands, GearCommands, TokenCommands enums
- Add run() dispatcher to each entity module
- Simplify top-level Commands enum to delegate to entity modules
- Update all CLI tests and bootstrap script
131 lines
3.9 KiB
Rust
131 lines
3.9 KiB
Rust
use crate::helpers::{create_token, run_brewlog, server_info};
|
|
|
|
#[test]
|
|
fn test_list_tokens_requires_authentication() {
|
|
let _ = server_info();
|
|
|
|
let output = run_brewlog(&["token", "list"], &[]);
|
|
|
|
assert!(
|
|
!output.status.success(),
|
|
"token list without auth should fail"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_list_tokens_with_authentication() {
|
|
let token = create_token("test-list-tokens");
|
|
|
|
let output = run_brewlog(&["token", "list"], &[("BREWLOG_TOKEN", &token)]);
|
|
|
|
assert!(
|
|
output.status.success(),
|
|
"token list with auth should succeed"
|
|
);
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains("test-list-tokens"),
|
|
"Should list the created token"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_revoke_token_requires_authentication() {
|
|
let _ = server_info();
|
|
|
|
let output = run_brewlog(&["token", "revoke", "--id", "1"], &[]);
|
|
|
|
assert!(
|
|
!output.status.success(),
|
|
"token revoke without auth should fail"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_revoke_token_with_authentication() {
|
|
let token = create_token("test-revoke-token");
|
|
|
|
// List tokens to get the ID
|
|
let list_output = run_brewlog(&["token", "list"], &[("BREWLOG_TOKEN", &token)]);
|
|
assert!(list_output.status.success());
|
|
|
|
let list_stdout = String::from_utf8_lossy(&list_output.stdout);
|
|
let tokens: serde_json::Value =
|
|
serde_json::from_str(&list_stdout).expect("Should parse token list as JSON");
|
|
|
|
// Find the token to revoke by name
|
|
let tokens_array = tokens.as_array().expect("Should be an array");
|
|
let token_to_revoke = tokens_array
|
|
.iter()
|
|
.find(|t| t["name"].as_str() == Some("test-revoke-token"))
|
|
.expect("Should find token to revoke");
|
|
|
|
let token_id = token_to_revoke["id"]
|
|
.as_i64()
|
|
.expect("Token should have ID");
|
|
|
|
let revoke_output = run_brewlog(
|
|
&["token", "revoke", "--id", &token_id.to_string()],
|
|
&[("BREWLOG_TOKEN", &token)],
|
|
);
|
|
|
|
assert!(
|
|
revoke_output.status.success(),
|
|
"Should be able to revoke token"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_revoked_token_cannot_be_used() {
|
|
// Create a token that we will revoke
|
|
let token_to_revoke = create_token("test-revoked-token");
|
|
|
|
// Create a second token that we'll use to revoke the first and verify
|
|
let admin_token = create_token("test-admin-token");
|
|
|
|
// List tokens to get the ID of the token we want to revoke
|
|
let list_output = run_brewlog(&["token", "list"], &[("BREWLOG_TOKEN", &admin_token)]);
|
|
assert!(list_output.status.success());
|
|
|
|
let list_stdout = String::from_utf8_lossy(&list_output.stdout);
|
|
let tokens: serde_json::Value =
|
|
serde_json::from_str(&list_stdout).expect("Should parse token list as JSON");
|
|
|
|
// Find the token to revoke by name
|
|
let tokens_array = tokens.as_array().expect("Should be an array");
|
|
let token_to_revoke_entry = tokens_array
|
|
.iter()
|
|
.find(|t| t["name"].as_str() == Some("test-revoked-token"))
|
|
.expect("Should find token to revoke");
|
|
|
|
let token_id = token_to_revoke_entry["id"]
|
|
.as_i64()
|
|
.expect("Token should have ID");
|
|
|
|
// Revoke the token
|
|
let revoke_output = run_brewlog(
|
|
&["token", "revoke", "--id", &token_id.to_string()],
|
|
&[("BREWLOG_TOKEN", &admin_token)],
|
|
);
|
|
assert!(
|
|
revoke_output.status.success(),
|
|
"Should successfully revoke token"
|
|
);
|
|
|
|
// Try to use the revoked token - it should fail
|
|
let list_with_revoked_output =
|
|
run_brewlog(&["token", "list"], &[("BREWLOG_TOKEN", &token_to_revoke)]);
|
|
|
|
assert!(
|
|
!list_with_revoked_output.status.success(),
|
|
"Revoked token should not be able to authenticate"
|
|
);
|
|
|
|
let stderr = String::from_utf8_lossy(&list_with_revoked_output.stderr);
|
|
assert!(
|
|
stderr.contains("401") || stderr.contains("Unauthorized") || stderr.contains("failed"),
|
|
"Error should indicate authentication failure, got: {}",
|
|
stderr
|
|
);
|
|
}
|