1433 lines
44 KiB
Rust
1433 lines
44 KiB
Rust
//! Integration tests for Enforcement repository
|
|
//!
|
|
//! These tests verify CRUD operations, queries, and constraints
|
|
//! for the Enforcement repository.
|
|
|
|
mod helpers;
|
|
|
|
use attune_common::{
|
|
models::enums::{EnforcementCondition, EnforcementStatus},
|
|
repositories::{
|
|
event::{CreateEnforcementInput, EnforcementRepository, UpdateEnforcementInput},
|
|
Create, Delete, FindById, List, Update,
|
|
},
|
|
Error,
|
|
};
|
|
use helpers::*;
|
|
use serde_json::json;
|
|
|
|
// ============================================================================
|
|
// CREATE Tests
|
|
// ============================================================================
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_create_enforcement_minimal() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
// Create pack, trigger, action, and rule
|
|
let pack = PackFixture::new_unique("enforcement_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Create enforcement with minimal fields
|
|
let input = CreateEnforcementInput {
|
|
rule: Some(rule.id),
|
|
rule_ref: rule.r#ref.clone(),
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
config: None,
|
|
event: None,
|
|
status: EnforcementStatus::Created,
|
|
payload: json!({}),
|
|
condition: EnforcementCondition::All,
|
|
conditions: json!([]),
|
|
};
|
|
|
|
let enforcement = EnforcementRepository::create(&pool, input).await.unwrap();
|
|
|
|
assert!(enforcement.id > 0);
|
|
assert_eq!(enforcement.rule, Some(rule.id));
|
|
assert_eq!(enforcement.rule_ref, rule.r#ref);
|
|
assert_eq!(enforcement.trigger_ref, trigger.r#ref);
|
|
assert_eq!(enforcement.config, None);
|
|
assert_eq!(enforcement.event, None);
|
|
assert_eq!(enforcement.status, EnforcementStatus::Created);
|
|
assert_eq!(enforcement.payload, json!({}));
|
|
assert_eq!(enforcement.condition, EnforcementCondition::All);
|
|
assert_eq!(enforcement.conditions, json!([]));
|
|
assert!(enforcement.created.timestamp() > 0);
|
|
assert_eq!(enforcement.resolved_at, None); // Not yet resolved
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_create_enforcement_with_event() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("event_enforcement_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Create an event
|
|
let event = EventFixture::new_unique(Some(trigger.id), &trigger.r#ref)
|
|
.with_payload(json!({"event": "data"}))
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let input = CreateEnforcementInput {
|
|
rule: Some(rule.id),
|
|
rule_ref: rule.r#ref.clone(),
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
config: None,
|
|
event: Some(event.id),
|
|
status: EnforcementStatus::Created,
|
|
payload: json!({"from": "event"}),
|
|
condition: EnforcementCondition::All,
|
|
conditions: json!([]),
|
|
};
|
|
|
|
let enforcement = EnforcementRepository::create(&pool, input).await.unwrap();
|
|
|
|
assert_eq!(enforcement.event, Some(event.id));
|
|
assert_eq!(enforcement.payload, json!({"from": "event"}));
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_create_enforcement_with_conditions() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("conditions_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let conditions = json!([
|
|
{"equals": {"event.status": "success"}},
|
|
{"greater_than": {"event.priority": 5}}
|
|
]);
|
|
|
|
let input = CreateEnforcementInput {
|
|
rule: Some(rule.id),
|
|
rule_ref: rule.r#ref.clone(),
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
config: None,
|
|
event: None,
|
|
status: EnforcementStatus::Created,
|
|
payload: json!({}),
|
|
condition: EnforcementCondition::All,
|
|
conditions: conditions.clone(),
|
|
};
|
|
|
|
let enforcement = EnforcementRepository::create(&pool, input).await.unwrap();
|
|
|
|
assert_eq!(enforcement.condition, EnforcementCondition::All);
|
|
assert_eq!(enforcement.conditions, conditions);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_create_enforcement_with_any_condition() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("any_condition_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let input = CreateEnforcementInput {
|
|
rule: Some(rule.id),
|
|
rule_ref: rule.r#ref.clone(),
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
config: None,
|
|
event: None,
|
|
status: EnforcementStatus::Created,
|
|
payload: json!({}),
|
|
condition: EnforcementCondition::Any,
|
|
conditions: json!([
|
|
{"equals": {"event.type": "webhook"}},
|
|
{"equals": {"event.type": "timer"}}
|
|
]),
|
|
};
|
|
|
|
let enforcement = EnforcementRepository::create(&pool, input).await.unwrap();
|
|
|
|
assert_eq!(enforcement.condition, EnforcementCondition::Any);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_create_enforcement_without_rule_id() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
// Enforcements can be created without a rule ID (rule may have been deleted)
|
|
let input = CreateEnforcementInput {
|
|
rule: None,
|
|
rule_ref: "deleted.rule".to_string(),
|
|
trigger_ref: "some.trigger".to_string(),
|
|
config: None,
|
|
event: None,
|
|
status: EnforcementStatus::Created,
|
|
payload: json!({"reason": "rule was deleted"}),
|
|
condition: EnforcementCondition::All,
|
|
conditions: json!([]),
|
|
};
|
|
|
|
let enforcement = EnforcementRepository::create(&pool, input).await.unwrap();
|
|
|
|
assert_eq!(enforcement.rule, None);
|
|
assert_eq!(enforcement.rule_ref, "deleted.rule");
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_create_enforcement_with_invalid_rule_fails() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
// Try to create enforcement with non-existent rule ID
|
|
let input = CreateEnforcementInput {
|
|
rule: Some(99999),
|
|
rule_ref: "nonexistent.rule".to_string(),
|
|
trigger_ref: "some.trigger".to_string(),
|
|
config: None,
|
|
event: None,
|
|
status: EnforcementStatus::Created,
|
|
payload: json!({}),
|
|
condition: EnforcementCondition::All,
|
|
conditions: json!([]),
|
|
};
|
|
|
|
let result = EnforcementRepository::create(&pool, input).await;
|
|
|
|
assert!(result.is_err());
|
|
// Foreign key constraint violation
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_create_enforcement_with_nonexistent_event_succeeds() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
// The enforcement.event column has no FK constraint (event is a hypertable
|
|
// and hypertables cannot be FK targets). A non-existent event ID is accepted
|
|
// as a dangling reference.
|
|
let input = CreateEnforcementInput {
|
|
rule: None,
|
|
rule_ref: "some.rule".to_string(),
|
|
trigger_ref: "some.trigger".to_string(),
|
|
config: None,
|
|
event: Some(99999),
|
|
status: EnforcementStatus::Created,
|
|
payload: json!({}),
|
|
condition: EnforcementCondition::All,
|
|
conditions: json!([]),
|
|
};
|
|
|
|
let result = EnforcementRepository::create(&pool, input).await;
|
|
|
|
assert!(result.is_ok());
|
|
let enforcement = result.unwrap();
|
|
assert_eq!(enforcement.event, Some(99999));
|
|
}
|
|
|
|
// ============================================================================
|
|
// READ Tests
|
|
// ============================================================================
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_find_enforcement_by_id() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("find_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let created_enforcement =
|
|
EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.with_payload(json!({"test": "data"}))
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let found = EnforcementRepository::find_by_id(&pool, created_enforcement.id)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert!(found.is_some());
|
|
let enforcement = found.unwrap();
|
|
assert_eq!(enforcement.id, created_enforcement.id);
|
|
assert_eq!(enforcement.rule, created_enforcement.rule);
|
|
assert_eq!(enforcement.rule_ref, created_enforcement.rule_ref);
|
|
assert_eq!(enforcement.status, created_enforcement.status);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_find_enforcement_by_id_not_found() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let result = EnforcementRepository::find_by_id(&pool, 99999)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert!(result.is_none());
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_get_enforcement_by_id() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("get_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let created_enforcement =
|
|
EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let enforcement = EnforcementRepository::get_by_id(&pool, created_enforcement.id)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(enforcement.id, created_enforcement.id);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_get_enforcement_by_id_not_found() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let result = EnforcementRepository::get_by_id(&pool, 99999).await;
|
|
|
|
assert!(result.is_err());
|
|
assert!(matches!(result.unwrap_err(), Error::NotFound { .. }));
|
|
}
|
|
|
|
// ============================================================================
|
|
// LIST Tests
|
|
// ============================================================================
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_list_enforcements_empty() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let enforcements = EnforcementRepository::list(&pool).await.unwrap();
|
|
// May have enforcements from other tests, just verify we can list without error
|
|
drop(enforcements);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_list_enforcements() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("list_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let before_count = EnforcementRepository::list(&pool).await.unwrap().len();
|
|
|
|
// Create multiple enforcements
|
|
let mut created_ids = vec![];
|
|
for i in 0..3 {
|
|
let enforcement =
|
|
EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.with_payload(json!({"index": i}))
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
created_ids.push(enforcement.id);
|
|
}
|
|
|
|
let enforcements = EnforcementRepository::list(&pool).await.unwrap();
|
|
|
|
assert!(enforcements.len() >= before_count + 3);
|
|
// Verify our enforcements are in the list
|
|
let our_enforcements: Vec<_> = enforcements
|
|
.iter()
|
|
.filter(|e| created_ids.contains(&e.id))
|
|
.collect();
|
|
assert_eq!(our_enforcements.len(), 3);
|
|
}
|
|
|
|
// ============================================================================
|
|
// UPDATE Tests
|
|
// ============================================================================
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_update_enforcement_status() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("update_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let enforcement = EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.with_status(EnforcementStatus::Created)
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let now = chrono::Utc::now();
|
|
let input = UpdateEnforcementInput {
|
|
status: Some(EnforcementStatus::Processed),
|
|
payload: None,
|
|
resolved_at: Some(now),
|
|
};
|
|
|
|
let updated = EnforcementRepository::update(&pool, enforcement.id, input)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(updated.id, enforcement.id);
|
|
assert_eq!(updated.status, EnforcementStatus::Processed);
|
|
assert!(updated.resolved_at.is_some());
|
|
assert!(updated.resolved_at.unwrap() >= enforcement.created);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_update_enforcement_status_transitions() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("status_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let enforcement = EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Test status transitions: Created -> Processed
|
|
let now = chrono::Utc::now();
|
|
let updated = EnforcementRepository::update(
|
|
&pool,
|
|
enforcement.id,
|
|
UpdateEnforcementInput {
|
|
status: Some(EnforcementStatus::Processed),
|
|
payload: None,
|
|
resolved_at: Some(now),
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(updated.status, EnforcementStatus::Processed);
|
|
assert!(updated.resolved_at.is_some());
|
|
|
|
// Test status transition: Processed -> Disabled (although unusual)
|
|
let updated = EnforcementRepository::update(
|
|
&pool,
|
|
enforcement.id,
|
|
UpdateEnforcementInput {
|
|
status: Some(EnforcementStatus::Disabled),
|
|
payload: None,
|
|
resolved_at: None,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(updated.status, EnforcementStatus::Disabled);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_update_enforcement_payload() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("payload_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let enforcement = EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.with_payload(json!({"initial": "data"}))
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let new_payload = json!({"updated": "data", "version": 2});
|
|
let input = UpdateEnforcementInput {
|
|
status: None,
|
|
payload: Some(new_payload.clone()),
|
|
resolved_at: None,
|
|
};
|
|
|
|
let updated = EnforcementRepository::update(&pool, enforcement.id, input)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(updated.payload, new_payload);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_update_enforcement_both_fields() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("both_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let enforcement = EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let now = chrono::Utc::now();
|
|
let new_payload = json!({"result": "success"});
|
|
let input = UpdateEnforcementInput {
|
|
status: Some(EnforcementStatus::Processed),
|
|
payload: Some(new_payload.clone()),
|
|
resolved_at: Some(now),
|
|
};
|
|
|
|
let updated = EnforcementRepository::update(&pool, enforcement.id, input)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(updated.status, EnforcementStatus::Processed);
|
|
assert_eq!(updated.payload, new_payload);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_update_enforcement_no_changes() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("nochange_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let enforcement = EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.with_payload(json!({"test": "data"}))
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let input = UpdateEnforcementInput {
|
|
status: None,
|
|
payload: None,
|
|
resolved_at: None,
|
|
};
|
|
|
|
let result = EnforcementRepository::update(&pool, enforcement.id, input)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Should return existing enforcement without updating
|
|
assert_eq!(result.id, enforcement.id);
|
|
assert_eq!(result.status, enforcement.status);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_update_enforcement_not_found() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let input = UpdateEnforcementInput {
|
|
status: Some(EnforcementStatus::Processed),
|
|
payload: None,
|
|
resolved_at: Some(chrono::Utc::now()),
|
|
};
|
|
|
|
let result = EnforcementRepository::update(&pool, 99999, input).await;
|
|
|
|
// When updating non-existent entity with changes, SQLx returns RowNotFound error
|
|
assert!(result.is_err());
|
|
}
|
|
|
|
// ============================================================================
|
|
// DELETE Tests
|
|
// ============================================================================
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_delete_enforcement() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("delete_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let enforcement = EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let deleted = EnforcementRepository::delete(&pool, enforcement.id)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert!(deleted);
|
|
|
|
// Verify it's gone
|
|
let found = EnforcementRepository::find_by_id(&pool, enforcement.id)
|
|
.await
|
|
.unwrap();
|
|
assert!(found.is_none());
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_delete_enforcement_not_found() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let deleted = EnforcementRepository::delete(&pool, 99999).await.unwrap();
|
|
|
|
assert!(!deleted);
|
|
}
|
|
|
|
// ============================================================================
|
|
// SPECIALIZED QUERY Tests
|
|
// ============================================================================
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_find_enforcements_by_rule() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("rule_query_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule1 = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.rule1", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Rule 1".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let rule2 = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.rule2", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Rule 2".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Create enforcements for rule1
|
|
for i in 0..3 {
|
|
EnforcementFixture::new_unique(Some(rule1.id), &rule1.r#ref, &trigger.r#ref)
|
|
.with_payload(json!({"rule": 1, "index": i}))
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
// Create enforcements for rule2
|
|
for i in 0..2 {
|
|
EnforcementFixture::new_unique(Some(rule2.id), &rule2.r#ref, &trigger.r#ref)
|
|
.with_payload(json!({"rule": 2, "index": i}))
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
let enforcements = EnforcementRepository::find_by_rule(&pool, rule1.id)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(enforcements.len(), 3);
|
|
for enforcement in &enforcements {
|
|
assert_eq!(enforcement.rule, Some(rule1.id));
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_find_enforcements_by_status() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("status_query_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Create enforcements with different statuses
|
|
let enf1 = EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.with_status(EnforcementStatus::Created)
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let enf2 = EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.with_status(EnforcementStatus::Processed)
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let enf3 = EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.with_status(EnforcementStatus::Processed)
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let processed_enforcements =
|
|
EnforcementRepository::find_by_status(&pool, EnforcementStatus::Processed)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Filter to only our test enforcements
|
|
let our_processed: Vec<_> = processed_enforcements
|
|
.iter()
|
|
.filter(|e| e.id == enf2.id || e.id == enf3.id)
|
|
.collect();
|
|
assert_eq!(our_processed.len(), 2);
|
|
for enforcement in &our_processed {
|
|
assert_eq!(enforcement.status, EnforcementStatus::Processed);
|
|
}
|
|
|
|
let created_enforcements =
|
|
EnforcementRepository::find_by_status(&pool, EnforcementStatus::Created)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Verify our created enforcement is in the list
|
|
let our_created: Vec<_> = created_enforcements
|
|
.iter()
|
|
.filter(|e| e.id == enf1.id)
|
|
.collect();
|
|
assert_eq!(our_created.len(), 1);
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_find_enforcements_by_event() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("event_query_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Create events
|
|
let event1 = EventFixture::new_unique(Some(trigger.id), &trigger.r#ref)
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let event2 = EventFixture::new_unique(Some(trigger.id), &trigger.r#ref)
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Create enforcements for event1
|
|
for i in 0..3 {
|
|
EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.with_event(event1.id)
|
|
.with_payload(json!({"event": 1, "index": i}))
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
// Create enforcement for event2
|
|
EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.with_event(event2.id)
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let enforcements = EnforcementRepository::find_by_event(&pool, event1.id)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(enforcements.len(), 3);
|
|
for enforcement in &enforcements {
|
|
assert_eq!(enforcement.event, Some(event1.id));
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// CASCADE & RELATIONSHIP Tests
|
|
// ============================================================================
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_delete_rule_sets_enforcement_rule_to_null() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("cascade_rule_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let enforcement = EnforcementFixture::new(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Delete the rule
|
|
use attune_common::repositories::Delete;
|
|
RuleRepository::delete(&pool, rule.id).await.unwrap();
|
|
|
|
// Enforcement should still exist but with NULL rule (ON DELETE SET NULL)
|
|
let found_enforcement = EnforcementRepository::find_by_id(&pool, enforcement.id)
|
|
.await
|
|
.unwrap()
|
|
.unwrap();
|
|
|
|
assert_eq!(found_enforcement.rule, None);
|
|
assert_eq!(found_enforcement.rule_ref, rule.r#ref); // rule_ref preserved
|
|
}
|
|
|
|
// ============================================================================
|
|
// TIMESTAMP Tests
|
|
// ============================================================================
|
|
|
|
#[tokio::test]
|
|
#[ignore = "integration test — requires database"]
|
|
async fn test_enforcement_resolved_at_lifecycle() {
|
|
let pool = create_test_pool().await.unwrap();
|
|
|
|
let pack = PackFixture::new_unique("timestamp_pack")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let trigger = TriggerFixture::new_unique(Some(pack.id), Some(pack.r#ref.clone()), "webhook")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
use attune_common::repositories::rule::{CreateRuleInput, RuleRepository};
|
|
let rule = RuleRepository::create(
|
|
&pool,
|
|
CreateRuleInput {
|
|
r#ref: format!("{}.test_rule", pack.r#ref),
|
|
pack: pack.id,
|
|
pack_ref: pack.r#ref.clone(),
|
|
label: "Test Rule".to_string(),
|
|
description: Some("Test".to_string()),
|
|
action: action.id,
|
|
action_ref: action.r#ref.clone(),
|
|
trigger: trigger.id,
|
|
trigger_ref: trigger.r#ref.clone(),
|
|
conditions: json!({}),
|
|
action_params: json!({}),
|
|
trigger_params: json!({}),
|
|
enabled: true,
|
|
is_adhoc: false,
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let enforcement = EnforcementFixture::new_unique(Some(rule.id), &rule.r#ref, &trigger.r#ref)
|
|
.create(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Initially, resolved_at is NULL
|
|
assert!(enforcement.created.timestamp() > 0);
|
|
assert_eq!(enforcement.resolved_at, None);
|
|
|
|
// Resolve the enforcement and verify resolved_at is set
|
|
let resolved_time = chrono::Utc::now();
|
|
let input = UpdateEnforcementInput {
|
|
status: Some(EnforcementStatus::Processed),
|
|
payload: None,
|
|
resolved_at: Some(resolved_time),
|
|
};
|
|
|
|
let updated = EnforcementRepository::update(&pool, enforcement.id, input)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(updated.created, enforcement.created); // created unchanged
|
|
assert!(updated.resolved_at.is_some());
|
|
assert!(updated.resolved_at.unwrap() >= enforcement.created);
|
|
}
|