A small Go tool that wraps the `/etc/hosts` + `once deploy --disable-tls` recipe needed to host a Once app on a single-label `<name>.local`. Adds the hosts entry, runs the deploy, polls /up, and on removal cleans the hosts line that the Once UI orphans (including the orphan-only case, where the app is already gone). Bubble Tea TUI for interactive use; non-interactive CLI for scripts. Curated apps live in /etc/once-add/apps.toml; seeded with Writebook + Campfire on first run.
76 lines
1.9 KiB
Go
76 lines
1.9 KiB
Go
package tui
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/charmbracelet/bubbles/spinner"
|
|
"github.com/charmbracelet/bubbles/textinput"
|
|
|
|
"once-add/internal/config"
|
|
)
|
|
|
|
// testModel builds a model without touching /etc, with components initialised
|
|
// so View() never panics.
|
|
func testModel() model {
|
|
sp := spinner.New()
|
|
sp.Spinner = spinner.Dot
|
|
return model{
|
|
customInput: textinput.New(),
|
|
nameInput: textinput.New(),
|
|
spinner: sp,
|
|
apps: []config.App{
|
|
{Name: "Writebook", Image: "ghcr.io/basecamp/writebook", Description: "Books", Suggested: "book"},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestViewAllStates(t *testing.T) {
|
|
states := []state{
|
|
stateMenu, stateSelectApp, stateCustomImage, stateName, stateDeploying,
|
|
stateRemovePick, stateRemoveConfirm, stateRemoving, stateDone, stateError,
|
|
}
|
|
for _, st := range states {
|
|
m := testModel()
|
|
m.state = st
|
|
m.name = "book"
|
|
m.image = "ghcr.io/basecamp/writebook"
|
|
m.resultURL = "http://book.local"
|
|
m.removeHosts = []string{"book.local", "chat.local"}
|
|
m.removeTarget = "chat.local"
|
|
m.doneMsg = "Removed chat.local"
|
|
m.err = fmt.Errorf("boom")
|
|
out := m.View()
|
|
if !strings.Contains(out, "once-add") {
|
|
t.Errorf("state %d: view missing title:\n%s", st, out)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRemoveDoneWording(t *testing.T) {
|
|
m := testModel()
|
|
m.state = stateDone
|
|
m.op = opRemove
|
|
m.doneMsg = "Removed chat.local"
|
|
if !strings.Contains(m.View(), "Removed chat.local") {
|
|
t.Errorf("remove done view missing message:\n%s", m.View())
|
|
}
|
|
}
|
|
|
|
func TestEmptyRemovePick(t *testing.T) {
|
|
m := testModel()
|
|
m.state = stateRemovePick
|
|
m.removeHosts = nil
|
|
if !strings.Contains(m.View(), "No deployed") {
|
|
t.Errorf("empty remove pick should say nothing to remove:\n%s", m.View())
|
|
}
|
|
}
|
|
|
|
func TestSelectShowsCustomRow(t *testing.T) {
|
|
m := testModel()
|
|
out := m.viewSelect()
|
|
if !strings.Contains(out, "Writebook") || !strings.Contains(out, "Custom image") {
|
|
t.Errorf("select view missing rows:\n%s", out)
|
|
}
|
|
}
|