test(db): enforce audit_log immutability and transactional atomicity

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 07:58:10 +02:00
parent c67b588188
commit 45aea6b702
+86
View File
@@ -0,0 +1,86 @@
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"
);
}