From 3c0faab41b06fcf92b2b673f80d3cba45d63c70c Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Thu, 7 Sep 2023 17:40:56 +0200 Subject: [PATCH] feat: add Spec(s) operations --- tests/metadata_test.go | 13 ++++++++++++- tests/path_gateway_dag_test.go | 4 +++- tests/path_gateway_tar_test.go | 1 + tests/path_gateway_unixfs_test.go | 6 ++++++ tests/redirects_file_test.go | 6 +++++- tests/trustless_gateway_car_test.go | 3 +++ tests/trustless_gateway_raw_test.go | 6 +++++- tooling/metadata.go | 14 +++++++------- tooling/test/sugar.go | 22 ++++++++++++++++++++++ tooling/test/test.go | 21 +++++++++++++++++++++ tooling/test/validate.go | 11 ++++++++--- 11 files changed, 93 insertions(+), 14 deletions(-) diff --git a/tests/metadata_test.go b/tests/metadata_test.go index a8b060eb0..f5406bc3a 100644 --- a/tests/metadata_test.go +++ b/tests/metadata_test.go @@ -4,10 +4,21 @@ import ( "testing" "github.com/ipfs/gateway-conformance/tooling" + "github.com/ipfs/gateway-conformance/tooling/test" ) +func logGatewayURL(t *testing.T) { + tooling.LogMetadata(t, struct { + GatewayURL string `json:"gateway_url"` + SubdomainGatewayURL string `json:"subdomain_gateway_url"` + }{ + GatewayURL: test.GatewayURL, + SubdomainGatewayURL: test.SubdomainGatewayURL, + }) +} + func TestMetadata(t *testing.T) { tooling.LogVersion(t) tooling.LogJobURL(t) - tooling.LogGatewayURL(t) + logGatewayURL(t) } diff --git a/tests/path_gateway_dag_test.go b/tests/path_gateway_dag_test.go index 31fa64b12..8b445bb0e 100644 --- a/tests/path_gateway_dag_test.go +++ b/tests/path_gateway_dag_test.go @@ -39,6 +39,7 @@ func TestGatewayJsonCbor(t *testing.T) { }, { Name: "GET UnixFS file with JSON bytes is returned with application/json Content-Type - with headers", + Spec: "specs.ipfs.tech/http-gateways/path-gateway/#accept-request-header", Hint: ` ## Quick regression check for JSON stored on UnixFS: ## it has nothing to do with DAG-JSON and JSON codecs, @@ -467,7 +468,7 @@ func TestNativeDag(t *testing.T) { Response: Expect(). Headers( Header("Content-Type").Hint("expected Content-Type").Equals("application/vnd.ipld.dag-{{format}}", row.Format), - Header("Content-Length").Hint("includes Content-Length").Equals("{{length}}", len(dagTraversal.RawData())), + Header("Content-Length").Spec("specs.ipfs.tech/http-gateways/path-gateway/#content-disposition-response-header").Hint("includes Content-Length").Equals("{{length}}", len(dagTraversal.RawData())), Header("Content-Disposition").Hint("includes Content-Disposition").Contains(`{{disposition}}; filename="{{cid}}.{{format}}"`, row.Disposition, dagTraversalCID, row.Format), Header("X-Content-Type-Options").Hint("includes nosniff hint").Contains("nosniff"), ), @@ -541,6 +542,7 @@ func TestNativeDag(t *testing.T) { }, { Name: Fmt("HEAD {{name}} with only-if-cached for missing block returns HTTP 412 Precondition Failed", row.Name), + Spec: "specs.ipfs.tech/http-gateways/path-gateway/#only-if-cached", Request: Request(). Path("/ipfs/{{cid}}", missingCID). Header("Cache-Control", "only-if-cached"). diff --git a/tests/path_gateway_tar_test.go b/tests/path_gateway_tar_test.go index df7145bb0..fc857792b 100644 --- a/tests/path_gateway_tar_test.go +++ b/tests/path_gateway_tar_test.go @@ -73,6 +73,7 @@ func TestTar(t *testing.T) { }, { Name: "GET TAR with explicit ?filename= succeeds with modified Content-Disposition header", + Spec: "specs.ipfs.tech/http-gateways/path-gateway/#content-disposition-response-header", Request: Request(). Path("/ipfs/{{cid}}", dirCID). Query("filename", "testтест.tar"). diff --git a/tests/path_gateway_unixfs_test.go b/tests/path_gateway_unixfs_test.go index 04f355062..9ee0318c5 100644 --- a/tests/path_gateway_unixfs_test.go +++ b/tests/path_gateway_unixfs_test.go @@ -199,6 +199,7 @@ func TestGatewayCache(t *testing.T) { // ========== { Name: "GET for /ipfs/ file with matching Etag in If-None-Match returns 304 Not Modified", + Spec: "specs.ipfs.tech/http-gateways/path-gateway/#if-none-match-request-header", Request: Request(). Path("/ipfs/{{cid}}/root2/root3/root4/index.html", fixture.MustGetCid()). Headers( @@ -209,6 +210,7 @@ func TestGatewayCache(t *testing.T) { }, { Name: "GET for /ipfs/ dir with index.html file with matching Etag in If-None-Match returns 304 Not Modified", + Spec: "specs.ipfs.tech/http-gateways/path-gateway/#if-none-match-request-header", Request: Request(). Path("/ipfs/{{cid}}/root2/root3/root4/", fixture.MustGetCid()). Headers( @@ -219,6 +221,7 @@ func TestGatewayCache(t *testing.T) { }, { Name: "GET for /ipfs/ file with matching third Etag in If-None-Match returns 304 Not Modified", + Spec: "specs.ipfs.tech/http-gateways/path-gateway/#if-none-match-request-header", Request: Request(). Path("/ipfs/{{cid}}/root2/root3/root4/index.html", fixture.MustGetCid()). Headers( @@ -229,6 +232,7 @@ func TestGatewayCache(t *testing.T) { }, { Name: "GET for /ipfs/ file with matching weak Etag in If-None-Match returns 304 Not Modified", + Spec: "specs.ipfs.tech/http-gateways/path-gateway/#if-none-match-request-header", Request: Request(). Path("/ipfs/{{cid}}/root2/root3/root4/index.html", fixture.MustGetCid()). Headers( @@ -239,6 +243,7 @@ func TestGatewayCache(t *testing.T) { }, { Name: "GET for /ipfs/ file with wildcard Etag in If-None-Match returns 304 Not Modified", + Spec: "specs.ipfs.tech/http-gateways/path-gateway/#if-none-match-request-header", Request: Request(). Path("/ipfs/{{cid}}/root2/root3/root4/index.html", fixture.MustGetCid()). Headers( @@ -249,6 +254,7 @@ func TestGatewayCache(t *testing.T) { }, { Name: "GET for /ipfs/ dir listing with matching weak Etag in If-None-Match returns 304 Not Modified", + Spec: "specs.ipfs.tech/http-gateways/path-gateway/#if-none-match-request-header", Request: Request(). Path("/ipfs/{{cid}}/root2/root3/", fixture.MustGetCid()). Headers( diff --git a/tests/redirects_file_test.go b/tests/redirects_file_test.go index af1c85c9a..d4b93e923 100644 --- a/tests/redirects_file_test.go +++ b/tests/redirects_file_test.go @@ -4,6 +4,7 @@ import ( "net/url" "testing" + "github.com/ipfs/gateway-conformance/tooling" "github.com/ipfs/gateway-conformance/tooling/car" . "github.com/ipfs/gateway-conformance/tooling/check" "github.com/ipfs/gateway-conformance/tooling/dnslink" @@ -14,6 +15,7 @@ import ( ) func TestRedirectsFileSupport(t *testing.T) { + tooling.LogSpecs(t, "specs.ipfs.tech/http-gateways/web-redirects-file/") fixture := car.MustOpenUnixfsCar("redirects_file/redirects.car") redirectDir := fixture.MustGetNode("examples") redirectDirCID := redirectDir.Base32Cid() @@ -164,7 +166,8 @@ func TestRedirectsFileSupport(t *testing.T) { Contains("could not parse _redirects:"), Contains(`forced redirects (or "shadowing") are not supported`), ), - ), + ).Spec("specs.ipfs.tech/http-gateways/web-redirects-file/#no-forced-redirects"), + Spec: "specs.ipfs.tech/http-gateways/web-redirects-file/#error-handling", }, { Name: "invalid file: request for $TOO_LARGE_REDIRECTS_DIR_HOSTNAME/not-found returns error about too large redirects file", @@ -179,6 +182,7 @@ func TestRedirectsFileSupport(t *testing.T) { Contains("redirects file size cannot exceed"), ), ), + Spec: "specs.ipfs.tech/http-gateways/web-redirects-file/#max-file-size", }, }...) diff --git a/tests/trustless_gateway_car_test.go b/tests/trustless_gateway_car_test.go index 6dbb6166e..328f67bfb 100644 --- a/tests/trustless_gateway_car_test.go +++ b/tests/trustless_gateway_car_test.go @@ -3,6 +3,7 @@ package tests import ( "testing" + "github.com/ipfs/gateway-conformance/tooling" "github.com/ipfs/gateway-conformance/tooling/car" . "github.com/ipfs/gateway-conformance/tooling/check" "github.com/ipfs/gateway-conformance/tooling/helpers" @@ -398,6 +399,8 @@ func TestTrustlessCarDagScopeAll(t *testing.T) { } func TestTrustlessCarEntityBytes(t *testing.T) { + tooling.LogSpecs(t, "specs.ipfs.tech/http-gateways/trustless-gateway/#entity-bytes-request-query-parameter") + singleLayerHamtMultiBlockFilesFixture := car.MustOpenUnixfsCar("trustless_gateway_car/single-layer-hamt-with-multi-block-files.car") subdirWithMixedBlockFiles := car.MustOpenUnixfsCar("trustless_gateway_car/subdir-with-mixed-block-files.car") missingBlockFixture := car.MustOpenUnixfsCar("trustless_gateway_car/file-3k-and-3-blocks-missing-block.car") diff --git a/tests/trustless_gateway_raw_test.go b/tests/trustless_gateway_raw_test.go index 8094f92d5..9e88a9c24 100644 --- a/tests/trustless_gateway_raw_test.go +++ b/tests/trustless_gateway_raw_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" + "github.com/ipfs/gateway-conformance/tooling" "github.com/ipfs/gateway-conformance/tooling/car" . "github.com/ipfs/gateway-conformance/tooling/check" "github.com/ipfs/gateway-conformance/tooling/specs" @@ -12,6 +13,7 @@ import ( ) func TestTrustlessRaw(t *testing.T) { + tooling.LogSpecs(t, "specs.ipfs.tech/http-gateways/trustless-gateway/#block-responses-application-vnd-ipld-raw") fixture := car.MustOpenUnixfsCar("gateway-raw-block.car") tests := SugarTests{ @@ -128,6 +130,8 @@ func TestTrustlessRaw(t *testing.T) { } func TestTrustlessRawRanges(t *testing.T) { + // @lidel: "The optional entity-bytes=from:to parameter is available only for CAR requests." + // Multi-range requests MUST conform to the HTTP semantics. The server does not // need to be able to support returning multiple ranges. However, it must respond // correctly. @@ -140,7 +144,7 @@ func TestTrustlessRawRanges(t *testing.T) { RunWithSpecs(t, SugarTests{ { - Name: "GETaa with application/vnd.ipld.raw with single range request includes correct bytes", + Name: "GET with application/vnd.ipld.raw with single range request includes correct bytes", Request: Request(). Path("/ipfs/{{cid}}", fixture.MustGetCid("dir", "ascii.txt")). Headers( diff --git a/tooling/metadata.go b/tooling/metadata.go index 547b431b8..22c9c0c59 100644 --- a/tooling/metadata.go +++ b/tooling/metadata.go @@ -3,8 +3,6 @@ package tooling import ( "encoding/json" "testing" - - "github.com/ipfs/gateway-conformance/tooling/test" ) func LogMetadata(t *testing.T, value interface{}) { @@ -34,12 +32,14 @@ func LogJobURL(t *testing.T) { }) } -func LogGatewayURL(t *testing.T) { +func LogSpecs(t *testing.T, specs ...string) { + if len(specs) == 0 { + return + } + LogMetadata(t, struct { - GatewayURL string `json:"gateway_url"` - SubdomainGatewayURL string `json:"subdomain_gateway_url"` + Specs []string `json:"specs"` }{ - GatewayURL: test.GatewayURL, - SubdomainGatewayURL: test.SubdomainGatewayURL, + Specs: specs, }) } diff --git a/tooling/test/sugar.go b/tooling/test/sugar.go index 966c96859..3527738d5 100644 --- a/tooling/test/sugar.go +++ b/tooling/test/sugar.go @@ -137,6 +137,7 @@ type ExpectBuilder struct { StatusCode_ int `json:"statusCode,omitempty"` Headers_ []HeaderBuilder `json:"headers,omitempty"` Body_ interface{} `json:"body,omitempty"` + Specs_ []string `json:"specs,omitempty"` } func Expect() ExpectBuilder { @@ -157,6 +158,16 @@ func (e ExpectBuilder) Header(h HeaderBuilder) ExpectBuilder { return e } +func (e ExpectBuilder) Spec(spec string) ExpectBuilder { + e.Specs_ = []string{spec} + return e +} + +func (e ExpectBuilder) Specs(specs ...string) ExpectBuilder { + e.Specs_ = specs + return e +} + func (e ExpectBuilder) Bytes(body string) ExpectBuilder { e.Body_ = []byte(body) return e @@ -213,6 +224,7 @@ type HeaderBuilder struct { Value_ string `json:"value,omitempty"` Check_ check.Check[[]string] `json:"check,omitempty"` Hint_ string `json:"hint,omitempty"` + Specs_ []string `json:"specs,omitempty"` Not_ bool `json:"not,omitempty"` } @@ -245,6 +257,16 @@ func (h HeaderBuilder) Hint(hint string) HeaderBuilder { return h } +func (h HeaderBuilder) Specs(specs ...string) HeaderBuilder { + h.Specs_ = specs + return h +} + +func (h HeaderBuilder) Spec(spec string) HeaderBuilder { + h.Specs_ = []string{spec} + return h +} + func (h HeaderBuilder) Equals(value string, args ...any) HeaderBuilder { h.Check_ = check.IsUniqAnd(check.IsEqual(value, args...)) return h diff --git a/tooling/test/test.go b/tooling/test/test.go index 33245cc07..f9a470843 100644 --- a/tooling/test/test.go +++ b/tooling/test/test.go @@ -6,12 +6,15 @@ import ( "testing" "time" + "github.com/ipfs/gateway-conformance/tooling" "github.com/ipfs/gateway-conformance/tooling/specs" ) type SugarTest struct { Name string Hint string + Spec string + Specs []string Request RequestBuilder Requests []RequestBuilder Response ExpectBuilder @@ -20,6 +23,22 @@ type SugarTest struct { type SugarTests []SugarTest +func (s *SugarTest) AllSpecs() []string { + if len(s.Specs) > 0 && s.Spec != "" { + panic("cannot have both Spec and Specs") + } + + if len(s.Specs) > 0 { + return s.Specs + } + + if s.Spec != "" { + return []string{s.Spec} + } + + return []string{} +} + func RunWithSpecs( t *testing.T, tests SugarTests, @@ -51,6 +70,7 @@ func run(t *testing.T, tests SugarTests) { if len(test.Requests) > 0 { t.Run(test.Name, func(t *testing.T) { + tooling.LogSpecs(t, test.AllSpecs()...) responses := make([]*http.Response, 0, len(test.Requests)) for _, req := range test.Requests { @@ -63,6 +83,7 @@ func run(t *testing.T, tests SugarTests) { }) } else { t.Run(test.Name, func(t *testing.T) { + tooling.LogSpecs(t, test.AllSpecs()...) _, res, localReport := runRequest(timeout, t, test, test.Request) validateResponse(t, test.Response, res, localReport) }) diff --git a/tooling/test/validate.go b/tooling/test/validate.go index 2187d06d4..15bc77f57 100644 --- a/tooling/test/validate.go +++ b/tooling/test/validate.go @@ -6,6 +6,7 @@ import ( "net/http" "testing" + "github.com/ipfs/gateway-conformance/tooling" "github.com/ipfs/gateway-conformance/tooling/check" ) @@ -16,15 +17,19 @@ func validateResponse( localReport Reporter, ) { t.Helper() + tooling.LogSpecs(t, expected.Specs_...) if expected.StatusCode_ != 0 { - if res.StatusCode != expected.StatusCode_ { - localReport(t, "Status code is not %d. It is %d", expected.StatusCode_, res.StatusCode) - } + t.Run("Status code", func(t *testing.T) { + if res.StatusCode != expected.StatusCode_ { + localReport(t, "Status code is not %d. It is %d", expected.StatusCode_, res.StatusCode) + } + }) } for _, header := range expected.Headers_ { t.Run(fmt.Sprintf("Header %s", header.Key_), func(t *testing.T) { + tooling.LogSpecs(t, header.Specs_...) actual := res.Header.Values(header.Key_) c := header.Check_