diff --git a/tests/t0118_gateway_car_test.go b/tests/t0118_gateway_car_test.go index 590250bb5..9ab4aaac6 100644 --- a/tests/t0118_gateway_car_test.go +++ b/tests/t0118_gateway_car_test.go @@ -119,3 +119,174 @@ func TestGatewayCar(t *testing.T) { transformedTests := helpers.StandardCARTestTransforms(t, tests) Run(t, transformedTests) } + +func TestGatewayCarEntityBytes(t *testing.T) { + multiBlockFileInDirFixture := car.MustOpenUnixfsCar("t0118-multiblock-file-in-directory.car") + fixture := multiBlockFileInDirFixture + + tests := SugarTests{ + { + Name: "GET CAR with entity-bytes for a full UnixFS file", + Hint: ` + dag-scope=entity&entity-bytes=0:* should return a CAR file with all the blocks needed to 'cat' + the full UnixFS file at the end of the specified path + `, + Request: Request(). + Path("ipfs/{{cid}}", multiBlockFileInDirFixture.MustGetCid("multiblock.txt")). + Query("format", "car"). + Query("dag-scope", "entity"). + Query("entity-bytes", "0:*"), + Response: Expect(). + Status(200). + Body( + IsCar(). + HasRoot(multiBlockFileInDirFixture.MustGetCid()). + HasBlocks(flattenStrings(t, + multiBlockFileInDirFixture.MustGetCid(), + multiBlockFileInDirFixture.MustGetCid("multiblock.txt"), + multiBlockFileInDirFixture.MustGetChildrenCids("multiblock.txt"), + )...). + Exactly(). + InThatOrder(), + ), + }, + { + Name: "GET CAR with entity-bytes for a UnixFS directory", + Hint: ` + dag-scope=entity&entity-bytes=X:Y should return a CAR file with all the blocks needed to 'ls' + a UnixFS directory at the end of the specified path if the terminal element is a directory + (i.e. entity-bytes is effectively optional if the entity is not a file) + `, + Request: Request(). + Path("ipfs/{{cid}}", fixture.MustGetCid()). + Query("format", "car"). + Query("dag-scope", "entity"), + Response: Expect(). + Status(200). + Body( + IsCar(). + HasRoot(fixture.MustGetCid()). + HasBlocks( + fixture.MustGetCid(), + ). + Exactly(). + InThatOrder(), + ), + }, + { + Name: "GET CAR with entity-bytes equivalent to a HTTP Range Request from the middle of a file to the end", + Hint: ` + The response MUST contain only the minimal set of blocks necessary for fulfilling the range request + `, + Request: Request(). + Path("ipfs/{{cid}}", fixture.MustGetCid()). + Query("format", "car"). + Query("dag-scope", "entity"), + Response: Expect(). + Status(200). + Body( + IsCar(). + HasRoot(fixture.MustGetCid()). + HasBlocks( + fixture.MustGetCid(), + ). + Exactly(). + InThatOrder(), + ), + }, + { + Name: "GET CAR with entity-bytes equivalent to a HTTP Range Request for the middle of a large file", + Hint: ` + The response MUST contain only the minimal set of blocks necessary for fulfilling the range request + `, + Request: Request(). + Path("ipfs/{{cid}}", fixture.MustGetCid()). + Query("format", "car"). + Query("dag-scope", "entity"), + Response: Expect(). + Status(200). + Body( + IsCar(). + HasRoot(fixture.MustGetCid()). + HasBlocks( + fixture.MustGetCid(), + ). + Exactly(). + InThatOrder(), + ), + }, + { + Name: "GET CAR with entity-bytes equivalent to HTTP Suffix Range Request for part of a small file", + Hint: ` + The response MUST contain only the minimal set of blocks necessary for fulfilling the range request + `, + Request: Request(). + Path("ipfs/{{cid}}", fixture.MustGetCid()). + Query("format", "car"). + Query("dag-scope", "entity"), + Response: Expect(). + Status(200). + Body( + IsCar(). + HasRoot(fixture.MustGetCid()). + HasBlocks( + fixture.MustGetCid(), + ). + Exactly(). + InThatOrder(), + ), + }, + { + Name: "GET CAR with entity-bytes requesting a range from the end of a file", + Hint: ` + The response MUST contain only the minimal set of blocks necessary for fulfilling the range request + `, + Request: Request(). + Path("ipfs/{{cid}}", fixture.MustGetCid()). + Query("format", "car"). + Query("dag-scope", "entity"), + Response: Expect(). + Status(200). + Body( + IsCar(). + HasRoot(fixture.MustGetCid()). + HasBlocks( + fixture.MustGetCid(), + ). + Exactly(). + InThatOrder(), + ), + }, + } + + transformedTests := helpers.StandardCARTestTransforms(t, tests) + Run(t, transformedTests) +} + +func flattenStrings(t *testing.T, values ...interface{}) []string { + var res []string + for _, v := range values { + switch tv := v.(type) { + case string: + res = append(res, tv) + case []string: + res = append(res, tv...) + default: + t.Fatal("only strings and string slices supported, this should be unreachable") + } + } + return res +} + +/* +- TODO(gateway-conformance): `/ipfs/dag-pb-file?format=car&entity-bytes=40000000000-40000000002` + + - Request a byte range from the middle of a big UnixFS `file`. The response MUST + contain only the minimal set of blocks necessary for fullfilling the range + request. + +- TODO(gateway-conformance): `/ipfs/10-bytes-cid?format=car&entity-bytes=4:-2` + + - Request a byte range from the middle of a small file, to -2 bytes from the end. + - (TODO confirm we want keep this -- added since it was explicitly stated as a supported thing in path-gateway.md) +*/