forked from cleveragents/cleveragents-core
195 lines
8.6 KiB
Gherkin
195 lines
8.6 KiB
Gherkin
Feature: SEC7 - Audit logging for apply
|
|
As a security-conscious developer
|
|
I want all security-relevant operations recorded in an audit log
|
|
So that I have a compliance trail of plan applies, cancellations, and changes
|
|
|
|
# ── Recording events ──────────────────────────────────────────
|
|
|
|
Scenario: Record a plan_applied event
|
|
Given a fresh audit service
|
|
When I record a "plan_applied" event with plan_id "plan-001" and project "myproject"
|
|
Then the audit log should contain 1 entry
|
|
And the entry event_type should be "plan_applied"
|
|
And the entry plan_id should be "plan-001"
|
|
And the entry project_name should be "myproject"
|
|
|
|
Scenario: Record a plan_cancelled event
|
|
Given a fresh audit service
|
|
When I record a "plan_cancelled" event with plan_id "plan-002" and details reason "user request"
|
|
Then the audit log should contain 1 entry
|
|
And the entry event_type should be "plan_cancelled"
|
|
And the entry details should contain key "reason"
|
|
|
|
Scenario: Record a resource_modified event
|
|
Given a fresh audit service
|
|
When I record a "resource_modified" event with details resource_id "res-001" and tool "file_write"
|
|
Then the audit log should contain 1 entry
|
|
And the entry event_type should be "resource_modified"
|
|
And the entry details key "tool" should be "file_write"
|
|
|
|
Scenario: Record a correction_applied event
|
|
Given a fresh audit service
|
|
When I record a "correction_applied" event with actor "openai/gpt-4o"
|
|
Then the entry actor_name should be "openai/gpt-4o"
|
|
|
|
Scenario: Record a config_changed event
|
|
Given a fresh audit service
|
|
When I record a "config_changed" event with user "hamza"
|
|
Then the entry user_identity should be "hamza"
|
|
|
|
Scenario: Record an entity_deleted event
|
|
Given a fresh audit service
|
|
When I record a "entity_deleted" event with details entity_type "resource" and entity_name "old-res"
|
|
Then the entry details key "entity_type" should be "resource"
|
|
And the entry details key "entity_name" should be "old-res"
|
|
|
|
Scenario: Record a session_created event
|
|
Given a fresh audit service
|
|
When I record a "session_created" event with session_id "sess-001"
|
|
Then the audit log should contain 1 entry
|
|
And the entry event_type should be "session_created"
|
|
And the entry details key "session_id" should be "sess-001"
|
|
|
|
Scenario: Record event with empty details
|
|
Given a fresh audit service
|
|
When I record a "plan_applied" event with no details
|
|
Then the audit log should contain 1 entry
|
|
And the entry details should be empty
|
|
|
|
Scenario: Record multiple events
|
|
Given a fresh audit service
|
|
When I record 5 "plan_applied" events
|
|
Then the audit log should contain 5 entries
|
|
|
|
# ── Querying / filtering ──────────────────────────────────────
|
|
|
|
Scenario: List entries filtered by plan_id
|
|
Given a fresh audit service with mixed events
|
|
When I list entries with plan filter "plan-A"
|
|
Then all returned entries should have plan_id "plan-A"
|
|
|
|
Scenario: List entries filtered by project
|
|
Given a fresh audit service with mixed events
|
|
When I list entries with project filter "project-X"
|
|
Then all returned entries should have project_name "project-X"
|
|
|
|
Scenario: List entries filtered by event_type
|
|
Given a fresh audit service with mixed events
|
|
When I list entries with type filter "plan_cancelled"
|
|
Then all returned entries should have event_type "plan_cancelled"
|
|
|
|
Scenario: List entries filtered by since timestamp
|
|
Given a fresh audit service with old and new events
|
|
When I list entries since "2099-01-01T00:00:00"
|
|
Then no entries should be returned
|
|
|
|
Scenario: List entries with limit
|
|
Given a fresh audit service with 20 events
|
|
When I list entries with limit 5
|
|
Then exactly 5 entries should be returned
|
|
|
|
Scenario: List entries returns newest first
|
|
Given a fresh audit service
|
|
When I record a "plan_applied" event with plan_id "first" and project "p"
|
|
And I record a "plan_applied" event with plan_id "second" and project "p"
|
|
And I list all entries
|
|
Then the first entry should have plan_id "second"
|
|
|
|
# ── Show single entry ─────────────────────────────────────────
|
|
|
|
Scenario: Show entry by ID
|
|
Given a fresh audit service
|
|
When I record a "plan_applied" event with plan_id "plan-show" and project "proj"
|
|
And I fetch the entry by its ID
|
|
Then the fetched entry should have event_type "plan_applied"
|
|
And the fetched entry should have plan_id "plan-show"
|
|
|
|
Scenario: Show non-existent entry returns None
|
|
Given a fresh audit service
|
|
When I fetch entry with ID 99999
|
|
Then no entry should be returned
|
|
|
|
# ── Count ──────────────────────────────────────────────────────
|
|
|
|
Scenario: Count returns total entries
|
|
Given a fresh audit service
|
|
When I record 7 "config_changed" events
|
|
Then the count should be 7
|
|
|
|
Scenario: Count on empty log returns zero
|
|
Given a fresh audit service
|
|
Then the count should be 0
|
|
|
|
# ── Retention / pruning ────────────────────────────────────────
|
|
|
|
Scenario: Prune deletes entries older than retention days
|
|
Given a fresh audit service with entries from 60 days ago
|
|
When I prune with retention 30 days
|
|
Then the audit log should be empty
|
|
|
|
Scenario: Prune keeps recent entries
|
|
Given a fresh audit service
|
|
When I record a "plan_applied" event with plan_id "recent" and project "p"
|
|
And I prune with retention 30 days
|
|
Then the audit log should contain 1 entry
|
|
|
|
Scenario: Prune with zero retention keeps everything
|
|
Given a fresh audit service with entries from 60 days ago
|
|
When I prune with retention 0 days
|
|
Then the audit log should not be empty
|
|
|
|
Scenario: Prune returns count of deleted entries
|
|
Given a fresh audit service with 10 entries from 90 days ago
|
|
When I prune with retention 30 days
|
|
Then the prune result should be 10
|
|
|
|
# ── Settings ───────────────────────────────────────────────────
|
|
|
|
Scenario: Default audit retention is zero (keep indefinitely)
|
|
Given default settings
|
|
Then audit_retention_days should be 0
|
|
|
|
Scenario: Audit retention can be configured via env var
|
|
Given settings with audit_retention_days 90
|
|
Then audit_retention_days should be 90
|
|
|
|
# ── Data class serialization ───────────────────────────────────
|
|
|
|
Scenario: AuditLogEntry as_dict returns all fields
|
|
Given a fresh audit service
|
|
When I record a "plan_applied" event with plan_id "p-1" and project "proj-1"
|
|
And I serialize the entry to dict
|
|
Then the dict should contain keys "id" "event_type" "plan_id" "project_name" "created_at" "details"
|
|
|
|
Scenario: AuditLogEntry details contain structured data
|
|
Given a fresh audit service
|
|
When I record a "plan_applied" event with details files_changed 3 and validation_passed true
|
|
And I serialize the entry to dict
|
|
Then the dict details key "files_changed" should be 3
|
|
|
|
# ── Edge cases ─────────────────────────────────────────────────
|
|
|
|
Scenario: Record event with all nullable fields as None
|
|
Given a fresh audit service
|
|
When I record a "plan_applied" event with all nullable fields as None
|
|
Then the entry plan_id should be None
|
|
And the entry project_name should be None
|
|
And the entry actor_name should be None
|
|
And the entry user_identity should be None
|
|
|
|
Scenario: Details with non-serializable values use str fallback
|
|
Given a fresh audit service
|
|
When I record a "plan_applied" event with datetime in details
|
|
Then the audit log should contain 1 entry
|
|
And the entry details should contain key "timestamp"
|
|
|
|
Scenario: Recording with invalid event_type raises ValueError
|
|
Given a fresh audit service
|
|
When I record an event with invalid event_type "plan_appleid"
|
|
Then a ValueError should be raised for audit service
|
|
And the audit error message should contain "plan_appleid"
|
|
|
|
Scenario: Service requires Settings instance
|
|
When I create an AuditService with invalid settings
|
|
Then a TypeError should be raised for audit service
|