[wip] cli capability parity
Some checks failed
CI / Rustfmt (push) Successful in 23s
CI / Cargo Audit & Deny (push) Successful in 30s
CI / Web Blocking Checks (push) Successful in 48s
CI / Security Blocking Checks (push) Successful in 8s
CI / Clippy (push) Failing after 1m55s
CI / Web Advisory Checks (push) Successful in 35s
CI / Security Advisory Checks (push) Successful in 37s
CI / Tests (push) Successful in 8m5s

This commit is contained in:
2026-03-06 16:58:50 -06:00
parent 48b6ca6bd7
commit 87d830f952
94 changed files with 3694 additions and 734 deletions

View File

@@ -1,4 +1,3 @@
#![cfg(feature = "integration-tests")]
//! Integration tests for health check and authentication endpoints
use axum::http::StatusCode;
@@ -8,6 +7,7 @@ use serde_json::json;
mod helpers;
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_register_debug() {
let ctx = TestContext::new()
.await
@@ -37,6 +37,7 @@ async fn test_register_debug() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_health_check() {
let ctx = TestContext::new()
.await
@@ -55,6 +56,7 @@ async fn test_health_check() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_health_detailed() {
let ctx = TestContext::new()
.await
@@ -75,6 +77,7 @@ async fn test_health_detailed() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_health_ready() {
let ctx = TestContext::new()
.await
@@ -91,6 +94,7 @@ async fn test_health_ready() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_health_live() {
let ctx = TestContext::new()
.await
@@ -107,6 +111,7 @@ async fn test_health_live() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_register_user() {
let ctx = TestContext::new()
.await
@@ -138,6 +143,7 @@ async fn test_register_user() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_register_duplicate_user() {
let ctx = TestContext::new()
.await
@@ -175,6 +181,7 @@ async fn test_register_duplicate_user() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_register_invalid_password() {
let ctx = TestContext::new()
.await
@@ -197,6 +204,7 @@ async fn test_register_invalid_password() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_login_success() {
let ctx = TestContext::new()
.await
@@ -239,6 +247,7 @@ async fn test_login_success() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_login_wrong_password() {
let ctx = TestContext::new()
.await
@@ -275,6 +284,7 @@ async fn test_login_wrong_password() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_login_nonexistent_user() {
let ctx = TestContext::new()
.await
@@ -296,6 +306,7 @@ async fn test_login_nonexistent_user() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_get_current_user() {
let ctx = TestContext::new()
.await
@@ -319,6 +330,7 @@ async fn test_get_current_user() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_get_current_user_unauthorized() {
let ctx = TestContext::new()
.await
@@ -333,6 +345,7 @@ async fn test_get_current_user_unauthorized() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_get_current_user_invalid_token() {
let ctx = TestContext::new()
.await
@@ -347,6 +360,7 @@ async fn test_get_current_user_invalid_token() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_refresh_token() {
let ctx = TestContext::new()
.await
@@ -397,6 +411,7 @@ async fn test_refresh_token() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_refresh_with_invalid_token() {
let ctx = TestContext::new()
.await

View File

@@ -1,4 +1,3 @@
#![cfg(feature = "integration-tests")]
//! Integration tests for pack registry system
//!
//! This module tests:
@@ -128,6 +127,7 @@ actions:
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_install_pack_from_local_directory() -> Result<()> {
let ctx = TestContext::new().await?.with_auth().await?;
let token = ctx.token().unwrap();
@@ -167,6 +167,7 @@ async fn test_install_pack_from_local_directory() -> Result<()> {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_install_pack_with_dependency_validation_success() -> Result<()> {
let ctx = TestContext::new().await?.with_auth().await?;
let token = ctx.token().unwrap();
@@ -217,6 +218,7 @@ async fn test_install_pack_with_dependency_validation_success() -> Result<()> {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_install_pack_with_missing_dependency_fails() -> Result<()> {
let ctx = TestContext::new().await?.with_auth().await?;
let token = ctx.token().unwrap();
@@ -256,6 +258,7 @@ async fn test_install_pack_with_missing_dependency_fails() -> Result<()> {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_install_pack_skip_deps_bypasses_validation() -> Result<()> {
let ctx = TestContext::new().await?.with_auth().await?;
let token = ctx.token().unwrap();
@@ -291,6 +294,7 @@ async fn test_install_pack_skip_deps_bypasses_validation() -> Result<()> {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_install_pack_with_runtime_validation() -> Result<()> {
let ctx = TestContext::new().await?.with_auth().await?;
let token = ctx.token().unwrap();
@@ -324,6 +328,7 @@ async fn test_install_pack_with_runtime_validation() -> Result<()> {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_install_pack_metadata_tracking() -> Result<()> {
let ctx = TestContext::new().await?.with_auth().await?;
let token = ctx.token().unwrap();
@@ -373,6 +378,7 @@ async fn test_install_pack_metadata_tracking() -> Result<()> {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_install_pack_force_reinstall() -> Result<()> {
let ctx = TestContext::new().await?.with_auth().await?;
let token = ctx.token().unwrap();
@@ -425,6 +431,7 @@ async fn test_install_pack_force_reinstall() -> Result<()> {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_install_pack_storage_path_created() -> Result<()> {
let ctx = TestContext::new().await?.with_auth().await?;
let token = ctx.token().unwrap();
@@ -475,6 +482,7 @@ async fn test_install_pack_storage_path_created() -> Result<()> {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_install_pack_invalid_source() -> Result<()> {
let ctx = TestContext::new().await?.with_auth().await?;
let token = ctx.token().unwrap();
@@ -505,6 +513,7 @@ async fn test_install_pack_invalid_source() -> Result<()> {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_install_pack_missing_pack_yaml() -> Result<()> {
let ctx = TestContext::new().await?.with_auth().await?;
let token = ctx.token().unwrap();
@@ -539,6 +548,7 @@ async fn test_install_pack_missing_pack_yaml() -> Result<()> {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_install_pack_invalid_pack_yaml() -> Result<()> {
let ctx = TestContext::new().await?.with_auth().await?;
let token = ctx.token().unwrap();
@@ -567,6 +577,7 @@ async fn test_install_pack_invalid_pack_yaml() -> Result<()> {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_install_pack_without_auth_fails() -> Result<()> {
let ctx = TestContext::new().await?; // No auth
@@ -592,6 +603,7 @@ async fn test_install_pack_without_auth_fails() -> Result<()> {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_multiple_pack_installations() -> Result<()> {
let ctx = TestContext::new().await?.with_auth().await?;
let token = ctx.token().unwrap();
@@ -639,6 +651,7 @@ async fn test_multiple_pack_installations() -> Result<()> {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_install_pack_version_upgrade() -> Result<()> {
let ctx = TestContext::new().await?.with_auth().await?;
let token = ctx.token().unwrap();

View File

@@ -1,4 +1,3 @@
#![cfg(feature = "integration-tests")]
//! Integration tests for pack workflow sync and validation
mod helpers;
@@ -59,6 +58,7 @@ tasks:
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_sync_pack_workflows_endpoint() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -95,6 +95,7 @@ async fn test_sync_pack_workflows_endpoint() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_validate_pack_workflows_endpoint() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -121,6 +122,7 @@ async fn test_validate_pack_workflows_endpoint() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_sync_nonexistent_pack_returns_404() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -137,6 +139,7 @@ async fn test_sync_nonexistent_pack_returns_404() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_validate_nonexistent_pack_returns_404() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -153,6 +156,7 @@ async fn test_validate_nonexistent_pack_returns_404() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_sync_workflows_requires_authentication() {
let ctx = TestContext::new().await.unwrap();
@@ -180,6 +184,7 @@ async fn test_sync_workflows_requires_authentication() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_validate_workflows_requires_authentication() {
let ctx = TestContext::new().await.unwrap();
@@ -207,6 +212,7 @@ async fn test_validate_workflows_requires_authentication() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_pack_creation_with_auto_sync() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -237,6 +243,7 @@ async fn test_pack_creation_with_auto_sync() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_pack_update_with_auto_resync() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();

View File

@@ -1,4 +1,3 @@
#![cfg(feature = "integration-tests")]
//! Integration tests for SSE execution stream endpoint
//!
//! These tests verify that:
@@ -87,6 +86,7 @@ async fn create_test_execution(pool: &PgPool, action_id: i64) -> Result<Executio
/// Run with: cargo test test_sse_stream_receives_execution_updates -- --ignored --nocapture
/// After starting: cargo run -p attune-api -- -c config.test.yaml
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_sse_stream_receives_execution_updates() -> Result<()> {
// Set up test context with auth
let ctx = TestContext::new().await?.with_auth().await?;
@@ -225,6 +225,7 @@ async fn test_sse_stream_receives_execution_updates() -> Result<()> {
/// Test that SSE stream correctly filters by execution_id
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_sse_stream_filters_by_execution_id() -> Result<()> {
// Set up test context with auth
let ctx = TestContext::new().await?.with_auth().await?;
@@ -326,6 +327,7 @@ async fn test_sse_stream_filters_by_execution_id() -> Result<()> {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_sse_stream_requires_authentication() -> Result<()> {
// Try to connect without token
let sse_url = "http://localhost:8080/api/v1/executions/stream";
@@ -371,6 +373,7 @@ async fn test_sse_stream_requires_authentication() -> Result<()> {
/// Test streaming all executions (no filter)
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_sse_stream_all_executions() -> Result<()> {
// Set up test context with auth
let ctx = TestContext::new().await?.with_auth().await?;
@@ -463,6 +466,7 @@ async fn test_sse_stream_all_executions() -> Result<()> {
/// Test that PostgreSQL NOTIFY triggers actually fire
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_postgresql_notify_trigger_fires() -> Result<()> {
let ctx = TestContext::new().await?;

View File

@@ -1,4 +1,3 @@
#![cfg(feature = "integration-tests")]
//! Integration tests for webhook API endpoints
use attune_api::{AppState, Server};
@@ -109,6 +108,7 @@ async fn get_auth_token(app: &axum::Router, username: &str, password: &str) -> S
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_enable_webhook() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -151,6 +151,7 @@ async fn test_enable_webhook() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_disable_webhook() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -201,6 +202,7 @@ async fn test_disable_webhook() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_regenerate_webhook_key() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -252,6 +254,7 @@ async fn test_regenerate_webhook_key() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_regenerate_webhook_key_not_enabled() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -288,6 +291,7 @@ async fn test_regenerate_webhook_key_not_enabled() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_receive_webhook() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -358,6 +362,7 @@ async fn test_receive_webhook() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_receive_webhook_invalid_key() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state));
@@ -387,6 +392,7 @@ async fn test_receive_webhook_invalid_key() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_receive_webhook_disabled() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -436,6 +442,7 @@ async fn test_receive_webhook_disabled() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_requires_auth_for_management() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -468,6 +475,7 @@ async fn test_webhook_requires_auth_for_management() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_receive_webhook_minimal_payload() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));

View File

@@ -1,4 +1,3 @@
#![cfg(feature = "integration-tests")]
//! Comprehensive integration tests for webhook security features (Phase 3)
//!
//! Tests cover:
@@ -123,6 +122,7 @@ fn generate_hmac_signature(payload: &[u8], secret: &str, algorithm: &str) -> Str
// ============================================================================
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_hmac_sha256_valid() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -189,6 +189,7 @@ async fn test_webhook_hmac_sha256_valid() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_hmac_sha512_valid() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -245,6 +246,7 @@ async fn test_webhook_hmac_sha512_valid() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_hmac_invalid_signature() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -300,6 +302,7 @@ async fn test_webhook_hmac_invalid_signature() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_hmac_missing_signature() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -352,6 +355,7 @@ async fn test_webhook_hmac_missing_signature() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_hmac_wrong_secret() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -414,6 +418,7 @@ async fn test_webhook_hmac_wrong_secret() {
// ============================================================================
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_rate_limit_enforced() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -489,6 +494,7 @@ async fn test_webhook_rate_limit_enforced() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_rate_limit_disabled() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -535,6 +541,7 @@ async fn test_webhook_rate_limit_disabled() {
// ============================================================================
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_ip_whitelist_allowed() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -605,6 +612,7 @@ async fn test_webhook_ip_whitelist_allowed() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_ip_whitelist_blocked() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -661,6 +669,7 @@ async fn test_webhook_ip_whitelist_blocked() {
// ============================================================================
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_payload_size_limit_enforced() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));
@@ -711,6 +720,7 @@ async fn test_webhook_payload_size_limit_enforced() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_payload_size_within_limit() {
let state = setup_test_state().await;
let server = Server::new(std::sync::Arc::new(state.clone()));

View File

@@ -1,4 +1,3 @@
#![cfg(feature = "integration-tests")]
//! Integration tests for workflow API endpoints
use attune_common::repositories::{
@@ -20,6 +19,7 @@ fn unique_pack_name() -> String {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_create_workflow_success() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -65,6 +65,7 @@ async fn test_create_workflow_success() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_create_workflow_duplicate_ref() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -110,6 +111,7 @@ async fn test_create_workflow_duplicate_ref() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_create_workflow_pack_not_found() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -132,6 +134,7 @@ async fn test_create_workflow_pack_not_found() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_get_workflow_by_ref() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -170,6 +173,7 @@ async fn test_get_workflow_by_ref() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_get_workflow_not_found() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -182,6 +186,7 @@ async fn test_get_workflow_not_found() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_list_workflows() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -228,6 +233,7 @@ async fn test_list_workflows() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_list_workflows_by_pack() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -295,6 +301,7 @@ async fn test_list_workflows_by_pack() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_list_workflows_with_filters() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -362,6 +369,7 @@ async fn test_list_workflows_with_filters() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_update_workflow() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -410,6 +418,7 @@ async fn test_update_workflow() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_update_workflow_not_found() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -428,6 +437,7 @@ async fn test_update_workflow_not_found() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_delete_workflow() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -469,6 +479,7 @@ async fn test_delete_workflow() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_delete_workflow_not_found() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();
@@ -481,6 +492,7 @@ async fn test_delete_workflow_not_found() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_create_workflow_requires_auth() {
let ctx = TestContext::new().await.unwrap();
@@ -505,6 +517,7 @@ async fn test_create_workflow_requires_auth() {
}
#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_workflow_validation() {
let ctx = TestContext::new().await.unwrap().with_auth().await.unwrap();