Files
wehub-resource-sync 36b3af2e3d
PR Check / Code Quality: Format (push) Failing after 1s
PR Check / Code Quality: Lint (darwin) (push) Failing after 0s
PR Check / Code Quality: Lint (freebsd) (push) Failing after 1s
PR Check / Code Quality: Lint (windows) (push) Failing after 1s
PR Check / Code Quality: Lint (linux) (push) Failing after 1s
PR Check / Security: Vulnerability Scan (push) Failing after 0s
Update Documentation / update-docs (push) Failing after 2s
PR Check / Code Quality: Vendor (push) Failing after 1s
PR Check / Code Quality: Coverage (push) Failing after 0s
PR Check / Tests: Unit (macos-latest) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04-arm) (push) Has been cancelled
PR Check / Tests: Unit (windows-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:22:06 +08:00

175 lines
4.9 KiB
Go

package tui
import (
"strings"
"testing"
"time"
"github.com/pranshuparmar/witr/pkg/model"
)
// pids extracts the PID order from a process slice for concise assertions.
func pids(ps []model.Process) []int {
out := make([]int, len(ps))
for i, p := range ps {
out[i] = p.PID
}
return out
}
func TestFilterProcesses(t *testing.T) {
m := InitialModel("test")
m.processes = []model.Process{
{PID: 1, Command: "nginx", User: "root"},
{PID: 2, Command: "redis", User: "redis"},
{PID: 3, Command: "nginx-worker", User: "www"},
}
// Empty filter keeps everything and mirrors into the table rows.
m.input.SetValue("")
m.filterProcesses()
if len(m.filtered) != 3 {
t.Fatalf("empty filter: filtered = %d, want 3", len(m.filtered))
}
if got := len(m.table.Rows()); got != 3 {
t.Errorf("empty filter: table rows = %d, want 3 (filtered must drive the table)", got)
}
// Substring filter narrows to matching commands, preserving order.
m.input.SetValue("nginx")
m.filterProcesses()
if got := pids(m.filtered); !equalInts(got, []int{1, 3}) {
t.Errorf("nginx filter: %v, want [1 3]", got)
}
// A filter that matches nothing clears the list.
m.input.SetValue("zzz")
m.filterProcesses()
if len(m.filtered) != 0 {
t.Errorf("no-match filter: filtered = %d, want 0", len(m.filtered))
}
}
func TestSortProcessesModel(t *testing.T) {
base := []model.Process{
{PID: 30, Command: "Bravo", User: "u", CPUPercent: 1, MemoryRSS: 100, StartedAt: time.Unix(300, 0)},
{PID: 10, Command: "alpha", User: "u", CPUPercent: 3, MemoryRSS: 300, StartedAt: time.Unix(100, 0)},
{PID: 20, Command: "charlie", User: "u", CPUPercent: 2, MemoryRSS: 200, StartedAt: time.Unix(200, 0)},
}
load := func() MainModel {
m := InitialModel("test")
m.processes = append([]model.Process(nil), base...)
return m
}
tests := []struct {
name string
col string
desc bool
want []int
}{
{"pid asc", "pid", false, []int{10, 20, 30}},
{"mem desc", "mem", true, []int{10, 20, 30}}, // RSS 300,200,100
{"cpu asc", "cpu", false, []int{30, 20, 10}}, // 1,2,3
{"name asc (case-insensitive)", "name", false, []int{10, 30, 20}}, // alpha,bravo,charlie
{"time asc", "time", false, []int{10, 20, 30}},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
m := load()
m.sortCol, m.sortDesc = tt.col, tt.desc
m.sortProcesses()
if got := pids(m.processes); !equalInts(got, tt.want) {
t.Errorf("sort %s = %v, want %v", tt.name, got, tt.want)
}
})
}
}
func TestSortPortsModel(t *testing.T) {
m := InitialModel("test")
m.ports = []model.OpenPort{
{Port: 443, Protocol: "tcp"},
{Port: 80, Protocol: "udp"},
{Port: 22, Protocol: "tcp"},
}
m.sortPortCol, m.sortPortDesc = "port", false
m.sortPorts()
if got := []int{m.ports[0].Port, m.ports[1].Port, m.ports[2].Port}; !equalInts(got, []int{22, 80, 443}) {
t.Errorf("port asc = %v, want [22 80 443]", got)
}
m.sortPortDesc = true
m.sortPorts()
if m.ports[0].Port != 443 {
t.Errorf("port desc head = %d, want 443", m.ports[0].Port)
}
}
func TestSortContainersModel(t *testing.T) {
m := InitialModel("test")
m.containers = []*model.ContainerMatch{{Name: "web"}, {Name: "Api"}, {Name: "db"}}
m.sortContainerCol, m.sortContainerDesc = "name", false
m.sortContainers()
want := []string{"Api", "db", "web"} // case-insensitive
for i, w := range want {
if m.containers[i].Name != w {
t.Errorf("container[%d] = %q, want %q", i, m.containers[i].Name, w)
}
}
}
func TestSortLocksModel(t *testing.T) {
m := InitialModel("test")
m.locks = []*model.LockedFile{{PID: 30}, {PID: 10}, {PID: 20}}
m.sortLockCol, m.sortLockDesc = "pid", false
m.sortLocks()
for i, w := range []int{10, 20, 30} {
if m.locks[i].PID != w {
t.Errorf("lock[%d].PID = %d, want %d", i, m.locks[i].PID, w)
}
}
}
func TestGetColumns(t *testing.T) {
m := InitialModel("test")
m.sortCol, m.sortDesc = "pid", true
cols := m.getColumns()
if len(cols) != 6 {
t.Fatalf("default cols = %d, want 6 (Command hidden)", len(cols))
}
if !strings.Contains(cols[0].Title, "↓") {
t.Errorf("PID header should carry a desc arrow, got %q", cols[0].Title)
}
m.showCmdCol = true
cols = m.getColumns()
if len(cols) != 7 || cols[6].Title != "Command" {
t.Errorf("showCmdCol cols = %d (last %q), want 7 ending in Command", len(cols), cols[len(cols)-1].Title)
}
}
func TestGetSecondaryColumnsArrows(t *testing.T) {
m := InitialModel("test")
m.sortPortCol, m.sortPortDesc = "state", true
if got := m.getPortColumns()[3].Title; !strings.Contains(got, "↓") {
t.Errorf("port State header = %q, want desc arrow", got)
}
m.sortContainerCol, m.sortContainerDesc = "image", false
if got := m.getContainerColumns()[3].Title; !strings.Contains(got, "↑") {
t.Errorf("container Image header = %q, want asc arrow", got)
}
m.sortLockCol, m.sortLockDesc = "path", false
if got := m.getLockColumns()[4].Title; !strings.Contains(got, "↑") {
t.Errorf("lock Path header = %q, want asc arrow", got)
}
}