Skip to content

Commit

Permalink
feat: targhets per gateway spec and smaller-range groups (#79)
Browse files Browse the repository at this point in the history
Co-authored-by: Marcin Rataj <lidel@lidel.org>
  • Loading branch information
hacdias and lidel authored Jun 19, 2023
1 parent 3d5a025 commit 6dd1aaf
Show file tree
Hide file tree
Showing 24 changed files with 1,186 additions and 971 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ jobs:
echo "IPFS_NS_MAP=${IPFS_NS_MAP}" >> $GITHUB_ENV
# note: the IPFS_NS_MAP set above will be passed the daemon
- uses: ipfs/start-ipfs-daemon-action@v1
with:
args: '--offline'
wait-for-addrs: false
- name: Provision Kubo Gateway
run: |
# Import car files
Expand All @@ -66,7 +69,6 @@ jobs:
xml: output.xml
html: output.html
markdown: output.md
args: -skip 'TestGatewayCar/GET_response_for_application/vnd.ipld.car/Header_Content-Length'
- name: Set summary
if: (failure() || success())
run: cat ./output.md >> $GITHUB_STEP_SUMMARY
Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ The examples are going to use `gateway-conformance` as a wrapper over `docker ru

### Testing only mature specs

By default, all mature tests are run. Mature tests generally refer to specifications whose [status is mature](https://specs.ipfs.tech/meta/spec-for-specs/).

```bash
gateway-conformance test
```
Expand All @@ -157,7 +159,21 @@ gateway-conformance test --specs +subdomain-gateway
### Testing mature specs, while disabling specific specs

```bash
gateway-conformance test --specs -subdomain-gateway
gateway-conformance test --specs -subdomain-gateway,-dnslink-gateway
```

### Testing specific spec (trustless gateway), while disabling a sub-part of it

```bash
gateway-conformance test --specs trustless-gateway,-trustless-gateway-ipns
```

### Skip a specific test

Tests are skipped using Go's standard syntax:

```bash
gateway-conformance test -skip 'TestGatewayCar/GET_response_for_application/vnd.ipld.car/Header_Content-Length'
```

### Extracting the test fixtures
Expand Down
22 changes: 15 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ module github.com/ipfs/gateway-conformance
go 1.20

require (
github.com/ipfs/boxo v0.8.2-0.20230510114019-33e3f0cd052b
github.com/ipfs/boxo v0.10.0
github.com/ipfs/go-cid v0.4.1
github.com/ipfs/go-unixfsnode v1.6.0
github.com/ipfs/go-unixfsnode v1.7.1
github.com/ipld/go-car v0.6.1
github.com/ipld/go-car/v2 v2.9.1-0.20230325062757-fff0e4397a3d
github.com/ipld/go-codec-dagpb v1.6.0
github.com/ipld/go-ipld-prime v0.20.0
github.com/libp2p/go-libp2p v0.26.3
github.com/stretchr/testify v1.8.2
github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.25.3
gopkg.in/yaml.v3 v3.0.1
)
Expand All @@ -19,7 +22,12 @@ require (
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
github.com/ipld/go-codec-dagpb v1.6.0 // indirect
github.com/ipfs/go-blockservice v0.5.0 // indirect
github.com/ipfs/go-ipfs-blockstore v1.3.0 // indirect
github.com/ipfs/go-ipfs-ds-help v1.1.0 // indirect
github.com/ipfs/go-ipfs-exchange-interface v0.2.0 // indirect
github.com/ipfs/go-merkledag v0.11.0 // indirect
github.com/ipfs/go-verifcid v0.0.2 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/libp2p/go-libp2p-record v0.2.0 // indirect
github.com/multiformats/go-multiaddr v0.8.0 // indirect
Expand All @@ -43,8 +51,8 @@ require (
github.com/ipfs/go-datastore v0.6.0 // indirect
github.com/ipfs/go-ipfs-util v0.0.2 // indirect
github.com/ipfs/go-ipld-cbor v0.0.6 // indirect
github.com/ipfs/go-ipld-format v0.4.0
github.com/ipfs/go-ipld-legacy v0.1.1 // indirect
github.com/ipfs/go-ipld-format v0.5.0
github.com/ipfs/go-ipld-legacy v0.2.1 // indirect
github.com/ipfs/go-log v1.0.5
github.com/ipfs/go-log/v2 v2.5.1 // indirect
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
Expand All @@ -57,7 +65,7 @@ require (
github.com/multiformats/go-base36 v0.2.0 // indirect
github.com/multiformats/go-multibase v0.2.0
github.com/multiformats/go-multicodec v0.9.0
github.com/multiformats/go-multihash v0.2.1
github.com/multiformats/go-multihash v0.2.3
github.com/multiformats/go-varint v0.0.7 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect
Expand Down
68 changes: 31 additions & 37 deletions go.sum

Large diffs are not rendered by default.

251 changes: 251 additions & 0 deletions tests/dnslink_gateway_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
package tests

import (
"net/url"
"testing"

"github.com/ipfs/gateway-conformance/tooling/car"
. "github.com/ipfs/gateway-conformance/tooling/check"
"github.com/ipfs/gateway-conformance/tooling/dnslink"
"github.com/ipfs/gateway-conformance/tooling/helpers"
"github.com/ipfs/gateway-conformance/tooling/specs"
. "github.com/ipfs/gateway-conformance/tooling/test"
"github.com/ipfs/gateway-conformance/tooling/tmpl"
)

// TODO(laurent): this were in t0114_gateway_subdomains_test.go

func TestDNSLinkGateway(t *testing.T) {
tests := SugarTests{
// ## ============================================================================
// ## Test DNSLink requests with a custom PublicGateway (hostname config)
// ## (DNSLink site at http://dnslink-test.example.com)
// ## ============================================================================
// # disable wildcard DNSLink gateway
// # and enable it on specific NSLink hostname
// ipfs config --json Gateway.NoDNSLink true && \
// ipfs config --json Gateway.PublicGateways '{
// "dnslink-enabled-on-fqdn.example.org": {
// "NoDNSLink": false,
// "UseSubdomains": false,
// "Paths": ["/ipfs"]
// },
// "only-dnslink-enabled-on-fqdn.example.org": {
// "NoDNSLink": false,
// "UseSubdomains": false,
// "Paths": []
// },
// "dnslink-disabled-on-fqdn.example.com": {
// "NoDNSLink": true,
// "UseSubdomains": false,
// "Paths": []
// }
// }' || exit 1

// # DNSLink test requires a daemon in online mode with precached /ipns/ mapping
// DNSLINK_FQDN="dnslink-enabled-on-fqdn.example.org"
// ONLY_DNSLINK_FQDN="only-dnslink-enabled-on-fqdn.example.org"
// NO_DNSLINK_FQDN="dnslink-disabled-on-fqdn.example.com"
// export IPFS_NS_MAP="$DNSLINK_FQDN:/ipfs/$CIDv1,$ONLY_DNSLINK_FQDN:/ipfs/$DIR_CID"

// # DNSLink enabled

// test_hostname_gateway_response_should_contain \
// "request for http://{dnslink-fqdn}/ PublicGateway returns expected payload" \
// "$DNSLINK_FQDN" \
// "http://127.0.0.1:$GWAY_PORT/" \
// "$CID_VAL"

// test_hostname_gateway_response_should_contain \
// "request for {dnslink-fqdn}/ipfs/{cid} returns expected payload when /ipfs is on Paths whitelist" \
// "$DNSLINK_FQDN" \
// "http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1" \
// "$CID_VAL"

// # Test for a fun edge case: DNSLink-only gateway without /ipfs/ namespace
// # mounted, and with subdirectory named "ipfs" ¯\_(ツ)_/¯
// test_hostname_gateway_response_should_contain \
// "request for {dnslink-fqdn}/ipfs/file.txt returns data from content root when /ipfs in not on Paths whitelist" \
// "$ONLY_DNSLINK_FQDN" \
// "http://127.0.0.1:$GWAY_PORT/ipfs/file.txt" \
// "I am a txt file"

// test_hostname_gateway_response_should_contain \
// "request for {dnslink-fqdn}/ipns/{peerid} returns 404 when path is not whitelisted" \
// "$DNSLINK_FQDN" \
// "http://127.0.0.1:$GWAY_PORT/ipns/$RSA_IPNS_IDv0" \
// "404 Not Found"

// test_hostname_gateway_response_should_contain \
// "request for {dnslink-fqdn}/ipns/{peerid} returns 404 when path is not whitelisted" \
// "$DNSLINK_FQDN" \
// "http://127.0.0.1:$GWAY_PORT/ipns/$ED25519_IPNS_IDv0" \
// "404 Not Found"

// # DNSLink disabled

// test_hostname_gateway_response_should_contain \
// "request for http://{dnslink-fqdn}/ returns 404 when NoDNSLink=true" \
// "$NO_DNSLINK_FQDN" \
// "http://127.0.0.1:$GWAY_PORT/" \
// "404 Not Found"

// test_hostname_gateway_response_should_contain \
// "request for {dnslink-fqdn}/ipfs/{cid} returns 404 when path is not whitelisted" \
// "$NO_DNSLINK_FQDN" \
// "http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv0" \
// "404 Not Found"

// ## ============================================================================
// ## Test wildcard DNSLink (any hostname, with default config)
// ## ============================================================================

// test_kill_ipfs_daemon

// # enable wildcard DNSLink gateway (any value in Host header)
// # and remove custom PublicGateways
// ipfs config --json Gateway.NoDNSLink false && \
// ipfs config --json Gateway.PublicGateways '{}' || exit 1

// # DNSLink test requires a daemon in online mode with precached /ipns/ mapping
// DNSLINK_FQDN="wildcard-dnslink-not-in-config.example.com"
// export IPFS_NS_MAP="$DNSLINK_FQDN:/ipfs/$CIDv1"

// # restart daemon to apply config changes
// test_launch_ipfs_daemon

// # make sure test setup is valid (fail if CoreAPI is unable to resolve)
// test_expect_success "spoofed DNSLink record resolves in cli" "
// ipfs resolve /ipns/$DNSLINK_FQDN > result &&
// test_should_contain \"$CIDv1\" result &&
// ipfs cat /ipns/$DNSLINK_FQDN > result &&
// test_should_contain \"$CID_VAL\" result
// "

// # gateway test
//
// test_hostname_gateway_response_should_contain \
// "request for http://{dnslink-fqdn}/ (wildcard) returns expected payload" \
// "$DNSLINK_FQDN" \
// "http://127.0.0.1:$GWAY_PORT/" \
// "$CID_VAL"
}

RunWithSpecs(t, helpers.UnwrapSubdomainTests(t, tests), specs.DNSLinkGateway)
}

// TODO(laurent): this was in t0115_gateway_dir_listing_test.go

func TestDNSLinkGatewayUnixFSDirectoryListing(t *testing.T) {
fixture := car.MustOpenUnixfsCar("t0115/fixtures.car")
file := fixture.MustGetNode("ą", "ę", "file-źł.txt")

dnsLinks := dnslink.MustOpenDNSLink("t0115/dnslink.yml")
dnsLink := dnsLinks.MustGet("website")

gatewayURL := SubdomainGatewayURL

tests := SugarTests{}

u, err := url.Parse(gatewayURL)
if err != nil {
t.Fatal(err)
}

dnsLinkHostname := tmpl.Fmt("{{dnslink}}.{{host}}", dnsLink, u.Host)

// ## ============================================================================
// ## Test dir listing on DNSLink gateway (eg. example.com)
// ## ============================================================================
tests = append(tests, SugarTests{
// # DNSLink test requires a daemon in online mode with precached /ipns/ mapping
// test_kill_ipfs_daemon
// DNSLINK_HOSTNAME="website.example.com"
// export IPFS_NS_MAP="$DNSLINK_HOSTNAME:/ipfs/$DIR_CID"
// test_launch_ipfs_daemon

// # Note that:
// # - this type of gateway is also tested in gateway_test.go#TestIPNSHostnameBacklinks
// # (go tests and sharness tests should be kept in sync)
// # - we skip DNS lookup by running curl with --resolve $DNSLINK_HOSTNAME:127.0.0.1

// test_expect_success "dnslink gw: backlink on root CID should be hidden" '
// curl -v -sD - --resolve $DNSLINK_HOSTNAME:$GWAY_PORT:127.0.0.1 http://$DNSLINK_HOSTNAME:$GWAY_PORT/ > list_response &&
// test_should_contain "Index of" list_response &&
// test_should_not_contain "<a href=\"/\">..</a>" list_response
// '
{
Name: "Backlink on root CID should be hidden (TODO: cleanup Kubo-specifics)",
Request: Request().
URL(`{{scheme}}://{{hostname}}/`, u.Scheme, dnsLinkHostname),
Response: Expect().
Body(
And(
Contains("Index of"),
Not(Contains(`<a href="/">..</a>`)),
),
),
},
// test_expect_success "dnslink gw: redirect dir listing to URL with trailing slash" '
// curl -sD - --resolve $DNSLINK_HOSTNAME:$GWAY_PORT:127.0.0.1 http://$DNSLINK_HOSTNAME:$GWAY_PORT/ą/ę > list_response &&
// test_should_contain "HTTP/1.1 301 Moved Permanently" list_response &&
// test_should_contain "Location: /%c4%85/%c4%99/" list_response
// '
{
Name: "Redirect dir listing to URL with trailing slash",
Request: Request().
URL(`{{scheme}}://{{hostname}}/ą/ę`, u.Scheme, dnsLinkHostname),
Response: Expect().
Status(301).
Headers(
Header("Location").Equals(`/%c4%85/%c4%99/`),
),
},
// test_expect_success "dnslink gw: Etag should be present" '
// curl -sD - --resolve $DNSLINK_HOSTNAME:$GWAY_PORT:127.0.0.1 http://$DNSLINK_HOSTNAME:$GWAY_PORT/ą/ę/ > list_response &&
// test_should_contain "Index of" list_response &&
// test_should_contain "Etag: \"DirIndex-" list_response
// '
// test_expect_success "dnslink gw: backlink on subdirectory should point at parent directory" '
// test_should_contain "<a href=\"/%C4%85/%C4%99/..\">..</a>" list_response
// '
// test_expect_success "dnslink gw: breadcrumbs should point at content root mounted at dnslink origin" '
// test_should_contain "/ipns/<a href=\"//$DNSLINK_HOSTNAME:$GWAY_PORT/\">website.example.com</a>/<a href=\"//$DNSLINK_HOSTNAME:$GWAY_PORT/%C4%85\">ą</a>/<a href=\"//$DNSLINK_HOSTNAME:$GWAY_PORT/%C4%85/%C4%99\">ę</a>" list_response
// '
// test_expect_success "dnslink gw: name column should be a link to content root mounted at dnslink origin" '
// test_should_contain "<a href=\"/%C4%85/%C4%99/file-%C5%BA%C5%82.txt\">file-źł.txt</a>" list_response
// '
// # DNSLink websites don't have public gateway mounted by default
// # See: https://github.com/ipfs/dir-index-html/issues/42
// test_expect_success "dnslink gw: hash column should be a CID link to cid.ipfs.tech" '
// test_should_contain "<a class=\"ipfs-hash\" translate=\"no\" href=\"https://cid.ipfs.tech/#$FILE_CID\" target=\"_blank\" rel=\"noreferrer noopener\">" list_response
// '
{
Name: "Regular dir listing (TODO: cleanup Kubo-specifics)",
Request: Request().
URL(`{{scheme}}://{{hostname}}/ą/ę/`, u.Scheme, dnsLinkHostname),
Response: Expect().
Headers(
Header("Etag").Contains(`"DirIndex-`),
).
BodyWithHint(`
- backlink on subdirectory should point at parent directory (TODO: kubo-specific)
- breadcrumbs should point at content root mounted at dnslink origin (TODO: kubo-specific)
- name column should be a link to content root mounted at dnslink origin
- hash column should be a CID link to cid.ipfs.tech
DNSLink websites don't have public gateway mounted by default
See: https://github.com/ipfs/dir-index-html/issues/42 (TODO: class and other attrs are kubo-specific)
`,
And(
Contains("Index of"),
Contains(`<a href="/%C4%85/%C4%99/..">..</a>`),
Contains(`/ipns/<a href="//{{hostname}}/">{{hostname}}</a>/<a href="//{{hostname}}/%C4%85">ą</a>/<a href="//{{hostname}}/%C4%85/%C4%99">ę</a>`, dnsLinkHostname),
Contains(`<a href="/%C4%85/%C4%99/file-%C5%BA%C5%82.txt">file-źł.txt</a>`),
Contains(`<a class="ipfs-hash" translate="no" href="https://cid.ipfs.tech/#{{cid}}" target="_blank" rel="noreferrer noopener">`, file.Cid()),
),
),
},
}...)

RunWithSpecs(t, helpers.UnwrapSubdomainTests(t, tests), specs.DNSLinkGateway)
}
17 changes: 8 additions & 9 deletions tests/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,14 @@ func (s *specsFlag) Set(value string) error {
for _, spec := range only {
spec.Enable()
}
} else {
// If all specs from the input are prefixed with a + or -,
// enable the specs prefixed with + and then disable the specs prefixed with -.
for _, spec := range enable {
spec.Enable()
}
for _, spec := range disable {
spec.Disable()
}
}
// If some specs from the input are prefixed with a + or -,
// enable the specs prefixed with + and then disable the specs prefixed with -.
for _, spec := range enable {
spec.Enable()
}
for _, spec := range disable {
spec.Disable()
}
*s = specsFlag(value)
return nil
Expand Down
Loading

0 comments on commit 6dd1aaf

Please sign in to comment.