inboxd 1.0.10 → 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/inbox-assistant/SKILL.md +99 -0
- package/CLAUDE.md +15 -0
- package/package.json +1 -1
- package/src/cli.js +364 -3
- package/src/gmail-monitor.js +364 -0
- package/src/sent-log.js +87 -0
- package/src/skill-installer.js +7 -2
- package/tests/gmail-monitor-patterns.test.js +232 -0
- package/tests/link-extraction.test.js +249 -0
- package/tests/older-than.test.js +127 -0
- package/tests/sent-log.test.js +142 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
|
|
5
|
+
// Test the logic patterns used by sent-log
|
|
6
|
+
// Similar structure to deletion-log.test.js
|
|
7
|
+
|
|
8
|
+
describe('Sent Log', () => {
|
|
9
|
+
const LOG_DIR = path.join(os.homedir(), '.config', 'inboxd');
|
|
10
|
+
const LOG_FILE = path.join(LOG_DIR, 'sent-log.json');
|
|
11
|
+
|
|
12
|
+
describe('Log entry structure', () => {
|
|
13
|
+
it('should have required fields for sent email tracking', () => {
|
|
14
|
+
const logEntry = {
|
|
15
|
+
sentAt: '2026-01-03T15:45:00.000Z',
|
|
16
|
+
account: 'dparedesi@uni.pe',
|
|
17
|
+
id: '19b84376ff5f5ed2',
|
|
18
|
+
threadId: '19b84376ff5f5ed2',
|
|
19
|
+
to: 'recipient@example.com',
|
|
20
|
+
subject: 'Test Subject',
|
|
21
|
+
bodyPreview: 'First 200 chars of body...',
|
|
22
|
+
replyToId: null,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
expect(logEntry).toHaveProperty('sentAt');
|
|
26
|
+
expect(logEntry).toHaveProperty('account');
|
|
27
|
+
expect(logEntry).toHaveProperty('id');
|
|
28
|
+
expect(logEntry).toHaveProperty('threadId');
|
|
29
|
+
expect(logEntry).toHaveProperty('to');
|
|
30
|
+
expect(logEntry).toHaveProperty('subject');
|
|
31
|
+
expect(logEntry).toHaveProperty('bodyPreview');
|
|
32
|
+
expect(logEntry).toHaveProperty('replyToId');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should track reply-to for replies', () => {
|
|
36
|
+
const replyEntry = {
|
|
37
|
+
sentAt: new Date().toISOString(),
|
|
38
|
+
account: 'test',
|
|
39
|
+
id: 'new-id',
|
|
40
|
+
threadId: 'thread-id',
|
|
41
|
+
to: 'sender@example.com',
|
|
42
|
+
subject: 'Re: Original Subject',
|
|
43
|
+
bodyPreview: 'My reply...',
|
|
44
|
+
replyToId: 'original-message-id',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
expect(replyEntry.replyToId).toBe('original-message-id');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should have null replyToId for new messages', () => {
|
|
51
|
+
const newEmailEntry = {
|
|
52
|
+
sentAt: new Date().toISOString(),
|
|
53
|
+
account: 'test',
|
|
54
|
+
id: 'new-id',
|
|
55
|
+
threadId: 'thread-id',
|
|
56
|
+
to: 'recipient@example.com',
|
|
57
|
+
subject: 'New Subject',
|
|
58
|
+
bodyPreview: 'Body...',
|
|
59
|
+
replyToId: null,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
expect(newEmailEntry.replyToId).toBeNull();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('Body preview logic', () => {
|
|
67
|
+
it('should truncate body to 200 chars', () => {
|
|
68
|
+
const longBody = 'A'.repeat(500);
|
|
69
|
+
const preview = longBody.substring(0, 200);
|
|
70
|
+
|
|
71
|
+
expect(preview).toHaveLength(200);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should not truncate short body', () => {
|
|
75
|
+
const shortBody = 'Short message';
|
|
76
|
+
const preview = shortBody.substring(0, 200);
|
|
77
|
+
|
|
78
|
+
expect(preview).toBe('Short message');
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('Log filtering', () => {
|
|
83
|
+
it('should filter entries by date range', () => {
|
|
84
|
+
const now = new Date();
|
|
85
|
+
const tenDaysAgo = new Date(now);
|
|
86
|
+
tenDaysAgo.setDate(tenDaysAgo.getDate() - 10);
|
|
87
|
+
const fortyDaysAgo = new Date(now);
|
|
88
|
+
fortyDaysAgo.setDate(fortyDaysAgo.getDate() - 40);
|
|
89
|
+
|
|
90
|
+
const entries = [
|
|
91
|
+
{ sentAt: now.toISOString(), id: 'recent' },
|
|
92
|
+
{ sentAt: tenDaysAgo.toISOString(), id: 'ten-days' },
|
|
93
|
+
{ sentAt: fortyDaysAgo.toISOString(), id: 'forty-days' },
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
const cutoff = new Date();
|
|
97
|
+
cutoff.setDate(cutoff.getDate() - 30);
|
|
98
|
+
|
|
99
|
+
const recentEntries = entries.filter((e) => new Date(e.sentAt) >= cutoff);
|
|
100
|
+
|
|
101
|
+
expect(recentEntries).toHaveLength(2);
|
|
102
|
+
expect(recentEntries.map(e => e.id)).toContain('recent');
|
|
103
|
+
expect(recentEntries.map(e => e.id)).toContain('ten-days');
|
|
104
|
+
expect(recentEntries.map(e => e.id)).not.toContain('forty-days');
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('Log path', () => {
|
|
109
|
+
it('should use correct log directory path', () => {
|
|
110
|
+
expect(LOG_DIR).toContain('.config');
|
|
111
|
+
expect(LOG_DIR).toContain('inboxd');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should use correct log file name', () => {
|
|
115
|
+
expect(LOG_FILE).toContain('sent-log.json');
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('Empty log handling', () => {
|
|
120
|
+
it('should return empty array for empty log', () => {
|
|
121
|
+
const emptyLog = [];
|
|
122
|
+
expect(emptyLog).toHaveLength(0);
|
|
123
|
+
expect(Array.isArray(emptyLog)).toBe(true);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should handle missing log file gracefully', () => {
|
|
127
|
+
// Simulate the readSentLog behavior when file doesn't exist
|
|
128
|
+
const readLogSafe = (fileExists, fileContent) => {
|
|
129
|
+
if (!fileExists) return [];
|
|
130
|
+
try {
|
|
131
|
+
return JSON.parse(fileContent);
|
|
132
|
+
} catch {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
expect(readLogSafe(false, '')).toEqual([]);
|
|
138
|
+
expect(readLogSafe(true, 'invalid json')).toEqual([]);
|
|
139
|
+
expect(readLogSafe(true, '[]')).toEqual([]);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|