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) } }) } }