261 lines
6.2 KiB
Go
261 lines
6.2 KiB
Go
package handlers
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"overlord-client/cmd/agent/config"
|
||
|
|
rt "overlord-client/cmd/agent/runtime"
|
||
|
|
"overlord-client/cmd/agent/wire"
|
||
|
|
|
||
|
|
"github.com/vmihailenco/msgpack/v5"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestHandleProcessList(t *testing.T) {
|
||
|
|
writer := &testWriter{}
|
||
|
|
env := &rt.Env{
|
||
|
|
Conn: writer,
|
||
|
|
Cfg: config.Config{},
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx := context.Background()
|
||
|
|
cmdID := "test-cmd-proc-1"
|
||
|
|
|
||
|
|
if err := HandleProcessList(ctx, env, cmdID); err != nil {
|
||
|
|
t.Fatalf("HandleProcessList failed: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(writer.msgs) != 1 {
|
||
|
|
t.Fatalf("Expected 1 message, got %d", len(writer.msgs))
|
||
|
|
}
|
||
|
|
|
||
|
|
var result wire.ProcessListResult
|
||
|
|
if err := msgpack.Unmarshal(writer.msgs[0], &result); err != nil {
|
||
|
|
t.Fatalf("Failed to unmarshal result: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if result.Type != "process_list_result" {
|
||
|
|
t.Errorf("Expected type 'process_list_result', got '%s'", result.Type)
|
||
|
|
}
|
||
|
|
if result.CommandID != cmdID {
|
||
|
|
t.Errorf("Expected CommandID '%s', got '%s'", cmdID, result.CommandID)
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(result.Processes) == 0 {
|
||
|
|
t.Log("WARNING: No processes returned (may indicate platform issue)")
|
||
|
|
} else {
|
||
|
|
t.Logf("Found %d processes", len(result.Processes))
|
||
|
|
|
||
|
|
for i, proc := range result.Processes {
|
||
|
|
if proc.PID <= 0 {
|
||
|
|
t.Errorf("Process %d has invalid PID: %d", i, proc.PID)
|
||
|
|
}
|
||
|
|
if proc.Name == "" {
|
||
|
|
t.Errorf("Process %d has empty name", i)
|
||
|
|
}
|
||
|
|
if proc.Memory < 0 {
|
||
|
|
t.Errorf("Process %d has negative memory: %d", i, proc.Memory)
|
||
|
|
}
|
||
|
|
if proc.CPU < 0 {
|
||
|
|
t.Errorf("Process %d has negative CPU: %f", i, proc.CPU)
|
||
|
|
}
|
||
|
|
|
||
|
|
validTypes := map[string]bool{
|
||
|
|
"system": true,
|
||
|
|
"service": true,
|
||
|
|
"own": true,
|
||
|
|
"other": true,
|
||
|
|
"": true,
|
||
|
|
}
|
||
|
|
if !validTypes[proc.Type] {
|
||
|
|
t.Errorf("Process %d has invalid type: %s", i, proc.Type)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pidExists := make(map[int32]bool)
|
||
|
|
for _, proc := range result.Processes {
|
||
|
|
pidExists[proc.PID] = true
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, proc := range result.Processes {
|
||
|
|
if proc.PPID != 0 && !pidExists[proc.PPID] {
|
||
|
|
t.Logf("INFO: Process %d (%s) has PPID %d which is not in the list (may have exited)",
|
||
|
|
proc.PID, proc.Name, proc.PPID)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHandleProcessList_ProcessTypes(t *testing.T) {
|
||
|
|
writer := &testWriter{}
|
||
|
|
env := &rt.Env{
|
||
|
|
Conn: writer,
|
||
|
|
Cfg: config.Config{},
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx := context.Background()
|
||
|
|
cmdID := "test-cmd-proc-2"
|
||
|
|
|
||
|
|
if err := HandleProcessList(ctx, env, cmdID); err != nil {
|
||
|
|
t.Fatalf("HandleProcessList failed: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(writer.msgs) != 1 {
|
||
|
|
t.Fatalf("Expected 1 message, got %d", len(writer.msgs))
|
||
|
|
}
|
||
|
|
|
||
|
|
var result wire.ProcessListResult
|
||
|
|
if err := msgpack.Unmarshal(writer.msgs[0], &result); err != nil {
|
||
|
|
t.Fatalf("Failed to unmarshal result: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
typeCounts := make(map[string]int)
|
||
|
|
for _, proc := range result.Processes {
|
||
|
|
typeCounts[proc.Type]++
|
||
|
|
}
|
||
|
|
|
||
|
|
t.Logf("Process type distribution:")
|
||
|
|
for typ, count := range typeCounts {
|
||
|
|
label := typ
|
||
|
|
if label == "" {
|
||
|
|
label = "uncategorized"
|
||
|
|
}
|
||
|
|
t.Logf(" %s: %d", label, count)
|
||
|
|
}
|
||
|
|
|
||
|
|
if typeCounts["system"] == 0 {
|
||
|
|
t.Log("WARNING: No system processes detected")
|
||
|
|
}
|
||
|
|
|
||
|
|
examples := make(map[string][]string)
|
||
|
|
for _, proc := range result.Processes {
|
||
|
|
if len(examples[proc.Type]) < 3 {
|
||
|
|
examples[proc.Type] = append(examples[proc.Type], proc.Name)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
t.Log("Example processes by type:")
|
||
|
|
for typ, procs := range examples {
|
||
|
|
label := typ
|
||
|
|
if label == "" {
|
||
|
|
label = "uncategorized"
|
||
|
|
}
|
||
|
|
t.Logf(" %s: %v", label, procs)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestProcessInfoFields(t *testing.T) {
|
||
|
|
|
||
|
|
proc := wire.ProcessInfo{
|
||
|
|
PID: 1234,
|
||
|
|
PPID: 1,
|
||
|
|
Name: "test.exe",
|
||
|
|
CPU: 25.5,
|
||
|
|
Memory: 1048576,
|
||
|
|
Username: "testuser",
|
||
|
|
Type: "own",
|
||
|
|
}
|
||
|
|
|
||
|
|
data, err := msgpack.Marshal(proc)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("Failed to marshal ProcessInfo: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
var decoded wire.ProcessInfo
|
||
|
|
if err := msgpack.Unmarshal(data, &decoded); err != nil {
|
||
|
|
t.Fatalf("Failed to unmarshal ProcessInfo: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if decoded.PID != proc.PID {
|
||
|
|
t.Errorf("PID mismatch: got %d, want %d", decoded.PID, proc.PID)
|
||
|
|
}
|
||
|
|
if decoded.PPID != proc.PPID {
|
||
|
|
t.Errorf("PPID mismatch: got %d, want %d", decoded.PPID, proc.PPID)
|
||
|
|
}
|
||
|
|
if decoded.Name != proc.Name {
|
||
|
|
t.Errorf("Name mismatch: got %s, want %s", decoded.Name, proc.Name)
|
||
|
|
}
|
||
|
|
if decoded.CPU != proc.CPU {
|
||
|
|
t.Errorf("CPU mismatch: got %f, want %f", decoded.CPU, proc.CPU)
|
||
|
|
}
|
||
|
|
if decoded.Memory != proc.Memory {
|
||
|
|
t.Errorf("Memory mismatch: got %d, want %d", decoded.Memory, proc.Memory)
|
||
|
|
}
|
||
|
|
if decoded.Username != proc.Username {
|
||
|
|
t.Errorf("Username mismatch: got %s, want %s", decoded.Username, proc.Username)
|
||
|
|
}
|
||
|
|
if decoded.Type != proc.Type {
|
||
|
|
t.Errorf("Type mismatch: got %s, want %s", decoded.Type, proc.Type)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestListProcesses_PlatformSpecific(t *testing.T) {
|
||
|
|
|
||
|
|
processes, err := listProcesses()
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("listProcesses failed: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(processes) == 0 {
|
||
|
|
t.Log("WARNING: No processes returned from listProcesses()")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
t.Logf("Retrieved %d processes from system", len(processes))
|
||
|
|
|
||
|
|
for i, proc := range processes {
|
||
|
|
if i < 10 {
|
||
|
|
t.Logf("Process: PID=%d PPID=%d Name=%s CPU=%.2f%% Mem=%d User=%s Type=%s",
|
||
|
|
proc.PID, proc.PPID, proc.Name, proc.CPU, proc.Memory, proc.Username, proc.Type)
|
||
|
|
}
|
||
|
|
|
||
|
|
if proc.PID <= 0 {
|
||
|
|
t.Errorf("Process %d has invalid PID: %d", i, proc.PID)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestProcessTypeClassification(t *testing.T) {
|
||
|
|
|
||
|
|
processes, err := listProcesses()
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("listProcesses failed: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(processes) == 0 {
|
||
|
|
t.Skip("No processes available for testing")
|
||
|
|
}
|
||
|
|
|
||
|
|
var foundSystem, foundService, foundOwn bool
|
||
|
|
for _, proc := range processes {
|
||
|
|
switch proc.Type {
|
||
|
|
case "system":
|
||
|
|
foundSystem = true
|
||
|
|
t.Logf("System process example: PID=%d Name=%s User=%s",
|
||
|
|
proc.PID, proc.Name, proc.Username)
|
||
|
|
case "service":
|
||
|
|
foundService = true
|
||
|
|
t.Logf("Service process example: PID=%d Name=%s User=%s",
|
||
|
|
proc.PID, proc.Name, proc.Username)
|
||
|
|
case "own":
|
||
|
|
foundOwn = true
|
||
|
|
t.Logf("Own process example: PID=%d Name=%s User=%s",
|
||
|
|
proc.PID, proc.Name, proc.Username)
|
||
|
|
}
|
||
|
|
|
||
|
|
if foundSystem && foundService && foundOwn {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if !foundSystem {
|
||
|
|
t.Log("INFO: No system processes detected in classification")
|
||
|
|
}
|
||
|
|
if !foundService {
|
||
|
|
t.Log("INFO: No service processes detected in classification")
|
||
|
|
}
|
||
|
|
if !foundOwn {
|
||
|
|
t.Log("INFO: No own processes detected in classification")
|
||
|
|
}
|
||
|
|
}
|