agent-messenger 2.10.1 → 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 +9 -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 +109 -49
- package/src/platforms/teams/token-extractor.ts +7 -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 { expect, mock,
|
|
1
|
+
import { expect, mock, it } from 'bun:test'
|
|
2
2
|
|
|
3
3
|
// Mock DiscordClient
|
|
4
4
|
const mockClient = {
|
|
@@ -21,7 +21,7 @@ const mockClient = {
|
|
|
21
21
|
}),
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
it('get returns note when it exists', async () => {
|
|
25
25
|
// given: user with a note
|
|
26
26
|
const userId = 'user_with_note'
|
|
27
27
|
|
|
@@ -34,7 +34,7 @@ test('get returns note when it exists', async () => {
|
|
|
34
34
|
expect(result?.note).toBe('This is a test note')
|
|
35
35
|
})
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
it('get returns null when note does not exist', async () => {
|
|
38
38
|
// given: user without a note
|
|
39
39
|
const userId = 'user_without_note'
|
|
40
40
|
|
|
@@ -45,7 +45,7 @@ test('get returns null when note does not exist', async () => {
|
|
|
45
45
|
expect(result).toBeNull()
|
|
46
46
|
})
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
it('set creates or updates note', async () => {
|
|
49
49
|
// given: user id and note content
|
|
50
50
|
const userId = 'user123'
|
|
51
51
|
const noteContent = 'Important person to remember'
|
|
@@ -58,7 +58,7 @@ test('set creates or updates note', async () => {
|
|
|
58
58
|
expect(result.note).toBe('Important person to remember')
|
|
59
59
|
})
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
it('set can update existing note', async () => {
|
|
62
62
|
// given: user with existing note
|
|
63
63
|
const userId = 'user_with_note'
|
|
64
64
|
const newNote = 'Updated note content'
|
|
@@ -71,7 +71,7 @@ test('set can update existing note', async () => {
|
|
|
71
71
|
expect(result.note).toBe('Updated note content')
|
|
72
72
|
})
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
it('set can clear note with empty string', async () => {
|
|
75
75
|
// given: user with existing note
|
|
76
76
|
const userId = 'user_with_note'
|
|
77
77
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect, mock,
|
|
1
|
+
import { expect, mock, it } from 'bun:test'
|
|
2
2
|
|
|
3
3
|
// Mock DiscordClient
|
|
4
4
|
const mockClient = {
|
|
@@ -38,7 +38,7 @@ const mockClient = {
|
|
|
38
38
|
})),
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
it('get returns user profile with all fields', async () => {
|
|
42
42
|
// given: user id
|
|
43
43
|
const userId = 'user123'
|
|
44
44
|
|
|
@@ -57,7 +57,7 @@ test('get returns user profile with all fields', async () => {
|
|
|
57
57
|
expect(profile.mutual_guilds?.[0].nick).toBe('TestNick')
|
|
58
58
|
})
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
it('get returns profile with minimal fields', async () => {
|
|
61
61
|
// given: mock with minimal profile
|
|
62
62
|
const minimalMock = {
|
|
63
63
|
getUserProfile: mock(async (userId: string) => ({
|
|
@@ -81,7 +81,7 @@ test('get returns profile with minimal fields', async () => {
|
|
|
81
81
|
expect(profile.mutual_guilds).toBeUndefined()
|
|
82
82
|
})
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
it('get formats connected accounts correctly', async () => {
|
|
85
85
|
// given: user with connected accounts
|
|
86
86
|
const userId = 'user789'
|
|
87
87
|
const profile = await mockClient.getUserProfile(userId)
|
|
@@ -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 { DiscordClient } from '../client'
|
|
4
4
|
import { DiscordCredentialManager } from '../credential-manager'
|
|
@@ -48,7 +48,7 @@ afterEach(() => {
|
|
|
48
48
|
credManagerLoadSpy?.mockRestore()
|
|
49
49
|
})
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
it('add: sends correct PUT request with emoji', async () => {
|
|
52
52
|
const consoleSpy = mock((_msg: string) => {})
|
|
53
53
|
const originalLog = console.log
|
|
54
54
|
console.log = consoleSpy
|
|
@@ -66,7 +66,7 @@ test('add: sends correct PUT request with emoji', async () => {
|
|
|
66
66
|
}
|
|
67
67
|
})
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
it('remove: sends correct DELETE request with emoji', async () => {
|
|
70
70
|
const consoleSpy = mock((_msg: string) => {})
|
|
71
71
|
const originalLog = console.log
|
|
72
72
|
console.log = consoleSpy
|
|
@@ -84,7 +84,7 @@ test('remove: sends correct DELETE request with emoji', async () => {
|
|
|
84
84
|
}
|
|
85
85
|
})
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
it('list: extracts reactions from message', async () => {
|
|
88
88
|
const consoleSpy = mock((_msg: string) => {})
|
|
89
89
|
const originalLog = console.log
|
|
90
90
|
console.log = consoleSpy
|
|
@@ -104,7 +104,7 @@ test('list: extracts reactions from message', async () => {
|
|
|
104
104
|
}
|
|
105
105
|
})
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
it('add: handles missing token gracefully', async () => {
|
|
108
108
|
// Temporarily override the credential manager spy to return null token
|
|
109
109
|
credManagerLoadSpy?.mockResolvedValue({
|
|
110
110
|
token: null,
|
|
@@ -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 { DiscordClient } from '../client'
|
|
4
4
|
import { DiscordCredentialManager } from '../credential-manager'
|
|
@@ -53,7 +53,7 @@ afterEach(() => {
|
|
|
53
53
|
credManagerGetCurrentServerSpy?.mockRestore()
|
|
54
54
|
})
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
it('list: returns servers with current marker', async () => {
|
|
57
57
|
// given: credential manager with servers
|
|
58
58
|
const credManager = new DiscordCredentialManager()
|
|
59
59
|
const config = await credManager.load()
|
|
@@ -67,7 +67,7 @@ test('list: returns servers with current marker', async () => {
|
|
|
67
67
|
expect(config.servers['server-2']).toBeDefined()
|
|
68
68
|
})
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
it('list: marks current server', async () => {
|
|
71
71
|
// given: credential manager with current server set
|
|
72
72
|
const credManager = new DiscordCredentialManager()
|
|
73
73
|
const config = await credManager.load()
|
|
@@ -80,7 +80,7 @@ test('list: marks current server', async () => {
|
|
|
80
80
|
expect(config.current_server).toBe('server-1')
|
|
81
81
|
})
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
it('info: returns server details', async () => {
|
|
84
84
|
// given: discord client with server data
|
|
85
85
|
const client = await new DiscordClient().login({ token: 'test-token' })
|
|
86
86
|
const server = await client.getServer('server-1')
|
|
@@ -95,7 +95,7 @@ test('info: returns server details', async () => {
|
|
|
95
95
|
expect(server.owner).toBe(true)
|
|
96
96
|
})
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
it('info: throws error for non-existent server', async () => {
|
|
99
99
|
// given: discord client
|
|
100
100
|
const client = await new DiscordClient().login({ token: 'test-token' })
|
|
101
101
|
|
|
@@ -109,7 +109,7 @@ test('info: throws error for non-existent server', async () => {
|
|
|
109
109
|
}
|
|
110
110
|
})
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
it('switch: updates current server', async () => {
|
|
113
113
|
// given: credential manager
|
|
114
114
|
const credManager = new DiscordCredentialManager()
|
|
115
115
|
|
|
@@ -120,7 +120,7 @@ test('switch: updates current server', async () => {
|
|
|
120
120
|
expect(credManager.setCurrentServer).toHaveBeenCalledWith('server-2')
|
|
121
121
|
})
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
it('current: returns current server info', async () => {
|
|
124
124
|
// given: credential manager with current server
|
|
125
125
|
const credManager = new DiscordCredentialManager()
|
|
126
126
|
const config = await credManager.load()
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import { expect,
|
|
1
|
+
import { expect, it } from 'bun:test'
|
|
2
2
|
|
|
3
3
|
import { snapshotCommand } from './snapshot'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
it('snapshot: command is defined', () => {
|
|
6
6
|
expect(snapshotCommand).toBeDefined()
|
|
7
7
|
expect(snapshotCommand.name()).toBe('snapshot')
|
|
8
8
|
})
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
it('snapshot: command has correct description', () => {
|
|
11
11
|
expect(snapshotCommand.description()).toContain('server overview')
|
|
12
12
|
})
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
it('snapshot: command has --channels-only option', () => {
|
|
15
15
|
const options = snapshotCommand.options
|
|
16
16
|
const channelsOnlyOption = options.find((opt) => opt.long === '--channels-only')
|
|
17
17
|
expect(channelsOnlyOption).toBeDefined()
|
|
18
18
|
})
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
it('snapshot: command has --users-only option', () => {
|
|
21
21
|
const options = snapshotCommand.options
|
|
22
22
|
const usersOnlyOption = options.find((opt) => opt.long === '--users-only')
|
|
23
23
|
expect(usersOnlyOption).toBeDefined()
|
|
24
24
|
})
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
it('snapshot: command has --limit option', () => {
|
|
27
27
|
const options = snapshotCommand.options
|
|
28
28
|
const limitOption = options.find((opt) => opt.long === '--limit')
|
|
29
29
|
expect(limitOption).toBeDefined()
|
|
@@ -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 { DiscordClient } from '../client'
|
|
4
4
|
import { DiscordCredentialManager } from '../credential-manager'
|
|
@@ -44,7 +44,7 @@ afterEach(() => {
|
|
|
44
44
|
credManagerLoadSpy?.mockRestore()
|
|
45
45
|
})
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
it('createThread: creates thread with name', async () => {
|
|
48
48
|
// given: discord client
|
|
49
49
|
const client = await new DiscordClient().login({ token: 'test-token' })
|
|
50
50
|
|
|
@@ -59,7 +59,7 @@ test('createThread: creates thread with name', async () => {
|
|
|
59
59
|
expect(thread.parent_id).toBe('ch-1')
|
|
60
60
|
})
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
it('createThread: creates thread with auto_archive_duration', async () => {
|
|
63
63
|
// given: discord client
|
|
64
64
|
const client = await new DiscordClient().login({ token: 'test-token' })
|
|
65
65
|
|
|
@@ -72,7 +72,7 @@ test('createThread: creates thread with auto_archive_duration', async () => {
|
|
|
72
72
|
})
|
|
73
73
|
})
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
it('createThread: creates thread with rate_limit_per_user', async () => {
|
|
76
76
|
// given: discord client
|
|
77
77
|
const client = await new DiscordClient().login({ token: 'test-token' })
|
|
78
78
|
|
|
@@ -85,7 +85,7 @@ test('createThread: creates thread with rate_limit_per_user', async () => {
|
|
|
85
85
|
})
|
|
86
86
|
})
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
it('archiveThread: archives thread', async () => {
|
|
89
89
|
// given: discord client
|
|
90
90
|
const client = await new DiscordClient().login({ token: 'test-token' })
|
|
91
91
|
|
|
@@ -98,7 +98,7 @@ test('archiveThread: archives thread', async () => {
|
|
|
98
98
|
expect(thread.thread_metadata?.archived).toBe(true)
|
|
99
99
|
})
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
it('archiveThread: unarchives thread when archived=false', async () => {
|
|
102
102
|
// given: discord client with unarchive mock
|
|
103
103
|
clientArchiveThreadSpy.mockResolvedValue({
|
|
104
104
|
id: 'thread-1',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect, mock,
|
|
1
|
+
import { expect, mock, it } from 'bun:test'
|
|
2
2
|
|
|
3
3
|
// Mock DiscordClient
|
|
4
4
|
const mockClient = {
|
|
@@ -41,7 +41,7 @@ const mockClient = {
|
|
|
41
41
|
]),
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
it('me returns current user info', async () => {
|
|
45
45
|
// given: authenticated user
|
|
46
46
|
const user = await mockClient.testAuth()
|
|
47
47
|
|
|
@@ -61,7 +61,7 @@ test('me returns current user info', async () => {
|
|
|
61
61
|
expect(result.bot).toBe(false)
|
|
62
62
|
})
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
it('info returns user details by id', async () => {
|
|
65
65
|
// given: user id
|
|
66
66
|
const userId = 'user123'
|
|
67
67
|
|
|
@@ -80,7 +80,7 @@ test('info returns user details by id', async () => {
|
|
|
80
80
|
expect(result.username).toBe('testuser')
|
|
81
81
|
})
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
it('list returns server members', async () => {
|
|
84
84
|
// given: server id
|
|
85
85
|
const serverId = 'server123'
|
|
86
86
|
|
|
@@ -101,7 +101,7 @@ test('list returns server members', async () => {
|
|
|
101
101
|
expect(result[2].username).toBe('bob')
|
|
102
102
|
})
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
it('list filters out bots when flag not set', async () => {
|
|
105
105
|
// given: server id and users with bots
|
|
106
106
|
const serverId = 'server123'
|
|
107
107
|
const users = await mockClient.listUsers(serverId)
|
|
@@ -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 { DiscordClient } from '@/platforms/discord/client'
|
|
4
4
|
import { whoamiAction, whoamiCommand } from '@/platforms/discord/commands/whoami'
|
|
@@ -35,19 +35,19 @@ afterEach(() => {
|
|
|
35
35
|
processExitSpy?.mockRestore()
|
|
36
36
|
})
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
it('whoami command is defined with correct name and description', () => {
|
|
39
39
|
expect(whoamiCommand).toBeDefined()
|
|
40
40
|
expect(whoamiCommand.name()).toBe('whoami')
|
|
41
41
|
expect(whoamiCommand.description()).toBe('Show current authenticated user')
|
|
42
42
|
})
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
it('whoami command has --pretty option', () => {
|
|
45
45
|
const options = whoamiCommand.options
|
|
46
46
|
const hasPretty = options.some((opt: { long?: string }) => opt.long === '--pretty')
|
|
47
47
|
expect(hasPretty).toBe(true)
|
|
48
48
|
})
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
it('whoami outputs expected user fields', async () => {
|
|
51
51
|
await whoamiAction({})
|
|
52
52
|
|
|
53
53
|
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
@@ -61,7 +61,7 @@ test('whoami outputs expected user fields', async () => {
|
|
|
61
61
|
)
|
|
62
62
|
})
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
it('whoami outputs pretty-printed JSON when pretty is true', async () => {
|
|
65
65
|
await whoamiAction({ pretty: true })
|
|
66
66
|
|
|
67
67
|
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
@@ -79,7 +79,7 @@ test('whoami outputs pretty-printed JSON when pretty is true', async () => {
|
|
|
79
79
|
)
|
|
80
80
|
})
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
it('whoami exits with error when not authenticated', async () => {
|
|
83
83
|
credManagerSpy.mockResolvedValue({ token: undefined })
|
|
84
84
|
|
|
85
85
|
await whoamiAction({})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterAll, afterEach, beforeEach, describe, expect,
|
|
1
|
+
import { afterAll, afterEach, beforeEach, describe, expect, it } from 'bun:test'
|
|
2
2
|
import { existsSync, rmSync } from 'node:fs'
|
|
3
3
|
import { join } from 'node:path'
|
|
4
4
|
|
|
@@ -39,7 +39,7 @@ afterAll(() => {
|
|
|
39
39
|
})
|
|
40
40
|
|
|
41
41
|
describe('DiscordCredentialManager', () => {
|
|
42
|
-
|
|
42
|
+
it('load returns default config when file does not exist', async () => {
|
|
43
43
|
const manager = setup()
|
|
44
44
|
const config = await manager.load()
|
|
45
45
|
|
|
@@ -50,7 +50,7 @@ describe('DiscordCredentialManager', () => {
|
|
|
50
50
|
})
|
|
51
51
|
})
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
it('save creates config file with correct permissions', async () => {
|
|
54
54
|
const testConfigDir = join(
|
|
55
55
|
import.meta.dir,
|
|
56
56
|
`.test-discord-config-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
@@ -76,13 +76,13 @@ describe('DiscordCredentialManager', () => {
|
|
|
76
76
|
expect(loaded).toEqual(config)
|
|
77
77
|
})
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
it('getToken returns null when not authenticated', async () => {
|
|
80
80
|
const manager = setup()
|
|
81
81
|
const token = await manager.getToken()
|
|
82
82
|
expect(token).toBeNull()
|
|
83
83
|
})
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
it('setToken saves token to config', async () => {
|
|
86
86
|
const manager = setup()
|
|
87
87
|
await manager.setToken('test-token-123')
|
|
88
88
|
|
|
@@ -90,7 +90,7 @@ describe('DiscordCredentialManager', () => {
|
|
|
90
90
|
expect(token).toBe('test-token-123')
|
|
91
91
|
})
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
it('getToken returns previously set token', async () => {
|
|
94
94
|
const manager = setup()
|
|
95
95
|
await manager.setToken('my-token')
|
|
96
96
|
|
|
@@ -98,7 +98,7 @@ describe('DiscordCredentialManager', () => {
|
|
|
98
98
|
expect(token).toBe('my-token')
|
|
99
99
|
})
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
it('clearToken removes token from config', async () => {
|
|
102
102
|
const manager = setup()
|
|
103
103
|
await manager.setToken('test-token')
|
|
104
104
|
await manager.clearToken()
|
|
@@ -107,13 +107,13 @@ describe('DiscordCredentialManager', () => {
|
|
|
107
107
|
expect(token).toBeNull()
|
|
108
108
|
})
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
it('getCurrentServer returns null when not set', async () => {
|
|
111
111
|
const manager = setup()
|
|
112
112
|
const server = await manager.getCurrentServer()
|
|
113
113
|
expect(server).toBeNull()
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
it('setCurrentServer saves server id', async () => {
|
|
117
117
|
const manager = setup()
|
|
118
118
|
await manager.setCurrentServer('server-456')
|
|
119
119
|
|
|
@@ -121,13 +121,13 @@ describe('DiscordCredentialManager', () => {
|
|
|
121
121
|
expect(server).toBe('server-456')
|
|
122
122
|
})
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
it('getServers returns empty object when no servers set', async () => {
|
|
125
125
|
const manager = setup()
|
|
126
126
|
const servers = await manager.getServers()
|
|
127
127
|
expect(servers).toEqual({})
|
|
128
128
|
})
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
it('setServers saves servers to config', async () => {
|
|
131
131
|
const manager = setup()
|
|
132
132
|
const servers = {
|
|
133
133
|
'server-1': { server_id: 'server-1', server_name: 'Server One' },
|
|
@@ -140,13 +140,13 @@ describe('DiscordCredentialManager', () => {
|
|
|
140
140
|
expect(loaded).toEqual(servers)
|
|
141
141
|
})
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
it('getCredentials returns null when not authenticated', async () => {
|
|
144
144
|
const manager = setup()
|
|
145
145
|
const creds = await manager.getCredentials()
|
|
146
146
|
expect(creds).toBeNull()
|
|
147
147
|
})
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
it('getCredentials returns null when token exists but no server selected', async () => {
|
|
150
150
|
const manager = setup()
|
|
151
151
|
await manager.setToken('test-token')
|
|
152
152
|
|
|
@@ -154,7 +154,7 @@ describe('DiscordCredentialManager', () => {
|
|
|
154
154
|
expect(creds).toBeNull()
|
|
155
155
|
})
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
it('getCredentials returns null when server selected but no token', async () => {
|
|
158
158
|
const manager = setup()
|
|
159
159
|
await manager.setCurrentServer('server-123')
|
|
160
160
|
|
|
@@ -162,7 +162,7 @@ describe('DiscordCredentialManager', () => {
|
|
|
162
162
|
expect(creds).toBeNull()
|
|
163
163
|
})
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
it('getCredentials returns token and serverId when both are set', async () => {
|
|
166
166
|
const manager = setup()
|
|
167
167
|
await manager.setToken('test-token-xyz')
|
|
168
168
|
await manager.setCurrentServer('server-789')
|
|
@@ -174,7 +174,7 @@ describe('DiscordCredentialManager', () => {
|
|
|
174
174
|
})
|
|
175
175
|
})
|
|
176
176
|
|
|
177
|
-
|
|
177
|
+
it('multiple operations preserve existing data', async () => {
|
|
178
178
|
const manager = setup()
|
|
179
179
|
await manager.setToken('token-1')
|
|
180
180
|
await manager.setCurrentServer('server-1')
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, spyOn,
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, spyOn, it } from 'bun:test'
|
|
2
2
|
|
|
3
3
|
import { DiscordClient } from './client'
|
|
4
4
|
import { DiscordCredentialManager } from './credential-manager'
|
|
@@ -42,7 +42,7 @@ afterEach(() => {
|
|
|
42
42
|
})
|
|
43
43
|
|
|
44
44
|
describe('ensureDiscordAuth', () => {
|
|
45
|
-
|
|
45
|
+
it('skips extraction when token already exists', async () => {
|
|
46
46
|
// given
|
|
47
47
|
getTokenSpy.mockResolvedValue('existing-token')
|
|
48
48
|
|
|
@@ -53,7 +53,7 @@ describe('ensureDiscordAuth', () => {
|
|
|
53
53
|
expect(extractSpy).not.toHaveBeenCalled()
|
|
54
54
|
})
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
it('extracts and saves credentials when no token', async () => {
|
|
57
57
|
// when
|
|
58
58
|
await ensureDiscordAuth()
|
|
59
59
|
|
|
@@ -71,7 +71,7 @@ describe('ensureDiscordAuth', () => {
|
|
|
71
71
|
})
|
|
72
72
|
})
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
it('sets first server as current', async () => {
|
|
75
75
|
// when
|
|
76
76
|
await ensureDiscordAuth()
|
|
77
77
|
|
|
@@ -79,7 +79,7 @@ describe('ensureDiscordAuth', () => {
|
|
|
79
79
|
expect(saveSpy).toHaveBeenCalledWith(expect.objectContaining({ current_server: 'server-1' }))
|
|
80
80
|
})
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
it('handles no servers with null current_server', async () => {
|
|
83
83
|
// given
|
|
84
84
|
listServersSpy.mockResolvedValue([])
|
|
85
85
|
|
|
@@ -90,7 +90,7 @@ describe('ensureDiscordAuth', () => {
|
|
|
90
90
|
expect(saveSpy).toHaveBeenCalledWith(expect.objectContaining({ current_server: null, servers: {} }))
|
|
91
91
|
})
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
it('does not save when extraction returns null', async () => {
|
|
94
94
|
// given
|
|
95
95
|
extractSpy.mockResolvedValue([])
|
|
96
96
|
|
|
@@ -102,7 +102,7 @@ describe('ensureDiscordAuth', () => {
|
|
|
102
102
|
expect(saveSpy).not.toHaveBeenCalled()
|
|
103
103
|
})
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
it('silently handles extraction failure', async () => {
|
|
106
106
|
// given
|
|
107
107
|
extractSpy.mockRejectedValue(new Error('Discord not found'))
|
|
108
108
|
|
|
@@ -113,7 +113,7 @@ describe('ensureDiscordAuth', () => {
|
|
|
113
113
|
expect(saveSpy).not.toHaveBeenCalled()
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
it('silently handles auth validation failure', async () => {
|
|
117
117
|
// given
|
|
118
118
|
testAuthSpy.mockRejectedValue(new Error('401 Unauthorized'))
|
|
119
119
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect,
|
|
1
|
+
import { expect, it } from 'bun:test'
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
DiscordChannelSchema,
|
|
@@ -19,66 +19,66 @@ import {
|
|
|
19
19
|
DiscordUserSchema,
|
|
20
20
|
} from '@/platforms/discord/index'
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
it('DiscordClient is exported from barrel', () => {
|
|
23
23
|
expect(typeof DiscordClient).toBe('function')
|
|
24
24
|
})
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
it('DiscordError is exported from barrel', () => {
|
|
27
27
|
expect(typeof DiscordError).toBe('function')
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
it('DiscordCredentialManager is exported from barrel', () => {
|
|
31
31
|
expect(typeof DiscordCredentialManager).toBe('function')
|
|
32
32
|
})
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
it('DiscordGuildSchema is exported from barrel', () => {
|
|
35
35
|
expect(typeof DiscordGuildSchema.parse).toBe('function')
|
|
36
36
|
})
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
it('DiscordChannelSchema is exported from barrel', () => {
|
|
39
39
|
expect(typeof DiscordChannelSchema.parse).toBe('function')
|
|
40
40
|
})
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
it('DiscordMessageSchema is exported from barrel', () => {
|
|
43
43
|
expect(typeof DiscordMessageSchema.parse).toBe('function')
|
|
44
44
|
})
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
it('DiscordUserSchema is exported from barrel', () => {
|
|
47
47
|
expect(typeof DiscordUserSchema.parse).toBe('function')
|
|
48
48
|
})
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
it('DiscordDMChannelSchema is exported from barrel', () => {
|
|
51
51
|
expect(typeof DiscordDMChannelSchema.parse).toBe('function')
|
|
52
52
|
})
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
it('DiscordReactionSchema is exported from barrel', () => {
|
|
55
55
|
expect(typeof DiscordReactionSchema.parse).toBe('function')
|
|
56
56
|
})
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
it('DiscordFileSchema is exported from barrel', () => {
|
|
59
59
|
expect(typeof DiscordFileSchema.parse).toBe('function')
|
|
60
60
|
})
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
it('DiscordMentionSchema is exported from barrel', () => {
|
|
63
63
|
expect(typeof DiscordMentionSchema.parse).toBe('function')
|
|
64
64
|
})
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
it('DiscordRelationshipSchema is exported from barrel', () => {
|
|
67
67
|
expect(typeof DiscordRelationshipSchema.parse).toBe('function')
|
|
68
68
|
})
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
it('DiscordSearchResultSchema is exported from barrel', () => {
|
|
71
71
|
expect(typeof DiscordSearchResultSchema.parse).toBe('function')
|
|
72
72
|
})
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
it('DiscordSearchResponseSchema is exported from barrel', () => {
|
|
75
75
|
expect(typeof DiscordSearchResponseSchema.parse).toBe('function')
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
it('DiscordCredentialsSchema is exported from barrel', () => {
|
|
79
79
|
expect(typeof DiscordCredentialsSchema.parse).toBe('function')
|
|
80
80
|
})
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
it('DiscordConfigSchema is exported from barrel', () => {
|
|
83
83
|
expect(typeof DiscordConfigSchema.parse).toBe('function')
|
|
84
84
|
})
|