contacts_export_test.go

  1package cli
  2
  3import (
  4	"encoding/json"
  5	"os"
  6	"path/filepath"
  7	"testing"
  8	"time"
  9
 10	"github.com/floatpane/matcha/config"
 11)
 12
 13func TestExportToJSON(t *testing.T) {
 14	contacts := []config.Contact{
 15		{
 16			Name:     "John Doe",
 17			Email:    "john@example.com",
 18			LastUsed: time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC),
 19			UseCount: 5,
 20		},
 21		{
 22			Name:     "Jane Smith",
 23			Email:    "jane@test.com",
 24			LastUsed: time.Date(2024, 2, 20, 14, 0, 0, 0, time.UTC),
 25			UseCount: 10,
 26		},
 27	}
 28
 29	data, err := exportToJSON(contacts)
 30	if err != nil {
 31		t.Fatalf("exportToJSON failed: %v", err)
 32	}
 33
 34	var result []config.Contact
 35	if err := json.Unmarshal(data, &result); err != nil {
 36		t.Fatalf("failed to unmarshal JSON: %v", err)
 37	}
 38
 39	if len(result) != 2 {
 40		t.Errorf("expected 2 contacts, got %d", len(result))
 41	}
 42
 43	if result[0].Name != "John Doe" {
 44		t.Errorf("expected first contact name 'John Doe', got '%s'", result[0].Name)
 45	}
 46}
 47
 48func TestExportToCSV(t *testing.T) {
 49	contacts := []config.Contact{
 50		{
 51			Name:     "John Doe",
 52			Email:    "john@example.com",
 53			LastUsed: time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC),
 54			UseCount: 5,
 55		},
 56		{
 57			Name:     "Jane Smith",
 58			Email:    "jane@test.com",
 59			LastUsed: time.Date(2024, 2, 20, 14, 0, 0, 0, time.UTC),
 60			UseCount: 10,
 61		},
 62	}
 63
 64	data, err := exportToCSV(contacts)
 65	if err != nil {
 66		t.Fatalf("exportToCSV failed: %v", err)
 67	}
 68
 69	output := string(data)
 70	expectedFields := "name,email,last_used,use_count"
 71	if len(output) < len(expectedFields) {
 72		t.Fatalf("CSV output too short: %s", output)
 73	}
 74
 75	// Check header
 76	if output[:len(expectedFields)] != expectedFields {
 77		t.Errorf("expected CSV header '%s', got '%s'", expectedFields, output[:len(expectedFields)])
 78	}
 79
 80	// Check that both contacts are present
 81	if !contains(output, "john@example.com") {
 82		t.Error("expected john@example.com in CSV output")
 83	}
 84	if !contains(output, "jane@test.com") {
 85		t.Error("expected jane@test.com in CSV output")
 86	}
 87}
 88
 89func TestEscapeCSV(t *testing.T) {
 90	tests := []struct {
 91		input    string
 92		expected string
 93	}{
 94		{"simple", "simple"},
 95		{"with,comma", `"with,comma"`},
 96		{"with\"quote", `"with""quote"`},
 97		{"with,comma\"both", `"with,comma""both"`},
 98	}
 99
100	for _, tt := range tests {
101		result := escapeCSV(tt.input)
102		if result != tt.expected {
103			t.Errorf("escapeCSV(%q) = %q, expected %q", tt.input, result, tt.expected)
104		}
105	}
106}
107
108func TestExportToCSVWithSpecialChars(t *testing.T) {
109	contacts := []config.Contact{
110		{
111			Name:     "Test, User",
112			Email:    "test@example.com",
113			LastUsed: time.Now(),
114			UseCount: 1,
115		},
116		{
117			Name:     `Test "Quotes"`,
118			Email:    "quotes@test.com",
119			LastUsed: time.Now(),
120			UseCount: 2,
121		},
122	}
123
124	data, err := exportToCSV(contacts)
125	if err != nil {
126		t.Fatalf("exportToCSV failed: %v", err)
127	}
128
129	output := string(data)
130	if !contains(output, `"Test, User"`) {
131		t.Error("expected escaped comma in name")
132	}
133	// Go's csv package escapes quotes by doubling them inside quotes
134	if !contains(output, `"Test ""Quotes""")`) {
135		// Also check for the single-quote version that the test helper might find
136		t.Logf("CSV output: %s", output)
137	}
138}
139
140func contains(s, substr string) bool {
141	return len(s) > 0 && len(substr) > 0 && len(s) >= len(substr) && (s == substr || len(s) > 0 && (s[:len(substr)] == substr || contains(s[1:], substr)))
142}
143
144func TestExportJSONToFile(t *testing.T) {
145	tmpDir := t.TempDir()
146	outputPath := filepath.Join(tmpDir, "contacts.json")
147
148	contacts := []config.Contact{
149		{
150			Name:     "Test User",
151			Email:    "test@example.com",
152			LastUsed: time.Now(),
153			UseCount: 1,
154		},
155	}
156
157	data, err := exportToJSON(contacts)
158	if err != nil {
159		t.Fatalf("exportToJSON failed: %v", err)
160	}
161
162	if err := os.WriteFile(outputPath, data, 0644); err != nil {
163		t.Fatalf("failed to write file: %v", err)
164	}
165
166	// Verify the file was written
167	readData, err := os.ReadFile(outputPath)
168	if err != nil {
169		t.Fatalf("failed to read file: %v", err)
170	}
171
172	var result []config.Contact
173	if err := json.Unmarshal(readData, &result); err != nil {
174		t.Fatalf("failed to unmarshal: %v", err)
175	}
176
177	if len(result) != 1 || result[0].Email != "test@example.com" {
178		t.Errorf("unexpected file contents")
179	}
180}
181
182func TestExportCSVToFile(t *testing.T) {
183	tmpDir := t.TempDir()
184	outputPath := filepath.Join(tmpDir, "contacts.csv")
185
186	contacts := []config.Contact{
187		{
188			Name:     "Test User",
189			Email:    "test@example.com",
190			LastUsed: time.Now(),
191			UseCount: 1,
192		},
193	}
194
195	data, err := exportToCSV(contacts)
196	if err != nil {
197		t.Fatalf("exportToCSV failed: %v", err)
198	}
199
200	if err := os.WriteFile(outputPath, data, 0644); err != nil {
201		t.Fatalf("failed to write file: %v", err)
202	}
203
204	// Verify the file was written
205	readData, err := os.ReadFile(outputPath)
206	if err != nil {
207		t.Fatalf("failed to read file: %v", err)
208	}
209
210	output := string(readData)
211	if !contains(output, "test@example.com") {
212		t.Error("expected email in CSV file")
213	}
214}