diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index a8045e43..8ed56350 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -22,5 +22,5 @@ jobs: - uses: actions/setup-go@v2 with: go-version: '^1.14' - - name: go test - run: go test -v ./ci/integration/... + - name: integration tests + run: ./ci/steps/integration.sh diff --git a/ci/integration/Dockerfile b/ci/integration/Dockerfile new file mode 100644 index 00000000..f81aa240 --- /dev/null +++ b/ci/integration/Dockerfile @@ -0,0 +1,3 @@ +FROM ubuntu:20.04 + +RUN apt-get update && apt-get install -y jq curl diff --git a/ci/integration/devurls_test.go b/ci/integration/devurls_test.go index 06553107..6dee8211 100644 --- a/ci/integration/devurls_test.go +++ b/ci/integration/devurls_test.go @@ -12,7 +12,6 @@ func TestDevURLCLI(t *testing.T) { run(t, "coder-cli-devurl-tests", func(t *testing.T, ctx context.Context, c *tcli.ContainerRunner) { c.Run(ctx, "which coder").Assert(t, tcli.Success(), - tcli.StdoutMatches("/usr/sbin/coder"), tcli.StderrEmpty(), ) diff --git a/ci/integration/envs_test.go b/ci/integration/envs_test.go index a4f75480..075f7222 100644 --- a/ci/integration/envs_test.go +++ b/ci/integration/envs_test.go @@ -2,27 +2,46 @@ package integration import ( "context" + "fmt" + "math" + "net/url" "regexp" "testing" + "time" "cdr.dev/coder-cli/ci/tcli" + "cdr.dev/coder-cli/coder-sdk" + "cdr.dev/slog" + "cdr.dev/slog/sloggers/slogtest" + "cdr.dev/slog/sloggers/slogtest/assert" + "github.com/google/go-cmp/cmp" ) -// From Coder organization images -// const ubuntuImgID = "5f443b16-30652892427b955601330fa5" +func cleanupClient(t *testing.T, ctx context.Context) *coder.Client { + creds := login(ctx, t) + + u, err := url.Parse(creds.url) + assert.Success(t, "parse base url", err) + + return &coder.Client{BaseURL: u, Token: creds.token} +} + +func cleanupEnv(t *testing.T, client *coder.Client, envID string) func() { + return func() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + slogtest.Info(t, "cleanuping up environment", slog.F("env_id", envID)) + _ = client.DeleteEnvironment(ctx, envID) + } +} func TestEnvsCLI(t *testing.T) { t.Parallel() run(t, "coder-cli-env-tests", func(t *testing.T, ctx context.Context, c *tcli.ContainerRunner) { headlessLogin(ctx, t, c) - - // Ensure binary is present. - c.Run(ctx, "which coder").Assert(t, - tcli.Success(), - tcli.StdoutMatches("/usr/sbin/coder"), - tcli.StderrEmpty(), - ) + client := cleanupClient(t, ctx) // Minimum args not received. c.Run(ctx, "coder envs create").Assert(t, @@ -49,21 +68,74 @@ func TestEnvsCLI(t *testing.T) { tcli.Error(), ) - // TODO(Faris) : uncomment this when we can safely purge the environments - // the integrations tests would create in the sidecar - // Successfully create environment. - // c.Run(ctx, "coder envs create --image "+ubuntuImgID+" test-ubuntu").Assert(t, - // tcli.Success(), - // // why does flog.Success write to stderr? - // tcli.StderrMatches(regexp.QuoteMeta("Successfully created environment \"test-ubuntu\"")), - // ) - - // TODO(Faris) : uncomment this when we can safely purge the environments - // the integrations tests would create in the sidecar - // Successfully provision environment with fractional resource amounts - // c.Run(ctx, fmt.Sprintf(`coder envs create -i %s -c 1.2 -m 1.4 non-whole-resource-amounts`, ubuntuImgID)).Assert(t, - // tcli.Success(), - // tcli.StderrMatches(regexp.QuoteMeta("Successfully created environment \"non-whole-resource-amounts\"")), - // ) + name := randString(10) + cpu := 2.3 + c.Run(ctx, fmt.Sprintf("coder envs create %s --image ubuntu --cpu %f", name, cpu)).Assert(t, + tcli.Success(), + ) + + c.Run(ctx, "coder envs ls").Assert(t, + tcli.Success(), + tcli.StdoutMatches(regexp.QuoteMeta(name)), + ) + + var env coder.Environment + c.Run(ctx, fmt.Sprintf(`coder envs ls -o json | jq '.[] | select(.name == "%s")'`, name)).Assert(t, + tcli.Success(), + tcli.StdoutJSONUnmarshal(&env), + ) + + // attempt to cleanup the environment even if tests fail + t.Cleanup(cleanupEnv(t, client, env.ID)) + + assert.Equal(t, "environment cpu was correctly set", cpu, float64(env.CPUCores), floatComparer) + + c.Run(ctx, fmt.Sprintf("coder envs watch-build %s", name)).Assert(t, + tcli.Success(), + ) + + c.Run(ctx, fmt.Sprintf("coder envs rm %s --force", name)).Assert(t, + tcli.Success(), + ) + }) + + run(t, "coder-cli-env-edit-tests", func(t *testing.T, ctx context.Context, c *tcli.ContainerRunner) { + headlessLogin(ctx, t, c) + client := cleanupClient(t, ctx) + + name := randString(10) + c.Run(ctx, fmt.Sprintf("coder envs create %s --image ubuntu --follow", name)).Assert(t, + tcli.Success(), + ) + + var env coder.Environment + c.Run(ctx, fmt.Sprintf(`coder envs ls -o json | jq '.[] | select(.name == "%s")'`, name)).Assert(t, + tcli.Success(), + tcli.StdoutJSONUnmarshal(&env), + ) + + // attempt to cleanup the environment even if tests fail + t.Cleanup(cleanupEnv(t, client, env.ID)) + + cpu := 2.1 + c.Run(ctx, fmt.Sprintf(`coder envs edit %s --cpu %f --follow`, name, cpu)).Assert(t, + tcli.Success(), + ) + + c.Run(ctx, fmt.Sprintf(`coder envs ls -o json | jq '.[] | select(.name == "%s")'`, name)).Assert(t, + tcli.Success(), + tcli.StdoutJSONUnmarshal(&env), + ) + assert.Equal(t, "cpu cores were updated", cpu, float64(env.CPUCores), floatComparer) + + c.Run(ctx, fmt.Sprintf("coder envs rm %s --force", name)).Assert(t, + tcli.Success(), + ) }) } + +var floatComparer = cmp.Comparer(func(x, y float64) bool { + delta := math.Abs(x - y) + mean := math.Abs(x+y) / 2.0 + return delta/mean < 0.001 +}) diff --git a/ci/integration/integration_test.go b/ci/integration/integration_test.go index eccf68a2..52527839 100644 --- a/ci/integration/integration_test.go +++ b/ci/integration/integration_test.go @@ -18,7 +18,7 @@ func run(t *testing.T, container string, execute func(t *testing.T, ctx context. defer cancel() c, err := tcli.NewContainerRunner(ctx, &tcli.ContainerConfig{ - Image: "codercom/enterprise-dev", + Image: "coder-cli-integration:latest", Name: container, BindMounts: map[string]string{ binpath: "/bin/coder", @@ -36,7 +36,6 @@ func TestCoderCLI(t *testing.T) { run(t, "test-coder-cli", func(t *testing.T, ctx context.Context, c *tcli.ContainerRunner) { c.Run(ctx, "which coder").Assert(t, tcli.Success(), - tcli.StdoutMatches("/usr/sbin/coder"), tcli.StderrEmpty(), ) @@ -87,7 +86,7 @@ func TestCoderCLI(t *testing.T) { var seededRand = rand.New(rand.NewSource(time.Now().UnixNano())) func randString(length int) string { - const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + const charset = "abcdefghijklmnopqrstuvwxyz" b := make([]byte, length) for i := range b { b[i] = charset[seededRand.Intn(len(charset))] diff --git a/ci/integration/setup_test.go b/ci/integration/setup_test.go index 566f40e9..1919f6a0 100644 --- a/ci/integration/setup_test.go +++ b/ci/integration/setup_test.go @@ -51,14 +51,17 @@ func build(path string) error { // write session tokens to the given container runner func headlessLogin(ctx context.Context, t *testing.T, runner *tcli.ContainerRunner) { creds := login(ctx, t) - cmd := exec.CommandContext(ctx, "sh", "-c", "mkdir -p ~/.config/coder && cat > ~/.config/coder/session") + cmd := exec.CommandContext(ctx, "sh", "-c", "mkdir -p $HOME/.config/coder && cat > $HOME/.config/coder/session") // !IMPORTANT: be careful that this does not appear in logs cmd.Stdin = strings.NewReader(creds.token) runner.RunCmd(cmd).Assert(t, tcli.Success(), ) - runner.Run(ctx, fmt.Sprintf("echo -ne %s > ~/.config/coder/url", creds.url)).Assert(t, + + cmd = exec.CommandContext(ctx, "sh", "-c", "cat > $HOME/.config/coder/url") + cmd.Stdin = strings.NewReader(creds.url) + runner.RunCmd(cmd).Assert(t, tcli.Success(), ) } diff --git a/ci/integration/users_test.go b/ci/integration/users_test.go index 2138aeed..55d554f2 100644 --- a/ci/integration/users_test.go +++ b/ci/integration/users_test.go @@ -14,7 +14,6 @@ func TestUsers(t *testing.T) { run(t, "users-cli-tests", func(t *testing.T, ctx context.Context, c *tcli.ContainerRunner) { c.Run(ctx, "which coder").Assert(t, tcli.Success(), - tcli.StdoutMatches("/usr/sbin/coder"), tcli.StderrEmpty(), ) diff --git a/ci/steps/integration.sh b/ci/steps/integration.sh new file mode 100755 index 00000000..1ef04178 --- /dev/null +++ b/ci/steps/integration.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -eo pipefail + +log() { + echo "--- $@" +} + +cd "$(git rev-parse --show-toplevel)" + +log "building integration test image" +docker build -f ./ci/integration/Dockerfile -t coder-cli-integration:latest . + +log "starting integration tests" +go test ./ci/integration -count=1 diff --git a/go.mod b/go.mod index 6c90ab40..cbc0fd04 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( cdr.dev/wsep v0.0.0-20200728013649-82316a09813f github.com/briandowns/spinner v1.11.1 github.com/fatih/color v1.9.0 + github.com/google/go-cmp v0.4.0 github.com/gorilla/websocket v1.4.2 github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f github.com/klauspost/compress v1.10.8 // indirect pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy