fetcher_test.go

  1package fetcher
  2
  3import (
  4	"bytes"
  5	"strings"
  6	"testing"
  7
  8	"github.com/floatpane/matcha/config"
  9)
 10
 11type testPartHeader map[string]string
 12
 13func (h testPartHeader) Add(key, value string) {
 14	h[key] = value
 15}
 16
 17func (h testPartHeader) Del(key string) {
 18	delete(h, key)
 19}
 20
 21func (h testPartHeader) Get(key string) string {
 22	return h[key]
 23}
 24
 25func (h testPartHeader) Set(key, value string) {
 26	h[key] = value
 27}
 28
 29func TestDecodePartUsesCharsetWhenContentTypeIsMalformed(t *testing.T) {
 30	header := testPartHeader{}
 31	header.Set("Content-Type", "text/plain; charset=iso-8859-1; broken")
 32
 33	decoded, err := decodePart(bytes.NewReader([]byte{0x63, 0x61, 0x66, 0xe9}), header)
 34	if err != nil {
 35		t.Fatalf("decodePart() returned error: %v", err)
 36	}
 37
 38	if decoded != "café" {
 39		t.Fatalf("decodePart() = %q, want %q", decoded, "café")
 40	}
 41}
 42
 43func TestDecodePartFallsBackToUTF8WhenMalformedContentTypeHasNoCharset(t *testing.T) {
 44	header := testPartHeader{}
 45	header.Set("Content-Type", "text/plain; broken")
 46
 47	decoded, err := decodePart(strings.NewReader("hello"), header)
 48	if err != nil {
 49		t.Fatalf("decodePart() returned error: %v", err)
 50	}
 51
 52	if decoded != "hello" {
 53		t.Fatalf("decodePart() = %q, want %q", decoded, "hello")
 54	}
 55}
 56
 57// TestFetchEmails is an integration test that requires a live IMAP server and valid credentials.
 58// NOTE: This test will be skipped if it cannot load a configuration file,
 59// making it safe to run in a CI environment without credentials.
 60// To run this test locally, ensure you have a valid `config.json` file.
 61func TestFetchEmails(t *testing.T) {
 62	// Attempt to load the configuration.
 63	cfg, err := config.LoadConfig()
 64	if err != nil {
 65		// If config doesn't exist, skip the test. This is useful for CI environments.
 66		t.Skipf("Skipping TestFetchEmails: could not load config: %v", err)
 67	}
 68
 69	// Check if there are any accounts configured
 70	if !cfg.HasAccounts() {
 71		t.Skip("Skipping TestFetchEmails: no accounts configured.")
 72	}
 73
 74	// Get the first account
 75	account := cfg.GetFirstAccount()
 76	if account == nil {
 77		t.Skip("Skipping TestFetchEmails: no accounts available.")
 78	}
 79
 80	// If the password is a placeholder, skip the test to avoid failed auth attempts.
 81	if account.Password == "" || account.Password == "supersecret" {
 82		t.Skip("Skipping TestFetchEmails: placeholder or empty password found in config.")
 83	}
 84
 85	emails, err := FetchEmails(account, 10, 10)
 86	if err != nil {
 87		t.Fatalf("FetchEmails() failed with error: %v", err)
 88	}
 89
 90	if len(emails) == 0 {
 91		// This is not necessarily a failure, but we can log it.
 92		t.Log("FetchEmails() returned 0 emails. This might be expected.")
 93	}
 94
 95	// Check that the emails are sorted from newest to oldest.
 96	// Skip emails with zero/invalid dates when checking sort order.
 97	if len(emails) > 1 {
 98		var validEmails []Email
 99		for _, e := range emails {
100			if !e.Date.IsZero() {
101				validEmails = append(validEmails, e)
102			}
103		}
104		if len(validEmails) > 1 {
105			if validEmails[0].Date.Before(validEmails[len(validEmails)-1].Date) {
106				t.Error("Emails do not appear to be sorted from newest to oldest.")
107			}
108		}
109	}
110
111	// Check a sample email for expected content.
112	for _, email := range emails {
113		if email.Subject == "" && email.From == "" {
114			t.Errorf("Fetched email has empty subject and from fields: %+v", email)
115		}
116	}
117
118	// Verify that AccountID is set on fetched emails
119	for _, email := range emails {
120		if email.AccountID != account.ID {
121			t.Errorf("Expected AccountID %s, got %s", account.ID, email.AccountID)
122		}
123	}
124}
125
126// TestFetchEmailsWithCustomServer tests fetching with a custom server configuration.
127// This test is skipped unless a custom account is configured.
128func TestFetchEmailsWithCustomServer(t *testing.T) {
129	// Attempt to load the configuration.
130	cfg, err := config.LoadConfig()
131	if err != nil {
132		t.Skipf("Skipping TestFetchEmailsWithCustomServer: could not load config: %v", err)
133	}
134
135	// Look for a custom account
136	var customAccount *config.Account
137	for i := range cfg.Accounts {
138		if cfg.Accounts[i].ServiceProvider == "custom" {
139			customAccount = &cfg.Accounts[i]
140			break
141		}
142	}
143
144	if customAccount == nil {
145		t.Skip("Skipping TestFetchEmailsWithCustomServer: no custom account configured.")
146	}
147
148	if customAccount.Password == "" || customAccount.Password == "supersecret" {
149		t.Skip("Skipping TestFetchEmailsWithCustomServer: placeholder or empty password found.")
150	}
151
152	if customAccount.IMAPServer == "" {
153		t.Skip("Skipping TestFetchEmailsWithCustomServer: no IMAP server configured.")
154	}
155
156	emails, err := FetchEmails(customAccount, 5, 0)
157	if err != nil {
158		t.Fatalf("FetchEmails() with custom server failed: %v", err)
159	}
160
161	t.Logf("Fetched %d emails from custom server %s", len(emails), customAccount.IMAPServer)
162}