diff --git a/cli-e2e/AGENTS.md b/cli-e2e/AGENTS.md index 299d25c729..f7bca804c7 100644 --- a/cli-e2e/AGENTS.md +++ b/cli-e2e/AGENTS.md @@ -18,6 +18,7 @@ Shell-first end-to-end tests for logseq CLI. - List declared sync case ids: `bb list-sync-cases` - Run sync cases with build preflight unless `--skip-build` is provided: `bb test-sync` - `bb test-sync --help` for options + - Configure sync E2EE password: `--e2ee-password ` (default: `11111`) - Run only sync MVP case: `bb test-sync --skip-build --case sync-upload-download-mvp` ### Sync suite prerequisites diff --git a/cli-e2e/spec/sync_cases.edn b/cli-e2e/spec/sync_cases.edn index 6fd04c738d..211f028408 100644 --- a/cli-e2e/spec/sync_cases.edn +++ b/cli-e2e/spec/sync_cases.edn @@ -33,13 +33,13 @@ "python3 '{{repo-root}}/cli-e2e/scripts/prepare_sync_config.py' --output '{{tmp-dir}}/cli-b.edn' --auth-path '{{auth-path}}' --http-base '{{sync-http-base}}' --ws-url '{{sync-ws-url}}'" "python3 '{{repo-root}}/cli-e2e/scripts/db_sync_server.py' start --repo-root '{{repo-root}}' --pid-file '{{tmp-dir}}/db-sync-server.pid' --log-file '{{tmp-dir}}/db-sync-server.log' --data-dir '{{tmp-dir}}/db-sync-server-data' --port {{sync-port}} --auth-path '{{auth-path}}'" "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json graph create --graph {{graph-arg}} >/dev/null" - "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync ensure-keys --graph {{graph-arg}} --e2ee-password '11111' --upload-keys"], + "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync ensure-keys --graph {{graph-arg}} --e2ee-password {{e2ee-password-arg}} --upload-keys"], :cmds ["{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync upload --graph {{graph-arg}}" - "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync start --graph {{graph-arg}} --e2ee-password '11111'" + "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync start --graph {{graph-arg}} --e2ee-password {{e2ee-password-arg}}" "python3 '{{repo-root}}/cli-e2e/scripts/wait_sync_status.py' --cli '{{repo-root}}/static/logseq-cli.js' --data-dir '{{data-dir}}' --config '{{config-path}}' --graph '{{graph}}' --timeout-s 120 --interval-s 1" - "{{cli-home}} --data-dir '{{tmp-dir}}/graphs-b' --config '{{tmp-dir}}/cli-b.edn' --output json sync download --graph {{graph-arg}} --e2ee-password '11111'" - "{{cli-home}} --data-dir '{{tmp-dir}}/graphs-b' --config '{{tmp-dir}}/cli-b.edn' --output json sync start --graph {{graph-arg}} --e2ee-password '11111'" + "{{cli-home}} --data-dir '{{tmp-dir}}/graphs-b' --config '{{tmp-dir}}/cli-b.edn' --output json sync download --graph {{graph-arg}} --e2ee-password {{e2ee-password-arg}}" + "{{cli-home}} --data-dir '{{tmp-dir}}/graphs-b' --config '{{tmp-dir}}/cli-b.edn' --output json sync start --graph {{graph-arg}} --e2ee-password {{e2ee-password-arg}}" "python3 '{{repo-root}}/cli-e2e/scripts/wait_sync_status.py' --cli '{{repo-root}}/static/logseq-cli.js' --data-dir '{{tmp-dir}}/graphs-b' --config '{{tmp-dir}}/cli-b.edn' --graph '{{graph}}' --timeout-s 120 --interval-s 1"]}}, :cases [{:tags [:happy-path :bootstrap :a-to-b], @@ -97,21 +97,21 @@ "sleep 1" "{{cli}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json server stop --graph {{graph-arg}}" "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync upload --graph {{graph-arg}}" - "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync start --graph {{graph-arg}} --e2ee-password '11111'" + "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync start --graph {{graph-arg}} --e2ee-password {{e2ee-password-arg}}" "python3 '{{repo-root}}/cli-e2e/scripts/wait_sync_status.py' --cli '{{repo-root}}/static/logseq-cli.js' --data-dir '{{tmp-dir}}/graphs-b' --config '{{tmp-dir}}/cli-b.edn' --graph '{{graph}}' --timeout-s 120 --interval-s 1" "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json upsert page --graph {{graph-arg}} --page SyncMultiBatchTwo >/dev/null" "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json upsert block --graph {{graph-arg}} --target-page SyncMultiBatchTwo --content '{{batch-marker-2}}' >/dev/null" "sleep 1" "{{cli}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json server stop --graph {{graph-arg}}" "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync upload --graph {{graph-arg}}" - "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync start --graph {{graph-arg}} --e2ee-password '11111'" + "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync start --graph {{graph-arg}} --e2ee-password {{e2ee-password-arg}}" "python3 '{{repo-root}}/cli-e2e/scripts/wait_sync_status.py' --cli '{{repo-root}}/static/logseq-cli.js' --data-dir '{{tmp-dir}}/graphs-b' --config '{{tmp-dir}}/cli-b.edn' --graph '{{graph}}' --timeout-s 120 --interval-s 1" "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json upsert page --graph {{graph-arg}} --page SyncMultiBatchThree >/dev/null" "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json upsert block --graph {{graph-arg}} --target-page SyncMultiBatchThree --content '{{batch-marker-3}}' >/dev/null" "sleep 1" "{{cli}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json server stop --graph {{graph-arg}}" "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync upload --graph {{graph-arg}}" - "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync start --graph {{graph-arg}} --e2ee-password '11111'" + "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync start --graph {{graph-arg}} --e2ee-password {{e2ee-password-arg}}" "python3 '{{repo-root}}/cli-e2e/scripts/wait_sync_status.py' --cli '{{repo-root}}/static/logseq-cli.js' --data-dir '{{tmp-dir}}/graphs-b' --config '{{tmp-dir}}/cli-b.edn' --graph '{{graph}}' --timeout-s 120 --interval-s 1" "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync upload --graph {{graph-arg}}" "python3 '{{repo-root}}/cli-e2e/scripts/wait_sync_status.py' --cli '{{repo-root}}/static/logseq-cli.js' --data-dir '{{tmp-dir}}/graphs-b' --config '{{tmp-dir}}/cli-b.edn' --graph '{{graph}}' --timeout-s 120 --interval-s 1" @@ -165,8 +165,8 @@ "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync stop --graph {{graph-arg}}" "{{cli-home}} --data-dir '{{tmp-dir}}/graphs-b' --config '{{tmp-dir}}/cli-b.edn' --output json sync stop --graph {{graph-arg}}" "python3 '{{repo-root}}/cli-e2e/scripts/random_bidirectional_block_ops.py' --cli '{{repo-root}}/static/logseq-cli.js' --graph '{{graph}}' --config-a '{{config-path}}' --data-dir-a '{{data-dir}}' --config-b '{{tmp-dir}}/cli-b.edn' --data-dir-b '{{tmp-dir}}/graphs-b' --page '{{random-page}}' --rounds-per-client {{rounds-per-client}} --seed {{random-seed}}" - "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync start --graph {{graph-arg}} --e2ee-password '11111'" - "{{cli-home}} --data-dir '{{tmp-dir}}/graphs-b' --config '{{tmp-dir}}/cli-b.edn' --output json sync start --graph {{graph-arg}} --e2ee-password '11111'" + "{{cli-home}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json sync start --graph {{graph-arg}} --e2ee-password {{e2ee-password-arg}}" + "{{cli-home}} --data-dir '{{tmp-dir}}/graphs-b' --config '{{tmp-dir}}/cli-b.edn' --output json sync start --graph {{graph-arg}} --e2ee-password {{e2ee-password-arg}}" "python3 '{{repo-root}}/cli-e2e/scripts/wait_sync_status.py' --cli '{{repo-root}}/static/logseq-cli.js' --data-dir '{{data-dir}}' --config '{{config-path}}' --graph '{{graph}}' --timeout-s 240 --interval-s 1 --min-tx-delta 1 --baseline-tx 0" "python3 '{{repo-root}}/cli-e2e/scripts/wait_sync_status.py' --cli '{{repo-root}}/static/logseq-cli.js' --data-dir '{{tmp-dir}}/graphs-b' --config '{{tmp-dir}}/cli-b.edn' --graph '{{graph}}' --timeout-s 240 --interval-s 1 --min-tx-delta 1 --baseline-tx 0" "python3 '{{repo-root}}/cli-e2e/scripts/compare_graph_queries.py' --cli '{{repo-root}}/static/logseq-cli.js' --graph '{{graph}}' --config-a '{{config-path}}' --data-dir-a '{{data-dir}}' --config-b '{{tmp-dir}}/cli-b.edn' --data-dir-b '{{tmp-dir}}/graphs-b' --query '[:find (pull ?b [:block/uuid :block/title :block/order {:block/parent [:block/uuid]}]) :where [?p :block/title \"{{random-page}}\"] [?b :block/page ?p] [?b :block/uuid]]' --require-result" diff --git a/cli-e2e/src/logseq/cli/e2e/main.clj b/cli-e2e/src/logseq/cli/e2e/main.clj index 6d1f6c69bd..f4666d8f4c 100644 --- a/cli-e2e/src/logseq/cli/e2e/main.clj +++ b/cli-e2e/src/logseq/cli/e2e/main.clj @@ -103,9 +103,12 @@ :targeted-run? targeted-run?})) (let [suite-context (when sync-suite? (sync-fixture/before-suite! {:run-command run-command})) + sync-context (if sync-suite? + (assoc suite-context :e2ee-password (:e2ee-password opts)) + suite-context) run-case* (if sync-suite? (fn [case case-opts] - (run-case (sync-fixture/prepare-case case suite-context) + (run-case (sync-fixture/prepare-case case sync-context) case-opts)) run-case)] (try @@ -232,22 +235,27 @@ (flush))) (defn- print-test-help! - [command-name] - (println (str "Usage: bb -f cli-e2e/bb.edn " command-name " [options]")) - (println) - (println "Options:") - (println " -h, --help Show this help and exit") - (println " --skip-build Skip build preflight steps") - (println " -i, --include TAG Run only cases with matching tag (repeatable)") - (println " --case ID Run a single case by id") - (println " --verbose Enable verbose output") - (println " --timings Print per-step timings and slow-step summary") - (println) - (println "Examples:") - (println (str " bb -f cli-e2e/bb.edn " command-name " --skip-build")) - (println (str " bb -f cli-e2e/bb.edn " command-name " --skip-build -i smoke")) - (println (str " bb -f cli-e2e/bb.edn " command-name " --skip-build --case global-help")) - (flush)) + [command-name suite] + (let [sync-suite? (= suite :sync)] + (println (str "Usage: bb -f cli-e2e/bb.edn " command-name " [options]")) + (println) + (println "Options:") + (println " -h, --help Show this help and exit") + (println " --skip-build Skip build preflight steps") + (println " -i, --include TAG Run only cases with matching tag (repeatable)") + (println " --case ID Run a single case by id") + (when sync-suite? + (println " --e2ee-password VALUE E2EE password for sync commands (Default: 11111)")) + (println " --verbose Enable verbose output") + (println " --timings Print per-step timings and slow-step summary") + (println) + (println "Examples:") + (println (str " bb -f cli-e2e/bb.edn " command-name " --skip-build")) + (println (str " bb -f cli-e2e/bb.edn " command-name " --skip-build -i smoke")) + (println (str " bb -f cli-e2e/bb.edn " command-name " --skip-build --case global-help")) + (when sync-suite? + (println (str " bb -f cli-e2e/bb.edn " command-name " --skip-build --e2ee-password 'my-secret'"))) + (flush))) (defn- test-suite! [opts {:keys [suite command-name] @@ -257,7 +265,7 @@ opts (assoc opts :suite suite)] (if (:help opts) (do - (print-test-help! command-name) + (print-test-help! command-name suite) {:status :help}) (let [started-at (System/nanoTime) passed (atom 0) diff --git a/cli-e2e/src/logseq/cli/e2e/sync_fixture.clj b/cli-e2e/src/logseq/cli/e2e/sync_fixture.clj index d4339282e5..6389109086 100644 --- a/cli-e2e/src/logseq/cli/e2e/sync_fixture.clj +++ b/cli-e2e/src/logseq/cli/e2e/sync_fixture.clj @@ -6,6 +6,7 @@ [logseq.cli.e2e.shell :as shell])) (def default-sync-port "18080") +(def default-e2ee-password "11111") (def ^:private heavy-setup-patterns [#"^mkdir -p '\{\{tmp-dir\}\}/home/logseq'$" @@ -71,8 +72,9 @@ :sync-ws-url sync-ws-url})) (defn prepare-case - [case {:keys [suite-auth-path suite-config-path sync-port sync-http-base sync-ws-url]}] - (let [lightweight-setup-prefix ["mkdir -p '{{tmp-dir}}/home/logseq'" + [case {:keys [suite-auth-path suite-config-path sync-port sync-http-base sync-ws-url e2ee-password]}] + (let [e2ee-password (or e2ee-password default-e2ee-password) + lightweight-setup-prefix ["mkdir -p '{{tmp-dir}}/home/logseq'" "cp '{{suite-auth-path}}' '{{tmp-dir}}/home/logseq/auth.json'" "cp '{{suite-config-path}}' '{{config-path}}'" "cp '{{suite-config-path}}' '{{tmp-dir}}/cli-b.edn'"] @@ -87,7 +89,9 @@ :suite-config-path suite-config-path :sync-port sync-port :sync-http-base sync-http-base - :sync-ws-url sync-ws-url}) + :sync-ws-url sync-ws-url + :e2ee-password e2ee-password + :e2ee-password-arg (shell-quote e2ee-password)}) (assoc :setup (vec (concat lightweight-setup-prefix setup'))) (assoc :cleanup cleanup')))) diff --git a/cli-e2e/test/logseq/cli/e2e/main_test.clj b/cli-e2e/test/logseq/cli/e2e/main_test.clj index 7f3cb02815..08f25003de 100644 --- a/cli-e2e/test/logseq/cli/e2e/main_test.clj +++ b/cli-e2e/test/logseq/cli/e2e/main_test.clj @@ -211,6 +211,34 @@ ["sync-status" true]] @run-case-seen)))) +(deftest run-sync-suite-forwards-e2ee-password-to-prepare-case + (let [seen-passwords (atom []) + sync-inventory {:excluded-command-prefixes ["login" "logout"] + :scopes {:sync {:commands ["sync status"] + :options []}}} + sync-cases [{:id "sync-status" + :cmds ["node static/logseq-cli.js sync status"] + :covers {:commands ["sync status"]}}]] + (with-redefs [sync-fixture/before-suite! (fn [_] + {:suite :sync}) + sync-fixture/prepare-case (fn [case suite-context] + (swap! seen-passwords conj (:e2ee-password suite-context)) + case) + sync-fixture/after-suite! (fn [_ _] nil)] + (main/run! {:suite :sync + :inventory sync-inventory + :cases sync-cases + :skip-build true + :e2ee-password "abc 123" + :run-command (fn [_] + {:exit 0 + :out "" + :err ""}) + :run-case (fn [case _opts] + {:id (:id case) + :status :ok})})) + (is (= ["abc 123"] @seen-passwords)))) + (deftest list-cases-defaults-to-non-sync (let [selected-suite (atom nil) output (with-out-str @@ -382,7 +410,8 @@ (is (string/includes? output "--skip-build")) (is (string/includes? output "--include TAG")) (is (string/includes? output "--case ID")) - (is (string/includes? output "--timings")))) + (is (string/includes? output "--timings")) + (is (not (string/includes? output "--e2ee-password"))))) (deftest test-sync-help-prints-usage-and-skips-execution (let [ran? (atom false) @@ -405,7 +434,9 @@ (is (string/includes? output "--skip-build")) (is (string/includes? output "--include TAG")) (is (string/includes? output "--case ID")) - (is (string/includes? output "--timings")))) + (is (string/includes? output "--timings")) + (is (string/includes? output "--e2ee-password VALUE")) + (is (string/includes? output "Default: 11111")))) (deftest test-single-case-enables-detailed-command-logging (let [command-opts (atom nil) diff --git a/cli-e2e/test/logseq/cli/e2e/sync_fixture_test.clj b/cli-e2e/test/logseq/cli/e2e/sync_fixture_test.clj index 3a512035d4..b60346788b 100644 --- a/cli-e2e/test/logseq/cli/e2e/sync_fixture_test.clj +++ b/cli-e2e/test/logseq/cli/e2e/sync_fixture_test.clj @@ -28,7 +28,23 @@ (is (= ["{{cli}} --data-dir {{data-dir-arg}} --config {{config-path-arg}} --output json server stop --graph {{graph-arg}}"] (:cleanup prepared))) (is (= "/tmp/suite/auth.json" (get-in prepared [:vars :suite-auth-path]))) - (is (= "http://127.0.0.1:18080" (get-in prepared [:vars :sync-http-base]))))) + (is (= "http://127.0.0.1:18080" (get-in prepared [:vars :sync-http-base]))) + (is (= "11111" (get-in prepared [:vars :e2ee-password]))) + (is (= "11111" (get-in prepared [:vars :e2ee-password-arg]))))) + +(deftest prepare-case-accepts-custom-e2ee-password + (let [suite-context {:suite-auth-path "/tmp/suite/auth.json" + :suite-config-path "/tmp/suite/config.edn" + :sync-port "18080" + :sync-http-base "http://127.0.0.1:18080" + :sync-ws-url "ws://127.0.0.1:18080/sync/%s" + :e2ee-password "pass word"} + prepared (sync-fixture/prepare-case {:id "sync-case" + :setup [] + :cleanup []} + suite-context)] + (is (= "pass word" (get-in prepared [:vars :e2ee-password]))) + (is (= "'pass word'" (get-in prepared [:vars :e2ee-password-arg]))))) (deftest before-and-after-suite-run-expected-commands (let [calls (atom [])