diff options
| author | EuAndreh <eu@euandre.org> | 2026-04-25 16:19:52 -0300 |
|---|---|---|
| committer | EuAndreh <eu@euandre.org> | 2026-04-25 16:19:52 -0300 |
| commit | a72bdd32f9f6e83858ad2670fabfd5136a2edefe (patch) | |
| tree | 49aa60f9f860eff6b2646d261e17a78bf53c91c6 /tests | |
| parent | Expand RPL_ISUPPORT and have OPER emit MODE +o (diff) | |
| download | papod-a72bdd32f9f6e83858ad2670fabfd5136a2edefe.tar.gz papod-a72bdd32f9f6e83858ad2670fabfd5136a2edefe.tar.xz | |
Enforce +i, +k, +l on JOIN; add @ symbol for secret channels
JOIN now respects the channel modes that gate access:
- +i (invite-only): rejects with 473 unless the user is in the
per-channel invite list, which INVITE now populates
- +k <key>: rejects with 475 unless the JOIN key argument matches
the value tracked alongside +k in :chan-keys
- +l <n>: rejects with 471 once :chan-limits is at capacity
NAMES (and the burst sent on JOIN) now picks the channel symbol
based on chan-modes — '@' for +s, '*' for +p, '=' otherwise — so
secret/private channels are reported correctly.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/integration.clj | 5 | ||||
| -rw-r--r-- | tests/unit.clj | 114 |
2 files changed, 117 insertions, 2 deletions
diff --git a/tests/integration.clj b/tests/integration.clj index 3e12833..04efc72 100644 --- a/tests/integration.clj +++ b/tests/integration.clj @@ -46,7 +46,10 @@ :channels (atom {}) :ops (atom {}) :voiced (atom {}) - :chan-modes (atom {})})) + :chan-modes (atom {}) + :chan-keys (atom {}) + :chan-limits (atom {}) + :invites (atom {})})) (defn- make-client "Creates a simulated client connection using piped streams. diff --git a/tests/unit.clj b/tests/unit.clj index 6b9e36b..5439c71 100644 --- a/tests/unit.clj +++ b/tests/unit.clj @@ -112,7 +112,10 @@ {:conn conn :cracha cracha-state :process-id proc-id :clients (atom {}) :channels (atom {}) :ops (atom {}) :voiced (atom {}) - :chan-modes (atom {})}))) + :chan-modes (atom {}) + :chan-keys (atom {}) + :chan-limits (atom {}) + :invites (atom {})}))) (defn test-network! [conn] @@ -1788,6 +1791,115 @@ (is (= 1 (count replies))) (is (string/includes? (first replies) "391")))))) +(deftest test_channel-modes + (testing "secret channel (+s) uses @ symbol in NAMES" + (let [{:keys [test-network-id] :as components} + (test-components-with-network) + out (java.io.ByteArrayOutputStream.) + c (registered-client "alice" out test-network-id) + comp (assoc components + :clients + (atom {"alice" + {:w out :client-atom c}}))] + (handle-join ["#sec"] c comp) + ;; Mark channel as secret + (swap! (:chan-modes comp) assoc "#sec" "+nts") + (let [replies (replies-for! + {:command "NAMES" :params ["" "#sec"]} + c comp)] + (is (string/includes? (first replies) "353")) + (is (string/includes? (first replies) "@ #sec"))))) + (testing "+i (invite-only) blocks JOIN with 473" + (let [{:keys [test-network-id] :as components} + (test-components-with-network) + a-out (java.io.ByteArrayOutputStream.) + b-out (java.io.ByteArrayOutputStream.) + alice (registered-client "alice" a-out test-network-id) + bob (registered-client "bob" b-out test-network-id) + comp (assoc components + :clients (atom + {"alice" {:w a-out + :client-atom alice} + "bob" {:w b-out + :client-atom bob}}) + :channels (atom {}))] + (handle-join ["#priv"] alice comp) + (swap! (:chan-modes comp) assoc "#priv" "+nti") + (let [replies (handle-join ["#priv"] bob comp)] + (is (= 1 (count replies))) + (is (string/includes? (first replies) "473"))))) + (testing "INVITE allows +i bypass" + (let [{:keys [test-network-id] :as components} + (test-components-with-network) + a-out (java.io.ByteArrayOutputStream.) + b-out (java.io.ByteArrayOutputStream.) + alice (registered-client "alice" a-out test-network-id) + bob (registered-client "bob" b-out test-network-id) + comp (assoc components + :clients (atom + {"alice" {:w a-out + :client-atom alice} + "bob" {:w b-out + :client-atom bob}}) + :channels (atom {}))] + (handle-join ["#priv"] alice comp) + (swap! (:chan-modes comp) assoc "#priv" "+nti") + (replies-for! + {:command "INVITE" :params ["" "bob" "#priv"]} + alice comp) + ;; Bob should now be able to join + (let [replies (handle-join ["#priv"] bob comp)] + (is (empty? + (filter #(re-find #" 473 " %) replies)))))) + (testing "+l limit blocks JOIN with 471" + (let [{:keys [test-network-id] :as components} + (test-components-with-network) + a-out (java.io.ByteArrayOutputStream.) + b-out (java.io.ByteArrayOutputStream.) + alice (registered-client "alice" a-out test-network-id) + bob (registered-client "bob" b-out test-network-id) + comp (assoc components + :clients (atom + {"alice" {:w a-out + :client-atom alice} + "bob" {:w b-out + :client-atom bob}}) + :channels (atom {}))] + (handle-join ["#small"] alice comp) + (swap! (:chan-modes comp) assoc "#small" "+ntl") + (swap! (:chan-limits comp) assoc "#small" 1) + (let [replies (handle-join ["#small"] bob comp)] + (is (string/includes? (first replies) "471"))))) + (testing "+k key blocks JOIN with 475" + (let [{:keys [test-network-id] :as components} + (test-components-with-network) + a-out (java.io.ByteArrayOutputStream.) + b-out (java.io.ByteArrayOutputStream.) + alice (registered-client "alice" a-out test-network-id) + bob (registered-client "bob" b-out test-network-id) + comp (assoc components + :clients (atom + {"alice" {:w a-out + :client-atom alice} + "bob" {:w b-out + :client-atom bob}}) + :channels (atom {}))] + (handle-join ["#locked"] alice comp) + (swap! (:chan-modes comp) assoc "#locked" "+ntk") + (swap! (:chan-keys comp) assoc "#locked" "secret") + ;; No key + (let [replies (handle-join ["#locked"] bob comp)] + (is (string/includes? (first replies) "475"))) + ;; Wrong key + (let [replies (handle-join ["#locked" "wrong"] + bob comp)] + (is (string/includes? (first replies) "475"))) + ;; Right key + (let [replies (handle-join ["#locked" "secret"] + bob comp)] + (is (empty? + (filter #(re-find #" 475 " %) replies))))))) + (defn -main [& _args] (binding [*out* *err*] |
