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