mirror of
https://github.com/logseq/logseq.git
synced 2026-05-25 13:14:39 +00:00
refactor(cli): centralize output mode handling
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
"Shared CLI parsing utilities."
|
||||
(:require [babashka.cli :as cli]
|
||||
[clojure.string :as string]
|
||||
[logseq.cli.output-mode :as output-mode]
|
||||
[logseq.cli.style :as style]
|
||||
[logseq.common.config :as common-config]))
|
||||
|
||||
@@ -22,7 +23,7 @@
|
||||
:coerce :long}
|
||||
:output {:desc "Output format. Default: human"
|
||||
:alias :o
|
||||
:validate #{"human" "json" "edn"}}
|
||||
:validate output-mode/allowed-values}
|
||||
:verbose {:desc "Enable verbose debug logging to stderr"
|
||||
:alias :v
|
||||
:coerce :boolean}
|
||||
@@ -244,6 +245,12 @@
|
||||
|
||||
:else nil))
|
||||
|
||||
(defn- valid-leading-global-value?
|
||||
[opt-key value]
|
||||
(case opt-key
|
||||
:output (contains? output-mode/allowed-values value)
|
||||
true))
|
||||
|
||||
(defn parse-leading-global-opts
|
||||
[args]
|
||||
(loop [remaining args
|
||||
@@ -255,7 +262,9 @@
|
||||
(if (contains? global-flag-options opt-key)
|
||||
(recur (rest remaining) (assoc opts opt-key true))
|
||||
(if-let [value (second remaining)]
|
||||
(recur (drop 2 remaining) (assoc opts opt-key value))
|
||||
(if (valid-leading-global-value? opt-key value)
|
||||
(recur (drop 2 remaining) (assoc opts opt-key value))
|
||||
{:opts opts :args remaining})
|
||||
{:opts opts :args (rest remaining)}))
|
||||
{:opts opts :args remaining})))))
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
[clojure.walk :as walk]
|
||||
[logseq.cli.command.core :as core]
|
||||
[logseq.cli.command.id :as id-command]
|
||||
[logseq.cli.output-mode :as output-mode]
|
||||
[logseq.cli.server :as cli-server]
|
||||
[logseq.cli.style :as style]
|
||||
[logseq.cli.transport :as transport]
|
||||
@@ -999,85 +1000,69 @@
|
||||
(p/let [tree-data (attach-property-titles config (:repo action) tree-data)]
|
||||
(render-tree-text tree-data action)))
|
||||
|
||||
(defn- sanitize-structured-tree
|
||||
[tree-data]
|
||||
(-> tree-data
|
||||
strip-show-internal-data
|
||||
strip-block-uuid))
|
||||
|
||||
(defn- structured-show-result
|
||||
[mode data]
|
||||
{:status :ok
|
||||
:data data
|
||||
:output-format mode})
|
||||
|
||||
(defn- multi-id-structured-data
|
||||
[results]
|
||||
(mapv (fn [{:keys [ok? tree id error]}]
|
||||
(if ok?
|
||||
(sanitize-structured-tree tree)
|
||||
(multi-id-error-entry id error)))
|
||||
results))
|
||||
|
||||
(defn execute-show
|
||||
[action config]
|
||||
(-> (p/let [cfg (cli-server/ensure-server! config (:repo action))
|
||||
format (:output-format config)
|
||||
ids (:ids action)
|
||||
multi-id? (:multi-id? action)]
|
||||
(if (and (seq ids) multi-id?)
|
||||
(p/let [results (p/all (map (fn [id]
|
||||
(-> (build-tree-data cfg (assoc action :id id))
|
||||
(p/then (fn [tree-data]
|
||||
{:ok? true
|
||||
:id id
|
||||
:tree tree-data}))
|
||||
(p/catch (fn [error]
|
||||
{:ok? false
|
||||
:id id
|
||||
:error error}))))
|
||||
ids))
|
||||
ok-results (filter :ok? results)
|
||||
id->tree-ids (into {}
|
||||
(map (fn [{:keys [id tree]}]
|
||||
[id (collect-tree-ids (:root tree))]))
|
||||
ok-results)
|
||||
contained? (fn [id]
|
||||
(some (fn [[other-id tree-ids]]
|
||||
(and (not= other-id id)
|
||||
(contains? tree-ids id)))
|
||||
id->tree-ids))
|
||||
results (vec (remove (fn [{:keys [ok? id]}]
|
||||
(and ok? (contained? id)))
|
||||
results))
|
||||
sanitize-tree (fn [tree]
|
||||
(-> tree
|
||||
strip-show-internal-data
|
||||
strip-block-uuid))
|
||||
render-result (fn [{:keys [ok? tree id error]}]
|
||||
(if ok?
|
||||
(render-tree-text-with-properties cfg action tree)
|
||||
(multi-id-error-message id error)))]
|
||||
(case format
|
||||
:edn
|
||||
{:status :ok
|
||||
:data (mapv (fn [{:keys [ok? tree id error]}]
|
||||
(if ok?
|
||||
(sanitize-tree tree)
|
||||
(multi-id-error-entry id error)))
|
||||
results)
|
||||
:output-format :edn}
|
||||
|
||||
:json
|
||||
{:status :ok
|
||||
:data (mapv (fn [{:keys [ok? tree id error]}]
|
||||
(if ok?
|
||||
(sanitize-tree tree)
|
||||
(multi-id-error-entry id error)))
|
||||
results)
|
||||
:output-format :json}
|
||||
|
||||
(p/let [messages (p/all (map render-result results))]
|
||||
{:status :ok
|
||||
:data {:message (string/join multi-id-delimiter messages)}})))
|
||||
(p/let [tree-data (build-tree-data cfg action)]
|
||||
(case format
|
||||
:edn
|
||||
(let [tree-data (-> tree-data
|
||||
strip-show-internal-data
|
||||
strip-block-uuid)]
|
||||
{:status :ok
|
||||
:data tree-data
|
||||
:output-format :edn})
|
||||
|
||||
:json
|
||||
(let [tree-data (-> tree-data
|
||||
strip-show-internal-data
|
||||
strip-block-uuid)]
|
||||
{:status :ok
|
||||
:data tree-data
|
||||
:output-format :json})
|
||||
|
||||
(p/let [message (render-tree-text-with-properties cfg action tree-data)]
|
||||
{:status :ok
|
||||
:data {:message message}})))))))
|
||||
(p/let [cfg (cli-server/ensure-server! config (:repo action))
|
||||
mode (output-mode/parse (:output-format config))
|
||||
ids (:ids action)
|
||||
multi-id? (:multi-id? action)]
|
||||
(if (and (seq ids) multi-id?)
|
||||
(p/let [results (p/all (map (fn [id]
|
||||
(-> (build-tree-data cfg (assoc action :id id))
|
||||
(p/then (fn [tree-data]
|
||||
{:ok? true
|
||||
:id id
|
||||
:tree tree-data}))
|
||||
(p/catch (fn [error]
|
||||
{:ok? false
|
||||
:id id
|
||||
:error error}))))
|
||||
ids))
|
||||
ok-results (filter :ok? results)
|
||||
id->tree-ids (into {}
|
||||
(map (fn [{:keys [id tree]}]
|
||||
[id (collect-tree-ids (:root tree))]))
|
||||
ok-results)
|
||||
contained? (fn [id]
|
||||
(some (fn [[other-id tree-ids]]
|
||||
(and (not= other-id id)
|
||||
(contains? tree-ids id)))
|
||||
id->tree-ids))
|
||||
results (vec (remove (fn [{:keys [ok? id]}]
|
||||
(and ok? (contained? id)))
|
||||
results))
|
||||
render-result (fn [{:keys [ok? tree id error]}]
|
||||
(if ok?
|
||||
(render-tree-text-with-properties cfg action tree)
|
||||
(multi-id-error-message id error)))]
|
||||
(if (output-mode/structured? mode)
|
||||
(structured-show-result mode (multi-id-structured-data results))
|
||||
(p/let [messages (p/all (map render-result results))]
|
||||
{:status :ok
|
||||
:data {:message (string/join multi-id-delimiter messages)}})))
|
||||
(p/let [tree-data (build-tree-data cfg action)]
|
||||
(if (output-mode/structured? mode)
|
||||
(structured-show-result mode (sanitize-structured-tree tree-data))
|
||||
(p/let [message (render-tree-text-with-properties cfg action tree-data)]
|
||||
{:status :ok
|
||||
:data {:message message}}))))))
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
[logseq.cli.auth :as cli-auth]
|
||||
[logseq.cli.command.core :as core]
|
||||
[logseq.cli.config :as cli-config]
|
||||
[logseq.cli.output-mode :as output-mode]
|
||||
[logseq.cli.server :as cli-server]
|
||||
[logseq.cli.transport :as transport]
|
||||
[logseq.common.cognito-config :as cognito-config]
|
||||
@@ -76,7 +77,6 @@
|
||||
(def ^:private sync-start-timeout-ms 10000)
|
||||
(def ^:private sync-start-poll-interval-ms 1000)
|
||||
(def ^:private sync-download-timeout-ms (* 30 60 1000))
|
||||
(def ^:private structured-output-formats #{:json :edn})
|
||||
|
||||
(def ^:private sync-start-skipped-states
|
||||
#{:inactive :stopped})
|
||||
@@ -560,7 +560,7 @@
|
||||
[action config]
|
||||
(if (:progress-explicit? action)
|
||||
(true? (:progress action))
|
||||
(not (contains? structured-output-formats (:output-format config)))))
|
||||
(not (output-mode/structured? (:output-format config)))))
|
||||
|
||||
(defn- download-progress-message
|
||||
[graph-id event-type payload]
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
["fs" :as fs]
|
||||
["os" :as os]
|
||||
["path" :as node-path]
|
||||
[logseq.cli.output-mode :as output-mode]
|
||||
[logseq.common.graph :as common-graph]))
|
||||
|
||||
(defn- parse-int
|
||||
@@ -30,17 +31,6 @@
|
||||
|
||||
:else nil))
|
||||
|
||||
(def ^:private output-formats
|
||||
#{:human :json :edn})
|
||||
|
||||
(defn- parse-output-format
|
||||
[value]
|
||||
(let [kw (cond
|
||||
(keyword? value) value
|
||||
(string? value) (-> value string/trim string/lower-case keyword)
|
||||
:else nil)]
|
||||
(when (output-formats kw)
|
||||
kw)))
|
||||
|
||||
(defn- default-config-path
|
||||
[]
|
||||
@@ -105,7 +95,7 @@
|
||||
(assoc :logout-timeout-ms (parse-int (gobj/get env "LOGSEQ_CLI_LOGOUT_TIMEOUT_MS")))
|
||||
|
||||
(seq (gobj/get env "LOGSEQ_CLI_OUTPUT"))
|
||||
(assoc :output-format (parse-output-format (gobj/get env "LOGSEQ_CLI_OUTPUT")))
|
||||
(assoc :output-format (output-mode/parse (gobj/get env "LOGSEQ_CLI_OUTPUT")))
|
||||
|
||||
(seq (gobj/get env "LOGSEQ_CLI_CONFIG"))
|
||||
(assoc :config-path (gobj/get env "LOGSEQ_CLI_CONFIG")))))
|
||||
@@ -126,12 +116,12 @@
|
||||
(:config-path env)
|
||||
(:config-path defaults))
|
||||
file-config (or (read-config-file config-path) {})
|
||||
output-format (or (parse-output-format (:output-format opts))
|
||||
(parse-output-format (:output opts))
|
||||
(parse-output-format (:output-format env))
|
||||
(parse-output-format (:output env))
|
||||
(parse-output-format (:output-format file-config))
|
||||
(parse-output-format (:output file-config)))
|
||||
output-format (or (output-mode/parse (:output-format opts))
|
||||
(output-mode/parse (:output opts))
|
||||
(output-mode/parse (:output-format env))
|
||||
(output-mode/parse (:output env))
|
||||
(output-mode/parse (:output-format file-config))
|
||||
(output-mode/parse (:output file-config)))
|
||||
merged (merge defaults file-config env opts {:config-path config-path})
|
||||
list-title-max-display-width (or (parse-positive-int (:list-title-max-display-width merged))
|
||||
list-title-max-display-width-default)]
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
[clojure.string :as string]
|
||||
[clojure.walk :as walk]
|
||||
[logseq.cli.command.core :as command-core]
|
||||
[logseq.cli.output-mode :as output-mode]
|
||||
[logseq.cli.style :as style]
|
||||
[logseq.common.util :as common-util]
|
||||
["string-width" :default string-width]))
|
||||
@@ -954,11 +955,8 @@
|
||||
(let [result (-> result
|
||||
normalize-graph-result
|
||||
sanitize-result)
|
||||
format (cond
|
||||
(= output-format :edn) :edn
|
||||
(= output-format :json) :json
|
||||
:else :human)]
|
||||
(case format
|
||||
mode (or (output-mode/parse output-format) :human)]
|
||||
(case mode
|
||||
:json (->json result)
|
||||
:edn (->edn result)
|
||||
(->human result opts))))
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
"CLI entrypoint for invoking db-worker-node."
|
||||
(:refer-clojure :exclude [run!])
|
||||
(:require [lambdaisland.glogi :as log]
|
||||
[logseq.cli.command.core :as command-core]
|
||||
[logseq.cli.commands :as commands]
|
||||
[logseq.cli.config :as config]
|
||||
[logseq.cli.data-dir :as data-dir]
|
||||
[logseq.cli.format :as format]
|
||||
[logseq.cli.log :as cli-log]
|
||||
[logseq.cli.output-mode :as output-mode]
|
||||
[logseq.cli.profile :as profile]
|
||||
[logseq.cli.version :as version]
|
||||
[promesa.core :as p]))
|
||||
@@ -44,6 +46,30 @@
|
||||
:status (if (zero? (:exit-code result)) :ok :error)})))
|
||||
result))
|
||||
|
||||
(defn- parse-argv-output-format
|
||||
[args]
|
||||
(let [{:keys [opts]} (command-core/parse-leading-global-opts args)]
|
||||
(or (output-mode/parse (:output-format opts))
|
||||
(output-mode/parse (:output opts)))))
|
||||
|
||||
(defn- parsed-output-format
|
||||
[parsed]
|
||||
(or (output-mode/parse (get-in parsed [:options :output-format]))
|
||||
(output-mode/parse (get-in parsed [:options :output]))))
|
||||
|
||||
(defn- resolve-output-format
|
||||
[args parsed cfg result]
|
||||
(or (output-mode/parse (:output-format result))
|
||||
(output-mode/parse (:output-format cfg))
|
||||
(parsed-output-format parsed)
|
||||
(parse-argv-output-format args)))
|
||||
|
||||
(defn- format-opts
|
||||
[args parsed cfg result]
|
||||
(if-let [mode (resolve-output-format args parsed cfg result)]
|
||||
{:output-format mode}
|
||||
{}))
|
||||
|
||||
(defn- handle-unexpected-error
|
||||
"Provide clean, consistent error handling for unexpected errors in run!"
|
||||
[profile-session parsed cfg error]
|
||||
@@ -86,9 +112,18 @@
|
||||
(cond
|
||||
(:help? parsed)
|
||||
(p/resolved
|
||||
(attach-profile-lines profile-session parsed
|
||||
{:exit-code 0
|
||||
:output (:summary parsed)}))
|
||||
(let [mode (resolve-output-format args parsed nil nil)]
|
||||
(attach-profile-lines
|
||||
profile-session
|
||||
parsed
|
||||
{:exit-code 0
|
||||
:output (if (output-mode/structured? mode)
|
||||
(profile/time! profile-session "cli.format-result"
|
||||
(fn []
|
||||
(format/format-result {:status :ok
|
||||
:data {:message (:summary parsed)}}
|
||||
{:output-format mode})))
|
||||
(:summary parsed))})))
|
||||
|
||||
(not (:ok? parsed))
|
||||
(p/resolved
|
||||
@@ -101,7 +136,7 @@
|
||||
(format/format-result {:status :error
|
||||
:error (:error parsed)
|
||||
:command (:command parsed)}
|
||||
{})))}))
|
||||
(format-opts args parsed nil nil))))}))
|
||||
|
||||
(= :version (:command parsed))
|
||||
(p/resolved
|
||||
@@ -154,9 +189,7 @@
|
||||
(fn []
|
||||
(commands/execute (:action action-result) cfg)))
|
||||
(p/then (fn [result]
|
||||
(let [opts (cond-> cfg
|
||||
(:output-format result)
|
||||
(assoc :output-format (:output-format result)))]
|
||||
(let [opts (merge cfg (format-opts args parsed cfg result))]
|
||||
(attach-profile-lines
|
||||
profile-session
|
||||
parsed
|
||||
|
||||
29
src/main/logseq/cli/output_mode.cljs
Normal file
29
src/main/logseq/cli/output_mode.cljs
Normal file
@@ -0,0 +1,29 @@
|
||||
(ns logseq.cli.output-mode
|
||||
"Shared output mode utilities for CLI human/json/edn output handling."
|
||||
(:require [clojure.string :as string]))
|
||||
|
||||
(def allowed-keywords
|
||||
#{:human :json :edn})
|
||||
|
||||
(def allowed-values
|
||||
(into #{} (map name) allowed-keywords))
|
||||
|
||||
(def ^:private structured-keywords
|
||||
#{:json :edn})
|
||||
|
||||
(defn parse
|
||||
[value]
|
||||
(let [mode (cond
|
||||
(keyword? value) value
|
||||
(string? value) (some-> value string/trim string/lower-case keyword)
|
||||
:else nil)]
|
||||
(when (contains? allowed-keywords mode)
|
||||
mode)))
|
||||
|
||||
(defn string-value
|
||||
[value]
|
||||
(some-> (parse value) name))
|
||||
|
||||
(defn structured?
|
||||
[value]
|
||||
(contains? structured-keywords (parse value)))
|
||||
@@ -202,6 +202,14 @@
|
||||
|
||||
(p/resolved nil))))
|
||||
|
||||
(defn- contains-block-uuid?
|
||||
[value]
|
||||
(cond
|
||||
(map? value) (or (contains? value :block/uuid)
|
||||
(some contains-block-uuid? (vals value)))
|
||||
(sequential? value) (some contains-block-uuid? value)
|
||||
:else false))
|
||||
|
||||
(deftest test-render-referenced-entities-footer
|
||||
(let [render-footer (fn [ordered-uuids uuid->entity]
|
||||
(call-private 'render-referenced-entities-footer ordered-uuids uuid->entity))
|
||||
@@ -325,7 +333,7 @@
|
||||
:block/title "Root"
|
||||
:block/page {:db/id 201}}}
|
||||
:children-by-page-id {201 [{:db/id 12
|
||||
:block/title (str "Child [[" uuid-a "]]")
|
||||
:block/title (str "Child [[" uuid-a "]]" )
|
||||
:block/order 0
|
||||
:block/parent {:db/id 1}}]}
|
||||
:uuid-entities {(string/lower-case uuid-a)
|
||||
@@ -345,3 +353,66 @@
|
||||
(is (not (string/includes? plain "Referenced Entities (")))))
|
||||
(p/catch (fn [e] (is false (str "unexpected error: " e))))
|
||||
(p/finally done)))))
|
||||
|
||||
(deftest test-execute-show-structured-output-single-and-multi-id
|
||||
(async done
|
||||
(let [invoke-mock (make-show-invoke-mock
|
||||
{:entities-by-id {1 {:db/id 1
|
||||
:block/uuid (uuid "11111111-1111-1111-1111-111111111111")
|
||||
:block/title "Root A"
|
||||
:block/page {:db/id 101}}
|
||||
2 {:db/id 2
|
||||
:block/uuid (uuid "22222222-2222-2222-2222-222222222222")
|
||||
:block/title "Root B"
|
||||
:block/page {:db/id 102}}}
|
||||
:children-by-page-id {101 [{:db/id 11
|
||||
:block/uuid (uuid "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
|
||||
:block/title "Child A"
|
||||
:block/order 0
|
||||
:block/parent {:db/id 1}}]
|
||||
102 [{:db/id 22
|
||||
:block/uuid (uuid "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb")
|
||||
:block/title "Child B"
|
||||
:block/order 0
|
||||
:block/parent {:db/id 2}}]}})]
|
||||
(-> (p/with-redefs [cli-server/ensure-server! (fn [config _] config)
|
||||
transport/invoke invoke-mock]
|
||||
(p/let [single-json (show-command/execute-show {:type :show
|
||||
:repo "demo"
|
||||
:id 1
|
||||
:linked-references? false}
|
||||
{:output-format :json})
|
||||
single-edn (show-command/execute-show {:type :show
|
||||
:repo "demo"
|
||||
:id 1
|
||||
:linked-references? false}
|
||||
{:output-format :edn})
|
||||
multi-json (show-command/execute-show {:type :show
|
||||
:repo "demo"
|
||||
:ids [1 2]
|
||||
:multi-id? true
|
||||
:linked-references? false}
|
||||
{:output-format :json})
|
||||
multi-edn (show-command/execute-show {:type :show
|
||||
:repo "demo"
|
||||
:ids [1 2]
|
||||
:multi-id? true
|
||||
:linked-references? false}
|
||||
{:output-format :edn})]
|
||||
(doseq [result [single-json single-edn multi-json multi-edn]]
|
||||
(is (= :ok (:status result)))
|
||||
(is (not (contains-block-uuid? (:data result)))))
|
||||
|
||||
(is (= :json (:output-format single-json)))
|
||||
(is (= :edn (:output-format single-edn)))
|
||||
(is (= 1 (get-in single-json [:data :root :db/id])))
|
||||
(is (= 1 (get-in single-edn [:data :root :db/id])))
|
||||
|
||||
(is (= :json (:output-format multi-json)))
|
||||
(is (= :edn (:output-format multi-edn)))
|
||||
(is (= [1 2]
|
||||
(mapv #(get-in % [:root :db/id]) (:data multi-json))))
|
||||
(is (= [1 2]
|
||||
(mapv #(get-in % [:root :db/id]) (:data multi-edn))))))
|
||||
(p/catch (fn [e] (is false (str "unexpected error: " e))))
|
||||
(p/finally done)))))
|
||||
|
||||
@@ -688,6 +688,16 @@
|
||||
_ (is (= [] @subscribe-calls))
|
||||
_ (is (= [] @printed-lines))
|
||||
_ (is (= 0 @close-calls))
|
||||
_ (execute-with-runtime-auth {:type :sync-download
|
||||
:repo "logseq_db_demo"
|
||||
:graph "demo"
|
||||
:progress-explicit? false}
|
||||
{:base-url "http://example"
|
||||
:data-dir "/tmp"
|
||||
:output-format :edn})
|
||||
_ (is (= [] @subscribe-calls))
|
||||
_ (is (= [] @printed-lines))
|
||||
_ (is (= 0 @close-calls))
|
||||
_ (execute-with-runtime-auth {:type :sync-download
|
||||
:repo "logseq_db_demo"
|
||||
:graph "demo"
|
||||
|
||||
@@ -528,6 +528,12 @@
|
||||
(is (true? (:ok? result)))
|
||||
(is (= "json" (get-in result [:options :output])))))
|
||||
|
||||
(testing "global output option rejects invalid values"
|
||||
(let [result (commands/parse-args ["--output" "yaml" "graph" "list"])]
|
||||
(is (false? (:ok? result)))
|
||||
(is (= :invalid-options (get-in result [:error :code])))
|
||||
(is (string/includes? (string/lower-case (get-in result [:error :message])) "output"))))
|
||||
|
||||
(testing "global profile option defaults to absent"
|
||||
(let [result (commands/parse-args ["graph" "list"])]
|
||||
(is (true? (:ok? result)))
|
||||
|
||||
@@ -89,6 +89,15 @@
|
||||
:output "json"})]
|
||||
(is (= :edn (:output-format result)))))
|
||||
|
||||
(deftest test-output-format-invalid-values-fallback
|
||||
(let [dir (node-helper/create-tmp-dir)
|
||||
cfg-path (node-path/join dir "cli.edn")
|
||||
_ (fs/writeFileSync cfg-path "{:output-format :edn}")
|
||||
env {"LOGSEQ_CLI_OUTPUT" "yaml"}
|
||||
result (with-env env #(config/resolve-config {:config-path cfg-path
|
||||
:output "xml"}))]
|
||||
(is (= :edn (:output-format result)))))
|
||||
|
||||
(deftest test-default-paths
|
||||
(let [result (config/resolve-config {})
|
||||
expected-config-path (node-path/join (.homedir os) "logseq" "cli.edn")]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns logseq.cli.main-test
|
||||
(:require [cljs.test :refer [async deftest is]]
|
||||
(:require [cljs.reader :as reader]
|
||||
[cljs.test :refer [async deftest is]]
|
||||
[clojure.string :as string]
|
||||
[logseq.cli.commands :as commands]
|
||||
[logseq.cli.main :as cli-main]
|
||||
@@ -28,6 +29,40 @@
|
||||
(done)))
|
||||
(p/finally done))))
|
||||
|
||||
(deftest test-help-output-respects-structured-modes
|
||||
(async done
|
||||
(-> (p/let [json-result (cli-main/run! ["--output" "json" "--help"] {:exit? false})
|
||||
edn-result (cli-main/run! ["--output" "edn" "--help"] {:exit? false})
|
||||
json-output (js->clj (js/JSON.parse (:output json-result)) :keywordize-keys true)
|
||||
edn-output (reader/read-string (:output edn-result))]
|
||||
(is (= 0 (:exit-code json-result)))
|
||||
(is (= 0 (:exit-code edn-result)))
|
||||
(is (= "ok" (:status json-output)))
|
||||
(is (= :ok (:status edn-output)))
|
||||
(is (string/includes? (get-in json-output [:data :message]) "Usage: logseq"))
|
||||
(is (string/includes? (get-in edn-output [:data :message]) "Usage: logseq")))
|
||||
(p/catch (fn [e]
|
||||
(is false (str "unexpected error: " e))
|
||||
(done)))
|
||||
(p/finally done))))
|
||||
|
||||
(deftest test-parse-error-output-respects-structured-modes
|
||||
(async done
|
||||
(-> (p/let [json-result (cli-main/run! ["--output" "json" "wat"] {:exit? false})
|
||||
edn-result (cli-main/run! ["--output" "edn" "wat"] {:exit? false})
|
||||
json-output (js->clj (js/JSON.parse (:output json-result)) :keywordize-keys true)
|
||||
edn-output (reader/read-string (:output edn-result))]
|
||||
(is (= 1 (:exit-code json-result)))
|
||||
(is (= 1 (:exit-code edn-result)))
|
||||
(is (= "error" (:status json-output)))
|
||||
(is (= :error (:status edn-output)))
|
||||
(is (some? (get-in json-output [:error :message])))
|
||||
(is (some? (get-in edn-output [:error :message]))))
|
||||
(p/catch (fn [e]
|
||||
(is false (str "unexpected error: " e))
|
||||
(done)))
|
||||
(p/finally done))))
|
||||
|
||||
(deftest test-result->exit-code
|
||||
(let [result->exit-code #'cli-main/result->exit-code]
|
||||
(is (= 0 (result->exit-code {:status :ok})))
|
||||
|
||||
31
src/test/logseq/cli/output_mode_test.cljs
Normal file
31
src/test/logseq/cli/output_mode_test.cljs
Normal file
@@ -0,0 +1,31 @@
|
||||
(ns logseq.cli.output-mode-test
|
||||
(:require [cljs.test :refer [deftest is testing]]
|
||||
[logseq.cli.output-mode :as output-mode]))
|
||||
|
||||
(deftest test-allowed-output-modes
|
||||
(is (= #{:human :json :edn} output-mode/allowed-keywords))
|
||||
(is (= #{"human" "json" "edn"} output-mode/allowed-values)))
|
||||
|
||||
(deftest test-parse
|
||||
(testing "parses string and keyword output modes"
|
||||
(is (= :human (output-mode/parse "human")))
|
||||
(is (= :json (output-mode/parse " JSON ")))
|
||||
(is (= :edn (output-mode/parse :edn))))
|
||||
|
||||
(testing "returns nil for unknown or blank values"
|
||||
(is (nil? (output-mode/parse "")))
|
||||
(is (nil? (output-mode/parse " ")))
|
||||
(is (nil? (output-mode/parse "yaml")))
|
||||
(is (nil? (output-mode/parse :yaml)))
|
||||
(is (nil? (output-mode/parse nil)))))
|
||||
|
||||
(deftest test-structured-output
|
||||
(is (true? (output-mode/structured? :json)))
|
||||
(is (true? (output-mode/structured? "edn")))
|
||||
(is (false? (output-mode/structured? :human)))
|
||||
(is (false? (output-mode/structured? "yaml"))))
|
||||
|
||||
(deftest test-string-value
|
||||
(is (= "json" (output-mode/string-value :json)))
|
||||
(is (= "edn" (output-mode/string-value "EDN")))
|
||||
(is (nil? (output-mode/string-value :yaml))))
|
||||
Reference in New Issue
Block a user