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