config_test.go

  1package config
  2
  3import (
  4	"reflect"
  5	"testing"
  6
  7	"github.com/zalando/go-keyring"
  8)
  9
 10// TestSaveAndLoadConfig verifies that the config can be saved to and loaded from a file correctly.
 11func TestSaveAndLoadConfig(t *testing.T) {
 12	// Use an in-memory mock keyring so tests do not interact with the host OS keyring
 13	keyring.MockInit()
 14
 15	// Create a temporary directory for the test to avoid interfering with actual user config.
 16	tempDir := t.TempDir()
 17
 18	// Temporarily override the user home directory to our temp directory.
 19	// This ensures that our config file is written to a predictable, temporary location.
 20	t.Setenv("HOME", tempDir)
 21
 22	// Define a sample configuration to save with multiple accounts.
 23	expectedConfig := &Config{
 24		Accounts: []Account{
 25			{
 26				ID:              "test-id-1",
 27				Name:            "Test User",
 28				Email:           "test@example.com",
 29				Password:        "supersecret",
 30				ServiceProvider: "gmail",
 31			},
 32			{
 33				ID:              "test-id-2",
 34				Name:            "Custom User",
 35				Email:           "custom@example.com",
 36				Password:        "customsecret",
 37				ServiceProvider: "custom",
 38				IMAPServer:      "imap.custom.com",
 39				IMAPPort:        993,
 40				SMTPServer:      "smtp.custom.com",
 41				SMTPPort:        587,
 42			},
 43		},
 44	}
 45
 46	// Attempt to save the configuration.
 47	err := SaveConfig(expectedConfig)
 48	if err != nil {
 49		t.Fatalf("SaveConfig() failed: %v", err)
 50	}
 51
 52	// Attempt to load the configuration back.
 53	loadedConfig, err := LoadConfig()
 54	if err != nil {
 55		t.Fatalf("LoadConfig() failed: %v", err)
 56	}
 57
 58	// Compare the loaded configuration with the original one.
 59	// reflect.DeepEqual is used for a deep comparison of the structs.
 60	if !reflect.DeepEqual(loadedConfig, expectedConfig) {
 61		t.Errorf("Loaded config does not match expected config.\nGot:  %+v\nWant: %+v", loadedConfig, expectedConfig)
 62	}
 63}
 64
 65// TestAccountGetIMAPServer tests the logic that determines the IMAP server address.
 66func TestAccountGetIMAPServer(t *testing.T) {
 67	testCases := []struct {
 68		name    string
 69		account Account
 70		want    string
 71	}{
 72		{"Gmail", Account{ServiceProvider: "gmail"}, "imap.gmail.com"},
 73		{"iCloud", Account{ServiceProvider: "icloud"}, "imap.mail.me.com"},
 74		{"Custom", Account{ServiceProvider: "custom", IMAPServer: "imap.custom.com"}, "imap.custom.com"},
 75		{"Unsupported", Account{ServiceProvider: "yahoo"}, ""},
 76		{"Empty", Account{ServiceProvider: ""}, ""},
 77	}
 78
 79	for _, tc := range testCases {
 80		t.Run(tc.name, func(t *testing.T) {
 81			got := tc.account.GetIMAPServer()
 82			if got != tc.want {
 83				t.Errorf("GetIMAPServer() = %q, want %q", got, tc.want)
 84			}
 85		})
 86	}
 87}
 88
 89// TestAccountGetSMTPServer tests the logic that determines the SMTP server address.
 90func TestAccountGetSMTPServer(t *testing.T) {
 91	testCases := []struct {
 92		name    string
 93		account Account
 94		want    string
 95	}{
 96		{"Gmail", Account{ServiceProvider: "gmail"}, "smtp.gmail.com"},
 97		{"iCloud", Account{ServiceProvider: "icloud"}, "smtp.mail.me.com"},
 98		{"Custom", Account{ServiceProvider: "custom", SMTPServer: "smtp.custom.com"}, "smtp.custom.com"},
 99		{"Unsupported", Account{ServiceProvider: "yahoo"}, ""},
100		{"Empty", Account{ServiceProvider: ""}, ""},
101	}
102
103	for _, tc := range testCases {
104		t.Run(tc.name, func(t *testing.T) {
105			got := tc.account.GetSMTPServer()
106			if got != tc.want {
107				t.Errorf("GetSMTPServer() = %q, want %q", got, tc.want)
108			}
109		})
110	}
111}
112
113// TestConfigAddRemoveAccount tests adding and removing accounts from config.
114func TestConfigAddRemoveAccount(t *testing.T) {
115	// Use an in-memory mock keyring to test the deletion step cleanly
116	keyring.MockInit()
117
118	cfg := &Config{}
119
120	// Add an account
121	account := Account{
122		Name:            "Test",
123		Email:           "test@example.com",
124		ServiceProvider: "gmail",
125	}
126	cfg.AddAccount(account)
127
128	if len(cfg.Accounts) != 1 {
129		t.Fatalf("Expected 1 account, got %d", len(cfg.Accounts))
130	}
131
132	// Check that ID was auto-generated
133	if cfg.Accounts[0].ID == "" {
134		t.Error("Expected account ID to be auto-generated")
135	}
136
137	// Remove the account
138	accountID := cfg.Accounts[0].ID
139	removed := cfg.RemoveAccount(accountID)
140	if !removed {
141		t.Error("RemoveAccount should return true when account exists")
142	}
143
144	if len(cfg.Accounts) != 0 {
145		t.Fatalf("Expected 0 accounts after removal, got %d", len(cfg.Accounts))
146	}
147
148	// Try to remove non-existent account
149	removed = cfg.RemoveAccount("non-existent")
150	if removed {
151		t.Error("RemoveAccount should return false for non-existent account")
152	}
153}
154
155// TestConfigGetAccountByID tests retrieving accounts by ID.
156func TestConfigGetAccountByID(t *testing.T) {
157	cfg := &Config{
158		Accounts: []Account{
159			{ID: "id-1", Email: "test1@example.com"},
160			{ID: "id-2", Email: "test2@example.com"},
161		},
162	}
163
164	account := cfg.GetAccountByID("id-1")
165	if account == nil {
166		t.Fatal("Expected to find account with id-1")
167	}
168	if account.Email != "test1@example.com" {
169		t.Errorf("Expected email test1@example.com, got %s", account.Email)
170	}
171
172	// Non-existent ID
173	account = cfg.GetAccountByID("non-existent")
174	if account != nil {
175		t.Error("Expected nil for non-existent account ID")
176	}
177}
178
179// TestConfigGetAccountByEmail tests retrieving accounts by email.
180func TestConfigGetAccountByEmail(t *testing.T) {
181	cfg := &Config{
182		Accounts: []Account{
183			{ID: "id-1", Email: "test1@example.com"},
184			{ID: "id-2", Email: "test2@example.com"},
185		},
186	}
187
188	account := cfg.GetAccountByEmail("test2@example.com")
189	if account == nil {
190		t.Fatal("Expected to find account with test2@example.com")
191	}
192	if account.ID != "id-2" {
193		t.Errorf("Expected ID id-2, got %s", account.ID)
194	}
195
196	// Non-existent email
197	account = cfg.GetAccountByEmail("nonexistent@example.com")
198	if account != nil {
199		t.Error("Expected nil for non-existent account email")
200	}
201}
202
203// TestConfigHasAccounts tests the HasAccounts method.
204func TestConfigHasAccounts(t *testing.T) {
205	cfg := &Config{}
206	if cfg.HasAccounts() {
207		t.Error("Expected HasAccounts to return false for empty config")
208	}
209
210	cfg.AddAccount(Account{Email: "test@example.com"})
211	if !cfg.HasAccounts() {
212		t.Error("Expected HasAccounts to return true after adding account")
213	}
214}
215
216// TestAccountGetPorts tests the port retrieval methods.
217func TestAccountGetPorts(t *testing.T) {
218	// Gmail account should use default ports
219	gmailAccount := Account{ServiceProvider: "gmail"}
220	if gmailAccount.GetIMAPPort() != 993 {
221		t.Errorf("Expected Gmail IMAP port 993, got %d", gmailAccount.GetIMAPPort())
222	}
223	if gmailAccount.GetSMTPPort() != 587 {
224		t.Errorf("Expected Gmail SMTP port 587, got %d", gmailAccount.GetSMTPPort())
225	}
226
227	// Custom account with custom ports
228	customAccount := Account{
229		ServiceProvider: "custom",
230		IMAPPort:        1993,
231		SMTPPort:        1587,
232	}
233	if customAccount.GetIMAPPort() != 1993 {
234		t.Errorf("Expected custom IMAP port 1993, got %d", customAccount.GetIMAPPort())
235	}
236	if customAccount.GetSMTPPort() != 1587 {
237		t.Errorf("Expected custom SMTP port 1587, got %d", customAccount.GetSMTPPort())
238	}
239
240	// Custom account with default ports (0 means use default)
241	customDefaultAccount := Account{ServiceProvider: "custom"}
242	if customDefaultAccount.GetIMAPPort() != 993 {
243		t.Errorf("Expected default IMAP port 993 for custom with no port, got %d", customDefaultAccount.GetIMAPPort())
244	}
245	if customDefaultAccount.GetSMTPPort() != 587 {
246		t.Errorf("Expected default SMTP port 587 for custom with no port, got %d", customDefaultAccount.GetSMTPPort())
247	}
248}