@urugus/slack-cli 0.2.8 → 0.2.10

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.
Files changed (53) hide show
  1. package/.claude/settings.local.json +6 -2
  2. package/.github/workflows/ci.yml +2 -2
  3. package/README.md +8 -0
  4. package/dist/commands/send.d.ts.map +1 -1
  5. package/dist/commands/send.js +16 -2
  6. package/dist/commands/send.js.map +1 -1
  7. package/dist/commands/unread.d.ts.map +1 -1
  8. package/dist/commands/unread.js +32 -16
  9. package/dist/commands/unread.js.map +1 -1
  10. package/dist/types/commands.d.ts +2 -0
  11. package/dist/types/commands.d.ts.map +1 -1
  12. package/dist/utils/constants.d.ts +5 -0
  13. package/dist/utils/constants.d.ts.map +1 -1
  14. package/dist/utils/constants.js +5 -0
  15. package/dist/utils/constants.js.map +1 -1
  16. package/dist/utils/errors.d.ts.map +1 -1
  17. package/dist/utils/errors.js +1 -5
  18. package/dist/utils/errors.js.map +1 -1
  19. package/dist/utils/schedule-utils.d.ts +3 -0
  20. package/dist/utils/schedule-utils.d.ts.map +1 -0
  21. package/dist/utils/schedule-utils.js +34 -0
  22. package/dist/utils/schedule-utils.js.map +1 -0
  23. package/dist/utils/slack-api-client.d.ts +2 -1
  24. package/dist/utils/slack-api-client.d.ts.map +1 -1
  25. package/dist/utils/slack-api-client.js +3 -0
  26. package/dist/utils/slack-api-client.js.map +1 -1
  27. package/dist/utils/slack-operations/message-operations.d.ts +2 -1
  28. package/dist/utils/slack-operations/message-operations.d.ts.map +1 -1
  29. package/dist/utils/slack-operations/message-operations.js +11 -0
  30. package/dist/utils/slack-operations/message-operations.js.map +1 -1
  31. package/dist/utils/validators.d.ts +4 -0
  32. package/dist/utils/validators.d.ts.map +1 -1
  33. package/dist/utils/validators.js +31 -0
  34. package/dist/utils/validators.js.map +1 -1
  35. package/eslint.config.js +38 -0
  36. package/package.json +13 -15
  37. package/src/commands/send.ts +21 -3
  38. package/src/commands/unread.ts +52 -22
  39. package/src/types/commands.ts +2 -0
  40. package/src/utils/constants.ts +7 -0
  41. package/src/utils/errors.ts +1 -5
  42. package/src/utils/schedule-utils.ts +41 -0
  43. package/src/utils/slack-api-client.ts +10 -1
  44. package/src/utils/slack-operations/message-operations.ts +25 -1
  45. package/src/utils/validators.ts +38 -0
  46. package/tests/commands/send.test.ts +235 -44
  47. package/tests/index.test.ts +2 -2
  48. package/tests/utils/schedule-utils.test.ts +63 -0
  49. package/tests/utils/slack-api-client.test.ts +18 -1
  50. package/tests/utils/slack-operations/message-operations.test.ts +19 -1
  51. package/.eslintrc.json +0 -25
  52. package/src/utils/formatters/output-formatter.ts +0 -7
  53. package/tests/utils/slack-operations/channel-operations-refactored.test.ts +0 -179
