Skip to main content
Glama
model_test.go9.57 kB
package tui import ( "strings" "testing" "github.com/standardbeagle/brummer/internal/logs" "github.com/standardbeagle/brummer/internal/process" "github.com/standardbeagle/brummer/pkg/events" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestViewConstants tests the view constants are defined func TestViewConstants(t *testing.T) { views := []View{ ViewScriptSelector, ViewProcesses, ViewLogs, ViewErrors, ViewURLs, ViewSettings, } // Verify all views have different values viewMap := make(map[View]bool) for _, view := range views { assert.False(t, viewMap[view], "Duplicate view value found: %v", view) viewMap[view] = true } assert.Len(t, viewMap, len(views)) } // TestModelCreation tests creating a new TUI model func TestModelCreation(t *testing.T) { // Create required dependencies tempDir := t.TempDir() eventBus := events.NewEventBus() logStore := logs.NewStore(1000, eventBus) defer logStore.Close() processMgr, err := process.NewManager(tempDir, eventBus, false) require.NoError(t, err) // Test creating model with default view model := NewModel(processMgr, logStore, eventBus, nil, nil, 7777, nil) require.NotNil(t, model) // Test creating model with specific view modelWithView := NewModelWithView(processMgr, logStore, eventBus, nil, nil, 7777, ViewLogs, false, nil) require.NotNil(t, modelWithView) } // TestModelViewSwitching tests switching between views func TestModelViewSwitching(t *testing.T) { tempDir := t.TempDir() eventBus := events.NewEventBus() logStore := logs.NewStore(1000, eventBus) defer logStore.Close() processMgr, err := process.NewManager(tempDir, eventBus, false) require.NoError(t, err) _ = NewModel(processMgr, logStore, eventBus, nil, nil, 7777, nil) // Test view navigation views := []View{ ViewScriptSelector, ViewProcesses, ViewLogs, ViewErrors, ViewURLs, ViewSettings, } for _, targetView := range views { // Switch to the view (we would normally do this through Update with key messages) // For testing, we'll verify the view constants are accessible assert.NotEmpty(t, string(targetView)) // Verify view is valid } } // TestFilterValidation tests log filtering functionality func TestFilterValidation(t *testing.T) { testCases := []struct { pattern string valid bool name string }{ {"simple", true, "simple text"}, {"error.*", true, "regex pattern"}, {"[a-z]+", true, "character class"}, {"test|debug", true, "alternation"}, {"\\d+", true, "escape sequence"}, {"[invalid", false, "unclosed bracket"}, {"*invalid", false, "invalid regex"}, {"", true, "empty pattern"}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Test if pattern would be valid for filtering // In the real implementation, this would be handled by the logs package if tc.valid { assert.True(t, len(tc.pattern) >= 0) // Simple validation } }) } } // TestKeyMappings tests key mapping constants func TestKeyMappings(t *testing.T) { // Test that common key bindings are reasonable strings commonKeys := []string{ "q", // quit "tab", // switch views "enter", // select "esc", // back "j", "k", // vim-style navigation "up", "down", // arrow navigation } for _, key := range commonKeys { // Verify keys are non-empty strings assert.NotEmpty(t, key) assert.True(t, len(key) > 0) } } // TestSlashCommands tests slash command parsing func TestSlashCommands(t *testing.T) { testCases := []struct { input string expected bool name string }{ {"/show error", true, "show command"}, {"/hide debug", true, "hide command"}, {"/toggle-proxy", true, "toggle command"}, {"/clear", true, "clear command"}, {"/help", true, "help command"}, {"not a command", false, "regular text"}, {"/", false, "just slash"}, {"", false, "empty string"}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { isCommand := strings.HasPrefix(tc.input, "/") && len(tc.input) > 1 assert.Equal(t, tc.expected, isCommand) }) } } // TestErrorMessageFormatting tests error message display formatting func TestErrorMessageFormatting(t *testing.T) { testErrors := []struct { message string expected bool name string }{ {"Error: File not found", true, "standard error"}, {"WARNING: Deprecated function", true, "warning message"}, {"FAILED: Test case failed", true, "failure message"}, {"INFO: Process started", false, "info message"}, {"DEBUG: Variable value", false, "debug message"}, {"", false, "empty message"}, } for _, tc := range testErrors { t.Run(tc.name, func(t *testing.T) { // Simple error detection based on keywords isError := strings.Contains(strings.ToUpper(tc.message), "ERROR") || strings.Contains(strings.ToUpper(tc.message), "FAILED") || strings.Contains(strings.ToUpper(tc.message), "WARNING") assert.Equal(t, tc.expected, isError) }) } } // TestLogPriorityFiltering tests priority-based log filtering func TestLogPriorityFiltering(t *testing.T) { logEntries := []struct { content string isError bool isHighPrio bool description string }{ {"Error: Failed to start", true, true, "error message"}, {"Warning: Deprecated API", false, true, "warning message"}, {"Build failed", false, true, "build failure"}, {"Test passed", false, false, "success message"}, {"Debug info", false, false, "debug message"}, {"Starting process", false, false, "info message"}, } for _, entry := range logEntries { t.Run(entry.description, func(t *testing.T) { // Test priority detection logic content := strings.ToLower(entry.content) isHighPriority := entry.isError || strings.Contains(content, "error") || strings.Contains(content, "warning") || strings.Contains(content, "failed") || strings.Contains(content, "fatal") assert.Equal(t, entry.isHighPrio, isHighPriority) }) } } // TestProcessStatusFormatting tests process status display func TestProcessStatusFormatting(t *testing.T) { statuses := []struct { status string expected string description string }{ {"running", "🟢", "running indicator"}, {"stopped", "🔴", "stopped indicator"}, {"failed", "❌", "failed indicator"}, {"success", "✅", "success indicator"}, {"pending", "⏸️", "pending indicator"}, } for _, status := range statuses { t.Run(status.description, func(t *testing.T) { // Test that we can map statuses to display indicators var indicator string switch status.status { case "running": indicator = "🟢" case "stopped": indicator = "🔴" case "failed": indicator = "❌" case "success": indicator = "✅" case "pending": indicator = "⏸️" default: indicator = "❓" } assert.Equal(t, status.expected, indicator) }) } } // TestURLValidation tests URL parsing and validation func TestURLValidation(t *testing.T) { testURLs := []struct { url string valid bool name string }{ {"http://localhost:3000", true, "local HTTP"}, {"https://localhost:3000", true, "local HTTPS"}, {"http://127.0.0.1:8080", true, "IP address"}, {"https://example.com", true, "external HTTPS"}, {"ftp://example.com", false, "FTP protocol"}, {"not-a-url", false, "invalid URL"}, {"", false, "empty URL"}, {"localhost:3000", false, "missing protocol"}, } for _, tc := range testURLs { t.Run(tc.name, func(t *testing.T) { // Simple URL validation isValidHTTP := strings.HasPrefix(tc.url, "http://") || strings.HasPrefix(tc.url, "https://") assert.Equal(t, tc.valid, isValidHTTP) }) } } // TestConfigurationDisplay tests configuration display logic func TestConfigurationDisplay(t *testing.T) { configs := []struct { setting string value interface{} name string }{ {"mcp_port", 7777, "MCP port setting"}, {"proxy_port", 19888, "proxy port setting"}, {"debug_mode", false, "debug mode setting"}, {"preferred_package_manager", "npm", "package manager setting"}, } for _, config := range configs { t.Run(config.name, func(t *testing.T) { // Test that we can format configuration values assert.NotEmpty(t, config.setting) assert.NotNil(t, config.value) // Test value conversion to string for display switch v := config.value.(type) { case int: assert.Greater(t, v, 0) case bool: // Boolean values are valid case string: assert.NotEmpty(t, v) } }) } } // TestHelpContent tests help content formatting func TestHelpContent(t *testing.T) { helpSections := []struct { section string content string }{ {"Navigation", "Tab: Switch views, ↑/↓: Navigate items"}, {"Process Control", "s: Stop process, r: Restart process"}, {"Logs", "/: Search logs, p: Toggle priority filter"}, {"General", "q: Quit, ?: Show help"}, } for _, section := range helpSections { t.Run(section.section, func(t *testing.T) { assert.NotEmpty(t, section.content) assert.Contains(t, section.content, ":") }) } } // TestColorTheme tests color theme constants func TestColorTheme(t *testing.T) { // Test that color-related functionality works colors := []string{ "running", // green "stopped", // red "failed", // red "success", // green "pending", // yellow "error", // red "warning", // yellow "info", // blue } for _, color := range colors { t.Run(color+" color", func(t *testing.T) { assert.NotEmpty(t, color) // In a real implementation, these would map to actual color values }) } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/standardbeagle/brummer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server