@gravito/satellite-support 0.1.6 → 0.2.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/package.json CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "@gravito/satellite-support",
3
- "version": "0.1.6",
3
+ "sideEffects": false,
4
+ "version": "0.2.0",
4
5
  "type": "module",
5
6
  "main": "dist/index.js",
6
7
  "scripts": {
7
- "build": "tsup src/index.ts --format esm --dts",
8
+ "build": "tsup src/index.ts --format esm",
9
+ "build:dts": "tsup src/index.ts --format esm --dts",
8
10
  "typecheck": "bun tsc -p tsconfig.json --noEmit --skipLibCheck"
9
11
  },
10
12
  "types": "dist/index.d.ts",
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@gravito/satellite-support",
3
+ "version": "0.1.5",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "build": "tsup src/index.ts --format esm --dts",
8
+ "typecheck": "bun tsc -p tsconfig.json --noEmit --skipLibCheck"
9
+ },
10
+ "types": "dist/index.d.ts",
11
+ "dependencies": {
12
+ "@gravito/enterprise": "workspace:*",
13
+ "@gravito/atlas": "workspace:*",
14
+ "@gravito/ripple": "workspace:*",
15
+ "@gravito/core": "workspace:*"
16
+ },
17
+ "devDependencies": {
18
+ "ioredis": "^5.9.2",
19
+ "tsup": "^8.3.5",
20
+ "typescript": "^5.9.3"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/gravito-framework/gravito.git",
28
+ "directory": "satellites/support"
29
+ }
30
+ }
@@ -0,0 +1,219 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { Conversation } from '../src/Domain/Entities/Conversation'
3
+ import { Message } from '../src/Domain/Entities/Message'
4
+
5
+ describe('Support Domain - Conversation & Message', () => {
6
+ describe('Conversation Creation', () => {
7
+ it('應該能啟動新對話且初始狀態為 PENDING', () => {
8
+ const conv = Conversation.start('cust-123', {
9
+ type: 'ORDER',
10
+ id: 'ord-456',
11
+ })
12
+
13
+ expect(conv.id).toBeDefined()
14
+ const props = conv.unpack()
15
+ expect(props.participantId).toBe('cust-123')
16
+ expect(props.contextType).toBe('ORDER')
17
+ expect(props.contextId).toBe('ord-456')
18
+ expect(props.status).toBe('PENDING')
19
+ })
20
+
21
+ it('應該能設置主旨', () => {
22
+ const conv = Conversation.start('cust-123', {
23
+ type: 'FORM',
24
+ subject: '詢問退貨',
25
+ })
26
+
27
+ const props = conv.unpack()
28
+ expect(props.subject).toBe('詢問退貨')
29
+ })
30
+
31
+ it('應該支援不同的上下文類型', () => {
32
+ const contexts = ['ORDER', 'PRODUCT', 'FORM', 'GENERAL'] as const
33
+
34
+ contexts.forEach((type) => {
35
+ const conv = Conversation.start('cust-123', { type })
36
+ const props = conv.unpack()
37
+ expect(props.contextType).toBe(type)
38
+ })
39
+ })
40
+
41
+ it('應該自動設置 createdAt 和 lastMessageAt', () => {
42
+ const conv = Conversation.start('cust-123', {
43
+ type: 'ORDER',
44
+ })
45
+
46
+ const props = conv.unpack()
47
+ expect(props.createdAt).toBeInstanceOf(Date)
48
+ expect(props.lastMessageAt).toBeInstanceOf(Date)
49
+ })
50
+
51
+ it('應該能帶自定義 ID 啟動', () => {
52
+ const conv = new Conversation(
53
+ {
54
+ participantId: 'cust-123',
55
+ contextType: 'ORDER',
56
+ status: 'PENDING',
57
+ lastMessageAt: new Date(),
58
+ createdAt: new Date(),
59
+ },
60
+ 'conv-custom-123'
61
+ )
62
+
63
+ expect(conv.id).toBe('conv-custom-123')
64
+ })
65
+ })
66
+
67
+ describe('Message Creation', () => {
68
+ it('應該能建立新訊息且自動設置 createdAt', () => {
69
+ const msg = Message.create({
70
+ conversationId: 'conv-123',
71
+ sender: 'CUSTOMER',
72
+ type: 'TEXT',
73
+ content: 'Hello support!',
74
+ })
75
+
76
+ expect(msg.id).toBeDefined()
77
+ const props = msg.unpack()
78
+ expect(props.conversationId).toBe('conv-123')
79
+ expect(props.sender).toBe('CUSTOMER')
80
+ expect(props.type).toBe('TEXT')
81
+ expect(props.content).toBe('Hello support!')
82
+ expect(props.createdAt).toBeInstanceOf(Date)
83
+ })
84
+
85
+ it('應該支援不同的訊息類型', () => {
86
+ const types = ['TEXT', 'IMAGE', 'CARD'] as const
87
+
88
+ types.forEach((type) => {
89
+ const msg = Message.create({
90
+ conversationId: 'conv-123',
91
+ sender: 'CUSTOMER',
92
+ type,
93
+ content: `Content of ${type}`,
94
+ })
95
+
96
+ const props = msg.unpack()
97
+ expect(props.type).toBe(type)
98
+ })
99
+ })
100
+
101
+ it('應該支援來自顧客和客服的訊息', () => {
102
+ const customerMsg = Message.create({
103
+ conversationId: 'conv-123',
104
+ sender: 'CUSTOMER',
105
+ type: 'TEXT',
106
+ content: 'Hello',
107
+ })
108
+
109
+ const supportMsg = Message.create({
110
+ conversationId: 'conv-123',
111
+ sender: 'SUPPORT',
112
+ type: 'TEXT',
113
+ content: 'Hi there',
114
+ })
115
+
116
+ expect(customerMsg.unpack().sender).toBe('CUSTOMER')
117
+ expect(supportMsg.unpack().sender).toBe('SUPPORT')
118
+ })
119
+
120
+ it('應該能儲存 metadata', () => {
121
+ const msg = Message.create({
122
+ conversationId: 'conv-123',
123
+ sender: 'SUPPORT',
124
+ type: 'CARD',
125
+ content: 'Order details',
126
+ metadata: {
127
+ orderId: 'ord-456',
128
+ status: 'shipped',
129
+ },
130
+ })
131
+
132
+ const props = msg.unpack()
133
+ expect(props.metadata.orderId).toBe('ord-456')
134
+ expect(props.metadata.status).toBe('shipped')
135
+ })
136
+ })
137
+
138
+ describe('Conversation Workflow', () => {
139
+ it('應該能建立對話並添加訊息', () => {
140
+ const conv = Conversation.start('cust-123', {
141
+ type: 'ORDER',
142
+ id: 'ord-456',
143
+ })
144
+
145
+ const msg1 = Message.create({
146
+ conversationId: conv.id,
147
+ sender: 'CUSTOMER',
148
+ type: 'TEXT',
149
+ content: 'I have a question',
150
+ })
151
+
152
+ const msg2 = Message.create({
153
+ conversationId: conv.id,
154
+ sender: 'SUPPORT',
155
+ type: 'TEXT',
156
+ content: 'How can we help?',
157
+ })
158
+
159
+ expect(msg1.unpack().conversationId).toBe(conv.id)
160
+ expect(msg2.unpack().conversationId).toBe(conv.id)
161
+ })
162
+
163
+ it('應該能處理多個對話', () => {
164
+ const conv1 = Conversation.start('cust-123', { type: 'ORDER', id: 'ord-1' })
165
+ const conv2 = Conversation.start('cust-456', { type: 'PRODUCT', id: 'prod-1' })
166
+
167
+ expect(conv1.id).not.toBe(conv2.id)
168
+ expect(conv1.unpack().participantId).toBe('cust-123')
169
+ expect(conv2.unpack().participantId).toBe('cust-456')
170
+ })
171
+ })
172
+
173
+ describe('Edge Cases', () => {
174
+ it('應該能處理長文本訊息', () => {
175
+ const longContent = 'x'.repeat(10000)
176
+ const msg = Message.create({
177
+ conversationId: 'conv-123',
178
+ sender: 'CUSTOMER',
179
+ type: 'TEXT',
180
+ content: longContent,
181
+ })
182
+
183
+ expect(msg.unpack().content.length).toBe(10000)
184
+ })
185
+
186
+ it('應該能處理複雜的 metadata 結構', () => {
187
+ const msg = Message.create({
188
+ conversationId: 'conv-123',
189
+ sender: 'SUPPORT',
190
+ type: 'CARD',
191
+ content: 'Complex card',
192
+ metadata: {
193
+ nested: {
194
+ deep: {
195
+ structure: 'value',
196
+ },
197
+ },
198
+ array: [1, 2, 3],
199
+ },
200
+ })
201
+
202
+ const props = msg.unpack()
203
+ expect(props.metadata.nested.deep.structure).toBe('value')
204
+ expect(props.metadata.array).toEqual([1, 2, 3])
205
+ })
206
+
207
+ it('應該能處理空 metadata', () => {
208
+ const msg = Message.create({
209
+ conversationId: 'conv-123',
210
+ sender: 'CUSTOMER',
211
+ type: 'TEXT',
212
+ content: 'Simple message',
213
+ })
214
+
215
+ const props = msg.unpack()
216
+ expect(props.metadata).toBeUndefined()
217
+ })
218
+ })
219
+ })