From 5d80258a7799fa73b0c4bb6ab823610e5d990305 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Thu, 12 Mar 2026 12:20:39 +0900 Subject: [PATCH] Add e2e-test (not passed yet) Refine readme --- package-lock.json | 86 ++---- src/apps/cli/.test.env | 8 + src/apps/cli/README.md | 124 +++++---- src/apps/cli/commands/runCommand.ts | 29 +- src/apps/cli/package.json | 8 +- .../test-e2e-two-vaults-with-docker-linux.sh | 247 ++++++++++++++++++ src/apps/cli/test/test-push-pull-linux.sh | 15 +- src/apps/cli/test/test-setup-put-cat-linux.sh | 55 ++-- .../test-sync-two-local-databases-linux.sh | 17 +- src/apps/cli/util/couchdb-init.sh | 47 ++++ src/apps/cli/util/couchdb-start.sh | 4 + src/apps/cli/util/couchdb-stop.sh | 3 + src/apps/cli/util/minio-init.sh | 47 ++++ src/apps/cli/util/minio-start.sh | 2 + src/apps/cli/util/minio-stop.sh | 3 + src/lib | 2 +- 16 files changed, 520 insertions(+), 177 deletions(-) create mode 100644 src/apps/cli/.test.env create mode 100644 src/apps/cli/test/test-e2e-two-vaults-with-docker-linux.sh create mode 100755 src/apps/cli/util/couchdb-init.sh create mode 100755 src/apps/cli/util/couchdb-start.sh create mode 100755 src/apps/cli/util/couchdb-stop.sh create mode 100755 src/apps/cli/util/minio-init.sh create mode 100755 src/apps/cli/util/minio-start.sh create mode 100755 src/apps/cli/util/minio-stop.sh diff --git a/package-lock.json b/package-lock.json index d89d159..dfb7f22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1939,7 +1939,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.14.4.tgz", "integrity": "sha512-pUxEGmR+uu21OG/icAovjlu1fcYJzyVhhT0rsCrn+zi+nHtrS43Bp9KPn9KGa4NMspCUE++nkyiqziuIvJdwzw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@firebase/component": "0.7.0", "@firebase/logger": "0.5.0", @@ -2006,7 +2005,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.5.4.tgz", "integrity": "sha512-T7ifGmb+awJEcp542Ek4HtNfBxcBrnuk1ggUdqyFEdsXHdq7+wVlhvE6YukTL7NS8hIkEfL7TMAPx/uCNqt30g==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@firebase/app": "0.14.4", "@firebase/component": "0.7.0", @@ -2022,8 +2020,7 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/@firebase/app/node_modules/idb": { "version": "7.1.1", @@ -2492,7 +2489,6 @@ "integrity": "sha512-0AZUyYUfpMNcztR5l09izHwXkZpghLgCUaAGjtMwXnCg3bj4ml5VgiwqOMOxJ+Nw4qN/zJAaOQBcJ7KGkWStqQ==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" }, @@ -3085,7 +3081,8 @@ "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@multiformats/dns": { "version": "1.0.10", @@ -4947,7 +4944,6 @@ "integrity": "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "deepmerge": "^4.3.1", @@ -5427,7 +5423,6 @@ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -5632,7 +5627,6 @@ "integrity": "sha512-t4toy8X/YTnjYEPoY0pbDBg3EvDPg1elCDrfc+VupPHwoN/5/FNQ8Z+xBYIaEnOE2vVEyKwqYBzZ9h9rJtZVcg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/mocker": "4.0.16", "@vitest/utils": "4.0.16", @@ -5656,7 +5650,6 @@ "integrity": "sha512-I2Fy/ANdphi1yI46d15o0M1M4M0UJrUiVKkH5oKeRZZCdPg0fw/cfTKZzv9Ge9eobtJYp4BGblMzXdXH0vcl5g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/browser": "4.0.16", "@vitest/mocker": "4.0.16", @@ -6401,7 +6394,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -7410,7 +7402,8 @@ "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -8293,7 +8286,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -8404,7 +8396,6 @@ "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -10777,7 +10768,6 @@ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -11329,7 +11319,6 @@ "resolved": "https://registry.npmjs.org/libp2p/-/libp2p-2.8.11.tgz", "integrity": "sha512-EjkyN0CI6uP+e4OOkEcZvhbZtlwFl4Y0rkkMvDbXmcfILX4E4n/jKE4Ppoc1qhNufxToxVWCMDS2ipniQgiYaw==", "license": "Apache-2.0 OR MIT", - "peer": true, "dependencies": { "@chainsafe/is-ip": "^2.1.0", "@chainsafe/netmask": "^2.0.0", @@ -12630,7 +12619,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -12655,7 +12643,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "lilconfig": "^3.1.1" }, @@ -14226,7 +14213,8 @@ "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/sublevel-pouchdb": { "version": "9.0.0", @@ -14293,7 +14281,6 @@ "integrity": "sha512-0a/huwc8e2es+7KFi70esqsReRfRbrT8h1cJSY/+z1lF0yKM6TT+//HYu28Yxstr50H7ifaqZRDGd0KuKDxP7w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -14611,7 +14598,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -14739,7 +14725,6 @@ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" @@ -15345,7 +15330,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15482,7 +15466,6 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -16043,7 +16026,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -16077,7 +16059,6 @@ "integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.0.16", "@vitest/mocker": "4.0.16", @@ -16173,7 +16154,8 @@ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/wait-port": { "version": "1.1.0", @@ -16689,7 +16671,6 @@ "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "dev": true, "license": "ISC", - "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -18037,7 +18018,6 @@ "version": "0.14.4", "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.14.4.tgz", "integrity": "sha512-pUxEGmR+uu21OG/icAovjlu1fcYJzyVhhT0rsCrn+zi+nHtrS43Bp9KPn9KGa4NMspCUE++nkyiqziuIvJdwzw==", - "peer": true, "requires": { "@firebase/component": "0.7.0", "@firebase/logger": "0.5.0", @@ -18091,7 +18071,6 @@ "version": "0.5.4", "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.5.4.tgz", "integrity": "sha512-T7ifGmb+awJEcp542Ek4HtNfBxcBrnuk1ggUdqyFEdsXHdq7+wVlhvE6YukTL7NS8hIkEfL7TMAPx/uCNqt30g==", - "peer": true, "requires": { "@firebase/app": "0.14.4", "@firebase/component": "0.7.0", @@ -18103,8 +18082,7 @@ "@firebase/app-types": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", - "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", - "peer": true + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==" }, "@firebase/auth": { "version": "1.11.0", @@ -18432,7 +18410,6 @@ "version": "1.13.0", "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.13.0.tgz", "integrity": "sha512-0AZUyYUfpMNcztR5l09izHwXkZpghLgCUaAGjtMwXnCg3bj4ml5VgiwqOMOxJ+Nw4qN/zJAaOQBcJ7KGkWStqQ==", - "peer": true, "requires": { "tslib": "^2.1.0" } @@ -18905,7 +18882,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", - "dev": true + "dev": true, + "peer": true }, "@multiformats/dns": { "version": "1.0.10", @@ -20089,7 +20067,6 @@ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.4.tgz", "integrity": "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==", "dev": true, - "peer": true, "requires": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "deepmerge": "^4.3.1", @@ -20517,7 +20494,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, - "peer": true, "requires": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -20625,7 +20601,6 @@ "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.0.16.tgz", "integrity": "sha512-t4toy8X/YTnjYEPoY0pbDBg3EvDPg1elCDrfc+VupPHwoN/5/FNQ8Z+xBYIaEnOE2vVEyKwqYBzZ9h9rJtZVcg==", "dev": true, - "peer": true, "requires": { "@vitest/mocker": "4.0.16", "@vitest/utils": "4.0.16", @@ -20642,7 +20617,6 @@ "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.0.16.tgz", "integrity": "sha512-I2Fy/ANdphi1yI46d15o0M1M4M0UJrUiVKkH5oKeRZZCdPg0fw/cfTKZzv9Ge9eobtJYp4BGblMzXdXH0vcl5g==", "dev": true, - "peer": true, "requires": { "@vitest/browser": "4.0.16", "@vitest/mocker": "4.0.16", @@ -21165,8 +21139,7 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "peer": true + "dev": true }, "acorn-jsx": { "version": "5.3.2", @@ -21840,7 +21813,8 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", - "dev": true + "dev": true, + "peer": true }, "cross-spawn": { "version": "7.0.6", @@ -22433,7 +22407,6 @@ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", "dev": true, - "peer": true, "requires": { "@esbuild/aix-ppc64": "0.25.0", "@esbuild/android-arm": "0.25.0", @@ -22509,7 +22482,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.3.tgz", "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, - "peer": true, "requires": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -24105,8 +24077,7 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "dev": true, - "peer": true + "dev": true }, "js-sdsl": { "version": "4.3.0", @@ -24500,7 +24471,6 @@ "version": "2.8.11", "resolved": "https://registry.npmjs.org/libp2p/-/libp2p-2.8.11.tgz", "integrity": "sha512-EjkyN0CI6uP+e4OOkEcZvhbZtlwFl4Y0rkkMvDbXmcfILX4E4n/jKE4Ppoc1qhNufxToxVWCMDS2ipniQgiYaw==", - "peer": true, "requires": { "@chainsafe/is-ip": "^2.1.0", "@chainsafe/netmask": "^2.0.0", @@ -25385,7 +25355,6 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, - "peer": true, "requires": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -25397,7 +25366,6 @@ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", "dev": true, - "peer": true, "requires": { "lilconfig": "^3.1.1" } @@ -26500,7 +26468,8 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", - "dev": true + "dev": true, + "peer": true }, "sublevel-pouchdb": { "version": "9.0.0", @@ -26555,7 +26524,6 @@ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.41.1.tgz", "integrity": "sha512-0a/huwc8e2es+7KFi70esqsReRfRbrT8h1cJSY/+z1lF0yKM6TT+//HYu28Yxstr50H7ifaqZRDGd0KuKDxP7w==", "dev": true, - "peer": true, "requires": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -26740,8 +26708,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "peer": true + "dev": true } } }, @@ -26839,7 +26806,6 @@ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, - "peer": true, "requires": { "esbuild": "~0.27.0", "fsevents": "~2.3.3", @@ -27139,8 +27105,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "peer": true + "dev": true }, "uint8-varint": { "version": "2.0.4", @@ -27241,7 +27206,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, - "peer": true, "requires": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -27472,8 +27436,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "peer": true + "dev": true } } }, @@ -27489,7 +27452,6 @@ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.16.tgz", "integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==", "dev": true, - "peer": true, "requires": { "@vitest/expect": "4.0.16", "@vitest/mocker": "4.0.16", @@ -27530,7 +27492,8 @@ "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", - "dev": true + "dev": true, + "peer": true }, "wait-port": { "version": "1.1.0", @@ -27899,8 +27862,7 @@ "version": "2.8.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "dev": true, - "peer": true + "dev": true }, "yargs": { "version": "17.7.2", diff --git a/src/apps/cli/.test.env b/src/apps/cli/.test.env new file mode 100644 index 0000000..641c73c --- /dev/null +++ b/src/apps/cli/.test.env @@ -0,0 +1,8 @@ +hostname=http://127.0.0.1:5989/ +dbname=livesync-test-db2 +minioEndpoint=http://127.0.0.1:9000 +username=admin +password=testpassword +accessKey=minioadmin +secretKey=minioadmin +bucketName=livesync-test-bucket \ No newline at end of file diff --git a/src/apps/cli/README.md b/src/apps/cli/README.md index 5f0d8ff..ca9a383 100644 --- a/src/apps/cli/README.md +++ b/src/apps/cli/README.md @@ -16,12 +16,12 @@ This CLI version is built using the same core as the Obsidian plugin: ``` CLI Main └─ LiveSyncBaseCore - ├─ HeadlessServiceHub (All services without Obsidian dependencies) - └─ ServiceModules (Ported from main.ts) + ├─ NodeServiceHub (All services without Obsidian dependencies) + └─ ServiceModules (wired by initialiseServiceModulesCLI) ├─ FileAccessCLI (Node.js FileSystemAdapter) ├─ StorageEventManagerCLI ├─ ServiceFileAccessCLI - ├─ ServiceDatabaseFileAccess + ├─ ServiceDatabaseFileAccessCLI ├─ ServiceFileHandler └─ ServiceRebuilder ``` @@ -33,10 +33,14 @@ CLI Main - Implements same interface as Obsidian's file system 2. **Service Modules** (`serviceModules/`) - - Direct port from `main.ts` `initialiseServiceModules` + - Initialised by `initialiseServiceModulesCLI` - All core sync functionality preserved -3. **Main Entry Point** (`main.ts`) +3. **Service Hub and Settings Services** (`services/`) + - `NodeServiceHub` provides the CLI service context + - Node-specific settings and key-value services are provided without Obsidian dependencies + +4. **Main Entry Point** (`main.ts`) - Command-line interface - Settings management (JSON file) - Graceful shutdown handling @@ -59,43 +63,43 @@ As you know, the CLI is designed to be used in a headless environment. Hence all ```bash # Sync local database with CouchDB (no files will be changed). -node dist/index.cjs /path/to/your-local-database --settings /path/to/settings.json sync +npm run cli -- /path/to/your-local-database --settings /path/to/settings.json sync # Push files to local database -node dist/index.cjs /path/to/your-local-database --settings /path/to/settings.json push /your/storage/file.md /vault/path/file.md +npm run cli -- /path/to/your-local-database --settings /path/to/settings.json push /your/storage/file.md /vault/path/file.md # Pull files from local database -node dist/index.cjs /path/to/your-local-database --settings /path/to/settings.json pull /vault/path/file.md /your/storage/file.md +npm run cli -- /path/to/your-local-database --settings /path/to/settings.json pull /vault/path/file.md /your/storage/file.md # Verbose logging -node dist/index.cjs /path/to/your-local-database --settings /path/to/settings.json --verbose +npm run cli -- /path/to/your-local-database --settings /path/to/settings.json --verbose # Apply setup URI to settings file (settings only; does not run synchronisation) -node dist/index.cjs /path/to/your-local-database --settings /path/to/settings.json setup "obsidian://setuplivesync?settings=..." +npm run cli -- /path/to/your-local-database --settings /path/to/settings.json setup "obsidian://setuplivesync?settings=..." # Put text from stdin into local database -echo "Hello from stdin" | node dist/index.cjs /path/to/your-local-database --settings /path/to/settings.json put /vault/path/file.md +echo "Hello from stdin" | npm run cli -- /path/to/your-local-database --settings /path/to/settings.json put /vault/path/file.md # Output a file from local database to stdout -node dist/index.cjs /path/to/your-local-database --settings /path/to/settings.json cat /vault/path/file.md +npm run cli -- /path/to/your-local-database --settings /path/to/settings.json cat /vault/path/file.md # Output a specific revision of a file from local database -node dist/index.cjs /path/to/your-local-database --settings /path/to/settings.json cat-rev /vault/path/file.md 3-abcdef +npm run cli -- /path/to/your-local-database --settings /path/to/settings.json cat-rev /vault/path/file.md 3-abcdef # Pull a specific revision of a file from local database to local storage -node dist/index.cjs /path/to/your-local-database --settings /path/to/settings.json pull-rev /vault/path/file.md /your/storage/file.old.md 3-abcdef +npm run cli -- /path/to/your-local-database --settings /path/to/settings.json pull-rev /vault/path/file.md /your/storage/file.old.md 3-abcdef # List files in local database -node dist/index.cjs /path/to/your-local-database --settings /path/to/settings.json ls /vault/path/ +npm run cli -- /path/to/your-local-database --settings /path/to/settings.json ls /vault/path/ # Show metadata for a file in local database -node dist/index.cjs /path/to/your-local-database --settings /path/to/settings.json info /vault/path/file.md +npm run cli -- /path/to/your-local-database --settings /path/to/settings.json info /vault/path/file.md # Mark a file as deleted in local database -node dist/index.cjs /path/to/your-local-database --settings /path/to/settings.json rm /vault/path/file.md +npm run cli -- /path/to/your-local-database --settings /path/to/settings.json rm /vault/path/file.md # Resolve conflict by keeping a specific revision -node dist/index.cjs /path/to/your-local-database --settings /path/to/settings.json resolve /vault/path/file.md 3-abcdef +npm run cli -- /path/to/your-local-database --settings /path/to/settings.json resolve /vault/path/file.md 3-abcdef ``` ### Configuration @@ -157,15 +161,21 @@ Commands: resolve Resolve conflict by keeping the specified revision ``` +Run via npm script: + +```bash +npm run cli -- [database-path] [options] [command] [command-args] +``` + `info` output fields: -- `ID`: Document ID -- `Revision`: Current revision -- `Conflicts`: Conflicted revisions, or `N/A` -- `Filename`: Basename of path -- `Path`: Vault-relative path -- `Size`: Size in bytes -- `PastRevisions`: Available non-current revisions +- `id`: Document ID +- `revision`: Current revision +- `conflicts`: Conflicted revisions, or `N/A` +- `filename`: Basename of path +- `path`: Vault-relative path +- `size`: Size in bytes +- `revisions`: Available non-current revisions - `Chunks`: Number of chunk IDs - `child: ...`: Chunk ID list @@ -187,9 +197,9 @@ TODO: Conflict and resolution checks for real local databases. Create default settings, apply a setup URI, then run one sync cycle. ```bash -node dist/index.cjs init-settings /data/livesync-settings.json -printf '%s\n' "$SETUP_PASSPHRASE" | node dist/index.cjs /data/vault --settings /data/livesync-settings.json setup "$SETUP_URI" -node dist/index.cjs /data/vault --settings /data/livesync-settings.json sync +npm run cli -- init-settings /data/livesync-settings.json +printf '%s\n' "$SETUP_PASSPHRASE" | npm run cli -- /data/vault --settings /data/livesync-settings.json setup "$SETUP_URI" +npm run cli -- /data/vault --settings /data/livesync-settings.json sync ``` ### 2. Scripted import and export @@ -197,8 +207,8 @@ node dist/index.cjs /data/vault --settings /data/livesync-settings.json sync Push local files into the database from automation, and pull them back for export or backup. ```bash -node dist/index.cjs /data/vault --settings /data/livesync-settings.json push ./note.md notes/note.md -node dist/index.cjs /data/vault --settings /data/livesync-settings.json pull notes/note.md ./exports/note.md +npm run cli -- /data/vault --settings /data/livesync-settings.json push ./note.md notes/note.md +npm run cli -- /data/vault --settings /data/livesync-settings.json pull notes/note.md ./exports/note.md ``` ### 3. Revision inspection and restore @@ -206,9 +216,9 @@ node dist/index.cjs /data/vault --settings /data/livesync-settings.json pull not List metadata, find an older revision, then restore it by content (`cat-rev`) or file output (`pull-rev`). ```bash -node dist/index.cjs /data/vault --settings /data/livesync-settings.json info notes/note.md -node dist/index.cjs /data/vault --settings /data/livesync-settings.json cat-rev notes/note.md 3-abcdef -node dist/index.cjs /data/vault --settings /data/livesync-settings.json pull-rev notes/note.md ./restore/note.old.md 3-abcdef +npm run cli -- /data/vault --settings /data/livesync-settings.json info notes/note.md +npm run cli -- /data/vault --settings /data/livesync-settings.json cat-rev notes/note.md 3-abcdef +npm run cli -- /data/vault --settings /data/livesync-settings.json pull-rev notes/note.md ./restore/note.old.md 3-abcdef ``` ### 4. Conflict and cleanup workflow @@ -216,9 +226,9 @@ node dist/index.cjs /data/vault --settings /data/livesync-settings.json pull-rev Inspect conflicted revisions, resolve by keeping one revision, then delete obsolete files. ```bash -node dist/index.cjs /data/vault --settings /data/livesync-settings.json info notes/note.md -node dist/index.cjs /data/vault --settings /data/livesync-settings.json resolve notes/note.md 3-abcdef -node dist/index.cjs /data/vault --settings /data/livesync-settings.json rm notes/obsolete.md +npm run cli -- /data/vault --settings /data/livesync-settings.json info notes/note.md +npm run cli -- /data/vault --settings /data/livesync-settings.json resolve notes/note.md 3-abcdef +npm run cli -- /data/vault --settings /data/livesync-settings.json rm notes/obsolete.md ``` ### 5. CI smoke test for content round-trip @@ -226,8 +236,8 @@ node dist/index.cjs /data/vault --settings /data/livesync-settings.json rm notes Validate that `put`/`cat` is behaving as expected in a pipeline. ```bash -echo "hello-ci" | node dist/index.cjs /data/vault --settings /data/livesync-settings.json put ci/test.md -node dist/index.cjs /data/vault --settings /data/livesync-settings.json cat ci/test.md +echo "hello-ci" | npm run cli -- /data/vault --settings /data/livesync-settings.json put ci/test.md +npm run cli -- /data/vault --settings /data/livesync-settings.json cat ci/test.md ``` ## Development @@ -236,26 +246,40 @@ node dist/index.cjs /data/vault --settings /data/livesync-settings.json cat ci/t ``` src/apps/cli/ -├── commands/ # Command dispatcher and command utilities +├── commands/ # Command dispatcher and command utilities │ ├── runCommand.ts │ ├── types.ts │ └── utils.ts -├── adapters/ # Node.js FileSystem Adapter +├── adapters/ # Node.js FileSystem Adapter +│ ├── NodeConversionAdapter.ts │ ├── NodeFileSystemAdapter.ts │ ├── NodePathAdapter.ts -│ ├── NodeTypeGuardAdapter.ts -│ ├── NodeConversionAdapter.ts │ ├── NodeStorageAdapter.ts -│ ├── NodeVaultAdapter.ts -│ └── NodeTypes.ts -├── managers/ # CLI-specific managers +│ ├── NodeTypeGuardAdapter.ts +│ ├── NodeTypes.ts +│ └── NodeVaultAdapter.ts +├── lib/ +│ └── pouchdb-node.ts +├── managers/ # CLI-specific managers │ ├── CLIStorageEventManagerAdapter.ts │ └── StorageEventManagerCLI.ts -├── serviceModules/ # Service modules (ported from main.ts) +├── serviceModules/ # Service modules (ported from main.ts) │ ├── CLIServiceModules.ts +│ ├── DatabaseFileAccess.ts │ ├── FileAccessCLI.ts -│ ├── ServiceFileAccessImpl.ts -│ └── DatabaseFileAccess.ts -├── main.ts # CLI entry point -└── README.md # This file +│ └── ServiceFileAccessImpl.ts +├── services/ +│ ├── NodeKeyValueDBService.ts +│ ├── NodeServiceHub.ts +│ └── NodeSettingService.ts +├── test/ +│ ├── test-push-pull-linux.sh +│ ├── test-setup-put-cat-linux.sh +│ └── test-sync-two-local-databases-linux.sh +├── .gitignore +├── main.ts # CLI entry point +├── package.json +├── README.md # This file +├── tsconfig.json +└── vite.config.ts ``` diff --git a/src/apps/cli/commands/runCommand.ts b/src/apps/cli/commands/runCommand.ts index 637f9a5..9137920 100644 --- a/src/apps/cli/commands/runCommand.ts +++ b/src/apps/cli/commands/runCommand.ts @@ -237,22 +237,19 @@ export async function runCommand(options: CLIOptions, context: CLICommandContext }) .map((entry: { rev: string }) => entry.rev); const pastRevisionsText = - pastRevisions.length > 0 ? pastRevisions.map((rev: string) => ` rev: ${rev}`) : [" N/A"]; - - const out = - [ - `ID: ${doc._id}`, - `Revision: ${doc._rev ?? ""}`, - `Conflicts: ${conflictsText}`, - `Filename: ${filename}`, - `Path: ${docPath}`, - `Size: ${doc.size}`, - `PastRevisions:`, - ...pastRevisionsText, - `Chunks: ${children.length}`, - ...children.map((id) => ` child: ${id}`), - ].join("\n") + "\n"; - process.stdout.write(out); + pastRevisions.length > 0 ? pastRevisions.map((rev: string) => `${rev}`) : ["N/A"]; + const out = { + id: doc._id, + revision: doc._rev ?? "", + conflicts: conflictsText, + filename: filename, + path: docPath, + size: doc.size, + revisions: pastRevisionsText, + chunks: children.length, + children: children, + }; + process.stdout.write(JSON.stringify(out, null, 2) + "\n"); return true; } diff --git a/src/apps/cli/package.json b/src/apps/cli/package.json index ddfe3ef..327e4fe 100644 --- a/src/apps/cli/package.json +++ b/src/apps/cli/package.json @@ -2,14 +2,16 @@ "name": "self-hosted-livesync-cli", "private": true, "version": "0.0.0", + "main": "dist/index.cjs", "type": "module", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview", - "run": "node dist/index.cjs", - "buildRun": "npm run build && npm run", - "check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json" + "cli": "node dist/index.cjs", + "buildRun": "npm run build && npm run cli --", + "check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json", + "test:e2e:two-vaults": "bash test/test-e2e-two-vaults-with-docker-linux.sh" }, "dependencies": {}, "devDependencies": {} diff --git a/src/apps/cli/test/test-e2e-two-vaults-with-docker-linux.sh b/src/apps/cli/test/test-e2e-two-vaults-with-docker-linux.sh new file mode 100644 index 0000000..b23b00c --- /dev/null +++ b/src/apps/cli/test/test-e2e-two-vaults-with-docker-linux.sh @@ -0,0 +1,247 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +CLI_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)" +cd "$CLI_DIR" + +# verbose +CLI_CMD=(npm run cli -- -v ) +RUN_BUILD="${RUN_BUILD:-1}" +KEEP_TEST_DATA="${KEEP_TEST_DATA:-0}" +TEST_ENV_FILE="${TEST_ENV_FILE:-$CLI_DIR/.test.env}" + +if [[ ! -f "$TEST_ENV_FILE" ]]; then + echo "[ERROR] test env file not found: $TEST_ENV_FILE" >&2 + exit 1 +fi + +set -a +source "$TEST_ENV_FILE" +set +a + +for var in hostname dbname username password; do + if [[ -z "${!var:-}" ]]; then + echo "[ERROR] required variable '$var' is missing in $TEST_ENV_FILE" >&2 + exit 1 + fi +done + +COUCHDB_URI="${hostname%/}" +DB_SUFFIX="$(date +%s)-$RANDOM" +COUCHDB_DBNAME="${dbname}-${DB_SUFFIX}" + +VAULT_ROOT="$CLI_DIR/.livesync" +VAULT_A="$VAULT_ROOT/testvault_a" +VAULT_B="$VAULT_ROOT/testvault_b" +SETTINGS_A="$VAULT_ROOT/test-settings-a.json" +SETTINGS_B="$VAULT_ROOT/test-settings-b.json" +WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/livesync-cli-e2e.XXXXXX")" + +cleanup() { + local exit_code=$? + bash "$CLI_DIR/util/couchdb-stop.sh" >/dev/null 2>&1 || true + if [[ "$KEEP_TEST_DATA" != "1" ]]; then + rm -rf "$VAULT_A" "$VAULT_B" "$SETTINGS_A" "$SETTINGS_B" "$WORK_DIR" + else + echo "[INFO] KEEP_TEST_DATA=1, preserving test artefacts" + echo " vault a: $VAULT_A" + echo " vault b: $VAULT_B" + echo " settings: $SETTINGS_A, $SETTINGS_B" + echo " work dir: $WORK_DIR" + fi + exit "$exit_code" +} +trap cleanup EXIT + +run_cli() { + "${CLI_CMD[@]}" "$@" +} + +run_cli_a() { + run_cli "$VAULT_A" --settings "$SETTINGS_A" "$@" +} + +run_cli_b() { + run_cli "$VAULT_B" --settings "$SETTINGS_B" "$@" +} + +assert_contains() { + local haystack="$1" + local needle="$2" + local message="$3" + if ! grep -Fq "$needle" <<< "$haystack"; then + echo "[FAIL] $message" >&2 + echo "[FAIL] expected to find: $needle" >&2 + echo "[FAIL] actual output:" >&2 + echo "$haystack" >&2 + exit 1 + fi +} + +assert_equal() { + local expected="$1" + local actual="$2" + local message="$3" + if [[ "$expected" != "$actual" ]]; then + echo "[FAIL] $message" >&2 + echo "[FAIL] expected: $expected" >&2 + echo "[FAIL] actual: $actual" >&2 + exit 1 + fi +} + +assert_command_fails() { + local message="$1" + shift + set +e + "$@" >"$WORK_DIR/failed-command.log" 2>&1 + local exit_code=$? + set -e + if [[ "$exit_code" -eq 0 ]]; then + echo "[FAIL] $message" >&2 + cat "$WORK_DIR/failed-command.log" >&2 + exit 1 + fi +} + +sanitise_cat_stdout() { + sed '/^\[CLIWatchAdapter\] File watching is not enabled in CLI version$/d' +} + +sync_both() { + run_cli_a sync >/dev/null + run_cli_b sync >/dev/null +} + +curl_json() { + curl -4 -sS --fail --connect-timeout 3 --max-time 15 "$@" +} + +init_settings() { + local settings_file="$1" + run_cli init-settings --force "$settings_file" >/dev/null + SETTINGS_FILE="$settings_file" \ + COUCHDB_URI="$COUCHDB_URI" \ + COUCHDB_USER="$username" \ + COUCHDB_PASSWORD="$password" \ + COUCHDB_DBNAME="$COUCHDB_DBNAME" \ + node <<'NODE' +const fs = require("node:fs"); +const settingsPath = process.env.SETTINGS_FILE; +const data = JSON.parse(fs.readFileSync(settingsPath, "utf-8")); + +data.couchDB_URI = process.env.COUCHDB_URI; +data.couchDB_USER = process.env.COUCHDB_USER; +data.couchDB_PASSWORD = process.env.COUCHDB_PASSWORD; +data.couchDB_DBNAME = process.env.COUCHDB_DBNAME; +data.liveSync = true; +data.syncOnStart = false; +data.syncOnSave = false; +data.usePluginSync = false; +data.isConfigured = true; + +fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2), "utf-8"); +NODE +cat "$settings_file" +} + +echo "[INFO] stopping leftover CouchDB container if present" +bash "$CLI_DIR/util/couchdb-stop.sh" >/dev/null 2>&1 || true + +echo "[INFO] starting CouchDB test container" +bash "$CLI_DIR/util/couchdb-start.sh" + +echo "status" +docker ps --filter "name=couchdb-test" + +echo "[INFO] initialising CouchDB test container" +bash "$CLI_DIR/util/couchdb-init.sh" + +echo "[INFO] CouchDB create test database: $COUCHDB_DBNAME" +until (curl_json -X PUT --user "${username}:${password}" "${hostname}/${COUCHDB_DBNAME}" ); do sleep 5; done + +if [[ "$RUN_BUILD" == "1" ]]; then + echo "[INFO] building CLI" + npm run build +fi + +echo "[INFO] preparing vaults and settings" +rm -rf "$VAULT_A" "$VAULT_B" "$SETTINGS_A" "$SETTINGS_B" +mkdir -p "$VAULT_A" "$VAULT_B" +init_settings "$SETTINGS_A" +init_settings "$SETTINGS_B" + +echo "[INFO] test DB: $COUCHDB_DBNAME" + +TARGET_A_ONLY="e2e/a-only-info.md" +TARGET_SYNC="e2e/sync-info.md" +TARGET_PUSH="e2e/pushed-from-a.md" +TARGET_PUT="e2e/put-from-a.md" +TARGET_CONFLICT="e2e/conflict.md" + +echo "[CASE] A puts and A can get info" +printf 'alpha-from-a\n' | run_cli_a put "$TARGET_A_ONLY" >/dev/null +INFO_A_ONLY="$(run_cli_a info "$TARGET_A_ONLY")" +assert_contains "$INFO_A_ONLY" "\"path\": \"$TARGET_A_ONLY\"" "A info should include path after put" +echo "[PASS] A put/info" + +echo "[CASE] A puts, both sync, and B can get info" +printf 'visible-after-sync\n' | run_cli_a put "$TARGET_SYNC" >/dev/null +sync_both +INFO_B_SYNC="$(run_cli_b info "$TARGET_SYNC")" +assert_contains "$INFO_B_SYNC" "\"path\": \"$TARGET_SYNC\"" "B info should include path after sync" +echo "[PASS] sync A->B and B info" + +echo "[CASE] A pushes and puts, both sync, and B can pull and cat" +PUSH_SRC="$WORK_DIR/push-source.txt" +PULL_DST="$WORK_DIR/pull-destination.txt" +printf 'pushed-content-%s\n' "$DB_SUFFIX" > "$PUSH_SRC" +run_cli_a push "$PUSH_SRC" "$TARGET_PUSH" >/dev/null +printf 'put-content-%s\n' "$DB_SUFFIX" | run_cli_a put "$TARGET_PUT" >/dev/null +sync_both +run_cli_b pull "$TARGET_PUSH" "$PULL_DST" >/dev/null +if ! cmp -s "$PUSH_SRC" "$PULL_DST"; then + echo "[FAIL] B pull result does not match pushed source" >&2 + echo "--- source ---" >&2 + cat "$PUSH_SRC" >&2 + echo "--- pulled ---" >&2 + cat "$PULL_DST" >&2 + exit 1 +fi +CAT_B_PUT="$(run_cli_b cat "$TARGET_PUT" | sanitise_cat_stdout)" +assert_equal "put-content-$DB_SUFFIX" "$CAT_B_PUT" "B cat should return A put content" +echo "[PASS] push/pull and put/cat across vaults" + +echo "[CASE] A removes, both sync, and B can no longer cat" +run_cli_a rm "$TARGET_PUT" >/dev/null +sync_both +assert_command_fails "B cat should fail after A removed the file and synced" run_cli_b cat "$TARGET_PUT" +echo "[PASS] rm is replicated" + +echo "[CASE] verify conflict detection" +printf 'conflict-base\n' | run_cli_a put "$TARGET_CONFLICT" >/dev/null +sync_both +INFO_B_BASE="$(run_cli_b info "$TARGET_CONFLICT")" +assert_contains "$INFO_B_BASE" "\"path\": \"$TARGET_CONFLICT\"" "B should be able to info before creating conflict" + +printf 'conflict-from-a-%s\n' "$DB_SUFFIX" | run_cli_a put "$TARGET_CONFLICT" >/dev/null +printf 'conflict-from-b-%s\n' "$DB_SUFFIX" | run_cli_b put "$TARGET_CONFLICT" >/dev/null + +run_cli_a sync >/dev/null +run_cli_b sync >/dev/null +run_cli_a sync >/dev/null + +INFO_A_CONFLICT="$(run_cli_a info "$TARGET_CONFLICT")" +INFO_B_CONFLICT="$(run_cli_b info "$TARGET_CONFLICT")" +if grep -qF '"conflicts": "N/A"' <<< "$INFO_A_CONFLICT" && grep -qF '"conflicts": "N/A"' <<< "$INFO_B_CONFLICT"; then + echo "[FAIL] conflict was expected but both A and B show Conflicts: N/A" >&2 + echo "--- A info ---" >&2 + echo "$INFO_A_CONFLICT" >&2 + echo "--- B info ---" >&2 + echo "$INFO_B_CONFLICT" >&2 + exit 1 +fi +echo "[PASS] conflict detected by info" + +echo "[PASS] all requested E2E scenarios completed" \ No newline at end of file diff --git a/src/apps/cli/test/test-push-pull-linux.sh b/src/apps/cli/test/test-push-pull-linux.sh index ca9a846..ffa73ef 100644 --- a/src/apps/cli/test/test-push-pull-linux.sh +++ b/src/apps/cli/test/test-push-pull-linux.sh @@ -5,7 +5,7 @@ SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" CLI_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)" cd "$CLI_DIR" -CLI_ENTRY="${CLI_ENTRY:-$CLI_DIR/dist/index.cjs}" +CLI_CMD=(npm run cli --) RUN_BUILD="${RUN_BUILD:-1}" REMOTE_PATH="${REMOTE_PATH:-test/push-pull.txt}" @@ -19,13 +19,12 @@ if [[ "$RUN_BUILD" == "1" ]]; then npm run build fi -if [[ ! -f "$CLI_ENTRY" ]]; then - echo "[ERROR] CLI entry not found: $CLI_ENTRY" >&2 - exit 1 -fi +run_cli() { + "${CLI_CMD[@]}" "$@" +} echo "[INFO] generating settings from DEFAULT_SETTINGS -> $SETTINGS_FILE" -node "$CLI_ENTRY" init-settings --force "$SETTINGS_FILE" +run_cli init-settings --force "$SETTINGS_FILE" if [[ -n "${COUCHDB_URI:-}" && -n "${COUCHDB_USER:-}" && -n "${COUCHDB_PASSWORD:-}" && -n "${COUCHDB_DBNAME:-}" ]]; then echo "[INFO] applying CouchDB env vars to generated settings" @@ -52,10 +51,10 @@ PULLED_FILE="$WORK_DIR/pull-result.txt" printf 'push-pull-test %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$SRC_FILE" echo "[INFO] push -> $REMOTE_PATH" -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" push "$SRC_FILE" "$REMOTE_PATH" +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" push "$SRC_FILE" "$REMOTE_PATH" echo "[INFO] pull <- $REMOTE_PATH" -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" pull "$REMOTE_PATH" "$PULLED_FILE" +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" pull "$REMOTE_PATH" "$PULLED_FILE" if cmp -s "$SRC_FILE" "$PULLED_FILE"; then echo "[PASS] push/pull roundtrip matched" diff --git a/src/apps/cli/test/test-setup-put-cat-linux.sh b/src/apps/cli/test/test-setup-put-cat-linux.sh index eff5a84..0e4be1e 100755 --- a/src/apps/cli/test/test-setup-put-cat-linux.sh +++ b/src/apps/cli/test/test-setup-put-cat-linux.sh @@ -6,7 +6,7 @@ CLI_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)" REPO_ROOT="$(cd -- "$CLI_DIR/../../.." && pwd)" cd "$CLI_DIR" -CLI_ENTRY="${CLI_ENTRY:-$CLI_DIR/dist/index.cjs}" +CLI_CMD=(npm run cli --) RUN_BUILD="${RUN_BUILD:-1}" REMOTE_PATH="${REMOTE_PATH:-test/setup-put-cat.txt}" SETUP_PASSPHRASE="${SETUP_PASSPHRASE:-setup-passphrase}" @@ -21,13 +21,12 @@ if [[ "$RUN_BUILD" == "1" ]]; then npm run build fi -if [[ ! -f "$CLI_ENTRY" ]]; then - echo "[ERROR] CLI entry not found: $CLI_ENTRY" >&2 - exit 1 -fi +run_cli() { + "${CLI_CMD[@]}" "$@" +} echo "[INFO] generating settings from DEFAULT_SETTINGS -> $SETTINGS_FILE" -node "$CLI_ENTRY" init-settings --force "$SETTINGS_FILE" +run_cli init-settings --force "$SETTINGS_FILE" echo "[INFO] creating setup URI from settings" SETUP_URI="$( @@ -57,7 +56,7 @@ mkdir -p "$VAULT_DIR/test" echo "[INFO] applying setup URI" SETUP_LOG="$WORK_DIR/setup-output.log" set +e -printf '%s\n' "$SETUP_PASSPHRASE" | node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" setup "$SETUP_URI" \ +printf '%s\n' "$SETUP_PASSPHRASE" | run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" setup "$SETUP_URI" \ >"$SETUP_LOG" 2>&1 SETUP_EXIT=$? set -e @@ -78,11 +77,11 @@ SRC_FILE="$WORK_DIR/put-source.txt" printf 'setup-put-cat-test %s\nline-2\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$SRC_FILE" echo "[INFO] put -> $REMOTE_PATH" -cat "$SRC_FILE" | node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" put "$REMOTE_PATH" +cat "$SRC_FILE" | run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" put "$REMOTE_PATH" echo "[INFO] cat <- $REMOTE_PATH" CAT_OUTPUT="$WORK_DIR/cat-output.txt" -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" cat "$REMOTE_PATH" > "$CAT_OUTPUT" +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" cat "$REMOTE_PATH" > "$CAT_OUTPUT" CAT_OUTPUT_CLEAN="$WORK_DIR/cat-output-clean.txt" grep -v '^\[CLIWatchAdapter\] File watching is not enabled in CLI version$' "$CAT_OUTPUT" > "$CAT_OUTPUT_CLEAN" || true @@ -100,7 +99,7 @@ fi echo "[INFO] ls $REMOTE_PATH" LS_OUTPUT="$WORK_DIR/ls-output.txt" -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" ls "$REMOTE_PATH" > "$LS_OUTPUT" +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" ls "$REMOTE_PATH" > "$LS_OUTPUT" LS_LINE="$(grep -F "$REMOTE_PATH" "$LS_OUTPUT" | head -n 1 || true)" if [[ -z "$LS_LINE" ]]; then @@ -129,12 +128,12 @@ fi echo "[PASS] ls output format matched" echo "[INFO] adding more files for ls test cases" -printf 'file-a\n' | node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" put test/a-first.txt >/dev/null -printf 'file-z\n' | node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" put test/z-last.txt >/dev/null +printf 'file-a\n' | run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" put test/a-first.txt >/dev/null +printf 'file-z\n' | run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" put test/z-last.txt >/dev/null echo "[INFO] ls test/ (prefix filter and sorting)" LS_PREFIX_OUTPUT="$WORK_DIR/ls-prefix-output.txt" -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" ls test/ > "$LS_PREFIX_OUTPUT" +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" ls test/ > "$LS_PREFIX_OUTPUT" if [[ "$(wc -l < "$LS_PREFIX_OUTPUT")" -lt 3 ]]; then echo "[FAIL] ls prefix output expected at least 3 rows" >&2 @@ -164,7 +163,7 @@ echo "[PASS] ls prefix and sorting matched" echo "[INFO] ls no-match prefix" LS_EMPTY_OUTPUT="$WORK_DIR/ls-empty-output.txt" -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" ls no-such-prefix/ > "$LS_EMPTY_OUTPUT" +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" ls no-such-prefix/ > "$LS_EMPTY_OUTPUT" if [[ -s "$LS_EMPTY_OUTPUT" ]]; then echo "[FAIL] ls no-match prefix should produce empty output" >&2 cat "$LS_EMPTY_OUTPUT" >&2 @@ -174,7 +173,7 @@ echo "[PASS] ls no-match prefix matched" echo "[INFO] info $REMOTE_PATH" INFO_OUTPUT="$WORK_DIR/info-output.txt" -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" info "$REMOTE_PATH" > "$INFO_OUTPUT" +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" info "$REMOTE_PATH" > "$INFO_OUTPUT" # Check required label lines for label in "ID:" "Revision:" "Conflicts:" "Filename:" "Path:" "Size:" "Chunks:"; do @@ -225,7 +224,7 @@ echo "[PASS] info output format matched" echo "[INFO] info non-existent path" INFO_MISSING_EXIT=0 -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" info no-such-file.md > /dev/null || INFO_MISSING_EXIT=$? +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" info no-such-file.md > /dev/null || INFO_MISSING_EXIT=$? if [[ "$INFO_MISSING_EXIT" -eq 0 ]]; then echo "[FAIL] info on non-existent file should exit non-zero" >&2 exit 1 @@ -233,17 +232,17 @@ fi echo "[PASS] info non-existent path returns non-zero" echo "[INFO] rm test/z-last.txt" -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" rm test/z-last.txt > /dev/null +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" rm test/z-last.txt > /dev/null RM_CAT_EXIT=0 -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" cat test/z-last.txt > /dev/null || RM_CAT_EXIT=$? +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" cat test/z-last.txt > /dev/null || RM_CAT_EXIT=$? if [[ "$RM_CAT_EXIT" -eq 0 ]]; then echo "[FAIL] rm target should not be readable by cat" >&2 exit 1 fi LS_AFTER_RM="$WORK_DIR/ls-after-rm.txt" -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" ls test/ > "$LS_AFTER_RM" +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" ls test/ > "$LS_AFTER_RM" if grep -Fq $'test/z-last.txt\t' "$LS_AFTER_RM"; then echo "[FAIL] rm target should not appear in ls output" >&2 cat "$LS_AFTER_RM" >&2 @@ -252,7 +251,7 @@ fi echo "[PASS] rm removed target from visible entries" echo "[INFO] resolve test/a-first.txt using current revision" -RESOLVE_LS_LINE="$(node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" ls test/a-first.txt | head -n 1)" +RESOLVE_LS_LINE="$(run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" ls test/a-first.txt | head -n 1)" if [[ -z "$RESOLVE_LS_LINE" ]]; then echo "[FAIL] could not fetch revision for resolve test" >&2 exit 1 @@ -263,12 +262,12 @@ if [[ -z "$RESOLVE_REV" ]]; then exit 1 fi -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" resolve test/a-first.txt "$RESOLVE_REV" > /dev/null +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" resolve test/a-first.txt "$RESOLVE_REV" > /dev/null echo "[PASS] resolve accepted current revision" echo "[INFO] resolve with non-existent revision" RESOLVE_BAD_EXIT=0 -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" resolve test/a-first.txt 9-no-such-rev > /dev/null || RESOLVE_BAD_EXIT=$? +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" resolve test/a-first.txt 9-no-such-rev > /dev/null || RESOLVE_BAD_EXIT=$? if [[ "$RESOLVE_BAD_EXIT" -eq 0 ]]; then echo "[FAIL] resolve with non-existent revision should exit non-zero" >&2 exit 1 @@ -285,13 +284,13 @@ printf 'revision-v1\n' > "$REV_V1_FILE" printf 'revision-v2\n' > "$REV_V2_FILE" printf 'revision-v3\n' > "$REV_V3_FILE" -cat "$REV_V1_FILE" | node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" put "$REV_PATH" > /dev/null -cat "$REV_V2_FILE" | node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" put "$REV_PATH" > /dev/null -cat "$REV_V3_FILE" | node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" put "$REV_PATH" > /dev/null +cat "$REV_V1_FILE" | run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" put "$REV_PATH" > /dev/null +cat "$REV_V2_FILE" | run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" put "$REV_PATH" > /dev/null +cat "$REV_V3_FILE" | run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" put "$REV_PATH" > /dev/null echo "[INFO] info $REV_PATH (past revisions)" REV_INFO_OUTPUT="$WORK_DIR/rev-info-output.txt" -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" info "$REV_PATH" > "$REV_INFO_OUTPUT" +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" info "$REV_PATH" > "$REV_INFO_OUTPUT" PAST_REV="$(grep '^ rev: ' "$REV_INFO_OUTPUT" | head -n 1 | sed 's/^ rev: //')" if [[ -z "$PAST_REV" ]]; then @@ -302,7 +301,7 @@ fi echo "[INFO] cat-rev $REV_PATH @ $PAST_REV" REV_CAT_OUTPUT="$WORK_DIR/rev-cat-output.txt" -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" cat-rev "$REV_PATH" "$PAST_REV" > "$REV_CAT_OUTPUT" +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" cat-rev "$REV_PATH" "$PAST_REV" > "$REV_CAT_OUTPUT" if cmp -s "$REV_CAT_OUTPUT" "$REV_V1_FILE" || cmp -s "$REV_CAT_OUTPUT" "$REV_V2_FILE"; then echo "[PASS] cat-rev matched one of the past revisions from info" @@ -321,7 +320,7 @@ fi echo "[INFO] pull-rev $REV_PATH @ $PAST_REV" REV_PULL_OUTPUT="$WORK_DIR/rev-pull-output.txt" -node "$CLI_ENTRY" "$VAULT_DIR" --settings "$SETTINGS_FILE" pull-rev "$REV_PATH" "$REV_PULL_OUTPUT" "$PAST_REV" > /dev/null +run_cli "$VAULT_DIR" --settings "$SETTINGS_FILE" pull-rev "$REV_PATH" "$REV_PULL_OUTPUT" "$PAST_REV" > /dev/null if cmp -s "$REV_PULL_OUTPUT" "$REV_V1_FILE" || cmp -s "$REV_PULL_OUTPUT" "$REV_V2_FILE"; then echo "[PASS] pull-rev matched one of the past revisions from info" diff --git a/src/apps/cli/test/test-sync-two-local-databases-linux.sh b/src/apps/cli/test/test-sync-two-local-databases-linux.sh index e4f7eb6..8875943 100755 --- a/src/apps/cli/test/test-sync-two-local-databases-linux.sh +++ b/src/apps/cli/test/test-sync-two-local-databases-linux.sh @@ -6,7 +6,7 @@ SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" CLI_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)" cd "$CLI_DIR" -CLI_ENTRY="${CLI_ENTRY:-$CLI_DIR/dist/index.cjs}" +CLI_CMD=(npm run cli --) RUN_BUILD="${RUN_BUILD:-1}" COUCHDB_URI="${COUCHDB_URI:-}" COUCHDB_USER="${COUCHDB_USER:-}" @@ -26,10 +26,9 @@ if [[ "$RUN_BUILD" == "1" ]]; then npm run build fi -if [[ ! -f "$CLI_ENTRY" ]]; then - echo "[ERROR] CLI entry not found: $CLI_ENTRY" >&2 - exit 1 -fi +run_cli() { + "${CLI_CMD[@]}" "$@" +} DB_SUFFIX="$(date +%s)-$RANDOM" COUCHDB_DBNAME="${COUCHDB_DBNAME_BASE}-${DB_SUFFIX}" @@ -42,8 +41,8 @@ SETTINGS_A="$WORK_DIR/a-settings.json" SETTINGS_B="$WORK_DIR/b-settings.json" mkdir -p "$VAULT_A" "$VAULT_B" -node "$CLI_ENTRY" init-settings --force "$SETTINGS_A" >/dev/null -node "$CLI_ENTRY" init-settings --force "$SETTINGS_B" >/dev/null +run_cli init-settings --force "$SETTINGS_A" >/dev/null +run_cli init-settings --force "$SETTINGS_B" >/dev/null apply_settings() { local settings_file="$1" @@ -73,11 +72,11 @@ apply_settings "$SETTINGS_A" apply_settings "$SETTINGS_B" run_cli_a() { - node "$CLI_ENTRY" "$VAULT_A" --settings "$SETTINGS_A" "$@" + run_cli "$VAULT_A" --settings "$SETTINGS_A" "$@" } run_cli_b() { - node "$CLI_ENTRY" "$VAULT_B" --settings "$SETTINGS_B" "$@" + run_cli "$VAULT_B" --settings "$SETTINGS_B" "$@" } sync_a() { diff --git a/src/apps/cli/util/couchdb-init.sh b/src/apps/cli/util/couchdb-init.sh new file mode 100755 index 0000000..7537ef8 --- /dev/null +++ b/src/apps/cli/util/couchdb-init.sh @@ -0,0 +1,47 @@ +#!/bin/bash +set -euo pipefail + +if [[ -z "${hostname:-}" ]]; then + echo "ERROR: Hostname missing" + exit 1 +fi +if [[ -z "${username:-}" ]]; then + echo "ERROR: Username missing" + exit 1 +fi + +if [[ -z "${password:-}" ]]; then + echo "ERROR: Password missing" + exit 1 +fi +if [[ -z "${node:-}" ]]; then + echo "INFO: defaulting to _local" + node=_local +fi + +hostname="${hostname%/}" +# Podman environments often resolve localhost to ::1 while published ports are IPv4-only. +hostname="${hostname/localhost/127.0.0.1}" + +curl_json() { + curl -4 -sS --fail --connect-timeout 3 --max-time 15 "$@" +} + +echo "-- Configuring CouchDB by REST APIs... -->" +echo " Hostname: $hostname" +echo " Username: $username" + +until (curl_json -X POST "${hostname}/_cluster_setup" -H "Content-Type: application/json" -d "{\"action\":\"enable_single_node\",\"username\":\"${username}\",\"password\":\"${password}\",\"bind_address\":\"0.0.0.0\",\"port\":5984,\"singlenode\":true}" --user "${username}:${password}"); do sleep 5; done +until (curl_json -X PUT "${hostname}/_node/${node}/_config/chttpd/require_valid_user" -H "Content-Type: application/json" -d '"true"' --user "${username}:${password}"); do sleep 5; done +until (curl_json -X PUT "${hostname}/_node/${node}/_config/chttpd_auth/require_valid_user" -H "Content-Type: application/json" -d '"true"' --user "${username}:${password}"); do sleep 5; done +until (curl_json -X PUT "${hostname}/_node/${node}/_config/httpd/WWW-Authenticate" -H "Content-Type: application/json" -d '"Basic realm=\"couchdb\""' --user "${username}:${password}"); do sleep 5; done +until (curl_json -X PUT "${hostname}/_node/${node}/_config/httpd/enable_cors" -H "Content-Type: application/json" -d '"true"' --user "${username}:${password}"); do sleep 5; done +until (curl_json -X PUT "${hostname}/_node/${node}/_config/chttpd/enable_cors" -H "Content-Type: application/json" -d '"true"' --user "${username}:${password}"); do sleep 5; done +until (curl_json -X PUT "${hostname}/_node/${node}/_config/chttpd/max_http_request_size" -H "Content-Type: application/json" -d '"4294967296"' --user "${username}:${password}"); do sleep 5; done +until (curl_json -X PUT "${hostname}/_node/${node}/_config/couchdb/max_document_size" -H "Content-Type: application/json" -d '"50000000"' --user "${username}:${password}"); do sleep 5; done +until (curl_json -X PUT "${hostname}/_node/${node}/_config/cors/credentials" -H "Content-Type: application/json" -d '"true"' --user "${username}:${password}"); do sleep 5; done +until (curl_json -X PUT "${hostname}/_node/${node}/_config/cors/origins" -H "Content-Type: application/json" -d '"*"' --user "${username}:${password}"); do sleep 5; done + +# Create test database +until (curl_json -X PUT --user "${username}:${password}" "${hostname}/${dbname}" >/dev/null); do sleep 5; done +echo "<-- Configuring CouchDB by REST APIs Done!" diff --git a/src/apps/cli/util/couchdb-start.sh b/src/apps/cli/util/couchdb-start.sh new file mode 100755 index 0000000..038457f --- /dev/null +++ b/src/apps/cli/util/couchdb-start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e +echo "username: $username" +docker run -d --name couchdb-test -p 5989:5984 -e COUCHDB_USER=$username -e COUCHDB_PASSWORD=$password -e COUCHDB_SINGLE_NODE=y couchdb:3.5.0 \ No newline at end of file diff --git a/src/apps/cli/util/couchdb-stop.sh b/src/apps/cli/util/couchdb-stop.sh new file mode 100755 index 0000000..bf423f4 --- /dev/null +++ b/src/apps/cli/util/couchdb-stop.sh @@ -0,0 +1,3 @@ +#!/bin/bash +docker stop couchdb-test +docker rm couchdb-test \ No newline at end of file diff --git a/src/apps/cli/util/minio-init.sh b/src/apps/cli/util/minio-init.sh new file mode 100755 index 0000000..353832b --- /dev/null +++ b/src/apps/cli/util/minio-init.sh @@ -0,0 +1,47 @@ +#!/bin/bash +set -e +cat >/tmp/mybucket-rw.json < +# +# http://localhost:63315 +# http://localhost:63316 +# http://localhost +# GET +# PUT +# POST +# DELETE +# HEAD +# * +# +# " > /tmp/cors.xml +# docker run --rm --network host -v /tmp/mybucket-rw.json:/tmp/mybucket-rw.json --entrypoint=/bin/sh minio/mc -c " +# mc alias set myminio $minioEndpoint $username $password +# mc mb --ignore-existing myminio/$bucketName +# mc admin policy create myminio my-custom-policy /tmp/mybucket-rw.json +# echo 'Creating service account for user $username with access key $accessKey' +# mc admin user svcacct add --access-key '$accessKey' --secret-key '$secretKey' myminio '$username' +# mc admin policy attach myminio my-custom-policy --user '$accessKey' +# echo 'Verifying policy and user creation:' +# mc admin user svcacct info myminio '$accessKey' +# " + +docker run --rm --network host -v /tmp/mybucket-rw.json:/tmp/mybucket-rw.json --entrypoint=/bin/sh minio/mc -c " + mc alias set myminio $minioEndpoint $accessKey $secretKey + mc mb --ignore-existing myminio/$bucketName +" \ No newline at end of file diff --git a/src/apps/cli/util/minio-start.sh b/src/apps/cli/util/minio-start.sh new file mode 100755 index 0000000..4554786 --- /dev/null +++ b/src/apps/cli/util/minio-start.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker run -d --name minio-test -p 9000:9000 -p 9001:9001 -e MINIO_ROOT_USER=$accessKey -e MINIO_ROOT_PASSWORD=$secretKey -e MINIO_SERVER_URL=$minioEndpoint minio/minio server /data --console-address ':9001' \ No newline at end of file diff --git a/src/apps/cli/util/minio-stop.sh b/src/apps/cli/util/minio-stop.sh new file mode 100755 index 0000000..08703b7 --- /dev/null +++ b/src/apps/cli/util/minio-stop.sh @@ -0,0 +1,3 @@ +#!/bin/bash +docker stop minio-test +docker rm minio-test \ No newline at end of file diff --git a/src/lib b/src/lib index 3ce1f81..41e2340 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 3ce1f81a21d6a5278ff876478a89fca5ca6fbbeb +Subproject commit 41e234023530cea101473c5d2f781941b6bbc9e8