diff --git a/.github/workflows/e2e-tests-dispatch.yml b/.github/workflows/e2e-tests-dispatch.yml new file mode 100644 index 00000000..1387b9a5 --- /dev/null +++ b/.github/workflows/e2e-tests-dispatch.yml @@ -0,0 +1,30 @@ +name: End-to-end tests dispatch +on: + workflow_dispatch: + inputs: + clientId: + description: Client ID to use for authentication + required: true + type: string + clientSecret: + description: Client secret to use for authentication + type: string + required: true + oktaOrgUrl: + description: Okta organization URL + required: false + type: string + oktaAuthServer: + description: Okta authentication server identifier + required: false + type: string +jobs: + test: + uses: ./.github/workflows/e2e-tests.yml + with: + clientId: "${{ inputs.clientId }}" + ref: "${{ github.ref_name }}" + oktaOrgUrl: "${{ inputs.oktaOrgUrl }}" + oktaAuthServer: "${{ inputs.oktaAuthServer }}" + secrets: + clientSecret: "${{ inputs.clientSecret }}" diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml new file mode 100644 index 00000000..9e561b23 --- /dev/null +++ b/.github/workflows/e2e-tests.yml @@ -0,0 +1,46 @@ +name: End-to-end tests +on: + workflow_call: + inputs: + clientId: + description: Client ID to use for authentication + required: true + type: string + oktaOrgUrl: + description: Okta organization URL + required: false + type: string + oktaAuthServer: + description: Okta authentication server identifier + required: false + type: string + ref: + description: Reference branch, tag or commit SHA to checkout + required: false + type: string + default: main + secrets: + clientSecret: + description: Client secret to use for authentication + required: true +jobs: + test: + name: Run e2e tests + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + with: + repository: nobl9/nobl9-go + ref: ${{ inputs.ref }} + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: false + - name: Run tests + env: + NOBL9_SDK_CLIENT_ID: ${{ inputs.clientId }} + NOBL9_SDK_CLIENT_SECRET: ${{ secrets.clientSecret }} + NOBL9_SDK_OKTA_ORG_URL: ${{ inputs.oktaOrgUrl }} + NOBL9_SDK_OKTA_AUTH_SERVER: ${{ inputs.oktaAuthServer }} + run: make test/e2e diff --git a/.gitignore b/.gitignore index 0718f251..5c1db5c7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ bin/ # Dependency directories (remove the comment below to include it) -# vendor/ +go.work.sum # IDE .idea/ diff --git a/Makefile b/Makefile index 6fce6063..6cce7618 100644 --- a/Makefile +++ b/Makefile @@ -8,9 +8,9 @@ GO_ENUM_VERSION := v0.6.0 # renovate datasource=github-releases depName=securego/gosec GOSEC_VERSION := v2.20.0 # renovate datasource=github-releases depName=golangci/golangci-lint -GOLANGCI_LINT_VERSION := v1.59.0 +GOLANGCI_LINT_VERSION := v1.59.1 # renovate datasource=go depName=golang.org/x/vuln/cmd/govulncheck -GOVULNCHECK_VERSION := v1.1.1 +GOVULNCHECK_VERSION := v1.1.2 # renovate datasource=go depName=golang.org/x/tools/cmd/goimports GOIMPORTS_VERSION := v0.22.0 # renovate datasource=go depName=github.com/vburenin/ifacemaker @@ -36,11 +36,16 @@ define _print_check_step printf -- '------\n%s...\n' "${1}" endef -.PHONY: test test/record +.PHONY: test test/e2e test/record ## Run all unit tests. test: go test -race -cover ./... ./docs/mock_example +## Run all end-to-end tests (requires Nobl9 platform credentials). +test/e2e: + # The '-count=1' flag disables tests results caching, as per https://go.dev/doc/go1.10#test. + go test -count=1 -race -test.v -timeout=5m -tags=e2e_test ./tests + ## Record tests and save them in ./bin/recorded-tests.json. test/record: RECORD_FILE="$(abspath $(dir .))/$(BIN_DIR)/recorded-tests" ; \ diff --git a/docs/mock_example/go.mod b/docs/mock_example/go.mod index c824dffc..2fc76496 100644 --- a/docs/mock_example/go.mod +++ b/docs/mock_example/go.mod @@ -12,12 +12,13 @@ require ( github.com/BurntSushi/toml v1.4.0 // indirect github.com/MicahParks/jwkset v0.5.18 // indirect github.com/MicahParks/keyfunc/v3 v3.3.3 // indirect - github.com/aws/aws-sdk-go v1.53.15 // indirect + github.com/aws/aws-sdk-go v1.53.16 // indirect github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.16.0 // indirect github.com/goccy/go-yaml v1.11.3 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect @@ -26,10 +27,10 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/teambition/rrule-go v1.8.2 // indirect - golang.org/x/crypto v0.18.0 // indirect + golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 // indirect golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/docs/mock_example/go.sum b/docs/mock_example/go.sum index 9f51a0b1..982ce2c4 100644 --- a/docs/mock_example/go.sum +++ b/docs/mock_example/go.sum @@ -4,7 +4,7 @@ github.com/MicahParks/jwkset v0.5.18 h1:WLdyMngF7rCrnstQxA7mpRoxeaWqGzPM/0z40PJU github.com/MicahParks/jwkset v0.5.18/go.mod h1:q8ptTGn/Z9c4MwbcfeCDssADeVQb3Pk7PnVxrvi+2QY= github.com/MicahParks/keyfunc/v3 v3.3.3 h1:c6j9oSu1YUo0k//KwF1miIQlEMtqNlj7XBFLB8jtEmY= github.com/MicahParks/keyfunc/v3 v3.3.3/go.mod h1:f/UMyXdKfkZzmBeBFUeYk+zu066J1Fcl48f7Wnl5Z48= -github.com/aws/aws-sdk-go v1.53.15 h1:FtZmkg7xM8RfP2oY6p7xdKBYrRgkITk9yve2QV7N938= +github.com/aws/aws-sdk-go v1.53.16 h1:8oZjKQO/ml1WLUZw5hvF7pvYjPf8o9f57Wldoy/q9Qc= github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -22,8 +22,7 @@ github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I= github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= @@ -54,15 +53,14 @@ github.com/teambition/rrule-go v1.8.2 h1:lIjpjvWTj9fFUZCmuoVDrKVOtdiyzbzc93qTmRV github.com/teambition/rrule-go v1.8.2/go.mod h1:Ieq5AbrKGciP1V//Wq8ktsTXwSwJHDD5mD/wLBGl3p4= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo= golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= diff --git a/go.mod b/go.mod index a8700d54..bf4e2a72 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/BurntSushi/toml v1.4.0 github.com/MicahParks/jwkset v0.5.18 github.com/MicahParks/keyfunc/v3 v3.3.3 - github.com/aws/aws-sdk-go v1.53.17 + github.com/aws/aws-sdk-go v1.53.20 github.com/bmatcuk/doublestar/v4 v4.6.1 github.com/goccy/go-yaml v1.11.3 github.com/golang-jwt/jwt/v5 v5.2.1 @@ -14,21 +14,20 @@ require ( github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.9.0 github.com/teambition/rrule-go v1.8.2 - golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 + golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 golang.org/x/text v0.16.0 golang.org/x/time v0.5.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fatih/color v1.16.0 // indirect + github.com/fatih/color v1.17.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.18.0 // indirect golang.org/x/sys v0.20.0 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 082a6a1f..8431261b 100644 --- a/go.sum +++ b/go.sum @@ -4,15 +4,15 @@ github.com/MicahParks/jwkset v0.5.18 h1:WLdyMngF7rCrnstQxA7mpRoxeaWqGzPM/0z40PJU github.com/MicahParks/jwkset v0.5.18/go.mod h1:q8ptTGn/Z9c4MwbcfeCDssADeVQb3Pk7PnVxrvi+2QY= github.com/MicahParks/keyfunc/v3 v3.3.3 h1:c6j9oSu1YUo0k//KwF1miIQlEMtqNlj7XBFLB8jtEmY= github.com/MicahParks/keyfunc/v3 v3.3.3/go.mod h1:f/UMyXdKfkZzmBeBFUeYk+zu066J1Fcl48f7Wnl5Z48= -github.com/aws/aws-sdk-go v1.53.17 h1:TwtYMzVBTaqPVj/pcemHRIgk01OycWEcEUyUUX0tpCI= -github.com/aws/aws-sdk-go v1.53.17/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go v1.53.20 h1:cYWPvZLP1gPj5CfUdnfjaaA7WFK3FGoJ/R9+Ks1inU4= +github.com/aws/aws-sdk-go v1.53.20/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= 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/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= @@ -23,8 +23,8 @@ github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I= github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= @@ -51,10 +51,10 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/teambition/rrule-go v1.8.2 h1:lIjpjvWTj9fFUZCmuoVDrKVOtdiyzbzc93qTmRVe/J8= github.com/teambition/rrule-go v1.8.2/go.mod h1:Ieq5AbrKGciP1V//Wq8ktsTXwSwJHDD5mD/wLBGl3p4= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo= -golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM= +golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= @@ -63,8 +63,8 @@ golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= diff --git a/go.work b/go.work index d421f4ce..42d7e2f6 100644 --- a/go.work +++ b/go.work @@ -3,5 +3,4 @@ go 1.22 use ( . ./docs/mock_example - ./sdk/test_data/client/simple_module ) diff --git a/go.work.sum b/go.work.sum deleted file mode 100644 index 1e716742..00000000 --- a/go.work.sum +++ /dev/null @@ -1,70 +0,0 @@ -github.com/aws/aws-sdk-go v1.53.15/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= -github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/lestrrat-go/jwx v1.2.28 h1:uadI6o0WpOVrBSf498tRXZIwPpEtLnR9CvqPFXeI5sA= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= -github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= -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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -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-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/manifest/v1alpha/slo/validation.go b/manifest/v1alpha/slo/validation.go index 35d2d69f..1aba1f4e 100644 --- a/manifest/v1alpha/slo/validation.go +++ b/manifest/v1alpha/slo/validation.go @@ -402,6 +402,8 @@ var specValidationComposite = validation.New[Spec]( Rules(validation.SliceLength[[]Objective](1, 1). WithMessage("this SLO contains a composite objective. No more objectives can be added to it")). IncludeForEach(validation.New[Objective]( + validation.For(func(o Objective) ObjectiveBase { return o.ObjectiveBase }). + Include(objectiveBaseValidation), validation.ForPointer(func(o Objective) *CompositeSpec { return o.Composite }). WithName("composite"). Include(validation.New[CompositeSpec]( diff --git a/manifest/v1alpha/slo/validation_composite_test.go b/manifest/v1alpha/slo/validation_composite_test.go index d5a784da..faea71be 100644 --- a/manifest/v1alpha/slo/validation_composite_test.go +++ b/manifest/v1alpha/slo/validation_composite_test.go @@ -1,6 +1,7 @@ package slo import ( + "strings" "testing" "github.com/nobl9/nobl9-go/internal/testutils" @@ -15,6 +16,26 @@ func TestValidate_CompositeSLO(t *testing.T) { err := validate(slo) testutils.AssertNoError(t, slo, err) }) + t.Run("fails - invalid objective name - too long", func(t *testing.T) { + slo := validCompositeSLO() + slo.Spec.Objectives[0].Name = strings.Repeat("a", 64) + err := validate(slo) + + testutils.AssertContainsErrors(t, slo, err, 1, testutils.ExpectedError{ + Prop: "spec.objectives[0].name", + Code: validation.ErrorCodeStringLength, + }) + }) + t.Run("fails - invalid objective display name - too long", func(t *testing.T) { + slo := validCompositeSLO() + slo.Spec.Objectives[0].DisplayName = strings.Repeat("a", 64) + err := validate(slo) + + testutils.AssertContainsErrors(t, slo, err, 1, testutils.ExpectedError{ + Prop: "spec.objectives[0].displayName", + Code: validation.ErrorCodeStringMaxLength, + }) + }) t.Run("fails - spec.indicator provided", func(t *testing.T) { for _, ind := range []Indicator{ { diff --git a/package.json b/package.json index c0b75691..0c63ea1e 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "devDependencies": { "cspell": "8.8.4", "markdownlint-cli": "0.41.0", - "yaml": "2.4.3" + "yaml": "2.4.5" }, "scripts": { "check-trailing-whitespaces": "node ./scripts/check-trailing-whitespaces.js", diff --git a/sdk/credentials.go b/sdk/credentials.go index f34f600c..93f7b3af 100644 --- a/sdk/credentials.go +++ b/sdk/credentials.go @@ -71,7 +71,7 @@ type credentials struct { clientID string clientSecret string - mu sync.Mutex + mu sync.RWMutex } // GetEnvironment first ensures a token has been parsed before returning the environment, @@ -95,6 +95,8 @@ func (c *credentials) GetOrganization(ctx context.Context) (string, error) { if _, err := c.refreshAccessToken(ctx); err != nil { return "", errors.Wrap(err, "failed to get organization") } + c.mu.RLock() + defer c.mu.RUnlock() return c.organization, nil } @@ -105,6 +107,8 @@ func (c *credentials) GetUser(ctx context.Context) (string, error) { if _, err := c.refreshAccessToken(ctx); err != nil { return "", errors.Wrap(err, "failed to get user") } + c.mu.RLock() + defer c.mu.RUnlock() switch c.tokenType { case tokenTypeM2M: return c.claims.M2MProfile.Value.User, nil @@ -153,9 +157,13 @@ func (c *credentials) refreshAccessToken(ctx context.Context) (updated bool, err if c.config.DisableOkta { return false, nil } + c.mu.RLock() if !c.shouldRefresh() { + c.mu.RUnlock() return false, nil } + c.mu.RUnlock() + c.mu.Lock() defer c.mu.Unlock() if !c.shouldRefresh() { diff --git a/tests/helpers_test.go b/tests/helpers_test.go new file mode 100644 index 00000000..a6edcf00 --- /dev/null +++ b/tests/helpers_test.go @@ -0,0 +1,108 @@ +//go:build e2e_test + +package tests + +import ( + "context" + "fmt" + "regexp" + "strconv" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/nobl9/nobl9-go/manifest" + "github.com/nobl9/nobl9-go/manifest/v1alpha" + v1alphaProject "github.com/nobl9/nobl9-go/manifest/v1alpha/project" +) + +const objectDescription = "Object generated by e2e SDK tests" + +var ( + testStartTime = time.Now() + objectsCounter = atomic.Int64{} + uniqueTestIdentifierLabel = struct { + Key string + Value string + }{ + Key: "sdk-e2e-test-id", + Value: strconv.Itoa(int(testStartTime.UnixNano())), + } + commonAnnotations = v1alpha.MetadataAnnotations{"origin": "sdk-e2e-test"} +) + +var ( + timeRFC3339Regexp = regexp.MustCompile(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z`) + userIDRegexp = regexp.MustCompile(`[a-zA-Z0-9]{20}`) +) + +type objectsEqualityAssertFunc[T manifest.Object] func(t *testing.T, expected, actual T) + +func assertSubset[T manifest.Object](t *testing.T, actual, expected []T, f objectsEqualityAssertFunc[T]) { + t.Helper() + for i := range expected { + found := false + for j := range actual { + if actual[j].GetName() == expected[i].GetName() { + f(t, expected[i], actual[j]) + found = true + break + } + } + if !found { + t.Errorf("expected object %s not found in the actual list", expected[i].GetName()) + } + } +} + +func v1Apply[T manifest.Object](t *testing.T, ctx context.Context, inputs []T) { + t.Helper() + objects := make([]manifest.Object, 0, len(inputs)) + for _, input := range inputs { + objects = append(objects, input) + } + err := client.Objects().V1().Apply(ctx, objects) + require.NoError(t, err) +} + +func v1Delete[T manifest.Object](t *testing.T, ctx context.Context, inputs []T) { + t.Helper() + objects := make([]manifest.Object, 0, len(inputs)) + for _, input := range inputs { + objects = append(objects, input) + } + err := client.Objects().V1().Delete(ctx, objects) + require.NoError(t, err) +} + +func generateV1alphaProject(t *testing.T) v1alphaProject.Project { + t.Helper() + return v1alphaProject.New( + v1alphaProject.Metadata{ + Name: generateName(), + Labels: annotateLabels(t, v1alpha.Labels{}), + Annotations: commonAnnotations, + }, + v1alphaProject.Spec{ + Description: objectDescription, + }, + ) +} + +// generateName generates a unique name for the test object. +func generateName() string { + return fmt.Sprintf("sdk-e2e-%d-%d", objectsCounter.Add(1), testStartTime.UnixNano()) +} + +// annotateLabels adds origin label to the provided labels, +// so it's easier to locate the leftovers from these tests. +// It also adds unique test identifier label to the provided labels so that we can reliably retrieve objects created withing a given test without . +func annotateLabels(t *testing.T, labels v1alpha.Labels) v1alpha.Labels { + t.Helper() + labels["origin"] = []string{"sdk-e2e-test"} + labels[uniqueTestIdentifierLabel.Key] = []string{uniqueTestIdentifierLabel.Value} + labels["sdk-test-name"] = []string{t.Name()} + return labels +} diff --git a/tests/main_test.go b/tests/main_test.go new file mode 100644 index 00000000..cd35d774 --- /dev/null +++ b/tests/main_test.go @@ -0,0 +1,120 @@ +//go:build e2e_test + +package tests + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "testing" + + "github.com/nobl9/nobl9-go/sdk" +) + +const defaultProject = "sdk-e2e-default" + +var client *sdk.Client + +func TestMain(m *testing.M) { + os.Exit(runTestMain(m)) +} + +func runTestMain(m *testing.M) int { + var err error + if client, err = sdk.DefaultClient(); err != nil { + printError("failed to create %T: %v", client, err) + return 1 + } + client.Config.Project = defaultProject + org, err := client.GetOrganization(context.Background()) + if err != nil { + printError("failed to get test organization: %v", err) + return 1 + } + fmt.Printf("Running SDK end-to-end tests\nOrganization: %s\nAuth Server: %s\nClient ID: %s\n\n", + org, client.Config.OktaOrgURL.JoinPath(client.Config.OktaAuthServer), client.Config.ClientID) + defer cleanupLabels() + return m.Run() +} + +// cleanupLabels deletes all unique labels created during the test. +func cleanupLabels() { + labelID, err := getLabelIDByName(uniqueTestIdentifierLabel.Key) + if err != nil { + printError("failed to get label ID by name: %v", err) + return + } + + var buf bytes.Buffer + if err = json.NewEncoder(&buf).Encode([]string{labelID}); err != nil { + printError("failed to encode cleanup labels payload: %v", err) + return + } + req, err := client.CreateRequest( + context.Background(), + http.MethodPost, + "labels/delete", + nil, + nil, + &buf, + ) + if err != nil { + printError("failed to create cleanup labels request: %v", err) + return + } + resp, err := client.Do(req) + if err != nil { + printError("failed to send cleanup labels request: %v", err) + return + } + defer func() { _ = resp.Body.Close() }() + if resp.StatusCode >= 300 { + rawErr, _ := io.ReadAll(resp.Body) + printError("failed to cleanup labels, code: %d, body: %s", resp.StatusCode, string(rawErr)) + return + } +} + +func getLabelIDByName(name string) (string, error) { + req, err := client.CreateRequest( + context.Background(), + http.MethodGet, + "labels", + nil, + nil, + nil, + ) + if err != nil { + return "", err + } + resp, err := client.Do(req) + if err != nil { + return "", err + } + defer func() { _ = resp.Body.Close() }() + if resp.StatusCode != http.StatusOK { + rawErr, _ := io.ReadAll(resp.Body) + return "", fmt.Errorf("failed to cleanup labels, code: %d, body: %s", resp.StatusCode, string(rawErr)) + } + var labels []struct { + ID string `json:"id"` + Key string `json:"key"` + } + if err = json.NewDecoder(resp.Body).Decode(&labels); err != nil { + return "", err + } + for _, label := range labels { + if label.Key == name { + return label.ID, nil + } + } + return "", fmt.Errorf("label '%s' not found", name) +} + +func printError(format string, a ...interface{}) { + fmt.Fprintf(os.Stderr, format+"\n", a...) +} diff --git a/tests/v1alpha_project_test.go b/tests/v1alpha_project_test.go new file mode 100644 index 00000000..2735be7d --- /dev/null +++ b/tests/v1alpha_project_test.go @@ -0,0 +1,106 @@ +//go:build e2e_test + +package tests + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/nobl9/nobl9-go/manifest/v1alpha" + v1alphaProject "github.com/nobl9/nobl9-go/manifest/v1alpha/project" + objectsV1 "github.com/nobl9/nobl9-go/sdk/endpoints/objects/v1" +) + +func Test_Objects_V1_V1alpha_Project(t *testing.T) { + t.Parallel() + ctx := context.Background() + inputs := []v1alphaProject.Project{ + v1alphaProject.New( + v1alphaProject.Metadata{ + Name: generateName(), + Labels: annotateLabels(t, v1alpha.Labels{"team": []string{"green"}}), + Annotations: commonAnnotations, + }, + v1alphaProject.Spec{ + Description: objectDescription, + }, + ), + v1alphaProject.New( + v1alphaProject.Metadata{ + Name: generateName(), + Labels: annotateLabels(t, v1alpha.Labels{"team": []string{"orange"}}), + Annotations: commonAnnotations, + }, + v1alphaProject.Spec{ + Description: objectDescription, + }, + ), + v1alphaProject.New( + v1alphaProject.Metadata{ + Name: generateName(), + Labels: annotateLabels(t, v1alpha.Labels{"team": []string{"orange"}}), + Annotations: commonAnnotations, + }, + v1alphaProject.Spec{ + Description: objectDescription, + }, + ), + } + + v1Apply(t, ctx, inputs) + t.Cleanup(func() { v1Delete(t, ctx, inputs) }) + + filterTests := map[string]struct { + request objectsV1.GetProjectsRequest + expected []v1alphaProject.Project + returnsAll bool + }{ + "get all": { + request: objectsV1.GetProjectsRequest{}, + expected: inputs, + returnsAll: true, + }, + "filter by name": { + request: objectsV1.GetProjectsRequest{ + Names: []string{inputs[0].Metadata.Name}, + }, + expected: []v1alphaProject.Project{inputs[0]}, + }, + "filter by label": { + request: objectsV1.GetProjectsRequest{ + Labels: annotateLabels(t, v1alpha.Labels{"team": []string{"orange"}}), + }, + expected: []v1alphaProject.Project{inputs[1], inputs[2]}, + }, + "filter by label and name": { + request: objectsV1.GetProjectsRequest{ + Names: []string{inputs[2].Metadata.Name}, + Labels: annotateLabels(t, v1alpha.Labels{"team": []string{"orange"}}), + }, + expected: []v1alphaProject.Project{inputs[2]}, + }, + } + for name, test := range filterTests { + t.Run(name, func(t *testing.T) { + t.Parallel() + actual, err := client.Objects().V1().GetV1alphaProjects(ctx, test.request) + require.NoError(t, err) + if !test.returnsAll { + require.Len(t, actual, len(test.expected)) + } + assertSubset(t, actual, test.expected, assertProjectsAreEqual) + }) + } +} + +func assertProjectsAreEqual(t *testing.T, expected, actual v1alphaProject.Project) { + t.Helper() + assert.Regexp(t, timeRFC3339Regexp, actual.Spec.CreatedAt) + assert.Regexp(t, userIDRegexp, actual.Spec.CreatedBy) + actual.Spec.CreatedAt = "" + actual.Spec.CreatedBy = "" + assert.Equal(t, expected, actual) +} diff --git a/tests/v1alpha_service_test.go b/tests/v1alpha_service_test.go new file mode 100644 index 00000000..93d02f36 --- /dev/null +++ b/tests/v1alpha_service_test.go @@ -0,0 +1,131 @@ +//go:build e2e_test + +package tests + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/nobl9/nobl9-go/manifest" + "github.com/nobl9/nobl9-go/manifest/v1alpha" + v1alphaService "github.com/nobl9/nobl9-go/manifest/v1alpha/service" + objectsV1 "github.com/nobl9/nobl9-go/sdk/endpoints/objects/v1" +) + +func Test_Objects_V1_V1alpha_Service(t *testing.T) { + t.Parallel() + ctx := context.Background() + project := generateV1alphaProject(t) + allObjects := []manifest.Object{ + project, + v1alphaService.New( + v1alphaService.Metadata{ + Name: generateName(), + Project: defaultProject, + Labels: annotateLabels(t, v1alpha.Labels{"team": []string{"orange"}}), + Annotations: commonAnnotations, + }, + v1alphaService.Spec{ + Description: objectDescription, + }, + ), + v1alphaService.New( + v1alphaService.Metadata{ + Name: generateName(), + Project: project.GetName(), + Labels: annotateLabels(t, v1alpha.Labels{"team": []string{"orange"}}), + Annotations: commonAnnotations, + }, + v1alphaService.Spec{ + Description: objectDescription, + }, + ), + v1alphaService.New( + v1alphaService.Metadata{ + Name: generateName(), + Project: project.GetName(), + Labels: annotateLabels(t, v1alpha.Labels{"team": []string{"green"}}), + Annotations: commonAnnotations, + }, + v1alphaService.Spec{ + Description: objectDescription, + }, + ), + v1alphaService.New( + v1alphaService.Metadata{ + Name: generateName(), + Project: project.GetName(), + Labels: annotateLabels(t, v1alpha.Labels{"team": []string{"orange"}}), + Annotations: commonAnnotations, + }, + v1alphaService.Spec{ + Description: objectDescription, + }, + ), + } + + v1Apply(t, ctx, allObjects) + t.Cleanup(func() { v1Delete(t, ctx, allObjects) }) + inputs := manifest.FilterByKind[v1alphaService.Service](allObjects) + + filterTests := map[string]struct { + request objectsV1.GetServicesRequest + expected []v1alphaService.Service + returnsAll bool + }{ + "default project": { + request: objectsV1.GetServicesRequest{}, + expected: []v1alphaService.Service{inputs[0]}, + returnsAll: true, + }, + "filter by project": { + request: objectsV1.GetServicesRequest{ + Project: project.GetName(), + }, + expected: []v1alphaService.Service{inputs[1], inputs[2], inputs[3]}, + }, + "filter by name": { + request: objectsV1.GetServicesRequest{ + Project: project.GetName(), + Names: []string{inputs[1].Metadata.Name}, + }, + expected: []v1alphaService.Service{inputs[1]}, + }, + "filter by label": { + request: objectsV1.GetServicesRequest{ + Project: project.GetName(), + Labels: annotateLabels(t, v1alpha.Labels{"team": []string{"green"}}), + }, + expected: []v1alphaService.Service{inputs[2]}, + }, + "filter by label and name": { + request: objectsV1.GetServicesRequest{ + Project: project.GetName(), + Names: []string{inputs[3].Metadata.Name}, + Labels: annotateLabels(t, v1alpha.Labels{"team": []string{"orange"}}), + }, + expected: []v1alphaService.Service{inputs[3]}, + }, + } + for name, test := range filterTests { + t.Run(name, func(t *testing.T) { + t.Parallel() + actual, err := client.Objects().V1().GetV1alphaServices(ctx, test.request) + require.NoError(t, err) + if !test.returnsAll { + require.Len(t, actual, len(test.expected)) + } + assertSubset(t, actual, test.expected, assertServicesAreEqual) + }) + } +} + +func assertServicesAreEqual(t *testing.T, expected, actual v1alphaService.Service) { + t.Helper() + assert.NotNil(t, actual.Status) + actual.Status = nil + assert.Equal(t, expected, actual) +}