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
5 changes: 5 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ jobs:
go test -coverpkg=./... ./database/... -coverprofile=coverage-database-${{ matrix.go-version }}.out
go tool cover -func=coverage-database-${{ matrix.go-version }}.out | tail -n 1

- name: Run CLI tests
run: |
go test -coverpkg=./... ./cli/... -coverprofile=coverage-cli-${{ matrix.go-version }}.out
go tool cover -func=coverage-cli-${{ matrix.go-version }}.out | tail -n 1

- name: Merge coverage reports
run: |
echo "mode: set" > coverage-${{ matrix.go-version }}.out
Expand Down
35 changes: 35 additions & 0 deletions cli/accounts/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package accounts

import (
clitest "github.com/oullin/cli/clitest"
"testing"

"github.com/oullin/database"
)

func TestMakeHandler(t *testing.T) {
conn := clitest.MakeTestConnection(t, &database.APIKey{})
h, err := MakeHandler(conn, clitest.MakeTestEnv())
if err != nil {
t.Fatalf("make handler: %v", err)
}
if h.TokenHandler == nil || h.Tokens == nil {
t.Fatalf("handler not properly initialized")
}
if err := h.CreateAccount("sampleaccount"); err != nil {
t.Fatalf("create account: %v", err)
}
var key database.APIKey
if err := conn.Sql().First(&key, "account_name = ?", "sampleaccount").Error; err != nil {
t.Fatalf("key not saved: %v", err)
}
}

func TestMakeHandlerInvalidKey(t *testing.T) {
conn := clitest.MakeTestConnection(t)
env := clitest.MakeTestEnv()
env.App.MasterKey = "short"
if _, err := MakeHandler(conn, env); err == nil {
t.Fatalf("expected error")
}
}
51 changes: 51 additions & 0 deletions cli/accounts/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package accounts

import (
"testing"

clitest "github.com/oullin/cli/clitest"
"github.com/oullin/database"
)

func setupAccountHandler(t *testing.T) *Handler {
conn := clitest.MakeTestConnection(t, &database.APIKey{})
h, err := MakeHandler(conn, clitest.MakeTestEnv())
if err != nil {
t.Fatalf("make handler: %v", err)
}
return h
}

func TestCreateReadSignature(t *testing.T) {
h := setupAccountHandler(t)
if err := h.CreateAccount("tester"); err != nil {
t.Fatalf("create: %v", err)
}
if err := h.ReadAccount("tester"); err != nil {
t.Fatalf("read: %v", err)
}
if err := h.CreateSignature("tester"); err != nil {
t.Fatalf("signature: %v", err)
}
}

func TestCreateAccountInvalid(t *testing.T) {
h := setupAccountHandler(t)
if err := h.CreateAccount("ab"); err == nil {
t.Fatalf("expected error")
}
}

func TestReadAccountNotFound(t *testing.T) {
h := setupAccountHandler(t)
if err := h.ReadAccount("missing"); err == nil {
t.Fatalf("expected error")
}
}

func TestCreateSignatureNotFound(t *testing.T) {
h := setupAccountHandler(t)
if err := h.CreateSignature("missing"); err == nil {
t.Fatalf("expected error")
}
}
75 changes: 75 additions & 0 deletions cli/clitest/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package clitest

import (
"context"
"os/exec"
"testing"

"github.com/google/uuid"
"github.com/oullin/database"
"github.com/oullin/env"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/postgres"
)

func MakeTestConnection(t *testing.T, models ...interface{}) *database.Connection {
t.Helper()

if _, err := exec.LookPath("docker"); err != nil {
t.Skip("docker not installed")
}

ctx := context.Background()

pg, err := postgres.RunContainer(ctx,
testcontainers.WithImage("postgres:16-alpine"),
postgres.WithDatabase("testdb"),
postgres.WithUsername("test"),
postgres.WithPassword("secret"),
postgres.BasicWaitStrategies(),
)
if err != nil {
t.Fatalf("container run err: %v", err)
}
t.Cleanup(func() { pg.Terminate(ctx) })

host, err := pg.Host(ctx)
if err != nil {
t.Fatalf("host err: %v", err)
}
port, err := pg.MappedPort(ctx, "5432/tcp")
if err != nil {
t.Fatalf("port err: %v", err)
}

e := &env.Environment{
DB: env.DBEnvironment{
UserName: "test",
UserPassword: "secret",
DatabaseName: "testdb",
Port: port.Int(),
Host: host,
DriverName: database.DriverName,
SSLMode: "disable",
TimeZone: "UTC",
},
}

conn, err := database.MakeConnection(e)
if err != nil {
t.Fatalf("make connection: %v", err)
}
t.Cleanup(func() { conn.Close() })

if len(models) > 0 {
if err := conn.Sql().AutoMigrate(models...); err != nil {
t.Fatalf("migrate: %v", err)
}
}

return conn
}

