summaryrefslogtreecommitdiff
path: root/internal/repl
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-23 23:24:42 +0300
committerPaul Buetow <paul@buetow.org>2026-05-23 23:24:42 +0300
commit713e81daef9b01f24c4120c5e3e34d242f226797 (patch)
treecf760da16df9655900a828de7b8b5afba0e9d10f /internal/repl
parentd56e16dc85ff3efe7b5865bd27e4804423a6e463 (diff)
chore: bump go.mod from go 1.25.0 to go 1.26.0
Update the Go version directive to match the local runtime (go1.26.3). Also includes go mod tidy cleanup and unrelated test improvements.
Diffstat (limited to 'internal/repl')
-rw-r--r--internal/repl/commands_test.go8
-rw-r--r--internal/repl/repl_test.go61
-rw-r--r--internal/repl/rpnstate_test.go118
3 files changed, 187 insertions, 0 deletions
diff --git a/internal/repl/commands_test.go b/internal/repl/commands_test.go
index 102c34b..7d38d16 100644
--- a/internal/repl/commands_test.go
+++ b/internal/repl/commands_test.go
@@ -112,3 +112,11 @@ func TestExecuteCommandEmpty(t *testing.T) {
t.Fatalf("ExecuteCommand('') returned error: %v", err)
}
}
+
+func TestCmdStack(t *testing.T) {
+ output := cmdStack()
+ expected := "Use 'rpn show' to view the current stack state"
+ if output != expected {
+ t.Errorf("cmdStack() = %q, want %q", output, expected)
+ }
+}
diff --git a/internal/repl/repl_test.go b/internal/repl/repl_test.go
index 8112227..89fb89a 100644
--- a/internal/repl/repl_test.go
+++ b/internal/repl/repl_test.go
@@ -713,3 +713,64 @@ func TestDefaultExecutorCodePaths(t *testing.T) {
// Path 5: Whitespace only (trimmed to empty, returns early)
defaultExecutor(repl, " ")
}
+
+func TestDefaultCompleter(t *testing.T) {
+ repl := createTestREPL()
+ got := defaultCompleter(repl)
+ expected := Commands()
+ if len(got) != len(expected) {
+ t.Fatalf("defaultCompleter() returned %d items, want %d", len(got), len(expected))
+ }
+ for i, cmd := range expected {
+ if got[i] != cmd {
+ t.Errorf("defaultCompleter()[%d] = %q, want %q", i, got[i], cmd)
+ }
+ }
+}
+
+func TestDefaultGetCommandDescription(t *testing.T) {
+ repl := createTestREPL()
+ desc := repl.defaultGetCommandDescription("help")
+ if desc != "Show help information" {
+ t.Errorf("defaultGetCommandDescription(\"help\") = %q, want %q", desc, "Show help information")
+ }
+}
+
+func TestNewREPLNilArgs(t *testing.T) {
+ // NewREPL with nil args should create a valid REPL struct
+ repl := NewREPL(nil, nil, nil)
+ if repl == nil {
+ t.Fatal("NewREPL(nil, nil, nil) returned nil")
+ }
+ if repl.ttyChecker == nil {
+ t.Error("REPL.ttyChecker should not be nil")
+ }
+ if repl.historyMgr == nil {
+ t.Error("REPL.historyMgr should not be nil")
+ }
+ if repl.signalHandler == nil {
+ t.Error("REPL.signalHandler should not be nil")
+ }
+ if repl.rpnState == nil {
+ t.Error("REPL.rpnState should not be nil")
+ }
+}
+
+func TestREPLDefaultGetCommandDescription(t *testing.T) {
+ repl := createTestREPL()
+ tests := []struct {
+ cmd string
+ want string
+ }{
+ {"help", "Show help information"},
+ {"unknown", ""},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.cmd, func(t *testing.T) {
+ if got := repl.defaultGetCommandDescription(tt.cmd); got != tt.want {
+ t.Errorf("defaultGetCommandDescription(%q) = %q, want %q", tt.cmd, got, tt.want)
+ }
+ })
+ }
+}
diff --git a/internal/repl/rpnstate_test.go b/internal/repl/rpnstate_test.go
new file mode 100644
index 0000000..1d54dea
--- /dev/null
+++ b/internal/repl/rpnstate_test.go
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2026 Paul Buetow
+
+package repl
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "codeberg.org/snonux/gt/internal/rpn"
+)
+
+func TestNewRPNState(t *testing.T) {
+ vars := rpn.NewVariables()
+ rpnCalc := rpn.NewRPN(vars)
+ state := NewRPNState(vars, rpnCalc)
+ if state == nil {
+ t.Fatal("NewRPNState() returned nil")
+ }
+}
+
+func TestRPNStateLoadVariablesEmpty(t *testing.T) {
+ // When varStoreFile is empty, LoadVariables should return nil
+ state := &RPNState{
+ vars: rpn.NewVariables(),
+ rpnCalc: rpn.NewRPN(rpn.NewVariables()),
+ varStoreFile: "",
+ }
+ err := state.LoadVariables()
+ if err != nil {
+ t.Errorf("LoadVariables() with empty path = %v, want nil", err)
+ }
+}
+
+func TestRPNStateSaveVariablesEmpty(t *testing.T) {
+ state := &RPNState{
+ vars: rpn.NewVariables(),
+ rpnCalc: rpn.NewRPN(rpn.NewVariables()),
+ varStoreFile: "",
+ }
+ err := state.SaveVariables()
+ if err != nil {
+ t.Errorf("SaveVariables() with empty path = %v, want nil", err)
+ }
+}
+
+func TestGetVarStoreFilePath(t *testing.T) {
+ path := getVarStoreFilePath()
+ if path == "" {
+ t.Fatal("getVarStoreFilePath() returned empty string")
+ }
+ if !strings.Contains(path, ".local") {
+ t.Errorf("path should contain '.local', got %q", path)
+ }
+ if !strings.Contains(path, "gt") {
+ t.Errorf("path should contain 'gt', got %q", path)
+ }
+ if !strings.Contains(path, "vars") {
+ t.Errorf("path should contain 'vars', got %q", path)
+ }
+}
+
+func TestRPNStateSaveLoadRoundTrip(t *testing.T) {
+ tmpDir := t.TempDir()
+ varStorePath := filepath.Join(tmpDir, "vars.json")
+
+ vars := rpn.NewVariables()
+ vars.SetVariable("x", 42.0)
+ vars.SetVariable("y", 3.14)
+
+ rpnCalc := rpn.NewRPN(vars)
+ state := &RPNState{
+ vars: vars,
+ rpnCalc: rpnCalc,
+ varStoreFile: varStorePath,
+ }
+
+ err := state.SaveVariables()
+ if err != nil {
+ t.Fatalf("SaveVariables() error: %v", err)
+ }
+
+ // Verify the file exists
+ if _, err := os.Stat(varStorePath); os.IsNotExist(err) {
+ t.Fatal("SaveVariables() did not create file")
+ }
+
+ // Load into a new variable store
+ newVars := rpn.NewVariables()
+ newState := &RPNState{
+ vars: newVars,
+ rpnCalc: rpn.NewRPN(newVars),
+ varStoreFile: varStorePath,
+ }
+
+ err = newState.LoadVariables()
+ if err != nil {
+ t.Fatalf("LoadVariables() error: %v", err)
+ }
+
+ val, ok := newVars.GetVariable("x")
+ if !ok {
+ t.Fatal("variable x not found after load")
+ }
+ if val != 42.0 {
+ t.Errorf("x = %v, want 42.0", val)
+ }
+
+ val, ok = newVars.GetVariable("y")
+ if !ok {
+ t.Fatal("variable y not found after load")
+ }
+ if val != 3.14 {
+ t.Errorf("y = %v, want 3.14", val)
+ }
+}