;; Bank account server — stateful process, balance threaded through ;; recursive loop. Handles {deposit, Amt, From}, {withdraw, Amt, From}, ;; {balance, From}, stop. Tests stateful process patterns. (define er-bank-test-count 0) (define er-bank-test-pass 0) (define er-bank-test-fails (list)) (define er-bank-test (fn (name actual expected) (set! er-bank-test-count (+ er-bank-test-count 1)) (if (= actual expected) (set! er-bank-test-pass (+ er-bank-test-pass 1)) (append! er-bank-test-fails {:actual actual :expected expected :name name})))) (define bank-ev erlang-eval-ast) ;; Server fun shared by all tests — threaded via the program string. (define er-bank-server-src "Server = fun (Balance) -> receive {deposit, Amt, From} -> From ! ok, Server(Balance + Amt); {withdraw, Amt, From} -> if Amt > Balance -> From ! insufficient, Server(Balance); true -> From ! ok, Server(Balance - Amt) end; {balance, From} -> From ! Balance, Server(Balance); stop -> ok end end") ;; Open account, deposit, check balance. (er-bank-test "deposit 100 -> balance 100" (bank-ev (str er-bank-server-src ", Me = self(), Bank = spawn(fun () -> Server(0) end), Bank ! {deposit, 100, Me}, receive ok -> ok end, Bank ! {balance, Me}, receive B -> Bank ! stop, B end")) 100) ;; Multiple deposits accumulate. (er-bank-test "deposits accumulate" (bank-ev (str er-bank-server-src ", Me = self(), Bank = spawn(fun () -> Server(0) end), Bank ! {deposit, 50, Me}, receive ok -> ok end, Bank ! {deposit, 25, Me}, receive ok -> ok end, Bank ! {deposit, 10, Me}, receive ok -> ok end, Bank ! {balance, Me}, receive B -> Bank ! stop, B end")) 85) ;; Withdraw within balance succeeds; insufficient gets rejected. (er-bank-test "withdraw within balance" (bank-ev (str er-bank-server-src ", Me = self(), Bank = spawn(fun () -> Server(100) end), Bank ! {withdraw, 30, Me}, receive ok -> ok end, Bank ! {balance, Me}, receive B -> Bank ! stop, B end")) 70) (er-bank-test "withdraw insufficient" (get (bank-ev (str er-bank-server-src ", Me = self(), Bank = spawn(fun () -> Server(20) end), Bank ! {withdraw, 100, Me}, receive R -> Bank ! stop, R end")) :name) "insufficient") ;; State preserved across an insufficient withdrawal. (er-bank-test "state preserved on rejection" (bank-ev (str er-bank-server-src ", Me = self(), Bank = spawn(fun () -> Server(50) end), Bank ! {withdraw, 1000, Me}, receive _ -> ok end, Bank ! {balance, Me}, receive B -> Bank ! stop, B end")) 50) ;; Mixed deposits and withdrawals. (er-bank-test "mixed transactions" (bank-ev (str er-bank-server-src ", Me = self(), Bank = spawn(fun () -> Server(100) end), Bank ! {deposit, 50, Me}, receive ok -> ok end, Bank ! {withdraw, 30, Me}, receive ok -> ok end, Bank ! {deposit, 10, Me}, receive ok -> ok end, Bank ! {withdraw, 5, Me}, receive ok -> ok end, Bank ! {balance, Me}, receive B -> Bank ! stop, B end")) 125) ;; Server.stop terminates the bank cleanly — main can verify by ;; sending stop and then exiting normally. (er-bank-test "server stops cleanly" (get (bank-ev (str er-bank-server-src ", Me = self(), Bank = spawn(fun () -> Server(0) end), Bank ! stop, done")) :name) "done") ;; Two clients sharing one bank — interleaved transactions. (er-bank-test "two clients share bank" (bank-ev (str er-bank-server-src ", Me = self(), Bank = spawn(fun () -> Server(0) end), Client = fun (Amt) -> spawn(fun () -> Bank ! {deposit, Amt, self()}, receive ok -> Me ! deposited end end) end, Client(40), Client(60), receive deposited -> ok end, receive deposited -> ok end, Bank ! {balance, Me}, receive B -> Bank ! stop, B end")) 100) (define er-bank-test-summary (str "bank " er-bank-test-pass "/" er-bank-test-count))