func MakeTestEnv() *env.Environment {
return &env.Environment{App: env.AppEnvironment{MasterKey: uuid.NewString()[:32]}}
}
22 changes: 22 additions & 0 deletions cli/clitest/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package clitest

import (
"os/exec"
"testing"
)

func TestMakeTestEnv(t *testing.T) {
env := MakeTestEnv()
if len(env.App.MasterKey) != 32 {
t.Fatalf("expected master key length 32, got %d", len(env.App.MasterKey))
}
}

func TestMakeTestConnectionSkipsWithoutDocker(t *testing.T) {
if _, err := exec.LookPath("docker"); err != nil {
t.Skip("docker not available")
}
t.Run("skip", func(t *testing.T) {
MakeTestConnection(t)
})
}
95 changes: 95 additions & 0 deletions cli/panel/menu_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package panel

import (
"bufio"
"io"
"os"
"strings"
"testing"

"github.com/oullin/pkg"
)

func captureOutput(fn func()) string {
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
fn()
w.Close()
out, _ := io.ReadAll(r)
os.Stdout = old
return string(out)
}

func TestPrintLineAndGetChoiceNil(t *testing.T) {
m := Menu{Reader: bufio.NewReader(strings.NewReader("\n"))}
m.PrintLine()
if m.GetChoice() != 0 {
t.Fatalf("expected 0")
}
}

func TestPrint(t *testing.T) {
m := Menu{Reader: bufio.NewReader(strings.NewReader(""))}
_ = captureOutput(func() { m.Print() })
}

func TestCenterText(t *testing.T) {
m := Menu{}
if got := m.CenterText("hi", 6); got != " hi " {
t.Fatalf("unexpected: %q", got)
}
if got := m.CenterText("toolong", 4); got != "tool" {
t.Fatalf("unexpected truncation: %q", got)
}
}

func TestPrintOption(t *testing.T) {
m := Menu{}
out := captureOutput(func() { m.PrintOption("x", 5) })
if !strings.Contains(out, "║ x ║") {
t.Fatalf("unexpected output: %q", out)
}
}

func TestCaptureInput(t *testing.T) {
m := Menu{Reader: bufio.NewReader(strings.NewReader("2\n"))}
if err := m.CaptureInput(); err != nil {
t.Fatalf("capture: %v", err)
}
if m.GetChoice() != 2 {
t.Fatalf("choice: %d", m.GetChoice())
}

bad := Menu{Reader: bufio.NewReader(strings.NewReader("bad\n"))}
if err := bad.CaptureInput(); err == nil {
t.Fatalf("expected error")
}
}

func TestCaptureAccountName(t *testing.T) {
m := Menu{Reader: bufio.NewReader(strings.NewReader("Alice\n"))}
name, err := m.CaptureAccountName()
if err != nil || name != "Alice" {
t.Fatalf("got %q err %v", name, err)
}

bad := Menu{Reader: bufio.NewReader(strings.NewReader("\n"))}
if _, err := bad.CaptureAccountName(); err == nil {
t.Fatalf("expected error")
}
}

func TestCapturePostURL(t *testing.T) {
goodURL := "https://raw.githubusercontent.com/user/repo/file.md"
m := Menu{Reader: bufio.NewReader(strings.NewReader(goodURL + "\n")), Validator: pkg.GetDefaultValidator()}
in, err := m.CapturePostURL()
if err != nil || in.Url != goodURL {
t.Fatalf("got %v err %v", in, err)
}

m2 := Menu{Reader: bufio.NewReader(strings.NewReader("http://example.com\n")), Validator: pkg.GetDefaultValidator()}
if _, err := m2.CapturePostURL(); err == nil {
t.Fatalf("expected error")
}
}
Loading
Loading