;; 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 (ns backend-tests.rpc-webhooks-test (:require [app.common.uri :as u] [app.common.uuid :as uuid] [app.db :as db] [app.http :as http] [app.rpc :as-alias rpc] [app.storage :as sto] [backend-tests.helpers :as th] [clojure.test :as t] [mockery.core :refer [with-mocks]])) (t/use-fixtures :once th/state-init) (t/use-fixtures :each th/database-reset) (defn create-webhook-params [id team] {::th/type :create-webhook ::rpc/profile-id id :team-id team :uri (u/uri "http://example.com") :mtype "application/json"}) (defn check-webhook-format ([result] (t/is (contains? result :id)) (t/is (contains? result :team-id)) (t/is (contains? result :created-at)) (t/is (contains? result :profile-id)) (t/is (contains? result :updated-at)) (t/is (contains? result :uri)) (t/is (contains? result :mtype)))) (t/deftest webhook-crud (with-mocks [http-mock {:target 'app.http.client/req! :return {:status 200}}] (let [prof (th/create-profile* 1 {:is-active true}) team-id (:default-team-id prof) proj-id (:default-project-id prof) whook (volatile! nil)] (t/testing "create webhook" (let [params {::th/type :create-webhook ::rpc/profile-id (:id prof) :team-id team-id :uri (u/uri "http://example.com") :mtype "application/json"} out (th/command! params)] (t/is (nil? (:error out))) (t/is (= 1 (:call-count @http-mock))) (let [result (:result out)] (check-webhook-format result) (t/is (= (:uri params) (:uri result))) (t/is (= (:team-id params) (:team-id result))) (t/is (= (:mtype params) (:mtype result))) (vreset! whook result)))) (th/reset-mock! http-mock) (t/testing "update webhook 1 (success)" (let [params {::th/type :update-webhook ::rpc/profile-id (:id prof) :id (:id @whook) :uri (:uri @whook) :mtype "application/transit+json" :is-active false} out (th/command! params)] (t/is (nil? (:error out))) (t/is (= 0 (:call-count @http-mock))) (let [result (:result out)] (check-webhook-format result) (t/is (= (:id params) (:id result))) (t/is (= (:id @whook) (:id result))) (t/is (= (:uri params) (:uri result))) (t/is (= (:team-id @whook) (:team-id result))) (t/is (= (:mtype params) (:mtype result)))))) (th/reset-mock! http-mock) (t/testing "update webhook 2 (change uri)" (let [params {::th/type :update-webhook ::rpc/profile-id (:id prof) :id (:id @whook) :uri (str (:uri @whook) "/test") :mtype "application/transit+json" :is-active false} out (th/command! params)] (t/is (nil? (:error out))) (t/is (map? (:result out))) (t/is (= 1 (:call-count @http-mock))))) (th/reset-mock! http-mock) (t/testing "update webhook 3 (not authorized)" (let [params {::th/type :update-webhook ::rpc/profile-id uuid/zero :id (:id @whook) :uri (str (:uri @whook) "/test") :mtype "application/transit+json" :is-active false} out (th/command! params)] (t/is (= 0 (:call-count @http-mock))) (let [error (:error out) error-data (ex-data error)] (t/is (th/ex-info? error)) (t/is (= (:type error-data) :not-found)) (t/is (= (:code error-data) :object-not-found))))) (th/reset-mock! http-mock) (t/testing "delete webhook (success)" (let [params {::th/type :delete-webhook ::rpc/profile-id (:id prof) :id (:id @whook)} out (th/command! params)] (t/is (= 0 (:call-count @http-mock))) (t/is (nil? (:error out))) (t/is (nil? (:result out))) (let [rows (th/db-exec! ["select * from webhook"])] (t/is (= 0 (count rows)))))) (th/reset-mock! http-mock) (t/testing "delete webhook (unauthorized)" (let [params {::th/type :delete-webhook ::rpc/profile-id uuid/zero :id (:id @whook)} out (th/command! params)] (t/is (= 0 (:call-count @http-mock))) (let [error (:error out) error-data (ex-data error)] (t/is (th/ex-info? error)) (t/is (= (:type error-data) :not-found)) (t/is (= (:code error-data) :object-not-found)))))))) (t/deftest webhooks-permissions-crud-viewer-only (with-mocks [http-mock {:target 'app.http.client/req! :return {:status 200}}] (let [owner (th/create-profile* 1 {:is-active true}) viewer (th/create-profile* 2 {:is-active true}) team (th/create-team* 1 {:profile-id (:id owner)}) whook (volatile! nil)] (th/create-team-role* {:team-id (:id team) :profile-id (:id viewer) :role :viewer}) ;; Assert all roles for team (let [roles (th/db-query :team-profile-rel {:team-id (:id team)})] (t/is (= 2 (count roles)))) (t/testing "viewer creates a webhook" (let [viewers-webhook (create-webhook-params (:id viewer) (:id team)) out (th/command! viewers-webhook)] (t/is (nil? (:error out))) (t/is (= 1 (:call-count @http-mock))) (let [result (:result out)] (check-webhook-format result) (t/is (= (:uri viewers-webhook) (:uri result))) (t/is (= (:team-id viewers-webhook) (:team-id result))) (t/is (= (::rpc/profile-id viewers-webhook) (:profile-id result))) (t/is (= (:mtype viewers-webhook) (:mtype result))) (vreset! whook result)))) (th/reset-mock! http-mock) (t/testing "viewer updates it's own webhook (success)" (let [params {::th/type :update-webhook ::rpc/profile-id (:id viewer) :id (:id @whook) :uri (:uri @whook) :mtype "application/transit+json" :is-active false} out (th/command! params) result (:result out)] (t/is (nil? (:error out))) (t/is (= 0 (:call-count @http-mock))) (check-webhook-format result) (t/is (= (:is-active params) (:is-active result))) (t/is (= (:team-id @whook) (:team-id result))) (t/is (= (:mtype params) (:mtype result))) (vreset! whook result))) (th/reset-mock! http-mock) (t/testing "viewer deletes it's own webhook (success)" (let [params {::th/type :delete-webhook ::rpc/profile-id (:id viewer) :id (:id @whook)} out (th/command! params)] (t/is (= 0 (:call-count @http-mock))) (t/is (nil? (:error out))) (t/is (nil? (:result out))) (let [rows (th/db-exec! ["select * from webhook"])] (t/is (= 0 (count rows)))))) (th/reset-mock! http-mock)))) (t/deftest webhooks-permissions-crud-viewer-owner (with-mocks [http-mock {:target 'app.http.client/req! :return {:status 200}}] (let [owner (th/create-profile* 1 {:is-active true}) viewer (th/create-profile* 2 {:is-active true}) team (th/create-team* 1 {:profile-id (:id owner)}) whook (volatile! nil)] (th/create-team-role* {:team-id (:id team) :profile-id (:id viewer) :role :viewer}) (t/testing "owner creates a wehbook" (let [owners-webhook (create-webhook-params (:id owner) (:id team)) out (th/command! owners-webhook) result (:result out)] (t/is (nil? (:error out))) (t/is (= 1 (:call-count @http-mock))) (check-webhook-format result) (t/is (= (:uri owners-webhook) (:uri result))) (t/is (= (:team-id owners-webhook) (:team-id result))) (t/is (= (:mtype owners-webhook) (:mtype result))) (vreset! whook result))) (th/reset-mock! http-mock) (t/testing "viewer updates owner's webhook (unauthorized)" (let [params {::th/type :update-webhook ::rpc/profile-id (:id viewer) :id (:id @whook) :uri (str (:uri @whook) "/test") :mtype "application/transit+json" :is-active false} out (th/command! params)] (t/is (= 0 (:call-count @http-mock))) (let [error (:error out) error-data (ex-data error)] (t/is (th/ex-info? error)) (t/is (= (:type error-data) :not-found)) (t/is (= (:code error-data) :object-not-found))))) (th/reset-mock! http-mock) (t/testing "viewer deletes owner's webhook (unauthorized)" (let [params {::th/type :delete-webhook ::rpc/profile-id (:id viewer) :id (:id @whook)} out (th/command! params) error (:error out) error-data (ex-data error)] (t/is (= 0 (:call-count @http-mock))) (t/is (th/ex-info? error)) (t/is (= (:type error-data) :not-found)) (t/is (= (:code error-data) :object-not-found))))))) (t/deftest webhooks-quotes (with-mocks [http-mock {:target 'app.http.client/req! :return {:status 200}}] (let [prof (th/create-profile* 1 {:is-active true}) team-id (:default-team-id prof) params {::th/type :create-webhook ::rpc/profile-id (:id prof) :team-id team-id :uri "http://example.com" :mtype "application/json"} out1 (th/command! params) out2 (th/command! params) out3 (th/command! params) out4 (th/command! params) out5 (th/command! params) out6 (th/command! params) out7 (th/command! params) out8 (th/command! params) out9 (th/command! params)] (t/is (= 8 (:call-count @http-mock))) (t/is (nil? (:error out1))) (t/is (nil? (:error out2))) (t/is (nil? (:error out3))) (t/is (nil? (:error out4))) (t/is (nil? (:error out5))) (t/is (nil? (:error out6))) (t/is (nil? (:error out7))) (t/is (nil? (:error out8))) (let [error (:error out9) error-data (ex-data error)] (t/is (th/ex-info? error)) (t/is (= (:type error-data) :restriction)) (t/is (= (:code error-data) :webhooks-quote-reached))))))