45aea6b702
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
87 lines
2.2 KiB
Rust
87 lines
2.2 KiB
Rust
use db::Db;
|
|
use db::audit;
|
|
use domain::{AuditAction, AuditActor, NewAuditEvent};
|
|
use sqlx::PgPool;
|
|
use uuid::Uuid;
|
|
|
|
fn sample() -> NewAuditEvent {
|
|
NewAuditEvent {
|
|
actor: AuditActor::System,
|
|
action: AuditAction::Created,
|
|
entity_type: "object".into(),
|
|
entity_id: Uuid::new_v4(),
|
|
changes: vec![],
|
|
}
|
|
}
|
|
|
|
async fn count(pool: &PgPool) -> i64 {
|
|
sqlx::query_scalar("SELECT count(*) FROM audit_log")
|
|
.fetch_one(pool)
|
|
.await
|
|
.unwrap()
|
|
}
|
|
|
|
#[sqlx::test]
|
|
async fn update_delete_truncate_are_rejected(pool: PgPool) {
|
|
let db = Db::from_pool(pool);
|
|
|
|
audit::record(db.pool(), &sample()).await.unwrap();
|
|
|
|
// Each failing statement poisons its connection (Postgres enters aborted-transaction
|
|
// state). Acquire a fresh connection per statement so later assertions are independent.
|
|
let updated = sqlx::query("UPDATE audit_log SET action = 'deleted'")
|
|
.execute(db.pool())
|
|
.await;
|
|
|
|
assert!(updated.is_err(), "UPDATE must be rejected by the trigger");
|
|
|
|
let deleted = sqlx::query("DELETE FROM audit_log")
|
|
.execute(db.pool())
|
|
.await;
|
|
|
|
assert!(deleted.is_err(), "DELETE must be rejected by the trigger");
|
|
|
|
let truncated = sqlx::query("TRUNCATE audit_log").execute(db.pool()).await;
|
|
|
|
assert!(
|
|
truncated.is_err(),
|
|
"TRUNCATE must be rejected by the trigger"
|
|
);
|
|
|
|
assert_eq!(count(db.pool()).await, 1, "the row is still there");
|
|
}
|
|
|
|
#[sqlx::test]
|
|
async fn record_rolls_back_with_caller_transaction(pool: PgPool) {
|
|
let db = Db::from_pool(pool);
|
|
|
|
let mut tx = db.pool().begin().await.unwrap();
|
|
|
|
audit::record(&mut *tx, &sample()).await.unwrap();
|
|
|
|
tx.rollback().await.unwrap();
|
|
|
|
assert_eq!(
|
|
count(db.pool()).await,
|
|
0,
|
|
"a rolled-back audit record must not persist"
|
|
);
|
|
}
|
|
|
|
#[sqlx::test]
|
|
async fn record_commits_with_caller_transaction(pool: PgPool) {
|
|
let db = Db::from_pool(pool);
|
|
|
|
let mut tx = db.pool().begin().await.unwrap();
|
|
|
|
audit::record(&mut *tx, &sample()).await.unwrap();
|
|
|
|
tx.commit().await.unwrap();
|
|
|
|
assert_eq!(
|
|
count(db.pool()).await,
|
|
1,
|
|
"a committed audit record persists"
|
|
);
|
|
}
|