agent-messenger 2.10.0 → 2.10.2
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-plugin/plugin.json +1 -1
- package/dist/package.json +1 -1
- package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/teams/token-extractor.js +15 -2
- package/dist/src/platforms/teams/token-extractor.js.map +1 -1
- package/dist/src/shared/chromium/decryptor.d.ts +6 -0
- package/dist/src/shared/chromium/decryptor.d.ts.map +1 -1
- package/dist/src/shared/chromium/decryptor.js +26 -6
- package/dist/src/shared/chromium/decryptor.js.map +1 -1
- package/e2e/channeltalk.e2e.test.ts +13 -13
- package/e2e/channeltalkbot.e2e.test.ts +13 -13
- package/e2e/discord.e2e.test.ts +24 -24
- package/e2e/discordbot.e2e.test.ts +16 -16
- package/e2e/instagram.e2e.test.ts +10 -10
- package/e2e/kakaotalk.e2e.test.ts +7 -7
- package/e2e/line.e2e.test.ts +8 -8
- package/e2e/slack.e2e.test.ts +34 -34
- package/e2e/slackbot.e2e.test.ts +14 -14
- package/e2e/teams.e2e.test.ts +23 -23
- package/e2e/telegram.e2e.test.ts +8 -8
- package/e2e/webex.e2e.test.ts +14 -14
- package/e2e/whatsapp.e2e.test.ts +8 -8
- package/e2e/whatsappbot.e2e.test.ts +6 -6
- package/package.json +1 -1
- package/skills/agent-channeltalk/SKILL.md +1 -1
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +1 -1
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-instagram/SKILL.md +1 -1
- package/skills/agent-kakaotalk/SKILL.md +1 -1
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +1 -1
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +1 -1
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +1 -1
- package/skills/agent-wechatbot/SKILL.md +1 -1
- package/skills/agent-whatsapp/SKILL.md +1 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/channeltalk/client.test.ts +26 -26
- package/src/platforms/channeltalk/commands/auth.test.ts +16 -16
- package/src/platforms/channeltalk/commands/bot.test.ts +2 -2
- package/src/platforms/channeltalk/commands/chat.test.ts +3 -3
- package/src/platforms/channeltalk/commands/group.test.ts +4 -4
- package/src/platforms/channeltalk/commands/manager.test.ts +2 -2
- package/src/platforms/channeltalk/commands/message.test.ts +17 -17
- package/src/platforms/channeltalk/commands/snapshot.test.ts +7 -7
- package/src/platforms/channeltalk/commands/whoami.test.ts +3 -3
- package/src/platforms/channeltalk/credential-manager.test.ts +18 -18
- package/src/platforms/channeltalk/ensure-auth.test.ts +5 -5
- package/src/platforms/channeltalk/index.test.ts +23 -23
- package/src/platforms/channeltalk/token-extractor.test.ts +21 -21
- package/src/platforms/channeltalk/types.test.ts +12 -12
- package/src/platforms/channeltalkbot/client.test.ts +14 -14
- package/src/platforms/channeltalkbot/commands/auth.test.ts +16 -16
- package/src/platforms/channeltalkbot/commands/bot.test.ts +6 -6
- package/src/platforms/channeltalkbot/commands/chat.test.ts +9 -9
- package/src/platforms/channeltalkbot/commands/group.test.ts +6 -6
- package/src/platforms/channeltalkbot/commands/manager.test.ts +3 -3
- package/src/platforms/channeltalkbot/commands/message.test.ts +10 -10
- package/src/platforms/channeltalkbot/commands/snapshot.test.ts +7 -7
- package/src/platforms/channeltalkbot/commands/whoami.test.ts +4 -4
- package/src/platforms/channeltalkbot/credential-manager.test.ts +27 -27
- package/src/platforms/channeltalkbot/index.test.ts +15 -15
- package/src/platforms/discord/client.test.ts +28 -28
- package/src/platforms/discord/commands/auth.test.ts +7 -7
- package/src/platforms/discord/commands/channel.test.ts +7 -7
- package/src/platforms/discord/commands/dm.test.ts +4 -4
- package/src/platforms/discord/commands/file.test.ts +4 -4
- package/src/platforms/discord/commands/friend.test.ts +6 -6
- package/src/platforms/discord/commands/member.test.ts +5 -5
- package/src/platforms/discord/commands/mention.test.ts +5 -5
- package/src/platforms/discord/commands/message.test.ts +9 -9
- package/src/platforms/discord/commands/note.test.ts +6 -6
- package/src/platforms/discord/commands/profile.test.ts +4 -4
- package/src/platforms/discord/commands/reaction.test.ts +5 -5
- package/src/platforms/discord/commands/server.test.ts +7 -7
- package/src/platforms/discord/commands/snapshot.test.ts +6 -6
- package/src/platforms/discord/commands/thread.test.ts +6 -6
- package/src/platforms/discord/commands/user.test.ts +5 -5
- package/src/platforms/discord/commands/whoami.test.ts +6 -6
- package/src/platforms/discord/credential-manager.test.ts +16 -16
- package/src/platforms/discord/ensure-auth.test.ts +8 -8
- package/src/platforms/discord/index.test.ts +17 -17
- package/src/platforms/discord/listener.test.ts +33 -33
- package/src/platforms/discord/token-extractor.test.ts +53 -53
- package/src/platforms/discord/types.test.ts +26 -26
- package/src/platforms/discordbot/client.test.ts +31 -31
- package/src/platforms/discordbot/commands/auth.test.ts +18 -18
- package/src/platforms/discordbot/commands/channel.test.ts +11 -11
- package/src/platforms/discordbot/commands/file.test.ts +7 -7
- package/src/platforms/discordbot/commands/message.test.ts +25 -25
- package/src/platforms/discordbot/commands/reaction.test.ts +6 -6
- package/src/platforms/discordbot/commands/server.test.ts +12 -12
- package/src/platforms/discordbot/commands/snapshot.test.ts +13 -13
- package/src/platforms/discordbot/commands/thread.test.ts +10 -10
- package/src/platforms/discordbot/commands/user.test.ts +9 -9
- package/src/platforms/discordbot/commands/whoami.test.ts +4 -4
- package/src/platforms/discordbot/credential-manager.test.ts +28 -28
- package/src/platforms/instagram/client.test.ts +18 -18
- package/src/platforms/instagram/commands/auth.test.ts +11 -11
- package/src/platforms/instagram/commands/chat.test.ts +6 -6
- package/src/platforms/instagram/commands/message.test.ts +11 -11
- package/src/platforms/instagram/commands/shared.test.ts +12 -12
- package/src/platforms/instagram/commands/whoami.test.ts +3 -3
- package/src/platforms/instagram/credential-manager.test.ts +21 -21
- package/src/platforms/instagram/ensure-auth.test.ts +4 -4
- package/src/platforms/instagram/index.test.ts +9 -9
- package/src/platforms/instagram/listener.test.ts +8 -8
- package/src/platforms/instagram/token-extractor.test.ts +35 -35
- package/src/platforms/kakaotalk/client.test.ts +33 -33
- package/src/platforms/kakaotalk/commands/auth.test.ts +11 -11
- package/src/platforms/kakaotalk/commands/chat.test.ts +6 -6
- package/src/platforms/kakaotalk/commands/message.test.ts +7 -7
- package/src/platforms/kakaotalk/commands/whoami.test.ts +5 -5
- package/src/platforms/kakaotalk/credential-manager.test.ts +15 -15
- package/src/platforms/kakaotalk/index.test.ts +15 -15
- package/src/platforms/kakaotalk/listener.test.ts +17 -17
- package/src/platforms/line/client.test.ts +17 -17
- package/src/platforms/line/commands/auth.test.ts +8 -8
- package/src/platforms/line/commands/chat.test.ts +7 -7
- package/src/platforms/line/commands/friend.test.ts +6 -6
- package/src/platforms/line/commands/message.test.ts +7 -7
- package/src/platforms/line/commands/whoami.test.ts +6 -6
- package/src/platforms/line/credential-manager.test.ts +17 -17
- package/src/platforms/line/index.test.ts +10 -10
- package/src/platforms/line/listener.test.ts +15 -15
- package/src/platforms/line/types.test.ts +14 -14
- package/src/platforms/slack/cli.test.ts +8 -8
- package/src/platforms/slack/client.test.ts +151 -151
- package/src/platforms/slack/commands/activity.test.ts +13 -13
- package/src/platforms/slack/commands/auth.test.ts +34 -34
- package/src/platforms/slack/commands/bookmark.test.ts +9 -9
- package/src/platforms/slack/commands/channel.test.ts +17 -17
- package/src/platforms/slack/commands/drafts.test.ts +7 -7
- package/src/platforms/slack/commands/emoji.test.ts +3 -3
- package/src/platforms/slack/commands/file.test.ts +12 -12
- package/src/platforms/slack/commands/message.test.ts +19 -19
- package/src/platforms/slack/commands/pin.test.ts +7 -7
- package/src/platforms/slack/commands/reaction.test.ts +10 -10
- package/src/platforms/slack/commands/reminder.test.ts +9 -9
- package/src/platforms/slack/commands/saved.test.ts +7 -7
- package/src/platforms/slack/commands/sections.test.ts +5 -5
- package/src/platforms/slack/commands/snapshot.test.ts +13 -13
- package/src/platforms/slack/commands/unread.test.ts +6 -6
- package/src/platforms/slack/commands/user.test.ts +10 -10
- package/src/platforms/slack/commands/usergroup.test.ts +15 -15
- package/src/platforms/slack/commands/whoami.test.ts +6 -6
- package/src/platforms/slack/commands/workspace.test.ts +26 -26
- package/src/platforms/slack/credential-manager.test.ts +14 -14
- package/src/platforms/slack/ensure-auth.test.ts +21 -21
- package/src/platforms/slack/index.test.ts +12 -12
- package/src/platforms/slack/listener.test.ts +17 -17
- package/src/platforms/slack/token-extractor-node.test.ts +2 -2
- package/src/platforms/slack/token-extractor.test.ts +37 -37
- package/src/platforms/slack/types.test.ts +21 -21
- package/src/platforms/slackbot/client.test.ts +22 -22
- package/src/platforms/slackbot/commands/auth.test.ts +14 -14
- package/src/platforms/slackbot/commands/channel.test.ts +7 -7
- package/src/platforms/slackbot/commands/message.test.ts +13 -13
- package/src/platforms/slackbot/commands/reaction.test.ts +6 -6
- package/src/platforms/slackbot/commands/user.test.ts +7 -7
- package/src/platforms/slackbot/commands/whoami.test.ts +4 -4
- package/src/platforms/slackbot/credential-manager.test.ts +22 -22
- package/src/platforms/slackbot/types.test.ts +7 -7
- package/src/platforms/teams/client.test.ts +30 -30
- package/src/platforms/teams/commands/auth.test.ts +8 -8
- package/src/platforms/teams/commands/channel.test.ts +7 -7
- package/src/platforms/teams/commands/file.test.ts +4 -4
- package/src/platforms/teams/commands/message.test.ts +5 -5
- package/src/platforms/teams/commands/reaction.test.ts +4 -4
- package/src/platforms/teams/commands/snapshot.test.ts +7 -7
- package/src/platforms/teams/commands/team.test.ts +8 -8
- package/src/platforms/teams/commands/user.test.ts +4 -4
- package/src/platforms/teams/commands/whoami.test.ts +6 -6
- package/src/platforms/teams/credential-manager.test.ts +17 -17
- package/src/platforms/teams/ensure-auth.test.ts +13 -13
- package/src/platforms/teams/index.test.ts +15 -15
- package/src/platforms/teams/token-extractor.test.ts +219 -145
- package/src/platforms/teams/token-extractor.ts +13 -2
- package/src/platforms/teams/types.test.ts +26 -26
- package/src/platforms/telegram/app-config.test.ts +4 -4
- package/src/platforms/telegram/chat-utils.test.ts +12 -12
- package/src/platforms/telegram/client.test.ts +4 -4
- package/src/platforms/telegram/commands/auth.test.ts +16 -16
- package/src/platforms/telegram/commands/chat.test.ts +9 -9
- package/src/platforms/telegram/commands/message.test.ts +6 -6
- package/src/platforms/telegram/commands/shared.test.ts +3 -3
- package/src/platforms/telegram/commands/whoami.test.ts +3 -3
- package/src/platforms/telegram/credential-manager.test.ts +10 -10
- package/src/platforms/telegram/types.test.ts +6 -6
- package/src/platforms/webex/app-config.test.ts +8 -8
- package/src/platforms/webex/cli.test.ts +5 -5
- package/src/platforms/webex/client.test.ts +65 -65
- package/src/platforms/webex/commands/auth.test.ts +18 -18
- package/src/platforms/webex/commands/member.test.ts +5 -5
- package/src/platforms/webex/commands/message.test.ts +12 -12
- package/src/platforms/webex/commands/snapshot.test.ts +5 -5
- package/src/platforms/webex/commands/space.test.ts +10 -10
- package/src/platforms/webex/commands/whoami.test.ts +6 -6
- package/src/platforms/webex/credential-manager.test.ts +22 -22
- package/src/platforms/webex/encryption.test.ts +4 -4
- package/src/platforms/webex/ensure-auth.test.ts +5 -5
- package/src/platforms/webex/index.test.ts +5 -5
- package/src/platforms/webex/markdown-to-html.test.ts +33 -33
- package/src/platforms/webex/token-extractor.test.ts +23 -23
- package/src/platforms/webex/types.test.ts +27 -27
- package/src/platforms/wechatbot/client.test.ts +27 -27
- package/src/platforms/wechatbot/commands/auth.test.ts +15 -15
- package/src/platforms/wechatbot/commands/message.test.ts +8 -8
- package/src/platforms/wechatbot/commands/template.test.ts +9 -9
- package/src/platforms/wechatbot/commands/user.test.ts +7 -7
- package/src/platforms/wechatbot/commands/whoami.test.ts +5 -5
- package/src/platforms/wechatbot/credential-manager.test.ts +18 -18
- package/src/platforms/wechatbot/index.test.ts +10 -10
- package/src/platforms/wechatbot/types.test.ts +25 -25
- package/src/platforms/whatsapp/commands/auth.test.ts +13 -13
- package/src/platforms/whatsapp/commands/chat.test.ts +8 -8
- package/src/platforms/whatsapp/commands/message.test.ts +10 -10
- package/src/platforms/whatsapp/commands/whoami.test.ts +3 -3
- package/src/platforms/whatsapp/credential-manager.test.ts +23 -23
- package/src/platforms/whatsapp/ensure-auth.test.ts +4 -4
- package/src/platforms/whatsapp/index.test.ts +8 -8
- package/src/platforms/whatsapp/types.test.ts +42 -42
- package/src/platforms/whatsappbot/client.test.ts +27 -27
- package/src/platforms/whatsappbot/commands/auth.test.ts +14 -14
- package/src/platforms/whatsappbot/commands/message.test.ts +16 -16
- package/src/platforms/whatsappbot/commands/template.test.ts +9 -9
- package/src/platforms/whatsappbot/commands/whoami.test.ts +5 -5
- package/src/platforms/whatsappbot/credential-manager.test.ts +18 -18
- package/src/platforms/whatsappbot/index.test.ts +7 -7
- package/src/platforms/whatsappbot/types.test.ts +18 -18
- package/src/shared/chromium/browsers.test.ts +22 -22
- package/src/shared/chromium/cookie-reader.test.ts +13 -13
- package/src/shared/chromium/decryptor.test.ts +97 -32
- package/src/shared/chromium/decryptor.ts +27 -6
- package/src/shared/utils/concurrency.test.ts +6 -6
- package/src/shared/utils/derived-key-cache.test.ts +11 -11
- package/src/tui/utils.test.ts +31 -31
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, spyOn,
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, spyOn, it } from 'bun:test'
|
|
2
2
|
import * as childProcess from 'node:child_process'
|
|
3
3
|
|
|
4
4
|
import { WebexClient } from '../client'
|
|
@@ -42,7 +42,7 @@ describe('auth commands', () => {
|
|
|
42
42
|
})
|
|
43
43
|
|
|
44
44
|
describe('loginAction with --token', () => {
|
|
45
|
-
|
|
45
|
+
it('authenticates with provided token (bot token flow)', async () => {
|
|
46
46
|
protoSpy(WebexClient.prototype, 'login').mockResolvedValue(new WebexClient())
|
|
47
47
|
protoSpy(WebexClient.prototype, 'testAuth').mockResolvedValue(mockPerson)
|
|
48
48
|
protoSpy(WebexCredentialManager.prototype, 'saveConfig').mockResolvedValue(undefined)
|
|
@@ -56,7 +56,7 @@ describe('auth commands', () => {
|
|
|
56
56
|
expect(output.user.displayName).toBe('Test User')
|
|
57
57
|
})
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
it('saves tokenType as manual with expiresAt 0', async () => {
|
|
60
60
|
protoSpy(WebexClient.prototype, 'login').mockResolvedValue(new WebexClient())
|
|
61
61
|
protoSpy(WebexClient.prototype, 'testAuth').mockResolvedValue(mockPerson)
|
|
62
62
|
const saveSpy = protoSpy(WebexCredentialManager.prototype, 'saveConfig').mockResolvedValue(undefined)
|
|
@@ -71,7 +71,7 @@ describe('auth commands', () => {
|
|
|
71
71
|
})
|
|
72
72
|
|
|
73
73
|
describe('loginAction with --client-id and --client-secret', () => {
|
|
74
|
-
|
|
74
|
+
it('uses provided credentials for Device Grant flow', async () => {
|
|
75
75
|
protoSpy(WebexCredentialManager.prototype, 'requestDeviceCode').mockResolvedValue({
|
|
76
76
|
deviceCode: 'd',
|
|
77
77
|
userCode: 'u',
|
|
@@ -101,7 +101,7 @@ describe('auth commands', () => {
|
|
|
101
101
|
)
|
|
102
102
|
})
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
it('saves tokenType as oauth in config', async () => {
|
|
105
105
|
protoSpy(WebexCredentialManager.prototype, 'requestDeviceCode').mockResolvedValue({
|
|
106
106
|
deviceCode: 'd',
|
|
107
107
|
userCode: 'u',
|
|
@@ -125,7 +125,7 @@ describe('auth commands', () => {
|
|
|
125
125
|
expect(savedConfig.tokenType).toBe('oauth')
|
|
126
126
|
})
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
it('saves clientId and clientSecret in config', async () => {
|
|
129
129
|
protoSpy(WebexCredentialManager.prototype, 'requestDeviceCode').mockResolvedValue({
|
|
130
130
|
deviceCode: 'd',
|
|
131
131
|
userCode: 'u',
|
|
@@ -152,7 +152,7 @@ describe('auth commands', () => {
|
|
|
152
152
|
})
|
|
153
153
|
|
|
154
154
|
describe('statusAction', () => {
|
|
155
|
-
|
|
155
|
+
it('shows authenticated status when token is valid', async () => {
|
|
156
156
|
protoSpy(WebexCredentialManager.prototype, 'loadConfig').mockResolvedValue(null)
|
|
157
157
|
protoSpy(WebexCredentialManager.prototype, 'getToken').mockResolvedValue('valid-token')
|
|
158
158
|
protoSpy(WebexClient.prototype, 'login').mockResolvedValue(new WebexClient())
|
|
@@ -166,7 +166,7 @@ describe('auth commands', () => {
|
|
|
166
166
|
expect(output.user.displayName).toBe('Test User')
|
|
167
167
|
})
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
it('shows not authenticated when no token', async () => {
|
|
170
170
|
protoSpy(WebexCredentialManager.prototype, 'loadConfig').mockResolvedValue(null)
|
|
171
171
|
protoSpy(WebexCredentialManager.prototype, 'getToken').mockResolvedValue(null)
|
|
172
172
|
const exitSpy = protoSpy(process, 'exit').mockImplementation(() => undefined as never)
|
|
@@ -179,7 +179,7 @@ describe('auth commands', () => {
|
|
|
179
179
|
expect(exitSpy).toHaveBeenCalledWith(1)
|
|
180
180
|
})
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
it('shows not authenticated when token validation fails', async () => {
|
|
183
183
|
protoSpy(WebexCredentialManager.prototype, 'loadConfig').mockResolvedValue(null)
|
|
184
184
|
protoSpy(WebexCredentialManager.prototype, 'getToken').mockResolvedValue('invalid-token')
|
|
185
185
|
protoSpy(WebexClient.prototype, 'login').mockResolvedValue(new WebexClient())
|
|
@@ -192,7 +192,7 @@ describe('auth commands', () => {
|
|
|
192
192
|
expect(output.authenticated).toBe(false)
|
|
193
193
|
})
|
|
194
194
|
|
|
195
|
-
|
|
195
|
+
it('loads config for stored client credentials', async () => {
|
|
196
196
|
protoSpy(WebexCredentialManager.prototype, 'loadConfig').mockResolvedValue({
|
|
197
197
|
accessToken: 'at',
|
|
198
198
|
refreshToken: 'rt',
|
|
@@ -211,7 +211,7 @@ describe('auth commands', () => {
|
|
|
211
211
|
})
|
|
212
212
|
|
|
213
213
|
describe('extractAction', () => {
|
|
214
|
-
|
|
214
|
+
it('passes deviceUrl and tokenType to client.login', async () => {
|
|
215
215
|
protoSpy(WebexTokenExtractor.prototype, 'extract').mockResolvedValue({
|
|
216
216
|
accessToken: 'extracted-token-at-least-twenty-chars',
|
|
217
217
|
refreshToken: 'refresh-token',
|
|
@@ -232,7 +232,7 @@ describe('auth commands', () => {
|
|
|
232
232
|
})
|
|
233
233
|
})
|
|
234
234
|
|
|
235
|
-
|
|
235
|
+
it('attempts refresh when token is expired', async () => {
|
|
236
236
|
protoSpy(WebexTokenExtractor.prototype, 'extract').mockResolvedValue({
|
|
237
237
|
accessToken: 'expired-token-at-least-twenty-chars-',
|
|
238
238
|
refreshToken: 'valid-refresh-token',
|
|
@@ -257,7 +257,7 @@ describe('auth commands', () => {
|
|
|
257
257
|
expect(output.refreshed).toBe(true)
|
|
258
258
|
})
|
|
259
259
|
|
|
260
|
-
|
|
260
|
+
it('reports expired token with actionable hint when refresh fails', async () => {
|
|
261
261
|
protoSpy(WebexTokenExtractor.prototype, 'extract').mockResolvedValue({
|
|
262
262
|
accessToken: 'expired-token-at-least-twenty-chars-',
|
|
263
263
|
refreshToken: 'bad-refresh-token',
|
|
@@ -278,7 +278,7 @@ describe('auth commands', () => {
|
|
|
278
278
|
expect(exitSpy).toHaveBeenCalledWith(1)
|
|
279
279
|
})
|
|
280
280
|
|
|
281
|
-
|
|
281
|
+
it('rethrows non-auth errors even when token is expired', async () => {
|
|
282
282
|
protoSpy(WebexTokenExtractor.prototype, 'extract').mockResolvedValue({
|
|
283
283
|
accessToken: 'expired-token-at-least-twenty-chars-',
|
|
284
284
|
refreshToken: 'bad-refresh-token',
|
|
@@ -298,7 +298,7 @@ describe('auth commands', () => {
|
|
|
298
298
|
}
|
|
299
299
|
})
|
|
300
300
|
|
|
301
|
-
|
|
301
|
+
it('rethrows non-expiry auth errors', async () => {
|
|
302
302
|
protoSpy(WebexTokenExtractor.prototype, 'extract').mockResolvedValue({
|
|
303
303
|
accessToken: 'valid-token-at-least-twenty-chars-xx',
|
|
304
304
|
expiresAt: Date.now() + 3600000,
|
|
@@ -316,7 +316,7 @@ describe('auth commands', () => {
|
|
|
316
316
|
}
|
|
317
317
|
})
|
|
318
318
|
|
|
319
|
-
|
|
319
|
+
it('outputs no token found when extract returns null', async () => {
|
|
320
320
|
protoSpy(WebexTokenExtractor.prototype, 'extract').mockResolvedValue(null)
|
|
321
321
|
const exitSpy = protoSpy(process, 'exit').mockImplementation(() => undefined as never)
|
|
322
322
|
|
|
@@ -330,7 +330,7 @@ describe('auth commands', () => {
|
|
|
330
330
|
})
|
|
331
331
|
|
|
332
332
|
describe('logoutAction', () => {
|
|
333
|
-
|
|
333
|
+
it('clears credentials when authenticated', async () => {
|
|
334
334
|
protoSpy(WebexCredentialManager.prototype, 'loadConfig').mockResolvedValue({
|
|
335
335
|
accessToken: 'token',
|
|
336
336
|
refreshToken: 'refresh',
|
|
@@ -346,7 +346,7 @@ describe('auth commands', () => {
|
|
|
346
346
|
expect(output.success).toBe(true)
|
|
347
347
|
})
|
|
348
348
|
|
|
349
|
-
|
|
349
|
+
it('shows error when not authenticated', async () => {
|
|
350
350
|
protoSpy(WebexCredentialManager.prototype, 'loadConfig').mockResolvedValue(null)
|
|
351
351
|
const exitSpy = protoSpy(process, 'exit').mockImplementation(() => undefined as never)
|
|
352
352
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, mock, spyOn,
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, spyOn, it } from 'bun:test'
|
|
2
2
|
|
|
3
3
|
import { WebexError } from '../types'
|
|
4
4
|
|
|
@@ -59,7 +59,7 @@ describe('member commands', () => {
|
|
|
59
59
|
consoleSpy.mockRestore()
|
|
60
60
|
})
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
it('calls listMemberships with spaceId and outputs mapped members', async () => {
|
|
63
63
|
await listAction('room-1', {})
|
|
64
64
|
|
|
65
65
|
expect(mockListMemberships).toHaveBeenCalledWith('room-1', { max: undefined })
|
|
@@ -85,13 +85,13 @@ describe('member commands', () => {
|
|
|
85
85
|
)
|
|
86
86
|
})
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
it('passes limit option to listMemberships', async () => {
|
|
89
89
|
await listAction('room-1', { limit: 25 })
|
|
90
90
|
|
|
91
91
|
expect(mockListMemberships).toHaveBeenCalledWith('room-1', { max: 25 })
|
|
92
92
|
})
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
it('throws when not authenticated', async () => {
|
|
95
95
|
mockLogin.mockImplementation(async () => {
|
|
96
96
|
throw new WebexError('No Webex credentials found.', 'no_credentials')
|
|
97
97
|
})
|
|
@@ -101,7 +101,7 @@ describe('member commands', () => {
|
|
|
101
101
|
expect(mockHandleError).toHaveBeenCalledWith(expect.any(WebexError))
|
|
102
102
|
})
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
it('throws on API error', async () => {
|
|
105
105
|
mockListMemberships.mockImplementation(async () => {
|
|
106
106
|
throw new Error('API failure')
|
|
107
107
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterEach, beforeEach, expect, mock, spyOn,
|
|
1
|
+
import { afterEach, beforeEach, expect, mock, spyOn, it } from 'bun:test'
|
|
2
2
|
|
|
3
3
|
import { WebexError } from '../types'
|
|
4
4
|
|
|
@@ -77,7 +77,7 @@ afterEach(() => {
|
|
|
77
77
|
consoleLogSpy.mockRestore()
|
|
78
78
|
})
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
it('calls sendMessage with correct args and outputs result', async () => {
|
|
81
81
|
await sendAction('space_456', 'Hello world', { pretty: false })
|
|
82
82
|
|
|
83
83
|
expect(mockSendMessage).toHaveBeenCalledWith('space_456', 'Hello world', {
|
|
@@ -90,13 +90,13 @@ test('send: calls sendMessage with correct args and outputs result', async () =>
|
|
|
90
90
|
expect(output).toContain('user@example.com')
|
|
91
91
|
})
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
it('passes markdown option when --markdown flag is set on send', async () => {
|
|
94
94
|
await sendAction('space_456', '**bold**', { markdown: true, pretty: false })
|
|
95
95
|
|
|
96
96
|
expect(mockSendMessage).toHaveBeenCalledWith('space_456', '**bold**', { markdown: true })
|
|
97
97
|
})
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
it('throws when not authenticated on send', async () => {
|
|
100
100
|
mockLogin.mockImplementation(async () => {
|
|
101
101
|
throw new WebexError('No Webex credentials found.', 'no_credentials')
|
|
102
102
|
})
|
|
@@ -106,7 +106,7 @@ test('send: not authenticated shows error', async () => {
|
|
|
106
106
|
expect(mockHandleError).toHaveBeenCalledWith(expect.any(WebexError))
|
|
107
107
|
})
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
it('calls sendDirectMessage with email and text', async () => {
|
|
110
110
|
await dmAction('alice@example.com', 'Hello!', { pretty: false })
|
|
111
111
|
|
|
112
112
|
expect(mockSendDirectMessage).toHaveBeenCalledWith('alice@example.com', 'Hello!', {
|
|
@@ -117,7 +117,7 @@ test('dm: calls sendDirectMessage with email and text', async () => {
|
|
|
117
117
|
expect(output).toContain('msg_123')
|
|
118
118
|
})
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
it('passes markdown option to sendDirectMessage when --markdown flag is set', async () => {
|
|
121
121
|
await dmAction('alice@example.com', '**bold**', { markdown: true, pretty: false })
|
|
122
122
|
|
|
123
123
|
expect(mockSendDirectMessage).toHaveBeenCalledWith('alice@example.com', '**bold**', {
|
|
@@ -125,7 +125,7 @@ test('dm: with --markdown passes markdown option', async () => {
|
|
|
125
125
|
})
|
|
126
126
|
})
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
it('calls listMessages with limit and outputs array', async () => {
|
|
129
129
|
await listAction('space_456', { limit: 50, pretty: false })
|
|
130
130
|
|
|
131
131
|
expect(mockListMessages).toHaveBeenCalledWith('space_456', { max: 50 })
|
|
@@ -135,7 +135,7 @@ test('list: calls listMessages with limit and outputs array', async () => {
|
|
|
135
135
|
expect(output).toContain('msg_124')
|
|
136
136
|
})
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
it('calls getMessage with correct id and outputs result', async () => {
|
|
139
139
|
await getAction('msg_123', { pretty: false })
|
|
140
140
|
|
|
141
141
|
expect(mockGetMessage).toHaveBeenCalledWith('msg_123')
|
|
@@ -145,7 +145,7 @@ test('get: calls getMessage with correct id and outputs result', async () => {
|
|
|
145
145
|
expect(output).toContain('user@example.com')
|
|
146
146
|
})
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
it('calls deleteMessage and outputs deleted id when --force flag is set', async () => {
|
|
149
149
|
await deleteAction('msg_123', { force: true, pretty: false })
|
|
150
150
|
|
|
151
151
|
expect(mockDeleteMessage).toHaveBeenCalledWith('msg_123')
|
|
@@ -155,7 +155,7 @@ test('delete: with --force calls deleteMessage and outputs deleted id', async ()
|
|
|
155
155
|
expect(output).toContain('msg_123')
|
|
156
156
|
})
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
it('shows warning and does not delete without --force flag', async () => {
|
|
159
159
|
try {
|
|
160
160
|
await deleteAction('msg_123', { force: false, pretty: false })
|
|
161
161
|
} catch {}
|
|
@@ -167,7 +167,7 @@ test('delete: without --force shows warning and does not delete', async () => {
|
|
|
167
167
|
expect(output).toContain('--force')
|
|
168
168
|
})
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
it('calls editMessage with roomId in args and outputs result', async () => {
|
|
171
171
|
await editAction('msg_123', 'space_456', 'Updated message', { pretty: false })
|
|
172
172
|
|
|
173
173
|
expect(mockEditMessage).toHaveBeenCalledWith('msg_123', 'space_456', 'Updated message', {
|
|
@@ -179,7 +179,7 @@ test('edit: calls editMessage with roomId in args and outputs result', async ()
|
|
|
179
179
|
expect(output).toContain('Updated message')
|
|
180
180
|
})
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
it('passes markdown option to editMessage when --markdown flag is set', async () => {
|
|
183
183
|
await editAction('msg_123', 'space_456', '**updated**', { markdown: true, pretty: false })
|
|
184
184
|
|
|
185
185
|
expect(mockEditMessage).toHaveBeenCalledWith('msg_123', 'space_456', '**updated**', {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, mock, spyOn,
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, spyOn, it } from 'bun:test'
|
|
2
2
|
|
|
3
3
|
import { WebexError } from '../types'
|
|
4
4
|
|
|
@@ -79,7 +79,7 @@ describe('snapshot command', () => {
|
|
|
79
79
|
consoleSpy.mockRestore()
|
|
80
80
|
})
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
it('returns spaces with id and title only in brief mode', async () => {
|
|
83
83
|
await snapshotAction({})
|
|
84
84
|
|
|
85
85
|
expect(consoleSpy).toHaveBeenCalled()
|
|
@@ -91,7 +91,7 @@ describe('snapshot command', () => {
|
|
|
91
91
|
expect(output.hint).toBeDefined()
|
|
92
92
|
})
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
it('returns spaces with id, title, type, and lastActivity in full mode', async () => {
|
|
95
95
|
await snapshotAction({ full: true })
|
|
96
96
|
|
|
97
97
|
expect(consoleSpy).toHaveBeenCalled()
|
|
@@ -104,7 +104,7 @@ describe('snapshot command', () => {
|
|
|
104
104
|
expect(output.hint).toBeUndefined()
|
|
105
105
|
})
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
it('filters spaces to only those in my memberships', async () => {
|
|
108
108
|
await snapshotAction({})
|
|
109
109
|
|
|
110
110
|
const output = JSON.parse(consoleSpy.mock.calls[consoleSpy.mock.calls.length - 1][0])
|
|
@@ -112,7 +112,7 @@ describe('snapshot command', () => {
|
|
|
112
112
|
expect(output.spaces[0].id).toBe('space-1')
|
|
113
113
|
})
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
it('throws when not authenticated', async () => {
|
|
116
116
|
mockLogin.mockImplementation(async () => {
|
|
117
117
|
throw new WebexError('No Webex credentials found.', 'no_credentials')
|
|
118
118
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, mock, spyOn,
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, spyOn, it } from 'bun:test'
|
|
2
2
|
|
|
3
3
|
import { WebexError } from '../types'
|
|
4
4
|
|
|
@@ -74,7 +74,7 @@ afterEach(() => {
|
|
|
74
74
|
})
|
|
75
75
|
|
|
76
76
|
describe('listAction', () => {
|
|
77
|
-
|
|
77
|
+
it('calls listSpaces and outputs mapped array', async () => {
|
|
78
78
|
await listAction({})
|
|
79
79
|
|
|
80
80
|
expect(mockListSpaces).toHaveBeenCalled()
|
|
@@ -98,19 +98,19 @@ describe('listAction', () => {
|
|
|
98
98
|
)
|
|
99
99
|
})
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
it('passes type and limit options to listSpaces', async () => {
|
|
102
102
|
await listAction({ type: 'group', limit: 10 })
|
|
103
103
|
|
|
104
104
|
expect(mockListSpaces).toHaveBeenCalledWith({ type: 'group', max: 10 })
|
|
105
105
|
})
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
it('passes undefined type and limit when not provided', async () => {
|
|
108
108
|
await listAction({})
|
|
109
109
|
|
|
110
110
|
expect(mockListSpaces).toHaveBeenCalledWith({ type: undefined, max: undefined })
|
|
111
111
|
})
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
it('outputs pretty-printed JSON when pretty is true', async () => {
|
|
114
114
|
await listAction({ pretty: true })
|
|
115
115
|
|
|
116
116
|
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
@@ -137,7 +137,7 @@ describe('listAction', () => {
|
|
|
137
137
|
)
|
|
138
138
|
})
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
it('throws when not authenticated', async () => {
|
|
141
141
|
mockLogin.mockImplementation(async () => {
|
|
142
142
|
throw new WebexError('No Webex credentials found.', 'no_credentials')
|
|
143
143
|
})
|
|
@@ -150,7 +150,7 @@ describe('listAction', () => {
|
|
|
150
150
|
})
|
|
151
151
|
|
|
152
152
|
describe('infoAction', () => {
|
|
153
|
-
|
|
153
|
+
it('calls getSpace with spaceId and outputs space details', async () => {
|
|
154
154
|
await infoAction('space-1', {})
|
|
155
155
|
|
|
156
156
|
expect(mockGetSpace).toHaveBeenCalledWith('space-1')
|
|
@@ -168,7 +168,7 @@ describe('infoAction', () => {
|
|
|
168
168
|
)
|
|
169
169
|
})
|
|
170
170
|
|
|
171
|
-
|
|
171
|
+
it('outputs null for teamId when not present', async () => {
|
|
172
172
|
mockGetSpace.mockImplementation(() => Promise.resolve({ ...mockSpace, teamId: undefined }))
|
|
173
173
|
|
|
174
174
|
await infoAction('space-1', {})
|
|
@@ -179,7 +179,7 @@ describe('infoAction', () => {
|
|
|
179
179
|
expect(output.teamId).toBeNull()
|
|
180
180
|
})
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
it('outputs pretty-printed JSON when pretty is true', async () => {
|
|
183
183
|
await infoAction('space-1', { pretty: true })
|
|
184
184
|
|
|
185
185
|
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
@@ -200,7 +200,7 @@ describe('infoAction', () => {
|
|
|
200
200
|
)
|
|
201
201
|
})
|
|
202
202
|
|
|
203
|
-
|
|
203
|
+
it('throws when not authenticated', async () => {
|
|
204
204
|
mockLogin.mockImplementation(async () => {
|
|
205
205
|
throw new WebexError('No Webex credentials found.', 'no_credentials')
|
|
206
206
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterEach, beforeEach, expect, spyOn,
|
|
1
|
+
import { afterEach, beforeEach, expect, spyOn, it } from 'bun:test'
|
|
2
2
|
|
|
3
3
|
import * as clientModule from '../client'
|
|
4
4
|
import { WebexError } from '../types'
|
|
@@ -42,19 +42,19 @@ afterEach(() => {
|
|
|
42
42
|
processExitSpy?.mockRestore()
|
|
43
43
|
})
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
it('whoami command is defined with correct name and description', () => {
|
|
46
46
|
expect(whoamiCommand).toBeDefined()
|
|
47
47
|
expect(whoamiCommand.name()).toBe('whoami')
|
|
48
48
|
expect(whoamiCommand.description()).toBe('Show current authenticated user')
|
|
49
49
|
})
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
it('whoami command has --pretty option', () => {
|
|
52
52
|
const options = whoamiCommand.options
|
|
53
53
|
const hasPretty = options.some((opt: { long?: string }) => opt.long === '--pretty')
|
|
54
54
|
expect(hasPretty).toBe(true)
|
|
55
55
|
})
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
it('whoami calls testAuth and outputs user fields', async () => {
|
|
58
58
|
// given: authenticated webex user
|
|
59
59
|
// when: running whoami
|
|
60
60
|
await whoamiCommand.parseAsync([], { from: 'user' })
|
|
@@ -75,7 +75,7 @@ test('whoami calls testAuth and outputs user fields', async () => {
|
|
|
75
75
|
)
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
it('whoami outputs pretty-printed JSON when --pretty flag is passed', async () => {
|
|
79
79
|
// given: authenticated webex user
|
|
80
80
|
// when: running whoami with --pretty
|
|
81
81
|
await whoamiCommand.parseAsync(['--pretty'], { from: 'user' })
|
|
@@ -100,7 +100,7 @@ test('whoami outputs pretty-printed JSON when --pretty flag is passed', async ()
|
|
|
100
100
|
)
|
|
101
101
|
})
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
it('whoami exits with code 1 when not authenticated', async () => {
|
|
104
104
|
// given: no credentials
|
|
105
105
|
webexClientSpy.mockImplementation(
|
|
106
106
|
() =>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, mock,
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, it } from 'bun:test'
|
|
2
2
|
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
3
3
|
import { tmpdir } from 'node:os'
|
|
4
4
|
import { join } from 'node:path'
|
|
@@ -18,11 +18,11 @@ describe('WebexCredentialManager', () => {
|
|
|
18
18
|
await rm(tempDir, { recursive: true, force: true })
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
it('loadConfig returns null when no file exists', async () => {
|
|
22
22
|
expect(await credManager.loadConfig()).toBeNull()
|
|
23
23
|
})
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
it('saveConfig and loadConfig round-trip OAuth tokens', async () => {
|
|
26
26
|
const config = {
|
|
27
27
|
accessToken: 'test-access-token',
|
|
28
28
|
refreshToken: 'test-refresh-token',
|
|
@@ -33,7 +33,7 @@ describe('WebexCredentialManager', () => {
|
|
|
33
33
|
expect(loaded).toEqual(config)
|
|
34
34
|
})
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
it('getToken returns accessToken when not expired', async () => {
|
|
37
37
|
await credManager.saveConfig({
|
|
38
38
|
accessToken: 'valid-token',
|
|
39
39
|
refreshToken: 'refresh',
|
|
@@ -43,7 +43,7 @@ describe('WebexCredentialManager', () => {
|
|
|
43
43
|
expect(token).toBe('valid-token')
|
|
44
44
|
})
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
it('getToken returns null when expired and no refresh available', async () => {
|
|
47
47
|
await credManager.saveConfig({
|
|
48
48
|
accessToken: 'expired-token',
|
|
49
49
|
refreshToken: 'bad-refresh',
|
|
@@ -53,7 +53,7 @@ describe('WebexCredentialManager', () => {
|
|
|
53
53
|
expect(token).toBeNull()
|
|
54
54
|
})
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
it('getToken auto-refreshes expired token', async () => {
|
|
57
57
|
const originalFetch = globalThis.fetch
|
|
58
58
|
globalThis.fetch = mock(() =>
|
|
59
59
|
Promise.resolve(
|
|
@@ -87,7 +87,7 @@ describe('WebexCredentialManager', () => {
|
|
|
87
87
|
globalThis.fetch = originalFetch
|
|
88
88
|
})
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
it('requestDeviceCode calls device authorize endpoint', async () => {
|
|
91
91
|
const originalFetch = globalThis.fetch
|
|
92
92
|
globalThis.fetch = mock(() =>
|
|
93
93
|
Promise.resolve(
|
|
@@ -114,7 +114,7 @@ describe('WebexCredentialManager', () => {
|
|
|
114
114
|
globalThis.fetch = originalFetch
|
|
115
115
|
})
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
it('requestDeviceCode throws on failure', async () => {
|
|
118
118
|
const originalFetch = globalThis.fetch
|
|
119
119
|
globalThis.fetch = mock(() =>
|
|
120
120
|
Promise.resolve(new Response('{"error":"invalid_client"}', { status: 400 })),
|
|
@@ -125,7 +125,7 @@ describe('WebexCredentialManager', () => {
|
|
|
125
125
|
globalThis.fetch = originalFetch
|
|
126
126
|
})
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
it('pollDeviceToken polls until authorized', async () => {
|
|
129
129
|
const originalFetch = globalThis.fetch
|
|
130
130
|
let callCount = 0
|
|
131
131
|
globalThis.fetch = mock(() => {
|
|
@@ -152,7 +152,7 @@ describe('WebexCredentialManager', () => {
|
|
|
152
152
|
globalThis.fetch = originalFetch
|
|
153
153
|
})
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
it('clearCredentials removes the file', async () => {
|
|
156
156
|
await credManager.saveConfig({
|
|
157
157
|
accessToken: 'token',
|
|
158
158
|
refreshToken: 'refresh',
|
|
@@ -162,11 +162,11 @@ describe('WebexCredentialManager', () => {
|
|
|
162
162
|
expect(await credManager.loadConfig()).toBeNull()
|
|
163
163
|
})
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
it('clearCredentials does nothing when no file', async () => {
|
|
166
166
|
await credManager.clearCredentials() // Should not throw
|
|
167
167
|
})
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
it('saves credentials file with 0o600 permissions', async () => {
|
|
170
170
|
await credManager.saveConfig({
|
|
171
171
|
accessToken: 'token',
|
|
172
172
|
refreshToken: 'refresh',
|
|
@@ -179,7 +179,7 @@ describe('WebexCredentialManager', () => {
|
|
|
179
179
|
expect(mode).toBe(0o600)
|
|
180
180
|
})
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
it('pollDeviceToken with undefined clientSecret uses empty Basic auth', async () => {
|
|
183
183
|
const originalFetch = globalThis.fetch
|
|
184
184
|
let capturedAuth: string | null = null
|
|
185
185
|
globalThis.fetch = mock((url: string, init?: RequestInit) => {
|
|
@@ -202,7 +202,7 @@ describe('WebexCredentialManager', () => {
|
|
|
202
202
|
globalThis.fetch = originalFetch
|
|
203
203
|
})
|
|
204
204
|
|
|
205
|
-
|
|
205
|
+
it('pollDeviceToken does not auto-save config', async () => {
|
|
206
206
|
const originalFetch = globalThis.fetch
|
|
207
207
|
globalThis.fetch = mock(() =>
|
|
208
208
|
Promise.resolve(
|
|
@@ -225,7 +225,7 @@ describe('WebexCredentialManager', () => {
|
|
|
225
225
|
globalThis.fetch = originalFetch
|
|
226
226
|
})
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
it('getToken returns null when expired and no client credentials available', async () => {
|
|
229
229
|
await credManager.saveConfig({
|
|
230
230
|
accessToken: 'expired-token',
|
|
231
231
|
refreshToken: 'valid-refresh',
|
|
@@ -236,7 +236,7 @@ describe('WebexCredentialManager', () => {
|
|
|
236
236
|
expect(token).toBeNull()
|
|
237
237
|
})
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
it('getToken returns manual token without attempting refresh', async () => {
|
|
240
240
|
await credManager.saveConfig({
|
|
241
241
|
accessToken: 'my-bot-token',
|
|
242
242
|
refreshToken: '',
|
|
@@ -248,7 +248,7 @@ describe('WebexCredentialManager', () => {
|
|
|
248
248
|
expect(token).toBe('my-bot-token')
|
|
249
249
|
})
|
|
250
250
|
|
|
251
|
-
|
|
251
|
+
it('getToken uses stored clientId/clientSecret for refresh', async () => {
|
|
252
252
|
const originalFetch = globalThis.fetch
|
|
253
253
|
globalThis.fetch = mock(() =>
|
|
254
254
|
Promise.resolve(
|
|
@@ -277,7 +277,7 @@ describe('WebexCredentialManager', () => {
|
|
|
277
277
|
globalThis.fetch = originalFetch
|
|
278
278
|
})
|
|
279
279
|
|
|
280
|
-
|
|
280
|
+
it('saveConfig persists clientId and clientSecret', async () => {
|
|
281
281
|
await credManager.saveConfig({
|
|
282
282
|
accessToken: 'token',
|
|
283
283
|
refreshToken: 'refresh',
|
|
@@ -291,7 +291,7 @@ describe('WebexCredentialManager', () => {
|
|
|
291
291
|
expect(loaded?.clientSecret).toBe('my-client-secret')
|
|
292
292
|
})
|
|
293
293
|
|
|
294
|
-
|
|
294
|
+
it('getToken tries refresh for expired extracted tokens', async () => {
|
|
295
295
|
const originalFetch = globalThis.fetch
|
|
296
296
|
globalThis.fetch = mock(() =>
|
|
297
297
|
Promise.resolve(
|
|
@@ -323,7 +323,7 @@ describe('WebexCredentialManager', () => {
|
|
|
323
323
|
globalThis.fetch = originalFetch
|
|
324
324
|
})
|
|
325
325
|
|
|
326
|
-
|
|
326
|
+
it('getToken returns expired extracted token when refresh fails', async () => {
|
|
327
327
|
const originalFetch = globalThis.fetch
|
|
328
328
|
globalThis.fetch = mock(() =>
|
|
329
329
|
Promise.resolve(new Response('{"error":"invalid_grant"}', { status: 400 })),
|
|
@@ -342,7 +342,7 @@ describe('WebexCredentialManager', () => {
|
|
|
342
342
|
globalThis.fetch = originalFetch
|
|
343
343
|
})
|
|
344
344
|
|
|
345
|
-
|
|
345
|
+
it('getToken returns non-expired extracted token without refresh', async () => {
|
|
346
346
|
await credManager.saveConfig({
|
|
347
347
|
accessToken: 'valid-extracted-token',
|
|
348
348
|
refreshToken: 'refresh',
|
|
@@ -354,7 +354,7 @@ describe('WebexCredentialManager', () => {
|
|
|
354
354
|
expect(token).toBe('valid-extracted-token')
|
|
355
355
|
})
|
|
356
356
|
|
|
357
|
-
|
|
357
|
+
it('loadConfig handles old config without clientId/clientSecret', async () => {
|
|
358
358
|
// Write raw JSON without clientId/clientSecret fields
|
|
359
359
|
const credPath = join(tempDir, 'webex-credentials.json')
|
|
360
360
|
await writeFile(
|