add workspace/folder/environment commands and reorganize cli tests

This commit is contained in:
Gregory Schier
2026-02-16 08:29:35 -08:00
parent 6a23f0a5ee
commit 0bf24c0dc1
17 changed files with 610 additions and 183 deletions

View File

@@ -145,7 +145,7 @@ 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)
Status: in progress (`show`/`create`/`delete` implemented for workspace, request, folder, environment; JSON update flow pending)
1. `workspace show <id>`
2. `workspace create --name <name>` (and `--json`)

View File

@@ -0,0 +1,69 @@
# yaak-cli
Command-line interface for Yaak.
## Command Overview
Current top-level commands:
```text
yaakcli send <request_id>
yaakcli workspace list
yaakcli workspace show <workspace_id>
yaakcli workspace create --name <name>
yaakcli workspace delete <workspace_id> [--yes]
yaakcli request list <workspace_id>
yaakcli request show <request_id>
yaakcli request send <request_id>
yaakcli request create <workspace_id> --name <name> --url <url> [--method GET]
yaakcli request delete <request_id> [--yes]
yaakcli folder list <workspace_id>
yaakcli folder show <folder_id>
yaakcli folder create <workspace_id> --name <name>
yaakcli folder delete <folder_id> [--yes]
yaakcli environment list <workspace_id>
yaakcli environment show <environment_id>
yaakcli environment create <workspace_id> --name <name>
yaakcli environment delete <environment_id> [--yes]
```
Global options:
- `--data-dir <path>`: use a custom data directory
- `-e, --environment <id>`: environment to use during request rendering/sending
- `-v, --verbose`: verbose logging and send output
Notes:
- `send` is currently a shortcut for sending an HTTP request ID.
- `delete` commands prompt for confirmation unless `--yes` is provided.
- In non-interactive mode, `delete` commands require `--yes`.
## Examples
```bash
yaakcli workspace list
yaakcli workspace create --name "My Workspace"
yaakcli workspace show wk_abc
yaakcli request list wk_abc
yaakcli request show rq_abc
yaakcli request create wk_abc --name "Users" --url "https://api.example.com/users"
yaakcli request send rq_abc -e ev_abc
yaakcli request delete rq_abc --yes
yaakcli folder create wk_abc --name "Auth"
yaakcli environment create wk_abc --name "Production"
```
## Roadmap
Planned command expansion (JSON create/update, request schema, and polymorphic send) is tracked in `PLAN.md`.
When command behavior changes, update this README and verify with:
```bash
cargo run -q -p yaak-cli -- --help
cargo run -q -p yaak-cli -- request --help
cargo run -q -p yaak-cli -- workspace --help
cargo run -q -p yaak-cli -- folder --help
cargo run -q -p yaak-cli -- environment --help
```

View File

