Files
attune/crates/cli/tests/test_packs.rs
2026-03-04 22:02:34 -06:00

485 lines
14 KiB
Rust

//! Integration tests for CLI pack commands
#![allow(deprecated)]
use assert_cmd::Command;
use predicates::prelude::*;
use serde_json::json;
use wiremock::{
matchers::{body_json, method, path},
Mock, ResponseTemplate,
};
mod common;
use common::*;
#[tokio::test]
async fn test_pack_list_authenticated() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock pack list endpoint
mock_pack_list(&fixture.mock_server).await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("pack")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains("core"))
.stdout(predicate::str::contains("linux"));
}
#[tokio::test]
async fn test_pack_list_unauthenticated() {
let fixture = TestFixture::new().await;
fixture.write_default_config();
// Mock unauthorized response
mock_unauthorized(&fixture.mock_server, "/api/v1/packs").await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("pack")
.arg("list");
cmd.assert().failure();
}
#[tokio::test]
async fn test_pack_list_json_output() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock pack list endpoint
mock_pack_list(&fixture.mock_server).await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("--json")
.arg("pack")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains(r#""ref": "core""#))
.stdout(predicate::str::contains(r#""ref": "linux""#));
}
#[tokio::test]
async fn test_pack_list_yaml_output() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock pack list endpoint
mock_pack_list(&fixture.mock_server).await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("--yaml")
.arg("pack")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains("ref: core"))
.stdout(predicate::str::contains("ref: linux"));
}
#[tokio::test]
async fn test_pack_get_by_ref() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock pack get endpoint
mock_pack_get(&fixture.mock_server, "core").await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("pack")
.arg("show")
.arg("core");
cmd.assert()
.success()
.stdout(predicate::str::contains("core"))
.stdout(predicate::str::contains("core pack"));
}
#[tokio::test]
async fn test_pack_get_not_found() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock 404 response
mock_not_found(&fixture.mock_server, "/api/v1/packs/nonexistent").await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("pack")
.arg("show")
.arg("nonexistent");
cmd.assert()
.failure()
.stderr(predicate::str::contains("Error"));
}
#[tokio::test]
async fn test_pack_list_with_profile() {
let fixture = TestFixture::new().await;
// Create multi-profile config with authentication on default
let config = format!(
r#"
current_profile: staging
default_output_format: table
profiles:
default:
api_url: {}
auth_token: valid_token
refresh_token: refresh_token
description: Default server
staging:
api_url: {}
auth_token: staging_token
refresh_token: staging_refresh
description: Staging server
"#,
fixture.server_url(),
fixture.server_url()
);
fixture.write_config(&config);
// Mock pack list endpoint
mock_pack_list(&fixture.mock_server).await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--profile")
.arg("staging")
.arg("pack")
.arg("list");
cmd.assert().success();
}
#[tokio::test]
async fn test_pack_list_with_api_url_override() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock pack list endpoint
mock_pack_list(&fixture.mock_server).await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("pack")
.arg("list");
cmd.assert().success();
}
#[tokio::test]
async fn test_pack_get_json_output() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock pack get endpoint
mock_pack_get(&fixture.mock_server, "core").await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("-j")
.arg("pack")
.arg("show")
.arg("core");
cmd.assert()
.success()
.stdout(predicate::str::contains(r#""ref": "core""#));
}
// ── pack create tests ──────────────────────────────────────────────────
#[tokio::test]
async fn test_pack_create_non_interactive() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
mock_pack_create(&fixture.mock_server).await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("pack")
.arg("create")
.arg("--ref")
.arg("my_pack")
.arg("--label")
.arg("My Pack")
.arg("--description")
.arg("A test pack")
.arg("--pack-version")
.arg("0.1.0")
.arg("--tags")
.arg("test");
cmd.assert()
.success()
.stdout(predicate::str::contains("my_pack"))
.stdout(predicate::str::contains("created successfully"));
}
#[tokio::test]
async fn test_pack_create_json_output() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
mock_pack_create(&fixture.mock_server).await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("--json")
.arg("pack")
.arg("create")
.arg("--ref")
.arg("my_pack");
cmd.assert()
.success()
.stdout(predicate::str::contains(r#""ref": "my_pack""#))
.stdout(predicate::str::contains(r#""id": 42"#));
}
#[tokio::test]
async fn test_pack_create_conflict() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
mock_pack_create_conflict(&fixture.mock_server).await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("pack")
.arg("create")
.arg("--ref")
.arg("my_pack");
cmd.assert()
.failure()
.stderr(predicate::str::contains("already exists"));
}
#[tokio::test]
async fn test_pack_create_missing_ref() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("pack")
.arg("create");
cmd.assert()
.failure()
.stderr(predicate::str::contains("Pack ref is required"));
}
#[tokio::test]
async fn test_pack_create_default_label_from_ref() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Use a custom mock that validates the request body contains the derived label
Mock::given(method("POST"))
.and(path("/api/v1/packs"))
.and(body_json(json!({
"ref": "my_cool_pack",
"label": "My Cool Pack",
"version": "0.1.0",
"tags": []
})))
.respond_with(ResponseTemplate::new(201).set_body_json(json!({
"data": {
"id": 99,
"ref": "my_cool_pack",
"label": "My Cool Pack",
"version": "0.1.0",
"enabled": true,
"created": "2024-01-01T00:00:00Z",
"updated": "2024-01-01T00:00:00Z"
}
})))
.mount(&fixture.mock_server)
.await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("pack")
.arg("create")
.arg("--ref")
.arg("my_cool_pack");
cmd.assert()
.success()
.stdout(predicate::str::contains("my_cool_pack"))
.stdout(predicate::str::contains("created successfully"));
}
#[tokio::test]
async fn test_pack_create_default_version() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Verify the default version "0.1.0" is sent when --pack-version is not specified
Mock::given(method("POST"))
.and(path("/api/v1/packs"))
.and(body_json(json!({
"ref": "versioned_pack",
"label": "Versioned Pack",
"version": "0.1.0",
"tags": []
})))
.respond_with(ResponseTemplate::new(201).set_body_json(json!({
"data": {
"id": 7,
"ref": "versioned_pack",
"label": "Versioned Pack",
"version": "0.1.0",
"enabled": true,
"created": "2024-01-01T00:00:00Z",
"updated": "2024-01-01T00:00:00Z"
}
})))
.mount(&fixture.mock_server)
.await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("pack")
.arg("create")
.arg("--ref")
.arg("versioned_pack");
cmd.assert().success();
}
#[tokio::test]
async fn test_pack_create_with_tags() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
Mock::given(method("POST"))
.and(path("/api/v1/packs"))
.and(body_json(json!({
"ref": "tagged",
"label": "Tagged",
"version": "0.1.0",
"tags": ["networking", "monitoring"]
})))
.respond_with(ResponseTemplate::new(201).set_body_json(json!({
"data": {
"id": 10,
"ref": "tagged",
"label": "Tagged",
"version": "0.1.0",
"tags": ["networking", "monitoring"],
"enabled": true,
"created": "2024-01-01T00:00:00Z",
"updated": "2024-01-01T00:00:00Z"
}
})))
.mount(&fixture.mock_server)
.await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("pack")
.arg("create")
.arg("--ref")
.arg("tagged")
.arg("--tags")
.arg("networking,monitoring");
cmd.assert().success();
}
#[tokio::test]
async fn test_pack_list_empty_result() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock empty pack list
use serde_json::json;
use wiremock::{
matchers::{method, path},
Mock, ResponseTemplate,
};
Mock::given(method("GET"))
.and(path("/api/v1/packs"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"data": []
})))
.mount(&fixture.mock_server)
.await;
let mut cmd = Command::cargo_bin("attune").unwrap();
cmd.env("XDG_CONFIG_HOME", fixture.config_dir_path())
.env("HOME", fixture.config_dir_path())
.arg("--api-url")
.arg(fixture.server_url())
.arg("pack")
.arg("list");
cmd.assert().success();
}