|
| 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