mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-23 09:51:10 +01:00
add request show/delete commands and cli integration tests
This commit is contained in:
@@ -20,3 +20,8 @@ yaak-http = { workspace = true }
|
||||
yaak-models = { workspace = true }
|
||||
yaak-plugins = { workspace = true }
|
||||
yaak-templates = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "2"
|
||||
predicates = "3"
|
||||
tempfile = "3"
|
||||
|
||||
@@ -145,6 +145,8 @@ Existing behavior stays the same, just reorganized. Remove the `get` command.
|
||||
|
||||
### Phase 2: Add missing CRUD commands
|
||||
|
||||
Status: in progress (request `show` and `delete` implemented)
|
||||
|
||||
1. `workspace show <id>`
|
||||
2. `workspace create --name <name>` (and `--json`)
|
||||
3. `workspace update --json`
|
||||
|
||||
@@ -73,6 +73,12 @@ pub enum RequestCommands {
|
||||
workspace_id: String,
|
||||
},
|
||||
|
||||
/// Show a request as JSON
|
||||
Show {
|
||||
/// Request ID
|
||||
request_id: String,
|
||||
},
|
||||
|
||||
/// Send an HTTP request by ID
|
||||
Send {
|
||||
/// Request ID
|
||||
@@ -96,6 +102,16 @@ pub enum RequestCommands {
|
||||
#[arg(short, long)]
|
||||
url: String,
|
||||
},
|
||||
|
||||
/// Delete a request
|
||||
Delete {
|
||||
/// Request ID
|
||||
request_id: String,
|
||||
|
||||
/// Skip confirmation prompt
|
||||
#[arg(short, long)]
|
||||
yes: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::context::CliContext;
|
||||
use log::info;
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::{self, IsTerminal, Write};
|
||||
use tokio::sync::mpsc;
|
||||
use yaak_http::path_placeholders::apply_path_placeholders;
|
||||
use yaak_http::sender::{HttpSender, ReqwestSender};
|
||||
@@ -17,12 +18,14 @@ use yaak_templates::{RenderOptions, parse_and_render, render_json_value_raw};
|
||||
pub async fn run(ctx: &CliContext, args: RequestArgs, environment: Option<&str>, verbose: bool) {
|
||||
match args.command {
|
||||
RequestCommands::List { workspace_id } => list(ctx, &workspace_id),
|
||||
RequestCommands::Show { request_id } => show(ctx, &request_id),
|
||||
RequestCommands::Send { request_id } => {
|
||||
send_request_by_id(ctx, &request_id, environment, verbose).await;
|
||||
}
|
||||
RequestCommands::Create { workspace_id, name, method, url } => {
|
||||
create(ctx, workspace_id, name, method, url)
|
||||
}
|
||||
RequestCommands::Delete { request_id, yes } => delete(ctx, &request_id, yes),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +60,43 @@ fn create(ctx: &CliContext, workspace_id: String, name: String, method: String,
|
||||
println!("Created request: {}", created.id);
|
||||
}
|
||||
|
||||
fn show(ctx: &CliContext, request_id: &str) {
|
||||
let request = ctx
|
||||
.db()
|
||||
.get_http_request(request_id)
|
||||
.expect("Failed to get request");
|
||||
let output = serde_json::to_string_pretty(&request).expect("Failed to serialize request");
|
||||
println!("{output}");
|
||||
}
|
||||
|
||||
fn delete(ctx: &CliContext, request_id: &str, yes: bool) {
|
||||
if !yes && !confirm_delete_request(request_id) {
|
||||
println!("Aborted");
|
||||
return;
|
||||
}
|
||||
|
||||
let deleted = ctx
|
||||
.db()
|
||||
.delete_http_request_by_id(request_id, &UpdateSource::Sync)
|
||||
.expect("Failed to delete request");
|
||||
println!("Deleted request: {}", deleted.id);
|
||||
}
|
||||
|
||||
fn confirm_delete_request(request_id: &str) -> bool {
|
||||
if !io::stdin().is_terminal() {
|
||||
eprintln!("Refusing to delete in non-interactive mode without --yes");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
print!("Delete request {request_id}? [y/N]: ");
|
||||
io::stdout().flush().expect("Failed to flush stdout");
|
||||
|
||||
let mut input = String::new();
|
||||
io::stdin().read_line(&mut input).expect("Failed to read confirmation");
|
||||
|
||||
matches!(input.trim().to_lowercase().as_str(), "y" | "yes")
|
||||
}
|
||||
|
||||
/// Send a request by ID and print response in the same format as legacy `send`.
|
||||
pub async fn send_request_by_id(
|
||||
ctx: &CliContext,
|
||||
|
||||
120
crates-cli/yaak-cli/tests/request_cli.rs
Normal file
120
crates-cli/yaak-cli/tests/request_cli.rs
Normal file
@@ -0,0 +1,120 @@
|
||||
use assert_cmd::cargo::cargo_bin_cmd;
|
||||
use assert_cmd::Command;
|
||||
use predicates::str::contains;
|
||||
use std::path::Path;
|
||||
use tempfile::TempDir;
|
||||
use yaak_models::models::{HttpRequest, Workspace};
|
||||
use yaak_models::util::UpdateSource;
|
||||
|
||||
fn cli_cmd(data_dir: &Path) -> Command {
|
||||
let mut cmd = cargo_bin_cmd!("yaakcli");
|
||||
cmd.arg("--data-dir").arg(data_dir);
|
||||
cmd
|
||||
}
|
||||
|
||||
fn seed_workspace(data_dir: &Path, workspace_id: &str) {
|
||||
let db_path = data_dir.join("db.sqlite");
|
||||
let blob_path = data_dir.join("blobs.sqlite");
|
||||
let (query_manager, _blob_manager, _rx) =
|
||||
yaak_models::init_standalone(&db_path, &blob_path).expect("Failed to initialize DB");
|
||||
|
||||
let workspace = Workspace {
|
||||
id: workspace_id.to_string(),
|
||||
name: "Test Workspace".to_string(),
|
||||
description: "Integration test workspace".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
query_manager
|
||||
.connect()
|
||||
.upsert_workspace(&workspace, &UpdateSource::Sync)
|
||||
.expect("Failed to seed workspace");
|
||||
}
|
||||
|
||||
fn seed_request(data_dir: &Path, workspace_id: &str, request_id: &str) {
|
||||
let db_path = data_dir.join("db.sqlite");
|
||||
let blob_path = data_dir.join("blobs.sqlite");
|
||||
let (query_manager, _blob_manager, _rx) =
|
||||
yaak_models::init_standalone(&db_path, &blob_path).expect("Failed to initialize DB");
|
||||
|
||||
let request = HttpRequest {
|
||||
id: request_id.to_string(),
|
||||
workspace_id: workspace_id.to_string(),
|
||||
name: "Seeded Request".to_string(),
|
||||
method: "GET".to_string(),
|
||||
url: "https://example.com".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
query_manager
|
||||
.connect()
|
||||
.upsert_http_request(&request, &UpdateSource::Sync)
|
||||
.expect("Failed to seed request");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_show_and_delete_yes_round_trip() {
|
||||
let temp_dir = TempDir::new().expect("Failed to create temp dir");
|
||||
let data_dir = temp_dir.path();
|
||||
seed_workspace(data_dir, "wk_test");
|
||||
|
||||
let create_assert = cli_cmd(data_dir)
|
||||
.args([
|
||||
"request",
|
||||
"create",
|
||||
"wk_test",
|
||||
"--name",
|
||||
"Smoke Test",
|
||||
"--url",
|
||||
"https://example.com",
|
||||
])
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
let create_stdout = String::from_utf8_lossy(&create_assert.get_output().stdout).to_string();
|
||||
let request_id = create_stdout
|
||||
.trim()
|
||||
.split_once(": ")
|
||||
.map(|(_, id)| id.to_string())
|
||||
.expect("Expected request id in create output");
|
||||
|
||||
cli_cmd(data_dir)
|
||||
.args(["request", "show", &request_id])
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(contains(format!("\"id\": \"{request_id}\"")))
|
||||
.stdout(contains("\"workspaceId\": \"wk_test\""));
|
||||
|
||||
cli_cmd(data_dir)
|
||||
.args(["request", "delete", &request_id, "--yes"])
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(contains(format!("Deleted request: {request_id}")));
|
||||
|
||||
let db_path = data_dir.join("db.sqlite");
|
||||
let blob_path = data_dir.join("blobs.sqlite");
|
||||
let (query_manager, _blob_manager, _rx) =
|
||||
yaak_models::init_standalone(&db_path, &blob_path).expect("Failed to initialize DB");
|
||||
assert!(query_manager.connect().get_http_request(&request_id).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_delete_without_yes_fails_in_non_interactive_mode() {
|
||||
let temp_dir = TempDir::new().expect("Failed to create temp dir");
|
||||
let data_dir = temp_dir.path();
|
||||
seed_workspace(data_dir, "wk_test");
|
||||
seed_request(data_dir, "wk_test", "rq_seed_delete_noninteractive");
|
||||
|
||||
cli_cmd(data_dir)
|
||||
.args(["request", "delete", "rq_seed_delete_noninteractive"])
|
||||
.assert()
|
||||
.failure()
|
||||
.code(1)
|
||||
.stderr(contains("Refusing to delete in non-interactive mode without --yes"));
|
||||
|
||||
let db_path = data_dir.join("db.sqlite");
|
||||
let blob_path = data_dir.join("blobs.sqlite");
|
||||
let (query_manager, _blob_manager, _rx) =
|
||||
yaak_models::init_standalone(&db_path, &blob_path).expect("Failed to initialize DB");
|
||||
assert!(query_manager.connect().get_http_request("rq_seed_delete_noninteractive").is_ok());
|
||||
}
|
||||
Reference in New Issue
Block a user