@urugus/slack-cli 0.2.12 → 0.3.0
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/README.md +45 -0
- package/dist/commands/history-display.d.ts +5 -1
- package/dist/commands/history-display.d.ts.map +1 -1
- package/dist/commands/history-display.js +3 -3
- package/dist/commands/history-display.js.map +1 -1
- package/dist/commands/history.d.ts.map +1 -1
- package/dist/commands/history.js +28 -11
- package/dist/commands/history.js.map +1 -1
- package/dist/commands/search.d.ts +3 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +51 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/types/commands.d.ts +10 -0
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/utils/constants.d.ts +5 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +5 -0
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/formatters/search-formatters.d.ts +10 -0
- package/dist/utils/formatters/search-formatters.d.ts.map +1 -0
- package/dist/utils/formatters/search-formatters.js +91 -0
- package/dist/utils/formatters/search-formatters.js.map +1 -0
- package/dist/utils/slack-api-client.d.ts +5 -0
- package/dist/utils/slack-api-client.d.ts.map +1 -1
- package/dist/utils/slack-api-client.js +8 -0
- package/dist/utils/slack-api-client.js.map +1 -1
- package/dist/utils/slack-operations/index.d.ts +1 -0
- package/dist/utils/slack-operations/index.d.ts.map +1 -1
- package/dist/utils/slack-operations/index.js +3 -1
- package/dist/utils/slack-operations/index.js.map +1 -1
- package/dist/utils/slack-operations/message-operations.d.ts +1 -0
- package/dist/utils/slack-operations/message-operations.d.ts.map +1 -1
- package/dist/utils/slack-operations/message-operations.js +21 -0
- package/dist/utils/slack-operations/message-operations.js.map +1 -1
- package/dist/utils/slack-operations/search-operations.d.ts +29 -0
- package/dist/utils/slack-operations/search-operations.d.ts.map +1 -0
- package/dist/utils/slack-operations/search-operations.js +37 -0
- package/dist/utils/slack-operations/search-operations.js.map +1 -0
- package/dist/utils/validators.d.ts +16 -0
- package/dist/utils/validators.d.ts.map +1 -1
- package/dist/utils/validators.js +50 -0
- package/dist/utils/validators.js.map +1 -1
- package/package.json +5 -2
- package/.claude/settings.local.json +0 -75
- package/.github/dependabot.yml +0 -18
- package/.github/workflows/ci.yml +0 -70
- package/.github/workflows/pr-validation.yml +0 -41
- package/.prettierignore +0 -11
- package/.prettierrc +0 -10
- package/CHANGELOG.md +0 -61
- package/CLAUDE.md +0 -16
- package/eslint.config.js +0 -38
- package/src/commands/channels.ts +0 -50
- package/src/commands/config-subcommands.ts +0 -63
- package/src/commands/config.ts +0 -50
- package/src/commands/history-display.ts +0 -19
- package/src/commands/history-validators.ts +0 -46
- package/src/commands/history.ts +0 -61
- package/src/commands/scheduled.ts +0 -71
- package/src/commands/send.ts +0 -69
- package/src/commands/unread.ts +0 -122
- package/src/index.ts +0 -27
- package/src/types/commands.ts +0 -58
- package/src/types/config.ts +0 -20
- package/src/utils/channel-formatter.ts +0 -45
- package/src/utils/channel-resolver.ts +0 -82
- package/src/utils/client-factory.ts +0 -10
- package/src/utils/command-wrapper.ts +0 -27
- package/src/utils/config/config-file-manager.ts +0 -56
- package/src/utils/config/profile-manager.ts +0 -79
- package/src/utils/config/token-crypto-service.ts +0 -80
- package/src/utils/config-helper.ts +0 -21
- package/src/utils/constants.ts +0 -78
- package/src/utils/date-utils.ts +0 -8
- package/src/utils/error-utils.ts +0 -6
- package/src/utils/errors.ts +0 -33
- package/src/utils/format-utils.ts +0 -9
- package/src/utils/formatters/base-formatter.ts +0 -34
- package/src/utils/formatters/channel-formatters.ts +0 -71
- package/src/utils/formatters/channels-list-formatters.ts +0 -55
- package/src/utils/formatters/history-formatters.ts +0 -123
- package/src/utils/formatters/message-formatters.ts +0 -85
- package/src/utils/mention-utils.ts +0 -47
- package/src/utils/option-parsers.ts +0 -100
- package/src/utils/profile-config.ts +0 -161
- package/src/utils/schedule-utils.ts +0 -41
- package/src/utils/slack-api-client.ts +0 -135
- package/src/utils/slack-operations/base-client.ts +0 -30
- package/src/utils/slack-operations/channel-operations.ts +0 -161
- package/src/utils/slack-operations/index.ts +0 -3
- package/src/utils/slack-operations/message-operations.ts +0 -176
- package/src/utils/slack-patterns.ts +0 -9
- package/src/utils/token-utils.ts +0 -17
- package/src/utils/validators.ts +0 -263
- package/tests/commands/channels.test.ts +0 -250
- package/tests/commands/config.test.ts +0 -158
- package/tests/commands/history.test.ts +0 -403
- package/tests/commands/scheduled.test.ts +0 -131
- package/tests/commands/send.test.ts +0 -414
- package/tests/commands/unread.test.ts +0 -492
- package/tests/index.test.ts +0 -40
- package/tests/test-utils.ts +0 -28
- package/tests/utils/channel-resolver.test.ts +0 -161
- package/tests/utils/config/config-file-manager.test.ts +0 -118
- package/tests/utils/config/profile-manager.test.ts +0 -266
- package/tests/utils/config/token-crypto-service.test.ts +0 -98
- package/tests/utils/config.test.ts +0 -400
- package/tests/utils/date-utils.test.ts +0 -30
- package/tests/utils/error-utils.test.ts +0 -34
- package/tests/utils/format-utils.test.ts +0 -61
- package/tests/utils/mention-utils.test.ts +0 -100
- package/tests/utils/option-parsers.test.ts +0 -173
- package/tests/utils/profile-config.test.ts +0 -282
- package/tests/utils/schedule-utils.test.ts +0 -63
- package/tests/utils/slack-api-client.test.ts +0 -313
- package/tests/utils/slack-operations/channel-operations.test.ts +0 -248
- package/tests/utils/slack-operations/message-operations.test.ts +0 -163
- package/tests/utils/token-utils.test.ts +0 -33
- package/tests/utils/validators.test.ts +0 -307
- package/tsconfig.json +0 -22
- package/vitest.config.ts +0 -27
|
@@ -1,400 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
-
import * as fs from 'fs/promises';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import * as os from 'os';
|
|
5
|
-
import { ProfileConfigManager } from '../../src/utils/profile-config';
|
|
6
|
-
import type { Config, ConfigStore } from '../../src/types/config';
|
|
7
|
-
|
|
8
|
-
vi.mock('fs/promises');
|
|
9
|
-
vi.mock('os');
|
|
10
|
-
|
|
11
|
-
describe('ProfileConfigManager', () => {
|
|
12
|
-
let configManager: ProfileConfigManager;
|
|
13
|
-
const mockHomeDir = '/mock/home';
|
|
14
|
-
const mockConfigPath = path.join(mockHomeDir, '.slack-cli', 'config.json');
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
vi.clearAllMocks();
|
|
18
|
-
vi.mocked(os.homedir).mockReturnValue(mockHomeDir);
|
|
19
|
-
configManager = new ProfileConfigManager();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
afterEach(() => {
|
|
23
|
-
vi.restoreAllMocks();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe('setToken', () => {
|
|
27
|
-
it('should save token to default profile when no profile specified and no default set', async () => {
|
|
28
|
-
vi.mocked(fs.mkdir).mockResolvedValue(undefined);
|
|
29
|
-
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
30
|
-
vi.mocked(fs.chmod).mockResolvedValue(undefined);
|
|
31
|
-
vi.mocked(fs.readFile).mockRejectedValue({ code: 'ENOENT' });
|
|
32
|
-
|
|
33
|
-
await configManager.setToken('test-token-123');
|
|
34
|
-
|
|
35
|
-
const writeCall = vi.mocked(fs.writeFile).mock.calls[0];
|
|
36
|
-
const writtenData = JSON.parse(writeCall[1] as string) as ConfigStore;
|
|
37
|
-
|
|
38
|
-
expect(writtenData.profiles.default).toBeDefined();
|
|
39
|
-
expect(writtenData.profiles.default.token).toBe('test-token-123');
|
|
40
|
-
expect(writtenData.defaultProfile).toBe('default');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('should save token to current default profile when no profile specified', async () => {
|
|
44
|
-
const existingStore: ConfigStore = {
|
|
45
|
-
profiles: {
|
|
46
|
-
personal: {
|
|
47
|
-
token: 'personal-token',
|
|
48
|
-
updatedAt: '2025-01-01T00:00:00.000Z'
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
defaultProfile: 'personal'
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(existingStore));
|
|
55
|
-
vi.mocked(fs.mkdir).mockResolvedValue(undefined);
|
|
56
|
-
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
57
|
-
vi.mocked(fs.chmod).mockResolvedValue(undefined);
|
|
58
|
-
|
|
59
|
-
await configManager.setToken('updated-token-123');
|
|
60
|
-
|
|
61
|
-
const writeCall = vi.mocked(fs.writeFile).mock.calls[0];
|
|
62
|
-
const writtenData = JSON.parse(writeCall[1] as string) as ConfigStore;
|
|
63
|
-
|
|
64
|
-
expect(writtenData.profiles.personal.token).toBe('updated-token-123');
|
|
65
|
-
expect(writtenData.defaultProfile).toBe('personal');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should save token to specified profile', async () => {
|
|
69
|
-
vi.mocked(fs.mkdir).mockResolvedValue(undefined);
|
|
70
|
-
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
71
|
-
vi.mocked(fs.chmod).mockResolvedValue(undefined);
|
|
72
|
-
vi.mocked(fs.readFile).mockRejectedValue({ code: 'ENOENT' });
|
|
73
|
-
|
|
74
|
-
await configManager.setToken('work-token-123', 'work');
|
|
75
|
-
|
|
76
|
-
const writeCall = vi.mocked(fs.writeFile).mock.calls[0];
|
|
77
|
-
const writtenData = JSON.parse(writeCall[1] as string) as ConfigStore;
|
|
78
|
-
|
|
79
|
-
expect(writtenData.profiles.work).toBeDefined();
|
|
80
|
-
expect(writtenData.profiles.work.token).toBe('work-token-123');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should preserve existing profiles when adding new one', async () => {
|
|
84
|
-
const existingStore: ConfigStore = {
|
|
85
|
-
profiles: {
|
|
86
|
-
personal: {
|
|
87
|
-
token: 'personal-token',
|
|
88
|
-
updatedAt: '2025-01-01T00:00:00.000Z'
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
defaultProfile: 'personal'
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(existingStore));
|
|
95
|
-
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
96
|
-
vi.mocked(fs.chmod).mockResolvedValue(undefined);
|
|
97
|
-
|
|
98
|
-
await configManager.setToken('work-token-123', 'work');
|
|
99
|
-
|
|
100
|
-
const writeCall = vi.mocked(fs.writeFile).mock.calls[0];
|
|
101
|
-
const writtenData = JSON.parse(writeCall[1] as string) as ConfigStore;
|
|
102
|
-
|
|
103
|
-
expect(writtenData.profiles.personal).toBeDefined();
|
|
104
|
-
expect(writtenData.profiles.work).toBeDefined();
|
|
105
|
-
expect(writtenData.profiles.work.token).toBe('work-token-123');
|
|
106
|
-
expect(writtenData.defaultProfile).toBe('personal'); // default unchanged
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
describe('getConfig', () => {
|
|
111
|
-
it('should return config from default profile', async () => {
|
|
112
|
-
const mockStore: ConfigStore = {
|
|
113
|
-
profiles: {
|
|
114
|
-
default: {
|
|
115
|
-
token: 'default-token',
|
|
116
|
-
updatedAt: '2025-06-21T10:00:00.000Z'
|
|
117
|
-
},
|
|
118
|
-
work: {
|
|
119
|
-
token: 'work-token',
|
|
120
|
-
updatedAt: '2025-06-21T11:00:00.000Z'
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
defaultProfile: 'default'
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockStore));
|
|
127
|
-
|
|
128
|
-
const config = await configManager.getConfig();
|
|
129
|
-
|
|
130
|
-
expect(config).toEqual(mockStore.profiles.default);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('should return config from specified profile', async () => {
|
|
134
|
-
const mockStore: ConfigStore = {
|
|
135
|
-
profiles: {
|
|
136
|
-
default: {
|
|
137
|
-
token: 'default-token',
|
|
138
|
-
updatedAt: '2025-06-21T10:00:00.000Z'
|
|
139
|
-
},
|
|
140
|
-
work: {
|
|
141
|
-
token: 'work-token',
|
|
142
|
-
updatedAt: '2025-06-21T11:00:00.000Z'
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
defaultProfile: 'default'
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockStore));
|
|
149
|
-
|
|
150
|
-
const config = await configManager.getConfig('work');
|
|
151
|
-
|
|
152
|
-
expect(config).toEqual(mockStore.profiles.work);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it('should return null when profile does not exist', async () => {
|
|
156
|
-
const mockStore: ConfigStore = {
|
|
157
|
-
profiles: {
|
|
158
|
-
default: {
|
|
159
|
-
token: 'default-token',
|
|
160
|
-
updatedAt: '2025-06-21T10:00:00.000Z'
|
|
161
|
-
}
|
|
162
|
-
},
|
|
163
|
-
defaultProfile: 'default'
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockStore));
|
|
167
|
-
|
|
168
|
-
const config = await configManager.getConfig('nonexistent');
|
|
169
|
-
|
|
170
|
-
expect(config).toBeNull();
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
describe('listProfiles', () => {
|
|
175
|
-
it('should return all profiles with current profile marked', async () => {
|
|
176
|
-
const mockStore: ConfigStore = {
|
|
177
|
-
profiles: {
|
|
178
|
-
default: {
|
|
179
|
-
token: 'default-token',
|
|
180
|
-
updatedAt: '2025-06-21T10:00:00.000Z'
|
|
181
|
-
},
|
|
182
|
-
work: {
|
|
183
|
-
token: 'work-token',
|
|
184
|
-
updatedAt: '2025-06-21T11:00:00.000Z'
|
|
185
|
-
},
|
|
186
|
-
personal: {
|
|
187
|
-
token: 'personal-token',
|
|
188
|
-
updatedAt: '2025-06-21T12:00:00.000Z'
|
|
189
|
-
}
|
|
190
|
-
},
|
|
191
|
-
defaultProfile: 'work'
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockStore));
|
|
195
|
-
|
|
196
|
-
const profiles = await configManager.listProfiles();
|
|
197
|
-
|
|
198
|
-
expect(profiles).toHaveLength(3);
|
|
199
|
-
expect(profiles.find(p => p.name === 'work')?.isDefault).toBe(true);
|
|
200
|
-
expect(profiles.find(p => p.name === 'default')?.isDefault).toBe(false);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('should return empty array when no profiles exist', async () => {
|
|
204
|
-
vi.mocked(fs.readFile).mockRejectedValue({ code: 'ENOENT' });
|
|
205
|
-
|
|
206
|
-
const profiles = await configManager.listProfiles();
|
|
207
|
-
|
|
208
|
-
expect(profiles).toEqual([]);
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
describe('useProfile', () => {
|
|
213
|
-
it('should set default profile', async () => {
|
|
214
|
-
const mockStore: ConfigStore = {
|
|
215
|
-
profiles: {
|
|
216
|
-
default: {
|
|
217
|
-
token: 'default-token',
|
|
218
|
-
updatedAt: '2025-06-21T10:00:00.000Z'
|
|
219
|
-
},
|
|
220
|
-
work: {
|
|
221
|
-
token: 'work-token',
|
|
222
|
-
updatedAt: '2025-06-21T11:00:00.000Z'
|
|
223
|
-
}
|
|
224
|
-
},
|
|
225
|
-
defaultProfile: 'default'
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockStore));
|
|
229
|
-
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
230
|
-
vi.mocked(fs.chmod).mockResolvedValue(undefined);
|
|
231
|
-
|
|
232
|
-
await configManager.useProfile('work');
|
|
233
|
-
|
|
234
|
-
const writeCall = vi.mocked(fs.writeFile).mock.calls[0];
|
|
235
|
-
const writtenData = JSON.parse(writeCall[1] as string) as ConfigStore;
|
|
236
|
-
|
|
237
|
-
expect(writtenData.defaultProfile).toBe('work');
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it('should throw error when profile does not exist', async () => {
|
|
241
|
-
const mockStore: ConfigStore = {
|
|
242
|
-
profiles: {
|
|
243
|
-
default: {
|
|
244
|
-
token: 'default-token',
|
|
245
|
-
updatedAt: '2025-06-21T10:00:00.000Z'
|
|
246
|
-
}
|
|
247
|
-
},
|
|
248
|
-
defaultProfile: 'default'
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockStore));
|
|
252
|
-
|
|
253
|
-
await expect(configManager.useProfile('nonexistent')).rejects.toThrow('Profile "nonexistent" does not exist');
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
describe('getCurrentProfile', () => {
|
|
258
|
-
it('should return current default profile name', async () => {
|
|
259
|
-
const mockStore: ConfigStore = {
|
|
260
|
-
profiles: {
|
|
261
|
-
default: {
|
|
262
|
-
token: 'default-token',
|
|
263
|
-
updatedAt: '2025-06-21T10:00:00.000Z'
|
|
264
|
-
},
|
|
265
|
-
work: {
|
|
266
|
-
token: 'work-token',
|
|
267
|
-
updatedAt: '2025-06-21T11:00:00.000Z'
|
|
268
|
-
}
|
|
269
|
-
},
|
|
270
|
-
defaultProfile: 'work'
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockStore));
|
|
274
|
-
|
|
275
|
-
const currentProfile = await configManager.getCurrentProfile();
|
|
276
|
-
|
|
277
|
-
expect(currentProfile).toBe('work');
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
it('should return "default" when no default profile set', async () => {
|
|
281
|
-
const mockStore: ConfigStore = {
|
|
282
|
-
profiles: {
|
|
283
|
-
default: {
|
|
284
|
-
token: 'default-token',
|
|
285
|
-
updatedAt: '2025-06-21T10:00:00.000Z'
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockStore));
|
|
291
|
-
|
|
292
|
-
const currentProfile = await configManager.getCurrentProfile();
|
|
293
|
-
|
|
294
|
-
expect(currentProfile).toBe('default');
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
describe('clearConfig', () => {
|
|
299
|
-
it('should clear specific profile', async () => {
|
|
300
|
-
const mockStore: ConfigStore = {
|
|
301
|
-
profiles: {
|
|
302
|
-
default: {
|
|
303
|
-
token: 'default-token',
|
|
304
|
-
updatedAt: '2025-06-21T10:00:00.000Z'
|
|
305
|
-
},
|
|
306
|
-
work: {
|
|
307
|
-
token: 'work-token',
|
|
308
|
-
updatedAt: '2025-06-21T11:00:00.000Z'
|
|
309
|
-
}
|
|
310
|
-
},
|
|
311
|
-
defaultProfile: 'work'
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockStore));
|
|
315
|
-
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
316
|
-
vi.mocked(fs.chmod).mockResolvedValue(undefined);
|
|
317
|
-
|
|
318
|
-
await configManager.clearConfig('work');
|
|
319
|
-
|
|
320
|
-
const writeCall = vi.mocked(fs.writeFile).mock.calls[0];
|
|
321
|
-
const writtenData = JSON.parse(writeCall[1] as string) as ConfigStore;
|
|
322
|
-
|
|
323
|
-
expect(writtenData.profiles.work).toBeUndefined();
|
|
324
|
-
expect(writtenData.profiles.default).toBeDefined();
|
|
325
|
-
expect(writtenData.defaultProfile).toBe('default'); // fallback to default
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
it('should clear default profile and reset defaultProfile', async () => {
|
|
329
|
-
const mockStore: ConfigStore = {
|
|
330
|
-
profiles: {
|
|
331
|
-
default: {
|
|
332
|
-
token: 'default-token',
|
|
333
|
-
updatedAt: '2025-06-21T10:00:00.000Z'
|
|
334
|
-
},
|
|
335
|
-
work: {
|
|
336
|
-
token: 'work-token',
|
|
337
|
-
updatedAt: '2025-06-21T11:00:00.000Z'
|
|
338
|
-
}
|
|
339
|
-
},
|
|
340
|
-
defaultProfile: 'default'
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockStore));
|
|
344
|
-
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
345
|
-
vi.mocked(fs.chmod).mockResolvedValue(undefined);
|
|
346
|
-
|
|
347
|
-
await configManager.clearConfig('default');
|
|
348
|
-
|
|
349
|
-
const writeCall = vi.mocked(fs.writeFile).mock.calls[0];
|
|
350
|
-
const writtenData = JSON.parse(writeCall[1] as string) as ConfigStore;
|
|
351
|
-
|
|
352
|
-
expect(writtenData.profiles.default).toBeUndefined();
|
|
353
|
-
expect(writtenData.profiles.work).toBeDefined();
|
|
354
|
-
expect(writtenData.defaultProfile).toBe('work'); // switch to remaining profile
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
it('should delete config file when clearing last profile', async () => {
|
|
358
|
-
const mockStore: ConfigStore = {
|
|
359
|
-
profiles: {
|
|
360
|
-
default: {
|
|
361
|
-
token: 'default-token',
|
|
362
|
-
updatedAt: '2025-06-21T10:00:00.000Z'
|
|
363
|
-
}
|
|
364
|
-
},
|
|
365
|
-
defaultProfile: 'default'
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(mockStore));
|
|
369
|
-
vi.mocked(fs.unlink).mockResolvedValue(undefined);
|
|
370
|
-
|
|
371
|
-
await configManager.clearConfig('default');
|
|
372
|
-
|
|
373
|
-
expect(fs.unlink).toHaveBeenCalledWith(mockConfigPath);
|
|
374
|
-
});
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
describe('migration from old config', () => {
|
|
378
|
-
it('should automatically migrate old single-token config to profile format', async () => {
|
|
379
|
-
const oldConfig = {
|
|
380
|
-
token: 'old-token-123',
|
|
381
|
-
updatedAt: '2025-01-01T00:00:00.000Z'
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify(oldConfig));
|
|
385
|
-
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
386
|
-
vi.mocked(fs.chmod).mockResolvedValue(undefined);
|
|
387
|
-
|
|
388
|
-
const config = await configManager.getConfig();
|
|
389
|
-
|
|
390
|
-
expect(config).toEqual(oldConfig);
|
|
391
|
-
|
|
392
|
-
// Verify migration happened
|
|
393
|
-
const writeCall = vi.mocked(fs.writeFile).mock.calls[0];
|
|
394
|
-
const writtenData = JSON.parse(writeCall[1] as string) as ConfigStore;
|
|
395
|
-
|
|
396
|
-
expect(writtenData.profiles.default).toEqual(oldConfig);
|
|
397
|
-
expect(writtenData.defaultProfile).toBe('default');
|
|
398
|
-
});
|
|
399
|
-
});
|
|
400
|
-
});
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { formatUnixTimestamp, formatSlackTimestamp } from '../../src/utils/date-utils';
|
|
3
|
-
|
|
4
|
-
describe('date-utils', () => {
|
|
5
|
-
describe('formatUnixTimestamp', () => {
|
|
6
|
-
it('should format unix timestamp to ISO date string', () => {
|
|
7
|
-
const timestamp = 1640995200; // 2022-01-01 00:00:00 UTC
|
|
8
|
-
expect(formatUnixTimestamp(timestamp)).toBe('2022-01-01');
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('should handle different timestamps correctly', () => {
|
|
12
|
-
const timestamp = 1672531200; // 2023-01-01 00:00:00 UTC
|
|
13
|
-
expect(formatUnixTimestamp(timestamp)).toBe('2023-01-01');
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
describe('formatSlackTimestamp', () => {
|
|
18
|
-
it('should format Slack timestamp to locale string', () => {
|
|
19
|
-
const slackTimestamp = '1640995200.000000';
|
|
20
|
-
const result = formatSlackTimestamp(slackTimestamp);
|
|
21
|
-
expect(result).toContain('2022');
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('should handle timestamps with milliseconds', () => {
|
|
25
|
-
const slackTimestamp = '1640995200.123456';
|
|
26
|
-
const result = formatSlackTimestamp(slackTimestamp);
|
|
27
|
-
expect(result).toContain('2022');
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { extractErrorMessage } from '../../src/utils/error-utils';
|
|
3
|
-
|
|
4
|
-
describe('extractErrorMessage', () => {
|
|
5
|
-
it('should extract message from Error instance', () => {
|
|
6
|
-
const error = new Error('Test error message');
|
|
7
|
-
expect(extractErrorMessage(error)).toBe('Test error message');
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it('should convert string to string', () => {
|
|
11
|
-
const error = 'String error';
|
|
12
|
-
expect(extractErrorMessage(error)).toBe('String error');
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('should convert number to string', () => {
|
|
16
|
-
const error = 404;
|
|
17
|
-
expect(extractErrorMessage(error)).toBe('404');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should convert object to string', () => {
|
|
21
|
-
const error = { code: 'ERROR_CODE', detail: 'Some detail' };
|
|
22
|
-
expect(extractErrorMessage(error)).toBe('[object Object]');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should handle null', () => {
|
|
26
|
-
const error = null;
|
|
27
|
-
expect(extractErrorMessage(error)).toBe('null');
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should handle undefined', () => {
|
|
31
|
-
const error = undefined;
|
|
32
|
-
expect(extractErrorMessage(error)).toBe('undefined');
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { formatMessageWithMentions } from '../../src/utils/format-utils';
|
|
3
|
-
|
|
4
|
-
describe('formatMessageWithMentions', () => {
|
|
5
|
-
it('should replace user ID mentions with usernames', () => {
|
|
6
|
-
const message = 'Hello <@U784E34>, please check this';
|
|
7
|
-
const users = new Map([['U784E34', 'sakashita']]);
|
|
8
|
-
|
|
9
|
-
const result = formatMessageWithMentions(message, users);
|
|
10
|
-
|
|
11
|
-
expect(result).toBe('Hello @sakashita, please check this');
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('should handle multiple mentions', () => {
|
|
15
|
-
const message = '<@U784E34> and <@U123456> are working on this';
|
|
16
|
-
const users = new Map([
|
|
17
|
-
['U784E34', 'sakashita'],
|
|
18
|
-
['U123456', 'tanaka']
|
|
19
|
-
]);
|
|
20
|
-
|
|
21
|
-
const result = formatMessageWithMentions(message, users);
|
|
22
|
-
|
|
23
|
-
expect(result).toBe('@sakashita and @tanaka are working on this');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should keep user ID if username is not found', () => {
|
|
27
|
-
const message = 'Hello <@U784E34>, please check this';
|
|
28
|
-
const users = new Map<string, string>();
|
|
29
|
-
|
|
30
|
-
const result = formatMessageWithMentions(message, users);
|
|
31
|
-
|
|
32
|
-
expect(result).toBe('Hello @U784E34, please check this');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should handle messages without mentions', () => {
|
|
36
|
-
const message = 'This is a regular message';
|
|
37
|
-
const users = new Map<string, string>();
|
|
38
|
-
|
|
39
|
-
const result = formatMessageWithMentions(message, users);
|
|
40
|
-
|
|
41
|
-
expect(result).toBe('This is a regular message');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should handle empty message', () => {
|
|
45
|
-
const message = '';
|
|
46
|
-
const users = new Map<string, string>();
|
|
47
|
-
|
|
48
|
-
const result = formatMessageWithMentions(message, users);
|
|
49
|
-
|
|
50
|
-
expect(result).toBe('');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should handle malformed mentions', () => {
|
|
54
|
-
const message = 'Hello <@>, <@ >, <@invalid';
|
|
55
|
-
const users = new Map<string, string>();
|
|
56
|
-
|
|
57
|
-
const result = formatMessageWithMentions(message, users);
|
|
58
|
-
|
|
59
|
-
expect(result).toBe('Hello <@>, <@ >, <@invalid');
|
|
60
|
-
});
|
|
61
|
-
});
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { extractUserIdsFromMentions, extractAllUserIds } from '../../src/utils/mention-utils';
|
|
3
|
-
|
|
4
|
-
describe('mention-utils', () => {
|
|
5
|
-
describe('extractUserIdsFromMentions', () => {
|
|
6
|
-
it('should extract single user ID from mention', () => {
|
|
7
|
-
const text = 'Hello <@U123456789>';
|
|
8
|
-
const userIds = extractUserIdsFromMentions(text);
|
|
9
|
-
expect(userIds).toEqual(['U123456789']);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('should extract multiple user IDs from mentions', () => {
|
|
13
|
-
const text = 'Hey <@U123456789> and <@U987654321>, please check this';
|
|
14
|
-
const userIds = extractUserIdsFromMentions(text);
|
|
15
|
-
expect(userIds).toEqual(['U123456789', 'U987654321']);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('should handle duplicate mentions', () => {
|
|
19
|
-
const text = '<@U123456789> mentioned <@U123456789> again';
|
|
20
|
-
const userIds = extractUserIdsFromMentions(text);
|
|
21
|
-
expect(userIds).toEqual(['U123456789', 'U123456789']);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('should return empty array for text without mentions', () => {
|
|
25
|
-
const text = 'No mentions here';
|
|
26
|
-
const userIds = extractUserIdsFromMentions(text);
|
|
27
|
-
expect(userIds).toEqual([]);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should handle empty text', () => {
|
|
31
|
-
const userIds = extractUserIdsFromMentions('');
|
|
32
|
-
expect(userIds).toEqual([]);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should ignore malformed mentions', () => {
|
|
36
|
-
const text = 'Invalid <@> mention and <@lowercase> mention';
|
|
37
|
-
const userIds = extractUserIdsFromMentions(text);
|
|
38
|
-
expect(userIds).toEqual([]);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe('extractAllUserIds', () => {
|
|
43
|
-
it('should extract user IDs from message authors only', () => {
|
|
44
|
-
const messages = [
|
|
45
|
-
{ user: 'U111111111', text: 'Hello world' },
|
|
46
|
-
{ user: 'U222222222', text: 'Hi there' },
|
|
47
|
-
];
|
|
48
|
-
const userIds = extractAllUserIds(messages);
|
|
49
|
-
expect(userIds).toEqual(['U111111111', 'U222222222']);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should extract user IDs from mentions only', () => {
|
|
53
|
-
const messages = [
|
|
54
|
-
{ text: 'Hello <@U333333333>' },
|
|
55
|
-
{ text: 'Hi <@U444444444>' },
|
|
56
|
-
];
|
|
57
|
-
const userIds = extractAllUserIds(messages);
|
|
58
|
-
expect(userIds).toEqual(['U333333333', 'U444444444']);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should extract both authors and mentioned users', () => {
|
|
62
|
-
const messages = [
|
|
63
|
-
{ user: 'U111111111', text: 'Hello <@U222222222>' },
|
|
64
|
-
{ user: 'U333333333', text: 'Hi <@U444444444> and <@U555555555>' },
|
|
65
|
-
];
|
|
66
|
-
const userIds = extractAllUserIds(messages);
|
|
67
|
-
expect(userIds.sort()).toEqual([
|
|
68
|
-
'U111111111',
|
|
69
|
-
'U222222222',
|
|
70
|
-
'U333333333',
|
|
71
|
-
'U444444444',
|
|
72
|
-
'U555555555',
|
|
73
|
-
]);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should remove duplicate user IDs', () => {
|
|
77
|
-
const messages = [
|
|
78
|
-
{ user: 'U111111111', text: 'Hello <@U111111111>' },
|
|
79
|
-
{ user: 'U111111111', text: 'Another message' },
|
|
80
|
-
];
|
|
81
|
-
const userIds = extractAllUserIds(messages);
|
|
82
|
-
expect(userIds).toEqual(['U111111111']);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('should handle messages without user or text', () => {
|
|
86
|
-
const messages = [
|
|
87
|
-
{ user: 'U111111111' },
|
|
88
|
-
{ text: 'No user here' },
|
|
89
|
-
{},
|
|
90
|
-
];
|
|
91
|
-
const userIds = extractAllUserIds(messages);
|
|
92
|
-
expect(userIds).toEqual(['U111111111']);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('should handle empty messages array', () => {
|
|
96
|
-
const userIds = extractAllUserIds([]);
|
|
97
|
-
expect(userIds).toEqual([]);
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
});
|