From cb5e526e06f92c5a1785036673f2d2f24f143781 Mon Sep 17 00:00:00 2001 From: Daniel Baptista Dias Date: Fri, 12 May 2023 11:25:45 -0300 Subject: [PATCH] Creating CLI e2e tests (#2494) * Moving CLI and Server external tests to 'testing' folder * wip - First draft of e2e test * Structuring tests * Adding cli e2e tests on CI * Fixing e2e tests * Updating CI command * Improved internal CLI e2e structure * Update Github action script * Fixing runtime * Adding tests and fixing error on CLI * Fixing apply command * Fixing test behavior * Fixing test * Adding command to avoid test cache * Fix datastore to consider new behavior * Updated tests --- .github/workflows/pull-request.yaml | 25 ++++ cli/actions/datastore.go | 7 +- cli/file/definition.go | 4 + cli/formatters/datastore.go | 4 +- cli/formatters/yaml.go | 2 +- cli/go.mod | 8 +- cli/go.sum | 18 +++ cli/utils/api.go | 32 +++-- go.work | 1 + go.work.sum | 11 +- server/go.mod | 2 +- server/go.sum | 7 +- testing/README.md | 1 + testing/cli-e2etest/Makefile | 8 ++ testing/cli-e2etest/command/exec.go | 68 +++++++++ .../environment/jaeger/cli-config.yaml | 3 + .../jaeger/resources/data-store.yaml | 11 ++ .../jaeger/server-setup/collector.config.yaml | 27 ++++ .../jaeger/server-setup/docker-compose.yaml | 66 +++++++++ .../jaeger/server-setup/tracetest-config.yaml | 21 +++ .../server-setup/tracetest-provision.yaml | 9 ++ testing/cli-e2etest/environment/manager.go | 131 ++++++++++++++++++ testing/cli-e2etest/go.mod | 17 +++ testing/cli-e2etest/go.sum | 35 +++++ testing/cli-e2etest/helpers/common.go | 51 +++++++ testing/cli-e2etest/main.go | 8 ++ .../datastore/apply_new_datastore_test.go | 54 ++++++++ .../datastore/delete_datastore_test.go | 59 ++++++++ .../datastore/list_datastore_test.go | 67 +++++++++ .../cli-e2etest/testscenarios/help_test.go | 25 ++++ .../cli-e2etest/testscenarios/types/types.go | 12 ++ .../cli-e2etest/testscenarios/version_test.go | 21 +++ testing/cli-e2etest/tracetestcli/exec.go | 57 ++++++++ 33 files changed, 840 insertions(+), 32 deletions(-) create mode 100644 testing/cli-e2etest/Makefile create mode 100644 testing/cli-e2etest/command/exec.go create mode 100644 testing/cli-e2etest/environment/jaeger/cli-config.yaml create mode 100644 testing/cli-e2etest/environment/jaeger/resources/data-store.yaml create mode 100644 testing/cli-e2etest/environment/jaeger/server-setup/collector.config.yaml create mode 100644 testing/cli-e2etest/environment/jaeger/server-setup/docker-compose.yaml create mode 100644 testing/cli-e2etest/environment/jaeger/server-setup/tracetest-config.yaml create mode 100644 testing/cli-e2etest/environment/jaeger/server-setup/tracetest-provision.yaml create mode 100644 testing/cli-e2etest/environment/manager.go create mode 100644 testing/cli-e2etest/go.mod create mode 100644 testing/cli-e2etest/go.sum create mode 100644 testing/cli-e2etest/helpers/common.go create mode 100644 testing/cli-e2etest/main.go create mode 100644 testing/cli-e2etest/testscenarios/datastore/apply_new_datastore_test.go create mode 100644 testing/cli-e2etest/testscenarios/datastore/delete_datastore_test.go create mode 100644 testing/cli-e2etest/testscenarios/datastore/list_datastore_test.go create mode 100644 testing/cli-e2etest/testscenarios/help_test.go create mode 100644 testing/cli-e2etest/testscenarios/types/types.go create mode 100644 testing/cli-e2etest/testscenarios/version_test.go create mode 100644 testing/cli-e2etest/tracetestcli/exec.go diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 66ccef0d2a..1d77df8705 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -292,6 +292,31 @@ jobs: TEST_ENV="${{ matrix.test_env }}" \ ./run.bash + e2e-cli: + name: CLI e2e tests + needs: [build-docker] + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - name: Checkout + uses: actions/checkout@v3 + - uses: actions/download-artifact@v3 + with: + name: tracetest-go + path: dist/ + - name: Run tests + run: | + find ./dist -name 'tracetest' -exec cp {} ./dist \; + chmod +x ./dist/tracetest + + export TRACETEST_COMMAND=$PWD/dist/tracetest + export TEST_ENVIRONMENT=jaeger + export TAG=pr-${{ github.event.pull_request.number }} + + cd ./testing/cli-e2etest + make test + deploy: name: Deploy test infra needs: [build-docker] diff --git a/cli/actions/datastore.go b/cli/actions/datastore.go index d0606682f4..6bf73763bf 100644 --- a/cli/actions/datastore.go +++ b/cli/actions/datastore.go @@ -7,7 +7,6 @@ import ( "github.com/kubeshop/tracetest/cli/openapi" "github.com/kubeshop/tracetest/cli/utils" "github.com/kubeshop/tracetest/server/model/yaml" - "github.com/mitchellh/mapstructure" ) type dataStoreActions struct { @@ -42,11 +41,7 @@ func (d dataStoreActions) GetID(file *file.File) (string, error) { } func (d *dataStoreActions) Apply(ctx context.Context, fileContent file.File) (result *file.File, err error) { - var dataStore openapi.DataStore - mapstructure.Decode(fileContent.Definition().Spec, &dataStore) - - result, err = d.resourceClient.Update(ctx, fileContent, currentConfigID) - return result, err + return d.resourceClient.Update(ctx, fileContent, currentConfigID) } func (d *dataStoreActions) List(ctx context.Context, args utils.ListArgs) (*file.File, error) { diff --git a/cli/file/definition.go b/cli/file/definition.go index e7e7e69b56..7670325ac7 100644 --- a/cli/file/definition.go +++ b/cli/file/definition.go @@ -113,6 +113,10 @@ func (f File) Contents() string { return string(f.contents) } +func (f File) ContentType() string { + return "text/yaml" +} + var ( hasIDRegex = regexp.MustCompile(`(?m:^\s+id:\s*[0-9a-zA-Z\-_]+$)`) indentSizeRegex = regexp.MustCompile(`(?m:^(\s+)\w+)`) diff --git a/cli/formatters/datastore.go b/cli/formatters/datastore.go index 8cc79b2704..63ff45c91b 100644 --- a/cli/formatters/datastore.go +++ b/cli/formatters/datastore.go @@ -2,6 +2,7 @@ package formatters import ( "github.com/alexeyco/simpletable" + "github.com/goccy/go-yaml" "github.com/kubeshop/tracetest/cli/file" "github.com/kubeshop/tracetest/cli/openapi" ) @@ -54,9 +55,8 @@ func (f DatastoreFormatter) ToListTable(file *file.File) (*simpletable.Header, * func (f DatastoreFormatter) ToStruct(file *file.File) (interface{}, error) { var datastoreResource openapi.DataStoreResource - nullableDataStore := openapi.NewNullableDataStoreResource(&datastoreResource) - err := nullableDataStore.UnmarshalJSON([]byte(file.Contents())) + err := yaml.Unmarshal([]byte(file.Contents()), &datastoreResource) if err != nil { return nil, err } diff --git a/cli/formatters/yaml.go b/cli/formatters/yaml.go index b8a5352f2a..8fa778563a 100644 --- a/cli/formatters/yaml.go +++ b/cli/formatters/yaml.go @@ -3,7 +3,7 @@ package formatters import ( "fmt" - "gopkg.in/yaml.v2" + "github.com/goccy/go-yaml" "github.com/kubeshop/tracetest/cli/file" ) diff --git a/cli/go.mod b/cli/go.mod index 882687239a..7f6c65bb1f 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -7,12 +7,13 @@ require ( github.com/compose-spec/compose-go v1.5.1 github.com/cucumber/ci-environment/go v0.0.0-20220915001957-711b1c82415f github.com/denisbrodbeck/machineid v1.0.1 - github.com/joho/godotenv v1.3.0 + github.com/goccy/go-yaml v1.11.0 github.com/kubeshop/tracetest/server v0.0.0-20230208220354-63c9594b2160 github.com/mitchellh/mapstructure v1.5.0 github.com/pterm/pterm v0.12.55 github.com/segmentio/analytics-go/v3 v3.2.1 github.com/spf13/cobra v1.6.1 + github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.1 go.uber.org/zap v1.23.0 @@ -30,6 +31,7 @@ require ( github.com/distribution/distribution/v3 v3.0.0-20220907155224-78b9c98c5c31 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/fatih/color v1.13.0 // indirect github.com/fluidtruck/deepcopy v1.0.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-logr/logr v1.2.3 // indirect @@ -48,6 +50,8 @@ require ( github.com/knadh/koanf v1.4.3 // indirect github.com/lithammer/fuzzysearch v1.1.5 // indirect github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -67,7 +71,6 @@ require ( github.com/spf13/afero v1.9.3 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect @@ -87,6 +90,7 @@ require ( golang.org/x/sys v0.6.0 // indirect golang.org/x/term v0.5.0 // indirect golang.org/x/text v0.8.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect google.golang.org/grpc v1.52.0 // indirect google.golang.org/protobuf v1.28.1 // indirect diff --git a/cli/go.sum b/cli/go.sum index 9637f43ad4..5eb20bd8f8 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -131,6 +131,8 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fluidtruck/deepcopy v1.0.0 h1:StSep9r2WwYwDfoO7CWu1a8cZT5y3UUOelGxlSvFxIw= github.com/fluidtruck/deepcopy v1.0.0/go.mod h1:AinXaEHc7itYqWSjX9OOwxBbM8/ELusxM8WSwP4RroM= @@ -155,8 +157,13 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54= +github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -317,6 +324,7 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kubeshop/tracetest/server v0.0.0-20230208220354-63c9594b2160 h1:vSWsqho5dJxAJmrgKc1RClvjD4UpP9TlUNyCkiGkOnM= github.com/kubeshop/tracetest/server v0.0.0-20230208220354-63c9594b2160/go.mod h1:fytz7/+EWBsIBNfS7MKrdDRQ2Dq4Ck+P2Z3jzvpDFY8= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMrOBEp1c= github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -324,11 +332,16 @@ github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3v github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= @@ -525,6 +538,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -680,6 +694,8 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -762,6 +778,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/cli/utils/api.go b/cli/utils/api.go index b44e10bbb1..bbc6d4076b 100644 --- a/cli/utils/api.go +++ b/cli/utils/api.go @@ -44,7 +44,6 @@ func GetAPIClient(cliConfig config.Config) *openapi.APIClient { type ResourceClient struct { Client http.Client BaseUrl string - BaseHeader http.Header ResourceType string } @@ -61,20 +60,15 @@ func GetResourceAPIClient(resourceType string, cliConfig config.Config) Resource } baseUrl := fmt.Sprintf("%s://%s/api/%s", cliConfig.Scheme, cliConfig.Endpoint, resourceType) - baseHeader := http.Header{ - "x-client-id": []string{analytics.ClientID()}, - "Content-Type": []string{"application/json"}, - } return ResourceClient{ Client: client, BaseUrl: baseUrl, - BaseHeader: baseHeader, ResourceType: resourceType, } } -func (resourceClient ResourceClient) NewRequest(url string, method string, body string) (*http.Request, error) { +func (resourceClient ResourceClient) NewRequest(url, method, body, contentType string) (*http.Request, error) { var reqBody io.Reader if body != "" { reqBody = StringToIOReader(body) @@ -85,13 +79,20 @@ func (resourceClient ResourceClient) NewRequest(url string, method string, body return nil, err } - request.Header = resourceClient.BaseHeader + if contentType == "" { + contentType = "application/json" + } + + request.Header.Set("x-client-id", analytics.ClientID()) + request.Header.Set("Content-Type", contentType) + request.Header.Set("Accept", contentType) + return request, err } func (resourceClient ResourceClient) Update(ctx context.Context, file file.File, ID string) (*file.File, error) { url := fmt.Sprintf("%s/%s", resourceClient.BaseUrl, ID) - request, err := resourceClient.NewRequest(url, http.MethodPut, file.Contents()) + request, err := resourceClient.NewRequest(url, http.MethodPut, file.Contents(), file.ContentType()) if err != nil { return nil, fmt.Errorf("could not create request: %w", err) } @@ -117,6 +118,11 @@ func (resourceClient ResourceClient) Update(ctx context.Context, file file.File, return nil, fmt.Errorf("invalid %s: %s", resourceClient.ResourceType, validationError) } + responseContentType := resp.Header.Get("Content-type") + if responseContentType == "" { + responseContentType = "application/json" + } + file = file.SaveChanges(IOReadCloserToString(resp.Body)) return &file, nil @@ -124,7 +130,7 @@ func (resourceClient ResourceClient) Update(ctx context.Context, file file.File, func (resourceClient ResourceClient) Delete(ctx context.Context, ID string) error { url := fmt.Sprintf("%s/%s", resourceClient.BaseUrl, ID) - request, err := resourceClient.NewRequest(url, http.MethodDelete, "") + request, err := resourceClient.NewRequest(url, http.MethodDelete, "", "") if err != nil { return fmt.Errorf("could not delete resource: %w", err) } @@ -134,7 +140,7 @@ func (resourceClient ResourceClient) Delete(ctx context.Context, ID string) erro } func (resourceClient ResourceClient) Get(ctx context.Context, id string) (*file.File, error) { - request, err := resourceClient.NewRequest(fmt.Sprintf("%s/%s", resourceClient.BaseUrl, id), http.MethodGet, "") + request, err := resourceClient.NewRequest(fmt.Sprintf("%s/%s", resourceClient.BaseUrl, id), http.MethodGet, "", "") if err != nil { return nil, fmt.Errorf("could not create request: %w", err) } @@ -181,7 +187,7 @@ func parseListResponse(body string) (BaseListResponse, error) { func (resourceClient ResourceClient) List(ctx context.Context, listArgs ListArgs) (*file.File, error) { url := fmt.Sprintf("%s?skip=%d&take=%d&sortBy=%s&sortDirection=%s", resourceClient.BaseUrl, listArgs.Skip, listArgs.Take, listArgs.SortBy, listArgs.SortDirection) - request, err := resourceClient.NewRequest(url, http.MethodGet, "") + request, err := resourceClient.NewRequest(url, http.MethodGet, "", "") if err != nil { return nil, fmt.Errorf("could not create request: %w", err) } @@ -218,7 +224,7 @@ func (resourceClient ResourceClient) List(ctx context.Context, listArgs ListArgs } func (resourceClient ResourceClient) Create(ctx context.Context, file file.File) (*file.File, error) { - request, err := resourceClient.NewRequest(resourceClient.BaseUrl, http.MethodPost, file.Contents()) + request, err := resourceClient.NewRequest(resourceClient.BaseUrl, http.MethodPost, file.Contents(), file.ContentType()) if err != nil { return nil, fmt.Errorf("could not create request: %w", err) } diff --git a/go.work b/go.work index ca57d1dcb7..3674264c35 100644 --- a/go.work +++ b/go.work @@ -3,4 +3,5 @@ go 1.20 use ( ./cli ./server + ./testing/cli-e2etest ) diff --git a/go.work.sum b/go.work.sum index b539f12517..c2488af30c 100644 --- a/go.work.sum +++ b/go.work.sum @@ -149,6 +149,9 @@ cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIE cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= +github.com/Azure/azure-sdk-for-go v56.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= +github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -165,9 +168,6 @@ github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= @@ -195,7 +195,6 @@ github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= @@ -239,8 +238,8 @@ go.opentelemetry.io/otel/metric v0.31.0/go.mod h1:ohmwj9KTSIeBnDBm/ZwH2PSZxZzoOa go.opentelemetry.io/otel/sdk v1.9.0/go.mod h1:AEZc8nt5bd2F7BC24J5R0mrjYnpEgYHyTcM/vrSple4= go.opentelemetry.io/otel/sdk/metric v0.31.0/go.mod h1:fl0SmNnX9mN9xgU6OLYLMBMrNAsaZQi7qBwprwO3abk= go.opentelemetry.io/otel/trace v1.9.0/go.mod h1:2737Q0MuG8q1uILYm2YYVkAyLtOofiTNGg6VODnOiPo= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -277,9 +276,11 @@ golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= diff --git a/server/go.mod b/server/go.mod index 3c0d1de857..3ff531a442 100644 --- a/server/go.mod +++ b/server/go.mod @@ -13,6 +13,7 @@ require ( github.com/fluidtruck/deepcopy v1.0.0 github.com/fsnotify/fsnotify v1.6.0 github.com/fullstorydev/grpcurl v1.8.6 + github.com/goccy/go-yaml v1.11.0 github.com/gogo/protobuf v1.3.2 github.com/golang-migrate/migrate/v4 v4.15.2 github.com/golang/protobuf v1.5.2 @@ -76,7 +77,6 @@ require ( github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-redis/redis/v7 v7.4.1 // indirect - github.com/goccy/go-yaml v1.11.0 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect diff --git a/server/go.sum b/server/go.sum index 8e3c878e04..1a994618fc 100644 --- a/server/go.sum +++ b/server/go.sum @@ -691,7 +691,10 @@ github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9G github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0= github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZIVCbJBpTUoY0= github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI= github.com/go-redis/redis/v7 v7.4.1/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= @@ -1140,6 +1143,7 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -1842,6 +1846,7 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2151,8 +2156,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= diff --git a/testing/README.md b/testing/README.md index d61e149c86..e9567d960e 100644 --- a/testing/README.md +++ b/testing/README.md @@ -3,5 +3,6 @@ In this folder, we have some of the test automation structures used to evaluate Tracetest. The tests are: +- **cli-e2etest**: CLI End-to-end tests, where we run a compiled CLI against some setups of Tracetest infrastructure and we check if the CLI commands are properly working; - **cli-smoketest**: simple CLI test, where we check the CLI was correctly compiled and can run simple commands (as `tracetest version`); - **server-tracetesting**: set of [dogfooding](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) tests, where run some trace-based tests against the current version of Tracetest to check if the Tracetest API is working fine. diff --git a/testing/cli-e2etest/Makefile b/testing/cli-e2etest/Makefile new file mode 100644 index 0000000000..b3e5782c1f --- /dev/null +++ b/testing/cli-e2etest/Makefile @@ -0,0 +1,8 @@ +help: Makefile ## show list of commands + @echo "Choose a command run:" + @echo "" + @awk 'BEGIN {FS = ":.*?## "} /[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-40s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort + +test: # run tests for this application + go clean -testcache + go test -timeout 150s ./... diff --git a/testing/cli-e2etest/command/exec.go b/testing/cli-e2etest/command/exec.go new file mode 100644 index 0000000000..6226915bb8 --- /dev/null +++ b/testing/cli-e2etest/command/exec.go @@ -0,0 +1,68 @@ +package command + +import ( + "fmt" + "os/exec" + "strings" +) + +type ExecResult struct { + CommandExecuted string + StdOut string + StdErr string + ExitCode int +} + +func (r *ExecResult) String() string { + return fmt.Sprintf("commandText: [%s] \nexitCode: %d \nstdout: [%s] \nstderr: [%s]", r.CommandExecuted, r.ExitCode, r.StdOut, r.StdErr) +} + +func Exec(command string, args ...string) (*ExecResult, error) { + checkIfCommandExists(command) + + fullCommand := fmt.Sprintf("%s %s", command, strings.Join(args, " ")) + + cmd := exec.Command(command, args...) + + out, err := cmd.Output() + output := string(out) + + if err != nil { + return handleRunError(err, fullCommand, output, cmd) + } + + exitCode := cmd.ProcessState.ExitCode() + return &ExecResult{ + CommandExecuted: fullCommand, + StdOut: output, + ExitCode: exitCode, + }, nil +} + +func checkIfCommandExists(command string) { + path, err := exec.LookPath(command) + if err != nil { + panic(fmt.Sprintf("error when checking if %s exists, error: %s", command, err.Error())) + } + + if path == "" { + panic(fmt.Sprintf("%s was not found on this machine", command)) + } +} + +func handleRunError(err error, fullCommand string, output string, cmd *exec.Cmd) (*ExecResult, error) { + exitError, castOk := err.(*exec.ExitError) + + if !castOk { + return nil, fmt.Errorf("error when executing command: \n%s \nerror: %w", fullCommand, err) + } + + commandResult := &ExecResult{ + CommandExecuted: fullCommand, + StdOut: output, + StdErr: string(exitError.Stderr), + ExitCode: exitError.ExitCode(), + } + + return commandResult, nil +} diff --git a/testing/cli-e2etest/environment/jaeger/cli-config.yaml b/testing/cli-e2etest/environment/jaeger/cli-config.yaml new file mode 100644 index 0000000000..cc15f11ad5 --- /dev/null +++ b/testing/cli-e2etest/environment/jaeger/cli-config.yaml @@ -0,0 +1,3 @@ +scheme: http +endpoint: localhost:11633 +analyticsEnabled: false diff --git a/testing/cli-e2etest/environment/jaeger/resources/data-store.yaml b/testing/cli-e2etest/environment/jaeger/resources/data-store.yaml new file mode 100644 index 0000000000..e0b3b344bf --- /dev/null +++ b/testing/cli-e2etest/environment/jaeger/resources/data-store.yaml @@ -0,0 +1,11 @@ +type: DataStore +spec: + id: current + name: jaeger + type: jaeger + default: true + createdAt: 2023-05-09T18:22:34.840021Z + jaeger: + endpoint: jaeger:16685 + tls: + insecure: true diff --git a/testing/cli-e2etest/environment/jaeger/server-setup/collector.config.yaml b/testing/cli-e2etest/environment/jaeger/server-setup/collector.config.yaml new file mode 100644 index 0000000000..8bd0f3dbd7 --- /dev/null +++ b/testing/cli-e2etest/environment/jaeger/server-setup/collector.config.yaml @@ -0,0 +1,27 @@ +receivers: + otlp: + protocols: + grpc: + http: + +processors: + batch: + timeout: 100ms + + # Data sources: traces + probabilistic_sampler: + hash_seed: 22 + sampling_percentage: 100 + +exporters: + jaeger: + endpoint: jaeger:14250 + tls: + insecure: true + +service: + pipelines: + traces: + receivers: [otlp] + processors: [probabilistic_sampler, batch] + exporters: [jaeger] diff --git a/testing/cli-e2etest/environment/jaeger/server-setup/docker-compose.yaml b/testing/cli-e2etest/environment/jaeger/server-setup/docker-compose.yaml new file mode 100644 index 0000000000..3a885f5fd7 --- /dev/null +++ b/testing/cli-e2etest/environment/jaeger/server-setup/docker-compose.yaml @@ -0,0 +1,66 @@ +version: '3' +services: + + tracetest: + image: kubeshop/tracetest:${TAG:-latest} + platform: linux/amd64 + volumes: + - type: bind + source: ./tracetest-config.yaml + target: /app/tracetest.yaml + - type: bind + source: ./tracetest-provision.yaml + target: /app/provision.yaml + command: --provisioning-file /app/provision.yaml + ports: + - 11633:11633 + extra_hosts: + - "host.docker.internal:host-gateway" + depends_on: + postgres: + condition: service_healthy + otel-collector: + condition: service_started + healthcheck: + test: ["CMD", "wget", "--spider", "localhost:11633"] + interval: 1s + timeout: 3s + retries: 60 + environment: + TRACETEST_DEV: ${TRACETEST_DEV} + + postgres: + image: postgres:14 + environment: + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + test: pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" + interval: 1s + timeout: 5s + retries: 60 + ports: + - 5432 + + otel-collector: + image: otel/opentelemetry-collector:0.54.0 + command: + - "--config" + - "/otel-local-config.yaml" + volumes: + - ./collector.config.yaml:/otel-local-config.yaml + depends_on: + - jaeger + ports: + - 4317 + + jaeger: + image: jaegertracing/all-in-one:latest + restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "--spider", "localhost:16686"] + interval: 1s + timeout: 3s + retries: 60 + ports: + - 16685 diff --git a/testing/cli-e2etest/environment/jaeger/server-setup/tracetest-config.yaml b/testing/cli-e2etest/environment/jaeger/server-setup/tracetest-config.yaml new file mode 100644 index 0000000000..cbe3226feb --- /dev/null +++ b/testing/cli-e2etest/environment/jaeger/server-setup/tracetest-config.yaml @@ -0,0 +1,21 @@ +postgres: + host: postgres + user: postgres + password: postgres + port: 5432 + dbname: postgres + params: sslmode=disable + +telemetry: + exporters: + collector: + serviceName: tracetest + sampling: 100 # 100% + exporter: + type: collector + collector: + endpoint: otel-collector:4317 + +server: + telemetry: + exporter: collector diff --git a/testing/cli-e2etest/environment/jaeger/server-setup/tracetest-provision.yaml b/testing/cli-e2etest/environment/jaeger/server-setup/tracetest-provision.yaml new file mode 100644 index 0000000000..a47e7af05e --- /dev/null +++ b/testing/cli-e2etest/environment/jaeger/server-setup/tracetest-provision.yaml @@ -0,0 +1,9 @@ +--- +type: PollingProfile +spec: + name: Default + strategy: periodic + default: true + periodic: + retryDelay: 5s + timeout: 10m diff --git a/testing/cli-e2etest/environment/manager.go b/testing/cli-e2etest/environment/manager.go new file mode 100644 index 0000000000..26084fe0d8 --- /dev/null +++ b/testing/cli-e2etest/environment/manager.go @@ -0,0 +1,131 @@ +package environment + +import ( + "fmt" + "os" + "path" + "runtime" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/kubeshop/tracetest/cli-e2etest/command" + "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" +) + +var ( + mutex = sync.Mutex{} + envCounter int64 = 0 + defaultEnv = "jaeger" + supportedEnvs = []string{"jaeger"} +) + +type Manager interface { + Name() string + Start(t *testing.T) + Close(t *testing.T) + GetCLIConfigPath(t *testing.T) string + GetManisfestResourcePath(t *testing.T, manifestName string) string +} + +type internalManager struct { + environmentType string + dockerComposeFilePath string + dockerProjectName string +} + +func CreateAndStart(t *testing.T) Manager { + t.Helper() + + mutex.Lock() + defer mutex.Unlock() + + environmentName := os.Getenv("TEST_ENVIRONMENT") + + if environmentName == "" { + environmentName = defaultEnv + } + + if !slices.Contains(supportedEnvs, environmentName) { + t.Fatalf("environment %s not registered", environmentName) + } + + environment := GetManager(environmentName) + environment.Start(t) + + return environment +} + +func getExecutingDir() string { + _, filename, _, _ := runtime.Caller(0) + return path.Dir(filename) +} + +// Today we are assuming that the internal manager only deals with docker-compose, +// but in the future we can rename it do "dockerManager" and create another Manager to handle kubernetes environments + +// This module assumes that no test will be run in parallel +// if we change this decision in the future, we will need to update the docker compose usage +// to use something like github.com/testcontainers/testcontainers-go +// (github.com/testcontainers/testcontainers-go/modules/compose in specific) + +func GetManager(environmentType string) Manager { + currentDir := getExecutingDir() + dockerComposeFilepath := fmt.Sprintf("%s/%s/server-setup/docker-compose.yaml", currentDir, environmentType) + + atomic.AddInt64(&envCounter, 1) + + return &internalManager{ + environmentType: environmentType, + dockerComposeFilePath: dockerComposeFilepath, + dockerProjectName: fmt.Sprintf("tracetest-env-%d", envCounter), + } +} + +func (m *internalManager) Name() string { + return m.environmentType +} + +func (m *internalManager) Start(t *testing.T) { + t.Helper() + + result, err := command.Exec( + "docker", "compose", + "--file", m.dockerComposeFilePath, // choose docker compose relative to the chosen environment + "--project-name", m.dockerProjectName, // create a project name to isolate this scenario + "up", "--detach") + + require.NoError(t, err) + require.Equal(t, 0, result.ExitCode) + + // TODO: think in a better way to assure readiness for Tracetest + time.Sleep(1000 * time.Millisecond) +} + +func (m *internalManager) Close(t *testing.T) { + t.Helper() + + result, err := command.Exec( + "docker", "compose", + "--file", m.dockerComposeFilePath, // choose docker compose relative to the chosen environment + "--project-name", m.dockerProjectName, // choose isolated project name + "rm", + "--force", // bypass removal question + "--volumes", // remove volumes attached to this project + "--stop", // force containers to stop + ) + require.NoError(t, err) + require.Equal(t, 0, result.ExitCode) +} + +func (m *internalManager) GetCLIConfigPath(t *testing.T) string { + currentDir := getExecutingDir() + return fmt.Sprintf("%s/%s/cli-config.yaml", currentDir, m.environmentType) +} + +func (m *internalManager) GetManisfestResourcePath(t *testing.T, manifestName string) string { + currentDir := getExecutingDir() + return fmt.Sprintf("%s/%s/resources/%s.yaml", currentDir, m.environmentType, manifestName) +} diff --git a/testing/cli-e2etest/go.mod b/testing/cli-e2etest/go.mod new file mode 100644 index 0000000000..166a6274ab --- /dev/null +++ b/testing/cli-e2etest/go.mod @@ -0,0 +1,17 @@ +module github.com/kubeshop/tracetest/cli-e2etest + +go 1.20 + +require ( + github.com/stretchr/testify v1.8.2 + golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/kr/pretty v0.3.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.8.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/testing/cli-e2etest/go.sum b/testing/cli-e2etest/go.sum new file mode 100644 index 0000000000..ccb17d2f50 --- /dev/null +++ b/testing/cli-e2etest/go.sum @@ -0,0 +1,35 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o= +golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/testing/cli-e2etest/helpers/common.go b/testing/cli-e2etest/helpers/common.go new file mode 100644 index 0000000000..06e45750b7 --- /dev/null +++ b/testing/cli-e2etest/helpers/common.go @@ -0,0 +1,51 @@ +package helpers + +import ( + "bytes" + "encoding/json" + "errors" + "io" + "testing" + + "github.com/goccy/go-yaml" + "github.com/stretchr/testify/require" +) + +func UnmarshalJSON[T any](t *testing.T, data string) T { + var value T + + err := json.Unmarshal([]byte(data), &value) + require.NoError(t, err) + + return value +} + +func UnmarshalYAML[T any](t *testing.T, data string) T { + var value T + + err := yaml.Unmarshal([]byte(data), &value) + require.NoError(t, err) + + return value +} + +func UnmarshalYAMLSequence[T any](t *testing.T, data string) []T { + decoder := yaml.NewDecoder(bytes.NewBuffer([]byte(data))) + + result := []T{} + + for { + var value T + err := decoder.Decode(&value) + + if errors.Is(err, io.EOF) { + break + } + + require.NoError(t, err) + + result = append(result, value) + } + + return result +} diff --git a/testing/cli-e2etest/main.go b/testing/cli-e2etest/main.go new file mode 100644 index 0000000000..79ac70dbc3 --- /dev/null +++ b/testing/cli-e2etest/main.go @@ -0,0 +1,8 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Welcome to the CLI e2e tests!") + fmt.Println("You should run it with `go test`.") +} diff --git a/testing/cli-e2etest/testscenarios/datastore/apply_new_datastore_test.go b/testing/cli-e2etest/testscenarios/datastore/apply_new_datastore_test.go new file mode 100644 index 0000000000..bc7168a3b2 --- /dev/null +++ b/testing/cli-e2etest/testscenarios/datastore/apply_new_datastore_test.go @@ -0,0 +1,54 @@ +package datastore + +import ( + "fmt" + "testing" + + "github.com/kubeshop/tracetest/cli-e2etest/environment" + "github.com/kubeshop/tracetest/cli-e2etest/helpers" + "github.com/kubeshop/tracetest/cli-e2etest/testscenarios/types" + "github.com/kubeshop/tracetest/cli-e2etest/tracetestcli" + "github.com/stretchr/testify/require" +) + +func TestApplyNewDatastore(t *testing.T) { + // instantiate require with testing helper + require := require.New(t) + + // setup isolated e2e environment + env := environment.CreateAndStart(t) + defer env.Close(t) + + cliConfig := env.GetCLIConfigPath(t) + + // Given I am a Tracetest CLI user + // And I have my server recently created + + // When I try to get a datastore without any server setup + // Then it should return an empty datastore + result := tracetestcli.Exec(t, "get datastore --id current", tracetestcli.WithCLIConfig(cliConfig)) + // TODO: we haven't defined a valid output to tell to the user that we are on `no-tracing mode` + require.Equal(0, result.ExitCode) + + dataStore := helpers.UnmarshalYAML[types.DataStoreResource](t, result.StdOut) + require.Equal("DataStore", dataStore.Type) + require.False(dataStore.Spec.Default) + + // When I try to set up a new datastore + // Then it should be applied with success + dataStorePath := env.GetManisfestResourcePath(t, "data-store") + + result = tracetestcli.Exec(t, fmt.Sprintf("apply datastore --file %s", dataStorePath), tracetestcli.WithCLIConfig(cliConfig)) + require.Equal(0, result.ExitCode) + + // When I try to get a datastore again + // Then it should return the datastore applied on the last step + result = tracetestcli.Exec(t, "get datastore --id current", tracetestcli.WithCLIConfig(cliConfig)) + require.Equal(0, result.ExitCode) + + dataStore = helpers.UnmarshalYAML[types.DataStoreResource](t, result.StdOut) + require.Equal("DataStore", dataStore.Type) + require.Equal("current", dataStore.Spec.ID) + require.Equal(env.Name(), dataStore.Spec.Name) + require.True(dataStore.Spec.Default) +} diff --git a/testing/cli-e2etest/testscenarios/datastore/delete_datastore_test.go b/testing/cli-e2etest/testscenarios/datastore/delete_datastore_test.go new file mode 100644 index 0000000000..7d8bb1d5cf --- /dev/null +++ b/testing/cli-e2etest/testscenarios/datastore/delete_datastore_test.go @@ -0,0 +1,59 @@ +package datastore + +import ( + "fmt" + "testing" + + "github.com/kubeshop/tracetest/cli-e2etest/environment" + "github.com/kubeshop/tracetest/cli-e2etest/helpers" + "github.com/kubeshop/tracetest/cli-e2etest/testscenarios/types" + "github.com/kubeshop/tracetest/cli-e2etest/tracetestcli" + "github.com/stretchr/testify/require" +) + +func TestDeleteDatastore(t *testing.T) { + // instantiate require with testing helper + require := require.New(t) + + // setup isolated e2e environment + env := environment.CreateAndStart(t) + defer env.Close(t) + + cliConfig := env.GetCLIConfigPath(t) + + // Given I am a Tracetest CLI user + // And I have my server recently created + + // When I try to set up a new datastore + // Then it should be applied with success + dataStorePath := env.GetManisfestResourcePath(t, "data-store") + + result := tracetestcli.Exec(t, fmt.Sprintf("apply datastore --file %s", dataStorePath), tracetestcli.WithCLIConfig(cliConfig)) + require.Equal(0, result.ExitCode) + + // When I try to get a datastore + // Then it should return the datastore applied on the last step + result = tracetestcli.Exec(t, "get datastore --id current", tracetestcli.WithCLIConfig(cliConfig)) + require.Equal(0, result.ExitCode) + + dataStore := helpers.UnmarshalYAML[types.DataStoreResource](t, result.StdOut) + require.Equal("DataStore", dataStore.Type) + require.Equal("current", dataStore.Spec.ID) + require.True(dataStore.Spec.Default) + + // When I try to delete the datastore + // Then it should delete with success + result = tracetestcli.Exec(t, "delete datastore --id current", tracetestcli.WithCLIConfig(cliConfig)) + require.Equal(0, result.ExitCode) + require.Contains(result.StdOut, "DataStore removed. Defaulting back to no-tracing mode") + + // When I try to get a datastore again + // Then it should return an empty datastore + result = tracetestcli.Exec(t, "get datastore --id current", tracetestcli.WithCLIConfig(cliConfig)) + // TODO: we haven't defined a valid output to tell to the user that we are on `no-tracing mode` + require.Equal(0, result.ExitCode) + + dataStore = helpers.UnmarshalYAML[types.DataStoreResource](t, result.StdOut) + require.Equal("DataStore", dataStore.Type) + require.False(dataStore.Spec.Default) +} diff --git a/testing/cli-e2etest/testscenarios/datastore/list_datastore_test.go b/testing/cli-e2etest/testscenarios/datastore/list_datastore_test.go new file mode 100644 index 0000000000..83894bdb60 --- /dev/null +++ b/testing/cli-e2etest/testscenarios/datastore/list_datastore_test.go @@ -0,0 +1,67 @@ +package datastore + +import ( + "fmt" + "strings" + "testing" + + "github.com/kubeshop/tracetest/cli-e2etest/environment" + "github.com/kubeshop/tracetest/cli-e2etest/helpers" + "github.com/kubeshop/tracetest/cli-e2etest/testscenarios/types" + "github.com/kubeshop/tracetest/cli-e2etest/tracetestcli" + "github.com/stretchr/testify/require" +) + +func TestListDatastore(t *testing.T) { + // instantiate require with testing helper + require := require.New(t) + + env := environment.CreateAndStart(t) + defer env.Close(t) + + cliConfig := env.GetCLIConfigPath(t) + + // Given I am a Tracetest CLI user + // And I have my server recently created + + // When I try to set up a new datastore + // Then it should be applied with success + dataStorePath := env.GetManisfestResourcePath(t, "data-store") + + result := tracetestcli.Exec(t, fmt.Sprintf("apply datastore --file %s", dataStorePath), tracetestcli.WithCLIConfig(cliConfig)) + require.Equal(0, result.ExitCode) + + // When I try to list datastore again on pretty mode + // Then it should print a table with 4 lines printed: header, separator, data store item and empty line + result = tracetestcli.Exec(t, "list datastore --output pretty", tracetestcli.WithCLIConfig(cliConfig)) + require.Equal(0, result.ExitCode) + require.Contains(result.StdOut, "current") + require.Contains(result.StdOut, env.Name()) + + lines := strings.Split(result.StdOut, "\n") + require.Len(lines, 4) + + // When I try to list datastore again on json mode + // Then it should print a JSON list with one item + result = tracetestcli.Exec(t, "list datastore --output json", tracetestcli.WithCLIConfig(cliConfig)) + require.Equal(0, result.ExitCode) + + dataStoresJSON := helpers.UnmarshalJSON[[]types.DataStoreResource](t, result.StdOut) + + require.Len(dataStoresJSON, 1) + require.Equal("DataStore", dataStoresJSON[0].Type) + require.Equal(env.Name(), dataStoresJSON[0].Spec.Name) + require.True(dataStoresJSON[0].Spec.Default) + + // When I try to list datastore again on yaml mode + // Then it should print a YAML list with one item + result = tracetestcli.Exec(t, "list datastore --output yaml", tracetestcli.WithCLIConfig(cliConfig)) + require.Equal(0, result.ExitCode) + + dataStoresYAML := helpers.UnmarshalYAMLSequence[types.DataStoreResource](t, result.StdOut) + + require.Len(dataStoresYAML, 1) + require.Equal("DataStore", dataStoresYAML[0].Type) + require.Equal(env.Name(), dataStoresYAML[0].Spec.Name) + require.True(dataStoresYAML[0].Spec.Default) +} diff --git a/testing/cli-e2etest/testscenarios/help_test.go b/testing/cli-e2etest/testscenarios/help_test.go new file mode 100644 index 0000000000..a37eec362f --- /dev/null +++ b/testing/cli-e2etest/testscenarios/help_test.go @@ -0,0 +1,25 @@ +package testscenarios + +import ( + "testing" + + "github.com/kubeshop/tracetest/cli-e2etest/tracetestcli" + "github.com/stretchr/testify/require" +) + +func TestHelpCommand(t *testing.T) { + // instantiate require with testing helper + require := require.New(t) + + // Given I am a Tracetest CLI user + // When I try to get help with the commands "tracetest help", "tracetest --help" or "tracetest -h" + // Then I should receive a message with sucess + + possibleCommands := []string{"help", "--help", "-h"} + + for _, helpCommand := range possibleCommands { + result := tracetestcli.Exec(t, helpCommand) + require.Equal(0, result.ExitCode) + require.Greater(len(result.StdOut), 0) + } +} diff --git a/testing/cli-e2etest/testscenarios/types/types.go b/testing/cli-e2etest/testscenarios/types/types.go new file mode 100644 index 0000000000..b2388be7c0 --- /dev/null +++ b/testing/cli-e2etest/testscenarios/types/types.go @@ -0,0 +1,12 @@ +package types + +type DataStore struct { + ID string `json:"id"` + Name string `json:"name"` + Default bool `json:"default"` +} + +type DataStoreResource struct { + Type string `json:"type"` + Spec DataStore `json:"spec"` +} diff --git a/testing/cli-e2etest/testscenarios/version_test.go b/testing/cli-e2etest/testscenarios/version_test.go new file mode 100644 index 0000000000..bf412294a9 --- /dev/null +++ b/testing/cli-e2etest/testscenarios/version_test.go @@ -0,0 +1,21 @@ +package testscenarios + +import ( + "testing" + + "github.com/kubeshop/tracetest/cli-e2etest/tracetestcli" + "github.com/stretchr/testify/require" +) + +func TestVersionCommand(t *testing.T) { + // instantiate require with testing helper + require := require.New(t) + + // Given I am a Tracetest CLI user + // When I try to check the tracetest version + // Then I should receive a version string with sucess + + result := tracetestcli.Exec(t, "version") + require.Equal(0, result.ExitCode) + require.Greater(len(result.StdOut), 0) +} diff --git a/testing/cli-e2etest/tracetestcli/exec.go b/testing/cli-e2etest/tracetestcli/exec.go new file mode 100644 index 0000000000..bd76390743 --- /dev/null +++ b/testing/cli-e2etest/tracetestcli/exec.go @@ -0,0 +1,57 @@ +package tracetestcli + +import ( + "fmt" + "os" + "strings" + "testing" + + "github.com/kubeshop/tracetest/cli-e2etest/command" + "github.com/stretchr/testify/require" +) + +const ( + defaultTracetestCommand = "tracetest" +) + +type ExecOption func(*executionState) + +type executionState struct { + cliConfigFile string +} + +func Exec(t *testing.T, tracetestSubCommand string, options ...ExecOption) *command.ExecResult { + state := &executionState{} + for _, option := range options { + option(state) + } + + if state.cliConfigFile != "" { + // append config at the start of the command + tracetestSubCommand = fmt.Sprintf("--config %s %s", state.cliConfigFile, tracetestSubCommand) + } + + tracetestCommand := getTracetestCommand() + tracetestSubCommands := strings.Split(tracetestSubCommand, " ") + + result, err := command.Exec(tracetestCommand, tracetestSubCommands...) + require.NoError(t, err) + + return result +} + +func getTracetestCommand() string { + tracetestCommand := os.Getenv("TRACETEST_COMMAND") + + if tracetestCommand == "" { + return defaultTracetestCommand + } + + return tracetestCommand +} + +func WithCLIConfig(cliConfig string) ExecOption { + return func(es *executionState) { + es.cliConfigFile = cliConfig + } +}