penpot/frontend/test/frontend_tests/data/workspace_mcp_test.cljs
Andrey Antukh b391a4c8d3
♻️ Add mcp integration state management refactor (#10226)
* ♻️ Add mcp integration state management refactor

* 🐛 Fix access tokens do not appear

* ♻️ Refactor some names

* ♻️ Refactor token deletion

---------

Co-authored-by: Luis de Dios <luis.dedios@kaleidos.net>
2026-06-16 18:35:30 +02:00

126 lines
5.8 KiB
Clojure

;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC Sucursal en España SL
(ns frontend-tests.data.workspace-mcp-test
(:require
[app.common.time :as ct]
[app.common.uuid :as uuid]
[app.main.data.profile :as du]
[app.main.data.workspace.mcp :as mcp]
[cljs.test :as t :include-macros true]
[potok.v2.core :as ptk]))
(t/deftest test-update-mcp-status
(t/testing "enables MCP in profile props and mcp state"
(let [state {:profile {:props {:mcp-enabled false}} :mcp {}}
result (ptk/update (mcp/update-mcp-status true) state)]
(t/is (true? (get-in result [:profile :props :mcp-enabled])))
(t/is (true? (get-in result [:mcp :enabled])))))
(t/testing "disables MCP in profile props and mcp state"
(let [state {:profile {:props {:mcp-enabled true}} :mcp {:enabled true}}
result (ptk/update (mcp/update-mcp-status false) state)]
(t/is (false? (get-in result [:profile :props :mcp-enabled])))
(t/is (false? (get-in result [:mcp :enabled]))))))
(t/deftest test-update-mcp-connection-status
(t/testing "sets connection status to connected"
(let [state {:mcp {:connection-status "disconnected"}}
result (ptk/update (mcp/update-mcp-connection-status "connected") state)]
(t/is (= "connected" (get-in result [:mcp :connection-status])))))
(t/testing "sets connection status to disconnected"
(let [state {:mcp {:connection-status "connected"}}
result (ptk/update (mcp/update-mcp-connection-status "disconnected") state)]
(t/is (= "disconnected" (get-in result [:mcp :connection-status]))))))
(t/deftest test-init-sets-enabled
(t/testing "init sets :mcp :enabled to true when profile has mcp-enabled"
(let [state {:mcp {} :profile {:props {:mcp-enabled true}}}
result (ptk/update (mcp/init) state)]
(t/is (true? (get-in result [:mcp :enabled])))))
(t/testing "init sets :mcp :enabled to false when profile has mcp-disabled"
(let [state {:mcp {:enabled true} :profile {:props {:mcp-enabled false}}}
result (ptk/update (mcp/init) state)]
(t/is (false? (get-in result [:mcp :enabled])))))
(t/testing "init sets :mcp :enabled to false when profile has no mcp-enabled prop"
(let [state {:mcp {:enabled true} :profile {:props {}}}
result (ptk/update (mcp/init) state)]
(t/is (false? (get-in result [:mcp :enabled]))))))
(t/deftest test-init-mcp-state
(let [token-id (uuid/next)]
(t/testing "with valid MCP token (future expiration)"
(let [future-date (ct/plus (ct/now) #js {:hours 24})
tokens [{:id token-id :type "mcp" :token "abc123" :expires-at future-date}]
event (#'mcp/init-mcp-state tokens)
state {:mcp {}}
result (ptk/update event state)]
(t/is (= "abc123" (get-in result [:mcp :token])))
(t/is (= token-id (get-in result [:mcp :token-id])))
(t/is (true? (get-in result [:mcp :token-valid])))
;; deref should return the token when valid
(t/is (some? @event))))
(t/testing "with MCP token with no expiration"
(let [tokens [{:id token-id :type "mcp" :token "abc123" :expires-at nil}]
event (#'mcp/init-mcp-state tokens)
state {:mcp {}}
result (ptk/update event state)]
(t/is (= "abc123" (get-in result [:mcp :token])))
(t/is (true? (get-in result [:mcp :token-valid])))
(t/is (some? @event))))
(t/testing "with expired MCP token"
(let [past-date (ct/minus (ct/now) #js {:hours 24})
tokens [{:id token-id :type "mcp" :token "abc123" :expires-at past-date}]
event (#'mcp/init-mcp-state tokens)
state {:mcp {}}
result (ptk/update event state)]
(t/is (= "abc123" (get-in result [:mcp :token])))
(t/is (false? (get-in result [:mcp :token-valid])))
;; deref should return nil when token is expired
(t/is (nil? @event))))
(t/testing "with no MCP token"
(let [tokens [{:id token-id :type nil :token "regular-token"}]
event (#'mcp/init-mcp-state tokens)
state {:mcp {:existing "data"}}
result (ptk/update event state)]
;; state should be unchanged when no MCP token exists
(t/is (= {:mcp {:existing "data"}} result))
(t/is (nil? @event))))
(t/testing "with mixed tokens finds MCP token"
(let [regular-id (uuid/next)
mcp-id (uuid/next)
tokens [{:id regular-id :type nil :token "regular"}
{:id mcp-id :type "mcp" :token "mcp-token" :expires-at nil}]
event (#'mcp/init-mcp-state tokens)
result (ptk/update event {:mcp {}})]
(t/is (= "mcp-token" (get-in result [:mcp :token])))
(t/is (= mcp-id (get-in result [:mcp :token-id])))))))
(t/deftest test-delete-access-token-optimistic-update
(let [token-1 {:id (uuid/next) :name "token-1"}
token-2 {:id (uuid/next) :name "token-2"}
token-3 {:id (uuid/next) :name "token-3"}]
(t/testing "removes token from :access-tokens optimistically"
(let [state {:access-tokens [token-1 token-2 token-3]}
event (du/delete-access-token {:id (:id token-2)})
result (ptk/update event state)]
(t/is (= 2 (count (:access-tokens result))))
(t/is (= [token-1 token-3] (:access-tokens result)))))
(t/testing "state unchanged when token id not found"
(let [state {:access-tokens [token-1 token-2]}
event (du/delete-access-token {:id (uuid/next)})
result (ptk/update event state)]
(t/is (= 2 (count (:access-tokens result))))))))