Remove the TUI; the web app is the frontend
money serve covers every screen the TUI had, so keeping both meant every behaviour change landing twice. internal/tui goes, and with it bubbletea, bubbles and lipgloss. `money` with no command now runs serve, the way it used to open the TUI, and `money tui` is an unknown command. Three invariants in CLAUDE.md were covered only by TUI tests. Two already had web counterparts; TestPairedLegsAreNotUntagged is ported to the API: a paired leg is neither listed as untagged nor offered to the rule builder, while an unpaired one still is. README's screen sections now describe the browser, which kept the behaviour and changed only the controls. CLAUDE.md names the web equivalents of the TUI functions its invariants pointed at, drops the tab completion rule that only bubbles' textinput needed, and says how to test the web app instead of how to drive a terminal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -386,3 +386,43 @@ func TestPageIsServed(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A paired leg is spoken for, so it is neither listed as untagged nor offered
|
||||
// to the rule builder; an unpaired leg is untagged like anything else, which
|
||||
// is how it gets noticed.
|
||||
func TestPairedLegsAreNotUntagged(t *testing.T) {
|
||||
_, h := newTestServer(t, `
|
||||
[[transfer]]
|
||||
from_account = "checking"
|
||||
from_desc = "*TO SAVINGS*"
|
||||
to_account = "savings"
|
||||
to_desc = "*FROM CHECKING*"
|
||||
`,
|
||||
fixtureTxn{"checking", "2026-02-01", "TRANSFER TO SAVINGS", -1000},
|
||||
fixtureTxn{"savings", "2026-02-01", "TRANSFER FROM CHECKING", 1000},
|
||||
fixtureTxn{"checking", "2026-02-20", "TRANSFER TO SAVINGS", -2000},
|
||||
fixtureTxn{"checking", "2026-02-05", "LIDL SOFIA", -500},
|
||||
)
|
||||
|
||||
var txns struct{ Rows []txnRow }
|
||||
call(t, h, "GET", "/api/transactions?untagged=1", nil, http.StatusOK, &txns)
|
||||
var seen []string
|
||||
for _, r := range txns.Rows {
|
||||
seen = append(seen, r.Date+" "+r.Description)
|
||||
}
|
||||
want := "2026-02-20 TRANSFER TO SAVINGS|2026-02-05 LIDL SOFIA"
|
||||
if strings.Join(seen, "|") != want {
|
||||
t.Errorf("untagged = %v, want only the unpaired leg and the shopping", seen)
|
||||
}
|
||||
|
||||
var p rulePreviewJSON
|
||||
call(t, h, "POST", "/api/rules/preview", rulePreviewReq{Glob: "*"}, http.StatusOK, &p)
|
||||
for _, r := range p.Rows {
|
||||
if r.Description == "TRANSFER FROM CHECKING" {
|
||||
t.Error("the arriving leg of a matched transfer is still offered for tagging")
|
||||
}
|
||||
if r.Description == "TRANSFER TO SAVINGS" && r.Count != 1 {
|
||||
t.Errorf("departing leg offered %d times, want only the unpaired one", r.Count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user