Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ platform: internal/legacy/archives/platform.phar php

.PHONY: integration-test
integration-test: platform
TEST_CLI_PATH="$(PWD)/platform" go test -failfast -mod=readonly -v ./tests/integration/...
TEST_CLI_PATH="$(PWD)/platform" go test ./tests/integration/...

golangci-lint:
command -v golangci-lint >/dev/null || go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ toolchain go1.22.4

require (
github.com/fatih/color v1.17.0
github.com/go-chi/chi/v5 v5.1.0
github.com/go-playground/validator/v10 v10.20.0
github.com/gofrs/flock v0.8.1
github.com/mattn/go-isatty v0.0.20
Expand Down
6 changes: 2 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
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/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
Expand Down Expand Up @@ -92,10 +94,6 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/platformsh/platformify v0.2.9 h1:JKakEM6kY3p+IG3/J427Cw8T97pyPnPMomx9FzWuvgE=
github.com/platformsh/platformify v0.2.9/go.mod h1:fgmCcfQfHbhe1oXsIdIhpnniyZu8IdIMOlcBAa/ygic=
github.com/platformsh/platformify v0.2.10 h1:5/b5hXpXWV0rVswstvx1fSmE7c7qaYs3u2pICDCcA3E=
github.com/platformsh/platformify v0.2.10/go.mod h1:fgmCcfQfHbhe1oXsIdIhpnniyZu8IdIMOlcBAa/ygic=
github.com/platformsh/platformify v0.2.11 h1:9TRej4tDgQahRfl1tDOGaCry79yXYXbzDR1ZMdOPsU8=
github.com/platformsh/platformify v0.2.11/go.mod h1:fgmCcfQfHbhe1oXsIdIhpnniyZu8IdIMOlcBAa/ygic=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down
101 changes: 101 additions & 0 deletions tests/integration/app_list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package integration

import (
"bytes"
"io"
"net/http/httptest"
"os"
"os/exec"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/platformsh/cli/tests/integration/mocks"
"github.com/platformsh/cli/tests/integration/mocks/api"
)

func TestAppList(t *testing.T) {
authServer := mocks.APITokenServer(t)
defer authServer.Close()

apiHandler := api.NewHandler(t)

apiServer := httptest.NewServer(apiHandler)
defer apiServer.Close()

apiHandler.SetProjects([]*api.Project{{
ID: mockProjectID,
Links: api.MakeHALLinks("self=/projects/"+mockProjectID,
"environments=/projects/"+mockProjectID+"/environments"),
DefaultBranch: "main",
}})

main := makeEnv(mockProjectID, "main", "production", "active", nil)
main.SetCurrentDeployment(&api.Deployment{
WebApps: map[string]api.App{
"app": {Name: "app", Type: "golang:1.23", Size: "AUTO"},
},
Services: map[string]api.App{},
Routes: map[string]any{},
Workers: map[string]api.Worker{
"app--worker1": {
App: api.App{Name: "app--worker1", Type: "golang:1.23", Size: "AUTO"},
Worker: api.WorkerInfo{Commands: api.Commands{Start: "sleep 60"}},
},
},
Links: api.MakeHALLinks("self=/projects/" + mockProjectID + "/environments/main/deployment/current"),
})

envs := []*api.Environment{
main,
makeEnv(mockProjectID, "staging", "staging", "active", "main"),
makeEnv(mockProjectID, "dev", "development", "active", "staging"),
makeEnv(mockProjectID, "fix", "development", "inactive", "dev"),
}

apiHandler.SetEnvironments(envs)

authenticatedCommand := func(args ...string) *exec.Cmd {
cmd := command(t, args...)
cmd.Env = append(
cmd.Env,
EnvPrefix+"API_BASE_URL="+apiServer.URL,
EnvPrefix+"API_AUTH_URL="+authServer.URL,
EnvPrefix+"TOKEN="+mocks.ValidAPITokens[0],
)
if testing.Verbose() {
cmd.Stderr = os.Stderr
}
return cmd
}

run := func(args ...string) string {
b, err := authenticatedCommand(args...).Output()
require.NoError(t, err)
return string(b)
}

assert.Equal(t, strings.TrimLeft(`
Name Type
app golang:1.23
`, "\n"), run("apps", "-p", mockProjectID, "-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", mockProjectID, "-e", "."))

servicesCmd := authenticatedCommand("services", "-p", mockProjectID, "-e", "main")
stdErrBuf := bytes.Buffer{}
servicesCmd.Stderr = &stdErrBuf
if testing.Verbose() {
servicesCmd.Stderr = io.MultiWriter(&stdErrBuf, os.Stderr)
}
require.NoError(t, servicesCmd.Run())
assert.Contains(t, stdErrBuf.String(), "No services found")
}
70 changes: 70 additions & 0 deletions tests/integration/auth_info_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package integration

import (
"net/http/httptest"
"os"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/platformsh/cli/tests/integration/mocks"
"github.com/platformsh/cli/tests/integration/mocks/api"
)

func TestAuthInfo(t *testing.T) {
authServer := mocks.APITokenServer(t)
defer authServer.Close()

apiHandler := api.NewHandler(t)
apiHandler.SetMyUser(&api.User{
ID: "my-user-id",
Deactivated: false,
Namespace: "ns",
Username: "my-username",
FirstName: "Foo",
LastName: "Bar",
Email: "[email protected]",
EmailVerified: true,
Picture: "https://example.com/profile.png",
Country: "NO",
PhoneNumberVerified: true,
MFAEnabled: true,
})

apiServer := httptest.NewServer(apiHandler)
defer apiServer.Close()

run := func(args ...string) string {
cmd := command(t, args...)
cmd.Env = append(
cmd.Env,
EnvPrefix+"API_BASE_URL="+apiServer.URL,
EnvPrefix+"API_AUTH_URL="+authServer.URL,
EnvPrefix+"TOKEN="+mocks.ValidAPITokens[0],
)
if testing.Verbose() {
cmd.Stderr = os.Stderr
}

b, err := cmd.Output()
require.NoError(t, err)
return string(b)
}

assert.Equal(t, strings.TrimLeft(`
+-----------------------+---------------------+
| Property | Value |
+-----------------------+---------------------+
| id | my-user-id |
| first_name | Foo |
| last_name | Bar |
| username | my-username |
| email | [email protected] |
| phone_number_verified | true |
+-----------------------+---------------------+
`, "\n"), run("auth:info", "-v"))

assert.Equal(t, "my-user-id\n", run("auth:info", "-P", "id"))
}
99 changes: 99 additions & 0 deletions tests/integration/environment_list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package integration

import (
"net/http/httptest"
"net/url"
"os"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/platformsh/cli/tests/integration/mocks"
"github.com/platformsh/cli/tests/integration/mocks/api"
)

func TestEnvironmentList(t *testing.T) {
authServer := mocks.APITokenServer(t)
defer authServer.Close()

apiHandler := api.NewHandler(t)
apiServer := httptest.NewServer(apiHandler)
defer apiServer.Close()

apiHandler.SetProjects([]*api.Project{
{
ID: mockProjectID,
Links: api.MakeHALLinks("self=/projects/"+mockProjectID, "environments=/projects/"+mockProjectID+"/environments"),
},
})
apiHandler.SetEnvironments([]*api.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 := func(args ...string) string {
cmd := command(t, args...)
cmd.Env = append(
cmd.Env,
EnvPrefix+"API_BASE_URL="+apiServer.URL,
EnvPrefix+"API_AUTH_URL="+authServer.URL,
EnvPrefix+"TOKEN="+mocks.ValidAPITokens[0],
)
if testing.Verbose() {
cmd.Stderr = os.Stderr
}

b, err := cmd.Output()
require.NoError(t, err)
return string(b)
}

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) *api.Environment {
return &api.Environment{
ID: name,
Name: name,
MachineName: name + "-xyz",
Title: strings.ToTitle(name[:1]) + name[1:],
Parent: parent,
Type: envType,
Status: status,
Project: projectID,
Links: api.MakeHALLinks(
"self=/projects/" + url.PathEscape(projectID) + "/environments/" + url.PathEscape(name),
),
}
}
22 changes: 15 additions & 7 deletions tests/integration/integration_test.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
// Run integration tests using, for example:
// TEST_CLI_PATH=./platform go run -v ./tests/...

package integration

import (
"log"
"os"
"os/exec"
"path/filepath"
"testing"

"github.com/stretchr/testify/require"
)

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 _validatedCommand != "" {
return _validatedCommand
Expand All @@ -22,11 +24,11 @@ func getCommandName(t *testing.T) string {
if candidate == "" {
t.Skip("enable by setting TEST_CLI_PATH (or use `make integration-test`)")
}
versionCmd := exec.Command(candidate, "version")
versionCmd := exec.Command(candidate, "--version")
versionCmd.Env = testEnv()
output, err := versionCmd.Output()
require.NoError(t, err, "the 'version' command must succeed under the CLI at: %s", candidate)
require.Equal(t, "Platform Test CLI 1.0.0\n", string(output))
require.NoError(t, err, "running '--version' must succeed under the CLI at: %s", candidate)
require.Contains(t, string(output), "Platform Test CLI ")
if testing.Verbose() {
log.Printf("Validated CLI command %s", candidate)
}
Expand All @@ -37,17 +39,23 @@ func getCommandName(t *testing.T) string {
func command(t *testing.T, args ...string) *exec.Cmd {
cmd := exec.Command(getCommandName(t), args...) //nolint:gosec
cmd.Env = testEnv()
cmd.Dir = os.TempDir()
return cmd
}

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=config.yaml",
"CLI_CONFIG_FILE="+configPath,
EnvPrefix+"NO_INTERACTION=1",
EnvPrefix+"VERSION=1.0.0",
EnvPrefix+"HOME="+os.TempDir(),
)
}
Loading
Loading