Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: dedicated target per gateway specification and smaller-range groups #79

Merged
merged 7 commits into from
Jun 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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