@@ -32,12 +32,10 @@ pub enum Commands {
/// Request commands
Request(RequestArgs),
/// Folder commands (coming soon)
#[command(hide = true)]
/// Folder commands
Folder(FolderArgs),
/// Environment commands (coming soon)
#[command(hide = true)]
/// Environment commands
Environment(EnvironmentArgs),
}
@@ -57,6 +55,29 @@ pub struct WorkspaceArgs {
pub enum WorkspaceCommands {
/// List all workspaces
List,
/// Show a workspace as JSON
Show {
/// Workspace ID
workspace_id: String,
},
/// Create a workspace
Create {
/// Workspace name
#[arg(short, long)]
name: String,
},
/// Delete a workspace
Delete {
/// Workspace ID
workspace_id: String,
/// Skip confirmation prompt
#[arg(short, long)]
yes: bool,
},
}
#[derive(Args)]
@@ -115,7 +136,83 @@ pub enum RequestCommands {
}
#[derive(Args)]
pub struct FolderArgs {}
pub struct FolderArgs {
#[command(subcommand)]
pub command: FolderCommands,
}
#[derive(Subcommand)]
pub enum FolderCommands {
/// List folders in a workspace
List {
/// Workspace ID
workspace_id: String,
},
/// Show a folder as JSON
Show {
/// Folder ID
folder_id: String,
},
/// Create a folder
Create {
/// Workspace ID
workspace_id: String,
/// Folder name
#[arg(short, long)]
name: String,
},
/// Delete a folder
Delete {
/// Folder ID
folder_id: String,
/// Skip confirmation prompt
#[arg(short, long)]
yes: bool,
},
}
#[derive(Args)]
pub struct EnvironmentArgs {}
pub struct EnvironmentArgs {
#[command(subcommand)]
pub command: EnvironmentCommands,
}
#[derive(Subcommand)]
pub enum EnvironmentCommands {
/// List environments in a workspace
List {
/// Workspace ID
workspace_id: String,
},
/// Show an environment as JSON
Show {
/// Environment ID
environment_id: String,
},
/// Create an environment
Create {
/// Workspace ID
workspace_id: String,
/// Environment name
#[arg(short, long)]
name: String,
},
/// Delete an environment
Delete {
/// Environment ID
environment_id: String,
/// Skip confirmation prompt
#[arg(short, long)]
yes: bool,
},
}

View File

@@ -0,0 +1,16 @@
use std::io::{self, IsTerminal, Write};
pub fn confirm_delete(resource_name: &str, resource_id: &str) -> bool {
if !io::stdin().is_terminal() {
eprintln!("Refusing to delete in non-interactive mode without --yes");
std::process::exit(1);
}
print!("Delete {resource_name} {resource_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")
}

View File

@@ -0,0 +1,64 @@
use crate::cli::{EnvironmentArgs, EnvironmentCommands};
use crate::commands::confirm::confirm_delete;
use crate::context::CliContext;
use yaak_models::models::Environment;
use yaak_models::util::UpdateSource;
pub fn run(ctx: &CliContext, args: EnvironmentArgs) {
match args.command {
EnvironmentCommands::List { workspace_id } => list(ctx, &workspace_id),
EnvironmentCommands::Show { environment_id } => show(ctx, &environment_id),
EnvironmentCommands::Create { workspace_id, name } => create(ctx, workspace_id, name),
EnvironmentCommands::Delete { environment_id, yes } => delete(ctx, &environment_id, yes),
}
}
fn list(ctx: &CliContext, workspace_id: &str) {
let environments =
ctx.db().list_environments_ensure_base(workspace_id).expect("Failed to list environments");
if environments.is_empty() {
println!("No environments found in workspace {}", workspace_id);
} else {
for environment in environments {
println!("{} - {} ({})", environment.id, environment.name, environment.parent_model);
}
}
}
fn show(ctx: &CliContext, environment_id: &str) {
let environment = ctx.db().get_environment(environment_id).expect("Failed to get environment");
let output =
serde_json::to_string_pretty(&environment).expect("Failed to serialize environment");
println!("{output}");
}
fn create(ctx: &CliContext, workspace_id: String, name: String) {
let environment = Environment {
workspace_id,
name,
parent_model: "environment".to_string(),
..Default::default()
};
let created = ctx
.db()
.upsert_environment(&environment, &UpdateSource::Sync)
.expect("Failed to create environment");
println!("Created environment: {}", created.id);
}
fn delete(ctx: &CliContext, environment_id: &str, yes: bool) {
if !yes && !confirm_delete("environment", environment_id) {
println!("Aborted");
return;
}
let deleted = ctx
.db()
.delete_environment_by_id(environment_id, &UpdateSource::Sync)
.expect("Failed to delete environment");
println!("Deleted environment: {}", deleted.id);
}

View File

@@ -0,0 +1,54 @@
use crate::cli::{FolderArgs, FolderCommands};
use crate::commands::confirm::confirm_delete;
use crate::context::CliContext;
use yaak_models::models::Folder;
use yaak_models::util::UpdateSource;
pub fn run(ctx: &CliContext, args: FolderArgs) {
match args.command {
FolderCommands::List { workspace_id } => list(ctx, &workspace_id),
FolderCommands::Show { folder_id } => show(ctx, &folder_id),
FolderCommands::Create { workspace_id, name } => create(ctx, workspace_id, name),
FolderCommands::Delete { folder_id, yes } => delete(ctx, &folder_id, yes),
}
}
fn list(ctx: &CliContext, workspace_id: &str) {
let folders = ctx.db().list_folders(workspace_id).expect("Failed to list folders");
if folders.is_empty() {
println!("No folders found in workspace {}", workspace_id);
} else {
for folder in folders {
println!("{} - {}", folder.id, folder.name);
}
}
}
fn show(ctx: &CliContext, folder_id: &str) {
let folder = ctx.db().get_folder(folder_id).expect("Failed to get folder");
let output = serde_json::to_string_pretty(&folder).expect("Failed to serialize folder");
println!("{output}");
}
fn create(ctx: &CliContext, workspace_id: String, name: String) {
let folder = Folder { workspace_id, name, ..Default::default() };
let created =
ctx.db().upsert_folder(&folder, &UpdateSource::Sync).expect("Failed to create folder");
println!("Created folder: {}", created.id);
}
fn delete(ctx: &CliContext, folder_id: &str, yes: bool) {
if !yes && !confirm_delete("folder", folder_id) {
println!("Aborted");
return;
}
let deleted = ctx
.db()
.delete_folder_by_id(folder_id, &UpdateSource::Sync)
.expect("Failed to delete folder");
println!("Deleted folder: {}", deleted.id);
}

View File

@@ -1,3 +1,6 @@
pub mod confirm;
pub mod environment;
pub mod folder;
pub mod request;
pub mod send;
pub mod workspace;

View File

@@ -1,9 +1,9 @@
use crate::cli::{RequestArgs, RequestCommands};
use crate::commands::confirm::confirm_delete;
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};
@@ -30,10 +30,7 @@ pub async fn run(ctx: &CliContext, args: RequestArgs, environment: Option<&str>,
}
fn list(ctx: &CliContext, workspace_id: &str) {
let requests = ctx
.db()
.list_http_requests(workspace_id)
.expect("Failed to list requests");
let requests = ctx.db().list_http_requests(workspace_id).expect("Failed to list requests");
if requests.is_empty() {
println!("No requests found in workspace {}", workspace_id);
} else {
@@ -61,16 +58,13 @@ fn create(ctx: &CliContext, workspace_id: String, name: String, method: String,
}
fn show(ctx: &CliContext, request_id: &str) {
let request = ctx
.db()
.get_http_request(request_id)
.expect("Failed to get request");
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) {
if !yes && !confirm_delete("request", request_id) {
println!("Aborted");
return;
}
@@ -82,21 +76,6 @@ fn delete(ctx: &CliContext, request_id: &str, yes: bool) {
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,
@@ -104,18 +83,11 @@ pub async fn send_request_by_id(
environment: Option<&str>,
verbose: bool,
) {
let request = ctx
.db()
.get_http_request(request_id)
.expect("Failed to get request");
let request = ctx.db().get_http_request(request_id).expect("Failed to get request");
let environment_chain = ctx
.db()
.resolve_environments(
&request.workspace_id,
request.folder_id.as_deref(),
environment,
)
.resolve_environments(&request.workspace_id, request.folder_id.as_deref(), environment)
.unwrap_or_default();
let plugin_context = PluginContext::new(None, Some(request.workspace_id.clone()));
@@ -169,11 +141,7 @@ pub async fn send_request_by_id(
if verbose {
println!();
}
println!(
"HTTP {} {}",
response.status,
response.status_reason.as_deref().unwrap_or("")
);
println!("HTTP {} {}", response.status, response.status_reason.as_deref().unwrap_or(""));
if verbose {
for (name, value) in &response.headers {
@@ -267,12 +235,5 @@ async fn render_http_request(
let (url, url_parameters) = apply_path_placeholders(&url, &url_parameters);
Ok(HttpRequest {
url,
url_parameters,
headers,
body,
authentication,
..request.to_owned()
})
Ok(HttpRequest { url, url_parameters, headers, body, authentication, ..request.to_owned() })
}

View File

@@ -1,9 +1,15 @@
use crate::cli::{WorkspaceArgs, WorkspaceCommands};
use crate::commands::confirm::confirm_delete;
use crate::context::CliContext;
use yaak_models::models::Workspace;
use yaak_models::util::UpdateSource;
pub fn run(ctx: &CliContext, args: WorkspaceArgs) {
match args.command {
WorkspaceCommands::List => list(ctx),
WorkspaceCommands::Show { workspace_id } => show(ctx, &workspace_id),
WorkspaceCommands::Create { name } => create(ctx, name),
WorkspaceCommands::Delete { workspace_id, yes } => delete(ctx, &workspace_id, yes),
}
}
@@ -17,3 +23,31 @@ fn list(ctx: &CliContext) {
}
}
}
fn show(ctx: &CliContext, workspace_id: &str) {
let workspace = ctx.db().get_workspace(workspace_id).expect("Failed to get workspace");
let output = serde_json::to_string_pretty(&workspace).expect("Failed to serialize workspace");
println!("{output}");
}
fn create(ctx: &CliContext, name: String) {
let workspace = Workspace { name, ..Default::default() };
let created = ctx
.db()
.upsert_workspace(&workspace, &UpdateSource::Sync)
.expect("Failed to create workspace");
println!("Created workspace: {}", created.id);
}
fn delete(ctx: &CliContext, workspace_id: &str, yes: bool) {
if !yes && !confirm_delete("workspace", workspace_id) {
println!("Aborted");
return;
}
let deleted = ctx
.db()
.delete_workspace_by_id(workspace_id, &UpdateSource::Sync)
.expect("Failed to delete workspace");
println!("Deleted workspace: {}", deleted.id);
}

View File

@@ -18,7 +18,8 @@ impl CliContext {
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 database");
yaak_models::init_standalone(&db_path, &blob_path)
.expect("Failed to initialize database");
let encryption_manager = Arc::new(EncryptionManager::new(query_manager.clone(), app_id));

View File

@@ -16,8 +16,9 @@ async fn main() {
let app_id = if cfg!(debug_assertions) { "app.yaak.desktop.dev" } else { "app.yaak.desktop" };
let data_dir =
data_dir.unwrap_or_else(|| dirs::data_dir().expect("Could not determine data directory").join(app_id));
let data_dir = data_dir.unwrap_or_else(|| {
dirs::data_dir().expect("Could not determine data directory").join(app_id)
});
let context = CliContext::initialize(data_dir, app_id).await;
@@ -34,13 +35,13 @@ async fn main() {
commands::request::run(&context, args, environment.as_deref(), verbose).await;
0
}
Commands::Folder(_) => {
eprintln!("Folder commands are not implemented yet");
1
Commands::Folder(args) => {
commands::folder::run(&context, args);
0
}
Commands::Environment(_) => {
eprintln!("Environment commands are not implemented yet");
1
Commands::Environment(args) => {
commands::environment::run(&context, args);
0
}
};

View File

@@ -0,0 +1,60 @@
#![allow(dead_code)]
use assert_cmd::Command;
use assert_cmd::cargo::cargo_bin_cmd;
use std::path::Path;
use yaak_models::models::{HttpRequest, Workspace};
use yaak_models::query_manager::QueryManager;
use yaak_models::util::UpdateSource;
pub fn cli_cmd(data_dir: &Path) -> Command {
let mut cmd = cargo_bin_cmd!("yaakcli");
cmd.arg("--data-dir").arg(data_dir);
cmd
}
pub fn parse_created_id(stdout: &[u8], label: &str) -> String {
String::from_utf8_lossy(stdout)
.trim()
.split_once(": ")
.map(|(_, id)| id.to_string())
.unwrap_or_else(|| panic!("Expected id in '{label}' output"))
}
pub fn query_manager(data_dir: &Path) -> QueryManager {
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");
query_manager
}
pub fn seed_workspace(data_dir: &Path, workspace_id: &str) {
let workspace = Workspace {
id: workspace_id.to_string(),
name: "Seed Workspace".to_string(),
description: "Seeded for integration tests".to_string(),
..Default::default()
};
query_manager(data_dir)
.connect()
.upsert_workspace(&workspace, &UpdateSource::Sync)
.expect("Failed to seed workspace");
}
pub fn seed_request(data_dir: &Path, workspace_id: &str, request_id: &str) {
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(data_dir)
.connect()
.upsert_http_request(&request, &UpdateSource::Sync)
.expect("Failed to seed request");
}

View File

@@ -0,0 +1,50 @@
mod common;
use common::{cli_cmd, parse_created_id, query_manager, seed_workspace};
use predicates::str::contains;
use tempfile::TempDir;
#[test]
fn create_list_show_delete_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");
cli_cmd(data_dir)
.args(["environment", "list", "wk_test"])
.assert()
.success()
.stdout(contains("Global Variables"));
let create_assert = cli_cmd(data_dir)
.args(["environment", "create", "wk_test", "--name", "Production"])
.assert()
.success();
let environment_id =
parse_created_id(&create_assert.get_output().stdout, "environment create");
cli_cmd(data_dir)
.args(["environment", "list", "wk_test"])
.assert()
.success()
.stdout(contains(&environment_id))
.stdout(contains("Production"));
cli_cmd(data_dir)
.args(["environment", "show", &environment_id])
.assert()
.success()
.stdout(contains(format!("\"id\": \"{environment_id}\"")))
.stdout(contains("\"parentModel\": \"environment\""));
cli_cmd(data_dir)
.args(["environment", "delete", &environment_id, "--yes"])
.assert()
.success()
.stdout(contains(format!("Deleted environment: {environment_id}")));
assert!(query_manager(data_dir)
.connect()
.get_environment(&environment_id)
.is_err());
}

View File

@@ -0,0 +1,40 @@
mod common;
use common::{cli_cmd, parse_created_id, query_manager, seed_workspace};
use predicates::str::contains;
use tempfile::TempDir;
#[test]
fn create_list_show_delete_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(["folder", "create", "wk_test", "--name", "Auth"])
.assert()
.success();
let folder_id = parse_created_id(&create_assert.get_output().stdout, "folder create");
cli_cmd(data_dir)
.args(["folder", "list", "wk_test"])
.assert()
.success()
.stdout(contains(&folder_id))
.stdout(contains("Auth"));
cli_cmd(data_dir)
.args(["folder", "show", &folder_id])
.assert()
.success()
.stdout(contains(format!("\"id\": \"{folder_id}\"")))
.stdout(contains("\"workspaceId\": \"wk_test\""));
cli_cmd(data_dir)
.args(["folder", "delete", &folder_id, "--yes"])
.assert()
.success()
.stdout(contains(format!("Deleted folder: {folder_id}")));
assert!(query_manager(data_dir).connect().get_folder(&folder_id).is_err());
}

View File

@@ -1,120 +0,0 @@
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());
}

View File

@@ -0,0 +1,62 @@
mod common;
use common::{cli_cmd, parse_created_id, query_manager, seed_request, seed_workspace};
use predicates::str::contains;
use tempfile::TempDir;
#[test]
fn 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 request_id = parse_created_id(&create_assert.get_output().stdout, "request create");
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}")));
assert!(query_manager(data_dir).connect().get_http_request(&request_id).is_err());
}
#[test]
fn 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"));
assert!(query_manager(data_dir)
.connect()
.get_http_request("rq_seed_delete_noninteractive")
.is_ok());
}

View File

@@ -0,0 +1,35 @@
mod common;
use common::{cli_cmd, parse_created_id, query_manager};
use predicates::str::contains;
use tempfile::TempDir;
#[test]
fn create_show_delete_round_trip() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let data_dir = temp_dir.path();
let create_assert = cli_cmd(data_dir)
.args(["workspace", "create", "--name", "WS One"])
.assert()
.success();
let workspace_id = parse_created_id(&create_assert.get_output().stdout, "workspace create");
cli_cmd(data_dir)
.args(["workspace", "show", &workspace_id])
.assert()
.success()
.stdout(contains(format!("\"id\": \"{workspace_id}\"")))
.stdout(contains("\"name\": \"WS One\""));
cli_cmd(data_dir)
.args(["workspace", "delete", &workspace_id, "--yes"])
.assert()
.success()
.stdout(contains(format!("Deleted workspace: {workspace_id}")));
assert!(query_manager(data_dir)
.connect()
.get_workspace(&workspace_id)
.is_err());
}