summaryrefslogtreecommitdiff
path: root/internal/repl/repl_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/repl/repl_test.go')
-rw-r--r--internal/repl/repl_test.go365
1 files changed, 347 insertions, 18 deletions
diff --git a/internal/repl/repl_test.go b/internal/repl/repl_test.go
index 53a707a..0751060 100644
--- a/internal/repl/repl_test.go
+++ b/internal/repl/repl_test.go
@@ -1,6 +1,7 @@
package repl
import (
+ "strings"
"testing"
"github.com/c-bata/go-prompt"
@@ -13,7 +14,6 @@ func TestExecutor(t *testing.T) {
func TestExecutorWithHelp(t *testing.T) {
// Test executor with help command
- // This should execute the help command and not print output
executor("help")
}
@@ -22,8 +22,6 @@ func TestExecutorWithClear(t *testing.T) {
}
func TestExecutorWithQuit(t *testing.T) {
- // This should exit REPL but we can't test the actual exit
- // We just verify the command is processed without error
executor("quit")
}
@@ -32,23 +30,17 @@ func TestExecutorWithExit(t *testing.T) {
}
func TestExecutorWithPercentage(t *testing.T) {
- // Test executor with a percentage calculation
- // Note: output is printed to stdout, we just verify it doesn't panic
executor("20% of 150")
}
func TestExecutorWithRPN(t *testing.T) {
- // Test executor with RPN command
executor("rpn 3 4 +")
}
func TestExecutorWithInvalid(t *testing.T) {
- // Test executor with invalid input
executor("invalid input")
}
-// Note: captureOutput is removed - we test for side effects instead of capturing output
-
func TestExecutorWithVars(t *testing.T) {
executor("rpn x 5 = vars")
}
@@ -58,7 +50,6 @@ func TestExecutorWithClearVariables(t *testing.T) {
}
func TestIsBuiltinCommand(t *testing.T) {
- // Test known built-in commands
tests := []struct {
input string
expected bool
@@ -84,29 +75,367 @@ func TestIsBuiltinCommand(t *testing.T) {
}
func TestIsBuiltinCommandWithSubcommand(t *testing.T) {
- // Test with help subcommands
_, ok := isBuiltinCommand("help clear")
if !ok {
t.Error("isBuiltinCommand('help clear') should return true")
}
}
+func TestIsBuiltinCommandEdgeCases(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ expected bool
+ }{
+ {"empty string", "", false},
+ {"single space", " ", false},
+ {"case insensitive - HELP", "HELP", true},
+ {"case insensitive - HeLp", "HeLp", true},
+ {"command with extra spaces", " help ", true},
+ {"partial match - hel", "hel", false},
+ {"partial match - cal", "cal", false},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ _, ok := isBuiltinCommand(tt.input)
+ if ok != tt.expected {
+ t.Errorf("isBuiltinCommand(%q) = %v, want %v", tt.input, ok, tt.expected)
+ }
+ })
+ }
+}
+
+func TestIsBuiltinCommandEdgeCasesWithAllCommands(t *testing.T) {
+ allCommands := []string{"help", "clear", "quit", "exit", "rpn", "calc"}
+ for _, cmd := range allCommands {
+ t.Run(cmd, func(t *testing.T) {
+ input, ok := isBuiltinCommand(cmd)
+ if !ok {
+ t.Errorf("isBuiltinCommand(%q) should return true for builtin command", cmd)
+ }
+ if input != cmd {
+ t.Errorf("isBuiltinCommand(%q) returned %q, want %q", cmd, input, cmd)
+ }
+ })
+ }
+}
+
+func TestIsBuiltinCommandWithMixedCase(t *testing.T) {
+ tests := []struct {
+ input string
+ expected bool
+ }{
+ {"HELP", true},
+ {"Help", true},
+ {"hElP", true},
+ {"CLEAR", true},
+ {"quit", true},
+ {"QUIT", true},
+ {"RPN", true},
+ {"calc", true},
+ {"CALC", true},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.input, func(t *testing.T) {
+ _, ok := isBuiltinCommand(tt.input)
+ if ok != tt.expected {
+ t.Errorf("isBuiltinCommand(%q) = %v, want %v", tt.input, ok, tt.expected)
+ }
+ })
+ }
+}
+
func TestCompleter(t *testing.T) {
- // Test completer with empty input
suggestions := completer(prompt.Document{})
- // completer returns suggestions for builtin commands
- // We just verify it doesn't panic
_ = suggestions
}
func TestCompleterWithPartialMatch(t *testing.T) {
- // Test completer with partial command
- suggestions := completer(prompt.Document{Text: "h"})
- // completer returns suggestions for builtin commands
+ // Use trailing space to ensure GetWordBeforeCursor() returns non-empty
+ suggestions := completer(prompt.Document{Text: "h "})
_ = suggestions
}
func TestCompleterWithClearPrefix(t *testing.T) {
- suggestions := completer(prompt.Document{Text: "cl"})
+ suggestions := completer(prompt.Document{Text: "cl "})
_ = suggestions
}
+
+func TestCompleterWithEmptyText(t *testing.T) {
+ suggestions := completer(prompt.Document{Text: ""})
+ if suggestions != nil {
+ t.Errorf("completer with empty text should return nil, got %d suggestions", len(suggestions))
+ }
+}
+
+func TestCompleterWithAllCommands(t *testing.T) {
+ allCommands := []string{"help", "clear", "quit", "exit", "rpn", "calc"}
+ for _, cmd := range allCommands {
+ t.Run(cmd, func(t *testing.T) {
+ suggestions := completer(prompt.Document{Text: cmd + " "})
+ _ = suggestions
+ })
+ }
+}
+
+func TestCompleterWithNoPrefix(t *testing.T) {
+ suggestions := completer(prompt.Document{Text: "xyz "})
+ if len(suggestions) > 0 {
+ t.Errorf("completer(%q) should return no suggestions, got %d", "xyz", len(suggestions))
+ }
+}
+
+// TestRunRPN tests the runRPN helper function
+func TestRunRPN(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ wantErr bool
+ }{
+ {"simple addition", "3 4 +", false},
+ {"simple subtraction", "10 3 -", false},
+ {"simple multiplication", "2 3 *", false},
+ {"simple division", "10 2 /", false},
+ {"power operation", "2 3 ^", false},
+ {"modulo operation", "10 3 %", false},
+ {"with variables", "x 5 = x x +", false},
+ {"empty input", "", true},
+ {"invalid input", "invalid", true},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ _, err := runRPN(tt.input)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("runRPN(%q) error = %v, wantErr %v", tt.input, err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestGetCommandDescription(t *testing.T) {
+ tests := []struct {
+ cmd string
+ wantPrefix string
+ }{
+ {"help", "Show help"},
+ {"clear", "Clear"},
+ {"quit", "Exit"},
+ {"exit", "Exit"},
+ {"rpn", "Evaluate an RPN"},
+ {"calc", "Same as rpn"},
+ {"unknown", ""},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.cmd, func(t *testing.T) {
+ desc := getCommandDescription(tt.cmd)
+ if tt.wantPrefix != "" && !strings.Contains(desc, tt.wantPrefix) {
+ t.Errorf("getCommandDescription(%q) = %q, should contain %q", tt.cmd, desc, tt.wantPrefix)
+ }
+ })
+ }
+}
+
+func TestGetCommandDescriptionForUnknownCommand(t *testing.T) {
+ desc := getCommandDescription("unknown")
+ if desc != "" {
+ t.Errorf("getCommandDescription(%q) = %q, should be empty", "unknown", desc)
+ }
+}
+
+func TestExecutorWithSingleOperator(t *testing.T) {
+ executor("+")
+ executor("-")
+ executor("*")
+ executor("/")
+ executor("^")
+ executor("%")
+ executor("dup")
+ executor("swap")
+ executor("pop")
+ executor("show")
+ executor("vars")
+ executor("clear")
+}
+
+func TestExecutorWithPercentageExpression(t *testing.T) {
+ executor("20% of 150")
+ executor("30 is what %% of 150")
+ executor("30 is 20%% of what")
+}
+
+func TestExecutorWithInvalidPercentage(t *testing.T) {
+ executor("invalid percentage input")
+}
+
+func TestExecutorWithOperatorOnly(t *testing.T) {
+ executor("1 2 +")
+ executor("+")
+}
+
+func TestExecutorWithRPNPrefix(t *testing.T) {
+ executor("rpn 3 4 +")
+}
+
+func TestExecutorWithCalcPrefix(t *testing.T) {
+ executor("calc 5 6 +")
+}
+
+func TestExecutorWithEmptyInput(t *testing.T) {
+ executor("")
+}
+
+func TestExecutorWithWhitespaceOnly(t *testing.T) {
+ executor(" ")
+}
+
+func TestExecutorWithInvalidInput(t *testing.T) {
+ tests := []string{"invalid input", "not a valid command", "xyz"}
+ for _, input := range tests {
+ t.Run(input, func(t *testing.T) {
+ executor(input)
+ })
+ }
+}
+
+func TestExecutorWithInvalidRPN(t *testing.T) {
+ executor("rpn 1 +")
+}
+
+func TestExecutorWithEmptyRPNPrefix(t *testing.T) {
+ executor("rpn")
+ executor("calc")
+}
+
+func TestExecutorWithAssignment(t *testing.T) {
+ executor("rpn x 42 =")
+ executor("rpn x")
+}
+
+func TestExecutorWithPercentageAndRPNFallback(t *testing.T) {
+ executor("20% of 150")
+ executor("3 4 +")
+}
+
+func TestGetHistoryPath(t *testing.T) {
+ path := getHistoryPath()
+ if path == "" {
+ t.Error("getHistoryPath() returned empty string")
+ }
+}
+
+func TestLoadHistory(t *testing.T) {
+ history := loadHistory()
+ _ = history
+}
+
+func TestSaveHistory(t *testing.T) {
+ err := saveHistory([]string{"test1", "test2"})
+ _ = err
+}
+
+func TestExecutorWithRPNExpressionOnly(t *testing.T) {
+ executor("5 3 +")
+}
+
+func TestExecutorWithRPNThenOperator(t *testing.T) {
+ executor("1 2 +")
+ executor("+")
+}
+
+func TestExecutorWithRPNThenRPN(t *testing.T) {
+ executor("rpn 1 2 +")
+ executor("rpn 3 4 +")
+}
+
+func TestExecutorWithRPNShow(t *testing.T) {
+ executor("rpn show")
+}
+
+func TestExecutorWithRPNDup(t *testing.T) {
+ executor("rpn dup")
+}
+
+func TestExecutorWithRPNSwap(t *testing.T) {
+ executor("rpn swap")
+}
+
+func TestExecutorWithRPNSingle(t *testing.T) {
+ executor("rpn 42")
+}
+
+func TestExecutorWithRPNMulti(t *testing.T) {
+ executor("rpn 1 2 3 4 5 +")
+}
+
+func TestExecutorWithStackOps(t *testing.T) {
+ executor("dup")
+ executor("swap")
+ executor("pop")
+ executor("show")
+}
+
+func TestExecutorWithRPNClear(t *testing.T) {
+ executor("rpn clear")
+}
+
+func TestExecutorWithHistoryCommands(t *testing.T) {
+ executor("vars")
+ executor("clear")
+}
+
+func TestExecutorWithMixedInput(t *testing.T) {
+ executor("25% of 200")
+ executor("10 20 +")
+}
+
+func TestExecutorWithRPNCalcMixed(t *testing.T) {
+ executor("rpn 1 2 +")
+ executor("3 4 +")
+ executor("calc 5 6 +")
+}
+
+func TestExecutorCommandsEdgeCases(t *testing.T) {
+ executor(" clear ")
+ executor("HELP")
+ executor("CLEAR")
+}
+
+func TestExecutorWithRPMPrefix(t *testing.T) {
+ executor("rpn 1 2 +")
+}
+
+func TestExecutorWithCalcPrefixMixed(t *testing.T) {
+ executor("calc 1 2 +")
+}
+
+func TestCompleterEdgeCases(t *testing.T) {
+ tests := []struct {
+ name string
+ doc prompt.Document
+ }{
+ {"single character q", prompt.Document{Text: "q"}},
+ {"single character e", prompt.Document{Text: "e"}},
+ {"single character r", prompt.Document{Text: "r"}},
+ {"partial help", prompt.Document{Text: "he"}},
+ {"partial quit", prompt.Document{Text: "qui"}},
+ {"partial exit", prompt.Document{Text: "ex"}},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ suggestions := completer(tt.doc)
+ _ = suggestions
+ })
+ }
+}
+
+func TestIsBuiltinCommandWithSubcommandHelp(t *testing.T) {
+ _, ok := isBuiltinCommand("help")
+ if !ok {
+ t.Error("isBuiltinCommand('help') should return true")
+ }
+}