okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

Aly Raffaufrefactor(runtime): share finite monitored actor calls0ca1666

main
2.7 KiB99 linesraw
1//// Timeout and reply/death ordering require process monitors and controlled
2//// acknowledgments that the external E2E API cannot arrange.
3
4import albedo/actor_call
5import gleam/erlang/process.{type Subject}
6import gleam/otp/actor
7import gleeunit/should
8
9type Ping {
10  Ping(reply: Subject(String))
11  Die
12}
13
14fn server(mode: fn(message) -> actor.Next(Nil, message)) -> Subject(message) {
15  let assert Ok(started) =
16    actor.new(Nil)
17    |> actor.on_message(fn(_, message) { mode(message) })
18    |> actor.start
19  started.data
20}
21
22fn answers(message: Ping) -> actor.Next(Nil, Ping) {
23  case message {
24    Ping(reply) -> {
25      process.send(reply, "pong")
26      actor.continue(Nil)
27    }
28    _ -> actor.continue(Nil)
29  }
30}
31
32fn never_replies(_message: Ping) -> actor.Next(Nil, Ping) {
33  actor.continue(Nil)
34}
35
36fn dies_on_call(message: Ping) -> actor.Next(Nil, Ping) {
37  case message {
38    Die -> actor.stop()
39    _ -> actor.continue(Nil)
40  }
41}
42
43pub fn a_reply_in_time_is_returned_test() -> Nil {
44  let subject = server(answers)
45  actor_call.try_call(subject, 2000, Ping)
46  |> should.equal(Ok("pong"))
47}
48
49pub fn a_stalled_callee_reports_a_timeout_test() -> Nil {
50  let subject = server(never_replies)
51  actor_call.try_call(subject, 20, Ping)
52  |> should.equal(Error(actor_call.TimedOut))
53}
54
55pub fn a_dead_callee_reports_the_death_test() -> Nil {
56  let subject = server(dies_on_call)
57  actor_call.try_call(subject, 2000, fn(_reply) { Die })
58  |> should.equal(Error(actor_call.CalleeDown))
59}
60
61pub fn a_reply_that_races_the_callee_exit_still_arrives_test() -> Nil {
62  let subject =
63    server(fn(message) {
64      case message {
65        Ping(reply) -> {
66          process.send(reply, "last words")
67          actor.stop()
68        }
69        _ -> actor.continue(Nil)
70      }
71    })
72  actor_call.try_call(subject, 2000, Ping)
73  |> should.equal(Ok("last words"))
74}
75
76pub fn an_unregistered_named_callee_reports_death_test() -> Nil {
77  let subject =
78    process.new_name("actor_call_test_missing") |> process.named_subject
79  actor_call.try_call(subject, 2000, Ping)
80  |> should.equal(Error(actor_call.CalleeDown))
81}
82
83pub fn finite_calls_remove_monitors_and_down_messages_test() -> Nil {
84  let before = monitor_state()
85  let replying = server(answers)
86  actor_call.try_call(replying, 2000, Ping) |> should.equal(Ok("pong"))
87  monitor_state() |> should.equal(before)
88  let stalled = server(never_replies)
89  actor_call.try_call(stalled, 20, Ping)
90  |> should.equal(Error(actor_call.TimedOut))
91  monitor_state() |> should.equal(before)
92  let dying = server(dies_on_call)
93  actor_call.try_call(dying, 2000, fn(_) { Die })
94  |> should.equal(Error(actor_call.CalleeDown))
95  monitor_state() |> should.equal(before)
96}
97
98@external(erlang, "albedo_actor_call_test_support", "monitor_state")
99fn monitor_state() -> #(Int, Int)