Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Initial setup for integration tests #80

Merged
merged 19 commits into from
Jul 29, 2020
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
3 changes: 1 addition & 2 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
name: build

on: [push]

jobs:
Expand All @@ -9,7 +8,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v1
- name: Build
run: ./ci/build.sh
run: ./ci/steps/build.sh
- name: Upload
uses: actions/upload-artifact@v2
with:
Expand Down
26 changes: 26 additions & 0 deletions .github/workflows/integration.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: integration
on:
push:
schedule:
- cron: '*/180 * * * *'

jobs:
integration:
runs-on: ubuntu-latest
env:
CODER_URL: ${{ secrets.CODER_URL }}
CODER_EMAIL: ${{ secrets.CODER_EMAIL }}
CODER_PASSWORD: ${{ secrets.CODER_PASSWORD }}
steps:
- uses: actions/checkout@v1
- uses: actions/cache@v1
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- uses: actions/setup-go@v2
with:
go-version: '^1.14'
- name: go test
run: go test -v ./ci/integration/...
46 changes: 46 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: test
on: [push]

jobs:
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/cache@v1
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: fmt
uses: ./ci/image
with:
args: ./ci/steps/fmt.sh
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/cache@v1
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: lint
uses: ./ci/image
with:
args: ./ci/steps/lint.sh
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/cache@v1
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: test
uses: ./ci/image
with:
args: go test ./internal/... ./cmd/...
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.idea
ci/bin
cmd/coder/coder
ci/integration/bin
ci/integration/env.sh
8 changes: 8 additions & 0 deletions ci/image/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM golang:1

ENV GOFLAGS="-mod=readonly"
ENV CI=true

RUN go get golang.org/x/tools/cmd/goimports
RUN go get golang.org/x/lint/golint
RUN go get github.com/mattn/goveralls
120 changes: 120 additions & 0 deletions ci/integration/integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package integration

import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"time"

"cdr.dev/coder-cli/ci/tcli"
"cdr.dev/slog/sloggers/slogtest/assert"
)

func build(path string) error {
cmd := exec.Command(
"sh", "-c",
fmt.Sprintf("cd ../../ && go build -o %s ./cmd/coder", path),
)
cmd.Env = append(os.Environ(), "GOOS=linux", "CGO_ENABLED=0")

_, err := cmd.CombinedOutput()
if err != nil {
return err
}
return nil
}

var binpath string

func init() {
cwd, err := os.Getwd()
if err != nil {
panic(err)
}

binpath = filepath.Join(cwd, "bin", "coder")
err = build(binpath)
if err != nil {
panic(err)
}
}

// 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")

// !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,
tcli.Success(),
)
}

func TestCoderCLI(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5)
defer cancel()

c, err := tcli.NewContainerRunner(ctx, &tcli.ContainerConfig{
Image: "codercom/enterprise-dev",
Name: "coder-cli-tests",
BindMounts: map[string]string{
binpath: "/bin/coder",
},
})
assert.Success(t, "new run container", err)
defer c.Close()

c.Run(ctx, "which coder").Assert(t,
tcli.Success(),
tcli.StdoutMatches("/usr/sbin/coder"),
tcli.StderrEmpty(),
)

c.Run(ctx, "coder version").Assert(t,
tcli.StderrEmpty(),
tcli.Success(),
tcli.StdoutMatches("linux"),
)

c.Run(ctx, "coder help").Assert(t,
tcli.Success(),
tcli.StderrMatches("Commands:"),
tcli.StderrMatches("Usage: coder"),
tcli.StdoutEmpty(),
)

headlessLogin(ctx, t, c)

c.Run(ctx, "coder envs").Assert(t,
tcli.Success(),
)

c.Run(ctx, "coder urls").Assert(t,
tcli.Error(),
)

c.Run(ctx, "coder sync").Assert(t,
tcli.Error(),
)

c.Run(ctx, "coder sh").Assert(t,
tcli.Error(),
)

c.Run(ctx, "coder logout").Assert(t,
tcli.Success(),
)

c.Run(ctx, "coder envs").Assert(t,
tcli.Error(),
)
}
75 changes: 75 additions & 0 deletions ci/integration/login_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package integration

import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"testing"

"cdr.dev/slog/sloggers/slogtest/assert"
)

type credentials struct {
url, token string
}

func login(ctx context.Context, t *testing.T) credentials {
var (
email = requireEnv(t, "CODER_EMAIL")
password = requireEnv(t, "CODER_PASSWORD")
rawURL = requireEnv(t, "CODER_URL")
)
sessionToken := getSessionToken(ctx, t, email, password, rawURL)

return credentials{
url: rawURL,
token: sessionToken,
}
}

func requireEnv(t *testing.T, key string) string {
value := os.Getenv(key)
assert.True(t, fmt.Sprintf("%q is nonempty", key), value != "")
return value
}

type loginBuiltInAuthReq struct {
Email string `json:"email"`
Password string `json:"password"`
}

type loginBuiltInAuthResp struct {
SessionToken string `json:"session_token"`
}

func getSessionToken(ctx context.Context, t *testing.T, email, password, rawURL string) string {
reqbody := loginBuiltInAuthReq{
Email: email,
Password: password,
}
body, err := json.Marshal(reqbody)
assert.Success(t, "marshal login req body", err)

u, err := url.Parse(rawURL)
assert.Success(t, "parse raw url", err)
u.Path = "/auth/basic/login"

req, err := http.NewRequestWithContext(ctx, http.MethodPost, u.String(), bytes.NewReader(body))
assert.Success(t, "new request", err)

resp, err := http.DefaultClient.Do(req)
assert.Success(t, "do request", err)
assert.Equal(t, "request status 201", http.StatusCreated, resp.StatusCode)

var tokenResp loginBuiltInAuthResp
err = json.NewDecoder(resp.Body).Decode(&tokenResp)
assert.Success(t, "decode response", err)

defer resp.Body.Close()

return tokenResp.SessionToken
}
4 changes: 2 additions & 2 deletions ci/build.sh → ci/steps/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ mkdir -p bin

build(){
tmpdir=$(mktemp -d)
go build -ldflags "-s -w -X main.version=${tag}" -o "$tmpdir/coder" ../cmd/coder
go build -ldflags "-s -w -X main.version=${tag}" -o "$tmpdir/coder" ../../cmd/coder

pushd "$tmpdir"
tarname="coder-cli-$GOOS-$GOARCH.tar.gz"
tar -czf "$tarname" coder
popd

cp "$tmpdir/$tarname" bin
cp "$tmpdir/$tarname" ../bin
rm -rf "$tmpdir"
}

Expand Down
16 changes: 16 additions & 0 deletions ci/steps/fmt.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash
echo "Formatting..."

go mod tidy
gofmt -w -s .
goimports -w "-local=$$(go list -m)" .

if [ "$CI" != "" ]; then
if [[ $(git ls-files --other --modified --exclude-standard) != "" ]]; then
echo "Files need generation or are formatted incorrectly:"
git -c color.ui=always status | grep --color=no '\e\[31m'
echo "Please run the following locally:"
echo " ./ci/steps/fmt.sh"
exit 1
fi
fi
6 changes: 6 additions & 0 deletions ci/steps/lint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash

echo "Linting..."

go vet ./...
golint -set_exit_status ./...
4 changes: 4 additions & 0 deletions ci/tcli/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Package tcli provides a framework for CLI integration testing.
// Execute commands on the raw host or inside a docker container.
// Define custom Assertion types to extend test functionality.
package tcli
Loading