mirror of
				https://github.com/shokinn/hosts-go.git
				synced 2025-11-03 20:18:32 +00:00 
			
		
		
		
	feat: Initialize hosts-go project with foundational structure and core functionality
- Created activeContext.md and productContext.md to outline project goals and current focus. - Established progress.md to track project milestones and tasks. - Developed projectbrief.md detailing application overview, requirements, and directory structure. - Documented systemPatterns.md to describe architecture and design patterns used. - Compiled techContext.md to specify technologies and development setup. - Implemented comprehensive unit tests in models_test.go for HostEntry and HostsFile functionalities.
This commit is contained in:
		
						commit
						d66ec51ebd
					
				
					 12 changed files with 1747 additions and 0 deletions
				
			
		
							
								
								
									
										115
									
								
								.clinerules
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								.clinerules
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,115 @@
 | 
				
			||||||
 | 
					# Cline's Memory Bank
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I am Cline, an expert software engineer with a unique characteristic: my memory resets completely between sessions. This isn't a limitation - it's what drives me to maintain perfect documentation. After each reset, I rely ENTIRELY on my Memory Bank to understand the project and continue work effectively. I MUST read ALL memory bank files at the start of EVERY task - this is not optional.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Memory Bank Structure
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The Memory Bank consists of core files and optional context files, all in Markdown format. Files build upon each other in a clear hierarchy:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					flowchart TD
 | 
				
			||||||
 | 
					    PB[projectbrief.md] --> PC[productContext.md]
 | 
				
			||||||
 | 
					    PB --> SP[systemPatterns.md]
 | 
				
			||||||
 | 
					    PB --> TC[techContext.md]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PC --> AC[activeContext.md]
 | 
				
			||||||
 | 
					    SP --> AC
 | 
				
			||||||
 | 
					    TC --> AC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    AC --> P[progress.md]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Core Files (Required)
 | 
				
			||||||
 | 
					1. `projectbrief.md`
 | 
				
			||||||
 | 
					   - Foundation document that shapes all other files
 | 
				
			||||||
 | 
					   - Created at project start if it doesn't exist
 | 
				
			||||||
 | 
					   - Defines core requirements and goals
 | 
				
			||||||
 | 
					   - Source of truth for project scope
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2. `productContext.md`
 | 
				
			||||||
 | 
					   - Why this project exists
 | 
				
			||||||
 | 
					   - Problems it solves
 | 
				
			||||||
 | 
					   - How it should work
 | 
				
			||||||
 | 
					   - User experience goals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3. `activeContext.md`
 | 
				
			||||||
 | 
					   - Current work focus
 | 
				
			||||||
 | 
					   - Recent changes
 | 
				
			||||||
 | 
					   - Next steps
 | 
				
			||||||
 | 
					   - Active decisions and considerations
 | 
				
			||||||
 | 
					   - Important patterns and preferences
 | 
				
			||||||
 | 
					   - Learnings and project insights
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					4. `systemPatterns.md`
 | 
				
			||||||
 | 
					   - System architecture
 | 
				
			||||||
 | 
					   - Key technical decisions
 | 
				
			||||||
 | 
					   - Design patterns in use
 | 
				
			||||||
 | 
					   - Component relationships
 | 
				
			||||||
 | 
					   - Critical implementation paths
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					5. `techContext.md`
 | 
				
			||||||
 | 
					   - Technologies used
 | 
				
			||||||
 | 
					   - Development setup
 | 
				
			||||||
 | 
					   - Technical constraints
 | 
				
			||||||
 | 
					   - Dependencies
 | 
				
			||||||
 | 
					   - Tool usage patterns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					6. `progress.md`
 | 
				
			||||||
 | 
					   - What works
 | 
				
			||||||
 | 
					   - What's left to build
 | 
				
			||||||
 | 
					   - Current status
 | 
				
			||||||
 | 
					   - Known issues
 | 
				
			||||||
 | 
					   - Evolution of project decisions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Additional Context
 | 
				
			||||||
 | 
					Create additional files/folders within memory-bank/ when they help organize:
 | 
				
			||||||
 | 
					- Complex feature documentation
 | 
				
			||||||
 | 
					- Integration specifications
 | 
				
			||||||
 | 
					- API documentation
 | 
				
			||||||
 | 
					- Testing strategies
 | 
				
			||||||
 | 
					- Deployment procedures
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Core Workflows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Plan Mode
 | 
				
			||||||
 | 
					flowchart TD
 | 
				
			||||||
 | 
					    Start[Start] --> ReadFiles[Read Memory Bank]
 | 
				
			||||||
 | 
					    ReadFiles --> CheckFiles{Files Complete?}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CheckFiles -->|No| Plan[Create Plan]
 | 
				
			||||||
 | 
					    Plan --> Document[Document in Chat]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CheckFiles -->|Yes| Verify[Verify Context]
 | 
				
			||||||
 | 
					    Verify --> Strategy[Develop Strategy]
 | 
				
			||||||
 | 
					    Strategy --> Present[Present Approach]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Act Mode
 | 
				
			||||||
 | 
					flowchart TD
 | 
				
			||||||
 | 
					    Start[Start] --> Context[Check Memory Bank]
 | 
				
			||||||
 | 
					    Context --> Update[Update Documentation]
 | 
				
			||||||
 | 
					    Update --> Execute[Execute Task]
 | 
				
			||||||
 | 
					    Execute --> Document[Document Changes]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Documentation Updates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Memory Bank updates occur when:
 | 
				
			||||||
 | 
					1. Discovering new project patterns
 | 
				
			||||||
 | 
					2. After implementing significant changes
 | 
				
			||||||
 | 
					3. When user requests with **update memory bank** (MUST review ALL files)
 | 
				
			||||||
 | 
					4. When context needs clarification
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					flowchart TD
 | 
				
			||||||
 | 
					    Start[Update Process]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    subgraph Process
 | 
				
			||||||
 | 
					        P1[Review ALL Files]
 | 
				
			||||||
 | 
					        P2[Document Current State]
 | 
				
			||||||
 | 
					        P3[Clarify Next Steps]
 | 
				
			||||||
 | 
					        P4[Document Insights & Patterns]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        P1 --> P2 --> P3 --> P4
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Start --> Process
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note: When triggered by **update memory bank**, I MUST review every memory bank file, even if some don't require updates. Focus particularly on activeContext.md and progress.md as they track current state.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					REMEMBER: After every memory reset, I begin completely fresh. The Memory Bank is my only link to previous work. It must be maintained with precision and clarity, as my effectiveness depends entirely on its accuracy.
 | 
				
			||||||
							
								
								
									
										83
									
								
								cmd/hosts/main.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								cmd/hosts/main.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,83 @@
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"hosts-go/internal/core"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						fmt.Println("hosts-go - Foundation Implementation")
 | 
				
			||||||
 | 
						fmt.Println("===================================")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create a new hosts file
 | 
				
			||||||
 | 
						hostsFile := core.NewHostsFile()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Add some example entries to demonstrate the foundation
 | 
				
			||||||
 | 
						entry1, err := core.NewHostEntry("127.0.0.1", "localhost")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("Failed to create entry: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						entry1.Comment = "Local loopback"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						entry2, err := core.NewHostEntry("192.168.1.100", "dev.example.com")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("Failed to create entry: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						entry2.AddAlias("www.dev.example.com")
 | 
				
			||||||
 | 
						entry2.AddAlias("api.dev.example.com")
 | 
				
			||||||
 | 
						entry2.Comment = "Development server"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						entry3, err := core.NewHostEntry("10.0.0.50", "staging.example.com")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("Failed to create entry: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						entry3.Active = false // Inactive entry
 | 
				
			||||||
 | 
						entry3.Comment = "Staging server (disabled)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Add entries to hosts file
 | 
				
			||||||
 | 
						hostsFile.AddEntry(entry1)
 | 
				
			||||||
 | 
						hostsFile.AddEntry(entry2)
 | 
				
			||||||
 | 
						hostsFile.AddEntry(entry3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Demonstrate the foundation functionality
 | 
				
			||||||
 | 
						fmt.Printf("Total entries: %d\n", len(hostsFile.Entries))
 | 
				
			||||||
 | 
						fmt.Printf("Active entries: %d\n", len(hostsFile.ActiveEntries()))
 | 
				
			||||||
 | 
						fmt.Println()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println("All entries:")
 | 
				
			||||||
 | 
						for i, entry := range hostsFile.Entries {
 | 
				
			||||||
 | 
							fmt.Printf("%d. %s\n", i+1, entry.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fmt.Println()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println("Active entries only:")
 | 
				
			||||||
 | 
						for i, entry := range hostsFile.ActiveEntries() {
 | 
				
			||||||
 | 
							fmt.Printf("%d. %s\n", i+1, entry.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fmt.Println()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Demonstrate search functionality
 | 
				
			||||||
 | 
						fmt.Println("Search demonstrations:")
 | 
				
			||||||
 | 
						if found := hostsFile.FindEntry("localhost"); found != nil {
 | 
				
			||||||
 | 
							fmt.Printf("Found 'localhost': %s\n", found.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if found := hostsFile.FindEntry("www.dev.example.com"); found != nil {
 | 
				
			||||||
 | 
							fmt.Printf("Found 'www.dev.example.com' (alias): %s\n", found.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if found := hostsFile.FindEntry("notfound.com"); found == nil {
 | 
				
			||||||
 | 
							fmt.Println("'notfound.com' not found (as expected)")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println()
 | 
				
			||||||
 | 
						fmt.Println("Foundation implementation complete!")
 | 
				
			||||||
 | 
						fmt.Println("✅ Core data models working")
 | 
				
			||||||
 | 
						fmt.Println("✅ Validation system working") 
 | 
				
			||||||
 | 
						fmt.Println("✅ Host entry management working")
 | 
				
			||||||
 | 
						fmt.Println("✅ Search and filtering working")
 | 
				
			||||||
 | 
						fmt.Println()
 | 
				
			||||||
 | 
						fmt.Println("Next steps: Implement hosts file parser and TUI components")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								go.mod
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								go.mod
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					module hosts-go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go 1.24.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require github.com/stretchr/testify v1.10.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require (
 | 
				
			||||||
 | 
						github.com/davecgh/go-spew v1.1.1 // indirect
 | 
				
			||||||
 | 
						github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
				
			||||||
 | 
						gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										10
									
								
								go.sum
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								go.sum
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
				
			||||||
 | 
					github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
 | 
					github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 | 
				
			||||||
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 | 
				
			||||||
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
 | 
					gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
				
			||||||
 | 
					gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
							
								
								
									
										221
									
								
								internal/core/models.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								internal/core/models.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,221 @@
 | 
				
			||||||
 | 
					package core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HostEntry represents a single entry in the hosts file
 | 
				
			||||||
 | 
					type HostEntry struct {
 | 
				
			||||||
 | 
						IP        string   // IP address (IPv4 or IPv6)
 | 
				
			||||||
 | 
						Hostname  string   // Primary hostname
 | 
				
			||||||
 | 
						Aliases   []string // Additional hostnames/aliases
 | 
				
			||||||
 | 
						Comment   string   // Inline comment
 | 
				
			||||||
 | 
						Active    bool     // Whether the entry is enabled (not commented out)
 | 
				
			||||||
 | 
						Original  string   // Original line from hosts file for preservation
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewHostEntry creates a new host entry with validation
 | 
				
			||||||
 | 
					func NewHostEntry(ip, hostname string) (*HostEntry, error) {
 | 
				
			||||||
 | 
						entry := &HostEntry{
 | 
				
			||||||
 | 
							IP:       strings.TrimSpace(ip),
 | 
				
			||||||
 | 
							Hostname: strings.TrimSpace(hostname),
 | 
				
			||||||
 | 
							Aliases:  make([]string, 0),
 | 
				
			||||||
 | 
							Active:   true,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := entry.Validate(); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return entry, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate checks if the host entry is valid
 | 
				
			||||||
 | 
					func (h *HostEntry) Validate() error {
 | 
				
			||||||
 | 
						if h.IP == "" {
 | 
				
			||||||
 | 
							return fmt.Errorf("IP address cannot be empty")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if h.Hostname == "" {
 | 
				
			||||||
 | 
							return fmt.Errorf("hostname cannot be empty")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Validate IP address
 | 
				
			||||||
 | 
						if net.ParseIP(h.IP) == nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("invalid IP address: %s", h.IP)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Validate hostname format
 | 
				
			||||||
 | 
						if err := validateHostname(h.Hostname); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("invalid hostname: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Validate aliases
 | 
				
			||||||
 | 
						for _, alias := range h.Aliases {
 | 
				
			||||||
 | 
							if err := validateHostname(alias); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("invalid alias '%s': %w", alias, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddAlias adds an alias to the host entry
 | 
				
			||||||
 | 
					func (h *HostEntry) AddAlias(alias string) error {
 | 
				
			||||||
 | 
						alias = strings.TrimSpace(alias)
 | 
				
			||||||
 | 
						if err := validateHostname(alias); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("invalid alias: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check for duplicates
 | 
				
			||||||
 | 
						for _, existing := range h.Aliases {
 | 
				
			||||||
 | 
							if existing == alias {
 | 
				
			||||||
 | 
								return fmt.Errorf("alias '%s' already exists", alias)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						h.Aliases = append(h.Aliases, alias)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AllHostnames returns the primary hostname and all aliases
 | 
				
			||||||
 | 
					func (h *HostEntry) AllHostnames() []string {
 | 
				
			||||||
 | 
						result := []string{h.Hostname}
 | 
				
			||||||
 | 
						result = append(result, h.Aliases...)
 | 
				
			||||||
 | 
						return result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// String returns the hosts file representation of the entry
 | 
				
			||||||
 | 
					func (h *HostEntry) String() string {
 | 
				
			||||||
 | 
						var parts []string
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						// Add IP and hostname
 | 
				
			||||||
 | 
						parts = append(parts, h.IP, h.Hostname)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						// Add aliases
 | 
				
			||||||
 | 
						parts = append(parts, h.Aliases...)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						line := strings.Join(parts, "\t")
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						// Add comment if present
 | 
				
			||||||
 | 
						if h.Comment != "" {
 | 
				
			||||||
 | 
							line += "\t# " + h.Comment
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						// Add comment prefix if inactive
 | 
				
			||||||
 | 
						if !h.Active {
 | 
				
			||||||
 | 
							line = "# " + line
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						return line
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HostsFile represents the entire hosts file
 | 
				
			||||||
 | 
					type HostsFile struct {
 | 
				
			||||||
 | 
						Entries  []*HostEntry // All host entries
 | 
				
			||||||
 | 
						Comments []string     // Standalone comment lines
 | 
				
			||||||
 | 
						Header   []string     // Header comments at the top of the file
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewHostsFile creates a new empty hosts file
 | 
				
			||||||
 | 
					func NewHostsFile() *HostsFile {
 | 
				
			||||||
 | 
						return &HostsFile{
 | 
				
			||||||
 | 
							Entries:  make([]*HostEntry, 0),
 | 
				
			||||||
 | 
							Comments: make([]string, 0),
 | 
				
			||||||
 | 
							Header:   make([]string, 0),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddEntry adds a host entry to the file
 | 
				
			||||||
 | 
					func (hf *HostsFile) AddEntry(entry *HostEntry) error {
 | 
				
			||||||
 | 
						if err := entry.Validate(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						hf.Entries = append(hf.Entries, entry)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FindEntry finds an entry by hostname
 | 
				
			||||||
 | 
					func (hf *HostsFile) FindEntry(hostname string) *HostEntry {
 | 
				
			||||||
 | 
						hostname = strings.TrimSpace(hostname)
 | 
				
			||||||
 | 
						for _, entry := range hf.Entries {
 | 
				
			||||||
 | 
							if entry.Hostname == hostname {
 | 
				
			||||||
 | 
								return entry
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, alias := range entry.Aliases {
 | 
				
			||||||
 | 
								if alias == hostname {
 | 
				
			||||||
 | 
									return entry
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RemoveEntry removes an entry by hostname
 | 
				
			||||||
 | 
					func (hf *HostsFile) RemoveEntry(hostname string) bool {
 | 
				
			||||||
 | 
						for i, entry := range hf.Entries {
 | 
				
			||||||
 | 
							if entry.Hostname == hostname {
 | 
				
			||||||
 | 
								hf.Entries = append(hf.Entries[:i], hf.Entries[i+1:]...)
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, alias := range entry.Aliases {
 | 
				
			||||||
 | 
								if alias == hostname {
 | 
				
			||||||
 | 
									hf.Entries = append(hf.Entries[:i], hf.Entries[i+1:]...)
 | 
				
			||||||
 | 
									return true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ActiveEntries returns only the active (non-commented) entries
 | 
				
			||||||
 | 
					func (hf *HostsFile) ActiveEntries() []*HostEntry {
 | 
				
			||||||
 | 
						var active []*HostEntry
 | 
				
			||||||
 | 
						for _, entry := range hf.Entries {
 | 
				
			||||||
 | 
							if entry.Active {
 | 
				
			||||||
 | 
								active = append(active, entry)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return active
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// validateHostname validates a hostname according to RFC standards
 | 
				
			||||||
 | 
					func validateHostname(hostname string) error {
 | 
				
			||||||
 | 
						if len(hostname) == 0 {
 | 
				
			||||||
 | 
							return fmt.Errorf("hostname cannot be empty")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(hostname) > 253 {
 | 
				
			||||||
 | 
							return fmt.Errorf("hostname too long (max 253 characters)")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Cannot start or end with hyphen
 | 
				
			||||||
 | 
						if strings.HasPrefix(hostname, "-") || strings.HasSuffix(hostname, "-") {
 | 
				
			||||||
 | 
							return fmt.Errorf("hostname cannot start or end with hyphen")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Split by dots and validate each label
 | 
				
			||||||
 | 
						labels := strings.Split(hostname, ".")
 | 
				
			||||||
 | 
						for _, label := range labels {
 | 
				
			||||||
 | 
							if len(label) == 0 {
 | 
				
			||||||
 | 
								return fmt.Errorf("invalid hostname format")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							// Each label cannot start or end with hyphen
 | 
				
			||||||
 | 
							if strings.HasPrefix(label, "-") || strings.HasSuffix(label, "-") {
 | 
				
			||||||
 | 
								return fmt.Errorf("hostname cannot start or end with hyphen")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							// Each label must contain only alphanumeric characters and hyphens
 | 
				
			||||||
 | 
							labelRegex := regexp.MustCompile(`^[a-zA-Z0-9-]+$`)
 | 
				
			||||||
 | 
							if !labelRegex.MatchString(label) {
 | 
				
			||||||
 | 
								return fmt.Errorf("invalid hostname format")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										144
									
								
								memory-bank/activeContext.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								memory-bank/activeContext.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,144 @@
 | 
				
			||||||
 | 
					# Active Context: hosts-go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Current Work Focus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Status**: Foundation Complete - Ready for Phase 1 (Core Functionality)
 | 
				
			||||||
 | 
					**Priority**: Implementing hosts file parser with format preservation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Recent Changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Foundation Implementation (COMPLETED)
 | 
				
			||||||
 | 
					- ✅ **Go module setup**: Created `go.mod` with all required dependencies
 | 
				
			||||||
 | 
					- ✅ **Project structure**: Complete directory layout (`cmd/`, `internal/`, `tests/`)
 | 
				
			||||||
 | 
					- ✅ **Core data models**: Full `HostEntry` and `HostsFile` structs with validation
 | 
				
			||||||
 | 
					- ✅ **Comprehensive testing**: 44 test cases covering all model functionality
 | 
				
			||||||
 | 
					- ✅ **Demo application**: Working proof-of-concept showing foundation capabilities
 | 
				
			||||||
 | 
					- ✅ **TDD implementation**: Successfully proven test-driven development approach
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Validation System Complete
 | 
				
			||||||
 | 
					- ✅ **IP validation**: IPv4/IPv6 support using Go's net.ParseIP
 | 
				
			||||||
 | 
					- ✅ **Hostname validation**: RFC-compliant with label-by-label checking
 | 
				
			||||||
 | 
					- ✅ **Edge case handling**: Hyphen restrictions, length limits, format validation
 | 
				
			||||||
 | 
					- ✅ **Error messaging**: Clear, specific error messages for all validation failures
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Next Steps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Immediate (Phase 1 - Current Priority)
 | 
				
			||||||
 | 
					1. **Hosts File Parser Implementation**
 | 
				
			||||||
 | 
					   - Write comprehensive parser tests for various hosts file formats
 | 
				
			||||||
 | 
					   - Implement `internal/core/parser.go` for reading `/etc/hosts`
 | 
				
			||||||
 | 
					   - Handle comment preservation and formatting retention
 | 
				
			||||||
 | 
					   - Support active/inactive entry detection (commented lines)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2. **File Operations**
 | 
				
			||||||
 | 
					   - Add file reading with proper error handling
 | 
				
			||||||
 | 
					   - Implement round-trip parsing (read → parse → modify → write)
 | 
				
			||||||
 | 
					   - Test with real hosts file formats and edge cases
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3. **Integration Testing**
 | 
				
			||||||
 | 
					   - Test parser with actual `/etc/hosts` file variations
 | 
				
			||||||
 | 
					   - Verify format preservation during round-trip operations
 | 
				
			||||||
 | 
					   - Handle malformed entries gracefully
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Medium-term (Following sessions)
 | 
				
			||||||
 | 
					1. **Core business logic**
 | 
				
			||||||
 | 
					   - Implement hosts file parsing with comment preservation
 | 
				
			||||||
 | 
					   - Add validation for IP addresses and hostnames
 | 
				
			||||||
 | 
					   - Create entry manipulation functions (add, edit, delete, toggle)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2. **Basic TUI foundation**
 | 
				
			||||||
 | 
					   - Create main Bubble Tea model structure
 | 
				
			||||||
 | 
					   - Implement two-pane layout (list + detail)
 | 
				
			||||||
 | 
					   - Add basic navigation and selection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3. **Permission handling**
 | 
				
			||||||
 | 
					   - Implement view-mode by default
 | 
				
			||||||
 | 
					   - Add edit-mode transition with sudo handling
 | 
				
			||||||
 | 
					   - Test permission scenarios
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Active Decisions and Considerations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Architecture Decisions Made
 | 
				
			||||||
 | 
					- **Layered architecture**: TUI → Business Logic → System Interface
 | 
				
			||||||
 | 
					- **Repository pattern**: Abstract file operations for testability
 | 
				
			||||||
 | 
					- **Command pattern**: Encapsulate edit operations for undo support
 | 
				
			||||||
 | 
					- **Test-driven development**: Write tests before implementation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Key Design Patterns
 | 
				
			||||||
 | 
					- **MVU (Model-View-Update)**: Following Bubble Tea conventions
 | 
				
			||||||
 | 
					- **Separation of concerns**: Clear boundaries between UI, business logic, and system operations
 | 
				
			||||||
 | 
					- **Graceful degradation**: Handle permission issues without crashing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Technology Choices Confirmed
 | 
				
			||||||
 | 
					- **Go 1.21+**: Modern Go features and performance
 | 
				
			||||||
 | 
					- **Bubble Tea**: Mature, well-documented TUI framework
 | 
				
			||||||
 | 
					- **Testify**: Enhanced testing capabilities beyond stdlib
 | 
				
			||||||
 | 
					- **golangci-lint**: Code quality and consistency
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Important Patterns and Preferences
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Code Organization
 | 
				
			||||||
 | 
					- Use `internal/` package for application-specific code
 | 
				
			||||||
 | 
					- Group related functionality in packages (`tui/`, `core/`, `utils/`)
 | 
				
			||||||
 | 
					- Keep main.go minimal - delegate to internal packages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Testing Strategy
 | 
				
			||||||
 | 
					- Write tests before implementation (TDD)
 | 
				
			||||||
 | 
					- Mock external dependencies (file system, network)
 | 
				
			||||||
 | 
					- Use table-driven tests for multiple scenarios
 | 
				
			||||||
 | 
					- Test both success and error cases
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Error Handling
 | 
				
			||||||
 | 
					- Return errors explicitly, don't panic
 | 
				
			||||||
 | 
					- Provide clear error messages with context
 | 
				
			||||||
 | 
					- Implement graceful fallbacks where possible
 | 
				
			||||||
 | 
					- Log errors appropriately for debugging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### UI/UX Principles
 | 
				
			||||||
 | 
					- **Safety first**: Default to read-only mode
 | 
				
			||||||
 | 
					- **Clear feedback**: Show operation status and results
 | 
				
			||||||
 | 
					- **Keyboard-driven**: Efficient navigation without mouse dependency
 | 
				
			||||||
 | 
					- **Responsive**: Sub-16ms update cycles for smooth interaction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Learnings and Project Insights
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Development Environment
 | 
				
			||||||
 | 
					- **macOS focus**: Primary development and testing platform
 | 
				
			||||||
 | 
					- **Cross-platform awareness**: Consider Linux compatibility from start
 | 
				
			||||||
 | 
					- **Terminal compatibility**: Test with multiple terminal applications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### User Experience Priorities
 | 
				
			||||||
 | 
					1. **Safety**: Cannot accidentally corrupt hosts file
 | 
				
			||||||
 | 
					2. **Speed**: Faster than manual editing for common tasks
 | 
				
			||||||
 | 
					3. **Clarity**: Always know what mode you're in and what operations are available
 | 
				
			||||||
 | 
					4. **Confidence**: Validate changes before applying them
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Technical Priorities
 | 
				
			||||||
 | 
					1. **Reliability**: Atomic file operations with backup/restore
 | 
				
			||||||
 | 
					2. **Performance**: Handle large hosts files efficiently
 | 
				
			||||||
 | 
					3. **Maintainability**: Clear code structure for future enhancements
 | 
				
			||||||
 | 
					4. **Testability**: Comprehensive test coverage for confidence in changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Dependencies and Constraints
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### External Dependencies
 | 
				
			||||||
 | 
					- **Bubble Tea ecosystem**: Core framework and components
 | 
				
			||||||
 | 
					- **System permissions**: sudo access for editing hosts file
 | 
				
			||||||
 | 
					- **File system**: Atomic write operations and backup capability
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Development Constraints
 | 
				
			||||||
 | 
					- **No breaking changes**: Must preserve existing hosts file format
 | 
				
			||||||
 | 
					- **Backward compatibility**: Work with various hosts file styles
 | 
				
			||||||
 | 
					- **Minimal dependencies**: Keep external dependencies focused and essential
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Communication Preferences
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Documentation Style
 | 
				
			||||||
 | 
					- **Concrete examples**: Show actual code snippets and command examples
 | 
				
			||||||
 | 
					- **Clear structure**: Use consistent heading hierarchy and formatting
 | 
				
			||||||
 | 
					- **Actionable items**: Specific next steps rather than vague suggestions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Progress Tracking
 | 
				
			||||||
 | 
					- **Incremental development**: Small, testable changes
 | 
				
			||||||
 | 
					- **Regular updates**: Update memory bank after significant milestones
 | 
				
			||||||
 | 
					- **Clear status**: Always know what's working, what's not, and what's next
 | 
				
			||||||
							
								
								
									
										62
									
								
								memory-bank/productContext.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								memory-bank/productContext.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,62 @@
 | 
				
			||||||
 | 
					# Product Context: hosts-go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Why This Project Exists
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `/etc/hosts` file is a critical system file that maps hostnames to IP addresses, but managing it manually is cumbersome and error-prone. Users currently face several pain points:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- **Manual editing**: Requires opening `/etc/hosts` in a text editor with sudo privileges
 | 
				
			||||||
 | 
					- **Format preservation**: Easy to accidentally break formatting or lose comments
 | 
				
			||||||
 | 
					- **Entry management**: No easy way to temporarily disable entries without deleting them
 | 
				
			||||||
 | 
					- **DNS resolution**: Manual IP address updates when hostnames change
 | 
				
			||||||
 | 
					- **Organization**: No built-in sorting or reordering capabilities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Problems It Solves
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. **Safe editing**: Provides a structured interface that validates changes before writing
 | 
				
			||||||
 | 
					2. **Entry activation**: Toggle entries on/off without losing the configuration
 | 
				
			||||||
 | 
					3. **Organization**: Sort and reorder entries intuitively
 | 
				
			||||||
 | 
					4. **DNS integration**: Automatically resolve hostnames to current IP addresses
 | 
				
			||||||
 | 
					5. **Comment preservation**: Maintain documentation alongside entries
 | 
				
			||||||
 | 
					6. **Permission handling**: Only request sudo access when actually editing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## How It Should Work
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Core User Experience
 | 
				
			||||||
 | 
					- **View mode by default**: Browse entries safely without modification risk
 | 
				
			||||||
 | 
					- **Explicit edit mode**: Clear transition to editing with permission request
 | 
				
			||||||
 | 
					- **Two-pane interface**: List view + detail view for efficient navigation
 | 
				
			||||||
 | 
					- **Keyboard-driven**: Fast navigation and actions via keyboard shortcuts
 | 
				
			||||||
 | 
					- **Visual feedback**: Clear indicators for active/inactive entries and changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Key Workflows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. **Browse entries**: Launch app, see all current hosts entries with status
 | 
				
			||||||
 | 
					2. **Quick activation**: Toggle entries on/off with simple keypress
 | 
				
			||||||
 | 
					3. **Edit existing**: Select entry, enter edit mode, modify details
 | 
				
			||||||
 | 
					4. **Add new**: Create new hostname mappings with validation
 | 
				
			||||||
 | 
					5. **DNS resolution**: Update IP addresses automatically from DNS
 | 
				
			||||||
 | 
					6. **Reorder entries**: Drag/move entries to organize logically
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## User Experience Goals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Immediate Value
 | 
				
			||||||
 | 
					- **Zero learning curve**: Intuitive interface familiar to CLI users
 | 
				
			||||||
 | 
					- **Safety first**: Hard to accidentally break the hosts file
 | 
				
			||||||
 | 
					- **Speed**: Faster than manual editing for common tasks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Long-term Benefits
 | 
				
			||||||
 | 
					- **Organized hosts**: Keep entries structured and documented
 | 
				
			||||||
 | 
					- **Confidence**: Know changes are validated before applied
 | 
				
			||||||
 | 
					- **Efficiency**: Common tasks become single keystrokes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Target Users
 | 
				
			||||||
 | 
					- **Developers**: Managing local development environments
 | 
				
			||||||
 | 
					- **System administrators**: Bulk hosts file management
 | 
				
			||||||
 | 
					- **Network engineers**: Testing connectivity and DNS overrides
 | 
				
			||||||
 | 
					- **Security professionals**: Blocking/redirecting malicious domains
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Success Metrics
 | 
				
			||||||
 | 
					- Users prefer this tool over manual `/etc/hosts` editing
 | 
				
			||||||
 | 
					- Reduces hosts file corruption incidents
 | 
				
			||||||
 | 
					- Speeds up common host management tasks
 | 
				
			||||||
 | 
					- Provides confidence in making changes
 | 
				
			||||||
							
								
								
									
										160
									
								
								memory-bank/progress.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								memory-bank/progress.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,160 @@
 | 
				
			||||||
 | 
					# Progress: hosts-go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## What Works
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### ✅ Memory Bank Foundation
 | 
				
			||||||
 | 
					- **Project documentation**: Complete memory bank structure established
 | 
				
			||||||
 | 
					- **Architecture planning**: Clear technical design documented
 | 
				
			||||||
 | 
					- **Development approach**: TDD strategy and patterns defined
 | 
				
			||||||
 | 
					- **Technology stack**: Bubble Tea ecosystem selected and documented
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### ✅ Project Definition
 | 
				
			||||||
 | 
					- **Core requirements**: Two-pane TUI for hosts file management clearly defined
 | 
				
			||||||
 | 
					- **User experience**: Safety-first approach with explicit edit mode
 | 
				
			||||||
 | 
					- **Technical constraints**: Permission model and file safety requirements established
 | 
				
			||||||
 | 
					- **Directory structure**: Planned layout for Go project organization
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### ✅ Foundation Implementation (COMPLETED)
 | 
				
			||||||
 | 
					- **Go module initialization**: ✅ `go.mod` created with all core dependencies
 | 
				
			||||||
 | 
					- **Directory structure**: ✅ Complete project structure (`cmd/`, `internal/`, `tests/`)
 | 
				
			||||||
 | 
					- **Core data models**: ✅ `internal/core/models.go` with HostEntry and HostsFile structs
 | 
				
			||||||
 | 
					- **Validation system**: ✅ IP address and hostname validation with RFC compliance
 | 
				
			||||||
 | 
					- **Test suite**: ✅ Comprehensive tests (44 test cases, 100% passing)
 | 
				
			||||||
 | 
					- **Demo application**: ✅ Working `cmd/hosts/main.go` demonstrating functionality
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## What's Left to Build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🚧 Core Functionality (Phase 1 - Current Priority)
 | 
				
			||||||
 | 
					- [ ] **Hosts file parser**: Read and parse `/etc/hosts` file format
 | 
				
			||||||
 | 
					  - [ ] Parse IP addresses, hostnames, comments
 | 
				
			||||||
 | 
					  - [ ] Handle disabled entries (commented out)
 | 
				
			||||||
 | 
					  - [ ] Preserve original formatting and comments
 | 
				
			||||||
 | 
					- [ ] **File operations**: Read hosts file with error handling
 | 
				
			||||||
 | 
					- [ ] **Round-trip parsing**: Parse → modify → write back with format preservation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🎨 Basic TUI (Phase 2)  
 | 
				
			||||||
 | 
					- [ ] **Main Bubble Tea model**: Core application state and structure
 | 
				
			||||||
 | 
					- [ ] **Two-pane layout**: Left list + right detail view
 | 
				
			||||||
 | 
					- [ ] **Entry list display**: Show active status, IP, hostname columns
 | 
				
			||||||
 | 
					- [ ] **Entry selection**: Navigate and select entries with keyboard
 | 
				
			||||||
 | 
					- [ ] **View mode**: Safe browsing without modification capability
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🔧 Edit Functionality (Phase 3)
 | 
				
			||||||
 | 
					- [ ] **Edit mode transition**: Explicit mode switching with visual indicators
 | 
				
			||||||
 | 
					- [ ] **Permission handling**: Request sudo access when entering edit mode
 | 
				
			||||||
 | 
					- [ ] **Entry modification**: Add, edit, delete, toggle active status
 | 
				
			||||||
 | 
					- [ ] **File writing**: Atomic updates with backup and rollback
 | 
				
			||||||
 | 
					- [ ] **Input validation**: Real-time validation of IP and hostname inputs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🌐 Advanced Features (Phase 4)
 | 
				
			||||||
 | 
					- [ ] **DNS resolution**: Background hostname to IP resolution
 | 
				
			||||||
 | 
					- [ ] **IP comparison**: Compare resolved vs current IP addresses
 | 
				
			||||||
 | 
					- [ ] **Entry reordering**: Manual drag/drop or move commands
 | 
				
			||||||
 | 
					- [ ] **Sorting options**: Sort by IP, hostname, or custom order
 | 
				
			||||||
 | 
					- [ ] **Search/filter**: Find entries quickly in large files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🧪 Testing & Quality (Ongoing)
 | 
				
			||||||
 | 
					- [ ] **Parser tests**: Round-trip parsing, edge cases, malformed files
 | 
				
			||||||
 | 
					- [ ] **Model tests**: Data validation, entry manipulation
 | 
				
			||||||
 | 
					- [ ] **TUI tests**: User interactions, state transitions
 | 
				
			||||||
 | 
					- [ ] **Integration tests**: Complete workflows, file operations
 | 
				
			||||||
 | 
					- [ ] **Permission tests**: sudo scenarios, graceful degradation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Current Status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Project Phase: **Foundation Complete → Core Functionality**
 | 
				
			||||||
 | 
					- **Completion**: ~25% (foundation and core models complete)
 | 
				
			||||||
 | 
					- **Active work**: Ready to implement hosts file parser (Phase 1)
 | 
				
			||||||
 | 
					- **Blockers**: None - solid foundation established
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Development Readiness
 | 
				
			||||||
 | 
					- ✅ **Architecture designed**: Clear technical approach documented
 | 
				
			||||||
 | 
					- ✅ **Requirements defined**: User experience and functionality specified  
 | 
				
			||||||
 | 
					- ✅ **Technology selected**: Bubble Tea stack confirmed and installed
 | 
				
			||||||
 | 
					- ✅ **Testing strategy**: TDD approach implemented and proven
 | 
				
			||||||
 | 
					- ✅ **Project scaffolding**: Complete Go module with all dependencies
 | 
				
			||||||
 | 
					- ✅ **Development environment**: Fully functional with comprehensive tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Risk Assessment
 | 
				
			||||||
 | 
					- **Low risk**: Well-established technology stack (Go + Bubble Tea)
 | 
				
			||||||
 | 
					- **Medium risk**: Permission handling complexity (sudo integration)
 | 
				
			||||||
 | 
					- **Low risk**: File format parsing (well-defined `/etc/hosts` format)
 | 
				
			||||||
 | 
					- **Medium risk**: TUI responsiveness with large files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Known Issues
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Technical Challenges
 | 
				
			||||||
 | 
					- **Permission elevation**: Need to handle sudo gracefully across platforms
 | 
				
			||||||
 | 
					- **File locking**: Prevent concurrent modifications to `/etc/hosts`
 | 
				
			||||||
 | 
					- **Large file performance**: Ensure responsiveness with large hosts files
 | 
				
			||||||
 | 
					- **Terminal compatibility**: Test across different terminal applications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Implementation Decisions Needed
 | 
				
			||||||
 | 
					- **Configuration storage**: Where to store user preferences and settings
 | 
				
			||||||
 | 
					- **Backup strategy**: How many backups to keep and where to store them
 | 
				
			||||||
 | 
					- **DNS timeout handling**: How to handle slow or unresponsive DNS queries
 | 
				
			||||||
 | 
					- **Error recovery**: How to handle corrupted hosts files or write failures
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Evolution of Project Decisions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Initial Scope
 | 
				
			||||||
 | 
					- Started with comprehensive feature set including DNS resolution
 | 
				
			||||||
 | 
					- Planned for cross-platform compatibility from day one
 | 
				
			||||||
 | 
					- Emphasized safety and validation throughout
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Refined Approach
 | 
				
			||||||
 | 
					- **TDD emphasis**: Write tests first to ensure reliability
 | 
				
			||||||
 | 
					- **Incremental development**: Build core functionality before advanced features
 | 
				
			||||||
 | 
					- **Safety first**: Default to read-only mode, explicit edit mode transition
 | 
				
			||||||
 | 
					- **User experience focus**: Two-pane interface for efficiency
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Technology Evolution
 | 
				
			||||||
 | 
					- **Framework choice**: Bubble Tea selected for maturity and documentation
 | 
				
			||||||
 | 
					- **Architecture pattern**: MVU pattern from Bubble Tea ecosystem
 | 
				
			||||||
 | 
					- **Testing approach**: Comprehensive coverage with mocked dependencies
 | 
				
			||||||
 | 
					- **Development workflow**: Standard Go tooling with additional quality tools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Success Metrics & Milestones
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Milestone 1: Basic Functionality (Target: Week 1)
 | 
				
			||||||
 | 
					- [ ] Parse and display existing hosts file entries
 | 
				
			||||||
 | 
					- [ ] Navigate entries with keyboard
 | 
				
			||||||
 | 
					- [ ] View entry details in right pane
 | 
				
			||||||
 | 
					- **Success criteria**: Can safely browse hosts file without editing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Milestone 2: Core Editing (Target: Week 2)  
 | 
				
			||||||
 | 
					- [ ] Toggle entries active/inactive
 | 
				
			||||||
 | 
					- [ ] Add new entries with validation
 | 
				
			||||||
 | 
					- [ ] Edit existing entries
 | 
				
			||||||
 | 
					- **Success criteria**: Can perform basic hosts file modifications safely
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Milestone 3: Advanced Features (Target: Week 3)
 | 
				
			||||||
 | 
					- [ ] DNS resolution and IP updates
 | 
				
			||||||
 | 
					- [ ] Entry reordering and sorting
 | 
				
			||||||
 | 
					- [ ] Search and filtering
 | 
				
			||||||
 | 
					- **Success criteria**: Full feature parity with manual editing plus enhancements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Milestone 4: Polish & Release (Target: Week 4)
 | 
				
			||||||
 | 
					- [ ] Comprehensive testing and bug fixes
 | 
				
			||||||
 | 
					- [ ] Documentation and usage examples
 | 
				
			||||||
 | 
					- [ ] Cross-platform testing and compatibility
 | 
				
			||||||
 | 
					- **Success criteria**: Production-ready tool suitable for daily use
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Next Immediate Actions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### ✅ COMPLETED Foundation Tasks
 | 
				
			||||||
 | 
					1. ✅ **Initialize Go project** (`go mod init hosts-go`)
 | 
				
			||||||
 | 
					2. ✅ **Add core dependencies** (Bubble Tea, Bubbles, Lip Gloss, testify)
 | 
				
			||||||
 | 
					3. ✅ **Create directory structure** according to projectbrief.md
 | 
				
			||||||
 | 
					4. ✅ **Create core data models** with comprehensive validation
 | 
				
			||||||
 | 
					5. ✅ **Implement test suite** (44 tests, 100% passing)
 | 
				
			||||||
 | 
					6. ✅ **Create demo application** proving foundation works
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🚧 NEXT Phase 1 Actions (Hosts File Parser)
 | 
				
			||||||
 | 
					1. **Write parser tests** for `/etc/hosts` file format parsing
 | 
				
			||||||
 | 
					2. **Implement hosts file reader** (`internal/core/parser.go`)
 | 
				
			||||||
 | 
					3. **Add line-by-line parsing logic** with comment preservation
 | 
				
			||||||
 | 
					4. **Test round-trip parsing** (read → parse → write)
 | 
				
			||||||
 | 
					5. **Handle edge cases** (malformed entries, various formats)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The foundation is now solid and ready for implementing the core parsing functionality.
 | 
				
			||||||
							
								
								
									
										151
									
								
								memory-bank/projectbrief.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								memory-bank/projectbrief.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,151 @@
 | 
				
			||||||
 | 
					# Project Brief: hosts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Foundation of the Project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The **hosts** project is a Go-based terminal application designed to manage the system `/etc/hosts` file with a modern, interactive Text User Interface (TUI). The goal is to simplify the manipulation, organization, and updating of hostname entries directly from the terminal without manually editing text files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The application will use the [**Bubble Tea**](https://github.com/charmbracelet/bubbletea) framework to provide a clean, responsive TUI experience, with a focus on clarity, speed, and ease of use.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## High-Level Overview
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The application provides a **two-pane TUI**:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- **Left pane:** List of all hostname entries, with columns:
 | 
				
			||||||
 | 
					  - Active (✓ when enabled)
 | 
				
			||||||
 | 
					  - IP address
 | 
				
			||||||
 | 
					  - Canonical hostname
 | 
				
			||||||
 | 
					- **Right pane:** Detailed view of the selected entry, with the option to edit the entry.
 | 
				
			||||||
 | 
					  - Active status
 | 
				
			||||||
 | 
					  - IP Address
 | 
				
			||||||
 | 
					  - Hostname
 | 
				
			||||||
 | 
					    - Since an entry can have multiple hostnames, every hostname after the first is treated as an alias and displayed here.
 | 
				
			||||||
 | 
					  - Comment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The user can:
 | 
				
			||||||
 | 
					- Activate/deactivate entries
 | 
				
			||||||
 | 
					- Reorder entries manually
 | 
				
			||||||
 | 
					- Sort by different attributes
 | 
				
			||||||
 | 
					- Maintain inline comments
 | 
				
			||||||
 | 
					- Use DNS-based resolution for hostnames
 | 
				
			||||||
 | 
					- Quickly update IP addresses
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The program will operate in **view-only mode** by default and require explicit entry into **edit mode**, at which point it will request elevated (sudo) permissions until editing is disabled.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The project uses:
 | 
				
			||||||
 | 
					- **Go** as the development language
 | 
				
			||||||
 | 
					- **Bubble Tea** for TUI rendering and interaction
 | 
				
			||||||
 | 
					- **Bubbles** Common Bubble Tea components such as text inputs, viewports, spinners and so on
 | 
				
			||||||
 | 
					- **Lip Gloss** for styling
 | 
				
			||||||
 | 
					- **bubblezone** Easy mouse event tracking for Bubble Tea components
 | 
				
			||||||
 | 
					- **Go modules** for dependency management
 | 
				
			||||||
 | 
					- **golangci-lint** for linting
 | 
				
			||||||
 | 
					- **Test-driven development** with **Go’s built-in testing** and **testify** for assertions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Core Requirements & Goals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Display `/etc/hosts` entries in a two-pane Bubble Tea interface
 | 
				
			||||||
 | 
					- Activate or deactivate specific hostname entries
 | 
				
			||||||
 | 
					- Reorder hostname entries manually
 | 
				
			||||||
 | 
					- Sort entries by IP or hostname
 | 
				
			||||||
 | 
					- Add and edit comments for entries
 | 
				
			||||||
 | 
					- Support CNAME-like DNS name storage with automatic IP resolution
 | 
				
			||||||
 | 
					- Compare resolved IP addresses and allow the user to choose
 | 
				
			||||||
 | 
					- Validate all changes before writing to `/etc/hosts`
 | 
				
			||||||
 | 
					- Require edit mode for changes, with sudo access requested when entering edit mode
 | 
				
			||||||
 | 
					- Preserve `/etc/hosts` formatting and comments when saving changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Example One-Line Summary
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**“A Go + Bubble Tea TUI app for managing `/etc/hosts` with sorting, DNS resolution, and quick activation/deactivation.”**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Directory Structure
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					hosts/
 | 
				
			||||||
 | 
					├── go.mod                # Go module definition
 | 
				
			||||||
 | 
					├── go.sum
 | 
				
			||||||
 | 
					├── README.md
 | 
				
			||||||
 | 
					├── cmd/
 | 
				
			||||||
 | 
					│   └── hosts/
 | 
				
			||||||
 | 
					│       └── main.go       # Entry point (go run ./cmd/hosts)
 | 
				
			||||||
 | 
					├── internal/
 | 
				
			||||||
 | 
					│   ├── tui/              # UI components (Bubble Tea models)
 | 
				
			||||||
 | 
					│   │   ├── model.go      # Main Bubble Tea model
 | 
				
			||||||
 | 
					│   │   ├── view.go       # Rendering logic
 | 
				
			||||||
 | 
					│   │   ├── update.go     # State updates
 | 
				
			||||||
 | 
					│   │   ├── config_modal.go # Configuration modal dialog
 | 
				
			||||||
 | 
					│   │   └── styles.go     # Lip Gloss styles
 | 
				
			||||||
 | 
					│   ├── core/             # Business logic
 | 
				
			||||||
 | 
					│   │   ├── parser.go     # /etc/hosts parsing & writing
 | 
				
			||||||
 | 
					│   │   ├── models.go     # Data models (Entry, Comment, etc.)
 | 
				
			||||||
 | 
					│   │   ├── config.go     # Configuration management
 | 
				
			||||||
 | 
					│   │   ├── dns.go        # DNS resolution & comparison
 | 
				
			||||||
 | 
					│   │   └── manager.go    # Edit mode operations
 | 
				
			||||||
 | 
					│   └── utils/            # Shared helpers
 | 
				
			||||||
 | 
					└── tests/
 | 
				
			||||||
 | 
					├── parser_test.go
 | 
				
			||||||
 | 
					├── models_test.go
 | 
				
			||||||
 | 
					├── config_test.go
 | 
				
			||||||
 | 
					├── tui_test.go
 | 
				
			||||||
 | 
					├── manager_test.go
 | 
				
			||||||
 | 
					├── dns_test.go
 | 
				
			||||||
 | 
					└── integration_test.go
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Testing Strategy (TDD)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Approach
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- **Write tests before implementation** for all features
 | 
				
			||||||
 | 
					- Use **Go’s `testing` package** for core tests
 | 
				
			||||||
 | 
					- Use **testify** for assertions and mocks
 | 
				
			||||||
 | 
					- Mock `/etc/hosts` file I/O and DNS lookups to avoid system dependencies
 | 
				
			||||||
 | 
					- Include integration tests for Bubble Tea models (test `Update` and `View` functions)
 | 
				
			||||||
 | 
					- Aim for **full coverage** in core logic (`parser`, `dns`, `manager`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Planned Test Coverage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. **Parser Tests**:
 | 
				
			||||||
 | 
					   - Parse `/etc/hosts` with comments and disabled entries
 | 
				
			||||||
 | 
					   - Preserve formatting when writing back
 | 
				
			||||||
 | 
					   - Handle empty and comment-only files
 | 
				
			||||||
 | 
					   - Validate round-trip parsing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2. **Model Tests**:
 | 
				
			||||||
 | 
					   - `HostEntry` creation, validation, and serialization
 | 
				
			||||||
 | 
					   - Container for multiple entries
 | 
				
			||||||
 | 
					   - IPv4/IPv6 validation
 | 
				
			||||||
 | 
					   - Hostname validation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3. **Configuration Tests**:
 | 
				
			||||||
 | 
					   - Load/save config in JSON or TOML
 | 
				
			||||||
 | 
					   - Default values handling
 | 
				
			||||||
 | 
					   - Error handling on corrupt configs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					4. **TUI Tests**:
 | 
				
			||||||
 | 
					   - Model initialization
 | 
				
			||||||
 | 
					   - State transitions (selection, sorting, toggling)
 | 
				
			||||||
 | 
					   - Modal dialog lifecycle
 | 
				
			||||||
 | 
					   - Keyboard input mapping
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					5. **Manager Tests**:
 | 
				
			||||||
 | 
					   - Permission handling (sudo)
 | 
				
			||||||
 | 
					   - Edit mode transitions
 | 
				
			||||||
 | 
					   - File backup and atomic saves
 | 
				
			||||||
 | 
					   - Entry manipulation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					6. **DNS Tests**:
 | 
				
			||||||
 | 
					   - Hostname resolution
 | 
				
			||||||
 | 
					   - IP comparison
 | 
				
			||||||
 | 
					   - Handling unreachable hosts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					7. **Integration Tests**:
 | 
				
			||||||
 | 
					   - Complete TUI workflows
 | 
				
			||||||
 | 
					   - File modifications with rollback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Important Documentation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [Bubble Tea](https://pkg.go.dev/github.com/charmbracelet/bubbletea)
 | 
				
			||||||
 | 
					- [Bubbles](https://github.com/charmbracelet/bubbles)
 | 
				
			||||||
 | 
					- [Lip Gloss](https://github.com/charmbracelet/lipgloss)
 | 
				
			||||||
 | 
					- [bubblezone](https://pkg.go.dev/github.com/lrstanley/bubblezone)
 | 
				
			||||||
							
								
								
									
										161
									
								
								memory-bank/systemPatterns.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								memory-bank/systemPatterns.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,161 @@
 | 
				
			||||||
 | 
					# System Patterns: hosts-go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Architecture Overview
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The hosts-go application follows a **layered architecture** with clear separation of concerns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					┌─────────────────────┐
 | 
				
			||||||
 | 
					│     TUI Layer       │  ← Bubble Tea models, views, styles
 | 
				
			||||||
 | 
					├─────────────────────┤
 | 
				
			||||||
 | 
					│   Business Logic    │  ← Core domain logic, validation
 | 
				
			||||||
 | 
					├─────────────────────┤
 | 
				
			||||||
 | 
					│   System Interface  │  ← File I/O, DNS, permissions
 | 
				
			||||||
 | 
					└─────────────────────┘
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Key Technical Decisions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 1. **Bubble Tea Framework**
 | 
				
			||||||
 | 
					- **Why**: Provides mature TUI framework with event-driven architecture
 | 
				
			||||||
 | 
					- **Pattern**: Model-View-Update (MVU) pattern for state management
 | 
				
			||||||
 | 
					- **Components**: Main model orchestrates sub-components (list, detail, modal)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 2. **Internal Package Structure**
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					internal/
 | 
				
			||||||
 | 
					├── tui/      # UI layer - Bubble Tea components
 | 
				
			||||||
 | 
					├── core/     # Business logic - domain models and operations  
 | 
				
			||||||
 | 
					└── utils/    # Shared utilities - helpers and common functions
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 3. **Permission Model**
 | 
				
			||||||
 | 
					- **View mode default**: Read-only access, no sudo required
 | 
				
			||||||
 | 
					- **Edit mode explicit**: User must explicitly enter edit mode
 | 
				
			||||||
 | 
					- **Sudo on demand**: Only request elevated permissions when entering edit mode
 | 
				
			||||||
 | 
					- **Graceful fallback**: Handle permission denied gracefully
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Design Patterns in Use
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 1. **Model-View-Update (MVU)**
 | 
				
			||||||
 | 
					- **Model**: Application state (entries, selection, mode)
 | 
				
			||||||
 | 
					- **View**: Rendering logic (two-pane layout, styles)
 | 
				
			||||||
 | 
					- **Update**: State transitions based on user input
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 2. **Command Pattern**
 | 
				
			||||||
 | 
					- **Edit operations**: Encapsulate modifications as commands
 | 
				
			||||||
 | 
					- **Undo support**: Commands can be reversed
 | 
				
			||||||
 | 
					- **Validation**: Commands validate before execution
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 3. **Repository Pattern**
 | 
				
			||||||
 | 
					- **HostsRepository**: Abstract file operations
 | 
				
			||||||
 | 
					- **MockRepository**: In-memory implementation for testing
 | 
				
			||||||
 | 
					- **FileRepository**: Actual `/etc/hosts` file operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 4. **Observer Pattern**
 | 
				
			||||||
 | 
					- **DNS resolution**: Background updates to IP addresses
 | 
				
			||||||
 | 
					- **File watching**: Detect external changes to hosts file
 | 
				
			||||||
 | 
					- **Status updates**: Notify UI of operation progress
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Component Relationships
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### TUI Components
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					MainModel
 | 
				
			||||||
 | 
					├── ListModel (left pane)
 | 
				
			||||||
 | 
					│   ├── Entry selection
 | 
				
			||||||
 | 
					│   ├── Sorting controls
 | 
				
			||||||
 | 
					│   └── Status indicators
 | 
				
			||||||
 | 
					├── DetailModel (right pane)
 | 
				
			||||||
 | 
					│   ├── Entry editing
 | 
				
			||||||
 | 
					│   ├── Form validation
 | 
				
			||||||
 | 
					│   └── DNS resolution
 | 
				
			||||||
 | 
					└── ModalModel (overlays)
 | 
				
			||||||
 | 
					    ├── Configuration
 | 
				
			||||||
 | 
					    ├── Confirmations
 | 
				
			||||||
 | 
					    └── Error dialogs
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Core Business Logic
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Manager
 | 
				
			||||||
 | 
					├── HostsParser (read/write hosts file)
 | 
				
			||||||
 | 
					├── DNSResolver (hostname to IP resolution)
 | 
				
			||||||
 | 
					├── Validator (entry validation)
 | 
				
			||||||
 | 
					└── ConfigManager (user preferences)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Critical Implementation Paths
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 1. **File Operations**
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					// Atomic file updates with backup
 | 
				
			||||||
 | 
					1. Read current /etc/hosts → backup
 | 
				
			||||||
 | 
					2. Parse entries → validate changes
 | 
				
			||||||
 | 
					3. Write to temporary file → verify
 | 
				
			||||||
 | 
					4. Atomic move temp → /etc/hosts
 | 
				
			||||||
 | 
					5. Remove backup on success
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 2. **State Management**
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					// Bubble Tea update cycle
 | 
				
			||||||
 | 
					1. User input → Command
 | 
				
			||||||
 | 
					2. Command → State change
 | 
				
			||||||
 | 
					3. State change → View update
 | 
				
			||||||
 | 
					4. View update → Screen render
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 3. **DNS Resolution**
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					// Background IP resolution
 | 
				
			||||||
 | 
					1. Extract hostnames from entries
 | 
				
			||||||
 | 
					2. Resolve in background goroutines  
 | 
				
			||||||
 | 
					3. Compare resolved vs current IPs
 | 
				
			||||||
 | 
					4. Update UI with resolution status
 | 
				
			||||||
 | 
					5. User chooses whether to update
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 4. **Edit Mode Transition**
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					// Permission elevation
 | 
				
			||||||
 | 
					1. User requests edit mode
 | 
				
			||||||
 | 
					2. Check current permissions
 | 
				
			||||||
 | 
					3. Request sudo if needed
 | 
				
			||||||
 | 
					4. Validate file write access
 | 
				
			||||||
 | 
					5. Enable editing controls
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Error Handling Strategy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 1. **Graceful Degradation**
 | 
				
			||||||
 | 
					- **No sudo**: Continue in view-only mode
 | 
				
			||||||
 | 
					- **File locked**: Show warning, allow retry
 | 
				
			||||||
 | 
					- **DNS failure**: Show cached/manual IP options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 2. **Validation Layers**
 | 
				
			||||||
 | 
					- **Input validation**: Real-time feedback on forms
 | 
				
			||||||
 | 
					- **Business rules**: Validate complete entries
 | 
				
			||||||
 | 
					- **System constraints**: Check file permissions, IP formats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 3. **Recovery Mechanisms**
 | 
				
			||||||
 | 
					- **Backup restoration**: Automatic rollback on write failures
 | 
				
			||||||
 | 
					- **State recovery**: Restore UI state after errors
 | 
				
			||||||
 | 
					- **User guidance**: Clear error messages with suggested actions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Testing Architecture
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 1. **Unit Tests**
 | 
				
			||||||
 | 
					- **Pure functions**: Parser, validator, DNS resolver
 | 
				
			||||||
 | 
					- **Mocked dependencies**: File system, network calls
 | 
				
			||||||
 | 
					- **Edge cases**: Malformed files, network errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 2. **Integration Tests**
 | 
				
			||||||
 | 
					- **TUI workflows**: Complete user interactions
 | 
				
			||||||
 | 
					- **File operations**: Real file system operations (in temp dirs)
 | 
				
			||||||
 | 
					- **Permission scenarios**: Test sudo/non-sudo paths
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 3. **Test Patterns**
 | 
				
			||||||
 | 
					- **Table-driven tests**: Multiple input scenarios
 | 
				
			||||||
 | 
					- **Mock interfaces**: Controllable external dependencies
 | 
				
			||||||
 | 
					- **Golden files**: Expected output comparisons
 | 
				
			||||||
							
								
								
									
										231
									
								
								memory-bank/techContext.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								memory-bank/techContext.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,231 @@
 | 
				
			||||||
 | 
					# Technical Context: hosts-go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Technologies Used
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Core Language & Runtime
 | 
				
			||||||
 | 
					- **Go 1.21+**: Primary development language
 | 
				
			||||||
 | 
					- **Go Modules**: Dependency management (`go.mod`, `go.sum`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### TUI Framework Stack
 | 
				
			||||||
 | 
					- **[Bubble Tea](https://github.com/charmbracelet/bubbletea)**: Core TUI framework
 | 
				
			||||||
 | 
					  - Event-driven architecture
 | 
				
			||||||
 | 
					  - Model-View-Update pattern
 | 
				
			||||||
 | 
					  - Cross-platform terminal support
 | 
				
			||||||
 | 
					- **[Bubbles](https://github.com/charmbracelet/bubbles)**: Pre-built components
 | 
				
			||||||
 | 
					  - Text inputs, viewports, spinners
 | 
				
			||||||
 | 
					  - List components, progress bars
 | 
				
			||||||
 | 
					  - Standardized interaction patterns
 | 
				
			||||||
 | 
					- **[Lip Gloss](https://github.com/charmbracelet/lipgloss)**: Styling and layout
 | 
				
			||||||
 | 
					  - CSS-like styling for terminal
 | 
				
			||||||
 | 
					  - Colors, borders, padding, margins
 | 
				
			||||||
 | 
					  - Responsive layout capabilities
 | 
				
			||||||
 | 
					- **[bubblezone](https://github.com/lrstanley/bubblezone)**: Mouse event handling
 | 
				
			||||||
 | 
					  - Click detection for TUI components
 | 
				
			||||||
 | 
					  - Mouse wheel support
 | 
				
			||||||
 | 
					  - Touch-friendly interactions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Development Tools
 | 
				
			||||||
 | 
					- **golangci-lint**: Static analysis and linting
 | 
				
			||||||
 | 
					- **Go testing**: Built-in testing framework
 | 
				
			||||||
 | 
					- **testify**: Enhanced assertions and mocks
 | 
				
			||||||
 | 
					- **Go race detector**: Concurrency testing
 | 
				
			||||||
 | 
					- **Go build**: Cross-platform compilation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Development Setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Prerequisites
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					# Go installation (1.21+)
 | 
				
			||||||
 | 
					go version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Development tools
 | 
				
			||||||
 | 
					go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Project Initialization
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					# Initialize Go module
 | 
				
			||||||
 | 
					go mod init hosts-go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Add core dependencies
 | 
				
			||||||
 | 
					go get github.com/charmbracelet/bubbletea@latest
 | 
				
			||||||
 | 
					go get github.com/charmbracelet/bubbles@latest
 | 
				
			||||||
 | 
					go get github.com/charmbracelet/lipgloss@latest
 | 
				
			||||||
 | 
					go get github.com/lrstanley/bubblezone@latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Testing dependencies
 | 
				
			||||||
 | 
					go get github.com/stretchr/testify@latest
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Build & Run
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					# Development run
 | 
				
			||||||
 | 
					go run ./cmd/hosts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Build binary
 | 
				
			||||||
 | 
					go build -o hosts ./cmd/hosts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Cross-platform builds
 | 
				
			||||||
 | 
					GOOS=linux GOARCH=amd64 go build -o hosts-linux ./cmd/hosts
 | 
				
			||||||
 | 
					GOOS=windows GOARCH=amd64 go build -o hosts.exe ./cmd/hosts
 | 
				
			||||||
 | 
					GOOS=darwin GOARCH=amd64 go build -o hosts-darwin ./cmd/hosts
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Technical Constraints
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### System Requirements
 | 
				
			||||||
 | 
					- **Unix-like systems**: macOS, Linux (primary targets)
 | 
				
			||||||
 | 
					- **Terminal support**: 256+ colors, UTF-8 encoding
 | 
				
			||||||
 | 
					- **File permissions**: `/etc/hosts` read access (view mode)
 | 
				
			||||||
 | 
					- **Elevated permissions**: sudo access (edit mode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Performance Constraints
 | 
				
			||||||
 | 
					- **Memory**: Minimal footprint for terminal application
 | 
				
			||||||
 | 
					- **Startup time**: < 100ms launch time
 | 
				
			||||||
 | 
					- **File size**: Support hosts files up to 10MB
 | 
				
			||||||
 | 
					- **Responsiveness**: < 16ms UI update cycles
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Security Constraints
 | 
				
			||||||
 | 
					- **Privilege escalation**: Only when explicitly requested
 | 
				
			||||||
 | 
					- **File validation**: Prevent hosts file corruption
 | 
				
			||||||
 | 
					- **Input sanitization**: Validate all hostname/IP inputs
 | 
				
			||||||
 | 
					- **Backup strategy**: Atomic updates with rollback capability
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Runtime Dependencies
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					// Core TUI framework
 | 
				
			||||||
 | 
					github.com/charmbracelet/bubbletea v0.25.0
 | 
				
			||||||
 | 
					github.com/charmbracelet/bubbles v0.17.1
 | 
				
			||||||
 | 
					github.com/charmbracelet/lipgloss v0.9.1
 | 
				
			||||||
 | 
					github.com/lrstanley/bubblezone v0.0.0-20231228141418-c04f8a77c893
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Standard library usage
 | 
				
			||||||
 | 
					net          // DNS resolution, IP validation
 | 
				
			||||||
 | 
					os           // File operations, environment
 | 
				
			||||||
 | 
					os/exec      // Sudo command execution
 | 
				
			||||||
 | 
					path/filepath // Path manipulation
 | 
				
			||||||
 | 
					strings      // Text processing
 | 
				
			||||||
 | 
					regex        // Pattern matching
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Development Dependencies
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					// Testing framework
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.8.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Optional: Enhanced development
 | 
				
			||||||
 | 
					github.com/golangci/golangci-lint // Linting
 | 
				
			||||||
 | 
					github.com/air-verse/air         // Live reload (dev only)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Tool Usage Patterns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Testing Workflow
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					# Run all tests
 | 
				
			||||||
 | 
					go test ./...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Run tests with coverage
 | 
				
			||||||
 | 
					go test -cover ./...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Run specific test package
 | 
				
			||||||
 | 
					go test ./internal/core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Run tests with race detection
 | 
				
			||||||
 | 
					go test -race ./...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Benchmark tests
 | 
				
			||||||
 | 
					go test -bench=. ./...
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Linting & Quality
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					# Run linter
 | 
				
			||||||
 | 
					golangci-lint run
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Format code
 | 
				
			||||||
 | 
					go fmt ./...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Vet code
 | 
				
			||||||
 | 
					go vet ./...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Check dependencies
 | 
				
			||||||
 | 
					go mod tidy
 | 
				
			||||||
 | 
					go mod verify
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Development Workflow
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					# Live reload during development
 | 
				
			||||||
 | 
					air
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Build and test
 | 
				
			||||||
 | 
					go build ./... && go test ./...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Install locally
 | 
				
			||||||
 | 
					go install ./cmd/hosts
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Platform-Specific Considerations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### macOS
 | 
				
			||||||
 | 
					- **Hosts file location**: `/etc/hosts`
 | 
				
			||||||
 | 
					- **Permission model**: sudo required for editing
 | 
				
			||||||
 | 
					- **Terminal compatibility**: Terminal.app, iTerm2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Linux
 | 
				
			||||||
 | 
					- **Hosts file location**: `/etc/hosts`
 | 
				
			||||||
 | 
					- **Permission model**: sudo/root required for editing
 | 
				
			||||||
 | 
					- **Terminal compatibility**: GNOME Terminal, Konsole, xterm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Windows (Future)
 | 
				
			||||||
 | 
					- **Hosts file location**: `C:\Windows\System32\drivers\etc\hosts`
 | 
				
			||||||
 | 
					- **Permission model**: Administrator elevation required
 | 
				
			||||||
 | 
					- **Terminal compatibility**: Windows Terminal, Command Prompt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Performance Optimizations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Memory Management
 | 
				
			||||||
 | 
					- **Lazy loading**: Only load visible entries in large files
 | 
				
			||||||
 | 
					- **String interning**: Reuse common hostname strings
 | 
				
			||||||
 | 
					- **Garbage collection**: Minimize allocations in render loop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### UI Responsiveness
 | 
				
			||||||
 | 
					- **Background processing**: DNS resolution in goroutines
 | 
				
			||||||
 | 
					- **Debounced updates**: Batch rapid state changes
 | 
				
			||||||
 | 
					- **Efficient rendering**: Only update changed UI components
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### File Operations
 | 
				
			||||||
 | 
					- **Streaming parser**: Handle large files without full memory load
 | 
				
			||||||
 | 
					- **Atomic writes**: Prevent corruption during updates
 | 
				
			||||||
 | 
					- **Change detection**: Only write when modifications exist
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Debugging & Profiling
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Debug Build
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					# Build with debug symbols
 | 
				
			||||||
 | 
					go build -gcflags="all=-N -l" ./cmd/hosts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Run with debug logging
 | 
				
			||||||
 | 
					DEBUG=1 ./hosts
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Profiling
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					# CPU profiling
 | 
				
			||||||
 | 
					go test -cpuprofile=cpu.prof -bench=.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Memory profiling  
 | 
				
			||||||
 | 
					go test -memprofile=mem.prof -bench=.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Analyze profiles
 | 
				
			||||||
 | 
					go tool pprof cpu.prof
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Common Debug Patterns
 | 
				
			||||||
 | 
					- **TUI debugging**: Log to file instead of stdout
 | 
				
			||||||
 | 
					- **State inspection**: JSON marshal model state
 | 
				
			||||||
 | 
					- **Event tracing**: Log all Bubble Tea messages
 | 
				
			||||||
							
								
								
									
										398
									
								
								tests/models_test.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										398
									
								
								tests/models_test.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,398 @@
 | 
				
			||||||
 | 
					package tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"hosts-go/internal/core"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/require"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNewHostEntry(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name        string
 | 
				
			||||||
 | 
							ip          string
 | 
				
			||||||
 | 
							hostname    string
 | 
				
			||||||
 | 
							expectError bool
 | 
				
			||||||
 | 
							errorMsg    string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "valid IPv4 entry",
 | 
				
			||||||
 | 
								ip:          "192.168.1.1",
 | 
				
			||||||
 | 
								hostname:    "example.com",
 | 
				
			||||||
 | 
								expectError: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "valid IPv6 entry",
 | 
				
			||||||
 | 
								ip:          "2001:db8::1",
 | 
				
			||||||
 | 
								hostname:    "example.com",
 | 
				
			||||||
 | 
								expectError: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "empty IP",
 | 
				
			||||||
 | 
								ip:          "",
 | 
				
			||||||
 | 
								hostname:    "example.com",
 | 
				
			||||||
 | 
								expectError: true,
 | 
				
			||||||
 | 
								errorMsg:    "IP address cannot be empty",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "empty hostname",
 | 
				
			||||||
 | 
								ip:          "192.168.1.1",
 | 
				
			||||||
 | 
								hostname:    "",
 | 
				
			||||||
 | 
								expectError: true,
 | 
				
			||||||
 | 
								errorMsg:    "hostname cannot be empty",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "invalid IP",
 | 
				
			||||||
 | 
								ip:          "999.999.999.999",
 | 
				
			||||||
 | 
								hostname:    "example.com",
 | 
				
			||||||
 | 
								expectError: true,
 | 
				
			||||||
 | 
								errorMsg:    "invalid IP address",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "invalid hostname",
 | 
				
			||||||
 | 
								ip:          "192.168.1.1",
 | 
				
			||||||
 | 
								hostname:    "-invalid.com",
 | 
				
			||||||
 | 
								expectError: true,
 | 
				
			||||||
 | 
								errorMsg:    "hostname cannot start or end with hyphen",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tt := range tests {
 | 
				
			||||||
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								entry, err := core.NewHostEntry(tt.ip, tt.hostname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if tt.expectError {
 | 
				
			||||||
 | 
									assert.Error(t, err)
 | 
				
			||||||
 | 
									assert.Contains(t, err.Error(), tt.errorMsg)
 | 
				
			||||||
 | 
									assert.Nil(t, entry)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									assert.NoError(t, err)
 | 
				
			||||||
 | 
									assert.NotNil(t, entry)
 | 
				
			||||||
 | 
									assert.Equal(t, tt.ip, entry.IP)
 | 
				
			||||||
 | 
									assert.Equal(t, tt.hostname, entry.Hostname)
 | 
				
			||||||
 | 
									assert.True(t, entry.Active)
 | 
				
			||||||
 | 
									assert.Empty(t, entry.Aliases)
 | 
				
			||||||
 | 
									assert.Empty(t, entry.Comment)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestHostEntry_AddAlias(t *testing.T) {
 | 
				
			||||||
 | 
						entry, err := core.NewHostEntry("192.168.1.1", "example.com")
 | 
				
			||||||
 | 
						require.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name        string
 | 
				
			||||||
 | 
							alias       string
 | 
				
			||||||
 | 
							expectError bool
 | 
				
			||||||
 | 
							errorMsg    string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "valid alias",
 | 
				
			||||||
 | 
								alias:       "www.example.com",
 | 
				
			||||||
 | 
								expectError: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "another valid alias",
 | 
				
			||||||
 | 
								alias:       "api.example.com",
 | 
				
			||||||
 | 
								expectError: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "duplicate alias",
 | 
				
			||||||
 | 
								alias:       "www.example.com",
 | 
				
			||||||
 | 
								expectError: true,
 | 
				
			||||||
 | 
								errorMsg:    "alias 'www.example.com' already exists",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "invalid alias",
 | 
				
			||||||
 | 
								alias:       "-invalid.com",
 | 
				
			||||||
 | 
								expectError: true,
 | 
				
			||||||
 | 
								errorMsg:    "invalid alias",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tt := range tests {
 | 
				
			||||||
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								err := entry.AddAlias(tt.alias)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if tt.expectError {
 | 
				
			||||||
 | 
									assert.Error(t, err)
 | 
				
			||||||
 | 
									assert.Contains(t, err.Error(), tt.errorMsg)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									assert.NoError(t, err)
 | 
				
			||||||
 | 
									assert.Contains(t, entry.Aliases, tt.alias)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestHostEntry_AllHostnames(t *testing.T) {
 | 
				
			||||||
 | 
						entry, err := core.NewHostEntry("192.168.1.1", "example.com")
 | 
				
			||||||
 | 
						require.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Test with no aliases
 | 
				
			||||||
 | 
						hostnames := entry.AllHostnames()
 | 
				
			||||||
 | 
						assert.Equal(t, []string{"example.com"}, hostnames)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Add aliases and test
 | 
				
			||||||
 | 
						require.NoError(t, entry.AddAlias("www.example.com"))
 | 
				
			||||||
 | 
						require.NoError(t, entry.AddAlias("api.example.com"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hostnames = entry.AllHostnames()
 | 
				
			||||||
 | 
						expected := []string{"example.com", "www.example.com", "api.example.com"}
 | 
				
			||||||
 | 
						assert.Equal(t, expected, hostnames)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestHostEntry_String(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name     string
 | 
				
			||||||
 | 
							setup    func() *core.HostEntry
 | 
				
			||||||
 | 
							expected string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "basic entry",
 | 
				
			||||||
 | 
								setup: func() *core.HostEntry {
 | 
				
			||||||
 | 
									entry, _ := core.NewHostEntry("192.168.1.1", "example.com")
 | 
				
			||||||
 | 
									return entry
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expected: "192.168.1.1\texample.com",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "entry with comment",
 | 
				
			||||||
 | 
								setup: func() *core.HostEntry {
 | 
				
			||||||
 | 
									entry, _ := core.NewHostEntry("192.168.1.1", "example.com")
 | 
				
			||||||
 | 
									entry.Comment = "development server"
 | 
				
			||||||
 | 
									return entry
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expected: "192.168.1.1\texample.com\t# development server",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "entry with aliases",
 | 
				
			||||||
 | 
								setup: func() *core.HostEntry {
 | 
				
			||||||
 | 
									entry, _ := core.NewHostEntry("192.168.1.1", "example.com")
 | 
				
			||||||
 | 
									entry.AddAlias("www.example.com")
 | 
				
			||||||
 | 
									entry.AddAlias("api.example.com")
 | 
				
			||||||
 | 
									return entry
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expected: "192.168.1.1\texample.com\twww.example.com\tapi.example.com",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "inactive entry",
 | 
				
			||||||
 | 
								setup: func() *core.HostEntry {
 | 
				
			||||||
 | 
									entry, _ := core.NewHostEntry("192.168.1.1", "example.com")
 | 
				
			||||||
 | 
									entry.Active = false
 | 
				
			||||||
 | 
									return entry
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expected: "# 192.168.1.1\texample.com",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "complete entry",
 | 
				
			||||||
 | 
								setup: func() *core.HostEntry {
 | 
				
			||||||
 | 
									entry, _ := core.NewHostEntry("192.168.1.1", "example.com")
 | 
				
			||||||
 | 
									entry.AddAlias("www.example.com")
 | 
				
			||||||
 | 
									entry.Comment = "test server"
 | 
				
			||||||
 | 
									return entry
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expected: "192.168.1.1\texample.com\twww.example.com\t# test server",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tt := range tests {
 | 
				
			||||||
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								entry := tt.setup()
 | 
				
			||||||
 | 
								result := entry.String()
 | 
				
			||||||
 | 
								assert.Equal(t, tt.expected, result)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestHostsFile_AddEntry(t *testing.T) {
 | 
				
			||||||
 | 
						hostsFile := core.NewHostsFile()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						entry, err := core.NewHostEntry("192.168.1.1", "example.com")
 | 
				
			||||||
 | 
						require.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = hostsFile.AddEntry(entry)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.Len(t, hostsFile.Entries, 1)
 | 
				
			||||||
 | 
						assert.Equal(t, entry, hostsFile.Entries[0])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestHostsFile_FindEntry(t *testing.T) {
 | 
				
			||||||
 | 
						hostsFile := core.NewHostsFile()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						entry1, _ := core.NewHostEntry("192.168.1.1", "example.com")
 | 
				
			||||||
 | 
						entry1.AddAlias("www.example.com")
 | 
				
			||||||
 | 
						entry2, _ := core.NewHostEntry("192.168.1.2", "test.com")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hostsFile.AddEntry(entry1)
 | 
				
			||||||
 | 
						hostsFile.AddEntry(entry2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name     string
 | 
				
			||||||
 | 
							hostname string
 | 
				
			||||||
 | 
							expected *core.HostEntry
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "find by primary hostname",
 | 
				
			||||||
 | 
								hostname: "example.com",
 | 
				
			||||||
 | 
								expected: entry1,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "find by alias",
 | 
				
			||||||
 | 
								hostname: "www.example.com",
 | 
				
			||||||
 | 
								expected: entry1,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "find second entry",
 | 
				
			||||||
 | 
								hostname: "test.com",
 | 
				
			||||||
 | 
								expected: entry2,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "not found",
 | 
				
			||||||
 | 
								hostname: "notfound.com",
 | 
				
			||||||
 | 
								expected: nil,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tt := range tests {
 | 
				
			||||||
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								result := hostsFile.FindEntry(tt.hostname)
 | 
				
			||||||
 | 
								assert.Equal(t, tt.expected, result)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestHostsFile_RemoveEntry(t *testing.T) {
 | 
				
			||||||
 | 
						hostsFile := core.NewHostsFile()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						entry1, _ := core.NewHostEntry("192.168.1.1", "example.com")
 | 
				
			||||||
 | 
						entry1.AddAlias("www.example.com")
 | 
				
			||||||
 | 
						entry2, _ := core.NewHostEntry("192.168.1.2", "test.com")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hostsFile.AddEntry(entry1)
 | 
				
			||||||
 | 
						hostsFile.AddEntry(entry2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name           string
 | 
				
			||||||
 | 
							hostname       string
 | 
				
			||||||
 | 
							expectedResult bool
 | 
				
			||||||
 | 
							remainingCount int
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:           "remove by primary hostname",
 | 
				
			||||||
 | 
								hostname:       "example.com",
 | 
				
			||||||
 | 
								expectedResult: true,
 | 
				
			||||||
 | 
								remainingCount: 1,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:           "remove non-existent",
 | 
				
			||||||
 | 
								hostname:       "notfound.com",
 | 
				
			||||||
 | 
								expectedResult: false,
 | 
				
			||||||
 | 
								remainingCount: 1,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tt := range tests {
 | 
				
			||||||
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								result := hostsFile.RemoveEntry(tt.hostname)
 | 
				
			||||||
 | 
								assert.Equal(t, tt.expectedResult, result)
 | 
				
			||||||
 | 
								assert.Len(t, hostsFile.Entries, tt.remainingCount)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestHostsFile_ActiveEntries(t *testing.T) {
 | 
				
			||||||
 | 
						hostsFile := core.NewHostsFile()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						entry1, _ := core.NewHostEntry("192.168.1.1", "example.com")
 | 
				
			||||||
 | 
						entry2, _ := core.NewHostEntry("192.168.1.2", "test.com")
 | 
				
			||||||
 | 
						entry2.Active = false // Inactive entry
 | 
				
			||||||
 | 
						entry3, _ := core.NewHostEntry("192.168.1.3", "active.com")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hostsFile.AddEntry(entry1)
 | 
				
			||||||
 | 
						hostsFile.AddEntry(entry2)
 | 
				
			||||||
 | 
						hostsFile.AddEntry(entry3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						activeEntries := hostsFile.ActiveEntries()
 | 
				
			||||||
 | 
						assert.Len(t, activeEntries, 2)
 | 
				
			||||||
 | 
						assert.Contains(t, activeEntries, entry1)
 | 
				
			||||||
 | 
						assert.Contains(t, activeEntries, entry3)
 | 
				
			||||||
 | 
						assert.NotContains(t, activeEntries, entry2)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestValidateHostname(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name        string
 | 
				
			||||||
 | 
							hostname    string
 | 
				
			||||||
 | 
							expectError bool
 | 
				
			||||||
 | 
							errorMsg    string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "valid simple hostname",
 | 
				
			||||||
 | 
								hostname:    "example",
 | 
				
			||||||
 | 
								expectError: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "valid domain",
 | 
				
			||||||
 | 
								hostname:    "example.com",
 | 
				
			||||||
 | 
								expectError: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "valid subdomain",
 | 
				
			||||||
 | 
								hostname:    "www.example.com",
 | 
				
			||||||
 | 
								expectError: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "valid with numbers",
 | 
				
			||||||
 | 
								hostname:    "server1.example.com",
 | 
				
			||||||
 | 
								expectError: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "valid with hyphens",
 | 
				
			||||||
 | 
								hostname:    "api-server.example.com",
 | 
				
			||||||
 | 
								expectError: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "empty hostname",
 | 
				
			||||||
 | 
								hostname:    "",
 | 
				
			||||||
 | 
								expectError: true,
 | 
				
			||||||
 | 
								errorMsg:    "hostname cannot be empty",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "starts with hyphen",
 | 
				
			||||||
 | 
								hostname:    "-invalid.com",
 | 
				
			||||||
 | 
								expectError: true,
 | 
				
			||||||
 | 
								errorMsg:    "hostname cannot start or end with hyphen",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "ends with hyphen",
 | 
				
			||||||
 | 
								hostname:    "invalid-.com",
 | 
				
			||||||
 | 
								expectError: true,
 | 
				
			||||||
 | 
								errorMsg:    "hostname cannot start or end with hyphen",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:        "too long hostname",
 | 
				
			||||||
 | 
								hostname:    string(make([]byte, 255)), // 255 characters
 | 
				
			||||||
 | 
								expectError: true,
 | 
				
			||||||
 | 
								errorMsg:    "hostname too long",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tt := range tests {
 | 
				
			||||||
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								// Test through NewHostEntry which calls validateHostname
 | 
				
			||||||
 | 
								_, err := core.NewHostEntry("192.168.1.1", tt.hostname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if tt.expectError {
 | 
				
			||||||
 | 
									assert.Error(t, err)
 | 
				
			||||||
 | 
									assert.Contains(t, err.Error(), tt.errorMsg)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									assert.NoError(t, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue