diff --git a/.gitignore b/.gitignore index 9b9bd940e7..d7710dabd1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ /legal/ /prebuilt/ /test/bin/ -/test/live/bin/ /vendor/ # RPM/DEB diff --git a/.semaphore/live-tests.yml b/.semaphore/live-tests.yml deleted file mode 100644 index c077d65826..0000000000 --- a/.semaphore/live-tests.yml +++ /dev/null @@ -1,43 +0,0 @@ -version: v1.0 -name: Live Integration Tests for Confluent CLI -agent: - machine: - type: s1-prod-ubuntu24-04-amd64-2 - -auto_cancel: - running: - when: "false" - -global_job_config: - prologue: - commands: - - checkout - - sem-version go $(cat .go-version) - - export PATH=$(go env GOPATH)/bin:$PATH - -execution_time_limit: - hours: 24 - -blocks: - - name: "Live Integration Tests" - task: - jobs: - - name: "Run Live Tests" - commands: - - . vault-sem-get-secret v1/ci/kv/apif/cli/live-testing-data - - . vault-sem-get-secret v1/ci/kv/apif/cli/slack-notifications-live-testing - - - | - set -e - - trap ' - RC=$? - if [ $RC -ne 0 ]; then - echo "Live tests failed, sending Slack notification..." - curl -X POST -H "Content-type: application/json" --data "{}" "$SLACK_WEBHOOK_URL" - fi - exit $RC - ' EXIT - - echo "Running all live tests..." - make live-test diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index f7558acb2b..b0f4994d7a 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -103,5 +103,3 @@ after_pipeline: promotions: - name: "Run macOS builds (manual)" pipeline_file: ".semaphore/macos.yml" - - name: "Run live integration tests" - pipeline_file: ".semaphore/live-tests.yml" diff --git a/Makefile b/Makefile index 7abd7c4d59..b16f43a27d 100644 --- a/Makefile +++ b/Makefile @@ -129,52 +129,6 @@ endif .PHONY: test test: unit-test integration-test -.PHONY: build-for-live-test -build-for-live-test: - go build -ldflags="-s -w -X main.disableUpdates=true" -o test/live/bin/confluent ./cmd/confluent - -.PHONY: live-test -live-test: build-for-live-test - @if [ -z "$(CLI_LIVE_TEST_GROUPS)" ]; then \ - CLI_LIVE_TEST=1 go test ./test/live/ -v -run=".*Live$$" \ - -tags="live_test,all" -timeout 1440m -parallel 10; \ - else \ - TAGS="live_test"; \ - for group in $$(echo "$(CLI_LIVE_TEST_GROUPS)" | tr ',' ' '); do \ - TAGS="$$TAGS,$$group"; \ - done; \ - CLI_LIVE_TEST=1 go test ./test/live/ -v -run=".*Live$$" \ - -tags="$$TAGS" -timeout 1440m -parallel 10; \ - fi - -.PHONY: live-test-core -live-test-core: - @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="core" - -.PHONY: live-test-kafka -live-test-kafka: - @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="kafka" - -.PHONY: live-test-schema-registry -live-test-schema-registry: - @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="schema_registry" - -.PHONY: live-test-iam -live-test-iam: - @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="iam" - -.PHONY: live-test-auth -live-test-auth: - @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="auth" - -.PHONY: live-test-connect -live-test-connect: - @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="connect" - -.PHONY: live-test-essential -live-test-essential: - @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="core,kafka,schema_registry,auth" - .PHONY: generate-packaging-patch generate-packaging-patch: diff -u Makefile debian/Makefile | sed "1 s_Makefile_cli/Makefile_" > debian/patches/standard_build_layout.patch diff --git a/debian/patches/standard_build_layout.patch b/debian/patches/standard_build_layout.patch index 5f33c16f14..75ef6fd07e 100644 --- a/debian/patches/standard_build_layout.patch +++ b/debian/patches/standard_build_layout.patch @@ -1,6 +1,6 @@ ---- cli/Makefile 2026-02-23 12:50:33.525992931 -0500 -+++ debian/Makefile 2025-09-22 14:01:33.829653470 -0400 -@@ -1,189 +1,163 @@ +--- cli/Makefile 2026-01-12 14:36:34.668189995 -0500 ++++ debian/Makefile 2025-09-19 13:34:24.112882728 -0400 +@@ -1,143 +1,163 @@ -SHELL := /bin/bash -GORELEASER_VERSION := v2.13.3 +SHELL=/bin/bash @@ -284,52 +284,6 @@ -.PHONY: test -test: unit-test integration-test - --.PHONY: build-for-live-test --build-for-live-test: -- go build -ldflags="-s -w -X main.disableUpdates=true" -o test/live/bin/confluent ./cmd/confluent -- --.PHONY: live-test --live-test: build-for-live-test -- @if [ -z "$(CLI_LIVE_TEST_GROUPS)" ]; then \ -- CLI_LIVE_TEST=1 go test ./test/live/ -v -run=".*Live$$" \ -- -tags="live_test,all" -timeout 1440m -parallel 10; \ -- else \ -- TAGS="live_test"; \ -- for group in $$(echo "$(CLI_LIVE_TEST_GROUPS)" | tr ',' ' '); do \ -- TAGS="$$TAGS,$$group"; \ -- done; \ -- CLI_LIVE_TEST=1 go test ./test/live/ -v -run=".*Live$$" \ -- -tags="$$TAGS" -timeout 1440m -parallel 10; \ -- fi -- --.PHONY: live-test-core --live-test-core: -- @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="core" -- --.PHONY: live-test-kafka --live-test-kafka: -- @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="kafka" -- --.PHONY: live-test-schema-registry --live-test-schema-registry: -- @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="schema_registry" -- --.PHONY: live-test-iam --live-test-iam: -- @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="iam" -- --.PHONY: live-test-auth --live-test-auth: -- @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="auth" -- --.PHONY: live-test-connect --live-test-connect: -- @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="connect" -- --.PHONY: live-test-essential --live-test-essential: -- @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="core,kafka,schema_registry,auth" -- -.PHONY: generate-packaging-patch -generate-packaging-patch: - diff -u Makefile debian/Makefile | sed "1 s_Makefile_cli/Makefile_" > debian/patches/standard_build_layout.patch diff --git a/go.mod b/go.mod index 77a4f6c6a7..e04a392cd6 100644 --- a/go.mod +++ b/go.mod @@ -68,7 +68,7 @@ require ( github.com/docker/go-connections v0.5.0 github.com/fatih/color v1.17.0 github.com/gdamore/tcell/v2 v2.7.4 - github.com/go-git/go-git/v5 v5.16.5 + github.com/go-git/go-git/v5 v5.13.0 github.com/go-jose/go-jose/v3 v3.0.4 github.com/gobuffalo/flect v1.0.2 github.com/gogo/protobuf v1.3.2 @@ -134,7 +134,7 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/ProtonMail/go-crypto v1.1.3 // indirect github.com/alecthomas/chroma/v2 v2.8.0 // indirect github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect @@ -165,7 +165,7 @@ require ( github.com/charmbracelet/x/windows v0.1.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect github.com/confluentinc/proto-go-setter v0.3.0 // indirect - github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/cyphar/filepath-securejoin v0.2.5 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect @@ -175,14 +175,14 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/gdamore/encoding v1.0.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-git/go-billy/v5 v5.6.0 // indirect github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/golang-jwt/jwt/v5 v5.2.2 // indirect - github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -229,7 +229,7 @@ require ( github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.2 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/pjbgf/sha1cd v0.3.2 // indirect + github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pkg/term v1.2.0-beta.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -238,7 +238,7 @@ require ( github.com/santhosh-tekuri/jsonschema/v5 v5.3.0 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/shopspring/decimal v1.3.1 // indirect - github.com/skeema/knownhosts v1.3.1 // indirect + github.com/skeema/knownhosts v1.3.0 // indirect github.com/spf13/afero v1.10.0 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect github.com/swaggest/jsonschema-go v0.3.39 // indirect diff --git a/go.sum b/go.sum index e761d8c46f..5e531fa2d2 100644 --- a/go.sum +++ b/go.sum @@ -71,8 +71,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/hcsshim v0.11.5 h1:haEcLNpj9Ka1gd3B3tAEs9CpE0c+1IhoL59w/exYU38= github.com/Microsoft/hcsshim v0.11.5/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= -github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= -github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= +github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/actgardner/gogen-avro/v10 v10.2.1 h1:z3pOGblRjAJCYpkIJ8CmbMJdksi4rAhaygw0dyXZ930= @@ -303,8 +303,8 @@ github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3H github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= -github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo= +github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= 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= @@ -337,8 +337,8 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg= github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg= -github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= -github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= +github.com/elazarl/goproxy v1.2.1 h1:njjgvO6cRG9rIqN2ebkqy6cQz2Njkx7Fsfv/zIZqgug= +github.com/elazarl/goproxy v1.2.1/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -370,12 +370,12 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= -github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8= +github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s= -github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M= +github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E= +github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -417,8 +417,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -703,8 +703,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= -github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= -github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= +github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -736,8 +736,8 @@ github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= @@ -764,8 +764,8 @@ github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= -github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= +github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/sourcegraph/go-lsp v0.0.0-20240223163137-f80c5dd31dfd h1:Dq5WSzWsP1TbVi10zPWBI5LKEBDg4Y1OhWEph1wr5WQ= diff --git a/test/live/README.md b/test/live/README.md deleted file mode 100644 index ebf7dac505..0000000000 --- a/test/live/README.md +++ /dev/null @@ -1,174 +0,0 @@ -# CLI Live Integration Tests - -Live integration tests run the real CLI binary against Confluent Cloud. They create, read, update, and delete real resources to verify end-to-end CLI behavior. - -## Prerequisites - -1. **CLI binary** — Build with `make build-for-live-test` -2. **Confluent Cloud credentials** — Set the following environment variables: - -| Variable | Required | Description | -|---|---|---| -| `CONFLUENT_CLOUD_EMAIL` | Yes | Confluent Cloud login email | -| `CONFLUENT_CLOUD_PASSWORD` | Yes | Confluent Cloud login password | -| `CLI_LIVE_TEST_CLOUD` | No | Cloud provider: `aws` (default), `gcp`, `azure` | -| `CLI_LIVE_TEST_REGION` | No | Cloud region (default: `us-east-1`) | -| `LIVE_TEST_ENVIRONMENT_ID` | Kafka topics only | Pre-existing environment ID for topic tests | -| `KAFKA_STANDARD_AWS_CLUSTER_ID` | Kafka topics only | Pre-existing cluster ID for topic tests | - -## Running Tests - -### All tests -```bash -make live-test -``` - -### By group -```bash -make live-test-core # environment, service account, API key -make live-test-essential # core + kafka -make live-test CLI_LIVE_TEST_GROUPS="kafka" # kafka only -``` - -### Multi-cloud -```bash -CLI_LIVE_TEST_CLOUD=gcp CLI_LIVE_TEST_REGION=us-east1 make live-test-essential -``` - -### Single test -```bash -CLI_LIVE_TEST=1 go test ./test/live/ -v -run TestLive/TestKafkaClusterCRUDLive \ - -tags="live_test,kafka" -timeout 30m -``` - -## Test Groups - -Tests are organized into groups via Go build tags: - -| Group | Tag | Tests | -|---|---|---| -| Core | `core` | Environment, Service Account, API Key CRUD | -| Kafka | `kafka` | Kafka Cluster CRUD, Kafka Topic CRUD | -| All | `all` | Everything | - -The `essential` group in Semaphore/Makefile maps to `core,kafka`. - -## Concurrency Model - -- Each test method calls `s.setupTestContext(t)` which creates an **isolated HOME directory** and logs in. This means each test has its own CLI config — no shared state. -- Tests opt in to concurrency by calling `t.Parallel()` at the start. All current tests do this. -- The `-parallel 10` flag in the Makefile controls max concurrent tests. -- Tests that need sequential execution (e.g., tests modifying shared external state) should simply omit the `t.Parallel()` call. - -## Writing a New Test - -### 1. Create a test file - -```go -//go:build live_test && (all || mygroup) - -package live - -func (s *CLILiveTestSuite) TestMyResourceCRUDLive() { - t := s.T() - t.Parallel() - state := s.setupTestContext(t) - - // ... test body ... -} -``` - -The test method name **must** end with `Live` to match the `-run=".*Live$"` filter. - -### 2. Define test steps - -Use `CLILiveTest` structs for each CLI command: - -```go -steps := []CLILiveTest{ - { - Name: "Create resource", - Args: "resource create my-name -o json", - JSONFieldsExist: []string{"id"}, - CaptureID: "resource_id", // captures JSON "id" field into state - }, - { - Name: "Describe resource", - Args: "resource describe {{.resource_id}} -o json", - UseStateVars: true, // enables {{.key}} template substitution - JSONFields: map[string]string{"name": "my-name"}, - }, -} -``` - -### 3. Register cleanup - -Always register cleanup **before** creating resources (LIFO execution order): - -```go -s.registerCleanup(t, "resource delete {{.resource_id}} --force", state) -``` - -### 4. Run steps - -```go -for _, step := range steps { - t.Run(step.Name, func(t *testing.T) { - s.runLiveCommand(t, step, state) - }) -} -``` - -### CLILiveTest Field Reference - -| Field | Type | Description | -|---|---|---| -| `Name` | `string` | Step name shown in output | -| `Args` | `string` | CLI arguments (supports `{{.key}}` when `UseStateVars` is true) | -| `ExitCode` | `int` | Expected exit code (default 0) | -| `Input` | `string` | Stdin content | -| `Contains` | `[]string` | Strings that must appear in output | -| `NotContains` | `[]string` | Strings that must NOT appear in output | -| `Regex` | `[]string` | Regex patterns output must match | -| `JSONFields` | `map[string]string` | JSON fields to check (empty value = any non-empty value) | -| `JSONFieldsExist` | `[]string` | JSON fields that must exist (any value) | -| `WantFunc` | `func(t, output, state)` | Custom assertion function | -| `CaptureID` | `string` | State key to store extracted JSON "id" field | -| `UseStateVars` | `bool` | Enable `{{.key}}` template substitution in Args | - -### Async Operations - -For operations that take time (e.g., cluster provisioning), use `waitForCondition`: - -```go -s.waitForCondition(t, - "kafka cluster describe {{.cluster_id}} -o json", - state, - func(output string) bool { - return strings.EqualFold(extractJSONField(t, output, "status"), "UP") - }, - 30*time.Second, // poll interval - 10*time.Minute, // timeout -) -``` - -## Adding a New Test Group - -1. Create test file(s) with build tag: `//go:build live_test && (all || mygroup)` -2. Add a Makefile target: - ```makefile - .PHONY: live-test-mygroup - live-test-mygroup: - @$(MAKE) live-test CLI_LIVE_TEST_GROUPS="mygroup" - ``` -3. Update the Semaphore promotion parameters if the group should be selectable in CI. - -## CI (Semaphore) - -Live tests are triggered via the "Run live integration tests" promotion in `.semaphore/semaphore.yml`. Parameters: - -- **CLI_LIVE_TEST_GROUPS** — Test group to run (default: `essential`) -- **CLI_LIVE_TEST_CLOUD** — Cloud provider (default: `aws`) -- **CLI_LIVE_TEST_REGION** — Cloud region (default: `us-east-1`) - -Credentials are loaded from Vault secrets in the Semaphore pipeline. diff --git a/test/live/api_key_live_test.go b/test/live/api_key_live_test.go deleted file mode 100644 index 44e6fbcfb0..0000000000 --- a/test/live/api_key_live_test.go +++ /dev/null @@ -1,87 +0,0 @@ -//go:build live_test && (all || core) - -package live - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func (s *CLILiveTestSuite) TestApiKeyCRUDLive() { - t := s.T() - t.Parallel() - state := s.setupTestContext(t) - - saName := uniqueName("apikey-sa") - apiKeyDescription := "Live test API key" - updatedDescription := "Updated live test API key" - - // Cleanup in LIFO order: delete API key first, then service account - s.registerCleanup(t, "iam service-account delete {{.sa_id}} --force", state) - s.registerCleanup(t, "api-key delete {{.api_key_id}} --force", state) - - steps := []CLILiveTest{ - { - Name: "Create service account for API key", - Args: `iam service-account create ` + saName + ` --description "SA for API key live test" -o json`, - ExitCode: 0, - JSONFieldsExist: []string{"id"}, - CaptureID: "sa_id", - }, - { - Name: "Create API key", - Args: `api-key create --resource cloud --service-account {{.sa_id}} --description "` + apiKeyDescription + `" -o json`, - UseStateVars: true, - ExitCode: 0, - JSONFieldsExist: []string{"api_key", "api_secret"}, - WantFunc: func(t *testing.T, output string, state *LiveTestState) { - t.Helper() - id := extractJSONField(t, output, "api_key") - require.NotEmpty(t, id, "failed to extract api_key from output:\n%s", output) - state.Set("api_key_id", id) - t.Logf("Captured api_key_id = %s", id) - }, - }, - { - Name: "List API keys", - Args: "api-key list --service-account {{.sa_id}}", - UseStateVars: true, - ExitCode: 0, - WantFunc: func(t *testing.T, output string, state *LiveTestState) { - t.Helper() - apiKeyID := state.Get("api_key_id") - if apiKeyID != "" { - require.Contains(t, output, apiKeyID, "API key list should contain the created key") - } - }, - }, - { - Name: "Update API key description", - Args: `api-key update {{.api_key_id}} --description "` + updatedDescription + `"`, - UseStateVars: true, - ExitCode: 0, - }, - { - Name: "Describe updated API key", - Args: "api-key describe {{.api_key_id}} -o json", - UseStateVars: true, - ExitCode: 0, - JSONFields: map[string]string{ - "description": updatedDescription, - }, - }, - { - Name: "Delete API key", - Args: "api-key delete {{.api_key_id}} --force", - UseStateVars: true, - ExitCode: 0, - }, - } - - for _, step := range steps { - t.Run(step.Name, func(t *testing.T) { - s.runLiveCommand(t, step, state) - }) - } -} diff --git a/test/live/connect_live_test.go b/test/live/connect_live_test.go deleted file mode 100644 index a4c03c4f63..0000000000 --- a/test/live/connect_live_test.go +++ /dev/null @@ -1,146 +0,0 @@ -//go:build live_test && (all || connect) - -package live - -import ( - "fmt" - "os" - "path/filepath" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func (s *CLILiveTestSuite) TestConnectClusterCRUDLive() { - t := s.T() - t.Parallel() - - envID := os.Getenv("LIVE_TEST_ENVIRONMENT_ID") - clusterID := os.Getenv("KAFKA_STANDARD_AWS_CLUSTER_ID") - apiKey := os.Getenv("KAFKA_STANDARD_AWS_API_KEY") - apiSecret := os.Getenv("KAFKA_STANDARD_AWS_API_SECRET") - if envID == "" || clusterID == "" || apiKey == "" || apiSecret == "" { - t.Skip("Skipping: LIVE_TEST_ENVIRONMENT_ID, KAFKA_STANDARD_AWS_CLUSTER_ID, KAFKA_STANDARD_AWS_API_KEY, and KAFKA_STANDARD_AWS_API_SECRET must be set") - } - - state := s.setupTestContext(t) - - connectorName := uniqueName("datagen") - topicName := uniqueName("connect-topic") - - // Create connector config file for DatagenSource (managed connector) - configContent := fmt.Sprintf(`{ - "connector.class": "DatagenSource", - "name": "%s", - "kafka.auth.mode": "KAFKA_API_KEY", - "kafka.api.key": "%s", - "kafka.api.secret": "%s", - "kafka.topic": "%s", - "output.data.format": "JSON", - "quickstart": "ORDERS", - "tasks.max": "1" -}`, connectorName, apiKey, apiSecret, topicName) - - configDir, err := os.MkdirTemp("", "cli-live-connect-*") - require.NoError(t, err) - t.Cleanup(func() { os.RemoveAll(configDir) }) - - configFile := filepath.Join(configDir, "connector-config.json") - require.NoError(t, os.WriteFile(configFile, []byte(configContent), 0644)) - - // Register cleanups in LIFO order: topic last, connector first - s.registerCleanup(t, "kafka topic delete "+topicName+" --cluster "+clusterID+" --environment "+envID+" --force", state) - s.registerCleanup(t, "connect cluster delete {{.connector_id}} --cluster "+clusterID+" --environment "+envID+" --force", state) - - // Phase 1: Setup and create connector - setupSteps := []CLILiveTest{ - { - Name: "Use environment", - Args: "environment use " + envID, - }, - { - Name: "Use kafka cluster", - Args: "kafka cluster use " + clusterID, - }, - { - Name: "Create topic for connector", - Args: "kafka topic create " + topicName + " --partitions 1", - }, - { - Name: "Create connector", - Args: "connect cluster create --config-file " + configFile + " -o json", - CaptureID: "connector_id", - WantFunc: func(t *testing.T, output string, state *LiveTestState) { - t.Helper() - // Also capture the connector name for list verification - name := extractJSONField(t, output, "name") - if name != "" { - state.Set("connector_name", name) - } - t.Logf("Connector create output: %s", output) - }, - }, - } - - for _, step := range setupSteps { - t.Run(step.Name, func(t *testing.T) { - s.runLiveCommand(t, step, state) - }) - } - - // Phase 2: Wait for connector to reach a usable state - t.Run("Wait for connector provisioned", func(t *testing.T) { - s.waitForCondition(t, - "connect cluster describe {{.connector_id}} -o json", - state, - func(output string) bool { - return strings.Contains(output, "RUNNING") || strings.Contains(output, "PROVISIONING") - }, - 15*time.Second, - 5*time.Minute, - ) - }) - - // Phase 3: CRUD and lifecycle operations - crudSteps := []CLILiveTest{ - { - Name: "List connectors", - Args: "connect cluster list", - Contains: []string{connectorName}, - Retries: 3, - }, - { - Name: "Describe connector", - Args: "connect cluster describe {{.connector_id}}", - UseStateVars: true, - Contains: []string{connectorName}, - }, - { - Name: "Pause connector", - Args: "connect cluster pause {{.connector_id}}", - UseStateVars: true, - Retries: 5, - RetryInterval: 15 * time.Second, - }, - { - Name: "Resume connector", - Args: "connect cluster resume {{.connector_id}}", - UseStateVars: true, - Retries: 5, - RetryInterval: 15 * time.Second, - }, - { - Name: "Delete connector", - Args: "connect cluster delete {{.connector_id}} --force", - UseStateVars: true, - }, - } - - for _, step := range crudSteps { - t.Run(step.Name, func(t *testing.T) { - s.runLiveCommand(t, step, state) - }) - } -} diff --git a/test/live/environment_live_test.go b/test/live/environment_live_test.go deleted file mode 100644 index 0fad3d9bce..0000000000 --- a/test/live/environment_live_test.go +++ /dev/null @@ -1,82 +0,0 @@ -//go:build live_test && (all || core) - -package live - -import ( - "testing" -) - -func (s *CLILiveTestSuite) TestEnvironmentCRUDLive() { - t := s.T() - t.Parallel() - state := s.setupTestContext(t) - - envName := uniqueName("env") - updatedEnvName := envName + "-updated" - - // Register cleanup immediately — runs even if test fails (LIFO order) - s.registerCleanup(t, "environment delete {{.env_id}} --force", state) - - steps := []CLILiveTest{ - { - Name: "Create environment", - Args: "environment create " + envName + " -o json", - ExitCode: 0, - JSONFields: map[string]string{ - "name": envName, - }, - JSONFieldsExist: []string{"id"}, - CaptureID: "env_id", - }, - { - Name: "Describe environment", - Args: "environment describe {{.env_id}} -o json", - UseStateVars: true, - ExitCode: 0, - JSONFields: map[string]string{ - "name": envName, - "id": "", // any non-empty value - }, - }, - { - Name: "List environments", - Args: "environment list", - UseStateVars: true, - ExitCode: 0, - Contains: []string{envName}, - }, - { - Name: "Update environment name", - Args: "environment update {{.env_id}} --name " + updatedEnvName, - UseStateVars: true, - ExitCode: 0, - }, - { - Name: "Describe updated environment", - Args: "environment describe {{.env_id}} -o json", - UseStateVars: true, - ExitCode: 0, - JSONFields: map[string]string{ - "name": updatedEnvName, - }, - }, - { - Name: "Delete environment", - Args: "environment delete {{.env_id}} --force", - UseStateVars: true, - ExitCode: 0, - }, - { - Name: "Verify deletion", - Args: "environment describe {{.env_id}}", - UseStateVars: true, - ExitCode: 1, - }, - } - - for _, step := range steps { - t.Run(step.Name, func(t *testing.T) { - s.runLiveCommand(t, step, state) - }) - } -} diff --git a/test/live/iam_rbac_live_test.go b/test/live/iam_rbac_live_test.go deleted file mode 100644 index ce2da76400..0000000000 --- a/test/live/iam_rbac_live_test.go +++ /dev/null @@ -1,74 +0,0 @@ -//go:build live_test && (all || iam) - -package live - -import ( - "os" - "testing" -) - -func (s *CLILiveTestSuite) TestRBACRoleBindingCRUDLive() { - t := s.T() - t.Parallel() - - envID := os.Getenv("LIVE_TEST_ENVIRONMENT_ID") - if envID == "" { - t.Skip("Skipping: LIVE_TEST_ENVIRONMENT_ID must be set") - } - - state := s.setupTestContext(t) - - saName := uniqueName("rbac-sa") - - // Cleanup in LIFO order: delete role binding, then service account - s.registerCleanup(t, "iam service-account delete {{.sa_id}} --force", state) - s.registerCleanup(t, "iam rbac role-binding delete --principal User:{{.sa_id}} --role EnvironmentAdmin --environment "+envID+" --force", state) - - steps := []CLILiveTest{ - { - Name: "List available roles", - Args: "iam rbac role list", - }, - { - Name: "Create service account for RBAC test", - Args: `iam service-account create ` + saName + ` --description "SA for RBAC live test" -o json`, - JSONFieldsExist: []string{"id"}, - CaptureID: "sa_id", - }, - { - Name: "Create role binding (EnvironmentAdmin)", - Args: "iam rbac role-binding create --principal User:{{.sa_id}} --role EnvironmentAdmin --environment " + envID, - UseStateVars: true, - }, - { - Name: "List role bindings for principal", - Args: "iam rbac role-binding list --principal User:{{.sa_id}} --environment " + envID, - UseStateVars: true, - Contains: []string{"EnvironmentAdmin"}, - Retries: 5, - }, - { - Name: "Delete role binding", - Args: "iam rbac role-binding delete --principal User:{{.sa_id}} --role EnvironmentAdmin --environment " + envID + " --force", - UseStateVars: true, - }, - { - Name: "Verify role binding deleted", - Args: "iam rbac role-binding list --principal User:{{.sa_id}} --environment " + envID, - UseStateVars: true, - NotContains: []string{"EnvironmentAdmin"}, - Retries: 5, - }, - { - Name: "Delete service account", - Args: "iam service-account delete {{.sa_id}} --force", - UseStateVars: true, - }, - } - - for _, step := range steps { - t.Run(step.Name, func(t *testing.T) { - s.runLiveCommand(t, step, state) - }) - } -} diff --git a/test/live/kafka_acl_live_test.go b/test/live/kafka_acl_live_test.go deleted file mode 100644 index 2e3d06f54c..0000000000 --- a/test/live/kafka_acl_live_test.go +++ /dev/null @@ -1,80 +0,0 @@ -//go:build live_test && (all || kafka) - -package live - -import ( - "os" - "testing" -) - -func (s *CLILiveTestSuite) TestKafkaACLCRUDLive() { - t := s.T() - t.Parallel() - - envID := os.Getenv("LIVE_TEST_ENVIRONMENT_ID") - clusterID := os.Getenv("KAFKA_STANDARD_AWS_CLUSTER_ID") - if envID == "" || clusterID == "" { - t.Skip("Skipping: LIVE_TEST_ENVIRONMENT_ID and KAFKA_STANDARD_AWS_CLUSTER_ID must be set") - } - - state := s.setupTestContext(t) - - saName := uniqueName("acl-sa") - topicName := uniqueName("acl-topic") - - // Cleanup in LIFO order: delete ACL, then service account - s.registerCleanup(t, "iam service-account delete {{.sa_id}} --force", state) - s.registerCleanup(t, "kafka acl delete --allow --service-account {{.sa_id}} --operations read,describe --topic "+topicName+" --cluster "+clusterID+" --environment "+envID+" --force", state) - - steps := []CLILiveTest{ - { - Name: "Create service account for ACL test", - Args: `iam service-account create ` + saName + ` --description "SA for ACL live test" -o json`, - JSONFieldsExist: []string{"id"}, - CaptureID: "sa_id", - }, - { - Name: "Use environment", - Args: "environment use " + envID, - }, - { - Name: "Use kafka cluster", - Args: "kafka cluster use " + clusterID, - }, - { - Name: "Create ACL allow read+describe on topic", - Args: "kafka acl create --allow --service-account {{.sa_id}} --operations read,describe --topic " + topicName, - UseStateVars: true, - Retries: 3, - }, - { - Name: "List ACLs for service account", - Args: "kafka acl list --service-account {{.sa_id}}", - UseStateVars: true, - Contains: []string{topicName}, - }, - { - Name: "Delete ACL", - Args: "kafka acl delete --allow --service-account {{.sa_id}} --operations read,describe --topic " + topicName + " --force", - UseStateVars: true, - }, - { - Name: "Verify ACL deleted", - Args: "kafka acl list --service-account {{.sa_id}}", - UseStateVars: true, - NotContains: []string{topicName}, - Retries: 3, - }, - { - Name: "Delete service account", - Args: "iam service-account delete {{.sa_id}} --force", - UseStateVars: true, - }, - } - - for _, step := range steps { - t.Run(step.Name, func(t *testing.T) { - s.runLiveCommand(t, step, state) - }) - } -} diff --git a/test/live/kafka_cluster_live_test.go b/test/live/kafka_cluster_live_test.go deleted file mode 100644 index 250c8a515e..0000000000 --- a/test/live/kafka_cluster_live_test.go +++ /dev/null @@ -1,101 +0,0 @@ -//go:build live_test && (all || kafka) - -package live - -import ( - "fmt" - "strings" - "testing" - "time" -) - -func (s *CLILiveTestSuite) TestKafkaClusterCRUDLive() { - t := s.T() - t.Parallel() - state := s.setupTestContext(t) - - cloud := liveTestCloud() - region := liveTestRegion() - clusterName := uniqueName("cluster") - updatedName := clusterName + "-updated" - envName := uniqueName("kafka-env") - - // Cleanup in LIFO order: delete cluster first, then environment - s.registerCleanup(t, "environment delete {{.env_id}} --force", state) - s.registerCleanup(t, "kafka cluster delete {{.cluster_id}} --force --environment {{.env_id}}", state) - - // Phase 1: Create environment and cluster - createSteps := []CLILiveTest{ - { - Name: "Create environment for kafka test", - Args: "environment create " + envName + " -o json", - JSONFieldsExist: []string{"id"}, - CaptureID: "env_id", - }, - { - Name: "Use environment", - Args: "environment use {{.env_id}}", - UseStateVars: true, - }, - { - Name: "Create basic kafka cluster", - Args: fmt.Sprintf("kafka cluster create %s --cloud %s --region %s --type basic -o json", clusterName, cloud, region), - UseStateVars: true, - CaptureID: "cluster_id", - }, - } - - for _, step := range createSteps { - t.Run(step.Name, func(t *testing.T) { - s.runLiveCommand(t, step, state) - }) - } - - // Phase 2: Wait for cluster to be provisioned (~5 min for basic) - t.Run("Wait for cluster provisioned", func(t *testing.T) { - s.waitForCondition(t, - "kafka cluster describe {{.cluster_id}} --environment {{.env_id}} -o json", - state, - func(output string) bool { - status := extractJSONField(t, output, "status") - return strings.EqualFold(status, "UP") - }, - 30*time.Second, - 10*time.Minute, - ) - }) - - // Phase 3: CRUD operations on the provisioned cluster - crudSteps := []CLILiveTest{ - { - Name: "List kafka clusters", - Args: "kafka cluster list --environment {{.env_id}}", - UseStateVars: true, - Contains: []string{clusterName}, - }, - { - Name: "Update cluster name", - Args: "kafka cluster update {{.cluster_id}} --name " + updatedName + " --environment {{.env_id}}", - UseStateVars: true, - }, - { - Name: "Describe updated cluster", - Args: "kafka cluster describe {{.cluster_id}} --environment {{.env_id}} -o json", - UseStateVars: true, - JSONFields: map[string]string{ - "name": updatedName, - }, - }, - { - Name: "Delete cluster", - Args: "kafka cluster delete {{.cluster_id}} --force --environment {{.env_id}}", - UseStateVars: true, - }, - } - - for _, step := range crudSteps { - t.Run(step.Name, func(t *testing.T) { - s.runLiveCommand(t, step, state) - }) - } -} diff --git a/test/live/kafka_consumer_group_live_test.go b/test/live/kafka_consumer_group_live_test.go deleted file mode 100644 index 392dbe1694..0000000000 --- a/test/live/kafka_consumer_group_live_test.go +++ /dev/null @@ -1,43 +0,0 @@ -//go:build live_test && (all || kafka) - -package live - -import ( - "os" - "testing" -) - -func (s *CLILiveTestSuite) TestKafkaConsumerGroupListLive() { - t := s.T() - t.Parallel() - - envID := os.Getenv("LIVE_TEST_ENVIRONMENT_ID") - clusterID := os.Getenv("KAFKA_STANDARD_AWS_CLUSTER_ID") - if envID == "" || clusterID == "" { - t.Skip("Skipping: LIVE_TEST_ENVIRONMENT_ID and KAFKA_STANDARD_AWS_CLUSTER_ID must be set") - } - - state := s.setupTestContext(t) - - steps := []CLILiveTest{ - { - Name: "Use environment", - Args: "environment use " + envID, - }, - { - Name: "Use kafka cluster", - Args: "kafka cluster use " + clusterID, - }, - { - Name: "List consumer groups", - Args: "kafka consumer group list", - // Verify command succeeds — there may or may not be active consumer groups - }, - } - - for _, step := range steps { - t.Run(step.Name, func(t *testing.T) { - s.runLiveCommand(t, step, state) - }) - } -} diff --git a/test/live/kafka_topic_live_test.go b/test/live/kafka_topic_live_test.go deleted file mode 100644 index 7a1015c8c8..0000000000 --- a/test/live/kafka_topic_live_test.go +++ /dev/null @@ -1,87 +0,0 @@ -//go:build live_test && (all || kafka) - -package live - -import ( - "os" - "testing" - - "github.com/stretchr/testify/require" -) - -func (s *CLILiveTestSuite) TestKafkaTopicCRUDLive() { - t := s.T() - t.Parallel() - - // This test requires a pre-existing cluster to avoid the ~5 min provisioning wait. - // Skip if env vars are not set (allows local dev without persistent infra). - envID := os.Getenv("LIVE_TEST_ENVIRONMENT_ID") - clusterID := os.Getenv("KAFKA_STANDARD_AWS_CLUSTER_ID") - if envID == "" || clusterID == "" { - t.Skip("Skipping kafka topic test: LIVE_TEST_ENVIRONMENT_ID and KAFKA_STANDARD_AWS_CLUSTER_ID must be set") - } - - state := s.setupTestContext(t) - - topicName := uniqueName("topic") - - // Best-effort cleanup: delete topic if it still exists - s.registerCleanup(t, "kafka topic delete {{.topic_name}} --cluster "+clusterID+" --environment "+envID+" --force", state) - state.Set("topic_name", topicName) - - steps := []CLILiveTest{ - { - Name: "Use environment", - Args: "environment use " + envID, - }, - { - Name: "Use kafka cluster", - Args: "kafka cluster use " + clusterID, - }, - { - Name: "Create topic", - Args: "kafka topic create " + topicName + " --partitions 3", - }, - { - Name: "List topics", - Args: "kafka topic list", - Contains: []string{topicName}, - }, - { - Name: "Describe topic", - Args: "kafka topic describe " + topicName + " -o json", - JSONFields: map[string]string{ - "name": topicName, - }, - WantFunc: func(t *testing.T, output string, state *LiveTestState) { - t.Helper() - partitions := extractJSONField(t, output, "partition_count") - require.Equal(t, "3", partitions, "topic should have 3 partitions") - }, - }, - { - Name: "Update topic config", - Args: "kafka topic update " + topicName + " --config retention.ms=86400000", - Retries: 5, - }, - { - Name: "Describe updated topic config", - Args: "kafka topic configuration list " + topicName + " -o json", - WantFunc: func(t *testing.T, output string, _ *LiveTestState) { - t.Helper() - require.Contains(t, output, "retention.ms", "output should contain retention.ms config") - require.Contains(t, output, "86400000", "retention.ms should be 86400000") - }, - }, - { - Name: "Delete topic", - Args: "kafka topic delete " + topicName + " --force", - }, - } - - for _, step := range steps { - t.Run(step.Name, func(t *testing.T) { - s.runLiveCommand(t, step, state) - }) - } -} diff --git a/test/live/live_test.go b/test/live/live_test.go deleted file mode 100644 index c5578440b8..0000000000 --- a/test/live/live_test.go +++ /dev/null @@ -1,385 +0,0 @@ -//go:build live_test - -package live - -import ( - "bytes" - "encoding/json" - "fmt" - "os" - "os/exec" - "path/filepath" - "regexp" - "runtime" - "strings" - "sync" - "testing" - "text/template" - "time" - - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" -) - -var liveBin = "test/live/bin/confluent" - -// CLILiveTest represents a single step in a live integration test. -type CLILiveTest struct { - // Name is the test step name shown in output. - Name string - // Args is the CLI arguments string (e.g., "environment create my-env -o json"). - // Supports {{.key}} template syntax when UseStateVars is true. - Args string - // ExitCode is the expected process exit code (default 0). - ExitCode int - // Input provides stdin content to the command. - Input string - // Contains lists strings that must appear in the command output. - Contains []string - // NotContains lists strings that must NOT appear in the command output. - NotContains []string - // Regex lists regex patterns that the output must match. - Regex []string - // JSONFields maps JSON field paths to expected values. - // An empty string value means "any value" (just check field exists with a non-empty value). - JSONFields map[string]string - // JSONFieldsExist lists JSON fields that must exist in the output (value can be anything including empty). - JSONFieldsExist []string - // WantFunc is an optional custom assertion function. - WantFunc func(t *testing.T, output string, state *LiveTestState) - // CaptureID stores the extracted "id" field from JSON output into state under this key. - CaptureID string - // UseStateVars enables {{.key}} template substitution in Args from state. - UseStateVars bool - // Retries is the number of times to retry on failure (0 means no retry). - // Useful for steps that may hit eventual consistency delays. - Retries int - // RetryInterval is the time to wait between retries. Defaults to 5s if Retries > 0. - RetryInterval time.Duration -} - -// LiveTestState is a thread-safe map for passing dynamic values between test steps. -// Each test gets its own LiveTestState with an isolated homeDir for CLI config. -type LiveTestState struct { - mu sync.Mutex - values map[string]string - homeDir string -} - -func NewLiveTestState() *LiveTestState { - return &LiveTestState{values: make(map[string]string)} -} - -func (s *LiveTestState) Set(key, value string) { - s.mu.Lock() - defer s.mu.Unlock() - s.values[key] = value -} - -func (s *LiveTestState) Get(key string) string { - s.mu.Lock() - defer s.mu.Unlock() - return s.values[key] -} - -func (s *LiveTestState) All() map[string]string { - s.mu.Lock() - defer s.mu.Unlock() - cp := make(map[string]string, len(s.values)) - for k, v := range s.values { - cp[k] = v - } - return cp -} - -// CLILiveTestSuite is the live integration test suite. -type CLILiveTestSuite struct { - suite.Suite - binPath string -} - -func TestLive(t *testing.T) { - if os.Getenv("CLI_LIVE_TEST") != "1" { - t.Skip("Skipping live test. Set CLI_LIVE_TEST=1 to run.") - } - suite.Run(t, new(CLILiveTestSuite)) -} - -func (s *CLILiveTestSuite) SetupSuite() { - req := require.New(s.T()) - - // Navigate to repo root (test/live -> repo root is ../..) - err := os.Chdir("../..") - req.NoError(err) - - dir, err := os.Getwd() - req.NoError(err) - - bin := liveBin - if runtime.GOOS == "windows" { - bin += ".exe" - } - - s.binPath = filepath.Join(dir, bin) - _, err = os.Stat(s.binPath) - req.NoError(err, "CLI binary not found at %s — run 'make build-for-live-test' first", s.binPath) -} - -// setupTestContext creates an isolated CLI config directory and logs in. -// Each test gets its own HOME so tests can run concurrently without clobbering shared state. -func (s *CLILiveTestSuite) setupTestContext(t *testing.T) *LiveTestState { - t.Helper() - - homeDir, err := os.MkdirTemp("", "cli-live-*") - require.NoError(t, err) - t.Cleanup(func() { os.RemoveAll(homeDir) }) - - state := NewLiveTestState() - state.homeDir = homeDir - - email := requiredEnv(t, "CONFLUENT_CLOUD_EMAIL") - password := requiredEnv(t, "CONFLUENT_CLOUD_PASSWORD") - - output := s.runRawCommand(t, "login", []string{ - homeEnvVar(homeDir), - fmt.Sprintf("CONFLUENT_CLOUD_EMAIL=%s", email), - fmt.Sprintf("CONFLUENT_CLOUD_PASSWORD=%s", password), - }, "", 0) - require.Contains(t, output, "Logged in", "login failed: %s", output) - - return state -} - -// homeEnvVar returns the HOME (or USERPROFILE on Windows) env var assignment for the given dir. -func homeEnvVar(dir string) string { - if runtime.GOOS == "windows" { - return "USERPROFILE=" + dir - } - return "HOME=" + dir -} - -// buildCommandEnv creates a command environment from os.Environ() with extraEnv overrides. -// Duplicate keys in os.Environ() are replaced by values from extraEnv. -func buildCommandEnv(extraEnv []string) []string { - overrideKeys := make(map[string]bool) - for _, e := range extraEnv { - if idx := strings.IndexByte(e, '='); idx >= 0 { - overrideKeys[e[:idx]] = true - } - } - - var result []string - for _, e := range os.Environ() { - if idx := strings.IndexByte(e, '='); idx >= 0 { - if overrideKeys[e[:idx]] { - continue - } - } - result = append(result, e) - } - return append(result, extraEnv...) -} - -// runLiveCommand executes a CLILiveTest step, performs template substitution, runs the command, -// validates output, and captures IDs. If the step has Retries > 0, the command is retried -// on failure with the specified interval between attempts. -func (s *CLILiveTestSuite) runLiveCommand(t *testing.T, step CLILiveTest, state *LiveTestState) string { - t.Helper() - - args := step.Args - if step.UseStateVars { - args = substituteStateVars(t, args, state) - } - - env := []string{homeEnvVar(state.homeDir)} - - var output string - if step.Retries > 0 { - interval := step.RetryInterval - if interval == 0 { - interval = 5 * time.Second - } - var lastErr error - for attempt := 0; attempt <= step.Retries; attempt++ { - if attempt > 0 { - t.Logf("Retry %d/%d after %s", attempt, step.Retries, interval) - time.Sleep(interval) - } - output, lastErr = s.tryRunRawCommand(args, env, step.Input, step.ExitCode) - if lastErr == nil { - break - } - } - require.NoError(t, lastErr, "command 'confluent %s' failed after %d retries:\n%s", args, step.Retries, output) - } else { - output = s.runRawCommand(t, args, env, step.Input, step.ExitCode) - } - - if step.CaptureID != "" { - id := extractID(t, output) - require.NotEmpty(t, id, "failed to extract ID from output for key %q:\n%s", step.CaptureID, output) - state.Set(step.CaptureID, id) - t.Logf("Captured %s = %s", step.CaptureID, id) - } - - s.validateLiveOutput(t, step, output, state) - - return output -} - -// runRawCommand executes the CLI binary with the given args string and returns combined output. -func (s *CLILiveTestSuite) runRawCommand(t *testing.T, argString string, env []string, input string, exitCode int) string { - t.Helper() - - args := shellSplit(argString) - cmd := exec.Command(s.binPath, args...) - cmd.Env = buildCommandEnv(env) - if input != "" { - cmd.Stdin = strings.NewReader(input) - } - - out, err := cmd.CombinedOutput() - if exitCode == 0 { - require.NoError(t, err, "command 'confluent %s' failed:\n%s", argString, string(out)) - } - require.Equal(t, exitCode, cmd.ProcessState.ExitCode(), - "unexpected exit code for 'confluent %s':\n%s", argString, string(out)) - - return string(out) -} - -// tryRunRawCommand is like runRawCommand but returns an error instead of failing the test. -// Used by runLiveCommand to support retries on transient failures. -func (s *CLILiveTestSuite) tryRunRawCommand(argString string, env []string, input string, expectedExitCode int) (string, error) { - args := shellSplit(argString) - cmd := exec.Command(s.binPath, args...) - cmd.Env = buildCommandEnv(env) - if input != "" { - cmd.Stdin = strings.NewReader(input) - } - - out, err := cmd.CombinedOutput() - output := string(out) - - if expectedExitCode == 0 && err != nil { - return output, fmt.Errorf("command 'confluent %s' failed:\n%s", argString, output) - } - if cmd.ProcessState.ExitCode() != expectedExitCode { - return output, fmt.Errorf("unexpected exit code %d (expected %d) for 'confluent %s':\n%s", - cmd.ProcessState.ExitCode(), expectedExitCode, argString, output) - } - - return output, nil -} - -// validateLiveOutput runs all assertion strategies on the command output. -func (s *CLILiveTestSuite) validateLiveOutput(t *testing.T, step CLILiveTest, output string, state *LiveTestState) { - t.Helper() - - for _, c := range step.Contains { - require.Contains(t, output, c, "output of '%s' missing expected string %q", step.Name, c) - } - - for _, nc := range step.NotContains { - require.NotContains(t, output, nc, "output of '%s' contains unexpected string %q", step.Name, nc) - } - - for _, pattern := range step.Regex { - re, err := regexp.Compile(pattern) - require.NoError(t, err, "invalid regex pattern: %s", pattern) - require.True(t, re.MatchString(output), "output of '%s' does not match regex %q:\n%s", step.Name, pattern, output) - } - - if len(step.JSONFields) > 0 || len(step.JSONFieldsExist) > 0 { - trimmed := strings.TrimSpace(output) - var parsed map[string]interface{} - if err := json.Unmarshal([]byte(trimmed), &parsed); err != nil { - // Try as JSON array and use the first element - var arr []map[string]interface{} - err2 := json.Unmarshal([]byte(trimmed), &arr) - require.NoError(t, err2, "output of '%s' is not valid JSON:\n%s", step.Name, output) - require.NotEmpty(t, arr, "JSON array output of '%s' is empty", step.Name) - parsed = arr[0] - } - - for field, expected := range step.JSONFields { - val, ok := parsed[field] - require.True(t, ok, "JSON output of '%s' missing field %q", step.Name, field) - if expected != "" { - require.Equal(t, expected, fmt.Sprintf("%v", val), - "JSON field %q of '%s' has unexpected value", field, step.Name) - } else { - require.NotEmpty(t, fmt.Sprintf("%v", val), - "JSON field %q of '%s' is empty", field, step.Name) - } - } - - for _, field := range step.JSONFieldsExist { - _, ok := parsed[field] - require.True(t, ok, "JSON output of '%s' missing field %q", step.Name, field) - } - } - - if step.WantFunc != nil { - step.WantFunc(t, output, state) - } -} - -// registerCleanup schedules a CLI delete command to run during test cleanup (even on failure). -func (s *CLILiveTestSuite) registerCleanup(t *testing.T, deleteArgString string, state *LiveTestState) { - t.Cleanup(func() { - args := substituteStateVars(t, deleteArgString, state) - t.Logf("Cleanup: confluent %s", args) - // Best-effort cleanup: log but don't fail - cmd := exec.Command(s.binPath, shellSplit(args)...) - cmd.Env = buildCommandEnv([]string{homeEnvVar(state.homeDir)}) - if err := cmd.Run(); err != nil { - t.Logf("Cleanup: resource already deleted, skipping") - } - }) -} - -// substituteStateVars replaces {{.key}} placeholders in a string with values from state. -func substituteStateVars(t *testing.T, s string, state *LiveTestState) string { - t.Helper() - - if !strings.Contains(s, "{{") { - return s - } - - tmpl, err := template.New("args").Option("missingkey=error").Parse(s) - require.NoError(t, err, "failed to parse template: %s", s) - - var buf bytes.Buffer - err = tmpl.Execute(&buf, state.All()) - require.NoError(t, err, "failed to execute template: %s", s) - - return buf.String() -} - -// extractID extracts the "id" field from JSON output. -func extractID(t *testing.T, output string) string { - t.Helper() - - trimmed := strings.TrimSpace(output) - var parsed map[string]interface{} - if err := json.Unmarshal([]byte(trimmed), &parsed); err != nil { - // Not JSON — try to extract from table-style output (first column of second line) - lines := strings.Split(trimmed, "\n") - for i, line := range lines { - if i == 0 { - continue // skip header - } - fields := strings.Fields(line) - if len(fields) > 0 { - return fields[0] - } - } - return "" - } - - if id, ok := parsed["id"]; ok { - return fmt.Sprintf("%v", id) - } - return "" -} diff --git a/test/live/live_utils.go b/test/live/live_utils.go deleted file mode 100644 index b5906e8b97..0000000000 --- a/test/live/live_utils.go +++ /dev/null @@ -1,122 +0,0 @@ -//go:build live_test - -package live - -import ( - "encoding/json" - "fmt" - "math/rand" - "os" - "os/exec" - "strings" - "testing" - "time" -) - -// uniqueName generates a unique resource name with the given prefix. -func uniqueName(prefix string) string { - return fmt.Sprintf("cli-live-%s-%06d", prefix, rand.Intn(1000000)) -} - -// liveTestCloud returns the cloud provider from env or defaults to "aws". -func liveTestCloud() string { - if v := os.Getenv("CLI_LIVE_TEST_CLOUD"); v != "" { - return v - } - return "aws" -} - -// liveTestRegion returns the cloud region from env or defaults to "us-east-1". -func liveTestRegion() string { - if v := os.Getenv("CLI_LIVE_TEST_REGION"); v != "" { - return v - } - return "us-east-1" -} - -// requiredEnv gets an environment variable or fails the test. -func requiredEnv(t *testing.T, key string) string { - t.Helper() - v := os.Getenv(key) - if v == "" { - t.Fatalf("required environment variable %s is not set", key) - } - return v -} - -// shellSplit splits a command string into arguments, respecting double and single quotes. -// Unlike strings.Fields, it treats quoted substrings as single arguments. -func shellSplit(s string) []string { - var args []string - var current strings.Builder - inQuote := false - var quoteChar byte - - for i := 0; i < len(s); i++ { - c := s[i] - switch { - case inQuote: - if c == quoteChar { - inQuote = false - } else { - current.WriteByte(c) - } - case c == '"' || c == '\'': - inQuote = true - quoteChar = c - case c == ' ' || c == '\t': - if current.Len() > 0 { - args = append(args, current.String()) - current.Reset() - } - default: - current.WriteByte(c) - } - } - if current.Len() > 0 { - args = append(args, current.String()) - } - return args -} - -// extractJSONField extracts a specific field value from JSON output. -func extractJSONField(t *testing.T, output, field string) string { - t.Helper() - var parsed map[string]interface{} - if err := json.Unmarshal([]byte(strings.TrimSpace(output)), &parsed); err != nil { - return "" - } - if val, ok := parsed[field]; ok { - return fmt.Sprintf("%v", val) - } - return "" -} - -// waitForCondition polls a CLI command at the given interval until condition returns true or timeout expires. -// The command template supports {{.key}} substitution from state. Command failures during polling are -// tolerated (the output is still checked against condition). Returns the last output on success. -func (s *CLILiveTestSuite) waitForCondition(t *testing.T, argsTemplate string, state *LiveTestState, - condition func(output string) bool, interval, timeout time.Duration) string { - t.Helper() - - deadline := time.Now().Add(timeout) - args := substituteStateVars(t, argsTemplate, state) - env := buildCommandEnv([]string{homeEnvVar(state.homeDir)}) - - var lastOutput string - for { - cmd := exec.Command(s.binPath, shellSplit(args)...) - cmd.Env = env - out, _ := cmd.CombinedOutput() - lastOutput = string(out) - - if condition(lastOutput) { - return lastOutput - } - if time.Now().After(deadline) { - t.Fatalf("waitForCondition timed out after %s — last output:\n%s", timeout, lastOutput) - } - t.Logf("Condition not met, retrying in %s...", interval) - time.Sleep(interval) - } -} diff --git a/test/live/login_live_test.go b/test/live/login_live_test.go deleted file mode 100644 index 9290c165a1..0000000000 --- a/test/live/login_live_test.go +++ /dev/null @@ -1,69 +0,0 @@ -//go:build live_test && (all || auth) - -package live - -import ( - "fmt" - "os" - "testing" - - "github.com/stretchr/testify/require" -) - -func (s *CLILiveTestSuite) TestLoginLogoutLive() { - t := s.T() - t.Parallel() - - email := requiredEnv(t, "CONFLUENT_CLOUD_EMAIL") - password := requiredEnv(t, "CONFLUENT_CLOUD_PASSWORD") - - // Create isolated HOME directory (not using setupTestContext since that already logs in) - homeDir, err := os.MkdirTemp("", "cli-live-auth-*") - require.NoError(t, err) - t.Cleanup(func() { os.RemoveAll(homeDir) }) - - homeEnv := homeEnvVar(homeDir) - - // Clear all cloud auth env vars so the CLI can't auth implicitly — forces explicit login - noAuthEnv := []string{ - homeEnv, - "CONFLUENT_CLOUD_API_KEY=", - "CONFLUENT_CLOUD_API_SECRET=", - "CONFLUENT_CLOUD_EMAIL=", - "CONFLUENT_CLOUD_PASSWORD=", - } - - // Step 1: Before login, commands requiring auth should fail - t.Run("Verify not logged in", func(t *testing.T) { - output := s.runRawCommand(t, "environment list", noAuthEnv, "", 1) - t.Logf("Pre-login output: %s", output) - }) - - // Step 2: Login with email and password - t.Run("Login with credentials", func(t *testing.T) { - output := s.runRawCommand(t, "login", []string{ - homeEnv, - "CONFLUENT_CLOUD_API_KEY=", - "CONFLUENT_CLOUD_API_SECRET=", - fmt.Sprintf("CONFLUENT_CLOUD_EMAIL=%s", email), - fmt.Sprintf("CONFLUENT_CLOUD_PASSWORD=%s", password), - }, "", 0) - require.Contains(t, output, "Logged in", "login should succeed") - }) - - // Step 3: Verify login succeeded by running an authenticated command (without API key env vars) - t.Run("Verify logged in", func(t *testing.T) { - s.runRawCommand(t, "environment list", noAuthEnv, "", 0) - }) - - // Step 4: Logout - t.Run("Logout", func(t *testing.T) { - s.runRawCommand(t, "logout", noAuthEnv, "", 0) - }) - - // Step 5: After logout, commands requiring auth should fail again - t.Run("Verify logged out", func(t *testing.T) { - output := s.runRawCommand(t, "environment list", noAuthEnv, "", 1) - t.Logf("Post-logout output: %s", output) - }) -} diff --git a/test/live/schema_registry_live_test.go b/test/live/schema_registry_live_test.go deleted file mode 100644 index f72b0fad21..0000000000 --- a/test/live/schema_registry_live_test.go +++ /dev/null @@ -1,109 +0,0 @@ -//go:build live_test && (all || schema_registry) - -package live - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" -) - -func (s *CLILiveTestSuite) TestSchemaRegistrySchemaCRUDLive() { - t := s.T() - t.Parallel() - - envID := os.Getenv("LIVE_TEST_ENVIRONMENT_ID") - if envID == "" { - t.Skip("Skipping: LIVE_TEST_ENVIRONMENT_ID must be set") - } - - state := s.setupTestContext(t) - - subjectName := uniqueName("sr-schema") + "-value" - - // Create temp avro schema files - schemaContent := `{"type":"record","name":"TestRecord","namespace":"io.confluent.test","fields":[{"name":"id","type":"int"},{"name":"name","type":"string"}]}` - - schemaDir, err := os.MkdirTemp("", "cli-live-schema-*") - require.NoError(t, err) - t.Cleanup(func() { os.RemoveAll(schemaDir) }) - - schemaFile := filepath.Join(schemaDir, "test.avsc") - require.NoError(t, os.WriteFile(schemaFile, []byte(schemaContent), 0644)) - - // Create a backward-compatible v2 schema (adds optional field) - schemaV2Content := `{"type":"record","name":"TestRecord","namespace":"io.confluent.test","fields":[{"name":"id","type":"int"},{"name":"name","type":"string"},{"name":"email","type":["null","string"],"default":null}]}` - schemaFileV2 := filepath.Join(schemaDir, "test_v2.avsc") - require.NoError(t, os.WriteFile(schemaFileV2, []byte(schemaV2Content), 0644)) - - // Cleanup: soft-delete all schema versions under this subject - s.registerCleanup(t, "schema-registry schema delete --subject "+subjectName+" --version all --force --environment "+envID, state) - - steps := []CLILiveTest{ - { - Name: "Use environment", - Args: "environment use " + envID, - }, - { - Name: "Register schema v1", - Args: "schema-registry schema create --subject " + subjectName + " --schema " + schemaFile + " --type avro --environment " + envID + " -o json", - JSONFieldsExist: []string{"id"}, - WantFunc: func(t *testing.T, output string, state *LiveTestState) { - t.Helper() - id := extractJSONField(t, output, "id") - require.NotEmpty(t, id, "failed to extract schema id") - state.Set("schema_id", id) - t.Logf("Captured schema_id = %s", id) - }, - }, - { - Name: "Register schema v2", - Args: "schema-registry schema create --subject " + subjectName + " --schema " + schemaFileV2 + " --type avro --environment " + envID + " -o json", - JSONFieldsExist: []string{"id"}, - }, - { - Name: "Describe schema by ID", - Args: "schema-registry schema describe {{.schema_id}} --environment " + envID, - UseStateVars: true, - Contains: []string{"TestRecord"}, - }, - { - Name: "Describe schema by subject and version", - Args: "schema-registry schema describe --subject " + subjectName + " --version latest --environment " + envID, - Contains: []string{"TestRecord", "email"}, - }, - { - Name: "List schemas for subject", - Args: "schema-registry schema list --subject-prefix " + subjectName + " --environment " + envID, - Contains: []string{subjectName}, - }, - { - Name: "List subjects", - Args: "schema-registry subject list --environment " + envID, - Contains: []string{subjectName}, - Retries: 3, - }, - { - Name: "Describe subject versions", - Args: "schema-registry subject describe " + subjectName + " --environment " + envID, - Contains: []string{"1"}, - }, - { - Name: "Describe global SR configuration", - Args: "schema-registry configuration describe --environment " + envID + " -o json", - JSONFieldsExist: []string{"compatibility_level"}, - }, - { - Name: "Delete all schema versions (soft delete)", - Args: "schema-registry schema delete --subject " + subjectName + " --version all --force --environment " + envID, - }, - } - - for _, step := range steps { - t.Run(step.Name, func(t *testing.T) { - s.runLiveCommand(t, step, state) - }) - } -} diff --git a/test/live/service_account_live_test.go b/test/live/service_account_live_test.go deleted file mode 100644 index 7cec96b820..0000000000 --- a/test/live/service_account_live_test.go +++ /dev/null @@ -1,82 +0,0 @@ -//go:build live_test && (all || core) - -package live - -import ( - "testing" -) - -func (s *CLILiveTestSuite) TestServiceAccountCRUDLive() { - t := s.T() - t.Parallel() - state := s.setupTestContext(t) - - saName := uniqueName("sa") - saDescription := "Live test service account" - updatedDescription := "Updated live test service account" - - // Register cleanup immediately - s.registerCleanup(t, "iam service-account delete {{.sa_id}} --force", state) - - steps := []CLILiveTest{ - { - Name: "Create service account", - Args: `iam service-account create ` + saName + ` --description "` + saDescription + `" -o json`, - ExitCode: 0, - JSONFields: map[string]string{ - "name": saName, - "description": saDescription, - }, - JSONFieldsExist: []string{"id"}, - CaptureID: "sa_id", - }, - { - Name: "Describe service account", - Args: "iam service-account describe {{.sa_id}} -o json", - UseStateVars: true, - ExitCode: 0, - JSONFields: map[string]string{ - "name": saName, - }, - }, - { - Name: "List service accounts", - Args: "iam service-account list", - ExitCode: 0, - Contains: []string{saName}, - }, - { - Name: "Update service account description", - Args: `iam service-account update {{.sa_id}} --description "` + updatedDescription + `"`, - UseStateVars: true, - ExitCode: 0, - }, - { - Name: "Describe updated service account", - Args: "iam service-account describe {{.sa_id}} -o json", - UseStateVars: true, - ExitCode: 0, - JSONFields: map[string]string{ - "description": updatedDescription, - }, - }, - { - Name: "Delete service account", - Args: "iam service-account delete {{.sa_id}} --force", - UseStateVars: true, - ExitCode: 0, - }, - { - Name: "Verify deletion", - Args: "iam service-account describe {{.sa_id}}", - UseStateVars: true, - ExitCode: 1, - }, - } - - for _, step := range steps { - t.Run(step.Name, func(t *testing.T) { - s.runLiveCommand(t, step, state) - }) - } -}