Skip to content

Commit

Permalink
feat: test CAR against both format parameter and accept header
Browse files Browse the repository at this point in the history
  • Loading branch information
aschmahmann committed May 17, 2023
1 parent 2fa123f commit d544f28
Showing 1 changed file with 79 additions and 20 deletions.
99 changes: 79 additions & 20 deletions tests/t0118_gateway_car_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tests

import (
"fmt"
"testing"

"github.com/ipfs/gateway-conformance/tooling/car"
Expand All @@ -13,35 +14,24 @@ 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
we will get a CAR that is a deterministic array of bytes.
`,
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=\"<cid>.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()).
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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 ""
}

0 comments on commit d544f28

Please sign in to comment.