fix: udp handling and ipv4 and ipv6 filtering
This commit is contained in:
303
internal/tui/model_test.go
Normal file
303
internal/tui/model_test.go
Normal file
@@ -0,0 +1,303 @@
|
||||
package tui
|
||||
|
||||
import (
|
||||
"snitch/internal/collector"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/x/exp/teatest"
|
||||
)
|
||||
|
||||
func TestTUI_InitialState(t *testing.T) {
|
||||
m := New(Options{
|
||||
Theme: "dark",
|
||||
Interval: time.Second,
|
||||
})
|
||||
|
||||
if m.showTCP != true {
|
||||
t.Error("expected showTCP to be true by default")
|
||||
}
|
||||
if m.showUDP != true {
|
||||
t.Error("expected showUDP to be true by default")
|
||||
}
|
||||
if m.showListening != true {
|
||||
t.Error("expected showListening to be true by default")
|
||||
}
|
||||
if m.showEstablished != true {
|
||||
t.Error("expected showEstablished to be true by default")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTUI_FilterOptions(t *testing.T) {
|
||||
m := New(Options{
|
||||
Theme: "dark",
|
||||
Interval: time.Second,
|
||||
TCP: true,
|
||||
UDP: false,
|
||||
FilterSet: true,
|
||||
})
|
||||
|
||||
if m.showTCP != true {
|
||||
t.Error("expected showTCP to be true")
|
||||
}
|
||||
if m.showUDP != false {
|
||||
t.Error("expected showUDP to be false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTUI_MatchesFilters(t *testing.T) {
|
||||
m := New(Options{
|
||||
Theme: "dark",
|
||||
Interval: time.Second,
|
||||
TCP: true,
|
||||
UDP: false,
|
||||
Listening: true,
|
||||
Established: false,
|
||||
FilterSet: true,
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
conn collector.Connection
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "tcp listen matches",
|
||||
conn: collector.Connection{Proto: "tcp", State: "LISTEN"},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "tcp6 listen matches",
|
||||
conn: collector.Connection{Proto: "tcp6", State: "LISTEN"},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "udp listen does not match",
|
||||
conn: collector.Connection{Proto: "udp", State: "LISTEN"},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "tcp established does not match",
|
||||
conn: collector.Connection{Proto: "tcp", State: "ESTABLISHED"},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := m.matchesFilters(tc.conn)
|
||||
if result != tc.expected {
|
||||
t.Errorf("matchesFilters() = %v, want %v", result, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTUI_MatchesSearch(t *testing.T) {
|
||||
m := New(Options{Theme: "dark"})
|
||||
m.searchQuery = "firefox"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
conn collector.Connection
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "process name matches",
|
||||
conn: collector.Connection{Process: "firefox"},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "process name case insensitive",
|
||||
conn: collector.Connection{Process: "Firefox"},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "no match",
|
||||
conn: collector.Connection{Process: "chrome"},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "matches in address",
|
||||
conn: collector.Connection{Raddr: "firefox.com"},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := m.matchesSearch(tc.conn)
|
||||
if result != tc.expected {
|
||||
t.Errorf("matchesSearch() = %v, want %v", result, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTUI_KeyBindings(t *testing.T) {
|
||||
tm := teatest.NewTestModel(t, New(Options{Theme: "dark", Interval: time.Hour}))
|
||||
|
||||
// test quit with 'q'
|
||||
tm.Send(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'q'}})
|
||||
|
||||
tm.WaitFinished(t, teatest.WithFinalTimeout(time.Second*3))
|
||||
}
|
||||
|
||||
func TestTUI_ToggleFilters(t *testing.T) {
|
||||
m := New(Options{Theme: "dark", Interval: time.Hour})
|
||||
|
||||
// initial state: all filters on
|
||||
if m.showTCP != true || m.showUDP != true {
|
||||
t.Fatal("expected all protocol filters on initially")
|
||||
}
|
||||
|
||||
// toggle TCP with 't'
|
||||
newModel, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'t'}})
|
||||
m = newModel.(model)
|
||||
|
||||
if m.showTCP != false {
|
||||
t.Error("expected showTCP to be false after toggle")
|
||||
}
|
||||
|
||||
// toggle UDP with 'u'
|
||||
newModel, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'u'}})
|
||||
m = newModel.(model)
|
||||
|
||||
if m.showUDP != false {
|
||||
t.Error("expected showUDP to be false after toggle")
|
||||
}
|
||||
|
||||
// toggle listening with 'l'
|
||||
newModel, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'l'}})
|
||||
m = newModel.(model)
|
||||
|
||||
if m.showListening != false {
|
||||
t.Error("expected showListening to be false after toggle")
|
||||
}
|
||||
|
||||
// toggle established with 'e'
|
||||
newModel, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'e'}})
|
||||
m = newModel.(model)
|
||||
|
||||
if m.showEstablished != false {
|
||||
t.Error("expected showEstablished to be false after toggle")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTUI_HelpToggle(t *testing.T) {
|
||||
m := New(Options{Theme: "dark", Interval: time.Hour})
|
||||
|
||||
if m.showHelp != false {
|
||||
t.Fatal("expected showHelp to be false initially")
|
||||
}
|
||||
|
||||
// toggle help with '?'
|
||||
newModel, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'?'}})
|
||||
m = newModel.(model)
|
||||
|
||||
if m.showHelp != true {
|
||||
t.Error("expected showHelp to be true after toggle")
|
||||
}
|
||||
|
||||
// toggle help off
|
||||
newModel, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'?'}})
|
||||
m = newModel.(model)
|
||||
|
||||
if m.showHelp != false {
|
||||
t.Error("expected showHelp to be false after second toggle")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTUI_CursorNavigation(t *testing.T) {
|
||||
m := New(Options{Theme: "dark", Interval: time.Hour})
|
||||
|
||||
// add some test data
|
||||
m.connections = []collector.Connection{
|
||||
{PID: 1, Process: "proc1", Proto: "tcp", State: "LISTEN"},
|
||||
{PID: 2, Process: "proc2", Proto: "tcp", State: "LISTEN"},
|
||||
{PID: 3, Process: "proc3", Proto: "tcp", State: "LISTEN"},
|
||||
}
|
||||
|
||||
if m.cursor != 0 {
|
||||
t.Fatal("expected cursor at 0 initially")
|
||||
}
|
||||
|
||||
// move down with 'j'
|
||||
newModel, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'j'}})
|
||||
m = newModel.(model)
|
||||
|
||||
if m.cursor != 1 {
|
||||
t.Errorf("expected cursor at 1 after down, got %d", m.cursor)
|
||||
}
|
||||
|
||||
// move down again
|
||||
newModel, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'j'}})
|
||||
m = newModel.(model)
|
||||
|
||||
if m.cursor != 2 {
|
||||
t.Errorf("expected cursor at 2 after second down, got %d", m.cursor)
|
||||
}
|
||||
|
||||
// move up with 'k'
|
||||
newModel, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'k'}})
|
||||
m = newModel.(model)
|
||||
|
||||
if m.cursor != 1 {
|
||||
t.Errorf("expected cursor at 1 after up, got %d", m.cursor)
|
||||
}
|
||||
|
||||
// go to top with 'g'
|
||||
newModel, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'g'}})
|
||||
m = newModel.(model)
|
||||
|
||||
if m.cursor != 0 {
|
||||
t.Errorf("expected cursor at 0 after 'g', got %d", m.cursor)
|
||||
}
|
||||
|
||||
// go to bottom with 'G'
|
||||
newModel, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'G'}})
|
||||
m = newModel.(model)
|
||||
|
||||
if m.cursor != 2 {
|
||||
t.Errorf("expected cursor at 2 after 'G', got %d", m.cursor)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTUI_WindowResize(t *testing.T) {
|
||||
m := New(Options{Theme: "dark", Interval: time.Hour})
|
||||
|
||||
newModel, _ := m.Update(tea.WindowSizeMsg{Width: 120, Height: 40})
|
||||
m = newModel.(model)
|
||||
|
||||
if m.width != 120 {
|
||||
t.Errorf("expected width 120, got %d", m.width)
|
||||
}
|
||||
if m.height != 40 {
|
||||
t.Errorf("expected height 40, got %d", m.height)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTUI_ViewRenders(t *testing.T) {
|
||||
m := New(Options{Theme: "dark", Interval: time.Hour})
|
||||
m.width = 120
|
||||
m.height = 40
|
||||
|
||||
m.connections = []collector.Connection{
|
||||
{PID: 1234, Process: "nginx", Proto: "tcp", State: "LISTEN", Laddr: "0.0.0.0", Lport: 80},
|
||||
}
|
||||
|
||||
// main view should render without panic
|
||||
view := m.View()
|
||||
if view == "" {
|
||||
t.Error("expected non-empty view")
|
||||
}
|
||||
|
||||
// help view
|
||||
m.showHelp = true
|
||||
helpView := m.View()
|
||||
if helpView == "" {
|
||||
t.Error("expected non-empty help view")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user