2020-05-27 03:03:00 -05:00
|
|
|
;; 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/.
|
|
|
|
;;
|
2021-04-10 02:43:04 -05:00
|
|
|
;; Copyright (c) UXBOX Labs SL
|
2020-05-27 03:03:00 -05:00
|
|
|
|
2021-05-28 06:50:42 -05:00
|
|
|
(ns app.services-files-test
|
2020-02-17 03:49:04 -05:00
|
|
|
(:require
|
2020-08-18 12:26:37 -05:00
|
|
|
[app.common.uuid :as uuid]
|
|
|
|
[app.db :as db]
|
2022-03-23 04:59:20 -05:00
|
|
|
[app.db.sql :as sql]
|
2020-08-18 12:26:37 -05:00
|
|
|
[app.http :as http]
|
2021-01-31 14:55:53 -05:00
|
|
|
[app.storage :as sto]
|
2021-05-28 06:50:42 -05:00
|
|
|
[app.test-helpers :as th]
|
2021-06-07 09:51:09 -05:00
|
|
|
[app.util.time :as dt]
|
2021-01-31 14:55:53 -05:00
|
|
|
[clojure.test :as t]
|
|
|
|
[datoteka.core :as fs]))
|
2020-02-17 03:49:04 -05:00
|
|
|
|
|
|
|
(t/use-fixtures :once th/state-init)
|
|
|
|
(t/use-fixtures :each th/database-reset)
|
|
|
|
|
|
|
|
(t/deftest files-crud
|
2021-01-31 14:55:53 -05:00
|
|
|
(let [prof (th/create-profile* 1 {:is-active true})
|
2020-05-27 02:33:09 -05:00
|
|
|
team-id (:default-team-id prof)
|
|
|
|
proj-id (:default-project-id prof)
|
2020-02-17 03:49:04 -05:00
|
|
|
file-id (uuid/next)
|
|
|
|
page-id (uuid/next)]
|
|
|
|
|
|
|
|
(t/testing "create file"
|
2020-12-24 08:32:19 -05:00
|
|
|
(let [data {::th/type :create-file
|
2020-02-17 03:49:04 -05:00
|
|
|
:profile-id (:id prof)
|
2020-05-14 06:49:11 -05:00
|
|
|
:project-id proj-id
|
2020-02-17 03:49:04 -05:00
|
|
|
:id file-id
|
2020-12-24 08:32:19 -05:00
|
|
|
:name "foobar"
|
|
|
|
:is-shared false}
|
|
|
|
out (th/mutation! data)]
|
2020-05-14 06:49:11 -05:00
|
|
|
|
2020-02-17 03:49:04 -05:00
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (nil? (:error out)))
|
|
|
|
|
|
|
|
(let [result (:result out)]
|
|
|
|
(t/is (= (:name data) (:name result)))
|
2020-05-14 06:49:11 -05:00
|
|
|
(t/is (= proj-id (:project-id result))))))
|
2020-02-17 03:49:04 -05:00
|
|
|
|
|
|
|
(t/testing "rename file"
|
2020-12-24 08:32:19 -05:00
|
|
|
(let [data {::th/type :rename-file
|
2020-02-17 03:49:04 -05:00
|
|
|
:id file-id
|
|
|
|
:name "new name"
|
|
|
|
:profile-id (:id prof)}
|
2020-12-24 08:32:19 -05:00
|
|
|
out (th/mutation! data)]
|
2020-04-06 16:32:54 -05:00
|
|
|
|
2020-02-17 03:49:04 -05:00
|
|
|
;; (th/print-result! out)
|
2020-04-06 16:32:54 -05:00
|
|
|
(let [result (:result out)]
|
|
|
|
(t/is (= (:id data) (:id result)))
|
|
|
|
(t/is (= (:name data) (:name result))))))
|
2020-02-17 03:49:04 -05:00
|
|
|
|
2021-05-07 04:36:34 -05:00
|
|
|
(t/testing "query files"
|
|
|
|
(let [data {::th/type :project-files
|
|
|
|
:project-id proj-id
|
|
|
|
:profile-id (:id prof)}
|
|
|
|
out (th/query! data)]
|
|
|
|
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (nil? (:error out)))
|
|
|
|
|
|
|
|
(let [result (:result out)]
|
|
|
|
(t/is (= 1 (count result)))
|
|
|
|
(t/is (= file-id (get-in result [0 :id])))
|
|
|
|
(t/is (= "new name" (get-in result [0 :name]))))))
|
|
|
|
|
2020-02-17 03:49:04 -05:00
|
|
|
(t/testing "query single file without users"
|
2020-12-24 08:32:19 -05:00
|
|
|
(let [data {::th/type :file
|
2020-02-17 03:49:04 -05:00
|
|
|
:profile-id (:id prof)
|
|
|
|
:id file-id}
|
2020-12-24 08:32:19 -05:00
|
|
|
out (th/query! data)]
|
2020-02-17 03:49:04 -05:00
|
|
|
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (nil? (:error out)))
|
|
|
|
|
|
|
|
(let [result (:result out)]
|
|
|
|
(t/is (= file-id (:id result)))
|
|
|
|
(t/is (= "new name" (:name result)))
|
2020-09-07 03:56:42 -05:00
|
|
|
(t/is (= 1 (count (get-in result [:data :pages]))))
|
2020-02-17 03:49:04 -05:00
|
|
|
(t/is (nil? (:users result))))))
|
|
|
|
|
|
|
|
(t/testing "delete file"
|
2020-12-24 08:32:19 -05:00
|
|
|
(let [data {::th/type :delete-file
|
2020-02-17 03:49:04 -05:00
|
|
|
:id file-id
|
|
|
|
:profile-id (:id prof)}
|
2020-12-24 08:32:19 -05:00
|
|
|
out (th/mutation! data)]
|
2020-02-17 03:49:04 -05:00
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (nil? (:error out)))
|
|
|
|
(t/is (nil? (:result out)))))
|
|
|
|
|
|
|
|
(t/testing "query single file after delete"
|
2020-12-24 08:32:19 -05:00
|
|
|
(let [data {::th/type :file
|
2020-02-17 03:49:04 -05:00
|
|
|
:profile-id (:id prof)
|
|
|
|
:id file-id}
|
2020-12-24 08:32:19 -05:00
|
|
|
out (th/query! data)]
|
2020-02-17 03:49:04 -05:00
|
|
|
|
|
|
|
;; (th/print-result! out)
|
|
|
|
|
2020-12-24 08:32:19 -05:00
|
|
|
(let [error (:error out)
|
2020-02-17 03:49:04 -05:00
|
|
|
error-data (ex-data error)]
|
|
|
|
(t/is (th/ex-info? error))
|
|
|
|
(t/is (= (:type error-data) :not-found)))))
|
|
|
|
|
|
|
|
(t/testing "query list files after delete"
|
2021-09-30 04:38:33 -05:00
|
|
|
(let [data {::th/type :project-files
|
2020-05-14 06:49:11 -05:00
|
|
|
:project-id proj-id
|
2020-02-17 03:49:04 -05:00
|
|
|
:profile-id (:id prof)}
|
2020-12-24 08:32:19 -05:00
|
|
|
out (th/query! data)]
|
2020-02-17 03:49:04 -05:00
|
|
|
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (nil? (:error out)))
|
|
|
|
|
|
|
|
(let [result (:result out)]
|
|
|
|
(t/is (= 0 (count result))))))
|
|
|
|
))
|
2021-01-31 14:55:53 -05:00
|
|
|
|
2022-03-23 04:59:20 -05:00
|
|
|
(t/deftest file-gc-task
|
2021-02-25 11:45:39 -05:00
|
|
|
(letfn [(create-file-media-object [{:keys [profile-id file-id]}]
|
|
|
|
(let [mfile {:filename "sample.jpg"
|
2022-03-04 12:00:16 -05:00
|
|
|
:path (th/tempfile "app/test_files/sample.jpg")
|
|
|
|
:mtype "image/jpeg"
|
2021-02-25 11:45:39 -05:00
|
|
|
:size 312043}
|
|
|
|
params {::th/type :upload-file-media-object
|
|
|
|
:profile-id profile-id
|
|
|
|
:file-id file-id
|
|
|
|
:is-local true
|
|
|
|
:name "testfile"
|
|
|
|
:content mfile}
|
|
|
|
out (th/mutation! params)]
|
2022-03-23 04:59:20 -05:00
|
|
|
|
|
|
|
;; (th/print-result! out)
|
|
|
|
|
2021-02-25 11:45:39 -05:00
|
|
|
(t/is (nil? (:error out)))
|
|
|
|
(:result out)))
|
|
|
|
|
|
|
|
(update-file [{:keys [profile-id file-id changes revn] :or {revn 0}}]
|
|
|
|
(let [params {::th/type :update-file
|
|
|
|
:id file-id
|
|
|
|
:session-id (uuid/random)
|
|
|
|
:profile-id profile-id
|
|
|
|
:revn revn
|
|
|
|
:changes changes}
|
|
|
|
out (th/mutation! params)]
|
|
|
|
(t/is (nil? (:error out)))
|
|
|
|
(:result out)))]
|
|
|
|
|
|
|
|
(let [storage (:app.storage/storage th/*system*)
|
|
|
|
|
|
|
|
profile (th/create-profile* 1)
|
|
|
|
file (th/create-file* 1 {:profile-id (:id profile)
|
|
|
|
:project-id (:default-project-id profile)
|
|
|
|
:is-shared false})
|
|
|
|
|
|
|
|
fmo1 (create-file-media-object {:profile-id (:id profile)
|
|
|
|
:file-id (:id file)})
|
|
|
|
fmo2 (create-file-media-object {:profile-id (:id profile)
|
|
|
|
:file-id (:id file)})
|
|
|
|
shid (uuid/random)
|
|
|
|
|
|
|
|
ures (update-file
|
|
|
|
{:file-id (:id file)
|
|
|
|
:profile-id (:id profile)
|
|
|
|
:revn 0
|
|
|
|
:changes
|
|
|
|
[{:type :add-obj
|
|
|
|
:page-id (first (get-in file [:data :pages]))
|
|
|
|
:id shid
|
|
|
|
:parent-id uuid/zero
|
|
|
|
:frame-id uuid/zero
|
|
|
|
:obj {:id shid
|
|
|
|
:name "image"
|
|
|
|
:frame-id uuid/zero
|
|
|
|
:parent-id uuid/zero
|
|
|
|
:type :image
|
|
|
|
:metadata {:id (:id fmo1)}}}]})]
|
|
|
|
|
2022-02-28 11:15:58 -05:00
|
|
|
;; Check that reference storage objets on filemediaobjects
|
|
|
|
;; are the same because of deduplication feature.
|
|
|
|
(t/is (= (:media-id fmo1) (:media-id fmo2)))
|
|
|
|
(t/is (= (:thumbnail-id fmo1) (:thumbnail-id fmo2)))
|
|
|
|
|
|
|
|
;; If we launch gc-touched-task, we should have 2 items to
|
|
|
|
;; freeze because of the deduplication (we have uploaded 2 times
|
|
|
|
;; 2 two same files).
|
2022-02-10 13:50:40 -05:00
|
|
|
(let [task (:app.storage/gc-touched-task th/*system*)
|
|
|
|
res (task {})]
|
2022-02-28 11:15:58 -05:00
|
|
|
|
|
|
|
(t/is (= 2 (:freeze res)))
|
2022-02-10 13:50:40 -05:00
|
|
|
(t/is (= 0 (:delete res))))
|
|
|
|
|
2021-11-15 09:53:10 -05:00
|
|
|
;; run the task immediately
|
2022-03-23 04:59:20 -05:00
|
|
|
(let [task (:app.tasks.file-gc/handler th/*system*)
|
2021-02-25 11:45:39 -05:00
|
|
|
res (task {})]
|
|
|
|
(t/is (= 0 (:processed res))))
|
|
|
|
|
2021-11-15 09:53:10 -05:00
|
|
|
;; make the file eligible for GC waiting 300ms (configured
|
2021-02-25 11:45:39 -05:00
|
|
|
;; timeout for testing)
|
|
|
|
(th/sleep 300)
|
|
|
|
|
|
|
|
;; run the task again
|
2022-03-23 04:59:20 -05:00
|
|
|
(let [task (:app.tasks.file-gc/handler th/*system*)
|
2021-02-25 11:45:39 -05:00
|
|
|
res (task {})]
|
|
|
|
(t/is (= 1 (:processed res))))
|
|
|
|
|
|
|
|
;; retrieve file and check trimmed attribute
|
|
|
|
(let [row (db/exec-one! th/*pool* ["select * from file where id = ?" (:id file)])]
|
|
|
|
(t/is (true? (:has-media-trimmed row))))
|
|
|
|
|
|
|
|
;; check file media objects
|
|
|
|
(let [rows (db/exec! th/*pool* ["select * from file_media_object where file_id = ?" (:id file)])]
|
|
|
|
(t/is (= 1 (count rows))))
|
|
|
|
|
|
|
|
;; The underlying storage objects are still available.
|
2022-02-28 11:15:58 -05:00
|
|
|
(t/is (some? @(sto/get-object storage (:media-id fmo2))))
|
|
|
|
(t/is (some? @(sto/get-object storage (:thumbnail-id fmo2))))
|
|
|
|
(t/is (some? @(sto/get-object storage (:media-id fmo1))))
|
|
|
|
(t/is (some? @(sto/get-object storage (:thumbnail-id fmo1))))
|
2021-02-25 11:45:39 -05:00
|
|
|
|
2022-02-10 13:50:40 -05:00
|
|
|
;; now, we have deleted the unused file-media-object, if we
|
|
|
|
;; execute the touched-gc task, we should see that two of them
|
|
|
|
;; are marked to be deleted.
|
2021-02-25 11:45:39 -05:00
|
|
|
(let [task (:app.storage/gc-touched-task th/*system*)
|
|
|
|
res (task {})]
|
2022-02-28 11:15:58 -05:00
|
|
|
(t/is (= 2 (:freeze res)))
|
|
|
|
(t/is (= 0 (:delete res))))
|
2021-02-25 11:45:39 -05:00
|
|
|
|
2022-02-10 13:50:40 -05:00
|
|
|
;; Finally, check that some of the objects that are marked as
|
|
|
|
;; deleted we are unable to retrieve them using standard storage
|
|
|
|
;; public api.
|
2022-02-28 11:15:58 -05:00
|
|
|
(t/is (some? @(sto/get-object storage (:media-id fmo2))))
|
|
|
|
(t/is (some? @(sto/get-object storage (:thumbnail-id fmo2))))
|
|
|
|
(t/is (some? @(sto/get-object storage (:media-id fmo1))))
|
|
|
|
(t/is (some? @(sto/get-object storage (:thumbnail-id fmo1))))
|
2021-02-25 11:45:39 -05:00
|
|
|
|
|
|
|
)))
|
|
|
|
|
|
|
|
(t/deftest permissions-checks-creating-file
|
|
|
|
(let [profile1 (th/create-profile* 1)
|
|
|
|
profile2 (th/create-profile* 2)
|
|
|
|
|
|
|
|
data {::th/type :create-file
|
|
|
|
:profile-id (:id profile2)
|
|
|
|
:project-id (:default-project-id profile1)
|
|
|
|
:name "foobar"
|
|
|
|
:is-shared false}
|
|
|
|
out (th/mutation! data)
|
|
|
|
error (:error out)]
|
|
|
|
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (th/ex-info? error))
|
|
|
|
(t/is (th/ex-of-type? error :not-found))))
|
|
|
|
|
|
|
|
(t/deftest permissions-checks-rename-file
|
|
|
|
(let [profile1 (th/create-profile* 1)
|
|
|
|
profile2 (th/create-profile* 2)
|
|
|
|
|
|
|
|
file (th/create-file* 1 {:project-id (:default-project-id profile1)
|
|
|
|
:profile-id (:id profile1)})
|
|
|
|
data {::th/type :rename-file
|
|
|
|
:id (:id file)
|
|
|
|
:profile-id (:id profile2)
|
|
|
|
:name "foobar"}
|
|
|
|
out (th/mutation! data)
|
|
|
|
error (:error out)]
|
|
|
|
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (th/ex-info? error))
|
|
|
|
(t/is (th/ex-of-type? error :not-found))))
|
|
|
|
|
|
|
|
(t/deftest permissions-checks-delete-file
|
|
|
|
(let [profile1 (th/create-profile* 1)
|
|
|
|
profile2 (th/create-profile* 2)
|
|
|
|
|
|
|
|
file (th/create-file* 1 {:project-id (:default-project-id profile1)
|
|
|
|
:profile-id (:id profile1)})
|
|
|
|
data {::th/type :delete-file
|
|
|
|
:profile-id (:id profile2)
|
|
|
|
:id (:id file)}
|
|
|
|
out (th/mutation! data)
|
|
|
|
error (:error out)]
|
|
|
|
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (th/ex-info? error))
|
|
|
|
(t/is (th/ex-of-type? error :not-found))))
|
|
|
|
|
|
|
|
(t/deftest permissions-checks-set-file-shared
|
|
|
|
(let [profile1 (th/create-profile* 1)
|
|
|
|
profile2 (th/create-profile* 2)
|
|
|
|
file (th/create-file* 1 {:project-id (:default-project-id profile1)
|
|
|
|
:profile-id (:id profile1)})
|
|
|
|
data {::th/type :set-file-shared
|
|
|
|
:profile-id (:id profile2)
|
|
|
|
:id (:id file)
|
|
|
|
:is-shared true}
|
|
|
|
out (th/mutation! data)
|
|
|
|
error (:error out)]
|
|
|
|
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (th/ex-info? error))
|
|
|
|
(t/is (th/ex-of-type? error :not-found))))
|
|
|
|
|
|
|
|
(t/deftest permissions-checks-link-to-library-1
|
|
|
|
(let [profile1 (th/create-profile* 1)
|
|
|
|
profile2 (th/create-profile* 2)
|
|
|
|
file1 (th/create-file* 1 {:project-id (:default-project-id profile1)
|
|
|
|
:profile-id (:id profile1)
|
|
|
|
:is-shared true})
|
|
|
|
file2 (th/create-file* 2 {:project-id (:default-project-id profile1)
|
|
|
|
:profile-id (:id profile1)})
|
|
|
|
|
|
|
|
data {::th/type :link-file-to-library
|
|
|
|
:profile-id (:id profile2)
|
|
|
|
:file-id (:id file2)
|
|
|
|
:library-id (:id file1)}
|
|
|
|
|
|
|
|
out (th/mutation! data)
|
|
|
|
error (:error out)]
|
|
|
|
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (th/ex-info? error))
|
|
|
|
(t/is (th/ex-of-type? error :not-found))))
|
|
|
|
|
|
|
|
(t/deftest permissions-checks-link-to-library-2
|
|
|
|
(let [profile1 (th/create-profile* 1)
|
|
|
|
profile2 (th/create-profile* 2)
|
|
|
|
file1 (th/create-file* 1 {:project-id (:default-project-id profile1)
|
|
|
|
:profile-id (:id profile1)
|
|
|
|
:is-shared true})
|
|
|
|
|
|
|
|
file2 (th/create-file* 2 {:project-id (:default-project-id profile2)
|
|
|
|
:profile-id (:id profile2)})
|
|
|
|
|
|
|
|
data {::th/type :link-file-to-library
|
|
|
|
:profile-id (:id profile2)
|
|
|
|
:file-id (:id file2)
|
|
|
|
:library-id (:id file1)}
|
|
|
|
|
|
|
|
out (th/mutation! data)
|
|
|
|
error (:error out)]
|
|
|
|
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (th/ex-info? error))
|
|
|
|
(t/is (th/ex-of-type? error :not-found))))
|
2021-01-31 14:55:53 -05:00
|
|
|
|
2022-03-23 04:59:20 -05:00
|
|
|
(t/deftest deletion
|
2021-06-07 09:51:09 -05:00
|
|
|
(let [task (:app.tasks.objects-gc/handler th/*system*)
|
|
|
|
profile1 (th/create-profile* 1)
|
|
|
|
file (th/create-file* 1 {:project-id (:default-project-id profile1)
|
|
|
|
:profile-id (:id profile1)})]
|
|
|
|
;; file is not deleted because it does not meet all
|
|
|
|
;; conditions to be deleted.
|
|
|
|
(let [result (task {:max-age (dt/duration 0)})]
|
|
|
|
(t/is (nil? result)))
|
|
|
|
|
|
|
|
;; query the list of files
|
|
|
|
(let [data {::th/type :project-files
|
|
|
|
:project-id (:default-project-id profile1)
|
|
|
|
:profile-id (:id profile1)}
|
|
|
|
out (th/query! data)]
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (nil? (:error out)))
|
|
|
|
(let [result (:result out)]
|
|
|
|
(t/is (= 1 (count result)))))
|
|
|
|
|
|
|
|
;; Request file to be deleted
|
|
|
|
(let [params {::th/type :delete-file
|
|
|
|
:id (:id file)
|
|
|
|
:profile-id (:id profile1)}
|
|
|
|
out (th/mutation! params)]
|
|
|
|
(t/is (nil? (:error out))))
|
|
|
|
|
|
|
|
;; query the list of files after soft deletion
|
|
|
|
(let [data {::th/type :project-files
|
|
|
|
:project-id (:default-project-id profile1)
|
|
|
|
:profile-id (:id profile1)}
|
|
|
|
out (th/query! data)]
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (nil? (:error out)))
|
|
|
|
(let [result (:result out)]
|
|
|
|
(t/is (= 0 (count result)))))
|
|
|
|
|
|
|
|
;; run permanent deletion (should be noop)
|
|
|
|
(let [result (task {:max-age (dt/duration {:minutes 1})})]
|
|
|
|
(t/is (nil? result)))
|
|
|
|
|
|
|
|
;; query the list of file libraries of a after hard deletion
|
|
|
|
(let [data {::th/type :file-libraries
|
|
|
|
:file-id (:id file)
|
|
|
|
:profile-id (:id profile1)}
|
|
|
|
out (th/query! data)]
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (nil? (:error out)))
|
|
|
|
(let [result (:result out)]
|
|
|
|
(t/is (= 0 (count result)))))
|
|
|
|
|
|
|
|
;; run permanent deletion
|
|
|
|
(let [result (task {:max-age (dt/duration 0)})]
|
|
|
|
(t/is (nil? result)))
|
|
|
|
|
|
|
|
;; query the list of file libraries of a after hard deletion
|
|
|
|
(let [data {::th/type :file-libraries
|
|
|
|
:file-id (:id file)
|
|
|
|
:profile-id (:id profile1)}
|
|
|
|
out (th/query! data)]
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(let [error (:error out)
|
|
|
|
error-data (ex-data error)]
|
|
|
|
(t/is (th/ex-info? error))
|
|
|
|
(t/is (= (:type error-data) :not-found))))
|
|
|
|
))
|
2022-02-08 07:25:25 -05:00
|
|
|
|
|
|
|
(t/deftest query-frame-thumbnails
|
2022-03-23 04:59:20 -05:00
|
|
|
(let [prof (th/create-profile* 1 {:is-active true})
|
|
|
|
file (th/create-file* 1 {:profile-id (:id prof)
|
|
|
|
:project-id (:default-project-id prof)
|
|
|
|
:is-shared false})
|
|
|
|
data {::th/type :file-frame-thumbnails
|
2022-02-08 07:25:25 -05:00
|
|
|
:profile-id (:id prof)
|
|
|
|
:file-id (:id file)
|
|
|
|
:frame-id (uuid/next)}]
|
|
|
|
|
2022-03-23 04:59:20 -05:00
|
|
|
;; insert an entry on the database with a test value for the thumbnail of this frame
|
|
|
|
(th/db-insert! :file-frame-thumbnail
|
|
|
|
{:file-id (:file-id data)
|
|
|
|
:frame-id (:frame-id data)
|
|
|
|
:data "testvalue"})
|
2022-02-08 07:25:25 -05:00
|
|
|
|
2022-03-23 04:59:20 -05:00
|
|
|
(let [{:keys [result error] :as out} (th/query! data)]
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (nil? error))
|
|
|
|
(t/is (= 1 (count result)))
|
|
|
|
(t/is (= "testvalue" (get result (:frame-id data)))))))
|
2022-02-08 07:25:25 -05:00
|
|
|
|
|
|
|
(t/deftest insert-frame-thumbnails
|
2022-03-23 04:59:20 -05:00
|
|
|
(let [prof (th/create-profile* 1 {:is-active true})
|
|
|
|
file (th/create-file* 1 {:profile-id (:id prof)
|
|
|
|
:project-id (:default-project-id prof)
|
|
|
|
:is-shared false})
|
|
|
|
data {::th/type :upsert-file-frame-thumbnail
|
2022-02-08 07:25:25 -05:00
|
|
|
:profile-id (:id prof)
|
|
|
|
:file-id (:id file)
|
|
|
|
:frame-id (uuid/next)
|
2022-03-23 04:59:20 -05:00
|
|
|
:data "test insert new value"}]
|
2022-02-08 07:25:25 -05:00
|
|
|
|
2022-03-23 04:59:20 -05:00
|
|
|
(let [out (th/mutation! data)]
|
|
|
|
(t/is (nil? (:error out)))
|
|
|
|
(t/is (nil? (:result out)))
|
|
|
|
(let [[result] (th/db-query :file-frame-thumbnail
|
|
|
|
{:file-id (:file-id data)
|
|
|
|
:frame-id (:frame-id data)})]
|
|
|
|
(t/is (= "test insert new value" (:data result)))))))
|
2022-02-08 07:25:25 -05:00
|
|
|
|
2022-03-23 04:59:20 -05:00
|
|
|
(t/deftest upsert-frame-thumbnails
|
2022-02-08 07:25:25 -05:00
|
|
|
(let [prof (th/create-profile* 1 {:is-active true})
|
|
|
|
file (th/create-file* 1 {:profile-id (:id prof)
|
|
|
|
:project-id (:default-project-id prof)
|
|
|
|
:is-shared false})
|
2022-03-23 04:59:20 -05:00
|
|
|
data {::th/type :upsert-file-frame-thumbnail
|
2022-02-08 07:25:25 -05:00
|
|
|
:profile-id (:id prof)
|
|
|
|
:file-id (:id file)
|
|
|
|
:frame-id (uuid/next)
|
|
|
|
:data "updated value"}]
|
|
|
|
|
2022-03-23 04:59:20 -05:00
|
|
|
;; insert an entry on the database with and old value for the thumbnail of this frame
|
|
|
|
(th/db-insert! :file-frame-thumbnail
|
|
|
|
{:file-id (:file-id data)
|
|
|
|
:frame-id (:frame-id data)
|
|
|
|
:data "old value"})
|
2022-02-08 07:25:25 -05:00
|
|
|
|
|
|
|
(let [out (th/mutation! data)]
|
2022-03-23 04:59:20 -05:00
|
|
|
;; (th/print-result! out)
|
|
|
|
|
2022-02-08 07:25:25 -05:00
|
|
|
(t/is (nil? (:error out)))
|
|
|
|
(t/is (nil? (:result out)))
|
|
|
|
|
2022-03-23 04:59:20 -05:00
|
|
|
;; retrieve the value from the database and check its content
|
|
|
|
(let [[result] (th/db-query :file-frame-thumbnail
|
|
|
|
{:file-id (:file-id data)
|
|
|
|
:frame-id (:frame-id data)})]
|
2022-02-08 07:25:25 -05:00
|
|
|
(t/is (= "updated value" (:data result)))))))
|
2022-03-23 04:59:20 -05:00
|
|
|
|
|
|
|
|
|
|
|
(t/deftest file-thumbnail-ops
|
|
|
|
(let [prof (th/create-profile* 1 {:is-active true})
|
|
|
|
file (th/create-file* 1 {:profile-id (:id prof)
|
|
|
|
:project-id (:default-project-id prof)
|
|
|
|
:revn 2
|
|
|
|
:is-shared false})
|
|
|
|
data {::th/type :file-thumbnail
|
|
|
|
:profile-id (:id prof)
|
|
|
|
:file-id (:id file)}]
|
|
|
|
|
|
|
|
(t/testing "query a thumbnail with single revn"
|
|
|
|
|
|
|
|
;; insert an entry on the database with a test value for the thumbnail of this frame
|
|
|
|
(th/db-insert! :file-thumbnail
|
|
|
|
{:file-id (:file-id data)
|
|
|
|
:revn 1
|
|
|
|
:data "testvalue1"})
|
|
|
|
|
|
|
|
(let [{:keys [result error] :as out} (th/query! data)]
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (nil? error))
|
|
|
|
(t/is (= 4 (count result)))
|
|
|
|
(t/is (= "testvalue1" (:data result)))
|
|
|
|
(t/is (= 1 (:revn result)))))
|
|
|
|
|
|
|
|
(t/testing "query thumbnail with two revisions"
|
|
|
|
;; insert an entry on the database with a test value for the thumbnail of this frame
|
|
|
|
(th/db-insert! :file-thumbnail
|
|
|
|
{:file-id (:file-id data)
|
|
|
|
:revn 2
|
|
|
|
:data "testvalue2"})
|
|
|
|
|
|
|
|
(let [{:keys [result error] :as out} (th/query! data)]
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (nil? error))
|
|
|
|
(t/is (= 4 (count result)))
|
|
|
|
(t/is (= "testvalue2" (:data result)))
|
|
|
|
(t/is (= 2 (:revn result))))
|
|
|
|
|
|
|
|
;; Then query the specific revn
|
|
|
|
(let [{:keys [result error] :as out} (th/query! (assoc data :revn 1))]
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (nil? error))
|
|
|
|
(t/is (= 4 (count result)))
|
|
|
|
(t/is (= "testvalue1" (:data result)))
|
|
|
|
(t/is (= 1 (:revn result)))))
|
|
|
|
|
|
|
|
(t/testing "upsert file-thumbnail"
|
|
|
|
(let [data {::th/type :upsert-file-thumbnail
|
|
|
|
:profile-id (:id prof)
|
|
|
|
:file-id (:id file)
|
|
|
|
:data "foobar"
|
|
|
|
:props {:baz 1}
|
|
|
|
:revn 2}
|
|
|
|
{:keys [result error] :as out} (th/mutation! data)]
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (nil? error))
|
|
|
|
(t/is (nil? result))))
|
|
|
|
|
|
|
|
(t/testing "query last result"
|
|
|
|
(let [{:keys [result error] :as out} (th/query! data)]
|
|
|
|
;; (th/print-result! out)
|
|
|
|
(t/is (nil? error))
|
|
|
|
(t/is (= 4 (count result)))
|
|
|
|
(t/is (= "foobar" (:data result)))
|
|
|
|
(t/is (= {:baz 1} (:props result)))
|
|
|
|
(t/is (= 2 (:revn result)))))
|
|
|
|
|
|
|
|
(t/testing "gc task"
|
|
|
|
;; make the file eligible for GC waiting 300ms (configured
|
|
|
|
;; timeout for testing)
|
|
|
|
(th/sleep 300)
|
|
|
|
|
|
|
|
;; run the task again
|
|
|
|
(let [task (:app.tasks.file-gc/handler th/*system*)
|
|
|
|
res (task {})]
|
|
|
|
(t/is (= 1 (:processed res))))
|
|
|
|
|
|
|
|
;; Then query the specific revn
|
|
|
|
(let [{:keys [result error] :as out} (th/query! (assoc data :revn 1))]
|
|
|
|
(t/is (= :not-found (th/ex-type error)))
|
|
|
|
(t/is (= :file-thumbnail-not-found (th/ex-code error)))))
|
|
|
|
))
|
|
|
|
|
|
|
|
|