From c67b7df2dee0806b29c85cb378ca306e617a80e6 Mon Sep 17 00:00:00 2001 From: galargh Date: Fri, 17 Mar 2023 19:48:06 +0100 Subject: [PATCH] refactor: entrypoint - add support for go test args to the entrypoint - update Go version to 1.20 - rename is-subdomain to subdomain-gateway-spec - add merged as argument to the extract-fixtures command - rename entrypoint as gateway-conformance - rename tests package from main to tests - rename build tag to no_subdomain_gateway_spec - add a utility function for accessing GATEWAY_CONFORMANCE_HOME --- .github/actions/extract-fixtures/action.yml | 7 +- .github/actions/test/action.yml | 26 +-- .github/workflows/test.yml | 3 +- .gitignore | 1 + Dockerfile | 10 +- Makefile | 20 +- entrypoint.go | 197 +++++++++++++------- fixtures/dir/ascii.txt | 1 - tests/t0113_gateway_symlink_test.go | 2 +- tests/t0114_gateway_subdomains_test.go | 6 +- tests/t0116_gateway_cache_test.go | 2 +- tests/t0117_gateway_block_test.go | 2 +- tests/t0118_gateway_car_test.go | 4 +- tests/t0122_gateway_tar_test.go | 2 +- tests/t0123_gateway_json_cbor_test.go | 2 +- tests/t0400_api_no_gateway_test.go | 2 +- tooling/car/unixfs.go | 7 +- tooling/cmd/merge_fixtures.go | 60 ++---- tooling/env.go | 17 ++ tooling/fixtures/list.go | 43 +++++ tooling/test/sugar.go | 2 +- 21 files changed, 254 insertions(+), 162 deletions(-) delete mode 100644 fixtures/dir/ascii.txt create mode 100644 tooling/env.go create mode 100644 tooling/fixtures/list.go diff --git a/.github/actions/extract-fixtures/action.yml b/.github/actions/extract-fixtures/action.yml index dd8be0134..4b358cabc 100644 --- a/.github/actions/extract-fixtures/action.yml +++ b/.github/actions/extract-fixtures/action.yml @@ -6,6 +6,10 @@ inputs: description: 'The path where the fixtures will be saved' required: true default: 'fixtures' + merged: + description: 'Whether the fixtures should be merged into a single file' + required: false + default: 'false' runs: using: 'composite' steps: @@ -15,8 +19,9 @@ runs: uses: pl-strflt/docker-container-action@v1 env: OUTPUT: ${{ inputs.output }} + MERGED: ${{ inputs.merged }} with: repository: ${{ steps.github.outputs.action_repository }} ref: ${{ steps.github.outputs.action_ref }} dockerfile: Dockerfile - args: extract-fixtures "$OUTPUT" + args: extract-fixtures --directory="$OUTPUT" --merged="$MERGED" diff --git a/.github/actions/test/action.yml b/.github/actions/test/action.yml index d6dedd237..ffd3425fa 100644 --- a/.github/actions/test/action.yml +++ b/.github/actions/test/action.yml @@ -18,38 +18,30 @@ inputs: markdown: description: "The path where the Markdown report will be saved" required: false - is-subdomain: - description: "Enable the test suite for subdomain gateways" + subdomain-gateway-spec: + description: "Whether the gateway implements the subdomain gateway spec" + required: false + default: "true" + args: + description: "The arguments to pass to the test command" required: false runs: using: "composite" steps: - id: github uses: pl-strflt/docker-container-action/.github/actions/github@v1 - - id: generate-args - name: Generate additional arguments - shell: bash - env: - IS_SUBDOMAIN: ${{ inputs.is-subdomain }} - run: | - ARGS="" - if [ "$IS_SUBDOMAIN" = "true" ]; then - ARGS="$ARGS --is-subdomain" - fi - - echo "args=${ARGS}" >> "$GITHUB_OUTPUT" - name: Run the test uses: pl-strflt/docker-container-action@v1 env: - GATEWAY_URL: ${{ inputs.gateway-url }} + URL: ${{ inputs.gateway-url }} JSON: ${{ inputs.json }} - ADDITIONAL_ARGS: ${{ steps.generate-args.outputs.args }} + SUBDOMAIN: ${{ inputs.subdomain-gateway-spec }} with: repository: ${{ steps.github.outputs.action_repository }} ref: ${{ steps.github.outputs.action_ref }} dockerfile: Dockerfile opts: --network=host - args: test --gateway-url "$GATEWAY_URL" --json-output "$JSON" ${ADDITIONAL_ARGS} + args: test --url="$URL" --json="$JSON" --subdomain="$SUBDOMAIN" -- ${{ inputs.args }} - name: Create the XML if: (inputs.xml || inputs.html || inputs.markdown) && (failure() || success()) uses: pl-strflt/gotest-json-to-junit-xml@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 28111098d..1824af752 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,7 +34,8 @@ jobs: xml: output.xml html: output.html markdown: output.md - is-subdomain: false + subdomain-gateway-spec: false + args: -skip TestGatewayCar - name: Set summary if: (failure() || success()) run: cat ./output.md >> $GITHUB_STEP_SUMMARY diff --git a/.gitignore b/.gitignore index 3eed21d8b..b76d55ad7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /fixtures.car /merge-fixtures /entrypoint +/gateway-conformance # Logs logs diff --git a/Dockerfile b/Dockerfile index 919e8bf1e..4e265264d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,11 @@ -FROM golang:1.19.1-buster +FROM golang:1.20-alpine WORKDIR /app -ENV TEST_PATH=/app - -RUN go install gotest.tools/gotestsum@v1.9.0 +ENV GATEWAY_CONFORMANCE_HOME=/app COPY ./go.mod ./go.sum ./ RUN go mod download COPY . . -RUN go build -o /entrypoint ./entrypoint.go +RUN go build -o ./gateway-conformance ./entrypoint.go -ENTRYPOINT ["/entrypoint"] +ENTRYPOINT ["/app/gateway-conformance"] diff --git a/Makefile b/Makefile index 744b035a5..988f383b1 100644 --- a/Makefile +++ b/Makefile @@ -15,18 +15,18 @@ merge-fixtures: go build -o merge-fixtures ./tooling/cmd/merge_fixtures.go # tools -fixtures.car: entrypoint - ./entrypoint merge-fixtures ./fixtures.car +fixtures.car: gateway-conformance + ./gateway-conformance extract-fixtures --merged=true --dir=. -entrypoint: - go build -o ./entrypoint ./entrypoint.go +gateway-conformance: + go build -o ./gateway-conformance ./entrypoint.go -_test: fixtures.car entrypoint - ./entrypoint test --json-output output.json --gateway-url ${GATEWAY_URL} --is-subdomain +_test: fixtures.car gateway-conformance + ./gateway-conformance test --json output.json --gateway-url ${GATEWAY_URL} -test-docker: fixtures.car entrypoint - docker build -t gway-test . - docker run --rm -v "${PWD}:/workspace" -w "/workspace" --network=host gway-test test +test-docker: fixtures.car gateway-conformance + docker build -t gateway-conformance . + docker run --rm -v "${PWD}:/workspace" -w "/workspace" --network=host gateway-conformance test output.xml: test-kubo docker run --rm -v "${PWD}:/workspace" -w "/workspace" --entrypoint "/bin/bash" ghcr.io/pl-strflt/saxon:v1 -c """ @@ -37,4 +37,4 @@ output.html: output.xml docker run --rm -v "${PWD}:/workspace" -w "/workspace" ghcr.io/pl-strflt/saxon:v1 -s:output.xml -xsl:/etc/junit-noframes-saxon.xsl -o:output.html open ./output.html -.PHONY: entrypoint +.PHONY: gateway-conformance diff --git a/entrypoint.go b/entrypoint.go index eb44c8167..f45e2d62d 100644 --- a/entrypoint.go +++ b/entrypoint.go @@ -1,119 +1,190 @@ package main import ( + "bytes" "fmt" + "io" "log" "os" "os/exec" "path/filepath" "strings" + "github.com/ipfs/gateway-conformance/tooling" "github.com/ipfs/gateway-conformance/tooling/cmd" - + "github.com/ipfs/gateway-conformance/tooling/fixtures" "github.com/urfave/cli/v2" ) +type event struct { + Action string + Test string `json:",omitempty"` +} + +type out struct { + Writer io.Writer +} + +func (o out) Write(p []byte) (n int, err error) { + os.Stdout.Write(p) + return o.Writer.Write(p) +} + +func copyFiles(inputPaths []string, outputDirectoryPath string) error { + err := os.MkdirAll(outputDirectoryPath, 0755) + if err != nil { + return err + } + for _, inputPath := range inputPaths { + outputPath := filepath.Join(outputDirectoryPath, filepath.Base(inputPath)) + src, err := os.Open(inputPath) + if err != nil { + return err + } + defer src.Close() + dst, err := os.Create(outputPath) + if err != nil { + return err + } + defer dst.Close() + _, err = io.Copy(dst, src) + if err != nil { + return err + } + } + return nil +} + func main() { - var subdomain bool var gatewayURL string var jsonOutput string + var subdomainGatewaySpec bool + var directory string + var merged bool app := &cli.App{ - Name: "entrypoint", + Name: "gateway-conformance", Usage: "Tooling for the gateway test suite", Commands: []*cli.Command{ { - Name: "test", + Name: "test", Aliases: []string{"t"}, - Usage: "Run the conformance test suite against your gateway", + Usage: "Run the conformance test suite against your gateway", Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "is-subdomain", - Aliases: []string{"s"}, - Usage: "Run the testsuite for subdomain gateways", - Value: false, - Destination: &subdomain, - }, &cli.StringFlag{ Name: "gateway-url", - Aliases: []string{"g"}, + Aliases: []string{"url", "g"}, Usage: "The URL of the gateway to test", Value: "http://localhost:8080", Destination: &gatewayURL, }, &cli.StringFlag{ Name: "json-output", - Aliases: []string{"j"}, + Aliases: []string{"json", "j"}, Usage: "The path to the JSON output file", - Value: "results.json", + Value: "", Destination: &jsonOutput, }, + &cli.BoolFlag{ + Name: "subdomain-gateway-spec", + Aliases: []string{"subdomain-gateway", "subdomain"}, + Usage: "Whether the gateway implements the subdomain gateway spec", + Value: true, + Destination: &subdomainGatewaySpec, + }, }, Action: func(cCtx *cli.Context) error { - // Capture the output path, we run the tests in a different folder. - jsonOutputAbs, err := filepath.Abs(jsonOutput) - if err != nil { - panic(err) - } + args := []string{"test", "./tests", "-test.v=test2json"} - testTagsList := []string{} - if subdomain { - testTagsList = append(testTagsList, "test_subdomains") + tags := []string{} + if !subdomainGatewaySpec { + tags = append(tags, "no_subdomain_gateway_spec") } - testTags := strings.Join(testTagsList, ",") - - // run gotestsum --jsonfile ${...} ./tests -tags="${testTags}" - cmd := exec.Command("gotestsum", "--jsonfile", jsonOutputAbs, "./tests", "-tags="+testTags) - cmd.Env = append(os.Environ(), "GATEWAY_URL="+gatewayURL) - - // if environ containts "TEST_PATH" then use its value in cmd.Dir - if testPath, ok := os.LookupEnv("TEST_PATH"); ok { - cmd.Dir = testPath + if len(tags) > 0 { + args = append(args, "-tags", strings.Join(tags, ",")) } - cmd.Stdout = os.Stdout - - fmt.Printf("running: %s\n", cmd.String()) - err = cmd.Run() - return err + + args = append(args, cCtx.Args().Slice()...) + + fmt.Println("go " + strings.Join(args, " ")) + + output := &bytes.Buffer{} + cmd := exec.Command("go", args...) + cmd.Dir = tooling.Home() + cmd.Env = append(os.Environ(), fmt.Sprintf("GATEWAY_URL=%s", gatewayURL)) + cmd.Stdout = out{output} + cmd.Stderr = os.Stderr + testErr := cmd.Run() + + if jsonOutput != "" { + json := &bytes.Buffer{} + cmd = exec.Command("go", "tool", "test2json", "-p", "Gateway Tests", "-t") + cmd.Stdin = output + cmd.Stdout = json + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + return err + } + // write jsonOutput to json file + f, err := os.Create(jsonOutput) + if err != nil { + return err + } + defer f.Close() + _, err = f.Write(json.Bytes()) + if err != nil { + return err + } + } + + return testErr }, }, { - Name: "extract-fixtures", + Name: "extract-fixtures", Aliases: []string{"e"}, - Usage: "Extract gateway testing fixture that is used by the conformance test suite", + Usage: "Extract gateway testing fixtures that are used by the conformance test suite", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "directory", + Aliases: []string{"dir"}, + Usage: "The directory to extract the fixtures to", + Required: true, + Destination: &directory, + }, + &cli.BoolFlag{ + Name: "merged", + Usage: "Merge the fixtures into a single CAR file", + Value: false, + Destination: &merged, + }, + }, Action: func(cCtx *cli.Context) error { - output := cCtx.Args().First() - if output == "" { - return fmt.Errorf("output path is required") + err := os.MkdirAll(directory, 0755) + if err != nil { + return err } - // mkdir -p output: - err := os.MkdirAll(output, 0755) + files, err := fixtures.List() if err != nil { return err } - // run shell command: `find /app/fixtures -name '*.car' -exec cp {} "${2}/" \;` - cmd := exec.Command("find", "/app/fixtures", "-name", "*.car", "-exec", "cp", "{}", output+"/", ";") - err = cmd.Run() - - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - - return err - }, - }, - { - Name: "merge-fixtures", - Aliases: []string{"m"}, - Usage: "Merge all the fixtures into a single CAR file", - Action: func(cCtx *cli.Context) error { - output := cCtx.Args().First() - if output == "" { - return fmt.Errorf("output path is required") + merged := cCtx.Bool("merged") + if merged { + err = cmd.Merge(filepath.Join(directory, "fixtures.car")) + if err != nil { + return err + } + } else { + err = copyFiles(files, directory) + if err != nil { + return err + } } - return cmd.MergeFixtures(output) + return nil }, }, }, diff --git a/fixtures/dir/ascii.txt b/fixtures/dir/ascii.txt deleted file mode 100644 index 8b7f203ca..000000000 --- a/fixtures/dir/ascii.txt +++ /dev/null @@ -1 +0,0 @@ -goodbye application/vnd.ipld.raw diff --git a/tests/t0113_gateway_symlink_test.go b/tests/t0113_gateway_symlink_test.go index d8abde270..54ffb51f2 100644 --- a/tests/t0113_gateway_symlink_test.go +++ b/tests/t0113_gateway_symlink_test.go @@ -1,4 +1,4 @@ -package main +package tests import ( "fmt" diff --git a/tests/t0114_gateway_subdomains_test.go b/tests/t0114_gateway_subdomains_test.go index 4d899c32e..1b30173c6 100644 --- a/tests/t0114_gateway_subdomains_test.go +++ b/tests/t0114_gateway_subdomains_test.go @@ -1,7 +1,7 @@ -//go:build test_subdomains -// +build test_subdomains +//go:build !no_subdomain_gateway_spec +// +build !no_subdomain_gateway_spec -package main +package tests import ( "fmt" diff --git a/tests/t0116_gateway_cache_test.go b/tests/t0116_gateway_cache_test.go index 1fbc0755c..caf1d9f37 100644 --- a/tests/t0116_gateway_cache_test.go +++ b/tests/t0116_gateway_cache_test.go @@ -1,4 +1,4 @@ -package main +package tests import ( "fmt" diff --git a/tests/t0117_gateway_block_test.go b/tests/t0117_gateway_block_test.go index b58ebe12e..34299496e 100644 --- a/tests/t0117_gateway_block_test.go +++ b/tests/t0117_gateway_block_test.go @@ -1,4 +1,4 @@ -package main +package tests import ( "fmt" diff --git a/tests/t0118_gateway_car_test.go b/tests/t0118_gateway_car_test.go index 5b22ce1de..ef780548c 100644 --- a/tests/t0118_gateway_car_test.go +++ b/tests/t0118_gateway_car_test.go @@ -1,6 +1,5 @@ -package main +package tests -/* Skip until https://github.com/ipfs/kubo/issues/9651 is resolved import ( "fmt" "testing" @@ -78,4 +77,3 @@ func TestGatewayCar(t *testing.T) { Run(t, tests) } -*/ diff --git a/tests/t0122_gateway_tar_test.go b/tests/t0122_gateway_tar_test.go index 6944d99f4..e5369b45a 100644 --- a/tests/t0122_gateway_tar_test.go +++ b/tests/t0122_gateway_tar_test.go @@ -1,4 +1,4 @@ -package main +package tests import ( "testing" diff --git a/tests/t0123_gateway_json_cbor_test.go b/tests/t0123_gateway_json_cbor_test.go index 22803b468..01fff9e51 100644 --- a/tests/t0123_gateway_json_cbor_test.go +++ b/tests/t0123_gateway_json_cbor_test.go @@ -1,4 +1,4 @@ -package main +package tests import ( "testing" diff --git a/tests/t0400_api_no_gateway_test.go b/tests/t0400_api_no_gateway_test.go index 81df4e340..f4c9cb2a5 100644 --- a/tests/t0400_api_no_gateway_test.go +++ b/tests/t0400_api_no_gateway_test.go @@ -1,4 +1,4 @@ -package main +package tests import ( "testing" diff --git a/tooling/car/unixfs.go b/tooling/car/unixfs.go index 1621a0358..7a3496a4c 100644 --- a/tooling/car/unixfs.go +++ b/tooling/car/unixfs.go @@ -5,10 +5,9 @@ import ( "fmt" "os" "path" - "path/filepath" - "runtime" "strings" + "github.com/ipfs/gateway-conformance/tooling/fixtures" "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" format "github.com/ipfs/go-ipld-format" @@ -94,9 +93,7 @@ func (d *UnixfsDag) MustGetRawData(names ...string) []byte { } func MustOpenUnixfsCar(file string) *UnixfsDag { - _, filename, _, _ := runtime.Caller(0) - basePath := filepath.Dir(filename) - fixturePath := path.Join(basePath, "..", "..", "fixtures", file) + fixturePath := path.Join(fixtures.Dir(), file) dag, err := newUnixfsDagFromCar(fixturePath) if err != nil { diff --git a/tooling/cmd/merge_fixtures.go b/tooling/cmd/merge_fixtures.go index a0c4d3cb8..b79b6e505 100644 --- a/tooling/cmd/merge_fixtures.go +++ b/tooling/cmd/merge_fixtures.go @@ -3,54 +3,24 @@ package cmd import ( "context" "fmt" - "os" - "path/filepath" - "time" + "github.com/ipfs/gateway-conformance/tooling/fixtures" "github.com/ipfs/go-cid" "github.com/ipld/go-car/v2/blockstore" ) -/** - * list all `*.car` file in the basePath directory, recursively - */ -func listAllCarFile(basePath string) []string { - var carFiles []string - - filepath.WalkDir(basePath, func(path string, d os.DirEntry, err error) error { - if err != nil { - return err - } - if d.IsDir() { - return nil - } - if filepath.Ext(path) == ".car" { - path, err := filepath.Abs(path) - if err != nil { - return err - } - - carFiles = append(carFiles, path) - } - - return nil - }) - - return carFiles -} - -func MergeFixtures(outputPath string) error { - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - carFiles := listAllCarFile("./fixtures") +func Merge(outputPath string) error { + inputPaths, err := fixtures.List() + if err != nil { + return err + } // First list all the roots in our fixtures roots := make([]cid.Cid, 0) - for _, f := range carFiles { - fmt.Printf("processing %s\n", f) - robs, err := blockstore.OpenReadOnly(f, + for _, path := range inputPaths { + fmt.Printf("processing %s\n", path) + robs, err := blockstore.OpenReadOnly(path, blockstore.UseWholeCIDs(true), ) if err != nil { @@ -73,28 +43,28 @@ func MergeFixtures(outputPath string) error { } // Then aggregate all our blocks. - for _, f := range carFiles { - fmt.Printf("processing %s\n", f) - robs, err := blockstore.OpenReadOnly(f, + for _, path := range inputPaths { + fmt.Printf("processing %s\n", path) + robs, err := blockstore.OpenReadOnly(path, blockstore.UseWholeCIDs(true), ) if err != nil { return err } - cids, err := robs.AllKeysChan(ctx) + cids, err := robs.AllKeysChan(context.Background()) if err != nil { return err } for c := range cids { fmt.Printf("Adding %s\n", c.String()) - block, err := robs.Get(ctx, c) + block, err := robs.Get(context.Background(), c) if err != nil { return err } - rout.Put(ctx, block) + rout.Put(context.Background(), block) } } diff --git a/tooling/env.go b/tooling/env.go new file mode 100644 index 000000000..cb0765cd7 --- /dev/null +++ b/tooling/env.go @@ -0,0 +1,17 @@ +package tooling + +import ( + "os" + "path/filepath" + "runtime" +) + +func Home() string { + home := os.Getenv("GATEWAY_CONFORMANCE_HOME") + if home == "" { + _, filename, _, _ := runtime.Caller(0) + basePath := filepath.Dir(filename) + return filepath.Join(basePath, "..") + } + return home +} diff --git a/tooling/fixtures/list.go b/tooling/fixtures/list.go new file mode 100644 index 000000000..d58cbec83 --- /dev/null +++ b/tooling/fixtures/list.go @@ -0,0 +1,43 @@ +package fixtures + +import ( + "os" + "path" + "path/filepath" + + "github.com/ipfs/gateway-conformance/tooling" +) + +func Dir() string { + home := tooling.Home() + return path.Join(home, "fixtures") +} + +func List() ([]string, error) { + var carFiles []string + + err := filepath.WalkDir(Dir(), func(path string, d os.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + return nil + } + if filepath.Ext(path) == ".car" { + path, err := filepath.Abs(path) + if err != nil { + return err + } + + carFiles = append(carFiles, path) + } + + return nil + }) + + if err != nil { + return nil, err + } + + return carFiles, nil +} diff --git a/tooling/test/sugar.go b/tooling/test/sugar.go index 728ecfaac..2b47100dd 100644 --- a/tooling/test/sugar.go +++ b/tooling/test/sugar.go @@ -126,4 +126,4 @@ func (h HeaderBuilder) Equals(value string, args ...any) HeaderBuilder { func (h HeaderBuilder) IsEmpty() HeaderBuilder { h.Check = check.CheckIsEmpty{} return h -} \ No newline at end of file +}