From d544f28a7a9d4f9c2a0266d2f4055e78f72a7010 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Wed, 17 May 2023 16:38:24 -0400 Subject: [PATCH] feat: test CAR against both format parameter and accept header --- tests/t0118_gateway_car_test.go | 99 ++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 20 deletions(-) diff --git a/tests/t0118_gateway_car_test.go b/tests/t0118_gateway_car_test.go index ba1c298d1..7fae7861b 100644 --- a/tests/t0118_gateway_car_test.go +++ b/tests/t0118_gateway_car_test.go @@ -1,6 +1,7 @@ package tests import ( + "fmt" "testing" "github.com/ipfs/gateway-conformance/tooling/car" @@ -13,7 +14,7 @@ func TestGatewayCar(t *testing.T) { tests := SugarTests{ { - Name: "GET response for application/vnd.ipld.car", + Name: "GET default CAR response", Hint: ` CAR stream is not deterministic, as blocks can arrive in random order, but if we have a small file that fits into a single block, and export its CID @@ -21,27 +22,16 @@ func TestGatewayCar(t *testing.T) { `, Request: Request(). Path("ipfs/%s/subdir/ascii.txt", fixture.MustGetCid()). - Headers( - Header("Accept", "application/vnd.ipld.car"), - ), + Query("format", "car"), Response: Expect(). Status(200). Headers( Header("Content-Type"). Hint("Expected content type to be application/vnd.ipld.car"). Contains("application/vnd.ipld.car"), - Header("Content-Length"). - Hint("CAR is streamed, gateway may not have the entire thing, unable to calculate total size"). - IsEmpty(), Header("Content-Disposition"). Hint("Expected content disposition to be attachment; filename=\".car\""). Contains("attachment; filename=\"%s.car\"", fixture.MustGetCid()), - Header("X-Content-Type-Options"). - Hint("CAR is streamed, gateway may not have the entire thing, unable to calculate total size"). - Equals("nosniff"), - Header("Accept-Ranges"). - Hint("CAR is streamed, gateway may not have the entire thing, unable to support range-requests. Partial downloads and resumes should be handled using IPLD selectors: https://github.com/ipfs/go-ipfs/issues/8769"). - Equals("none"), ).Body( IsCar(). HasRoot(fixture.MustGetCid()). @@ -55,7 +45,7 @@ func TestGatewayCar(t *testing.T) { ), }, { - Name: "GET with ?format=car&dag-scope=block params returns expected blocks", + Name: "GET CAR with dag-scope=block", Hint: ` dag-scope=block should return a CAR file with only the root block and a block for each optional path component. @@ -72,14 +62,13 @@ func TestGatewayCar(t *testing.T) { HasBlocks( fixture.MustGetCid(), fixture.MustGetCid("subdir"), - fixture.MustGetCid("subdir", "ascii.txt"), ). Exactly(). InThatOrder(), ), }, { - Name: "GET with ?format=car&dag-scope=entity params returns expected blocks", + Name: "GET CAR with dag-scope=entity", Hint: ` dag-scope=entity should return a CAR file with all the blocks needed to 'cat' a UnixFS file at the end of the specified path, or to 'ls' a UnixFS directory @@ -96,15 +85,13 @@ func TestGatewayCar(t *testing.T) { HasRoot(fixture.MustGetCid()). HasBlocks( fixture.MustGetCid(), - fixture.MustGetCid("subdir"), - fixture.MustGetCid("subdir", "ascii.txt"), ). Exactly(). InThatOrder(), ), }, { - Name: "GET with ?format=car&dag-scope=all params returns expected blocks", + Name: "GET CAR with dag-scope=all", Hint: ` dag-scope=all should return a CAR file with the entire contiguous DAG that begins at the end of the path query, after blocks required to verify path segments. @@ -129,5 +116,77 @@ func TestGatewayCar(t *testing.T) { }, } - Run(t, tests) + transformedTests := standardCARTestTransforms(t, tests) + Run(t, transformedTests) +} + +func standardCARTestTransforms(t *testing.T, tests SugarTests) SugarTests { + t.Helper() + + var out SugarTests + for _, test := range tests { + out = append(out, checkBothFormatAndAcceptHeaderCAR(t, applyStandardCarResponseHeaders(t, test))...) + } + return out +} + +func applyStandardCarResponseHeaders(t *testing.T, test SugarTest) SugarTest { + test.Response = test.Response.Headers( + Header("Content-Length"). + Hint("CAR is streamed, gateway may not have the entire thing, unable to calculate total size"). + IsEmpty(), + Header("X-Content-Type-Options"). + Hint("CAR is streamed, gateway may not have the entire thing, unable to calculate total size"). + Equals("nosniff"), + Header("Accept-Ranges"). + Hint("CAR is streamed, gateway may not have the entire thing, unable to support range-requests. Partial downloads and resumes should be handled using IPLD selectors: https://github.com/ipfs/go-ipfs/issues/8769"). + Equals("none"), + ) + return test +} + +func checkBothFormatAndAcceptHeaderCAR(t *testing.T, testWithFormatParam SugarTest) SugarTests { + t.Helper() + + formatParamReq := testWithFormatParam.Request + expected := testWithFormatParam.Response + + carFormatQueryParams, found := formatParamReq.Query_["format"] + if !found { + t.Fatal("could not find 'format' query parameter") + } + + if len(carFormatQueryParams) != 1 { + t.Fatal("only using a format parameter is supported") + } + carFormatQueryParam := carFormatQueryParams[0] + + acceptHeaderReq := formatParamReq.Clone() + delete(acceptHeaderReq.Query_, "format") + + return SugarTests{ + { + Name: fmt.Sprintf("%s (format=car)", testWithFormatParam.Name), + Hint: fmt.Sprintf("%s\n%s", testWithFormatParam.Hint, "Request using format=car"), + Request: formatParamReq, + Response: expected, + }, + { + Name: fmt.Sprintf("%s (Accept Header)", testWithFormatParam.Name), + Hint: fmt.Sprintf("%s\n%s", testWithFormatParam.Hint, "Request using an Accept header"), + Request: acceptHeaderReq. + Headers( + Header("Accept", transformCARFormatParameterToAcceptHeader(t, carFormatQueryParam)), + ), + Response: expected, + }, + } +} + +func transformCARFormatParameterToAcceptHeader(t *testing.T, param string) string { + if param == "car" { + return "application/vnd.ipld.car" + } + t.Fatalf("can only convert the CAR format parameter to an accept header. Got %q", param) + return "" }