agent-messenger 2.3.0 → 2.4.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/.claude-plugin/README.md +16 -16
- package/.claude-plugin/marketplace.json +29 -29
- package/.claude-plugin/plugin.json +5 -5
- package/CONTRIBUTING.md +1 -1
- package/README.md +8 -5
- package/bun.lock +70 -110
- package/bunfig.toml +3 -0
- package/dist/package.json +11 -3
- package/dist/src/platforms/discordbot/client.js +2 -2
- package/dist/src/platforms/discordbot/client.js.map +1 -1
- package/dist/src/platforms/kakaotalk/cli.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/cli.js +2 -1
- package/dist/src/platforms/kakaotalk/cli.js.map +1 -1
- package/dist/src/platforms/kakaotalk/client.d.ts +2 -1
- package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/client.js +52 -2
- package/dist/src/platforms/kakaotalk/client.js.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/index.d.ts +1 -0
- package/dist/src/platforms/kakaotalk/commands/index.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/index.js +1 -0
- package/dist/src/platforms/kakaotalk/commands/index.js.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/profile.d.ts +3 -0
- package/dist/src/platforms/kakaotalk/commands/profile.d.ts.map +1 -0
- package/dist/src/platforms/kakaotalk/commands/profile.js +19 -0
- package/dist/src/platforms/kakaotalk/commands/profile.js.map +1 -0
- package/dist/src/platforms/kakaotalk/index.d.ts +2 -2
- package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/index.js +1 -1
- package/dist/src/platforms/kakaotalk/index.js.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.js +2 -1
- package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
- package/dist/src/platforms/kakaotalk/types.d.ts +16 -0
- package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/types.js +8 -0
- package/dist/src/platforms/kakaotalk/types.js.map +1 -1
- package/dist/src/platforms/line/client.d.ts.map +1 -1
- package/dist/src/platforms/line/client.js +9 -36
- package/dist/src/platforms/line/client.js.map +1 -1
- package/dist/src/platforms/line/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/line/commands/auth.js +32 -20
- package/dist/src/platforms/line/commands/auth.js.map +1 -1
- package/dist/src/platforms/teams/commands/reaction.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/reaction.js +2 -0
- package/dist/src/platforms/teams/commands/reaction.js.map +1 -1
- package/dist/src/platforms/wechatbot/cli.d.ts +5 -0
- package/dist/src/platforms/wechatbot/cli.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/cli.js +18 -0
- package/dist/src/platforms/wechatbot/cli.js.map +1 -0
- package/dist/src/platforms/wechatbot/client.d.ts +36 -0
- package/dist/src/platforms/wechatbot/client.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/client.js +208 -0
- package/dist/src/platforms/wechatbot/client.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/auth.d.ts +28 -0
- package/dist/src/platforms/wechatbot/commands/auth.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/auth.js +164 -0
- package/dist/src/platforms/wechatbot/commands/auth.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/index.d.ts +5 -0
- package/dist/src/platforms/wechatbot/commands/index.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/index.js +5 -0
- package/dist/src/platforms/wechatbot/commands/index.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/message.d.ts +18 -0
- package/dist/src/platforms/wechatbot/commands/message.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/message.js +80 -0
- package/dist/src/platforms/wechatbot/commands/message.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/shared.d.ts +9 -0
- package/dist/src/platforms/wechatbot/commands/shared.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/shared.js +13 -0
- package/dist/src/platforms/wechatbot/commands/shared.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/template.d.ts +19 -0
- package/dist/src/platforms/wechatbot/commands/template.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/template.js +76 -0
- package/dist/src/platforms/wechatbot/commands/template.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/user.d.ts +20 -0
- package/dist/src/platforms/wechatbot/commands/user.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/user.js +53 -0
- package/dist/src/platforms/wechatbot/commands/user.js.map +1 -0
- package/dist/src/platforms/wechatbot/credential-manager.d.ts +17 -0
- package/dist/src/platforms/wechatbot/credential-manager.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/credential-manager.js +121 -0
- package/dist/src/platforms/wechatbot/credential-manager.js.map +1 -0
- package/dist/src/platforms/wechatbot/index.d.ts +5 -0
- package/dist/src/platforms/wechatbot/index.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/index.js +4 -0
- package/dist/src/platforms/wechatbot/index.js.map +1 -0
- package/dist/src/platforms/wechatbot/types.d.ts +94 -0
- package/dist/src/platforms/wechatbot/types.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/types.js +54 -0
- package/dist/src/platforms/wechatbot/types.js.map +1 -0
- package/dist/src/platforms/whatsapp/client.d.ts +1 -0
- package/dist/src/platforms/whatsapp/client.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/client.js +27 -13
- package/dist/src/platforms/whatsapp/client.js.map +1 -1
- package/dist/src/platforms/whatsapp/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/commands/auth.js +21 -18
- package/dist/src/platforms/whatsapp/commands/auth.js.map +1 -1
- package/dist/src/platforms/whatsapp/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/credential-manager.js +14 -8
- package/dist/src/platforms/whatsapp/credential-manager.js.map +1 -1
- package/docs/content/docs/agent-skills.mdx +4 -4
- package/docs/content/docs/cli/channeltalk.mdx +1 -1
- package/docs/content/docs/cli/channeltalkbot.mdx +1 -1
- package/docs/content/docs/cli/discord.mdx +1 -1
- package/docs/content/docs/cli/discordbot.mdx +1 -1
- package/docs/content/docs/cli/instagram.mdx +1 -1
- package/docs/content/docs/cli/kakaotalk.mdx +1 -1
- package/docs/content/docs/cli/line.mdx +1 -1
- package/docs/content/docs/cli/meta.json +1 -0
- package/docs/content/docs/cli/slack.mdx +1 -1
- package/docs/content/docs/cli/slackbot.mdx +1 -1
- package/docs/content/docs/cli/teams.mdx +1 -1
- package/docs/content/docs/cli/webex.mdx +1 -1
- package/docs/content/docs/cli/wechatbot.mdx +179 -0
- package/docs/content/docs/cli/whatsapp.mdx +1 -1
- package/docs/content/docs/cli/whatsappbot.mdx +1 -1
- package/docs/content/docs/sdk/meta.json +1 -1
- package/docs/content/docs/sdk/wechatbot.mdx +282 -0
- package/docs/content/docs/tui.mdx +1 -1
- package/docs/src/app/page.tsx +5 -5
- package/package.json +11 -3
- 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 +24 -1
- package/skills/agent-line/SKILL.md +7 -11
- package/skills/agent-line/references/authentication.md +13 -4
- 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 +385 -0
- package/skills/agent-whatsapp/SKILL.md +12 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/discord/credential-manager.test.ts +18 -1
- package/src/platforms/discordbot/client.ts +2 -2
- package/src/platforms/instagram/commands/auth.test.ts +216 -0
- package/src/platforms/instagram/commands/chat.test.ts +127 -0
- package/src/platforms/instagram/commands/message.test.ts +178 -0
- package/src/platforms/kakaotalk/cli.ts +2 -1
- package/src/platforms/kakaotalk/client.test.ts +157 -0
- package/src/platforms/kakaotalk/client.ts +57 -3
- package/src/platforms/kakaotalk/commands/auth.test.ts +299 -0
- package/src/platforms/kakaotalk/commands/chat.test.ts +97 -0
- package/src/platforms/kakaotalk/commands/index.ts +1 -0
- package/src/platforms/kakaotalk/commands/message.test.ts +113 -0
- package/src/platforms/kakaotalk/commands/profile.test.ts +84 -0
- package/src/platforms/kakaotalk/commands/profile.ts +21 -0
- package/src/platforms/kakaotalk/index.test.ts +5 -0
- package/src/platforms/kakaotalk/index.ts +2 -0
- package/src/platforms/kakaotalk/protocol/session.ts +2 -0
- package/src/platforms/kakaotalk/types.ts +18 -0
- package/src/platforms/line/client.ts +14 -39
- package/src/platforms/line/commands/auth.test.ts +141 -0
- package/src/platforms/line/commands/auth.ts +28 -19
- package/src/platforms/line/commands/chat.test.ts +110 -0
- package/src/platforms/line/commands/friend.test.ts +98 -0
- package/src/platforms/line/commands/message.test.ts +119 -0
- package/src/platforms/line/commands/profile.test.ts +85 -0
- package/src/platforms/slackbot/commands/channel.test.ts +139 -0
- package/src/platforms/slackbot/commands/message.test.ts +226 -0
- package/src/platforms/slackbot/commands/reaction.test.ts +90 -0
- package/src/platforms/slackbot/commands/user.test.ts +143 -0
- package/src/platforms/teams/commands/reaction.test.ts +45 -61
- package/src/platforms/teams/commands/reaction.ts +2 -0
- package/src/platforms/telegram/commands/chat.test.ts +125 -0
- package/src/platforms/telegram/commands/message.test.ts +92 -0
- package/src/platforms/webex/commands/member.test.ts +65 -58
- package/src/platforms/webex/commands/message.test.ts +78 -121
- package/src/platforms/webex/commands/snapshot.test.ts +59 -46
- package/src/platforms/webex/commands/space.test.ts +49 -48
- package/src/platforms/wechatbot/cli.ts +24 -0
- package/src/platforms/wechatbot/client.test.ts +497 -0
- package/src/platforms/wechatbot/client.ts +268 -0
- package/src/platforms/wechatbot/commands/auth.test.ts +211 -0
- package/src/platforms/wechatbot/commands/auth.ts +203 -0
- package/src/platforms/wechatbot/commands/index.ts +4 -0
- package/src/platforms/wechatbot/commands/message.test.ts +155 -0
- package/src/platforms/wechatbot/commands/message.ts +104 -0
- package/src/platforms/wechatbot/commands/shared.ts +22 -0
- package/src/platforms/wechatbot/commands/template.test.ts +199 -0
- package/src/platforms/wechatbot/commands/template.ts +102 -0
- package/src/platforms/wechatbot/commands/user.test.ts +165 -0
- package/src/platforms/wechatbot/commands/user.ts +75 -0
- package/src/platforms/wechatbot/credential-manager.test.ts +255 -0
- package/src/platforms/wechatbot/credential-manager.ts +148 -0
- package/src/platforms/wechatbot/index.test.ts +49 -0
- package/src/platforms/wechatbot/index.ts +19 -0
- package/src/platforms/wechatbot/types.test.ts +223 -0
- package/src/platforms/wechatbot/types.ts +107 -0
- package/src/platforms/whatsapp/client.ts +24 -13
- package/src/platforms/whatsapp/commands/auth.test.ts +311 -0
- package/src/platforms/whatsapp/commands/auth.ts +21 -17
- package/src/platforms/whatsapp/commands/chat.test.ts +198 -0
- package/src/platforms/whatsapp/commands/message.test.ts +231 -0
- package/src/platforms/whatsapp/credential-manager.test.ts +20 -0
- package/src/platforms/whatsapp/credential-manager.ts +17 -8
- package/src/platforms/whatsappbot/commands/auth.test.ts +217 -0
- package/src/platforms/whatsappbot/commands/message.test.ts +198 -0
- package/src/platforms/whatsappbot/commands/template.test.ts +112 -0
|
@@ -1,89 +1,73 @@
|
|
|
1
|
-
import { afterEach, beforeEach, expect,
|
|
1
|
+
import { afterEach, beforeEach, expect, spyOn, test } from 'bun:test'
|
|
2
2
|
|
|
3
3
|
import { TeamsClient } from '../client'
|
|
4
4
|
import { TeamsCredentialManager } from '../credential-manager'
|
|
5
5
|
import { addAction, removeAction } from './reaction'
|
|
6
6
|
|
|
7
|
-
let
|
|
8
|
-
let
|
|
9
|
-
let
|
|
7
|
+
let addReactionSpy: ReturnType<typeof spyOn>
|
|
8
|
+
let removeReactionSpy: ReturnType<typeof spyOn>
|
|
9
|
+
let getTokenWithExpirySpy: ReturnType<typeof spyOn>
|
|
10
|
+
let consoleLogSpy: ReturnType<typeof spyOn>
|
|
11
|
+
let processExitSpy: ReturnType<typeof spyOn>
|
|
10
12
|
|
|
11
13
|
beforeEach(() => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
teams: {},
|
|
22
|
-
},
|
|
23
|
-
},
|
|
14
|
+
addReactionSpy = spyOn(TeamsClient.prototype, 'addReaction').mockResolvedValue(undefined)
|
|
15
|
+
removeReactionSpy = spyOn(TeamsClient.prototype, 'removeReaction').mockResolvedValue(undefined)
|
|
16
|
+
getTokenWithExpirySpy = spyOn(TeamsCredentialManager.prototype, 'getTokenWithExpiry').mockImplementation(() =>
|
|
17
|
+
Promise.resolve({ token: 'test-token', tokenExpiresAt: undefined }),
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
consoleLogSpy = spyOn(console, 'log').mockImplementation(() => {})
|
|
21
|
+
processExitSpy = spyOn(process, 'exit').mockImplementation((_code?: number) => {
|
|
22
|
+
throw new Error(`process.exit(${_code})`)
|
|
24
23
|
})
|
|
25
24
|
})
|
|
26
25
|
|
|
27
26
|
afterEach(() => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
addReactionSpy.mockRestore()
|
|
28
|
+
removeReactionSpy.mockRestore()
|
|
29
|
+
getTokenWithExpirySpy.mockRestore()
|
|
30
|
+
consoleLogSpy.mockRestore()
|
|
31
|
+
processExitSpy.mockRestore()
|
|
31
32
|
})
|
|
32
33
|
|
|
33
34
|
test('add: sends correct POST request with emoji', async () => {
|
|
34
|
-
const consoleSpy = mock((_msg: string) => {})
|
|
35
|
-
const originalLog = console.log
|
|
36
|
-
console.log = consoleSpy
|
|
37
|
-
|
|
38
35
|
try {
|
|
39
36
|
await addAction('team123', 'ch123', 'msg123', 'like', { pretty: false })
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
37
|
+
} catch {}
|
|
38
|
+
|
|
39
|
+
expect(consoleLogSpy).toHaveBeenCalled()
|
|
40
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
41
|
+
expect(output.success).toBe(true)
|
|
42
|
+
expect(output.team_id).toBe('team123')
|
|
43
|
+
expect(output.channel_id).toBe('ch123')
|
|
44
|
+
expect(output.message_id).toBe('msg123')
|
|
45
|
+
expect(output.emoji).toBe('like')
|
|
50
46
|
})
|
|
51
47
|
|
|
52
48
|
test('remove: sends correct DELETE request with emoji', async () => {
|
|
53
|
-
const consoleSpy = mock((_msg: string) => {})
|
|
54
|
-
const originalLog = console.log
|
|
55
|
-
console.log = consoleSpy
|
|
56
|
-
|
|
57
49
|
try {
|
|
58
50
|
await removeAction('team123', 'ch123', 'msg123', 'like', { pretty: false })
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
51
|
+
} catch {}
|
|
52
|
+
|
|
53
|
+
expect(consoleLogSpy).toHaveBeenCalled()
|
|
54
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
55
|
+
expect(output.success).toBe(true)
|
|
56
|
+
expect(output.team_id).toBe('team123')
|
|
57
|
+
expect(output.channel_id).toBe('ch123')
|
|
58
|
+
expect(output.message_id).toBe('msg123')
|
|
59
|
+
expect(output.emoji).toBe('like')
|
|
69
60
|
})
|
|
70
61
|
|
|
71
62
|
test('add: handles missing token gracefully', async () => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const consoleSpy = mock((_msg: string) => {})
|
|
75
|
-
const originalLog = console.log
|
|
76
|
-
const originalExit = process.exit
|
|
77
|
-
process.exit = mock(() => {}) as any
|
|
78
|
-
console.log = consoleSpy
|
|
63
|
+
getTokenWithExpirySpy.mockImplementation(() => Promise.resolve(null))
|
|
79
64
|
|
|
80
65
|
try {
|
|
81
66
|
await addAction('team123', 'ch123', 'msg123', 'like', { pretty: false })
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
67
|
+
} catch {}
|
|
68
|
+
|
|
69
|
+
expect(consoleLogSpy).toHaveBeenCalled()
|
|
70
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
71
|
+
expect(output.error).toBeDefined()
|
|
72
|
+
expect(processExitSpy).toHaveBeenCalledWith(1)
|
|
89
73
|
})
|
|
@@ -20,6 +20,7 @@ export async function addAction(
|
|
|
20
20
|
if (!cred) {
|
|
21
21
|
console.log(formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty))
|
|
22
22
|
process.exit(1)
|
|
23
|
+
return
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
const client = await new TeamsClient().login({ token: cred.token, tokenExpiresAt: cred.tokenExpiresAt })
|
|
@@ -56,6 +57,7 @@ export async function removeAction(
|
|
|
56
57
|
if (!cred) {
|
|
57
58
|
console.log(formatOutput({ error: 'Not authenticated. Run "auth extract" first.' }, options.pretty))
|
|
58
59
|
process.exit(1)
|
|
60
|
+
return
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
const client = await new TeamsClient().login({ token: cred.token, tokenExpiresAt: cred.tokenExpiresAt })
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
const mockListChats = mock(() =>
|
|
4
|
+
Promise.resolve([
|
|
5
|
+
{ id: 'chat-1', title: 'General', type: 'supergroup', unread_count: 0 },
|
|
6
|
+
{ id: 'chat-2', title: 'Random', type: 'group', unread_count: 5 },
|
|
7
|
+
]),
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
const mockSearchChats = mock(() =>
|
|
11
|
+
Promise.resolve([{ id: 'chat-1', title: 'General', type: 'supergroup', unread_count: 0 }]),
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
const mockGetChat = mock(() =>
|
|
15
|
+
Promise.resolve({ id: 'chat-1', title: 'General', type: 'supergroup', unread_count: 0 }),
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
const mockClient = {
|
|
19
|
+
listChats: mockListChats,
|
|
20
|
+
searchChats: mockSearchChats,
|
|
21
|
+
getChat: mockGetChat,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
mock.module('./shared', () => ({
|
|
25
|
+
withTelegramClient: async (_opts: unknown, fn: (client: typeof mockClient) => Promise<unknown>) =>
|
|
26
|
+
fn(mockClient),
|
|
27
|
+
}))
|
|
28
|
+
|
|
29
|
+
import { chatCommand } from './chat'
|
|
30
|
+
|
|
31
|
+
describe('chat commands', () => {
|
|
32
|
+
let consoleSpy: ReturnType<typeof spyOn>
|
|
33
|
+
let processExitSpy: ReturnType<typeof spyOn>
|
|
34
|
+
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
mockListChats.mockReset()
|
|
37
|
+
mockListChats.mockImplementation(() =>
|
|
38
|
+
Promise.resolve([
|
|
39
|
+
{ id: 'chat-1', title: 'General', type: 'supergroup', unread_count: 0 },
|
|
40
|
+
{ id: 'chat-2', title: 'Random', type: 'group', unread_count: 5 },
|
|
41
|
+
]),
|
|
42
|
+
)
|
|
43
|
+
mockSearchChats.mockReset()
|
|
44
|
+
mockSearchChats.mockImplementation(() =>
|
|
45
|
+
Promise.resolve([{ id: 'chat-1', title: 'General', type: 'supergroup', unread_count: 0 }]),
|
|
46
|
+
)
|
|
47
|
+
mockGetChat.mockReset()
|
|
48
|
+
mockGetChat.mockImplementation(() =>
|
|
49
|
+
Promise.resolve({ id: 'chat-1', title: 'General', type: 'supergroup', unread_count: 0 }),
|
|
50
|
+
)
|
|
51
|
+
consoleSpy = spyOn(console, 'log').mockImplementation(() => {})
|
|
52
|
+
processExitSpy = spyOn(process, 'exit').mockImplementation((() => {}) as (code?: number) => never)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
afterEach(() => {
|
|
56
|
+
consoleSpy.mockRestore()
|
|
57
|
+
processExitSpy.mockRestore()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('list subcommand', () => {
|
|
61
|
+
test('calls listChats with default limit', async () => {
|
|
62
|
+
await chatCommand.parseAsync(['list'], { from: 'user' })
|
|
63
|
+
|
|
64
|
+
expect(mockListChats).toHaveBeenCalledWith(20)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('calls listChats with custom limit', async () => {
|
|
68
|
+
await chatCommand.parseAsync(['list', '--limit', '5'], { from: 'user' })
|
|
69
|
+
|
|
70
|
+
expect(mockListChats).toHaveBeenCalledWith(5)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('outputs JSON array to console', async () => {
|
|
74
|
+
await chatCommand.parseAsync(['list'], { from: 'user' })
|
|
75
|
+
|
|
76
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
77
|
+
const output = consoleSpy.mock.calls[0][0] as string
|
|
78
|
+
const parsed = JSON.parse(output)
|
|
79
|
+
expect(parsed).toBeArray()
|
|
80
|
+
expect(parsed).toHaveLength(2)
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
describe('search subcommand', () => {
|
|
85
|
+
test('calls searchChats with query and default limit', async () => {
|
|
86
|
+
await chatCommand.parseAsync(['search', 'General'], { from: 'user' })
|
|
87
|
+
|
|
88
|
+
expect(mockSearchChats).toHaveBeenCalledWith('General', 20)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('calls searchChats with query and custom limit', async () => {
|
|
92
|
+
await chatCommand.parseAsync(['search', 'General', '--limit', '3'], { from: 'user' })
|
|
93
|
+
|
|
94
|
+
expect(mockSearchChats).toHaveBeenCalledWith('General', 3)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
test('outputs JSON array to console', async () => {
|
|
98
|
+
await chatCommand.parseAsync(['search', 'General'], { from: 'user' })
|
|
99
|
+
|
|
100
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
101
|
+
const output = consoleSpy.mock.calls[0][0] as string
|
|
102
|
+
const parsed = JSON.parse(output)
|
|
103
|
+
expect(parsed).toBeArray()
|
|
104
|
+
expect(parsed).toHaveLength(1)
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
describe('get subcommand', () => {
|
|
109
|
+
test('calls getChat with chat reference', async () => {
|
|
110
|
+
await chatCommand.parseAsync(['get', 'chat-1'], { from: 'user' })
|
|
111
|
+
|
|
112
|
+
expect(mockGetChat).toHaveBeenCalledWith('chat-1')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
test('outputs JSON object to console', async () => {
|
|
116
|
+
await chatCommand.parseAsync(['get', 'chat-1'], { from: 'user' })
|
|
117
|
+
|
|
118
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
119
|
+
const output = consoleSpy.mock.calls[0][0] as string
|
|
120
|
+
const parsed = JSON.parse(output)
|
|
121
|
+
expect(parsed).toBeObject()
|
|
122
|
+
expect(parsed.id).toBe('chat-1')
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
})
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
const mockListMessages = mock(() =>
|
|
4
|
+
Promise.resolve([
|
|
5
|
+
{ id: 1, text: 'Hello', sender_id: 'user-1', date: 1000 },
|
|
6
|
+
{ id: 2, text: 'World', sender_id: 'user-2', date: 2000 },
|
|
7
|
+
]),
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
const mockSendMessage = mock(() =>
|
|
11
|
+
Promise.resolve({ id: 3, text: 'Sent message', sender_id: 'user-1', date: 3000 }),
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
const mockClient = {
|
|
15
|
+
listMessages: mockListMessages,
|
|
16
|
+
sendMessage: mockSendMessage,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
mock.module('./shared', () => ({
|
|
20
|
+
withTelegramClient: async (_opts: unknown, fn: (client: typeof mockClient) => Promise<unknown>) =>
|
|
21
|
+
fn(mockClient),
|
|
22
|
+
}))
|
|
23
|
+
|
|
24
|
+
import { messageCommand } from './message'
|
|
25
|
+
|
|
26
|
+
describe('message commands', () => {
|
|
27
|
+
let consoleSpy: ReturnType<typeof spyOn>
|
|
28
|
+
let processExitSpy: ReturnType<typeof spyOn>
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
mockListMessages.mockReset()
|
|
32
|
+
mockListMessages.mockImplementation(() =>
|
|
33
|
+
Promise.resolve([
|
|
34
|
+
{ id: 1, text: 'Hello', sender_id: 'user-1', date: 1000 },
|
|
35
|
+
{ id: 2, text: 'World', sender_id: 'user-2', date: 2000 },
|
|
36
|
+
]),
|
|
37
|
+
)
|
|
38
|
+
mockSendMessage.mockReset()
|
|
39
|
+
mockSendMessage.mockImplementation(() =>
|
|
40
|
+
Promise.resolve({ id: 3, text: 'Sent message', sender_id: 'user-1', date: 3000 }),
|
|
41
|
+
)
|
|
42
|
+
consoleSpy = spyOn(console, 'log').mockImplementation(() => {})
|
|
43
|
+
processExitSpy = spyOn(process, 'exit').mockImplementation((() => {}) as (code?: number) => never)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
consoleSpy.mockRestore()
|
|
48
|
+
processExitSpy.mockRestore()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
describe('list subcommand', () => {
|
|
52
|
+
test('calls listMessages with chat reference and default limit', async () => {
|
|
53
|
+
await messageCommand.parseAsync(['list', 'chat-123'], { from: 'user' })
|
|
54
|
+
|
|
55
|
+
expect(mockListMessages).toHaveBeenCalledWith('chat-123', 20)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('calls listMessages with custom limit', async () => {
|
|
59
|
+
await messageCommand.parseAsync(['list', 'chat-123', '--limit', '10'], { from: 'user' })
|
|
60
|
+
|
|
61
|
+
expect(mockListMessages).toHaveBeenCalledWith('chat-123', 10)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('outputs JSON to console', async () => {
|
|
65
|
+
await messageCommand.parseAsync(['list', 'chat-123'], { from: 'user' })
|
|
66
|
+
|
|
67
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
68
|
+
const output = consoleSpy.mock.calls[0][0] as string
|
|
69
|
+
const parsed = JSON.parse(output)
|
|
70
|
+
expect(parsed).toBeArray()
|
|
71
|
+
expect(parsed).toHaveLength(2)
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
describe('send subcommand', () => {
|
|
76
|
+
test('calls sendMessage with chat reference and text', async () => {
|
|
77
|
+
await messageCommand.parseAsync(['send', 'chat-123', 'Hello there'], { from: 'user' })
|
|
78
|
+
|
|
79
|
+
expect(mockSendMessage).toHaveBeenCalledWith('chat-123', 'Hello there')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('outputs JSON to console', async () => {
|
|
83
|
+
await messageCommand.parseAsync(['send', 'chat-123', 'Hello there'], { from: 'user' })
|
|
84
|
+
|
|
85
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
86
|
+
const output = consoleSpy.mock.calls[0][0] as string
|
|
87
|
+
const parsed = JSON.parse(output)
|
|
88
|
+
expect(parsed).toBeObject()
|
|
89
|
+
expect(parsed.id).toBe(3)
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
})
|
|
@@ -1,65 +1,74 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
|
|
1
|
+
import { afterAll, afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
|
|
2
2
|
|
|
3
|
-
import { WebexClient } from '../client'
|
|
4
3
|
import { WebexError } from '../types'
|
|
4
|
+
|
|
5
|
+
const mockHandleError = mock((err: Error) => {
|
|
6
|
+
throw err
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
mock.module('@/shared/utils/error-handler', () => ({
|
|
10
|
+
handleError: mockHandleError,
|
|
11
|
+
}))
|
|
12
|
+
|
|
13
|
+
const mockMembers = [
|
|
14
|
+
{
|
|
15
|
+
id: 'mem-1',
|
|
16
|
+
roomId: 'room-1',
|
|
17
|
+
personId: 'person-1',
|
|
18
|
+
personEmail: 'alice@example.com',
|
|
19
|
+
personDisplayName: 'Alice',
|
|
20
|
+
isModerator: true,
|
|
21
|
+
created: '2024-01-01T00:00:00.000Z',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'mem-2',
|
|
25
|
+
roomId: 'room-1',
|
|
26
|
+
personId: 'person-2',
|
|
27
|
+
personEmail: 'bob@example.com',
|
|
28
|
+
personDisplayName: 'Bob',
|
|
29
|
+
isModerator: false,
|
|
30
|
+
created: '2024-01-02T00:00:00.000Z',
|
|
31
|
+
},
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
const mockListMemberships = mock(() => Promise.resolve(mockMembers))
|
|
35
|
+
const mockLogin = mock(() => Promise.resolve({ listMemberships: mockListMemberships }))
|
|
36
|
+
|
|
37
|
+
mock.module('../client', () => ({
|
|
38
|
+
WebexClient: class {
|
|
39
|
+
login = mockLogin
|
|
40
|
+
},
|
|
41
|
+
}))
|
|
42
|
+
|
|
5
43
|
import { listAction } from './member'
|
|
6
44
|
|
|
45
|
+
afterAll(() => {
|
|
46
|
+
mock.restore()
|
|
47
|
+
})
|
|
48
|
+
|
|
7
49
|
describe('member commands', () => {
|
|
8
50
|
let consoleSpy: ReturnType<typeof spyOn>
|
|
9
|
-
let consoleErrorSpy: ReturnType<typeof spyOn>
|
|
10
|
-
let processExitSpy: ReturnType<typeof spyOn>
|
|
11
|
-
let stderrOutput: string
|
|
12
|
-
let origStderrWrite: typeof process.stderr.write
|
|
13
|
-
const mockMembers = [
|
|
14
|
-
{
|
|
15
|
-
id: 'mem-1',
|
|
16
|
-
roomId: 'room-1',
|
|
17
|
-
personId: 'person-1',
|
|
18
|
-
personEmail: 'alice@example.com',
|
|
19
|
-
personDisplayName: 'Alice',
|
|
20
|
-
isModerator: true,
|
|
21
|
-
created: '2024-01-01T00:00:00.000Z',
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
id: 'mem-2',
|
|
25
|
-
roomId: 'room-1',
|
|
26
|
-
personId: 'person-2',
|
|
27
|
-
personEmail: 'bob@example.com',
|
|
28
|
-
personDisplayName: 'Bob',
|
|
29
|
-
isModerator: false,
|
|
30
|
-
created: '2024-01-02T00:00:00.000Z',
|
|
31
|
-
},
|
|
32
|
-
]
|
|
33
51
|
|
|
34
52
|
beforeEach(() => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
53
|
+
mockListMemberships.mockReset().mockImplementation(() => Promise.resolve(mockMembers))
|
|
54
|
+
mockLogin.mockReset().mockImplementation(() =>
|
|
55
|
+
Promise.resolve({ listMemberships: mockListMemberships }),
|
|
56
|
+
)
|
|
57
|
+
mockHandleError.mockReset().mockImplementation((err: Error) => {
|
|
58
|
+
throw err
|
|
39
59
|
})
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
process.stderr.write = ((chunk: string | Uint8Array) => {
|
|
43
|
-
stderrOutput += typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk)
|
|
44
|
-
return true
|
|
45
|
-
}) as typeof process.stderr.write
|
|
46
|
-
spyOn(WebexClient.prototype, 'login').mockResolvedValue(new WebexClient() as any)
|
|
47
|
-
spyOn(WebexClient.prototype, 'listMemberships').mockResolvedValue(mockMembers)
|
|
60
|
+
|
|
61
|
+
consoleSpy = spyOn(console, 'log').mockImplementation(() => {})
|
|
48
62
|
})
|
|
49
63
|
|
|
50
64
|
afterEach(() => {
|
|
51
|
-
|
|
52
|
-
mock.restore()
|
|
65
|
+
consoleSpy.mockRestore()
|
|
53
66
|
})
|
|
54
67
|
|
|
55
68
|
test('listAction calls listMemberships with spaceId and outputs mapped members', async () => {
|
|
56
|
-
const listMembershipsSpy = spyOn(WebexClient.prototype, 'listMemberships').mockResolvedValue(
|
|
57
|
-
mockMembers,
|
|
58
|
-
)
|
|
59
|
-
|
|
60
69
|
await listAction('room-1', {})
|
|
61
70
|
|
|
62
|
-
expect(
|
|
71
|
+
expect(mockListMemberships).toHaveBeenCalledWith('room-1', { max: undefined })
|
|
63
72
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
64
73
|
JSON.stringify([
|
|
65
74
|
{
|
|
@@ -83,30 +92,28 @@ describe('member commands', () => {
|
|
|
83
92
|
})
|
|
84
93
|
|
|
85
94
|
test('listAction passes limit option to listMemberships', async () => {
|
|
86
|
-
const listMembershipsSpy = spyOn(WebexClient.prototype, 'listMemberships').mockResolvedValue(
|
|
87
|
-
mockMembers,
|
|
88
|
-
)
|
|
89
|
-
|
|
90
95
|
await listAction('room-1', { limit: 25 })
|
|
91
96
|
|
|
92
|
-
expect(
|
|
97
|
+
expect(mockListMemberships).toHaveBeenCalledWith('room-1', { max: 25 })
|
|
93
98
|
})
|
|
94
99
|
|
|
95
100
|
test('listAction handles not-authenticated case', async () => {
|
|
96
|
-
|
|
97
|
-
new WebexError('No Webex credentials found.', 'no_credentials')
|
|
98
|
-
)
|
|
101
|
+
mockLogin.mockImplementation(async () => {
|
|
102
|
+
throw new WebexError('No Webex credentials found.', 'no_credentials')
|
|
103
|
+
})
|
|
99
104
|
|
|
100
|
-
await expect(listAction('room-1', {})).rejects.toThrow('
|
|
105
|
+
await expect(listAction('room-1', {})).rejects.toThrow('No Webex credentials found.')
|
|
101
106
|
|
|
102
|
-
expect(
|
|
107
|
+
expect(mockHandleError).toHaveBeenCalledWith(expect.any(WebexError))
|
|
103
108
|
})
|
|
104
109
|
|
|
105
110
|
test('listAction handles API error', async () => {
|
|
106
|
-
|
|
111
|
+
mockListMemberships.mockImplementation(async () => {
|
|
112
|
+
throw new Error('API failure')
|
|
113
|
+
})
|
|
107
114
|
|
|
108
|
-
await expect(listAction('room-1', {})).rejects.toThrow('
|
|
115
|
+
await expect(listAction('room-1', {})).rejects.toThrow('API failure')
|
|
109
116
|
|
|
110
|
-
expect(
|
|
117
|
+
expect(mockHandleError).toHaveBeenCalledWith(expect.any(Error))
|
|
111
118
|
})
|
|
112
119
|
})
|