Files
attune/crates/cli/tests/test_rules_triggers_sensors.rs
2026-02-04 17:46:30 -06:00

632 lines
19 KiB
Rust

//! Integration tests for CLI rules, triggers, and sensors 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::*;
// ============================================================================
// Rule Tests
// ============================================================================
#[tokio::test]
async fn test_rule_list_authenticated() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock rule list endpoint
mock_rule_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("rule")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains("On Webhook"));
}
#[tokio::test]
async fn test_rule_list_unauthenticated() {
let fixture = TestFixture::new().await;
fixture.write_default_config();
// Mock unauthorized response
mock_unauthorized(&fixture.mock_server, "/api/v1/rules").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("rule")
.arg("list");
cmd.assert().failure();
}
#[tokio::test]
async fn test_rule_list_json_output() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock rule list endpoint
mock_rule_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("rule")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains(r#""ref": "core.on_webhook""#));
}
#[tokio::test]
async fn test_rule_list_yaml_output() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock rule list endpoint
mock_rule_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("rule")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains("ref: core.on_webhook"));
}
#[tokio::test]
async fn test_rule_get_by_ref() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock rule get endpoint
Mock::given(method("GET"))
.and(path("/api/v1/rules/core.on_webhook"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"data": {
"id": 1,
"ref": "core.on_webhook",
"pack": 1,
"pack_ref": "core",
"label": "On Webhook",
"description": "Handle webhook events",
"trigger": 1,
"trigger_ref": "core.webhook",
"action": 1,
"action_ref": "core.echo",
"enabled": true,
"conditions": {},
"action_params": {},
"trigger_params": {},
"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("rule")
.arg("show")
.arg("core.on_webhook");
cmd.assert()
.success()
.stdout(predicate::str::contains("On Webhook"))
.stdout(predicate::str::contains("Handle webhook events"));
}
#[tokio::test]
async fn test_rule_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/rules/nonexistent.rule").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("rule")
.arg("show")
.arg("nonexistent.rule");
cmd.assert()
.failure()
.stderr(predicate::str::contains("Error"));
}
#[tokio::test]
async fn test_rule_list_by_pack() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock rule list endpoint with pack filter via query parameter
mock_rule_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("rule")
.arg("list")
.arg("--pack")
.arg("core");
cmd.assert().success();
}
// ============================================================================
// Trigger Tests
// ============================================================================
#[tokio::test]
async fn test_trigger_list_authenticated() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock trigger list endpoint
mock_trigger_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("trigger")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains("Webhook Trigger"));
}
#[tokio::test]
async fn test_trigger_list_unauthenticated() {
let fixture = TestFixture::new().await;
fixture.write_default_config();
// Mock unauthorized response
mock_unauthorized(&fixture.mock_server, "/api/v1/triggers").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("trigger")
.arg("list");
cmd.assert().failure();
}
#[tokio::test]
async fn test_trigger_list_json_output() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock trigger list endpoint
mock_trigger_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("trigger")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains(r#""ref": "core.webhook""#));
}
#[tokio::test]
async fn test_trigger_list_yaml_output() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock trigger list endpoint
mock_trigger_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("trigger")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains("ref: core.webhook"));
}
#[tokio::test]
async fn test_trigger_get_by_ref() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock trigger get endpoint
Mock::given(method("GET"))
.and(path("/api/v1/triggers/core.webhook"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"data": {
"id": 1,
"ref": "core.webhook",
"pack": 1,
"pack_ref": "core",
"label": "Webhook Trigger",
"description": "Webhook trigger",
"enabled": true,
"param_schema": {},
"out_schema": {},
"webhook_enabled": false,
"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("trigger")
.arg("show")
.arg("core.webhook");
cmd.assert()
.success()
.stdout(predicate::str::contains("Webhook Trigger"))
.stdout(predicate::str::contains("Webhook trigger"));
}
#[tokio::test]
async fn test_trigger_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/triggers/nonexistent.trigger").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("trigger")
.arg("show")
.arg("nonexistent.trigger");
cmd.assert()
.failure()
.stderr(predicate::str::contains("Error"));
}
// ============================================================================
// Sensor Tests
// ============================================================================
#[tokio::test]
async fn test_sensor_list_authenticated() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock sensor list endpoint
mock_sensor_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("sensor")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains("Webhook Sensor"));
}
#[tokio::test]
async fn test_sensor_list_unauthenticated() {
let fixture = TestFixture::new().await;
fixture.write_default_config();
// Mock unauthorized response
mock_unauthorized(&fixture.mock_server, "/api/v1/sensors").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("sensor")
.arg("list");
cmd.assert().failure();
}
#[tokio::test]
async fn test_sensor_list_json_output() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock sensor list endpoint
mock_sensor_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("sensor")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains(r#""ref": "core.webhook_sensor""#));
}
#[tokio::test]
async fn test_sensor_list_yaml_output() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock sensor list endpoint
mock_sensor_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("sensor")
.arg("list");
cmd.assert()
.success()
.stdout(predicate::str::contains("ref: core.webhook_sensor"));
}
#[tokio::test]
async fn test_sensor_get_by_ref() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock sensor get endpoint
Mock::given(method("GET"))
.and(path("/api/v1/sensors/core.webhook_sensor"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"data": {
"id": 1,
"ref": "core.webhook_sensor",
"pack": 1,
"pack_ref": "core",
"label": "Webhook Sensor",
"description": "Webhook sensor",
"enabled": true,
"trigger_types": ["core.webhook"],
"entry_point": "webhook_sensor.py",
"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("sensor")
.arg("show")
.arg("core.webhook_sensor");
cmd.assert()
.success()
.stdout(predicate::str::contains("Webhook Sensor"))
.stdout(predicate::str::contains("Webhook sensor"));
}
#[tokio::test]
async fn test_sensor_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/sensors/nonexistent.sensor").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("sensor")
.arg("show")
.arg("nonexistent.sensor");
cmd.assert()
.failure()
.stderr(predicate::str::contains("Error"));
}
#[tokio::test]
async fn test_sensor_list_by_pack() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock sensor list endpoint with pack filter via query parameter
mock_sensor_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("sensor")
.arg("list")
.arg("--pack")
.arg("core");
cmd.assert().success();
}
// ============================================================================
// Cross-feature Tests
// ============================================================================
#[tokio::test]
async fn test_all_list_commands_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
staging:
api_url: {}
auth_token: staging_token
refresh_token: staging_refresh
"#,
fixture.server_url(),
fixture.server_url()
);
fixture.write_config(&config);
// Mock all list endpoints
mock_rule_list(&fixture.mock_server).await;
mock_trigger_list(&fixture.mock_server).await;
mock_sensor_list(&fixture.mock_server).await;
// Test rule list with profile
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("rule")
.arg("list");
cmd.assert().success();
// Test trigger list with profile
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("trigger")
.arg("list");
cmd.assert().success();
// Test sensor list with profile
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("sensor")
.arg("list");
cmd.assert().success();
}
#[tokio::test]
async fn test_empty_list_results() {
let fixture = TestFixture::new().await;
fixture.write_authenticated_config("valid_token", "refresh_token");
// Mock empty lists
Mock::given(method("GET"))
.and(path("/api/v1/rules"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({"data": []})))
.mount(&fixture.mock_server)
.await;
Mock::given(method("GET"))
.and(path("/api/v1/triggers"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({"data": []})))
.mount(&fixture.mock_server)
.await;
Mock::given(method("GET"))
.and(path("/api/v1/sensors"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({"data": []})))
.mount(&fixture.mock_server)
.await;
// All should succeed with empty results
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("rule")
.arg("list");
cmd.assert().success();
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("trigger")
.arg("list");
cmd.assert().success();
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("sensor")
.arg("list");
cmd.assert().success();
}