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}