//// A full inbox must reject new webhook deliveries rather than silently lose mail. import albedo/daemon/store import albedo/harness/extensions/webhooks/ledger as webhooks import gleam/bit_array import gleam/dynamic/decode import gleam/option.{None} import gleeunit/should import sqlight @external(erlang, "albedo_webhooks_test_support", "sign") fn sign(body: BitArray, secret: String) -> String fn headers(signature: String) -> List(#(String, String)) { [#("x-albedo-signature", signature)] } fn database() -> store.Store { let assert Ok(db) = store.start( ":memory:", "PRAGMA foreign_keys=ON; CREATE TABLE sessions(id TEXT PRIMARY KEY); INSERT INTO sessions VALUES('infra'),('other');", ) let assert Ok(Nil) = webhooks.initialise(db) db } pub fn full_inbox_refuses_new_delivery_test() -> Nil { let db = database() let assert Ok(webhooks.Provisioned(hook, key)) = webhooks.create(db, webhooks.Human, "infra", "outage", None) store.query(db, fn(connection) { sqlight.query( "WITH RECURSIVE nums(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM nums WHERE n<1000) INSERT INTO mail(id,recipient,sender_name,kind,body,created_at) SELECT 'seed-'||n,'infra','outage','webhook','x',0 FROM nums", connection, [], decode.dynamic, ) }) |> should.be_ok let body = bit_array.from_string("outage") webhooks.accept(db, hook.id, headers(sign(body, key)), body, None) |> should.equal(Error(webhooks.Overloaded)) // The refused delivery left no half behind. store.query(db, fn(connection) { sqlight.query( "SELECT count(*) FROM webhook_deliveries", connection, [], decode.field(0, decode.int, decode.success), ) }) |> should.equal(Ok([0])) store.close(db) }