re-uploading work

This commit is contained in:
2026-02-04 17:46:30 -06:00
commit 3b14c65998
1388 changed files with 381262 additions and 0 deletions

View File

@@ -0,0 +1,570 @@
//! Integration tests for CLI action commands
#![allow(deprecated)]
use assert_cmd::Command;
use predicates::prelude::*;
use serde_json::json;
use wiremock::{
matchers::{method, path},
Mock, ResponseTemplate,
};
mod common;
use common::*;
#[tokio::test]
async fn test_action_list_authenticated() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock action list endpoint
mock_action_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("action")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains("core.echo"))
.stdout(predicate::str::contains("Echo a message"));
}
#[tokio::test]
async fn test_action_list_unauthenticated() {
let fixture = TestFixture::new().await;
fixture.write_default_config();
// Mock unauthorized response
mock_unauthorized(&fixture.mock_server, "/api/v1/actions").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("action")
.arg("list");
cmd.assert().failure();
}
#[tokio::test]
async fn test_action_list_json_output() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock action list endpoint
mock_action_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("action")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains(r#""ref""#))
.stdout(predicate::str::contains(r#"core.echo"#));
}
#[tokio::test]
async fn test_action_list_yaml_output() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock action list endpoint
mock_action_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("action")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains("core.echo"))
.stdout(predicate::str::contains("Echo a message"));
}
#[tokio::test]
async fn test_action_get_by_ref() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock action get endpoint
Mock::given(method("GET"))
.and(path("/api/v1/actions/core.echo"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"data": {
"id": 1,
"ref": "core.echo",
"pack": 1,
"pack_ref": "core",
"label": "Echo Action",
"description": "Echo a message",
"entrypoint": "echo.py",
"runtime": null,
"param_schema": {
"message": {
"type": "string",
"description": "Message to echo",
"required": true
}
},
"out_schema": null,
"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("action")
.arg("show")
.arg("core.echo");
cmd.assert()
.success()
.stdout(predicate::str::contains("core.echo"))
.stdout(predicate::str::contains("Echo a message"));
}
#[tokio::test]
async fn test_action_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/actions/nonexistent.action").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("action")
.arg("show")
.arg("nonexistent.action");
cmd.assert()
.failure()
.stderr(predicate::str::contains("Error"));
}
#[tokio::test]
async fn test_action_execute_with_parameters() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock action execute endpoint
mock_action_execute(&fixture.mock_server, 42).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("action")
.arg("execute")
.arg("core.echo")
.arg("--param")
.arg("message=Hello World");
cmd.assert()
.success()
.stdout(predicate::str::contains("42").or(predicate::str::contains("scheduled")));
}
#[tokio::test]
async fn test_action_execute_multiple_parameters() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock action execute endpoint
mock_action_execute(&fixture.mock_server, 100).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("action")
.arg("execute")
.arg("linux.run_command")
.arg("--param")
.arg("cmd=ls -la")
.arg("--param")
.arg("timeout=30");
cmd.assert().success();
}
#[tokio::test]
async fn test_action_execute_with_json_parameters() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock action execute endpoint
mock_action_execute(&fixture.mock_server, 101).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("action")
.arg("execute")
.arg("core.webhook")
.arg("--params-json")
.arg(r#"{"url": "https://example.com", "method": "POST"}"#);
cmd.assert().success();
}
#[tokio::test]
async fn test_action_execute_without_parameters() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock action execute endpoint
mock_action_execute(&fixture.mock_server, 200).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("action")
.arg("execute")
.arg("core.no_params_action");
cmd.assert().success();
}
#[tokio::test]
async fn test_action_execute_json_output() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock action execute endpoint
mock_action_execute(&fixture.mock_server, 150).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("action")
.arg("execute")
.arg("core.echo")
.arg("--param")
.arg("message=test");
cmd.assert()
.success()
.stdout(predicate::str::contains("150"))
.stdout(predicate::str::contains("scheduled"));
}
#[tokio::test]
async fn test_action_execute_wait_for_completion() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock action execute endpoint
mock_action_execute(&fixture.mock_server, 250).await;
// Mock execution polling - first running, then succeeded
Mock::given(method("GET"))
.and(path("/api/v1/executions/250"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"data": {
"id": 250,
"action": 1,
"action_ref": "core.echo",
"config": {"message": "test"},
"parent": null,
"enforcement": null,
"executor": null,
"status": "succeeded",
"result": {"output": "test"},
"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("action")
.arg("execute")
.arg("core.echo")
.arg("--param")
.arg("message=test")
.arg("--wait");
cmd.assert()
.success()
.stdout(predicate::str::contains("succeeded"));
}
#[tokio::test]
#[ignore = "Profile switching needs more investigation - CLI integration issue"]
async fn test_action_execute_with_profile() {
let fixture = TestFixture::new().await;
// Create multi-profile config
let config = format!(
r#"
current_profile: default
default_output_format: table
profiles:
default:
api_url: {}
auth_token: default_token
refresh_token: default_refresh
production:
api_url: {}
auth_token: prod_token
refresh_token: prod_refresh
"#,
fixture.server_url(),
fixture.server_url()
);
fixture.write_config(&config);
// Mock action execute endpoint
mock_action_execute(&fixture.mock_server, 300).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("production")
.arg("action")
.arg("execute")
.arg("core.echo")
.arg("--param")
.arg("message=prod_test");
cmd.assert().success();
}
#[tokio::test]
async fn test_action_execute_invalid_param_format() {
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("action")
.arg("execute")
.arg("core.echo")
.arg("--param")
.arg("invalid_format_no_equals");
cmd.assert()
.failure()
.stderr(predicate::str::contains("Error").or(predicate::str::contains("=")));
}
#[tokio::test]
async fn test_action_execute_invalid_json_parameters() {
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("action")
.arg("execute")
.arg("core.echo")
.arg("--params-json")
.arg(r#"{"invalid json"#);
cmd.assert()
.failure()
.stderr(predicate::str::contains("Error").or(predicate::str::contains("JSON")));
}
#[tokio::test]
async fn test_action_list_by_pack() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock action list for a specific pack
Mock::given(method("GET"))
.and(path("/api/v1/packs/core/actions"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"data": [
{
"id": 1,
"ref": "core.echo",
"pack_ref": "core",
"label": "Echo Action",
"description": "Echo a message",
"entrypoint": "echo.py",
"runtime": null,
"created": "2024-01-01T00:00:00Z",
"updated": "2024-01-01T00:00:00Z"
}
],
"meta": {
"page": 1,
"limit": 50,
"total": 1,
"total_pages": 1
}
})))
.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("action")
.arg("list")
.arg("--pack")
.arg("core");
cmd.assert().success();
}
#[tokio::test]
async fn test_action_execute_async_flag() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock action execute endpoint
mock_action_execute(&fixture.mock_server, 400).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("action")
.arg("execute")
.arg("core.long_running");
// Note: default behavior is async (no --wait), so no --async flag needed
cmd.assert()
.success()
.stdout(predicate::str::contains("scheduled").or(predicate::str::contains("400")));
}
#[tokio::test]
async fn test_action_list_empty_result() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock empty action list
Mock::given(method("GET"))
.and(path("/api/v1/actions"))
.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("action")
.arg("list");
cmd.assert().success();
}
#[tokio::test]
async fn test_action_get_shows_parameters() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock action get with detailed parameters
Mock::given(method("GET"))
.and(path("/api/v1/actions/core.complex"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"data": {
"id": 5,
"ref": "core.complex",
"pack": 1,
"pack_ref": "core",
"label": "Complex Action",
"description": "Complex action with multiple params",
"entrypoint": "complex.py",
"runtime": null,
"param_schema": {
"required_string": {
"type": "string",
"description": "A required string parameter",
"required": true
},
"optional_number": {
"type": "integer",
"description": "An optional number",
"required": false,
"default": 42
},
"boolean_flag": {
"type": "boolean",
"description": "A boolean flag",
"required": false,
"default": false
}
},
"out_schema": null,
"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("action")
.arg("show")
.arg("core.complex");
cmd.assert()
.success()
.stdout(predicate::str::contains("required_string"))
.stdout(predicate::str::contains("optional_number"));
}