package main import ( "context" "strings" "testing" "time" ) func TestFS9P_Basic(t *testing.T) { fs := NewFS9P("/tmp") // Test CWD if fs.CWD() != "/tmp" { t.Errorf("CWD() = %q, want /tmp", fs.CWD()) } fs.SetCWD("/home") if fs.CWD() != "/home" { t.Errorf("CWD() = %q, want /home", fs.CWD()) } // Test Env fs.SetEnv("FOO", "bar") if fs.GetEnv("FOO") != "bar" { t.Errorf("GetEnv(FOO) = %q, want bar", fs.GetEnv("FOO")) } // Test HostInfo info := fs.HostInfo9P() if !strings.Contains(info, "platform=") { t.Errorf("HostInfo9P() missing platform") } } func TestFS9P_ToolsWithoutRegistry(t *testing.T) { fs := NewFS9P("/tmp") // Without registry, ListTools should return nil tools := fs.ListTools() if tools != nil { t.Errorf("ListTools() without registry = %v, want nil", tools) } // Load should fail without registry err := fs.LoadTool("shell") if err == nil { t.Error("LoadTool without registry should fail") } } func TestFS9P_HandleCtl(t *testing.T) { fs := NewFS9P("/tmp") // Unknown command err := fs.HandleCtl("unknown") if err == nil { t.Error("HandleCtl(unknown) should fail") } // load without name err = fs.HandleCtl("load") if err == nil { t.Error("HandleCtl(load) without name should fail") } // unload without name err = fs.HandleCtl("unload") if err == nil { t.Error("HandleCtl(unload) without name should fail") } } func TestFS9P_ProcLimit(t *testing.T) { fs := NewFS9P("/tmp") fs.procLimit = 2 // Create a fake registry and session ID reg, err := NewRegistry() if err != nil { t.Skip("no tools directory available") } fs.SetRegistry(reg, "test-session") // Load a simple tool if available if err := reg.Load("test-session", "shell"); err != nil { t.Skip("shell tool not available") } // Proc limit test would require actual tool execution // For now just verify the limit is set if fs.procLimit != 2 { t.Errorf("procLimit = %d, want 2", fs.procLimit) } } func TestFS9P_ParsePayload(t *testing.T) { tests := []struct { input string want map[string]string }{ { input: "tool=shell\ncmd=ls -la\n", want: map[string]string{"tool": "shell", "cmd": "ls -la"}, }, { input: "key=value with spaces\n", want: map[string]string{"key": "value with spaces"}, }, { input: "multi=line\\none\\ntwo\n", want: map[string]string{"multi": "line\none\ntwo"}, }, { input: "escaped=back\\\\slash\n", want: map[string]string{"escaped": "back\\slash"}, }, } for _, tt := range tests { got := parsePayload(tt.input) for k, v := range tt.want { if got[k] != v { t.Errorf("parsePayload(%q)[%q] = %q, want %q", tt.input, k, got[k], v) } } } } func TestFS9P_ProcLifecycle(t *testing.T) { fs := NewFS9P("/tmp") // No procs initially pids := fs.ListProcs() if len(pids) != 0 { t.Errorf("ListProcs() = %v, want empty", pids) } // GetProc for non-existent if fs.GetProc(999) != nil { t.Error("GetProc(999) should return nil") } // DismissProc for non-existent if fs.DismissProc(999) { t.Error("DismissProc(999) should return false") } // SignalProc for non-existent if err := fs.SignalProc(999, 15); err == nil { t.Error("SignalProc(999) should fail") } } func TestFS9P_HandleProcCtl(t *testing.T) { fs := NewFS9P("/tmp") // Unknown command err := fs.HandleProcCtl(1, "unknown") if err == nil { t.Error("HandleProcCtl(unknown) should fail") } // signal without number err = fs.HandleProcCtl(1, "signal") if err == nil { t.Error("HandleProcCtl(signal) without number should fail") } // signal with invalid number err = fs.HandleProcCtl(1, "signal abc") if err == nil { t.Error("HandleProcCtl(signal abc) should fail") } // dismiss on non-existent (should succeed silently) err = fs.HandleProcCtl(999, "dismiss") if err != nil { t.Errorf("HandleProcCtl(dismiss) on non-existent = %v, want nil", err) } } func TestProc9P_Stat(t *testing.T) { proc := &Proc9P{ PID: 1, Tool: "shell", StartTime: time.Now(), done: make(chan struct{}), } // Running state stat := proc.Stat() if !strings.Contains(stat, "running") { t.Errorf("Stat() for running proc should contain 'running', got %q", stat) } // Mark as exited proc.Exited = true proc.ExitCode = 0 proc.EndTime = time.Now() stat = proc.Stat() if !strings.Contains(stat, "exited 0") { t.Errorf("Stat() for exited proc should contain 'exited 0', got %q", stat) } } func TestFS9P_NewProcWithoutRegistry(t *testing.T) { fs := NewFS9P("/tmp") ctx := context.Background() _, _, err := fs.NewProc(ctx, "tool=shell\ncmd=ls\n", false) if err == nil { t.Error("NewProc without registry should fail") } }