Something went wrong. Try again.
small gleam coding and persistent agent daemon with a detachable cli and a canvas ui forked from okami.mom/albedo
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155//// Timeout and reply/death ordering require process monitors and controlled//// acknowledgments that the external E2E API cannot arrange.
import albedo/actor_callimport gleam/erlang/process.{type Subject}import gleam/otp/actorimport gleeunit/should
type Ping { Ping(reply: Subject(String)) Die}
fn server(mode: fn(message) -> actor.Next(Nil, message)) -> Subject(message) { let assert Ok(started) = actor.new(Nil) |> actor.on_message(fn(_, message) { mode(message) }) |> actor.start started.data}
fn answers(message: Ping) -> actor.Next(Nil, Ping) { case message { Ping(reply) -> { process.send(reply, "pong") actor.continue(Nil) } _ -> actor.continue(Nil) }}
fn never_replies(_message: Ping) -> actor.Next(Nil, Ping) { actor.continue(Nil)}
fn dies_on_call(message: Ping) -> actor.Next(Nil, Ping) { case message { Die -> actor.stop() _ -> actor.continue(Nil) }}
pub fn a_reply_in_time_is_returned_test() -> Nil { let subject = server(answers) actor_call.try_call(subject, 2000, Ping) |> should.equal(Ok("pong"))}
pub fn a_stalled_callee_reports_a_timeout_test() -> Nil { let subject = server(never_replies) actor_call.try_call(subject, 20, Ping) |> should.equal(Error(actor_call.TimedOut))}
pub fn a_dead_callee_reports_the_death_test() -> Nil { let subject = server(dies_on_call) actor_call.try_call(subject, 2000, fn(_reply) { Die }) |> should.equal(Error(actor_call.CalleeDown))}
pub fn a_reply_that_races_the_callee_exit_still_arrives_test() -> Nil { let subject = server(fn(message) { case message { Ping(reply) -> { process.send(reply, "last words") actor.stop() } _ -> actor.continue(Nil) } }) actor_call.try_call(subject, 2000, Ping) |> should.equal(Ok("last words"))}
pub fn an_unregistered_named_callee_reports_death_test() -> Nil { let subject = process.new_name("actor_call_test_missing") |> process.named_subject actor_call.try_call(subject, 2000, Ping) |> should.equal(Error(actor_call.CalleeDown))}
pub fn finite_calls_remove_monitors_and_down_messages_test() -> Nil { let before = monitor_state() let replying = server(answers) actor_call.try_call(replying, 2000, Ping) |> should.equal(Ok("pong")) monitor_state() |> should.equal(before) let stalled = server(never_replies) actor_call.try_call(stalled, 20, Ping) |> should.equal(Error(actor_call.TimedOut)) monitor_state() |> should.equal(before) let dying = server(dies_on_call) actor_call.try_call(dying, 2000, fn(_) { Die }) |> should.equal(Error(actor_call.CalleeDown)) monitor_state() |> should.equal(before)}
pub fn a_late_reply_cannot_accumulate_after_a_timeout_test() -> Nil { let held = process.new_subject() let subject = server(fn(message) { case message { Ping(reply) -> process.send(held, reply) Die -> Nil } actor.continue(Nil) }) let before = mailbox_size() actor_call.try_call(subject, 20, Ping) |> should.equal(Error(actor_call.TimedOut)) let assert Ok(reply) = process.receive(held, 2000) process.send(reply, "late") mailbox_size() |> should.equal(before)}
@external(erlang, "albedo_actor_call_test_support", "mailbox_size")fn mailbox_size() -> Int
@external(erlang, "albedo_actor_call_test_support", "monitor_state")fn monitor_state() -> #(Int, Int)
pub fn waiting_for_a_definite_reply_cleans_up_on_reply_and_death_test() -> Nil { let before = monitor_state() actor_call.await_reply(server(answers), Ping) |> should.equal(Ok("pong")) monitor_state() |> should.equal(before) actor_call.await_reply(server(dies_on_call), fn(_) { Die }) |> should.equal(Error(actor_call.CalleeDown)) monitor_state() |> should.equal(before)}
pub fn waiting_pins_delivery_to_the_monitored_incarnation_test() -> Nil { let name = process.new_name("await_reply_incarnation") let assert Ok(_) = actor.new(Nil) |> actor.on_message(fn(_, message) { answers(message) }) |> actor.named(name) |> actor.start actor_call.await_reply(process.named_subject(name), fn(reply) { let assert Ok(_) = process.unregister(name) let assert Ok(_) = actor.new(Nil) |> actor.on_message(fn(_, message) { case message { Ping(reply) -> process.send(reply, "replacement") Die -> Nil } actor.continue(Nil) }) |> actor.named(name) |> actor.start Ping(reply) }) |> should.equal(Ok("pong"))}