diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cc4c40e..4ac6fe7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -127,15 +127,6 @@ build: - dist/ expire_in: 1 day -integration-test-linux: - stage: integration-test - image: cimg/go:1.22 - extends: .go-cache - dependencies: - - build - script: | - TEST_CLI_PATH=$PWD/dist/platform_linux_amd64_v1/platform go test -v ./tests/... - release: stage: release rules: diff --git a/Makefile b/Makefile index 2ca1202..213518d 100644 --- a/Makefile +++ b/Makefile @@ -84,11 +84,7 @@ release: goreleaser clean-phar internal/legacy/archives/platform.phar php ## Rel .PHONY: test test: ## Run unit tests go clean -testcache - go test -v -race -short -cover ./... - -.PHONY: integration-test -integration-test: single - go test -v ./tests/... + go test -v -race -mod=readonly -cover ./... golangci-lint: command -v golangci-lint >/dev/null || go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) diff --git a/internal/mockapi/api_server.go b/pkg/mockapi/api_server.go similarity index 96% rename from internal/mockapi/api_server.go rename to pkg/mockapi/api_server.go index 3a1b62c..1e1f40e 100644 --- a/internal/mockapi/api_server.go +++ b/pkg/mockapi/api_server.go @@ -53,6 +53,7 @@ func NewHandler(t *testing.T) *Handler { h.Mux.Post("/organizations/{id}/subscriptions", h.handleCreateSubscription) h.Mux.Get("/subscriptions/{id}", h.handleGetSubscription) + h.Mux.Get("/organizations/{id}/subscriptions/can-create", h.handleCanCreateSubscriptions) h.Mux.Get("/organizations/{id}/setup/options", func(w http.ResponseWriter, _ *http.Request) { type options struct { Plans []string `json:"plans"` diff --git a/internal/mockapi/auth_server.go b/pkg/mockapi/auth_server.go similarity index 100% rename from internal/mockapi/auth_server.go rename to pkg/mockapi/auth_server.go diff --git a/internal/mockapi/environments.go b/pkg/mockapi/environments.go similarity index 100% rename from internal/mockapi/environments.go rename to pkg/mockapi/environments.go diff --git a/internal/mockapi/model.go b/pkg/mockapi/model.go similarity index 95% rename from internal/mockapi/model.go rename to pkg/mockapi/model.go index 6e7bb7a..d8c0a5f 100644 --- a/internal/mockapi/model.go +++ b/pkg/mockapi/model.go @@ -33,6 +33,18 @@ type Subscription struct { ProjectUI string `json:"project_ui"` } +type CanCreateRequiredAction struct { + Action string `json:"action"` + Type string `json:"type"` +} + +type CanCreateResponse struct { + CanCreate bool `json:"can_create"` + Message string `json:"message"` + + RequiredAction *CanCreateRequiredAction `json:"required_action"` +} + type ProjectRepository struct { URL string `json:"url"` } diff --git a/internal/mockapi/orgs.go b/pkg/mockapi/orgs.go similarity index 100% rename from internal/mockapi/orgs.go rename to pkg/mockapi/orgs.go diff --git a/internal/mockapi/projects.go b/pkg/mockapi/projects.go similarity index 100% rename from internal/mockapi/projects.go rename to pkg/mockapi/projects.go diff --git a/internal/mockapi/store.go b/pkg/mockapi/store.go similarity index 87% rename from internal/mockapi/store.go rename to pkg/mockapi/store.go index 1ccd985..3b0baf7 100644 --- a/internal/mockapi/store.go +++ b/pkg/mockapi/store.go @@ -13,6 +13,8 @@ type store struct { subscriptions map[string]*Subscription userGrants []*UserGrant + canCreate map[string]*CanCreateResponse + projectBackups map[string]map[string]*Backup } @@ -43,6 +45,15 @@ func (s *store) SetOrgs(orgs []*Org) { } } +func (s *store) SetCanCreate(orgID string, r *CanCreateResponse) { + s.Lock() + defer s.Unlock() + if s.canCreate == nil { + s.canCreate = make(map[string]*CanCreateResponse) + } + s.canCreate[orgID] = r +} + func (s *store) SetUserGrants(grants []*UserGrant) { s.Lock() defer s.Unlock() diff --git a/internal/mockapi/subscriptions.go b/pkg/mockapi/subscriptions.go similarity index 87% rename from internal/mockapi/subscriptions.go rename to pkg/mockapi/subscriptions.go index 921567c..894eb8e 100644 --- a/internal/mockapi/subscriptions.go +++ b/pkg/mockapi/subscriptions.go @@ -70,3 +70,14 @@ func (h *Handler) handleGetSubscription(w http.ResponseWriter, req *http.Request } _ = json.NewEncoder(w).Encode(sub) } + +func (h *Handler) handleCanCreateSubscriptions(w http.ResponseWriter, req *http.Request) { + h.store.RLock() + defer h.store.RUnlock() + id := chi.URLParam(req, "id") + cc := h.store.canCreate[id] + if cc == nil { + cc = &CanCreateResponse{CanCreate: true} + } + _ = json.NewEncoder(w).Encode(cc) +} diff --git a/internal/mockapi/users.go b/pkg/mockapi/users.go similarity index 100% rename from internal/mockapi/users.go rename to pkg/mockapi/users.go diff --git a/tests/app_config_test.go b/tests/app_config_test.go deleted file mode 100644 index 113e7c1..0000000 --- a/tests/app_config_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package tests - -import ( - "encoding/base64" - "encoding/json" - "net/http/httptest" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/platformsh/cli/internal/mockapi" -) - -func TestAppConfig(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - apiHandler := mockapi.NewHandler(t) - - projectID := "aht1iegh3nei9" - - apiHandler.SetProjects([]*mockapi.Project{{ - ID: projectID, - Links: mockapi.MakeHALLinks("self=/projects/"+projectID, - "environments=/projects/"+projectID+"/environments"), - DefaultBranch: "main", - }}) - - main := makeEnv(projectID, "main", "production", "active", nil) - main.SetCurrentDeployment(&mockapi.Deployment{ - WebApps: map[string]mockapi.App{ - "app": {Name: "app", Type: "golang:1.23", Size: "M", Disk: 2048, Mounts: map[string]mockapi.Mount{}}, - }, - Links: mockapi.MakeHALLinks("self=/projects/" + projectID + "/environments/main/deployment/current"), - }) - envs := []*mockapi.Environment{main} - - apiHandler.SetEnvironments(envs) - - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - run := runnerWithAuth(t, apiServer.URL, authServer.URL) - - assert.Equal(t, strings.TrimLeft(` -name: app -type: 'golang:1.23' -size: M -disk: 2048 -mounts: { } -`, "\n"), run("app:config", "-p", projectID, "-e", ".", "--refresh")) - - assert.Equal(t, "golang:1.23\n", run("app:config", "-p", projectID, "-e", ".", "--refresh", "-P", "type")) -} - -func TestAppConfigLocal(t *testing.T) { - run := runWithLocalApp(t, &mockapi.App{ - Name: "local-app", - Type: "golang:1.24", - Size: "L", - Disk: 1024, - Mounts: map[string]mockapi.Mount{ - "example": { - Source: "local", - SourcePath: "example", - }, - }, - }) - - assert.Equal(t, strings.TrimLeft(` -name: local-app -type: 'golang:1.24' -size: L -disk: 1024 -mounts: - example: - source: local - source_path: example -`, "\n"), run("app:config")) - - assert.Equal(t, "local\n", run("app:config", "--property", "mounts.example.source")) -} - -func runWithLocalApp(t *testing.T, app *mockapi.App) func(args ...string) string { - return func(args ...string) string { - j, err := json.Marshal(app) - require.NoError(t, err) - cmd := command(t, args...) - cmd.Env = append(cmd.Env, "PLATFORM_APPLICATION="+base64.StdEncoding.EncodeToString(j)) - b, err := cmd.Output() - require.NoError(t, err) - return string(b) - } -} diff --git a/tests/app_list_test.go b/tests/app_list_test.go deleted file mode 100644 index 83cb1ec..0000000 --- a/tests/app_list_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package tests - -import ( - "net/http/httptest" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/platformsh/cli/internal/mockapi" -) - -func TestAppList(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - apiHandler := mockapi.NewHandler(t) - - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - projectID := "nu8ohgeizah1a" - - apiHandler.SetProjects([]*mockapi.Project{{ - ID: projectID, - Links: mockapi.MakeHALLinks("self=/projects/"+projectID, - "environments=/projects/"+projectID+"/environments"), - DefaultBranch: "main", - }}) - - main := makeEnv(projectID, "main", "production", "active", nil) - main.SetCurrentDeployment(&mockapi.Deployment{ - WebApps: map[string]mockapi.App{ - "app": {Name: "app", Type: "golang:1.23", Size: "AUTO"}, - }, - Services: map[string]mockapi.App{}, - Routes: map[string]any{}, - Workers: map[string]mockapi.Worker{ - "app--worker1": { - App: mockapi.App{Name: "app--worker1", Type: "golang:1.23", Size: "AUTO"}, - Worker: mockapi.WorkerInfo{Commands: mockapi.Commands{Start: "sleep 60"}}, - }, - }, - Links: mockapi.MakeHALLinks("self=/projects/" + projectID + "/environments/main/deployment/current"), - }) - - envs := []*mockapi.Environment{ - main, - makeEnv(projectID, "staging", "staging", "active", "main"), - makeEnv(projectID, "dev", "development", "active", "staging"), - makeEnv(projectID, "fix", "development", "inactive", "dev"), - } - - apiHandler.SetEnvironments(envs) - - run := runnerWithAuth(t, apiServer.URL, authServer.URL) - - assert.Equal(t, strings.TrimLeft(` -Name Type -app golang:1.23 -`, "\n"), run("apps", "-p", projectID, "-e", ".", "--refresh", "--format", "tsv")) - - assert.Equal(t, strings.TrimLeft(` -+--------------+-------------+-------------------+ -| Name | Type | Commands | -+--------------+-------------+-------------------+ -| app--worker1 | golang:1.23 | start: 'sleep 60' | -+--------------+-------------+-------------------+ -`, "\n"), run("workers", "-v", "-p", projectID, "-e", ".")) - - runCombinedOutput := runnerCombinedOutput(t, apiServer.URL, authServer.URL) - co, err := runCombinedOutput("services", "-p", projectID, "-e", "main") - require.NoError(t, err) - assert.Contains(t, co, "No services found") -} diff --git a/tests/auth_info_test.go b/tests/auth_info_test.go deleted file mode 100644 index c0fe036..0000000 --- a/tests/auth_info_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package tests - -import ( - "net/http/httptest" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/platformsh/cli/internal/mockapi" -) - -func TestAuthInfo(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - apiHandler := mockapi.NewHandler(t) - apiHandler.SetMyUser(&mockapi.User{ - ID: "my-user-id", - Deactivated: false, - Namespace: "ns", - Username: "my-username", - FirstName: "Foo", - LastName: "Bar", - Email: "my-user@example.com", - EmailVerified: true, - Picture: "https://example.com/profile.png", - Country: "NO", - PhoneNumberVerified: true, - MFAEnabled: true, - }) - - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - run := runnerWithAuth(t, apiServer.URL, authServer.URL) - - assert.Equal(t, strings.TrimLeft(` -+-----------------------+---------------------+ -| Property | Value | -+-----------------------+---------------------+ -| id | my-user-id | -| first_name | Foo | -| last_name | Bar | -| username | my-username | -| email | my-user@example.com | -| phone_number_verified | true | -+-----------------------+---------------------+ -`, "\n"), run("auth:info", "-v", "--refresh")) - - assert.Equal(t, "my-user-id\n", run("auth:info", "-P", "id")) -} diff --git a/tests/backup_test.go b/tests/backup_test.go deleted file mode 100644 index 885c903..0000000 --- a/tests/backup_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package tests - -import ( - "net/http/httptest" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/platformsh/cli/internal/mockapi" -) - -func TestBackupList(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - apiHandler := mockapi.NewHandler(t) - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - projectID := "rai7quieroohu" - - apiHandler.SetProjects([]*mockapi.Project{ - { - ID: projectID, - DefaultBranch: "main", - Links: mockapi.MakeHALLinks( - "self=/projects/"+projectID, - "environments=/projects/"+projectID+"/environments", - ), - }, - }) - main := makeEnv(projectID, "main", "production", "active", nil) - main.Links["backups"] = mockapi.HALLink{HREF: "/projects/" + projectID + "//environments/main/backups"} - apiHandler.SetEnvironments([]*mockapi.Environment{main}) - - created1, err := time.Parse(time.RFC3339, "2014-04-01T10:00:00+01:00") - require.NoError(t, err) - created2, err := time.Parse(time.RFC3339, "2015-04-01T10:00:00+01:00") - require.NoError(t, err) - - apiHandler.SetProjectBackups(projectID, []*mockapi.Backup{ - { - ID: "123", - EnvironmentID: "main", - Status: "CREATED", - Safe: true, - Restorable: true, - Automated: false, - CommitID: "foo", - CreatedAt: created1, - }, - { - ID: "456", - EnvironmentID: "main", - Status: "CREATED", - Safe: false, - Restorable: true, - Automated: true, - CommitID: "bar", - CreatedAt: created2, - }, - }) - - run := runnerWithAuth(t, apiServer.URL, authServer.URL) - - assert.Equal(t, strings.TrimLeft(` -+---------------------------+-----------+------------+ -| Created | Backup ID | Restorable | -+---------------------------+-----------+------------+ -| 2015-04-01T09:00:00+00:00 | 456 | true | -| 2014-04-01T09:00:00+00:00 | 123 | true | -+---------------------------+-----------+------------+ -`, "\n"), run("backups", "-p", projectID, "-e", ".")) - - assert.Equal(t, strings.TrimLeft(` -+---------------------------+-----------+------------+-----------+-----------+ -| Created | Backup ID | Restorable | Automated | Commit ID | -+---------------------------+-----------+------------+-----------+-----------+ -| 2015-04-01T09:00:00+00:00 | 456 | true | true | bar | -| 2014-04-01T09:00:00+00:00 | 123 | true | false | foo | -+---------------------------+-----------+------------+-----------+-----------+ -`, "\n"), run("backups", "-p", projectID, "-e", ".", "--columns", "+automated,commit_id")) -} - -func TestBackupCreate(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - apiHandler := mockapi.NewHandler(t) - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - projectID := "vei8wah5Ohl2e" - - apiHandler.SetProjects([]*mockapi.Project{ - { - ID: projectID, - DefaultBranch: "main", - Links: mockapi.MakeHALLinks( - "self=/projects/"+projectID, - "environments=/projects/"+projectID+"/environments", - ), - }, - }) - main := makeEnv(projectID, "main", "production", "active", nil) - main.Links["backups"] = mockapi.HALLink{HREF: "/projects/" + projectID + "/environments/main//backups"} - main.Links["#backup"] = mockapi.HALLink{HREF: "/projects/" + projectID + "/environments/main/backups"} - apiHandler.SetEnvironments([]*mockapi.Environment{main}) - - run := runnerWithAuth(t, apiServer.URL, authServer.URL) - - run("backup", "-p", projectID, "-e", ".") - - assert.NotEmpty(t, run("backups", "-p", projectID, "-e", ".")) -} diff --git a/tests/config.yaml b/tests/config.yaml deleted file mode 100644 index e5382cb..0000000 --- a/tests/config.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# Test CLI configuration -application: - name: 'Platform Test CLI' - slug: 'platform-test-cli' - version: '1.0.0' - executable: 'platform-test' - env_prefix: 'TEST_CLI_' - user_config_dir: '.platform-test-cli' - -service: - name: 'Platform.sh Testing' - env_prefix: 'PLATFORM_' - project_config_dir: '.platform' - -api: - # Placeholder URLs which can be replaced during tests. - base_url: 'http://127.0.0.1' - auth_url: 'http://127.0.0.1' - - organizations: true - centralized_permissions: true - teams: true - user_verification: true - metrics: true - - vendor_filter: 'test-vendor' - -ssh: - domain_wildcards: ['*.cli-tests.example.com'] - -detection: - git_remote_name: 'platform-test' - git_domain: 'git.cli-tests.example.com' - site_domains: ['cli-tests.example.com'] - cluster_header: 'X-Platform-Cluster' diff --git a/tests/environment_list_test.go b/tests/environment_list_test.go deleted file mode 100644 index b64a8a7..0000000 --- a/tests/environment_list_test.go +++ /dev/null @@ -1,84 +0,0 @@ -package tests - -import ( - "net/http/httptest" - "net/url" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/platformsh/cli/internal/mockapi" -) - -func TestEnvironmentList(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - apiHandler := mockapi.NewHandler(t) - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - apiHandler.SetProjects([]*mockapi.Project{ - { - ID: mockProjectID, - Links: mockapi.MakeHALLinks( - "self=/projects/"+mockProjectID, - "environments=/projects/"+mockProjectID+"/environments", - ), - }, - }) - apiHandler.SetEnvironments([]*mockapi.Environment{ - makeEnv(mockProjectID, "main", "production", "active", nil), - makeEnv(mockProjectID, "staging", "staging", "active", "main"), - makeEnv(mockProjectID, "dev", "development", "active", "staging"), - makeEnv(mockProjectID, "fix", "development", "inactive", "dev"), - }) - - run := runnerWithAuth(t, apiServer.URL, authServer.URL) - - assert.Equal(t, strings.TrimLeft(` -+-----------+---------+----------+-------------+ -| ID | Title | Status | Type | -+-----------+---------+----------+-------------+ -| main | Main | Active | production | -| staging | Staging | Active | staging | -| dev | Dev | Active | development | -| fix | Fix | Inactive | development | -+-----------+---------+----------+-------------+ -`, "\n"), run("environment:list", "-v", "-p", mockProjectID)) - - assert.Equal(t, strings.TrimLeft(` -ID Title Status Type -main Main Active production -staging Staging Active staging -dev Dev Active development -fix Fix Inactive development -`, "\n"), run("environment:list", "-v", "-p", mockProjectID, "--format", "plain")) - - assert.Equal(t, strings.TrimLeft(` -ID Title Status Type -main Main Active production -staging Staging Active staging -dev Dev Active development -`, "\n"), run("environment:list", "-v", "-p", mockProjectID, "--format", "plain", "--no-inactive")) - - assert.Equal(t, "fix\n", - run("environment:list", "-v", "-p", mockProjectID, "--pipe", "--status=inactive")) -} - -func makeEnv(projectID, name, envType, status string, parent any) *mockapi.Environment { - return &mockapi.Environment{ - ID: name, - Name: name, - MachineName: name + "-xyz", - Title: strings.ToTitle(name[:1]) + name[1:], - Parent: parent, - Type: envType, - Status: status, - Project: projectID, - Links: mockapi.MakeHALLinks( - "self=/projects/" + url.PathEscape(projectID) + "/environments/" + url.PathEscape(name), - ), - } -} diff --git a/tests/mount_list_test.go b/tests/mount_list_test.go deleted file mode 100644 index 4e685c0..0000000 --- a/tests/mount_list_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package tests - -import ( - "net/http/httptest" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/platformsh/cli/internal/mockapi" -) - -func TestMountList(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - apiHandler := mockapi.NewHandler(t) - - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - projectID := "oa3chu0foot4s" - - apiHandler.SetProjects([]*mockapi.Project{{ - ID: projectID, - Links: mockapi.MakeHALLinks("self=/projects/"+projectID, - "environments=/projects/"+projectID+"/environments"), - DefaultBranch: "main", - }}) - - main := makeEnv(projectID, "main", "production", "active", nil) - main.Links["pf:ssh:app"] = mockapi.HALLink{HREF: "ssh://" + projectID + "--app@ssh.example.com"} - main.SetCurrentDeployment(&mockapi.Deployment{ - WebApps: map[string]mockapi.App{ - "app": { - Name: "app", - Type: "golang:1.23", - Size: "AUTO", - Mounts: map[string]mockapi.Mount{ - "/public/sites/default/files": {Source: "local", SourcePath: "files"}, - }}, - }, - Services: map[string]mockapi.App{}, - Routes: map[string]any{}, - Workers: map[string]mockapi.Worker{}, - Links: mockapi.MakeHALLinks("self=/projects/" + projectID + "/environments/main/deployment/current"), - }) - - apiHandler.SetEnvironments([]*mockapi.Environment{main}) - - run := runnerWithAuth(t, apiServer.URL, authServer.URL) - - assert.Equal(t, strings.TrimLeft(` -Mount path Definition -public/sites/default/files "source: local -source_path: files" -`, "\n"), run("mounts", "-p", projectID, "-e", "main", "--refresh", "--format", "tsv")) - - assert.Equal(t, "public/sites/default/files\n", run("mounts", "-p", projectID, "-e", "main", "--paths")) -} - -func TestMountListLocal(t *testing.T) { - run := runWithLocalApp(t, &mockapi.App{ - Name: "local-app", - Type: "golang:1.24", - Size: "L", - Mounts: map[string]mockapi.Mount{ - "/tmp": {Source: "local", SourcePath: "tmp"}, - }, - }) - - assert.Equal(t, strings.TrimLeft(` -+------------+------------------+ -| Mount path | Definition | -+------------+------------------+ -| tmp | source: local | -| | source_path: tmp | -+------------+------------------+ -`, "\n"), run("mounts")) -} diff --git a/tests/org_create_test.go b/tests/org_create_test.go deleted file mode 100644 index 1c68b0b..0000000 --- a/tests/org_create_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package tests - -import ( - "net/http/httptest" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/platformsh/cli/internal/mockapi" -) - -func TestOrgCreate(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - myUserID := "user-for-org-create-test" - - apiHandler := mockapi.NewHandler(t) - apiHandler.SetMyUser(&mockapi.User{ID: myUserID}) - apiHandler.SetOrgs([]*mockapi.Org{ - makeOrg("org-id-1", "acme", "ACME Inc.", myUserID), - }) - - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - run := runnerWithAuth(t, apiServer.URL, authServer.URL) - - // TODO disable the cache? - run("cc") - - assert.Equal(t, strings.TrimLeft(` -+------+-----------+--------------------------------------+ -| Name | Label | Owner email | -+------+-----------+--------------------------------------+ -| acme | ACME Inc. | user-for-org-create-test@example.com | -+------+-----------+--------------------------------------+ -`, "\n"), run("orgs")) - - runCombinedOutput := runnerCombinedOutput(t, apiServer.URL, authServer.URL) - - co, err := runCombinedOutput("org:create", "--name", "hooli", "--yes") - assert.Error(t, err) - assert.Contains(t, co, "--country is required") - - co, err = runCombinedOutput("org:create", "--name", "hooli", "--yes", "--country", "XY") - assert.Error(t, err) - assert.Contains(t, co, "Invalid country: XY") - - co, err = runCombinedOutput("org:create", "--name", "hooli", "--yes", "--country", "US") - assert.NoError(t, err) - assert.Contains(t, co, "Hooli") - - assert.Equal(t, strings.TrimLeft(` -+-------+-----------+--------------------------------------+ -| Name | Label | Owner email | -+-------+-----------+--------------------------------------+ -| acme | ACME Inc. | user-for-org-create-test@example.com | -| hooli | Hooli | user-for-org-create-test@example.com | -+-------+-----------+--------------------------------------+ -`, "\n"), run("orgs")) -} diff --git a/tests/org_info_test.go b/tests/org_info_test.go deleted file mode 100644 index 2391037..0000000 --- a/tests/org_info_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package tests - -import ( - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/platformsh/cli/internal/mockapi" -) - -func TestOrgInfo(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - myUserID := "user-for-org-info-test" - - apiHandler := mockapi.NewHandler(t) - apiHandler.SetMyUser(&mockapi.User{ID: myUserID}) - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - apiHandler.SetOrgs([]*mockapi.Org{ - makeOrg("org-id-1", "org-1", "Org 1", myUserID), - }) - - run := runnerWithAuth(t, apiServer.URL, authServer.URL) - - assert.Contains(t, run("org:info", "-o", "org-1", "--format", "csv", "--refresh"), `Property,Value -id,org-id-1 -name,org-1 -label,Org 1 -owner_id,user-for-org-info-test -capabilities,`) - - assert.Equal(t, "Org 1\n", run("org:info", "-o", "org-1", "label")) - - runCombinedOutput := runnerCombinedOutput(t, apiServer.URL, authServer.URL) - co, err := runCombinedOutput("org:info", "-o", "org-1", "label", "New Label") - assert.NoError(t, err) - assert.Contains(t, co, "Property label set to: New Label\n") - - assert.Equal(t, "New Label\n", run("org:info", "-o", "org-1", "label")) -} diff --git a/tests/org_list_test.go b/tests/org_list_test.go deleted file mode 100644 index 74fac41..0000000 --- a/tests/org_list_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package tests - -import ( - "net/http/httptest" - "net/url" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/platformsh/cli/internal/mockapi" -) - -func TestOrgList(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - myUserID := "user-id-1" - - apiHandler := mockapi.NewHandler(t) - apiHandler.SetMyUser(&mockapi.User{ID: myUserID}) - apiHandler.SetOrgs([]*mockapi.Org{ - makeOrg("org-id-1", "acme", "ACME Inc.", myUserID), - makeOrg("org-id-2", "four-seasons", "Four Seasons Total Landscaping", myUserID), - makeOrg("org-id-3", "duff", "Duff Beer", "user-id-2"), - }) - - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - run := runnerWithAuth(t, apiServer.URL, authServer.URL) - - assert.Equal(t, strings.TrimLeft(` -+--------------+--------------------------------+-----------------------+ -| Name | Label | Owner email | -+--------------+--------------------------------+-----------------------+ -| acme | ACME Inc. | user-id-1@example.com | -| duff | Duff Beer | user-id-2@example.com | -| four-seasons | Four Seasons Total Landscaping | user-id-1@example.com | -+--------------+--------------------------------+-----------------------+ -`, "\n"), run("orgs")) - - assert.Equal(t, strings.TrimLeft(` -Name Label Owner email -acme ACME Inc. user-id-1@example.com -duff Duff Beer user-id-2@example.com -four-seasons Four Seasons Total Landscaping user-id-1@example.com -`, "\n"), run("orgs", "--format", "plain")) - - assert.Equal(t, strings.TrimLeft(` -org-id-1,acme -org-id-3,duff -org-id-2,four-seasons -`, "\n"), run("orgs", "--format", "csv", "--columns", "id,name", "--no-header")) -} - -func makeOrg(id, name, label, owner string) *mockapi.Org { - return &mockapi.Org{ - ID: id, - Name: name, - Label: label, - Owner: owner, - Capabilities: []string{}, - Links: mockapi.MakeHALLinks("self=/organizations/" + url.PathEscape(id)), - } -} diff --git a/tests/project_create_test.go b/tests/project_create_test.go deleted file mode 100644 index dc4d35d..0000000 --- a/tests/project_create_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package tests - -import ( - "bytes" - "io" - "net/http/httptest" - "os" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/platformsh/cli/internal/mockapi" -) - -func TestProjectCreate(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - apiHandler := mockapi.NewHandler(t) - apiHandler.SetOrgs([]*mockapi.Org{ - makeOrg("cli-test-id", "cli-tests", "CLI Test Org", "my-user-id"), - }) - - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - title := "Test Project Title" - region := "test-region" - - cmd := authenticatedCommand(t, apiServer.URL, authServer.URL, - "project:create", "-v", "--region", region, "--title", title, "--org", "cli-tests") - - var stdErrBuf bytes.Buffer - var stdOutBuf bytes.Buffer - cmd.Stderr = &stdErrBuf - if testing.Verbose() { - cmd.Stderr = io.MultiWriter(&stdErrBuf, os.Stderr) - } - cmd.Stdout = &stdOutBuf - t.Log("Running:", cmd) - require.NoError(t, cmd.Run()) - - // stdout should contain the project ID. - projectID := strings.TrimSpace(stdOutBuf.String()) - assert.NotEmpty(t, projectID) - - // stderr should contain various messages. - stderr := stdErrBuf.String() - - assert.Contains(t, stderr, "The estimated monthly cost of this project is: $1,000 USD") - assert.Contains(t, stderr, "Region: "+region) - assert.Contains(t, stderr, "Project ID: "+projectID) - assert.Contains(t, stderr, "Project title: "+title) -} diff --git a/tests/project_info_test.go b/tests/project_info_test.go deleted file mode 100644 index f818936..0000000 --- a/tests/project_info_test.go +++ /dev/null @@ -1,84 +0,0 @@ -package tests - -import ( - "net/http/httptest" - "net/url" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/platformsh/cli/internal/mockapi" -) - -func TestProjectInfo(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - myUserID := "my-user-id" - vendor := "test-vendor" - - apiHandler := mockapi.NewHandler(t) - apiHandler.SetMyUser(&mockapi.User{ID: myUserID}) - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - apiHandler.SetOrgs([]*mockapi.Org{ - makeOrg("org-id-1", "org-1", "Org 1", myUserID), - }) - - projectID := "eer4jee4ri3mo" - created, err := time.Parse(time.RFC3339, "2014-04-01T10:00:00+01:00") - require.NoError(t, err) - - apiHandler.SetProjects([]*mockapi.Project{ - { - ID: projectID, - Title: "Project 1", - Region: "region-1", - Organization: "org-id-1", - Vendor: vendor, - Repository: mockapi.ProjectRepository{ - URL: "git@git.region-1.example.com:mock-project.git", - }, - DefaultBranch: "main", - CreatedAt: created, - UpdatedAt: created.Add(time.Second * 86400), - Links: mockapi.MakeHALLinks( - "self=/projects/"+url.PathEscape(projectID), - "#edit=/projects/"+url.PathEscape(projectID), - ), - }, - }) - - run := runnerWithAuth(t, apiServer.URL, authServer.URL) - - expectedLines := `Property Value -id eer4jee4ri3mo -title Project 1 -region region-1 -organization org-id-1 -vendor test-vendor -repository url: 'git@git.region-1.example.com:mock-project.git' -default_branch main -created_at 2014-04-01T09:00:00+00:00 -updated_at 2014-04-02T09:00:00+00:00 -git git@git.region-1.example.com:mock-project.git` - - output := run("pro:info", "-p", projectID, "--format", "plain", "--refresh") - - for _, line := range strings.Split(expectedLines, "\n") { - assert.True(t, strings.Contains(output, line+"\n")) - } - - assert.Equal(t, "2014-04-01\n", run("pro:info", "-p", projectID, "created_at", "--date-fmt", "Y-m-d")) - - assert.Equal(t, "Project 1\n", run("pro:info", "-p", projectID, "title")) - - run("pro:info", "-v", "-p", projectID, "title", "New Title") - - // TODO --refresh should not be needed here - assert.Equal(t, "New Title\n", run("pro:info", "-p", projectID, "title", "--refresh")) -} diff --git a/tests/project_list_test.go b/tests/project_list_test.go deleted file mode 100644 index d39a847..0000000 --- a/tests/project_list_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package tests - -import ( - "net/http/httptest" - "net/url" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/platformsh/cli/internal/mockapi" -) - -func TestProjectList(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - myUserID := "my-user-id" - otherUserID := "other-user-id" - vendor := "test-vendor" - - apiHandler := mockapi.NewHandler(t) - apiHandler.SetMyUser(&mockapi.User{ID: myUserID}) - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - apiHandler.SetOrgs([]*mockapi.Org{ - makeOrg("org-id-1", "org-1", "Org 1", myUserID), - makeOrg("org-id-2", "org-2", "Org 2", otherUserID), - }) - apiHandler.SetProjects([]*mockapi.Project{ - makeProject("project-id-1", "org-id-1", vendor, "Project 1", "region-1"), - makeProject("project-id-2", "org-id-2", vendor, "Project 2", "region-2"), - makeProject("project-id-3", "org-id-2", vendor, "Project 3", "region-2"), - makeProject("project-other-vendor-3", "org-other-vendor", "acme", "Other Vendor's Project", "region-1"), - }) - apiHandler.SetUserGrants([]*mockapi.UserGrant{ - { - ResourceID: "org-id-1", - ResourceType: "organization", - OrganizationID: "org-id-1", - UserID: myUserID, - Permissions: []string{"admin"}, - }, - { - ResourceID: "project-id-1", - ResourceType: "project", - OrganizationID: "org-id-1", - UserID: myUserID, - Permissions: []string{"admin"}, - }, - { - ResourceID: "project-id-2", - ResourceType: "project", - OrganizationID: "org-id-2", - UserID: "user-id-2", - Permissions: []string{"admin"}, - }, - { - ResourceID: "project-id-2", - ResourceType: "project", - OrganizationID: "org-id-2", - UserID: myUserID, - Permissions: []string{"viewer", "development:admin"}, - }, - { - ResourceID: "project-id-3", - ResourceType: "project", - OrganizationID: "org-id-2", - UserID: myUserID, - Permissions: []string{"viewer", "development:contributor"}, - }, - }) - - run := runnerWithAuth(t, apiServer.URL, authServer.URL) - - assert.Equal(t, strings.TrimLeft(` -+--------------+-----------+----------+--------------+ -| ID | Title | Region | Organization | -+--------------+-----------+----------+--------------+ -| project-id-1 | Project 1 | region-1 | org-1 | -| project-id-2 | Project 2 | region-2 | org-2 | -| project-id-3 | Project 3 | region-2 | org-2 | -+--------------+-----------+----------+--------------+ -`, "\n"), run("pro", "-v")) - - assert.Equal(t, strings.TrimLeft(` -ID Title Region Organization -project-id-1 Project 1 region-1 org-1 -project-id-2 Project 2 region-2 org-2 -project-id-3 Project 3 region-2 org-2 -`, "\n"), run("pro", "-v", "--format", "plain")) - - assert.Equal(t, strings.TrimLeft(` -ID,Organization ID -project-id-1,org-id-1 -project-id-2,org-id-2 -project-id-3,org-id-2 -`, "\n"), run("pro", "-v", "--format", "csv", "--columns", "id,organization_id")) - - assert.Equal(t, strings.TrimLeft(` -ID Title Region Organization -project-id-1 Project 1 region-1 org-1 -`, "\n"), run("pro", "-v", "--format", "plain", "--my")) - - assert.Equal(t, strings.TrimLeft(` -project-id-1 -project-id-2 -project-id-3 -`, "\n"), run("pro", "-v", "--pipe")) -} - -func makeProject(id, org, vendor, title, region string) *mockapi.Project { - return &mockapi.Project{ - ID: id, - Organization: org, - Vendor: vendor, - Title: title, - Region: region, - Links: mockapi.MakeHALLinks("self=/projects/" + url.PathEscape(id)), - } -} diff --git a/tests/ssh_cert_test.go b/tests/ssh_cert_test.go deleted file mode 100644 index 3e2cb41..0000000 --- a/tests/ssh_cert_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package tests - -import ( - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/platformsh/cli/internal/mockapi" -) - -func TestSSHCerts(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - myUserID := "my-user-id" - - apiHandler := mockapi.NewHandler(t) - apiHandler.SetMyUser(&mockapi.User{ID: myUserID}) - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - run := runnerWithAuth(t, apiServer.URL, authServer.URL) - - output := run("ssh-cert:info") - assert.Regexp(t, `(?m)^filename: .+?id_ed25519-cert\.pub$`, output) - assert.Contains(t, output, "key_id: test-key-id\n") - assert.Contains(t, output, "key_type: ssh-ed25519-cert-v01@openssh.com\n") -} diff --git a/tests/tests.go b/tests/tests.go deleted file mode 100644 index 3b7d10e..0000000 --- a/tests/tests.go +++ /dev/null @@ -1,118 +0,0 @@ -// Package tests contains integration tests, which run the CLI as a shell command and verify its output. -// -// A TEST_CLI_PATH environment variable can be provided to override the path to a -// CLI executable. It defaults to `platform` in the repository root. -package tests - -import ( - "bytes" - "os" - "os/exec" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/platformsh/cli/internal/mockapi" -) - -var _validatedCommand string - -// The legacy CLI identifier expects project IDs to be alphanumeric. -// See: https://github.com/platformsh/legacy-cli/blob/main/src/Service/Identifier.php#L75 -const mockProjectID = "abcdefg123456" - -func getCommandName(t *testing.T) string { - if testing.Short() { - t.Skip("skipping integration test due to -short flag") - } - if _validatedCommand != "" { - return _validatedCommand - } - candidate := os.Getenv("TEST_CLI_PATH") - if candidate != "" { - _, err := os.Stat(candidate) - require.NoError(t, err) - } else { - matches, _ := filepath.Glob("../dist/platform_*/platform") - if len(matches) == 0 { - t.Skipf("skipping integration tests: CLI not found matching path: %s", "../dist/platform_*/platform") - return "" - } - c, err := filepath.Abs(matches[0]) - require.NoError(t, err) - candidate = c - } - versionCmd := exec.Command(candidate, "--version") - versionCmd.Env = testEnv() - output, err := versionCmd.Output() - require.NoError(t, err, "running '--version' must succeed under the CLI at: %s", candidate) - require.Contains(t, string(output), "Platform Test CLI ") - t.Logf("Validated CLI command %s", candidate) - _validatedCommand = candidate - return _validatedCommand -} - -func command(t *testing.T, args ...string) *exec.Cmd { - cmd := exec.Command(getCommandName(t), args...) //nolint:gosec - cmd.Env = testEnv() - cmd.Dir = os.TempDir() - if testing.Verbose() { - cmd.Stderr = os.Stderr - } - return cmd -} - -func authenticatedCommand(t *testing.T, apiURL, authURL string, args ...string) *exec.Cmd { - cmd := command(t, args...) - cmd.Env = append( - cmd.Env, - EnvPrefix+"API_BASE_URL="+apiURL, - EnvPrefix+"API_AUTH_URL="+authURL, - EnvPrefix+"TOKEN="+mockapi.ValidAPITokens[0], - ) - return cmd -} - -// runnerWithAuth returns a function to authenticate and run a CLI command, returning stdout output. -// This asserts that the command has not failed. -func runnerWithAuth(t *testing.T, apiURL, authURL string) func(args ...string) string { - return func(args ...string) string { - cmd := authenticatedCommand(t, apiURL, authURL, args...) - t.Log("Running:", cmd) - b, err := cmd.Output() - require.NoError(t, err) - return string(b) - } -} - -// runnerCombinedOutput returns a function to authenticate and run a CLI command, returning combined output. -func runnerCombinedOutput(t *testing.T, apiURL, authURL string) func(args ...string) (string, error) { - return func(args ...string) (string, error) { - cmd := authenticatedCommand(t, apiURL, authURL, args...) - var b bytes.Buffer - cmd.Stdout = &b - cmd.Stderr = &b - t.Log("Running:", cmd) - err := cmd.Run() - return b.String(), err - } -} - -const EnvPrefix = "TEST_CLI_" - -func testEnv() []string { - configPath, err := filepath.Abs("config.yaml") - if err != nil { - panic(err) - } - return append( - os.Environ(), - "COLUMNS=120", - "CLI_CONFIG_FILE="+configPath, - EnvPrefix+"NO_INTERACTION=1", - EnvPrefix+"VERSION=1.0.0", - EnvPrefix+"HOME="+os.TempDir(), - "TZ=UTC", - ) -} diff --git a/tests/user_list_test.go b/tests/user_list_test.go deleted file mode 100644 index 227aee5..0000000 --- a/tests/user_list_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package tests - -import ( - "net/http/httptest" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/platformsh/cli/internal/mockapi" -) - -func TestUserList(t *testing.T) { - authServer := mockapi.NewAuthServer(t) - defer authServer.Close() - - myUserID := "my-user-id" - vendor := "test-vendor" - - apiHandler := mockapi.NewHandler(t) - apiHandler.SetMyUser(&mockapi.User{ID: myUserID}) - apiServer := httptest.NewServer(apiHandler) - defer apiServer.Close() - - apiHandler.SetOrgs([]*mockapi.Org{ - makeOrg("org-id-1", "org-1", "Org 1", myUserID), - }) - apiHandler.SetProjects([]*mockapi.Project{ - makeProject(mockProjectID, "org-id-1", vendor, "Project 1", "region-1"), - }) - apiHandler.SetUserGrants([]*mockapi.UserGrant{ - { - ResourceID: "org-id-1", - ResourceType: "organization", - OrganizationID: "org-id-1", - UserID: myUserID, - Permissions: []string{"admin"}, - }, - { - ResourceID: mockProjectID, - ResourceType: "project", - OrganizationID: "org-id-1", - UserID: myUserID, - Permissions: []string{"admin"}, - }, - { - ResourceID: mockProjectID, - ResourceType: "project", - OrganizationID: "org-id-1", - UserID: "user-id-2", - Permissions: []string{"viewer", "development:viewer"}, - }, - { - ResourceID: mockProjectID, - ResourceType: "project", - OrganizationID: "org-id-1", - UserID: "user-id-3", - Permissions: []string{"viewer", "production:viewer", "development:admin", "staging:contributor"}, - }, - }) - - run := runnerWithAuth(t, apiServer.URL, authServer.URL) - - assert.Equal(t, strings.TrimLeft(` -+------------------------+-----------------+--------------+------------+ -| Email address | Name | Project role | ID | -+------------------------+-----------------+--------------+------------+ -| my-user-id@example.com | User my-user-id | admin | my-user-id | -| user-id-2@example.com | User user-id-2 | viewer | user-id-2 | -| user-id-3@example.com | User user-id-3 | viewer | user-id-3 | -+------------------------+-----------------+--------------+------------+ -`, "\n"), run("users", "-p", mockProjectID)) - - assert.Equal(t, strings.TrimLeft(` -Email address Name Project role ID Permissions -my-user-id@example.com User my-user-id admin my-user-id admin -user-id-2@example.com User user-id-2 viewer user-id-2 viewer, development:viewer -user-id-3@example.com User user-id-3 viewer user-id-3 viewer, production:viewer, development:admin, staging:contributor -`, "\n"), run("users", "-p", mockProjectID, "--format", "plain", "--columns", "+perm%")) -}