Skip to content

Commit 6fe2749

Browse files
rianjsclaude
andauthored
docs: add integration tests documentation (#17)
Port integration-tests.md from gmail-ro with updates for gro: - Update all commands from gmro to gro mail - Update config paths from ~/.config/gmail-ro/ to ~/.config/google-readonly/ - Add config commands section (show, test, clear) - Update temp directory paths from gmail-test to gro-test Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent fb17366 commit 6fe2749

1 file changed

Lines changed: 326 additions & 0 deletions

File tree

integration-tests.md

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
# Integration Tests
2+
3+
Comprehensive integration test suite for gro. Tests are designed to work against any active Gmail account with standard inbox content.
4+
5+
## Test Environment Setup
6+
7+
### Prerequisites
8+
- Valid OAuth credentials configured (`~/.config/google-readonly/credentials.json`)
9+
- OAuth token (stored in system keychain or `~/.config/google-readonly/token.json`)
10+
- Access to a Gmail account with:
11+
- At least some messages in the inbox
12+
- At least one email with attachments (for attachment tests)
13+
- At least one email thread with multiple messages
14+
15+
### Verification
16+
```bash
17+
ls ~/.config/google-readonly/credentials.json
18+
gro config show # Check configuration status
19+
gro mail search "is:inbox" --max 1 # Quick connectivity check
20+
```
21+
22+
---
23+
24+
## Version Command
25+
26+
| Test Case | Command | Expected Result |
27+
|-----------|---------|-----------------|
28+
| Print version | `gro --version` | Shows "gro <version>" |
29+
30+
---
31+
32+
## Config Commands
33+
34+
| Test Case | Command | Expected Result |
35+
|-----------|---------|-----------------|
36+
| Show config | `gro config show` | Shows credentials and token status |
37+
| Test connectivity | `gro config test` | Shows "Successfully connected to Gmail API" |
38+
| Clear token | `gro config clear` | Removes stored OAuth token |
39+
40+
---
41+
42+
## Search Operations
43+
44+
### Basic Search
45+
46+
| Test Case | Command | Expected Result |
47+
|-----------|---------|-----------------|
48+
| Search inbox | `gro mail search "is:inbox" --max 5` | Returns messages with ID, ThreadID, From, Subject, Date, Snippet |
49+
| Search with default limit | `gro mail search "is:inbox"` | Returns up to 10 messages (default) |
50+
| Custom result limit | `gro mail search "is:inbox" --max 3` | Returns exactly 3 messages |
51+
| JSON output | `gro mail search "is:inbox" --max 2 --json` | Valid JSON array with message objects |
52+
| No results | `gro mail search "xyznonexistent12345uniquequery67890"` | "No messages found." |
53+
| Search unread | `gro mail search "is:unread" --max 5` | Returns unread messages (if any) |
54+
| Search starred | `gro mail search "is:starred" --max 5` | Returns starred messages (if any) |
55+
56+
### Query Operators
57+
58+
| Test Case | Command | Expected Result |
59+
|-----------|---------|-----------------|
60+
| From filter | `gro mail search "from:noreply" --max 3` | Messages from addresses containing "noreply" |
61+
| Subject filter | `gro mail search "subject:welcome" --max 3` | Messages with "welcome" in subject |
62+
| Has attachment | `gro mail search "has:attachment" --max 3` | Messages with attachments |
63+
| Date range | `gro mail search "after:2024/01/01" --max 3` | Messages after date |
64+
| Combined query | `gro mail search "is:inbox has:attachment" --max 3` | Inbox messages with attachments |
65+
| Label filter | `gro mail search "label:inbox" --max 3` | Messages in inbox |
66+
67+
### JSON Validation
68+
69+
| Test Case | Command | Expected Result |
70+
|-----------|---------|-----------------|
71+
| JSON has required fields | `gro mail search "is:inbox" --max 1 --json \| jq '.[0] \| keys'` | Contains: id, threadId, from, subject, date, snippet |
72+
| JSON ID is string | `gro mail search "is:inbox" --max 1 --json \| jq -e '.[0].id \| type == "string"'` | Returns true |
73+
| JSON ThreadID present | `gro mail search "is:inbox" --max 1 --json \| jq -e '.[0].threadId != null'` | Returns true |
74+
75+
---
76+
77+
## Read Operations
78+
79+
### Read Single Message
80+
81+
| Test Case | Command | Expected Result |
82+
|-----------|---------|-----------------|
83+
| Read by ID | `MSG_ID=$(gro mail search "is:inbox" --max 1 --json \| jq -r '.[0].id'); gro mail read "$MSG_ID"` | Shows ID, From, To, Subject, Date, Body |
84+
| Read JSON output | `MSG_ID=$(gro mail search "is:inbox" --max 1 --json \| jq -r '.[0].id'); gro mail read "$MSG_ID" --json` | Valid JSON with body content |
85+
| Non-existent message | `gro mail read "0000000000000000"` | Error: 404 or "not found" |
86+
| Invalid message ID | `gro mail read "invalid-id-format"` | Error message |
87+
88+
### Read Content Verification
89+
90+
| Test Case | Command | Expected Result |
91+
|-----------|---------|-----------------|
92+
| Body included | `MSG_ID=$(gro mail search "is:inbox" --max 1 --json \| jq -r '.[0].id'); gro mail read "$MSG_ID" --json \| jq -e '.body != null'` | Returns true |
93+
| Headers present | `MSG_ID=$(gro mail search "is:inbox" --max 1 --json \| jq -r '.[0].id'); gro mail read "$MSG_ID"` | Output contains "From:", "To:", "Subject:", "Date:" |
94+
95+
---
96+
97+
## Thread Operations
98+
99+
### Thread by Thread ID
100+
101+
| Test Case | Command | Expected Result |
102+
|-----------|---------|-----------------|
103+
| View thread | `THREAD_ID=$(gro mail search "is:inbox" --max 1 --json \| jq -r '.[0].threadId'); gro mail thread "$THREAD_ID"` | Shows "Thread contains N message(s)" and all messages |
104+
| Thread JSON | `THREAD_ID=$(gro mail search "is:inbox" --max 1 --json \| jq -r '.[0].threadId'); gro mail thread "$THREAD_ID" --json` | Valid JSON array of messages |
105+
106+
### Thread by Message ID
107+
108+
| Test Case | Command | Expected Result |
109+
|-----------|---------|-----------------|
110+
| Thread from message ID | `MSG_ID=$(gro mail search "is:inbox" --max 1 --json \| jq -r '.[0].id'); gro mail thread "$MSG_ID"` | Shows thread containing that message |
111+
| Thread message count | `MSG_ID=$(gro mail search "is:inbox" --max 1 --json \| jq -r '.[0].id'); gro mail thread "$MSG_ID" --json \| jq 'length >= 1'` | Returns true (at least 1 message) |
112+
113+
### Error Cases
114+
115+
| Test Case | Command | Expected Result |
116+
|-----------|---------|-----------------|
117+
| Non-existent thread | `gro mail thread "0000000000000000"` | Error: 404 or "not found" |
118+
119+
---
120+
121+
## Labels Operations
122+
123+
### List Labels
124+
125+
| Test Case | Command | Expected Result |
126+
|-----------|---------|-----------------|
127+
| List all labels | `gro mail labels` | Shows NAME, TYPE, TOTAL, UNREAD columns |
128+
| Labels JSON output | `gro mail labels --json` | Valid JSON array with label objects |
129+
| Labels JSON has fields | `gro mail labels --json \| jq -e '.[0] \| has("id", "name", "type")'` | Returns true |
130+
131+
### Label Display in Messages
132+
133+
| Test Case | Command | Expected Result |
134+
|-----------|---------|-----------------|
135+
| Search shows labels | `gro mail search "is:inbox" --max 1` | Output may include "Labels:" line if message has user labels |
136+
| Search shows categories | `gro mail search "category:updates" --max 1` | Output may include "Categories: updates" |
137+
| Search JSON has labels | `gro mail search "is:inbox" --max 1 --json \| jq '.[0] \| has("labels", "categories")'` | Returns true |
138+
| Read shows labels | `MSG_ID=$(gro mail search "is:inbox" --max 1 --json \| jq -r '.[0].id'); gro mail read "$MSG_ID"` | Output may include "Labels:" and "Categories:" lines |
139+
140+
### Label-Based Search
141+
142+
| Test Case | Command | Expected Result |
143+
|-----------|---------|-----------------|
144+
| Search by label | `gro mail search "label:inbox" --max 3` | Returns inbox messages |
145+
| Search by category | `gro mail search "category:updates" --max 3` | Returns updates category messages |
146+
| Exclude category | `gro mail search "is:inbox -category:promotions" --max 3` | Returns inbox excluding promotions |
147+
| Combined label search | `gro mail search "is:inbox -category:social -category:promotions" --max 3` | Inbox excluding social and promotions |
148+
149+
---
150+
151+
## Attachment Operations
152+
153+
### Setup: Find Message with Attachments
154+
```bash
155+
# Store a message ID with attachments for subsequent tests
156+
ATTACHMENT_MSG_ID=$(gro mail search "has:attachment" --max 1 --json | jq -r '.[0].id')
157+
```
158+
159+
### List Attachments
160+
161+
| Test Case | Command | Expected Result |
162+
|-----------|---------|-----------------|
163+
| List attachments | `gro mail attachments list "$ATTACHMENT_MSG_ID"` | Shows filename, type, size for each attachment |
164+
| List JSON | `gro mail attachments list "$ATTACHMENT_MSG_ID" --json` | Valid JSON array with attachment metadata |
165+
| No attachments | `MSG_ID=$(gro mail search "is:inbox -has:attachment" --max 1 --json \| jq -r '.[0].id'); gro mail attachments list "$MSG_ID"` | "No attachments found for message." |
166+
167+
### JSON Validation
168+
169+
| Test Case | Command | Expected Result |
170+
|-----------|---------|-----------------|
171+
| Attachment has filename | `gro mail attachments list "$ATTACHMENT_MSG_ID" --json \| jq -e '.[0].filename != null'` | Returns true |
172+
| Attachment has mimeType | `gro mail attachments list "$ATTACHMENT_MSG_ID" --json \| jq -e '.[0].mimeType != null'` | Returns true |
173+
| Attachment has size | `gro mail attachments list "$ATTACHMENT_MSG_ID" --json \| jq -e '.[0].size >= 0'` | Returns true |
174+
175+
### Download Attachments
176+
177+
| Test Case | Command | Expected Result |
178+
|-----------|---------|-----------------|
179+
| Download all (no flag) | `gro mail attachments download "$ATTACHMENT_MSG_ID"` | Error: "must specify --filename or --all" |
180+
| Download all | `gro mail attachments download "$ATTACHMENT_MSG_ID" --all -o /tmp/gro-test` | Downloads all attachments, shows "Downloaded: ..." |
181+
| Download specific file | `FILENAME=$(gro mail attachments list "$ATTACHMENT_MSG_ID" --json \| jq -r '.[0].filename'); gro mail attachments download "$ATTACHMENT_MSG_ID" -f "$FILENAME" -o /tmp/gro-test` | Downloads specific file |
182+
| Non-existent filename | `gro mail attachments download "$ATTACHMENT_MSG_ID" -f "nonexistent-file-12345.xyz"` | Error: "attachment not found" |
183+
| Verify file created | `ls /tmp/gro-test/` | Downloaded files exist |
184+
| Verify file size > 0 | `stat -f%z /tmp/gro-test/* \| head -1` (macOS) | Non-zero file size |
185+
186+
### Zip Extraction (if zip attachment available)
187+
188+
| Test Case | Command | Expected Result |
189+
|-----------|---------|-----------------|
190+
| Find zip attachment | `ZIP_MSG_ID=$(gro mail search "has:attachment filename:zip" --max 1 --json \| jq -r '.[0].id')` | Message ID or null |
191+
| Download and extract | `gro mail attachments download "$ZIP_MSG_ID" -f "*.zip" --extract -o /tmp/gro-zip-test` | Extracts to directory |
192+
| Verify extraction | `ls /tmp/gro-zip-test/*/` | Extracted files present |
193+
194+
---
195+
196+
## Error Handling
197+
198+
| Test Case | Command | Expected Result |
199+
|-----------|---------|-----------------|
200+
| Missing required arg (search) | `gro mail search` | Error: accepts 1 arg(s), received 0 |
201+
| Missing required arg (read) | `gro mail read` | Error: accepts 1 arg(s), received 0 |
202+
| Missing required arg (thread) | `gro mail thread` | Error: accepts 1 arg(s), received 0 |
203+
| Missing required arg (attachments list) | `gro mail attachments list` | Error: accepts 1 arg(s), received 0 |
204+
| Invalid subcommand | `gro invalid` | Error: unknown command |
205+
| Help flag | `gro --help` | Shows usage information |
206+
| Mail help | `gro mail --help` | Shows mail-specific help |
207+
| Search help | `gro mail search --help` | Shows search-specific help |
208+
209+
---
210+
211+
## Output Format Consistency
212+
213+
### Text Output Structure
214+
215+
| Command Type | Expected Fields |
216+
|--------------|-----------------|
217+
| Search | ID, ThreadID, From, Subject, Date, Labels (if any), Categories (if any), Snippet, separator (---) |
218+
| Read | ID, From, To, Subject, Date, Labels (if any), Categories (if any), "--- Body ---", body content |
219+
| Thread | "Thread contains N message(s)", per-message: "=== Message X of Y ===", ID, From, To, Subject, Date, Labels, Categories, body |
220+
| Labels | NAME, TYPE, TOTAL, UNREAD columns |
221+
| Attachments List | "Found N attachment(s):", numbered list with filename, Type, Size |
222+
223+
### JSON Schema Validation
224+
225+
| Type | Required Fields |
226+
|------|-----------------|
227+
| Search result | id, threadId, from, subject, date, snippet, labels, categories |
228+
| Message | id, threadId, from, to, subject, date, body, labels, categories |
229+
| Attachment | filename, mimeType, size, partId |
230+
| Label | id, name, type, messagesTotal, messagesUnread |
231+
232+
---
233+
234+
## End-to-End Workflows
235+
236+
### Workflow 1: Search -> Read -> Thread
237+
```bash
238+
# 1. Search for a message
239+
MSG_ID=$(gro mail search "is:inbox" --max 1 --json | jq -r '.[0].id')
240+
241+
# 2. Read the full message
242+
gro mail read "$MSG_ID"
243+
244+
# 3. View the full thread
245+
gro mail thread "$MSG_ID"
246+
```
247+
248+
### Workflow 2: Find and Download Attachments
249+
```bash
250+
# 1. Find message with attachments
251+
ATTACHMENT_MSG_ID=$(gro mail search "has:attachment" --max 1 --json | jq -r '.[0].id')
252+
253+
# 2. List attachments
254+
gro mail attachments list "$ATTACHMENT_MSG_ID"
255+
256+
# 3. Download all attachments
257+
gro mail attachments download "$ATTACHMENT_MSG_ID" --all -o /tmp/gro-attachments
258+
259+
# 4. Verify downloads
260+
ls -la /tmp/gro-attachments/
261+
```
262+
263+
### Workflow 3: JSON Pipeline
264+
```bash
265+
# Extract all From addresses from recent inbox messages
266+
gro mail search "is:inbox" --max 10 --json | jq -r '.[].from'
267+
268+
# Get message bodies from a thread
269+
THREAD_ID=$(gro mail search "is:inbox" --max 1 --json | jq -r '.[0].threadId')
270+
gro mail thread "$THREAD_ID" --json | jq -r '.[].body'
271+
```
272+
273+
---
274+
275+
## Test Execution Checklist
276+
277+
### Setup
278+
- [ ] Build latest: `make build`
279+
- [ ] Verify credentials exist: `ls ~/.config/google-readonly/credentials.json`
280+
- [ ] Quick connectivity test: `gro mail search "is:inbox" --max 1`
281+
282+
### Core Commands
283+
- [ ] `gro --version`
284+
- [ ] `gro config show`
285+
- [ ] `gro config test`
286+
- [ ] `gro mail search` with various queries
287+
- [ ] `gro mail read` by message ID
288+
- [ ] `gro mail thread` by thread ID
289+
- [ ] `gro mail thread` by message ID
290+
- [ ] `gro mail labels` (list all labels)
291+
292+
### Labels
293+
- [ ] `gro mail labels` text output
294+
- [ ] `gro mail labels --json` JSON output
295+
- [ ] Search by label/category
296+
- [ ] Labels/categories in message output
297+
298+
### Attachments
299+
- [ ] `gro mail attachments list`
300+
- [ ] `gro mail attachments download --all`
301+
- [ ] `gro mail attachments download --filename`
302+
- [ ] Zip extraction with `--extract`
303+
304+
### Output Formats
305+
- [ ] Text output for all commands
306+
- [ ] JSON output for all commands
307+
- [ ] JSON validates with jq
308+
309+
### Error Handling
310+
- [ ] Missing arguments
311+
- [ ] Invalid IDs
312+
- [ ] Non-existent resources
313+
314+
### Cleanup
315+
- [ ] Remove test downloads: `rm -rf /tmp/gro-test /tmp/gro-zip-test /tmp/gro-attachments`
316+
317+
---
318+
319+
## Adding New Tests
320+
321+
When adding new features or fixing bugs:
322+
323+
1. Add test cases to the appropriate section above
324+
2. Include both happy path and error cases
325+
3. Document any known limitations or edge cases
326+
4. Update the "Test Execution Checklist" if needed

0 commit comments

Comments
 (0)