@@ -1,179 +0,0 @@
1
- import { describe, it, expect, beforeEach, vi } from 'vitest';
2
- import { ChannelOperations } from '../../../src/utils/slack-operations/channel-operations';
3
- import { WebClient } from '@slack/web-api';
4
-
5
- vi.mock('@slack/web-api');
6
-
7
- describe('ChannelOperations - refactored listUnreadChannels', () => {
8
- let channelOps: ChannelOperations;
9
- let mockClient: any;
10
-
11
- beforeEach(() => {
12
- vi.resetAllMocks();
13
- mockClient = {
14
- conversations: {
15
- list: vi.fn(),
16
- info: vi.fn(),
17
- history: vi.fn(),
18
- },
19
- };
20
- channelOps = new ChannelOperations(mockClient as WebClient);
21
- });
22
-
23
- describe('listUnreadChannels', () => {
24
- it('should fetch unread channels with proper separation of concerns', async () => {
25
- const mockChannels = [
26
- { id: 'C1', name: 'general' },
27
- { id: 'C2', name: 'random' },
28
- ];
29
-
30
- mockClient.conversations.list.mockResolvedValueOnce({
31
- channels: mockChannels,
32
- });
33
-
34
- // Channel 1 - has unread messages
35
- mockClient.conversations.info.mockResolvedValueOnce({
36
- channel: { id: 'C1', last_read: '1234567890.000000' },
37
- });
38
- mockClient.conversations.history
39
- .mockResolvedValueOnce({ messages: [{ ts: '1234567900.000000' }] }) // latest message
40
- .mockResolvedValueOnce({ messages: [{ ts: '1234567900.000000' }, { ts: '1234567895.000000' }] }); // messages after last_read
41
-
42
- // Channel 2 - no unread messages
43
- mockClient.conversations.info.mockResolvedValueOnce({
44
- channel: { id: 'C2', last_read: '1234567900.000000' },
45
- });
46
- mockClient.conversations.history
47
- .mockResolvedValueOnce({ messages: [{ ts: '1234567890.000000' }] }) // latest message
48
- .mockResolvedValueOnce({ messages: [] }); // no messages after last_read
49
-
50
- const result = await channelOps.listUnreadChannels();
51
-
52
- expect(result).toHaveLength(1);
53
- expect(result[0]).toMatchObject({
54
- id: 'C1',
55
- name: 'general',
56
- unread_count: 2,
57
- unread_count_display: 2,
58
- });
59
-
60
- // Verify the separation of concerns - each method is called appropriately
61
- expect(mockClient.conversations.list).toHaveBeenCalledOnce();
62
- expect(mockClient.conversations.info).toHaveBeenCalledTimes(2);
63
- expect(mockClient.conversations.history).toHaveBeenCalledTimes(4);
64
- });
65
-
66
- it('should handle channels with no last_read timestamp', async () => {
67
- const mockChannels = [
68
- { id: 'C1', name: 'general' },
69
- ];
70
-
71
- mockClient.conversations.list.mockResolvedValueOnce({
72
- channels: mockChannels,
73
- });
74
-
75
- mockClient.conversations.info.mockResolvedValueOnce({
76
- channel: { id: 'C1' }, // no last_read
77
- });
78
-
79
- mockClient.conversations.history
80
- .mockResolvedValueOnce({ messages: [{ ts: '1234567900.000000' }] }) // check if has messages
81
- .mockResolvedValueOnce({
82
- messages: [
83
- { ts: '1234567900.000000' },
84
- { ts: '1234567895.000000' },
85
- { ts: '1234567890.000000' },
86
- ]
87
- }); // all messages are unread
88
-
89
- const result = await channelOps.listUnreadChannels();
90
-
91
- expect(result).toHaveLength(1);
92
- expect(result[0]).toMatchObject({
93
- id: 'C1',
94
- name: 'general',
95
- unread_count: 3,
96
- unread_count_display: 3,
97
- });
98
- });
99
-
100
- it('should skip channels with no messages', async () => {
101
- const mockChannels = [
102
- { id: 'C1', name: 'general' },
103
- ];
104
-
105
- mockClient.conversations.list.mockResolvedValueOnce({
106
- channels: mockChannels,
107
- });
108
-
109
- mockClient.conversations.info.mockResolvedValueOnce({
110
- channel: { id: 'C1', last_read: '1234567890.000000' },
111
- });
112
-
113
- mockClient.conversations.history
114
- .mockResolvedValueOnce({ messages: [] }); // no messages at all
115
-
116
- const result = await channelOps.listUnreadChannels();
117
-
118
- expect(result).toHaveLength(0);
119
- });
120
-
121
- it('should handle rate limit errors gracefully', async () => {
122
- const mockChannels = [
123
- { id: 'C1', name: 'general' },
124
- { id: 'C2', name: 'random' },
125
- ];
126
-
127
- mockClient.conversations.list.mockResolvedValueOnce({
128
- channels: mockChannels,
129
- });
130
-
131
- // Channel 1 - rate limited
132
- mockClient.conversations.info.mockRejectedValueOnce(new Error('rate_limited'));
133
-
134
- // Channel 2 - successful
135
- mockClient.conversations.info.mockResolvedValueOnce({
136
- channel: { id: 'C2', last_read: '1234567890.000000' },
137
- });
138
- mockClient.conversations.history
139
- .mockResolvedValueOnce({ messages: [{ ts: '1234567900.000000' }] })
140
- .mockResolvedValueOnce({ messages: [{ ts: '1234567900.000000' }] });
141
-
142
- const result = await channelOps.listUnreadChannels();
143
-
144
- expect(result).toHaveLength(1);
145
- expect(result[0].id).toBe('C2');
146
- });
147
- });
148
-
149
- describe('private methods (indirectly tested)', () => {
150
- it('fetchAllChannels should handle large channel lists', async () => {
151
- const mockChannels = [
152
- { id: 'C1', name: 'channel-1' },
153
- { id: 'C2', name: 'channel-2' },
154
- { id: 'C3', name: 'channel-3' },
155
- ];
156
-
157
- mockClient.conversations.list.mockResolvedValueOnce({
158
- channels: mockChannels,
159
- });
160
-
161
- // Mock all channels having no unread messages for simplicity
162
- mockChannels.forEach(() => {
163
- mockClient.conversations.info.mockResolvedValueOnce({
164
- channel: { last_read: '9999999999.000000' },
165
- });
166
- mockClient.conversations.history.mockResolvedValueOnce({ messages: [] });
167
- });
168
-
169
- const result = await channelOps.listUnreadChannels();
170
-
171
- expect(result).toHaveLength(0);
172
- expect(mockClient.conversations.list).toHaveBeenCalledWith({
173
- types: 'public_channel,private_channel,im,mpim',
174
- exclude_archived: true,
175
- limit: 1000,
176
- });
177
- });
178
- });
179
- });