Files
egutierrez 33b11a63c8 feat: añadir sistema de comandos directos (!command)
Implementa pkg/command/ como core puro: tipos Spec/ParsedArgs, parser de
args key=value con soporte de comillas, specs de 8 comandos built-in
(help, tools, tool, ping, status, info, clear, version) y BuiltinNames()
para aliases.

En agents/runtime.go: nuevo flujo handleEvent que prioriza comandos sobre
LLM — custom rules del agente → built-in handlers → comando desconocido →
LLM fallback. Handlers en agents/commands.go. El comando !tool ejecuta
tools directamente via Registry con args key=value parseados.

LLM ahora es opcional: si no hay provider configurado, el agente corre
como simple_bot respondiendo solo a comandos.

Se extrae executeActions() como helper reutilizable para ambos flujos
(comando y no-comando).
2026-03-07 01:11:26 +00:00

91 lines
2.4 KiB
Go

package command
import (
"testing"
)
func TestParseArgs_Empty(t *testing.T) {
p := ParseArgs(nil)
if len(p.Positional) != 0 {
t.Errorf("expected 0 positional, got %d", len(p.Positional))
}
if len(p.Named) != 0 {
t.Errorf("expected 0 named, got %d", len(p.Named))
}
}
func TestParseArgs_Positional(t *testing.T) {
p := ParseArgs([]string{"ssh_command"})
if len(p.Positional) != 1 || p.Positional[0] != "ssh_command" {
t.Errorf("expected [ssh_command], got %v", p.Positional)
}
}
func TestParseArgs_Named(t *testing.T) {
p := ParseArgs([]string{"host=server1", "command=uptime"})
if p.Named["host"] != "server1" {
t.Errorf("expected host=server1, got %q", p.Named["host"])
}
if p.Named["command"] != "uptime" {
t.Errorf("expected command=uptime, got %q", p.Named["command"])
}
}
func TestParseArgs_QuotedValue(t *testing.T) {
p := ParseArgs([]string{`host=server1`, `command="uptime`, `-a"`})
if p.Named["host"] != "server1" {
t.Errorf("expected host=server1, got %q", p.Named["host"])
}
if p.Named["command"] != "uptime -a" {
t.Errorf("expected command='uptime -a', got %q", p.Named["command"])
}
}
func TestParseArgs_Mixed(t *testing.T) {
p := ParseArgs([]string{"ssh_command", "host=server1", "command=ls"})
if len(p.Positional) != 1 || p.Positional[0] != "ssh_command" {
t.Errorf("expected positional [ssh_command], got %v", p.Positional)
}
if p.Named["host"] != "server1" {
t.Errorf("expected host=server1, got %q", p.Named["host"])
}
}
func TestParseArgs_SingleQuotes(t *testing.T) {
p := ParseArgs([]string{`query='hello`, `world'`})
if p.Named["query"] != "hello world" {
t.Errorf("expected query='hello world', got %q", p.Named["query"])
}
}
func TestArgsToJSON_Empty(t *testing.T) {
result := ArgsToJSON(nil)
if result != "" {
t.Errorf("expected empty string, got %q", result)
}
}
func TestArgsToJSON_Values(t *testing.T) {
result := ArgsToJSON(map[string]string{"host": "server1", "command": "uptime"})
if result == "" {
t.Error("expected non-empty JSON")
}
// Should contain both keys
if !contains(result, `"host"`) || !contains(result, `"server1"`) {
t.Errorf("JSON missing expected keys: %s", result)
}
}
func contains(s, sub string) bool {
return len(s) >= len(sub) && (s == sub || len(s) > 0 && containsStr(s, sub))
}
func containsStr(s, sub string) bool {
for i := 0; i <= len(s)-len(sub); i++ {
if s[i:i+len(sub)] == sub {
return true
}
}
return false
}