Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(testing): refactor + add initial set of unit tests #48

Merged
merged 3 commits into from
Jan 16, 2024
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
buoy
dist/
dist/

cover.out
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ GOLANGCI_LINT_ARGS ?=
lint: $(GOLANGCI_LINT)
$(GOLANGCI_LINT) run $(GOLANGCI_LINT_ARGS)
unit:
go test ./...
go test ./... -coverprofile=cover.out -covermode=atomic

2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be
github.com/sahilm/fuzzy v0.1.0
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.2
github.com/tidwall/gjson v1.17.0
k8s.io/api v0.28.1
k8s.io/apimachinery v0.28.1
Expand All @@ -21,6 +22,7 @@ require (
require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
)
Expand Down
27 changes: 23 additions & 4 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import (
tea "github.com/charmbracelet/bubbletea"
"github.com/everettraven/buoy/pkg/charm/models"
"github.com/everettraven/buoy/pkg/charm/styles"
"github.com/everettraven/buoy/pkg/paneler"
"github.com/everettraven/buoy/pkg/factories/datastream"
"github.com/everettraven/buoy/pkg/factories/panel"
"github.com/everettraven/buoy/pkg/types"
"github.com/spf13/cobra"
"sigs.k8s.io/controller-runtime/pkg/client/config"
Expand All @@ -38,6 +39,10 @@ func init() {
rootCommand.Flags().String("theme", styles.DefaultThemePath, "path to theme file")
}

type ErrorSetter interface {
SetError(err error)
}

func run(path string, themePath string) error {
var raw []byte
var ext string
Expand Down Expand Up @@ -76,21 +81,35 @@ func run(path string, themePath string) error {
log.Fatalf("loading theme: %s", err)
}

p := panel.NewPanelFactory(theme)

cfg := config.GetConfigOrDie()
p, err := paneler.NewDefaultPaneler(cfg, theme)
df, err := datastream.NewDatastreamFactory(cfg)
if err != nil {
log.Fatalf("configuring paneler: %s", err)
log.Fatalf("configuring datastream factory: %s", err)
}

panelModels := []tea.Model{}
for _, panel := range dash.Panels {
mod, err := p.Model(panel)
mod, err := p.ModelForPanel(panel)
if err != nil {
log.Fatalf("getting model for panel %q: %s", panel.Name, err)
}
panelModels = append(panelModels, mod)
}

for _, panel := range panelModels {
dataStream, err := df.DatastreamForModel(panel)
if err != nil {
if errSetter, ok := panel.(ErrorSetter); ok {
errSetter.SetError(err)
} else {
log.Fatalf("getting datastream for model: %s", err)
}
}
go dataStream.Run(make(<-chan struct{}))
}

m := models.NewDashboard(models.DefaultDashboardKeys, theme, panelModels...)
if _, err := tea.NewProgram(m, tea.WithAltScreen()).Run(); err != nil {
fmt.Println("Error running program:", err)
Expand Down
4 changes: 2 additions & 2 deletions pkg/charm/models/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ type Dashboard struct {
width int
help help.Model
keys DashboardKeyMap
theme *styles.Theme
theme styles.Theme
}

func NewDashboard(keys DashboardKeyMap, theme *styles.Theme, panels ...tea.Model) *Dashboard {
func NewDashboard(keys DashboardKeyMap, theme styles.Theme, panels ...tea.Model) *Dashboard {
tabs := []Tab{}
for _, panel := range panels {
if namer, ok := panel.(Namer); ok {
Expand Down
36 changes: 36 additions & 0 deletions pkg/charm/models/dashboard_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package models

import (
"testing"

"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/everettraven/buoy/pkg/charm/models/panels"
"github.com/everettraven/buoy/pkg/charm/styles"
"github.com/everettraven/buoy/pkg/types"
"github.com/stretchr/testify/assert"
)

func TestDashboardUpdate(t *testing.T) {
panels := []tea.Model{
panels.NewItem(types.Item{
PanelBase: types.PanelBase{
Name: "test",
},
}, viewport.New(10, 10), styles.Theme{}),
}

d := NewDashboard(DefaultDashboardKeys, styles.Theme{}, panels...)

t.Log("WindowSizeUpdate")
d.Update(tea.WindowSizeMsg{Width: 50, Height: 50})
assert.Equal(t, 50, d.width)

t.Log("toggle detailed help")
d.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("ctrl+h")})
assert.True(t, d.help.ShowAll)

t.Log("quit the program")
_, cmd := d.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("q")})
assert.Equal(t, cmd(), tea.Quit())
}
28 changes: 24 additions & 4 deletions pkg/charm/models/panels/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,26 @@ import (

"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/everettraven/buoy/pkg/charm/styles"
"github.com/everettraven/buoy/pkg/types"
)

// Item is a tea.Model implementation
// that represents an item panel
type Item struct {
viewport viewport.Model
name string
mutex *sync.Mutex
item types.Item
theme styles.Theme
err error
}

func NewItem(name string, viewport viewport.Model) *Item {
func NewItem(item types.Item, viewport viewport.Model, theme styles.Theme) *Item {
return &Item{
viewport: viewport,
name: name,
mutex: &sync.Mutex{},
item: item,
theme: theme,
}
}

Expand All @@ -37,6 +42,9 @@ func (m *Item) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

func (m *Item) View() string {
if m.err != nil {
return m.err.Error()
}
return m.viewport.View()
}

Expand All @@ -47,5 +55,17 @@ func (m *Item) SetContent(content string) {
}

func (m *Item) Name() string {
return m.name
return m.item.Name
}

func (m *Item) ItemDefinition() types.Item {
return m.item
}

func (m *Item) Theme() styles.Theme {
return m.theme
}

func (m *Item) SetError(err error) {
m.err = err
}
32 changes: 32 additions & 0 deletions pkg/charm/models/panels/item_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package panels

import (
"errors"
"testing"

"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/everettraven/buoy/pkg/charm/styles"
"github.com/everettraven/buoy/pkg/types"
"github.com/stretchr/testify/assert"
)

func TestItemUpdate(t *testing.T) {
item := NewItem(types.Item{}, viewport.New(10, 10), styles.Theme{})
item.Update(tea.WindowSizeMsg{Width: 50, Height: 50})
assert.Equal(t, 50, item.viewport.Width)
assert.Equal(t, 25, item.viewport.Height)
}

func TestItemViewWithError(t *testing.T) {
item := NewItem(types.Item{}, viewport.New(10, 10), styles.Theme{})
err := errors.New("some error")
item.SetError(err)
assert.Equal(t, err.Error(), item.View())
}

func TestViewWithContent(t *testing.T) {
item := NewItem(types.Item{}, viewport.New(50, 50), styles.Theme{})
item.SetContent("some content")
assert.Contains(t, item.View(), "some content")
}
24 changes: 19 additions & 5 deletions pkg/charm/models/panels/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/everettraven/buoy/pkg/charm/styles"
"github.com/everettraven/buoy/pkg/types"
"github.com/muesli/reflow/wrap"
"github.com/sahilm/fuzzy"
)
Expand Down Expand Up @@ -65,25 +66,26 @@ const modeSearched = "searched"
type Logs struct {
viewport viewport.Model
searchbar textinput.Model
name string
mutex *sync.Mutex
content string
contentUpdated bool
mode string
keys LogsKeyMap
strictSearch bool
theme *styles.Theme
theme styles.Theme
log *types.Logs
err error
}

func NewLogs(keys LogsKeyMap, name string, theme *styles.Theme) *Logs {
func NewLogs(keys LogsKeyMap, log *types.Logs, theme styles.Theme) *Logs {
searchbar := textinput.New()
searchbar.Prompt = "> "
searchbar.Placeholder = "search term"
vp := viewport.New(10, 10)
return &Logs{
viewport: vp,
searchbar: searchbar,
name: name,
log: log,
mutex: &sync.Mutex{},
content: "",
mode: modeLogs,
Expand Down Expand Up @@ -149,6 +151,10 @@ func (m *Logs) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

func (m *Logs) View() string {
if m.err != nil {
return m.err.Error()
}

searchMode := "fuzzy"
if m.strictSearch {
searchMode = "strict"
Expand Down Expand Up @@ -184,7 +190,15 @@ func (m *Logs) AddContent(content string) {
}

func (m *Logs) Name() string {
return m.name
return m.log.Name
}

func (m *Logs) LogDefinition() *types.Logs {
return m.log
}

func (m *Logs) SetError(err error) {
m.err = err
}

// searchLogs searches the logs for the term in the searchbar
Expand Down
89 changes: 89 additions & 0 deletions pkg/charm/models/panels/logs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package panels

import (
"errors"
"testing"

tea "github.com/charmbracelet/bubbletea"
"github.com/everettraven/buoy/pkg/charm/styles"
"github.com/stretchr/testify/assert"
)

func TestEnterSearchMode(t *testing.T) {
logs := NewLogs(DefaultLogsKeys, nil, styles.Theme{})
logs.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("/")})
assert.Equal(t, logs.mode, modeSearching)
}

func TestExecuteSearch(t *testing.T) {
logs := NewLogs(DefaultLogsKeys, nil, styles.Theme{})
logs.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("/")})
logs.Update(tea.KeyMsg{Type: tea.KeyEnter})
assert.Equal(t, logs.mode, modeSearched)
}

func TestExitSearchMode(t *testing.T) {
logs := NewLogs(DefaultLogsKeys, nil, styles.Theme{})
logs.mode = modeSearching
logs.searchbar.Focus()
logs.Update(tea.KeyMsg{Type: tea.KeyEsc})
assert.Equal(t, logs.mode, modeLogs)
assert.False(t, logs.searchbar.Focused())

logs.mode = modeSearched
logs.searchbar.Focus()
logs.Update(tea.KeyMsg{Type: tea.KeyEsc})
assert.Equal(t, logs.mode, modeLogs)
assert.False(t, logs.searchbar.Focused())
}

func TestSearchModeToggle(t *testing.T) {
logs := NewLogs(DefaultLogsKeys, nil, styles.Theme{})
logs.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("/")})
logs.Update(tea.KeyMsg{Type: tea.KeyCtrlS})
assert.True(t, logs.strictSearch)
logs.Update(tea.KeyMsg{Type: tea.KeyCtrlS})
assert.False(t, logs.strictSearch)
}

func TestSearchLogs(t *testing.T) {
t.Log("strict search")
logs := NewLogs(DefaultLogsKeys, nil, styles.Theme{})
logs.strictSearch = true
logs.content = "some log line\nlog line with a search term\n"
logs.viewport.Width = 50
logs.searchbar.SetValue("search")
match := logs.searchLogs()
assert.Equal(t, "log line with a search term\n", match)

t.Log("fuzzy search")
logs.searchbar.SetValue("sll")
logs.strictSearch = false
match = logs.searchLogs()
assert.Equal(t, "some log line\n", match)
}

func TestLogsWindowSizeUpdate(t *testing.T) {
logs := NewLogs(DefaultLogsKeys, nil, styles.Theme{})
logs.Update(tea.WindowSizeMsg{Width: 100, Height: 100})
assert.Equal(t, logs.viewport.Width, 100)
assert.Equal(t, logs.viewport.Height, 50)
}

func TestLogsAddContent(t *testing.T) {
logs := NewLogs(DefaultLogsKeys, nil, styles.Theme{})
logs.AddContent("some log line\n")
assert.Equal(t, "\nsome log line\n", logs.content)
assert.True(t, logs.contentUpdated)

logs.Update(tea.KeyMsg{Type: tea.KeyDown})
assert.False(t, logs.contentUpdated)
}

func TestLogsViewWithError(t *testing.T) {
logs := NewLogs(DefaultLogsKeys, nil, styles.Theme{})
err := errors.New("some error")
logs.SetError(err)
assert.Equal(t, err, logs.err)
assert.Equal(t, err.Error(), logs.View())
}
Loading