From dc4085fe52f21dccd03405796bbb40376d05e622 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Mon, 25 Mar 2024 19:32:33 -0700 Subject: [PATCH 1/6] test: load kubo fixtures and byte range tests on them chore: single kubo gateway for parallel tests chore: attempting to remove beforeEach for byte-ranges chore: working on dx for e2e chore: move gateway conformance to global setup/teardown test: e2e fixtures for subdomain and path routing chore: remove unnecessary service worker wait --- .gitignore | 2 + package-lock.json | 486 +++++++++++++++++++--- package.json | 2 + playwright.config.js | 10 +- src/components/collapsible.tsx | 2 +- src/components/config.tsx | 4 +- test-e2e/byte-range.test.ts | 33 +- test-e2e/fixtures/config-test-fixtures.ts | 79 ++++ test-e2e/fixtures/create-kubo-node.ts | 19 +- test-e2e/fixtures/do-range-request.ts | 39 ++ test-e2e/fixtures/load-fixture-data.ts | 9 - test-e2e/fixtures/load-kubo-fixtures.ts | 124 ++++++ test-e2e/fixtures/locators.ts | 16 +- test-e2e/fixtures/set-sw-config.ts | 60 ++- test-e2e/global-setup.js | 17 - test-e2e/global-setup.ts | 12 + test-e2e/global-teardown.ts | 7 + test-e2e/layout.test.ts | 1 + test-e2e/path-routing.test.ts | 7 +- test-e2e/subdomain-detection.test.ts | 30 +- test-e2e/website-loading.test.ts | 34 +- 21 files changed, 821 insertions(+), 172 deletions(-) create mode 100644 test-e2e/fixtures/config-test-fixtures.ts create mode 100644 test-e2e/fixtures/do-range-request.ts delete mode 100644 test-e2e/fixtures/load-fixture-data.ts create mode 100644 test-e2e/fixtures/load-kubo-fixtures.ts delete mode 100644 test-e2e/global-setup.js create mode 100644 test-e2e/global-setup.ts create mode 100644 test-e2e/global-teardown.ts diff --git a/.gitignore b/.gitignore index 45cdd944..ffd04799 100644 --- a/.gitignore +++ b/.gitignore @@ -224,3 +224,5 @@ playwright-report .direnv *.nix .coverage +test-e2e/fixtures/data/test-repo +test-e2e/fixtures/data/gateway-conformance-fixtures diff --git a/package-lock.json b/package-lock.json index 6922ffbd..5ff3a07c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,8 @@ "copyfiles": "^2.4.1", "css-loader": "^6.10.0", "eslint-config-standard-with-typescript": "^34.0.1", + "execa": "^8.0.1", + "glob": "^10.3.10", "html-webpack-plugin": "^5.6.0", "http-proxy": "^1.18.1", "ipfsd-ctl": "^13.0.0", @@ -8246,6 +8248,26 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/copyfiles/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/copyfiles/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -9138,6 +9160,48 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/del/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/del/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/del/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/del/node_modules/p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -12037,6 +12101,48 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/flat-cache/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/flat-cache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flat-cache/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/flat-cache/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -12350,6 +12456,16 @@ "node": ">=0.10.0" } }, + "node_modules/gh-pages/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/gh-pages/node_modules/find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", @@ -12380,6 +12496,26 @@ "node": ">=8" } }, + "node_modules/gh-pages/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/gh-pages/node_modules/globby": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", @@ -12423,6 +12559,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gh-pages/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/gh-pages/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -12495,20 +12643,22 @@ } }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -12532,28 +12682,6 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/global-agent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", @@ -15021,6 +15149,48 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-processinfo/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/istanbul-lib-processinfo/node_modules/p-map": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", @@ -21890,6 +22060,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/nyc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/nyc/node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -21977,6 +22157,26 @@ "node": ">=8.0.0" } }, + "node_modules/nyc/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/nyc/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -22013,6 +22213,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/nyc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/nyc/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -22975,6 +23187,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/patch-package/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/patch-package/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -23024,6 +23246,26 @@ "node": ">=10" } }, + "node_modules/patch-package/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/patch-package/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -23045,6 +23287,18 @@ "node": ">=10" } }, + "node_modules/patch-package/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/patch-package/node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -23564,6 +23818,16 @@ "node": ">=12" } }, + "node_modules/playwright-test/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/playwright-test/node_modules/c8": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/c8/-/c8-8.0.1.tgz", @@ -23653,6 +23917,26 @@ "node": ">=8.0.0" } }, + "node_modules/playwright-test/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/playwright-test/node_modules/globby": { "version": "14.0.1", "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz", @@ -23673,6 +23957,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/playwright-test/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/playwright-test/node_modules/path-type": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", @@ -24418,6 +24714,16 @@ "readable-stream": "^3.4.0" } }, + "node_modules/react-native-test-runner/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/react-native-test-runner/node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -24550,6 +24856,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/react-native-test-runner/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/react-native-test-runner/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -24696,6 +25022,18 @@ "node": ">=6" } }, + "node_modules/react-native-test-runner/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/react-native-test-runner/node_modules/normalize-package-data": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", @@ -25674,28 +26012,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/roarr": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", @@ -26861,6 +27177,16 @@ "node": ">=8" } }, + "node_modules/spawn-wrap/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/spawn-wrap/node_modules/foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -26874,6 +27200,26 @@ "node": ">=8.0.0" } }, + "node_modules/spawn-wrap/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/spawn-wrap/node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -26889,6 +27235,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/spawn-wrap/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/spawn-wrap/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -27699,6 +28057,26 @@ "concat-map": "0.0.1" } }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/test-exclude/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", diff --git a/package.json b/package.json index 60ccecc3..7b107cf6 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,8 @@ "copyfiles": "^2.4.1", "css-loader": "^6.10.0", "eslint-config-standard-with-typescript": "^34.0.1", + "execa": "^8.0.1", + "glob": "^10.3.10", "html-webpack-plugin": "^5.6.0", "http-proxy": "^1.18.1", "ipfsd-ctl": "^13.0.0", diff --git a/playwright.config.js b/playwright.config.js index bdaab8b8..10374885 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -9,11 +9,7 @@ export default defineConfig({ forbidOnly: Boolean(process.env.CI), /* Retry on CI only */ retries: (process.env.CI != null) ? 2 : 0, - /** - * Opt out of parallel tests by setting workers to 1. - * We don't want to bombard Helia gateway with parallel requests, it's not ready for that yet. - */ - workers: 1, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ // reporter: 'html', // Uncomment to generate HTML report use: { @@ -28,7 +24,9 @@ export default defineConfig({ ignoreHTTPSErrors: true }, - globalSetup: './test-e2e/global-setup.js', + + globalSetup: './test-e2e/global-setup.ts', + globalTeardown: './test-e2e/global-teardown.ts', projects: [ { diff --git a/src/components/collapsible.tsx b/src/components/collapsible.tsx index 6e75d243..72203ca3 100644 --- a/src/components/collapsible.tsx +++ b/src/components/collapsible.tsx @@ -14,7 +14,7 @@ export function Collapsible ({ children, collapsedLabel, expandedLabel, collapse return ( { setCollapsed(!isCollapsed) }} /> - +
{children}
diff --git a/src/components/config.tsx b/src/components/config.tsx index be18bfca..9f6d96d2 100644 --- a/src/components/config.tsx +++ b/src/components/config.tsx @@ -89,8 +89,8 @@ export default (): JSX.Element | null => { return (
- - + + { void saveConfig() }} /> diff --git a/test-e2e/byte-range.test.ts b/test-e2e/byte-range.test.ts index 144221b6..2775a6eb 100644 --- a/test-e2e/byte-range.test.ts +++ b/test-e2e/byte-range.test.ts @@ -1,20 +1,29 @@ -import { test, expect } from '@playwright/test' -import { waitForServiceWorker } from './fixtures/wait-for-service-worker.js' +import { testPathRouting as test, expect } from './fixtures/config-test-fixtures.js' +import { doRangeRequest } from './fixtures/do-range-request.js' test.describe('byte-ranges', () => { test('should be able to get a single character', async ({ page }) => { - await page.goto('/', { waitUntil: 'networkidle' }) - // wait for service worker to load - await waitForServiceWorker(page) + const { text, byteSize, statusCode } = await doRangeRequest({ page, range: 'bytes=1-2', path: '/ipfs/bafkqaddimvwgy3zao5xxe3debi' }) - const partialText = await page.evaluate(async () => { - const response = await fetch('/ipfs/bafkqaddimvwgy3zao5xxe3debi', { headers: { range: 'bytes=1-2' } }) - const text = await response.text() - return text - }) + expect(statusCode).toBe(206) + expect(byteSize).toBe(2) + expect(text).toBe('el') + }) + + test('can get 0-0 byte range from car with missing data', async ({ page, request }) => { + const { text, byteSize, statusCode } = await doRangeRequest({ page, range: 'bytes=0-0', path: '/ipfs/QmYhmPjhFjYFyaoiuNzYv8WGavpSRDwdHWe5B4M5du5Rtk' }) + expect(statusCode).toBe(206) + expect(byteSize).toBe(1) + expect(text).toBe('+') + }) - if (partialText == null) throw new Error('missing response') + test('can get trailing byte range from car with missing data', async ({ page }) => { + const { bytes, byteSize, statusCode } = await doRangeRequest({ page, range: 'bytes=2200-', path: '/ipfs/QmYhmPjhFjYFyaoiuNzYv8WGavpSRDwdHWe5B4M5du5Rtk' }) + // TODO: do we need to check the full 872 bytes...? + const tailBytes = [254, 0, 186, 192, 51, 66, 190, 27, 53, 147, 195, 115, 213, 65, 50, 246, 231, 155, 151, 106, 247, 199, 27, 193, 30, 214, 167, 87, 207, 246, 215, 109, 7, 72, 10, 217, 255, 62, 162, 153, 179, 12, 120, 75, 156, 74, 249, 212, 63, 218, 127, 121, 88, 111, 51, 172, 189, 176, 104, 4, 120, 182, 106, 44, 86, 33, 15, 120, 106, 126, 239, 188, 14, 190, 138, 125, 146, 14, 169, 101, 236, 250, 12, 210, 47, 145, 81, 104, 102, 153, 36, 245, 127, 60, 229, 121, 91, 204, 159, 235, 148, 44, 156, 193, 4, 59, 49, 124, 43, 30, 173, 26, 189, 95, 48, 35, 48, 91, 178, 43, 176, 171, 211, 145, 160, 251, 124, 201, 201, 29, 94, 70, 105, 216, 83, 99, 107, 86, 53, 157, 254, 16, 141, 147, 175, 2, 180, 137, 55, 174, 125, 172, 217, 214, 114, 46, 220, 23, 45, 81, 204, 215, 51, 114, 7, 115, 223, 226, 73, 114, 105, 6, 208, 213, 74, 116, 24, 98, 243, 201, 254, 195, 40, 227, 127, 14, 158, 125, 162, 150, 25, 15, 68, 101, 217, 162, 37, 253, 252, 79, 13, 33, 115, 57, 136, 0, 222, 45, 45, 105, 30, 245, 189, 133, 13, 14, 123, 15, 232, 237, 37, 8, 84, 212, 21, 233, 46, 136, 38, 236, 239, 216, 186, 175, 188, 165, 168, 69, 223, 159, 33, 96, 240, 68, 134, 121, 122, 4, 125, 16, 190, 105, 139, 78, 40, 86, 4, 125, 198, 224, 86, 198, 20, 47, 12, 207, 170, 127, 227, 92, 152, 37, 117, 137, 86, 85, 56, 67, 118, 157, 45, 31, 217, 81, 207, 129, 195, 28, 10, 238, 91, 142, 208, 116, 37, 28, 140, 161, 212, 45, 10, 208, 12, 37, 102, 165, 5, 65, 36, 153, 160, 100, 252, 115, 39, 47, 99, 24, 70, 90, 36, 190, 138, 186, 156, 30, 216, 238, 168, 207, 6, 28, 224, 108, 8, 249, 18, 143, 177, 198, 200, 189, 184, 33, 139, 249, 40, 56, 173, 235, 245, 84, 66, 123, 133, 195, 118, 145, 168, 2, 36, 118, 243, 195, 128, 234, 100, 105, 180, 141, 195, 9, 31, 204, 33, 83, 245, 138, 93, 20, 136, 151, 153, 188, 92, 65, 204, 254, 187, 69, 122, 26, 147, 86, 141, 41, 160, 75, 15, 136, 44, 186, 129, 176, 23, 87, 108, 217, 91, 195, 156, 7, 222, 74, 109, 209, 226, 15, 0, 190, 80, 194, 209, 51, 76, 5, 94, 95, 40, 206, 124, 251, 139, 162, 142, 142, 180, 4, 30, 213, 5, 44, 156, 227, 233, 80, 224, 74, 225, 6, 72, 129, 38, 11, 104, 166, 184, 225, 174, 152, 76, 206, 117, 64, 158, 252, 221, 11, 148, 24, 250, 171, 89, 117, 252, 126, 95, 169, 74, 133, 20, 180, 160, 209, 104, 31, 220, 179, 238, 33, 85, 234, 190, 30, 149, 15, 190, 57, 248, 134, 57, 26, 176, 175, 237, 133, 238, 151, 27, 135, 111, 167, 217, 12, 149, 173, 36, 34, 102, 50, 197, 17, 209, 164, 15, 61, 182, 195, 48, 56, 112, 143, 91, 210, 122, 240, 191, 144, 53, 246, 164, 169, 21, 119, 94, 235, 249, 131, 231, 162, 226, 61, 23, 81, 203, 253, 120, 160, 106, 41, 22, 70, 11, 149, 140, 231, 53, 149, 91, 197, 118, 210, 133, 206, 232, 188, 103, 61, 130, 28, 158, 104, 210, 20, 239, 143, 47, 30, 51, 127, 7, 23, 123, 55, 90, 133, 81, 40, 235, 70, 247, 0, 217, 174, 10, 190, 210, 104, 45, 121, 8, 201, 246, 180, 74, 210, 59, 145, 181, 196, 243, 192, 243, 62, 17, 57, 30, 215, 171, 186, 1, 193, 143, 94, 79, 215, 170, 79, 8, 38, 138, 138, 107, 145, 58, 122, 165, 144, 92, 179, 109, 115, 111, 122, 125, 7, 128, 112, 215, 115, 138, 93, 77, 163, 11, 144, 235, 41, 78, 22, 130, 204, 25, 33, 6, 236, 142, 250, 25, 20, 241, 220, 244, 90, 54, 149, 18, 5, 179, 175, 77, 130, 156, 97, 197, 68, 146, 235, 129, 53, 39, 33, 33, 156, 91, 176, 239, 247, 142, 203, 102, 10, 24, 227, 204, 20, 3, 55, 126, 73, 249, 152, 220, 163, 3, 251, 48, 251, 209, 182, 194, 65, 44, 40, 201, 27, 67, 64, 174, 252, 115, 139, 85, 181, 161, 214, 88, 103, 222, 188, 17, 198, 112, 123, 59, 138, 107, 173, 51, 212, 154, 189, 35, 214, 151, 82, 15, 26, 198, 39, 88, 241, 57, 58, 77, 113, 9, 74, 133, 101, 197, 12, 24, 121, 159, 87, 46, 44, 165, 83, 148, 13, 152, 175, 255, 240, 49, 110, 36, 124, 211, 219, 86, 232, 198, 153, 118, 221, 175, 139, 215, 45, 217, 101, 246, 101, 34, 154, 41, 179, 113, 48, 131, 146, 31, 226, 215, 119, 65, 97, 35, 136, 72, 179, 2, 146, 187, 120, 175, 229, 11, 143, 132, 151, 217, 134, 136, 54, 5, 211, 242, 70, 250, 210, 238, 151, 68, 63, 195, 112, 59, 179, 213, 163, 35, 28, 226, 35, 160, 44, 195, 31] - expect(partialText).toBe('el') + expect(statusCode).toBe(206) + expect(byteSize).toBe(872) + expect(bytes).toStrictEqual(tailBytes) }) }) diff --git a/test-e2e/fixtures/config-test-fixtures.ts b/test-e2e/fixtures/config-test-fixtures.ts new file mode 100644 index 00000000..e1c862c7 --- /dev/null +++ b/test-e2e/fixtures/config-test-fixtures.ts @@ -0,0 +1,79 @@ +import { test as base, type Page } from '@playwright/test' +import { setConfig, setSubdomainConfig } from './set-sw-config.js' +import { waitForServiceWorker } from './wait-for-service-worker.js' + +/** + * You should use this fixture instead of the `test` fixture from `@playwright/test` when testing path routing via the service worker. + */ +export const testPathRouting = base.extend({ + page: async ({ page }, use) => { + await page.goto('http://127.0.0.1:3333', { waitUntil: 'networkidle' }) + await waitForServiceWorker(page) + await setConfig({ + page, + config: { + gateways: [process.env.KUBO_GATEWAY as string], + routers: [process.env.KUBO_GATEWAY as string] + } + }) + + await use(page) + } +}) + +/** + * When testing subdomain routing via the service worker, using this fixture will automatically set the config for the subdomain. + * This is useful for testing subdomain routing without having to manually set the config for each subdomain. + * + * @example + * + * ```ts + * import { testSubdomainRouting as test, expect } from './fixtures/config-test-fixtures.js' + * + * test.describe('subdomain-detection', () => { + * test('path requests are redirected to subdomains', async ({ page }) => { + * await page.goto('http://bafkqablimvwgy3y.ipfs.localhost:3333/', { waitUntil: 'networkidle' }) + * const bodyTextLocator = page.locator('body') + * await expect(bodyTextLocator).toContainText('hello') + * }) + * }) + * ``` + */ +export const testSubdomainRouting = base.extend({ + page: async ({ page }, use) => { + await page.goto('http://localhost:3333', { waitUntil: 'networkidle' }) + await waitForServiceWorker(page) + + const oldPageGoto = page.goto.bind(page) + page.goto = async (url: Parameters[0], options: Parameters[1]): ReturnType => { + const response = await oldPageGoto(url, options) + if (['.ipfs.', '.ipns.'].some((part) => url.includes(part))) { + await setSubdomainConfig({ + page, + config: { + autoReload: true, + gateways: [process.env.KUBO_GATEWAY as string], + routers: [process.env.KUBO_GATEWAY as string] + } + }) + } else { + // already set on root. + } + return response + } + + // set config for the initial page + await setConfig({ + page, + config: { + autoReload: true, + gateways: [process.env.KUBO_GATEWAY as string], + routers: [process.env.KUBO_GATEWAY as string] + } + }) + + await use(page) + } +}) + +export { expect } from '@playwright/test' diff --git a/test-e2e/fixtures/create-kubo-node.ts b/test-e2e/fixtures/create-kubo-node.ts index 6cf8590b..bdee1215 100644 --- a/test-e2e/fixtures/create-kubo-node.ts +++ b/test-e2e/fixtures/create-kubo-node.ts @@ -1,6 +1,7 @@ import { createController, type Controller } from 'ipfsd-ctl' import { path as kuboPath } from 'kubo' import * as kuboRpcClient from 'kubo-rpc-client' +import { kuboRepoDir } from './load-kubo-fixtures.js' export async function createKuboNode (): Promise { return createController({ @@ -8,23 +9,7 @@ export async function createKuboNode (): Promise { ipfsBin: kuboPath(), test: true, ipfsOptions: { - config: { - Addresses: { - Swarm: [ - '/ip4/0.0.0.0/tcp/4001', - '/ip4/0.0.0.0/tcp/4002/ws' - ], - Gateway: '/ip4/127.0.0.1/tcp/8180' - }, - Gateway: { - NoFetch: true, - ExposeRoutingAPI: true, - HTTPHeaders: { - 'Access-Control-Allow-Origin': ['*'], - 'Access-Control-Allow-Methods': ['GET', 'POST', 'PUT', 'OPTIONS'] - } - } - } + repo: kuboRepoDir }, args: ['--enable-pubsub-experiment', '--enable-namesys-pubsub'] }) diff --git a/test-e2e/fixtures/do-range-request.ts b/test-e2e/fixtures/do-range-request.ts new file mode 100644 index 00000000..8d11ed3e --- /dev/null +++ b/test-e2e/fixtures/do-range-request.ts @@ -0,0 +1,39 @@ +import type { Page } from '@playwright/test' + +export interface RangeRequestResult { + byteSize: number + /** + * playwright doesn't provide a way to get the raw bytes, so we have to convert the ArrayBuffer to an array of numbers + */ + bytes: number[] + headers: Record + statusCode: number + text: string +} + +/** + * Normally, you could use request.get in playwright to query a server, but this does not go to the service worker + */ +export async function doRangeRequest ({ page, range, path }: { range: string, page: Page, path: string }): Promise { + return page.evaluate(async ({ path, range }) => { + const response = await fetch(path, { headers: { range } }) + const clone = response.clone() + const buffer = await response.arrayBuffer() + const byteSize = buffer.byteLength + const bytes = Array.from(new Uint8Array(buffer)) + const text = await clone.text() + const statusCode = response.status + const headers = {} + response.headers.forEach((value, key) => { + headers[key] = value + }) + + return { + byteSize, + bytes, + headers, + statusCode, + text + } + }, { path, range }) +} diff --git a/test-e2e/fixtures/load-fixture-data.ts b/test-e2e/fixtures/load-fixture-data.ts deleted file mode 100644 index 2fe33309..00000000 --- a/test-e2e/fixtures/load-fixture-data.ts +++ /dev/null @@ -1,9 +0,0 @@ -import loadFixture from 'aegir/fixtures' -import drain from 'it-drain' -import type { Controller } from 'ipfsd-ctl' - -export async function loadFixtureDataCar (controller: Controller, path: string): Promise { - const fixtureData = `test-e2e/fixtures/data/${path}` - const buf = loadFixture(fixtureData) - await drain(controller.api.dag.import([buf])) -} diff --git a/test-e2e/fixtures/load-kubo-fixtures.ts b/test-e2e/fixtures/load-kubo-fixtures.ts new file mode 100644 index 00000000..d79d54ee --- /dev/null +++ b/test-e2e/fixtures/load-kubo-fixtures.ts @@ -0,0 +1,124 @@ +/** + * This is required to update gateway-conformance fixtures + * + * Can only be ran from node + * + * external command dependencies: + * - `docker` + * - `npx` + */ + +import { readFile } from 'node:fs/promises' +import { dirname, basename, relative, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import { logger } from '@libp2p/logger' +import { $, type Options } from 'execa' +import { glob } from 'glob' + +// eslint-disable-next-line @typescript-eslint/naming-convention +const __dirname = dirname(fileURLToPath(import.meta.url)) + +const log = logger('kubo-init') + +// This needs to match the `repo` property provided to `ipfsd-ctl` in `createKuboNode` so our kubo instance in tests use the same repo +export const kuboRepoDir = resolve(__dirname, 'data/test-repo') +export const GWC_FIXTURES_PATH = resolve(__dirname, 'data/gateway-conformance-fixtures') + +export async function loadKuboFixtures (): Promise { + await $`mkdir -p ${kuboRepoDir}` + await $`mkdir -p ${GWC_FIXTURES_PATH}` + + await attemptKuboInit() + + await configureKubo() + + await downloadFixtures() + + await loadFixtures() +} + +function getExecaOptions ({ cwd, ipfsNsMap }: { cwd?: string, ipfsNsMap?: string } = {}): Options { + return { + cwd: cwd ?? __dirname, + env: { + IPFS_PATH: kuboRepoDir, + IPFS_NS_MAP: ipfsNsMap + } + } +} + +async function attemptKuboInit (): Promise { + const execaOptions = getExecaOptions() + try { + await $(execaOptions)`npx -y kubo init` + log('Kubo initialized at %s', kuboRepoDir) + } catch (e: any) { + if (e.stderr?.includes('ipfs daemon is running') === true) { + log('Kubo is already running') + return + } + if (e.stderr?.includes('already exists!') === true) { + log('Kubo was already initialized at %s', kuboRepoDir) + return + } + + throw e + } +} + +async function configureKubo (): Promise { + const execaOptions = getExecaOptions() + try { + await $(execaOptions)`npx -y kubo config Addresses.Gateway /ip4/127.0.0.1/tcp/8180` + await $(execaOptions)`npx -y kubo config --json Gateway.NoFetch true` + await $(execaOptions)`npx -y kubo config --json Gateway.ExposeRoutingAPI true` + await $(execaOptions)`npx -y kubo config --json Gateway.HTTPHeaders.Access-Control-Allow-Origin '["*"]'` + await $(execaOptions)`npx -y kubo config --json Gateway.HTTPHeaders.Access-Control-Allow-Methods '["GET", "POST", "PUT", "OPTIONS"]'` + log('Kubo configured') + } catch (e) { + log.error('Failed to configure Kubo', e) + } +} + +async function downloadFixtures (): Promise { + // if the fixtures are already downloaded, we don't need to download them again + const allFixtures = await glob([`${GWC_FIXTURES_PATH}/**/*.car`, `${GWC_FIXTURES_PATH}/**/*.ipns-record`, `${GWC_FIXTURES_PATH}/dnslinks.json`]) + if (allFixtures.length > 0) { + log('Fixtures already downloaded') + return + } + + log('Downloading fixtures') + try { + await $`docker run -v ${process.cwd()}:/workspace -w /workspace ghcr.io/ipfs/gateway-conformance:v0.4.2 extract-fixtures --directory ${relative('.', GWC_FIXTURES_PATH)} --merged false` + } catch (e) { + log.error('Error downloading fixtures, assuming current or previous success', e) + } +} + +async function loadFixtures (): Promise { + const execaOptions = getExecaOptions() + + for (const carFile of await glob([`${resolve(__dirname, 'data')}/**/*.car`])) { + log('Loading *.car fixture %s', carFile) + const { stdout } = await $(execaOptions)`npx -y kubo dag import --pin-roots=false --offline ${carFile}` + stdout.split('\n').forEach(log) + } + + for (const ipnsRecord of await glob([`${GWC_FIXTURES_PATH}/**/*.ipns-record`])) { + const key = basename(ipnsRecord, '.ipns-record') + const relativePath = relative(GWC_FIXTURES_PATH, ipnsRecord) + log('Loading *.ipns-record fixture %s', relativePath) + const { stdout } = await $(({ ...execaOptions }))`cd ${GWC_FIXTURES_PATH} && npx -y kubo routing put --allow-offline "/ipns/${key}" "${relativePath}"` + stdout.split('\n').forEach(log) + } + + const json = await readFile(`${GWC_FIXTURES_PATH}/dnslinks.json`, 'utf-8') + const { subdomains, domains } = JSON.parse(json) + const subdomainDnsLinks = Object.entries(subdomains).map(([key, value]) => `${key}.example.com:${value}`).join(',') + const domainDnsLinks = Object.entries(domains).map(([key, value]) => `${key}:${value}`).join(',') + const ipfsNsMap = `${domainDnsLinks},${subdomainDnsLinks}` + + // TODO: provide this to kubo instance in tests + return ipfsNsMap +} diff --git a/test-e2e/fixtures/locators.ts b/test-e2e/fixtures/locators.ts index 542cbc7b..199b6025 100644 --- a/test-e2e/fixtures/locators.ts +++ b/test-e2e/fixtures/locators.ts @@ -1,7 +1,10 @@ -import type { Locator, Page } from '@playwright/test' +import type { FrameLocator, Locator, Page } from '@playwright/test' export interface GetLocator { - (page: Page): Locator + (page: Page | FrameLocator): Locator +} +export interface GetFrameLocator { + (page: Page | FrameLocator): FrameLocator } export const getHeader: GetLocator = (page) => page.locator('.e2e-header') @@ -10,4 +13,13 @@ export const getConfigButton: GetLocator = (page) => page.locator('.e2e-header-c export const getConfigPage: GetLocator = (page) => page.locator('.e2e-config-page') export const getConfigPageInput: GetLocator = (page) => page.locator('.e2e-config-page-input') export const getConfigPageButton: GetLocator = (page) => page.locator('.e2e-config-page-button') +export const getIframeLocator: GetFrameLocator = (page) => page.frameLocator('iframe') +export const getConfigGatewaysInput: GetLocator = (page) => page.locator('.e2e-config-page-input-gateways') +export const getConfigRoutersInput: GetLocator = (page) => page.locator('.e2e-config-page-input-routers') export const getConfigAutoReloadInput: GetLocator = (page) => page.locator('.e2e-config-page-input-autoreload') + +export const getConfigButtonIframe: GetLocator = (page) => getIframeLocator(page).locator('.e2e-collapsible-button') +export const getConfigGatewaysInputIframe: GetLocator = (page) => getConfigGatewaysInput(getIframeLocator(page)) +export const getConfigRoutersInputIframe: GetLocator = (page) => getConfigRoutersInput(getIframeLocator(page)) +export const getConfigAutoReloadInputIframe: GetLocator = (page) => getConfigAutoReloadInput(getIframeLocator(page)) +export const getConfigPageButtonIframe: GetLocator = (page) => getConfigPageButton(getIframeLocator(page)) diff --git a/test-e2e/fixtures/set-sw-config.ts b/test-e2e/fixtures/set-sw-config.ts index a3d03a7c..35ad46a1 100644 --- a/test-e2e/fixtures/set-sw-config.ts +++ b/test-e2e/fixtures/set-sw-config.ts @@ -4,13 +4,54 @@ * * Note that this was only tested and confirmed working for subdomain pages. */ +// import { getConfigAutoReloadInput, getConfigButton, getConfigGatewaysInput, getConfigPage, getConfigPageButton, getConfigRoutersInput } from './locators.js' +import { getConfigAutoReloadInput, getConfigAutoReloadInputIframe, getConfigButton, getConfigButtonIframe, getConfigGatewaysInput, getConfigGatewaysInputIframe, getConfigPage, getConfigPageButton, getConfigPageButtonIframe, getConfigRoutersInput, getConfigRoutersInputIframe } from './locators.js' import { waitForServiceWorker } from './wait-for-service-worker.js' import type { ConfigDb } from '../../src/lib/config-db.js' import type { Page } from '@playwright/test' +export async function setConfigViaUiSubdomain ({ page, config }: { page: Page, config: Partial }): Promise { + await waitForServiceWorker(page) + + await getConfigButtonIframe(page).isVisible() + await getConfigButtonIframe(page).click() + + for (const [key] of Object.entries(config)) { + if (key === 'autoReload') { + await getConfigAutoReloadInputIframe(page).click() + } + } + await getConfigGatewaysInputIframe(page).locator('input').fill(JSON.stringify([process.env.KUBO_GATEWAY])) + await getConfigRoutersInputIframe(page).locator('input').fill(JSON.stringify([process.env.KUBO_GATEWAY])) + + await getConfigPageButtonIframe(page).click() + + await getConfigPage(page).isHidden() +} + +export async function setConfigViaUi ({ page, config }: { page: Page, config: Partial }): Promise { + await waitForServiceWorker(page) + + await getConfigButton(page).isVisible() + await getConfigButton(page).click() + await getConfigPage(page).isVisible() + + for (const [key] of Object.entries(config)) { + if (key === 'autoReload') { + await getConfigAutoReloadInput(page).click() + } + } + + await getConfigGatewaysInput(page).locator('input').fill(JSON.stringify([process.env.KUBO_GATEWAY])) + await getConfigRoutersInput(page).locator('input').fill(JSON.stringify([process.env.KUBO_GATEWAY])) + + await getConfigPageButton(page).click() + + await getConfigPage(page).isHidden() +} + // TODO: ensure that the config can be set on root and loaded properly by subdomains with playwright export async function setConfig ({ page, config }: { page: Page, config: Partial }): Promise { - await waitForServiceWorker(page) // we can't pass through functions we already have defined, so many of these things are copied over from /src/lib/generic-db.ts await page.evaluate(async (configInPage) => { const dbName = 'helia-sw' @@ -58,7 +99,22 @@ export async function setConfig ({ page, config }: { page: Page, config: Partial }) channel.postMessage({ target: 'SW', action: 'RELOAD_CONFIG', source: 'WINDOW' }) await swResponsePromise + }, { + gateways: [process.env.KUBO_GATEWAY], + routers: [process.env.KUBO_GATEWAY], + ...config + }) +} + +export async function setSubdomainConfig ({ page, config }: { page: Page, config: Partial }): Promise { + await waitForServiceWorker(page) + + await page.evaluate(async (configInPage) => { // TODO: we shouldn't need this. We should be able to just post a message to the service worker to reload it's config. window.postMessage({ source: 'helia-sw-config-iframe', target: 'PARENT', action: 'RELOAD_CONFIG', config: configInPage }, { targetOrigin: window.location.origin }) - }, config) + }, { + gateways: [process.env.KUBO_GATEWAY], + routers: [process.env.KUBO_GATEWAY], + ...config + }) } diff --git a/test-e2e/global-setup.js b/test-e2e/global-setup.js deleted file mode 100644 index e8ec84b1..00000000 --- a/test-e2e/global-setup.js +++ /dev/null @@ -1,17 +0,0 @@ -import { chromium } from '@playwright/test' - -/** - * TODO: we may want to set up a kubo daemon for testing fetching of content - * see https://github.com/ipfs/ipfs-webui/blob/main/test/e2e/setup/global-setup.js for inspiration - * - * @param {import('@playwright/test').Config} config - */ -const globalSetup = async (config) => { - const baseURL = 'http://localhost:3000' - const browser = await chromium.launch() - const page = await browser.newPage() - await page.goto(baseURL) - await browser.close() -} - -export default globalSetup diff --git a/test-e2e/global-setup.ts b/test-e2e/global-setup.ts new file mode 100644 index 00000000..f6826ac6 --- /dev/null +++ b/test-e2e/global-setup.ts @@ -0,0 +1,12 @@ +import { type Config } from '@playwright/test' +import { createKuboNode } from './fixtures/create-kubo-node.js' +import { loadKuboFixtures } from './fixtures/load-kubo-fixtures.js' + +export default async function globalSetup (config: Config): Promise { + const controller = await createKuboNode() + await Promise.all([controller.start(), loadKuboFixtures()]) + + process.env.KUBO_PID = `${await controller.pid()}` + const gateway = `http://${controller.api.gatewayHost}:${controller.api.gatewayPort}` + process.env.KUBO_GATEWAY = gateway +} diff --git a/test-e2e/global-teardown.ts b/test-e2e/global-teardown.ts new file mode 100644 index 00000000..2dc5261e --- /dev/null +++ b/test-e2e/global-teardown.ts @@ -0,0 +1,7 @@ +import { type Config } from '@playwright/test' +import { $ } from 'execa' +import { kuboRepoDir } from './fixtures/load-kubo-fixtures.js' + +export default async function globalTeardown (config: Config): Promise { + await $`rm -rf ${kuboRepoDir}` +} diff --git a/test-e2e/layout.test.ts b/test-e2e/layout.test.ts index 5589aad8..a7353d5e 100644 --- a/test-e2e/layout.test.ts +++ b/test-e2e/layout.test.ts @@ -1,3 +1,4 @@ +// import { testPathRouting as test, expect } from './fixtures/config-test-fixtures.js' import { test, expect } from '@playwright/test' import { getConfigButton, getConfigPage, getConfigPageButton, getConfigPageInput, getHeader, getHeaderTitle } from './fixtures/locators.js' diff --git a/test-e2e/path-routing.test.ts b/test-e2e/path-routing.test.ts index 1f97e6b5..096ba9ee 100644 --- a/test-e2e/path-routing.test.ts +++ b/test-e2e/path-routing.test.ts @@ -1,12 +1,7 @@ -import { test, expect } from '@playwright/test' -import { waitForServiceWorker } from './fixtures/wait-for-service-worker.js' +import { testPathRouting as test, expect } from './fixtures/config-test-fixtures.js' test.describe('path-routing', () => { test('can load identity CID via path', async ({ page }) => { - // explicitly loading at 127.0.0.1 so subdomain redirection is not triggered - await page.goto('http://127.0.0.1:3333', { waitUntil: 'networkidle' }) - // wait for service worker to load - await waitForServiceWorker(page) const response = await page.goto('http://127.0.0.1:3333/ipfs/bafkqablimvwgy3y', { waitUntil: 'networkidle' }) expect(response?.status()).toBe(200) diff --git a/test-e2e/subdomain-detection.test.ts b/test-e2e/subdomain-detection.test.ts index 6675e5a9..7badf0f3 100644 --- a/test-e2e/subdomain-detection.test.ts +++ b/test-e2e/subdomain-detection.test.ts @@ -1,13 +1,13 @@ -import { test, expect } from '@playwright/test' -import { setConfig } from './fixtures/set-sw-config.js' +import { test } from '@playwright/test' +import { testSubdomainRouting, expect } from './fixtures/config-test-fixtures.js' +import { setConfig, setSubdomainConfig } from './fixtures/set-sw-config.js' import { waitForServiceWorker } from './fixtures/wait-for-service-worker.js' test.describe('subdomain-detection', () => { - test('path requests are redirected to subdomains', async ({ page, context }) => { - await page.goto('/', { waitUntil: 'networkidle' }) - // wait for service worker to load on main url + test('path requests are redirected to subdomains', async ({ page }) => { + await page.goto('http://localhost:3333', { waitUntil: 'networkidle' }) await waitForServiceWorker(page) - + await setConfig({ page, config: { autoReload: false, gateways: [process.env.KUBO_GATEWAY as string], routers: [process.env.KUBO_GATEWAY as string] } }) const initialResponse = await page.goto('/ipfs/bafkqablimvwgy3y', { waitUntil: 'commit' }) expect(initialResponse?.url()).toBe('http://bafkqablimvwgy3y.ipfs.localhost:3333/') @@ -25,17 +25,19 @@ test.describe('subdomain-detection', () => { await expect(bodyTextLocator).toContainText('hello') }) - test('path requests are redirected to subdomains automatically with autoreload enabled', async ({ page, context }) => { - await page.goto('/', { waitUntil: 'networkidle' }) - await waitForServiceWorker(page) + test('enabling autoreload automatically loads the subdomain', async ({ page }) => { + await page.goto('http://bafkqablimvwgy3y.ipfs.localhost:3333/', { waitUntil: 'networkidle' }) + await setSubdomainConfig({ page, config: { autoReload: true, gateways: [process.env.KUBO_GATEWAY as string], routers: [process.env.KUBO_GATEWAY as string] } }) - const initialResponse = await page.goto('/ipfs/bafkqablimvwgy3y', { waitUntil: 'commit' }) + const bodyTextLocator = page.locator('body') - expect(initialResponse?.url()).toBe('http://bafkqablimvwgy3y.ipfs.localhost:3333/') - expect(initialResponse?.request()?.redirectedFrom()?.url()).toBe('http://localhost:3333/ipfs/bafkqablimvwgy3y') + await expect(bodyTextLocator).toContainText('hello') + }) +}) - await page.waitForURL('http://bafkqablimvwgy3y.ipfs.localhost:3333') - await setConfig({ page, config: { autoReload: true } }) +testSubdomainRouting.describe('subdomain-detection auto fixture', () => { + testSubdomainRouting('loads subdomains easily', async ({ page }) => { + await page.goto('http://bafkqablimvwgy3y.ipfs.localhost:3333/', { waitUntil: 'networkidle' }) const bodyTextLocator = page.locator('body') diff --git a/test-e2e/website-loading.test.ts b/test-e2e/website-loading.test.ts index fd8002d6..e27ac23e 100644 --- a/test-e2e/website-loading.test.ts +++ b/test-e2e/website-loading.test.ts @@ -1,42 +1,16 @@ -import { test, expect } from '@playwright/test' -import { createKuboNode } from './fixtures/create-kubo-node.js' -import { loadFixtureDataCar } from './fixtures/load-fixture-data.js' -import { setConfig } from './fixtures/set-sw-config.js' -import { waitForServiceWorker } from './fixtures/wait-for-service-worker.js' -import type { ConfigDb } from '../src/lib/config-db.js' -import type { Controller } from 'ipfsd-ctl' +import { testPathRouting as test, expect } from './fixtures/config-test-fixtures.js' test.describe('website-loading', () => { - let controller: Controller<'go'> - let config: Partial - - test.beforeAll(async () => { - controller = await createKuboNode() - await controller.start() - await loadFixtureDataCar(controller, 'gateway-conformance-fixtures.car') - config = { - gateways: [`http://${controller.api.gatewayHost}:${controller.api.gatewayPort}`], - routers: [`http://${controller.api.gatewayHost}:${controller.api.gatewayPort}`] - } - }) - - test.beforeEach(async ({ page }) => { - // not testing subdomain redirection here - await page.goto('http://127.0.0.1:3000', { waitUntil: 'networkidle' }) - await setConfig({ page, config }) - await waitForServiceWorker(page) - }) - test('ensure unixfs directory trailing slash is added', async ({ page }) => { - const response = await page.goto('http://127.0.0.1:3000/ipfs/bafybeifq2rzpqnqrsdupncmkmhs3ckxxjhuvdcbvydkgvch3ms24k5lo7q') + const response = await page.goto('http://127.0.0.1:3333/ipfs/bafybeifq2rzpqnqrsdupncmkmhs3ckxxjhuvdcbvydkgvch3ms24k5lo7q') // playwright follows redirects so we won't see the 301 expect(response?.status()).toBe(200) - expect(response?.url()).toBe('http://127.0.0.1:3000/ipfs/bafybeifq2rzpqnqrsdupncmkmhs3ckxxjhuvdcbvydkgvch3ms24k5lo7q/') + expect(response?.url()).toBe('http://127.0.0.1:3333/ipfs/bafybeifq2rzpqnqrsdupncmkmhs3ckxxjhuvdcbvydkgvch3ms24k5lo7q/') }) test('ensure that index.html is returned for the root path', async ({ page }) => { - const response = await page.goto('http://127.0.0.1:3000/ipfs/bafybeifq2rzpqnqrsdupncmkmhs3ckxxjhuvdcbvydkgvch3ms24k5lo7q/') + const response = await page.goto('http://127.0.0.1:3333/ipfs/bafybeifq2rzpqnqrsdupncmkmhs3ckxxjhuvdcbvydkgvch3ms24k5lo7q/') expect(response?.status()).toBe(200) const headers = await response?.allHeaders() From e0cbc6544d2c104c6b18e3e26858c85e35e0d606 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:35:08 -0700 Subject: [PATCH 2/6] chore: leftover comment cleanup --- test-e2e/fixtures/set-sw-config.ts | 1 - test-e2e/layout.test.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/test-e2e/fixtures/set-sw-config.ts b/test-e2e/fixtures/set-sw-config.ts index 35ad46a1..d1a447e1 100644 --- a/test-e2e/fixtures/set-sw-config.ts +++ b/test-e2e/fixtures/set-sw-config.ts @@ -4,7 +4,6 @@ * * Note that this was only tested and confirmed working for subdomain pages. */ -// import { getConfigAutoReloadInput, getConfigButton, getConfigGatewaysInput, getConfigPage, getConfigPageButton, getConfigRoutersInput } from './locators.js' import { getConfigAutoReloadInput, getConfigAutoReloadInputIframe, getConfigButton, getConfigButtonIframe, getConfigGatewaysInput, getConfigGatewaysInputIframe, getConfigPage, getConfigPageButton, getConfigPageButtonIframe, getConfigRoutersInput, getConfigRoutersInputIframe } from './locators.js' import { waitForServiceWorker } from './wait-for-service-worker.js' import type { ConfigDb } from '../../src/lib/config-db.js' diff --git a/test-e2e/layout.test.ts b/test-e2e/layout.test.ts index a7353d5e..5589aad8 100644 --- a/test-e2e/layout.test.ts +++ b/test-e2e/layout.test.ts @@ -1,4 +1,3 @@ -// import { testPathRouting as test, expect } from './fixtures/config-test-fixtures.js' import { test, expect } from '@playwright/test' import { getConfigButton, getConfigPage, getConfigPageButton, getConfigPageInput, getHeader, getHeaderTitle } from './fixtures/locators.js' From af939d40373e0c0b90a90da478ada1d47249a3d0 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:36:02 -0700 Subject: [PATCH 3/6] chore: remove unused dev-dep --- package-lock.json | 1 - package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5ff3a07c..79adef18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,7 +37,6 @@ "html-webpack-plugin": "^5.6.0", "http-proxy": "^1.18.1", "ipfsd-ctl": "^13.0.0", - "it-drain": "^3.0.5", "kubo": "^0.27.0", "kubo-rpc-client": "^3.0.4", "mini-css-extract-plugin": "^2.8.1", diff --git a/package.json b/package.json index 7b107cf6..913a7d8a 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,6 @@ "html-webpack-plugin": "^5.6.0", "http-proxy": "^1.18.1", "ipfsd-ctl": "^13.0.0", - "it-drain": "^3.0.5", "kubo": "^0.27.0", "kubo-rpc-client": "^3.0.4", "mini-css-extract-plugin": "^2.8.1", From c138b90f72e4528a2cafa0db5434b7b6d7b3c633 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:37:15 -0700 Subject: [PATCH 4/6] chore: add gateway-conformance fixtures --- ...SZpdHs7oHbXub2G5WC8Tx4NQhyd2d_9.ipns-record | Bin 0 -> 394 bytes ...tnGufN7MEtPRCNHkKpNuA4onsRa3_10.ipns-record | Bin 0 -> 1082 bytes .../gateway-conformance-fixtures/dnslinks.json | 11 +++++++++++ .../gateway-conformance-fixtures/fixtures.car | Bin 0 -> 163532 bytes ...ckoqzwqeqwudfr74kfd11zcyk3b7l_7.ipns-record | Bin 0 -> 394 bytes ...2z4pwgp15pgv3ho1azvidttzh8yy2_8.ipns-record | Bin 0 -> 398 bytes ...m8c_v1-v2-broken-signature-v2_1.ipns-record | Bin 0 -> 334 bytes ...h4y_v1-v2-broken-signature-v1_2.ipns-record | Bin 0 -> 334 bytes ...kd10m97m36bjt66my99hb6103f_v2_3.ipns-record | Bin 0 -> 188 bytes ...auqdc6a7c3y7d5i9fi8mk9w_v1-v2_4.ipns-record | Bin 0 -> 326 bytes ...zae9kpw_v1-v2-broken-v1-value_5.ipns-record | Bin 0 -> 377 bytes ...32iwm9pdt9nq3y5rpn3ln9j12zfhe_0.ipns-record | Bin 0 -> 394 bytes ...jimndrkydqm81cqtlb5ak6p7ku_v1_6.ipns-record | Bin 0 -> 144 bytes 13 files changed, 11 insertions(+) create mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d_9.ipns-record create mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3_10.ipns-record create mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/dnslinks.json create mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/fixtures.car create mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dghjous0agrwavl8vzl64xckoqzwqeqwudfr74kfd11zcyk3b7l_7.ipns-record create mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dhjghbwdvbo6mi40htrq6e2z4pwgp15pgv3ho1azvidttzh8yy2_8.ipns-record create mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5diamp7qnnvs1p1gzmku3eijkeijs3418j23j077zrkok63xdm8c_v1-v2-broken-signature-v2_1.ipns-record create mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dilgf7gorsh9vcqqq4myo6jd4zmqkuy9pxyxi5fua3uf7axph4y_v1-v2-broken-signature-v1_2.ipns-record create mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dit2ku9mutlfgwyz8u730on38kd10m97m36bjt66my99hb6103f_v2_3.ipns-record create mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dlkw8pxuw9qmqayfdeh4kfebhmreauqdc6a7c3y7d5i9fi8mk9w_v1-v2_4.ipns-record create mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dlmit2tuwdvnx4sbnyqgmvbxftl0eo3f33wwtb9gr7yozae9kpw_v1-v2-broken-v1-value_5.ipns-record create mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dlxdsdu5fpuu7h69wu4ohp32iwm9pdt9nq3y5rpn3ln9j12zfhe_0.ipns-record create mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dm4tm0wt8srkg9h9suud4wuiwjimndrkydqm81cqtlb5ak6p7ku_v1_6.ipns-record diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d_9.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d_9.ipns-record new file mode 100644 index 0000000000000000000000000000000000000000..39b2f41a40866132588557e1be7beb97f50b79d4 GIT binary patch literal 394 zcmd;b)XywPE7ng+Ov^4x%}lN=PB${n&MYr8Hc3r4N--}iPt7c-D9<^J}Yul1#e?Les=qsP9*TpHppd@W%Xk@HwV4`bi6k=#> zWnyAwYN=;oU}$b+5v9Rk(9qDcxZ%Nrr`k>qKjaU!b6-4rYi;`fm-}a{vCo?xRu}4g z^l|FihwZAz%U!lw{rY|+(EGO7p=OmC>knNP>?`)U^*Whrs^fvF0qEJ$Sl0Mn+K ATmS$7 literal 0 HcmV?d00001 diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3_10.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3_10.ipns-record new file mode 100644 index 0000000000000000000000000000000000000000..b37d9b75b50ce3baa472e0c790c63cc4476c8631 GIT binary patch literal 1082 zcmd;b)XywPE7ng+Ov^4x%}lN=PB${n&MYr8Hc3r4N--}iPt7c-D92(*IMXR5Go)>GsSynodq(GRGFw`JL;^nAdmmZe7@< z?;9u0)~oe;B6n8l#B-O4jHLznwHFfawsGeD^WdI5dD6CW_D`?(IR6u4yS}n>0Xs|C zV|Deu10TJ;+15H9y#1%w)-Pu|{~C5NEh+2ohgE%4GcFi^=!o1|c=mvclzM~Aq?sH1 zV;9|>pJ(C4b;k1K_pY`7N=jcyC2D2fRy9#Bx;b;nX--YmkL>f5|4X=(dm6Aa@2=jn zPF^r?@jIi2gJJgHoU|F^9$YA16dbkmCI8(bi=coUk?re*CGPIOm;0ZgQ?hD>F0X=C zL#OarR>@Lloz*!)FR?K>|cBO@yVa}y&! z15licsfm%1;pVJGT^wtTb=KA$^<13Nv3!=^X2&xt9)0ASbyDGP?WdlXx7q)`d@Z+h z!6|`%_KMwXk1o8Mo?{-MVA*x}^cm9=t?8#{Bqsazy{TE_Y}T4V{h&^m3)r^)bGv-5U2WMvuSX z7vtkGPzgn;O5JiMe~&<38&ZhUC?flR8gS@+~<#_0!o~ z@AZ8}8?WuOn%~@gsOVv8C3nV1oCd(eK6PTncvj@)S}Wr`}S-X)$F5@;ne3PMT?z z=NIb*B<2n+zLRAc6lAY~eeG%iDJwNZBx`P>1GC& uWCn)RutZ?qi=aH?W(21e0yA-PDnka)$jp??lFA5t$v6k5Dx|U?l>q?Oq1V#@ literal 0 HcmV?d00001 diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/dnslinks.json b/test-e2e/fixtures/data/gateway-conformance-fixtures/dnslinks.json new file mode 100644 index 00000000..5740db46 --- /dev/null +++ b/test-e2e/fixtures/data/gateway-conformance-fixtures/dnslinks.json @@ -0,0 +1,11 @@ +{ + "domains": { + "dnslink-subdomain-gw-test.example.org": "/ipfs/bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze", + "dnslink-test.example.com": "/ipfs/bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am" + }, + "subdomains": { + "dnslink-enabled-on-fqdn": "/ipfs/QmYBhLYDwVFvxos9h8CGU2ibaY66QNgv8hpfewxaQrPiZj", + "dnslink-enabled-with-spa": "/ipfs/bafybeib5lboymwd6p2eo4qb2lkueaine577flvsjjeuevmp2nlio72xv5q", + "website": "/ipfs/bafybeig6ka5mlwkl4subqhaiatalkcleo4jgnr3hqwvpmsqfca27cijp3i" + } +} \ No newline at end of file diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/fixtures.car b/test-e2e/fixtures/data/gateway-conformance-fixtures/fixtures.car new file mode 100644 index 0000000000000000000000000000000000000000..24191adf603abd0999561972e122c9096655950d GIT binary patch literal 163532 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 K|@%Ra3F&_Y7|A1$z~Nfc*R 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 AM*si- literal 0 HcmV?d00001 diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dghjous0agrwavl8vzl64xckoqzwqeqwudfr74kfd11zcyk3b7l_7.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dghjous0agrwavl8vzl64xckoqzwqeqwudfr74kfd11zcyk3b7l_7.ipns-record new file mode 100644 index 0000000000000000000000000000000000000000..7186c709e3ceb29fbfda0fd38b4e43109f26ce55 GIT binary patch literal 394 zcmd;b)XywPE7ng+OsgzP%}gpbDJ>~TPs%edElf?%O)*N(FRU_2%C1byDM~d;%{Iy` zFEckWPRl5(%qh<_sY*@F%oTE|Wo^}bx%d5*Sx4q7wQu^*{jh&wK$gukjlQ6!w<^zC z3zgkZ3FJ7HbjEJnE2pg~YAb1T`}pU#Pwl?`TRqWK?I))MgOaq7p^>q!fr+l6afpG1 zm4T&|siB^QrJ=E@n00{M& AZ2$lO literal 0 HcmV?d00001 diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dhjghbwdvbo6mi40htrq6e2z4pwgp15pgv3ho1azvidttzh8yy2_8.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dhjghbwdvbo6mi40htrq6e2z4pwgp15pgv3ho1azvidttzh8yy2_8.ipns-record new file mode 100644 index 0000000000000000000000000000000000000000..28676f4d9a8824989d039a03376aeea9ae63d0d3 GIT binary patch literal 398 zcmd;b*3T?RE7ng+OfM}=O)W~yH7(7`Dl96`NUcmPsWdJ(O;0J#&95>m%Pc8L&M3>s zHa9W`DyYad&q&O!$WAdauP799P;T3(=A&^x$Y#D|lG@q>LT5|;wEh}r>YUo65W?xX z)~)KP*_E#3s?1Ys{Wt72pZosTYWup`{b6o#URK<3lJD3g7?h-q42_I+4NPq4G7B@V2@KoE$;q~X{`u_XeTsd(Ml(;WP%n0C_P+X~( z`|*~;{f_SyrzS~i|89=(;gu+H(2(HipQ0Kl{`~X07?TnkT`#}eZV&jprZX;04hiv* z);`_Lz>>_skQ$blQ<@qG|ef_EiKH*%`Fsi=za8K-t7|2`{7T-&*Z4SoSC*KQnW9i!Qks_LHHmR)a!81eGy?0GCjE(f|Me literal 0 HcmV?d00001 diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dilgf7gorsh9vcqqq4myo6jd4zmqkuy9pxyxi5fua3uf7axph4y_v1-v2-broken-signature-v1_2.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dilgf7gorsh9vcqqq4myo6jd4zmqkuy9pxyxi5fua3uf7axph4y_v1-v2-broken-signature-v1_2.ipns-record new file mode 100644 index 0000000000000000000000000000000000000000..cb914e98935285d36324920ed73cf82a2f3bf39a GIT binary patch literal 334 zcmdG|ef_EiKH*&CL|z&CDxH%*jkqC@v{YOOs$wk~T6lGS)S)&^0s(F*LF=G`BJ^ z)-$s-F)%fX(qJ%XSn%=CMiVCoqoaLU>++`c72KF_xNyNnKZ%D%w)3t@otPav#XYft zuY5~)Hs@-)ja$BnBxHvMS4S*elkTixbTwsWZ%Zw^%vN@)WXu#yyR4d44`?LDVZge5%_$R15*`JS&+&A01K#iGXMYp literal 0 HcmV?d00001 diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dit2ku9mutlfgwyz8u730on38kd10m97m36bjt66my99hb6103f_v2_3.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dit2ku9mutlfgwyz8u730on38kd10m97m36bjt66my99hb6103f_v2_3.ipns-record new file mode 100644 index 0000000000000000000000000000000000000000..ddce7382d79027426e6c02ea1bdabfd5078e8468 GIT binary patch literal 188 zcmZ>A$SRu=5vMKA&Us}@ok+LDMuqFSTTDgYuK6_K{ori3Ku+HTpM&2J0OO znRQR?%c*A5FGu8b{{P#!i)$&PSH;rgkPshf1_s8(5-|Y`sbPsZrKu4r`k4i3#rjE! zY1xH|DJA9UndRkX#wEE0sf8J-RXHhnrd8>g8NsQArKx$zsSFuFU70DFC6y7Zq9KFs-}ipe6I< z-fhKtq3<)8&OJ-L=6a9qNXV-9=h;F^xlj1bc2wmyeR4&1spZk7PW6ZRIPOeYviDR2 z^AA1=1|?}DLnC8d0}EY4lMq8AD?@WD17kfiOA`ZAqbLmqgN6kk4{bDYa_|b=k>cFx z{ra+^8`B#dGj%?n3jtkexxY-q5{~@pUNm7{?GxX!)j>y!=UdhlI!^b?a$I29Ae3d{Udom+9wXifbFFBPV1E?i4C9|Y5 P0-u|6V5&kY3sM;X9A9~{ literal 0 HcmV?d00001 diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dlmit2tuwdvnx4sbnyqgmvbxftl0eo3f33wwtb9gr7yozae9kpw_v1-v2-broken-v1-value_5.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dlmit2tuwdvnx4sbnyqgmvbxftl0eo3f33wwtb9gr7yozae9kpw_v1-v2-broken-v1-value_5.ipns-record new file mode 100644 index 0000000000000000000000000000000000000000..9b4953e9440c39508eb798c0d477b730c1918a43 GIT binary patch literal 377 zcmd-w)6XnOE7ng+Ov^4zOwTFJEvu@?G|I`(H7QI_t;|VHO)9NOG%hJgO{z-It~4&l z&nnJHPc9U4@XO7%eJ|>F$M=6C4|BsZXQK#ni^GwZ>Nf0H(eiTgZH~Fi*z*nfJEy7? z$gYT7?E7T(r>SM4vxS^)eHU?7tW4vRU{I1aGBh&QHL%b%Gzl>@vNANcGBDONvotX< zHHy+;Flbot@z6#SCkL;2r?=LpIlTMd=vlLIZNQEB?$4i!F@+`X`^J)>-?r`LejBYH zSFF5trLK{ZR=dV~=tp2OYn=NhwrgQ*)wR;wS-d7OE=>*z@sVa=U|cK_6TpxfmY7qT z8exa*^^B78^whG7+}xr>pcl*2jg5Iw0W>c&C9|Y50-uj^V5&kY3sM;X1?i5@ literal 0 HcmV?d00001 diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dlxdsdu5fpuu7h69wu4ohp32iwm9pdt9nq3y5rpn3ln9j12zfhe_0.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dlxdsdu5fpuu7h69wu4ohp32iwm9pdt9nq3y5rpn3ln9j12zfhe_0.ipns-record new file mode 100644 index 0000000000000000000000000000000000000000..9cd7491bf7e71ec8f11e908b51aa908702a4a353 GIT binary patch literal 394 zcmd;b)XywPE7ng+Osh;v%}g>*OUp4TNzE)w$xALX&dsheDKaunGEFWiD$lMeD#}PM zNlD5}HOoxOPpqsct;jYl%@lIj#Aa`PX!4XzpKmQY)6e$9U7<`kzNU$F%{D)#mwVMZ z{GZ-h%zgj3z3+a9thHt*l@~txHDQw3!_thyQ+_E$H*2s|5P0dSAWyk;;nVFJVQW=5IUpX*UA(aKG3;+eR BnWX># literal 0 HcmV?d00001 diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dm4tm0wt8srkg9h9suud4wuiwjimndrkydqm81cqtlb5ak6p7ku_v1_6.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dm4tm0wt8srkg9h9suud4wuiwjimndrkydqm81cqtlb5ak6p7ku_v1_6.ipns-record new file mode 100644 index 0000000000000000000000000000000000000000..c6e2a0f5c5a7482bb42367715f262e5f85044b37 GIT binary patch literal 144 zcmV;B0B`>aBrj=jW^*rMVPaAk35WqNF6ZZ&#mX%aw3oqH(c zT>wh5G^qr9f#GHkR1G}+apD(BEi>AoF4(LlER1|>po#OObsy8PYkTL*I#d`GdbIAB yYOV&%>sSXE03sVQF)}kPFgPtSG*mG%Ix#moFf%STIW#aeGFm7AFo2-(!m%{xT{cJn literal 0 HcmV?d00001 From ab11acf77b07fea00f68f024ad36b9b6c840ac09 Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Tue, 26 Mar 2024 14:06:52 -0700 Subject: [PATCH 5/6] chore: attempt to resolve loading fixtures in CI --- test-e2e/fixtures/load-kubo-fixtures.ts | 33 ++++++++++++++----------- test-e2e/global-setup.ts | 3 ++- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/test-e2e/fixtures/load-kubo-fixtures.ts b/test-e2e/fixtures/load-kubo-fixtures.ts index d79d54ee..7354e0b6 100644 --- a/test-e2e/fixtures/load-kubo-fixtures.ts +++ b/test-e2e/fixtures/load-kubo-fixtures.ts @@ -9,10 +9,10 @@ */ import { readFile } from 'node:fs/promises' -import { dirname, basename, relative, resolve } from 'node:path' +import { dirname, relative, resolve } from 'node:path' import { fileURLToPath } from 'node:url' import { logger } from '@libp2p/logger' -import { $, type Options } from 'execa' +import { $ } from 'execa' import { glob } from 'glob' // eslint-disable-next-line @typescript-eslint/naming-convention @@ -37,7 +37,7 @@ export async function loadKuboFixtures (): Promise { await loadFixtures() } -function getExecaOptions ({ cwd, ipfsNsMap }: { cwd?: string, ipfsNsMap?: string } = {}): Options { +function getExecaOptions ({ cwd, ipfsNsMap }: { cwd?: string, ipfsNsMap?: string } = {}): { cwd: string, env: Record } { return { cwd: cwd ?? __dirname, env: { @@ -80,12 +80,14 @@ async function configureKubo (): Promise { } } -async function downloadFixtures (): Promise { +async function downloadFixtures (force = false): Promise { + if (!force) { // if the fixtures are already downloaded, we don't need to download them again - const allFixtures = await glob([`${GWC_FIXTURES_PATH}/**/*.car`, `${GWC_FIXTURES_PATH}/**/*.ipns-record`, `${GWC_FIXTURES_PATH}/dnslinks.json`]) - if (allFixtures.length > 0) { - log('Fixtures already downloaded') - return + const allFixtures = await glob([`${GWC_FIXTURES_PATH}/**/*.car`, `${GWC_FIXTURES_PATH}/**/*.ipns-record`, `${GWC_FIXTURES_PATH}/dnslinks.json`]) + if (allFixtures.length > 0) { + log('Fixtures already downloaded') + return + } } log('Downloading fixtures') @@ -105,13 +107,14 @@ async function loadFixtures (): Promise { stdout.split('\n').forEach(log) } - for (const ipnsRecord of await glob([`${GWC_FIXTURES_PATH}/**/*.ipns-record`])) { - const key = basename(ipnsRecord, '.ipns-record') - const relativePath = relative(GWC_FIXTURES_PATH, ipnsRecord) - log('Loading *.ipns-record fixture %s', relativePath) - const { stdout } = await $(({ ...execaOptions }))`cd ${GWC_FIXTURES_PATH} && npx -y kubo routing put --allow-offline "/ipns/${key}" "${relativePath}"` - stdout.split('\n').forEach(log) - } + // TODO: re-enable this when we resolve CI issue: see https://github.com/ipfs-shipyard/service-worker-gateway/actions/runs/8442583023/job/23124336180?pr=159#step:6:13 + // for (const ipnsRecord of await glob([`${GWC_FIXTURES_PATH}/**/*.ipns-record`])) { + // const key = basename(ipnsRecord, '.ipns-record') + // const relativePath = relative(GWC_FIXTURES_PATH, ipnsRecord) + // log('Loading *.ipns-record fixture %s', relativePath) + // const { stdout } = await $(({ ...execaOptions }))`cd ${GWC_FIXTURES_PATH} && npx -y kubo routing put --allow-offline "/ipns/${key}" "${relativePath}"` + // stdout.split('\n').forEach(log) + // } const json = await readFile(`${GWC_FIXTURES_PATH}/dnslinks.json`, 'utf-8') const { subdomains, domains } = JSON.parse(json) diff --git a/test-e2e/global-setup.ts b/test-e2e/global-setup.ts index f6826ac6..774f34f1 100644 --- a/test-e2e/global-setup.ts +++ b/test-e2e/global-setup.ts @@ -3,8 +3,9 @@ import { createKuboNode } from './fixtures/create-kubo-node.js' import { loadKuboFixtures } from './fixtures/load-kubo-fixtures.js' export default async function globalSetup (config: Config): Promise { + await loadKuboFixtures() const controller = await createKuboNode() - await Promise.all([controller.start(), loadKuboFixtures()]) + await controller.start() process.env.KUBO_PID = `${await controller.pid()}` const gateway = `http://${controller.api.gatewayHost}:${controller.api.gatewayPort}` From bbf0e3f3825ef32a96baed39836ee8ac7103828c Mon Sep 17 00:00:00 2001 From: Russell Dempsey <1173416+SgtPooki@users.noreply.github.com> Date: Tue, 26 Mar 2024 14:15:08 -0700 Subject: [PATCH 6/6] chore: test removing fixtures --- ...SZpdHs7oHbXub2G5WC8Tx4NQhyd2d_9.ipns-record | Bin 394 -> 0 bytes ...tnGufN7MEtPRCNHkKpNuA4onsRa3_10.ipns-record | Bin 1082 -> 0 bytes .../gateway-conformance-fixtures/dnslinks.json | 11 ----------- .../gateway-conformance-fixtures/fixtures.car | Bin 163532 -> 0 bytes ...ckoqzwqeqwudfr74kfd11zcyk3b7l_7.ipns-record | Bin 394 -> 0 bytes ...2z4pwgp15pgv3ho1azvidttzh8yy2_8.ipns-record | Bin 398 -> 0 bytes ...m8c_v1-v2-broken-signature-v2_1.ipns-record | Bin 334 -> 0 bytes ...h4y_v1-v2-broken-signature-v1_2.ipns-record | Bin 334 -> 0 bytes ...kd10m97m36bjt66my99hb6103f_v2_3.ipns-record | Bin 188 -> 0 bytes ...auqdc6a7c3y7d5i9fi8mk9w_v1-v2_4.ipns-record | Bin 326 -> 0 bytes ...zae9kpw_v1-v2-broken-v1-value_5.ipns-record | Bin 377 -> 0 bytes ...32iwm9pdt9nq3y5rpn3ln9j12zfhe_0.ipns-record | Bin 394 -> 0 bytes ...jimndrkydqm81cqtlb5ak6p7ku_v1_6.ipns-record | Bin 144 -> 0 bytes 13 files changed, 11 deletions(-) delete mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d_9.ipns-record delete mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3_10.ipns-record delete mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/dnslinks.json delete mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/fixtures.car delete mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dghjous0agrwavl8vzl64xckoqzwqeqwudfr74kfd11zcyk3b7l_7.ipns-record delete mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dhjghbwdvbo6mi40htrq6e2z4pwgp15pgv3ho1azvidttzh8yy2_8.ipns-record delete mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5diamp7qnnvs1p1gzmku3eijkeijs3418j23j077zrkok63xdm8c_v1-v2-broken-signature-v2_1.ipns-record delete mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dilgf7gorsh9vcqqq4myo6jd4zmqkuy9pxyxi5fua3uf7axph4y_v1-v2-broken-signature-v1_2.ipns-record delete mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dit2ku9mutlfgwyz8u730on38kd10m97m36bjt66my99hb6103f_v2_3.ipns-record delete mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dlkw8pxuw9qmqayfdeh4kfebhmreauqdc6a7c3y7d5i9fi8mk9w_v1-v2_4.ipns-record delete mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dlmit2tuwdvnx4sbnyqgmvbxftl0eo3f33wwtb9gr7yozae9kpw_v1-v2-broken-v1-value_5.ipns-record delete mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dlxdsdu5fpuu7h69wu4ohp32iwm9pdt9nq3y5rpn3ln9j12zfhe_0.ipns-record delete mode 100644 test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dm4tm0wt8srkg9h9suud4wuiwjimndrkydqm81cqtlb5ak6p7ku_v1_6.ipns-record diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d_9.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d_9.ipns-record deleted file mode 100644 index 39b2f41a40866132588557e1be7beb97f50b79d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 394 zcmd;b)XywPE7ng+Ov^4x%}lN=PB${n&MYr8Hc3r4N--}iPt7c-D9<^J}Yul1#e?Les=qsP9*TpHppd@W%Xk@HwV4`bi6k=#> zWnyAwYN=;oU}$b+5v9Rk(9qDcxZ%Nrr`k>qKjaU!b6-4rYi;`fm-}a{vCo?xRu}4g z^l|FihwZAz%U!lw{rY|+(EGO7p=OmC>knNP>?`)U^*Whrs^fvF0qEJ$Sl0Mn+K ATmS$7 diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3_10.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3_10.ipns-record deleted file mode 100644 index b37d9b75b50ce3baa472e0c790c63cc4476c8631..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1082 zcmd;b)XywPE7ng+Ov^4x%}lN=PB${n&MYr8Hc3r4N--}iPt7c-D92(*IMXR5Go)>GsSynodq(GRGFw`JL;^nAdmmZe7@< z?;9u0)~oe;B6n8l#B-O4jHLznwHFfawsGeD^WdI5dD6CW_D`?(IR6u4yS}n>0Xs|C zV|Deu10TJ;+15H9y#1%w)-Pu|{~C5NEh+2ohgE%4GcFi^=!o1|c=mvclzM~Aq?sH1 zV;9|>pJ(C4b;k1K_pY`7N=jcyC2D2fRy9#Bx;b;nX--YmkL>f5|4X=(dm6Aa@2=jn zPF^r?@jIi2gJJgHoU|F^9$YA16dbkmCI8(bi=coUk?re*CGPIOm;0ZgQ?hD>F0X=C zL#OarR>@Lloz*!)FR?K>|cBO@yVa}y&! z15licsfm%1;pVJGT^wtTb=KA$^<13Nv3!=^X2&xt9)0ASbyDGP?WdlXx7q)`d@Z+h z!6|`%_KMwXk1o8Mo?{-MVA*x}^cm9=t?8#{Bqsazy{TE_Y}T4V{h&^m3)r^)bGv-5U2WMvuSX z7vtkGPzgn;O5JiMe~&<38&ZhUC?flR8gS@+~<#_0!o~ z@AZ8}8?WuOn%~@gsOVv8C3nV1oCd(eK6PTncvj@)S}Wr`}S-X)$F5@;ne3PMT?z z=NIb*B<2n+zLRAc6lAY~eeG%iDJwNZBx`P>1GC& uWCn)RutZ?qi=aH?W(21e0yA-PDnka)$jp??lFA5t$v6k5Dx|U?l>q?Oq1V#@ diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/dnslinks.json b/test-e2e/fixtures/data/gateway-conformance-fixtures/dnslinks.json deleted file mode 100644 index 5740db46..00000000 --- a/test-e2e/fixtures/data/gateway-conformance-fixtures/dnslinks.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "domains": { - "dnslink-subdomain-gw-test.example.org": "/ipfs/bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze", - "dnslink-test.example.com": "/ipfs/bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am" - }, - "subdomains": { - "dnslink-enabled-on-fqdn": "/ipfs/QmYBhLYDwVFvxos9h8CGU2ibaY66QNgv8hpfewxaQrPiZj", - "dnslink-enabled-with-spa": "/ipfs/bafybeib5lboymwd6p2eo4qb2lkueaine577flvsjjeuevmp2nlio72xv5q", - "website": "/ipfs/bafybeig6ka5mlwkl4subqhaiatalkcleo4jgnr3hqwvpmsqfca27cijp3i" - } -} \ No newline at end of file diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/fixtures.car b/test-e2e/fixtures/data/gateway-conformance-fixtures/fixtures.car deleted file mode 100644 index 24191adf603abd0999561972e122c9096655950d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 163532 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 K|@%Ra3F&_Y7|A1$z~Nfc*R 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 AM*si- diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dghjous0agrwavl8vzl64xckoqzwqeqwudfr74kfd11zcyk3b7l_7.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dghjous0agrwavl8vzl64xckoqzwqeqwudfr74kfd11zcyk3b7l_7.ipns-record deleted file mode 100644 index 7186c709e3ceb29fbfda0fd38b4e43109f26ce55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 394 zcmd;b)XywPE7ng+OsgzP%}gpbDJ>~TPs%edElf?%O)*N(FRU_2%C1byDM~d;%{Iy` zFEckWPRl5(%qh<_sY*@F%oTE|Wo^}bx%d5*Sx4q7wQu^*{jh&wK$gukjlQ6!w<^zC z3zgkZ3FJ7HbjEJnE2pg~YAb1T`}pU#Pwl?`TRqWK?I))MgOaq7p^>q!fr+l6afpG1 zm4T&|siB^QrJ=E@n00{M& AZ2$lO diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dhjghbwdvbo6mi40htrq6e2z4pwgp15pgv3ho1azvidttzh8yy2_8.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dhjghbwdvbo6mi40htrq6e2z4pwgp15pgv3ho1azvidttzh8yy2_8.ipns-record deleted file mode 100644 index 28676f4d9a8824989d039a03376aeea9ae63d0d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 398 zcmd;b*3T?RE7ng+OfM}=O)W~yH7(7`Dl96`NUcmPsWdJ(O;0J#&95>m%Pc8L&M3>s zHa9W`DyYad&q&O!$WAdauP799P;T3(=A&^x$Y#D|lG@q>LT5|;wEh}r>YUo65W?xX z)~)KP*_E#3s?1Ys{Wt72pZosTYWup`{b6o#URK<3lJD3g7?h-q42_I+4NPq4G7B@V2@KoE$;q~X{`u_XeTsd(Ml(;WP%n0C_P+X~( z`|*~;{f_SyrzS~i|89=(;gu+H(2(HipQ0Kl{`~X07?TnkT`#}eZV&jprZX;04hiv* z);`_Lz>>_skQ$blQ<@qG|ef_EiKH*%`Fsi=za8K-t7|2`{7T-&*Z4SoSC*KQnW9i!Qks_LHHmR)a!81eGy?0GCjE(f|Me diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dilgf7gorsh9vcqqq4myo6jd4zmqkuy9pxyxi5fua3uf7axph4y_v1-v2-broken-signature-v1_2.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dilgf7gorsh9vcqqq4myo6jd4zmqkuy9pxyxi5fua3uf7axph4y_v1-v2-broken-signature-v1_2.ipns-record deleted file mode 100644 index cb914e98935285d36324920ed73cf82a2f3bf39a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 334 zcmdG|ef_EiKH*&CL|z&CDxH%*jkqC@v{YOOs$wk~T6lGS)S)&^0s(F*LF=G`BJ^ z)-$s-F)%fX(qJ%XSn%=CMiVCoqoaLU>++`c72KF_xNyNnKZ%D%w)3t@otPav#XYft zuY5~)Hs@-)ja$BnBxHvMS4S*elkTixbTwsWZ%Zw^%vN@)WXu#yyR4d44`?LDVZge5%_$R15*`JS&+&A01K#iGXMYp diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dit2ku9mutlfgwyz8u730on38kd10m97m36bjt66my99hb6103f_v2_3.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dit2ku9mutlfgwyz8u730on38kd10m97m36bjt66my99hb6103f_v2_3.ipns-record deleted file mode 100644 index ddce7382d79027426e6c02ea1bdabfd5078e8468..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 188 zcmZ>A$SRu=5vMKA&Us}@ok+LDMuqFSTTDgYuK6_K{ori3Ku+HTpM&2J0OO znRQR?%c*A5FGu8b{{P#!i)$&PSH;rgkPshf1_s8(5-|Y`sbPsZrKu4r`k4i3#rjE! zY1xH|DJA9UndRkX#wEE0sf8J-RXHhnrd8>g8NsQArKx$zsSFuFU70DFC6y7Zq9KFs-}ipe6I< z-fhKtq3<)8&OJ-L=6a9qNXV-9=h;F^xlj1bc2wmyeR4&1spZk7PW6ZRIPOeYviDR2 z^AA1=1|?}DLnC8d0}EY4lMq8AD?@WD17kfiOA`ZAqbLmqgN6kk4{bDYa_|b=k>cFx z{ra+^8`B#dGj%?n3jtkexxY-q5{~@pUNm7{?GxX!)j>y!=UdhlI!^b?a$I29Ae3d{Udom+9wXifbFFBPV1E?i4C9|Y5 P0-u|6V5&kY3sM;X9A9~{ diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dlmit2tuwdvnx4sbnyqgmvbxftl0eo3f33wwtb9gr7yozae9kpw_v1-v2-broken-v1-value_5.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dlmit2tuwdvnx4sbnyqgmvbxftl0eo3f33wwtb9gr7yozae9kpw_v1-v2-broken-v1-value_5.ipns-record deleted file mode 100644 index 9b4953e9440c39508eb798c0d477b730c1918a43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 377 zcmd-w)6XnOE7ng+Ov^4zOwTFJEvu@?G|I`(H7QI_t;|VHO)9NOG%hJgO{z-It~4&l z&nnJHPc9U4@XO7%eJ|>F$M=6C4|BsZXQK#ni^GwZ>Nf0H(eiTgZH~Fi*z*nfJEy7? z$gYT7?E7T(r>SM4vxS^)eHU?7tW4vRU{I1aGBh&QHL%b%Gzl>@vNANcGBDONvotX< zHHy+;Flbot@z6#SCkL;2r?=LpIlTMd=vlLIZNQEB?$4i!F@+`X`^J)>-?r`LejBYH zSFF5trLK{ZR=dV~=tp2OYn=NhwrgQ*)wR;wS-d7OE=>*z@sVa=U|cK_6TpxfmY7qT z8exa*^^B78^whG7+}xr>pcl*2jg5Iw0W>c&C9|Y50-uj^V5&kY3sM;X1?i5@ diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dlxdsdu5fpuu7h69wu4ohp32iwm9pdt9nq3y5rpn3ln9j12zfhe_0.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dlxdsdu5fpuu7h69wu4ohp32iwm9pdt9nq3y5rpn3ln9j12zfhe_0.ipns-record deleted file mode 100644 index 9cd7491bf7e71ec8f11e908b51aa908702a4a353..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 394 zcmd;b)XywPE7ng+Osh;v%}g>*OUp4TNzE)w$xALX&dsheDKaunGEFWiD$lMeD#}PM zNlD5}HOoxOPpqsct;jYl%@lIj#Aa`PX!4XzpKmQY)6e$9U7<`kzNU$F%{D)#mwVMZ z{GZ-h%zgj3z3+a9thHt*l@~txHDQw3!_thyQ+_E$H*2s|5P0dSAWyk;;nVFJVQW=5IUpX*UA(aKG3;+eR BnWX># diff --git a/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dm4tm0wt8srkg9h9suud4wuiwjimndrkydqm81cqtlb5ak6p7ku_v1_6.ipns-record b/test-e2e/fixtures/data/gateway-conformance-fixtures/k51qzi5uqu5dm4tm0wt8srkg9h9suud4wuiwjimndrkydqm81cqtlb5ak6p7ku_v1_6.ipns-record deleted file mode 100644 index c6e2a0f5c5a7482bb42367715f262e5f85044b37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 144 zcmV;B0B`>aBrj=jW^*rMVPaAk35WqNF6ZZ&#mX%aw3oqH(c zT>wh5G^qr9f#GHkR1G}+apD(BEi>AoF4(LlER1|>po#OObsy8PYkTL*I#d`GdbIAB yYOV&%>sSXE03sVQF)}kPFgPtSG*mG%Ix#moFf%STIW#aeGFm7AFo2-(!m%{xT{cJn