From 0aa1dee70e1c5ad33b40fcad69e4f01f86e00bfc Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 24 Feb 2026 17:13:10 +0100 Subject: [PATCH 1/2] test: add IPNS record with sub-path in Value field existing IPNS tests only cover records whose Value is a bare /ipfs/. add TestGatewayIPNSRecordWithSubpath to verify gateways correctly handle IPNS records where Value contains a sub-path (e.g. /ipfs//root2), testing both directory listing and file retrieval through path concatenation. reuses gateway-cache/fixtures.car and extends the shared IPNS record generator with makeSubpathRecord(). --- CHANGELOG.md | 4 ++ fixtures/gateway-cache/README.md | 13 +++++ ...a1d2rg1camf6al7p3huy63dojlm57c.ipns-record | Bin 0 -> 409 bytes fixtures/ipns_records/generator/main.go | 23 +++++++++ tests/path_gateway_ipns_test.go | 45 ++++++++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 fixtures/gateway-cache/k51qzi5uqu5djokp3m1keo36hoxtd6u3a1d2rg1camf6al7p3huy63dojlm57c.ipns-record diff --git a/CHANGELOG.md b/CHANGELOG.md index 575d6f196..9e6262697 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.11.1] - UNRELEASED +### Added +- New test `TestGatewayIPNSRecordWithSubpath` for IPNS records whose Value field contains a sub-path (e.g. `/ipfs//root2`). All prior IPNS tests only covered bare `/ipfs/` in the record Value. + ## [0.11.0] - 2026-02-24 ### Added - New test `TestDNSLinkGatewayIPNS` for DNSLink TXT records pointing at `/ipns/` instead of `/ipfs/`. All prior DNSLink tests only covered the `/ipfs/` case. [#272](https://github.com/ipfs/gateway-conformance/pull/272) diff --git a/fixtures/gateway-cache/README.md b/fixtures/gateway-cache/README.md index 41ad5f4e4..6b74dd388 100644 --- a/fixtures/gateway-cache/README.md +++ b/fixtures/gateway-cache/README.md @@ -41,3 +41,16 @@ ipfs dag export ${ROOT1_CID} > ./fixtures.car # FILE_CID=bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am # ./root2/root3/root4/index.html # TEST_IPNS_ID=k51qzi5uqu5dlxdsdu5fpuu7h69wu4ohp32iwm9pdt9nq3y5rpn3ln9j12zfhe ``` + +### ipns-record with sub-path in Value + +IPNS record whose Value points at `/ipfs/ROOT1_CID/root2` instead of just +`/ipfs/ROOT1_CID`. Used to test that gateways correctly handle IPNS records +with sub-paths embedded in the Value field. + +Generated by `makeSubpathRecord()` in `../ipns_records/generator/main.go` +(run with `cd ../ipns_records && go run generator/main.go`). + +``` +# SUBPATH_IPNS_ID=k51qzi5uqu5djokp3m1keo36hoxtd6u3a1d2rg1camf6al7p3huy63dojlm57c +``` diff --git a/fixtures/gateway-cache/k51qzi5uqu5djokp3m1keo36hoxtd6u3a1d2rg1camf6al7p3huy63dojlm57c.ipns-record b/fixtures/gateway-cache/k51qzi5uqu5djokp3m1keo36hoxtd6u3a1d2rg1camf6al7p3huy63dojlm57c.ipns-record new file mode 100644 index 0000000000000000000000000000000000000000..3693f9ea0597757f59ff6b72446884c49801ee08 GIT binary patch literal 409 zcmd;b*Uv0SE7ng+Osh;v%}g>*OUp4TNzE)w$xALX&dsheDKaunGEFWiD$lMeD#}PM zNlD5}HOoxOPpqsct;jYl&D1Z-&o40&atO=}Pg#0GH-WdcPO?g6@gZTau5e|Jd`0UxGnN&dAWnOxM6r z*Vr(`(A3J*%*xP6&)m?&!qU>n(l|9UCjBdTJLS(1?;_;ft zxHLH=#7CNefpM`!OaMb_SYl3TY6P`{BO^GqurxI/root2) instead of a bare CID. This record is +// placed in gateway-cache/ alongside the CAR it references. +func makeSubpathRecord() { + // ROOT1_CID from gateway-cache/fixtures.car (see ../gateway-cache/README.md) + root1CID, err := cid.Decode("bafybeib3ffl2teiqdncv3mkz4r23b5ctrwkzrrhctdbne6iboayxuxk5ui") + panicOnErr(err) + + v, err := path.Join(path.FromCid(root1CID), "root2") + panicOnErr(err) + + sk, _, name := makeKeyPair() + + rec, err := ipns.NewRecord(sk, v, seq, eol, ttl, ipns.WithV1Compatibility(true)) + panicOnErr(err) + + raw, err := ipns.MarshalRecord(rec) + panicOnErr(err) + + saveToFile(raw, "../gateway-cache/"+name.String()+".ipns-record", v) +} + func main() { makeV1Only() makeV1V2() @@ -197,4 +219,5 @@ func main() { makeV1V2WithBrokenSignatureV1() makeV1V2WithBrokenSignatureV2() makeV2Only() + makeSubpathRecord() } diff --git a/tests/path_gateway_ipns_test.go b/tests/path_gateway_ipns_test.go index 93eeb5568..f03c47430 100644 --- a/tests/path_gateway_ipns_test.go +++ b/tests/path_gateway_ipns_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/ipfs/gateway-conformance/tooling" + . "github.com/ipfs/gateway-conformance/tooling/check" . "github.com/ipfs/gateway-conformance/tooling/ipns" "github.com/ipfs/gateway-conformance/tooling/specs" . "github.com/ipfs/gateway-conformance/tooling/test" @@ -165,3 +166,47 @@ func TestRedirectCanonicalIPNS(t *testing.T) { RunWithSpecs(t, tests, specs.PathGatewayIPNS) } + +func TestGatewayIPNSRecordWithSubpath(t *testing.T) { + tooling.LogTestGroup(t, GroupIPNS) + + // IPNS record whose Value = /ipfs/ROOT1_CID/root2 (note the sub-path) + // reuses the gateway-cache/fixtures.car which has root2/root3/root4/index.html + ipnsSubpath := MustOpenIPNSRecordWithKey("gateway-cache/k51qzi5uqu5djokp3m1keo36hoxtd6u3a1d2rg1camf6al7p3huy63dojlm57c.ipns-record") + + tests := SugarTests{ + { + Name: "GET /ipns/{key}/ returns directory listing when IPNS record Value has sub-path", + Hint: ` + When an IPNS record Value contains a sub-path (e.g. /ipfs//root2), + the gateway must resolve the IPNS name, follow the sub-path from the + Value, and serve the directory listing at the resulting content path. + `, + Request: Request(). + Path("/ipns/{{key}}/", ipnsSubpath.Key()), + Response: Expect(). + Status(200). + Body( + And( + Contains("Index of"), + Contains("root3"), + ), + ), + }, + { + Name: "GET /ipns/{key}/root3/root4/index.html returns file when IPNS record Value has sub-path", + Hint: ` + When an IPNS record Value contains a sub-path (e.g. /ipfs//root2), + the gateway must concatenate the request path with the IPNS-resolved + content path and serve the resulting file. + `, + Request: Request(). + Path("/ipns/{{key}}/root3/root4/index.html", ipnsSubpath.Key()), + Response: Expect(). + Status(200). + Body("hello\n"), + }, + } + + RunWithSpecs(t, tests, specs.PathGatewayIPNS) +} From 5d3e7d7a3706aa38dbaf93a0669f206c9acc66a2 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 24 Feb 2026 17:25:00 +0100 Subject: [PATCH 2/2] test: add DNSLink test for dnslink path with sub-path add TestDNSLinkGatewayWithSubpath to verify gateways handle DNSLink TXT records where the path includes a sub-path (e.g. dnslink=/ipfs//root2). also simplify TestGatewayIPNSRecordWithSubpath to only test file retrieval, removing the directory listing case. --- CHANGELOG.md | 3 ++- fixtures/gateway-cache/dnslink.yml | 6 ++++++ tests/dnslink_gateway_test.go | 26 ++++++++++++++++++++++++++ tests/path_gateway_ipns_test.go | 19 ------------------- 4 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 fixtures/gateway-cache/dnslink.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e6262697..89af50970 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.11.1] - UNRELEASED +## [0.11.1] - 2026-02-24 ### Added - New test `TestGatewayIPNSRecordWithSubpath` for IPNS records whose Value field contains a sub-path (e.g. `/ipfs//root2`). All prior IPNS tests only covered bare `/ipfs/` in the record Value. +- New test `TestDNSLinkGatewayWithSubpath` for DNSLink TXT records pointing at a content path with a sub-path (e.g. `dnslink=/ipfs//root2`). ## [0.11.0] - 2026-02-24 ### Added diff --git a/fixtures/gateway-cache/dnslink.yml b/fixtures/gateway-cache/dnslink.yml new file mode 100644 index 000000000..4bed1b969 --- /dev/null +++ b/fixtures/gateway-cache/dnslink.yml @@ -0,0 +1,6 @@ +# yaml-language-server: $schema=../fixture.schema.json +dnslinks: + dnslink-with-subpath: + domain: dnslink-subpath.example.org + # ROOT1_CID from gateway-cache/fixtures.car with /root2 sub-path + path: /ipfs/bafybeib3ffl2teiqdncv3mkz4r23b5ctrwkzrrhctdbne6iboayxuxk5ui/root2 diff --git a/tests/dnslink_gateway_test.go b/tests/dnslink_gateway_test.go index 15023b2e9..66a3536dd 100644 --- a/tests/dnslink_gateway_test.go +++ b/tests/dnslink_gateway_test.go @@ -75,3 +75,29 @@ func TestDNSLinkGatewayUnixFSDirectoryListing(t *testing.T) { RunWithSpecs(t, tests, specs.DNSLinkGateway) } + +func TestDNSLinkGatewayWithSubpath(t *testing.T) { + tooling.LogTestGroup(t, GroupDNSLink) + + dnsLinks := dnslink.MustOpenDNSLink("gateway-cache/dnslink.yml") + dnsLink := dnsLinks.MustGet("dnslink-with-subpath") + + tests := SugarTests{ + { + Name: "GET for DNSLink with dnslink=/ipfs//sub-path returns file at sub-path", + Hint: ` + When a DNSLink TXT record points to a content path with a sub-path + (e.g. /ipfs//root2), the gateway must resolve the full path and + concatenate any additional request path segments to serve the file. + `, + Request: Request(). + Header("Host", dnsLink). + Path("/root3/root4/index.html"), + Response: Expect(). + Status(200). + Body("hello\n"), + }, + } + + RunWithSpecs(t, tests, specs.DNSLinkGateway) +} diff --git a/tests/path_gateway_ipns_test.go b/tests/path_gateway_ipns_test.go index f03c47430..8455cc13f 100644 --- a/tests/path_gateway_ipns_test.go +++ b/tests/path_gateway_ipns_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/ipfs/gateway-conformance/tooling" - . "github.com/ipfs/gateway-conformance/tooling/check" . "github.com/ipfs/gateway-conformance/tooling/ipns" "github.com/ipfs/gateway-conformance/tooling/specs" . "github.com/ipfs/gateway-conformance/tooling/test" @@ -175,24 +174,6 @@ func TestGatewayIPNSRecordWithSubpath(t *testing.T) { ipnsSubpath := MustOpenIPNSRecordWithKey("gateway-cache/k51qzi5uqu5djokp3m1keo36hoxtd6u3a1d2rg1camf6al7p3huy63dojlm57c.ipns-record") tests := SugarTests{ - { - Name: "GET /ipns/{key}/ returns directory listing when IPNS record Value has sub-path", - Hint: ` - When an IPNS record Value contains a sub-path (e.g. /ipfs//root2), - the gateway must resolve the IPNS name, follow the sub-path from the - Value, and serve the directory listing at the resulting content path. - `, - Request: Request(). - Path("/ipns/{{key}}/", ipnsSubpath.Key()), - Response: Expect(). - Status(200). - Body( - And( - Contains("Index of"), - Contains("root3"), - ), - ), - }, { Name: "GET /ipns/{key}/root3/root4/index.html returns file when IPNS record Value has sub-path", Hint: `