From c8e495f45e6b038d55d14c744151e10bfae03757 Mon Sep 17 00:00:00 2001 From: EuAndreh Date: Sat, 25 Apr 2026 16:34:59 -0300 Subject: WHOWAS now returns history of recently-disconnected clients QUIT and socket cleanup paths push a small (last 32) ring of {nick, username, realname, host, ts} entries into a new :whowas atom on the components map. WHOWAS reads that ring case- insensitively and answers with RPL_WHOWASUSER (314) + RPL_WHOISSERVER (312) for each match, terminated with RPL_ENDOFWHOWAS (369), or falls back to ERR_WASNOSUCHNICK (406) when nothing is found. --- tests/integration.clj | 3 ++- tests/unit.clj | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/integration.clj b/tests/integration.clj index 39993dc..84f8647 100644 --- a/tests/integration.clj +++ b/tests/integration.clj @@ -49,7 +49,8 @@ :chan-modes (atom {}) :chan-keys (atom {}) :chan-limits (atom {}) - :invites (atom {})})) + :invites (atom {}) + :whowas (atom [])})) (defn- make-client "Creates a simulated client connection using piped streams. diff --git a/tests/unit.clj b/tests/unit.clj index 5439c71..b8fc57f 100644 --- a/tests/unit.clj +++ b/tests/unit.clj @@ -115,7 +115,8 @@ :chan-modes (atom {}) :chan-keys (atom {}) :chan-limits (atom {}) - :invites (atom {})}))) + :invites (atom {}) + :whowas (atom [])}))) (defn test-network! [conn] @@ -1870,6 +1871,42 @@ (swap! (:chan-limits comp) assoc "#small" 1) (let [replies (handle-join ["#small"] bob comp)] (is (string/includes? (first replies) "471"))))) + (testing "WHOWAS returns history after QUIT" + (let [c (registered-client "alice" + (java.io.ByteArrayOutputStream.)) + components (test-components) + comp (assoc components + :clients (atom {"bob" {:client-atom + (atom + {:nick "bob" + :registered? true + :user + {:username "bobu" + :realname "Bob"}}) + :w + (java.io.ByteArrayOutputStream.)}}))] + ;; Bob quits + (let [bob (:client-atom (get @(:clients comp) "bob"))] + (replies-for! {:command "QUIT" :params [":bye"]} + bob comp)) + ;; Alice WHOWAS bob — gets history + (let [replies (replies-for! + {:command "WHOWAS" :params ["" "bob"]} + c comp)] + (is (>= (count replies) 2)) + (is (some #(re-find #" 314 " %) replies)) + (is (some #(re-find #" 369 " %) replies)) + (is (not (some #(re-find #" 406 " %) replies)))))) + (testing "WHOWAS for unknown nick returns 406" + (let [c (registered-client "alice" + (java.io.ByteArrayOutputStream.)) + components (test-components)] + (let [replies (replies-for! + {:command "WHOWAS" + :params ["" "neverexisted"]} + c components)] + (is (some #(re-find #" 406 " %) replies)) + (is (some #(re-find #" 369 " %) replies))))) (testing "+k key blocks JOIN with 475" (let [{:keys [test-network-id] :as components} (test-components-with-network) -- cgit v1.3