@lobehub/lobehub 2.0.0-next.186 → 2.0.0-next.188
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/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/models.json +89 -5
- package/locales/ar/plugin.json +5 -0
- package/locales/ar/providers.json +1 -0
- package/locales/bg-BG/models.json +68 -0
- package/locales/bg-BG/plugin.json +5 -0
- package/locales/bg-BG/providers.json +1 -0
- package/locales/de-DE/models.json +85 -0
- package/locales/de-DE/plugin.json +5 -0
- package/locales/de-DE/providers.json +1 -0
- package/locales/en-US/models.json +11 -10
- package/locales/en-US/plugin.json +5 -0
- package/locales/en-US/providers.json +1 -0
- package/locales/es-ES/models.json +72 -0
- package/locales/es-ES/plugin.json +5 -0
- package/locales/es-ES/providers.json +1 -0
- package/locales/fa-IR/models.json +86 -0
- package/locales/fa-IR/plugin.json +5 -0
- package/locales/fa-IR/providers.json +1 -0
- package/locales/fr-FR/models.json +49 -0
- package/locales/fr-FR/plugin.json +5 -0
- package/locales/fr-FR/providers.json +1 -0
- package/locales/it-IT/models.json +82 -0
- package/locales/it-IT/plugin.json +5 -0
- package/locales/it-IT/providers.json +1 -0
- package/locales/ja-JP/models.json +42 -5
- package/locales/ja-JP/plugin.json +5 -0
- package/locales/ja-JP/providers.json +1 -0
- package/locales/ko-KR/models.json +54 -0
- package/locales/ko-KR/plugin.json +5 -0
- package/locales/ko-KR/providers.json +1 -0
- package/locales/nl-NL/models.json +12 -1
- package/locales/nl-NL/plugin.json +5 -0
- package/locales/nl-NL/providers.json +1 -0
- package/locales/pl-PL/models.json +46 -0
- package/locales/pl-PL/plugin.json +5 -0
- package/locales/pl-PL/providers.json +1 -0
- package/locales/pt-BR/models.json +59 -0
- package/locales/pt-BR/plugin.json +5 -0
- package/locales/pt-BR/providers.json +1 -0
- package/locales/ru-RU/models.json +85 -0
- package/locales/ru-RU/plugin.json +5 -0
- package/locales/ru-RU/providers.json +1 -0
- package/locales/tr-TR/models.json +81 -0
- package/locales/tr-TR/plugin.json +5 -0
- package/locales/tr-TR/providers.json +1 -0
- package/locales/vi-VN/models.json +54 -0
- package/locales/vi-VN/plugin.json +5 -0
- package/locales/vi-VN/providers.json +1 -0
- package/locales/zh-CN/models.json +42 -5
- package/locales/zh-CN/plugin.json +5 -0
- package/locales/zh-CN/providers.json +1 -0
- package/locales/zh-TW/models.json +85 -0
- package/locales/zh-TW/plugin.json +5 -0
- package/locales/zh-TW/providers.json +1 -0
- package/package.json +1 -1
- package/packages/builtin-tool-gtd/src/manifest.ts +13 -8
- package/packages/builtin-tool-gtd/src/systemRole.ts +54 -19
- package/packages/builtin-tool-knowledge-base/package.json +1 -0
- package/packages/builtin-tool-knowledge-base/src/client/Inspector/ReadKnowledge/index.tsx +97 -0
- package/packages/builtin-tool-knowledge-base/src/client/Inspector/SearchKnowledgeBase/index.tsx +75 -0
- package/packages/builtin-tool-knowledge-base/src/client/Inspector/index.ts +11 -0
- package/packages/builtin-tool-knowledge-base/src/client/Render/ReadKnowledge/FileCard.tsx +12 -12
- package/packages/builtin-tool-knowledge-base/src/client/Render/ReadKnowledge/index.tsx +16 -25
- package/packages/builtin-tool-knowledge-base/src/client/Render/SearchKnowledgeBase/Item/index.tsx +21 -47
- package/packages/builtin-tool-knowledge-base/src/client/Render/SearchKnowledgeBase/index.tsx +19 -31
- package/packages/builtin-tool-knowledge-base/src/client/Render/index.ts +0 -5
- package/packages/builtin-tool-knowledge-base/src/client/index.ts +5 -1
- package/packages/builtin-tool-knowledge-base/src/executor/index.ts +119 -0
- package/packages/builtin-tool-local-system/package.json +1 -0
- package/packages/builtin-tool-local-system/src/client/Inspector/EditLocalFile/index.tsx +44 -29
- package/packages/builtin-tool-local-system/src/client/Inspector/GrepContent/index.tsx +20 -18
- package/packages/builtin-tool-local-system/src/client/Inspector/ListLocalFiles/index.tsx +76 -0
- package/packages/builtin-tool-local-system/src/client/Inspector/ReadLocalFile/index.tsx +8 -32
- package/packages/builtin-tool-local-system/src/client/Inspector/RenameLocalFile/index.tsx +62 -0
- package/packages/builtin-tool-local-system/src/client/Inspector/SearchLocalFiles/index.tsx +17 -11
- package/packages/builtin-tool-local-system/src/client/Inspector/WriteLocalFile/index.tsx +61 -0
- package/packages/builtin-tool-local-system/src/client/Inspector/index.ts +6 -0
- package/packages/builtin-tool-local-system/src/client/Render/EditLocalFile/index.tsx +6 -1
- package/packages/builtin-tool-local-system/src/client/Render/SearchFiles/SearchQuery/SearchView.tsx +19 -31
- package/packages/builtin-tool-local-system/src/client/Render/SearchFiles/SearchQuery/index.tsx +2 -42
- package/packages/builtin-tool-local-system/src/client/Render/index.ts +0 -2
- package/packages/builtin-tool-local-system/src/client/components/FilePathDisplay.tsx +56 -0
- package/packages/builtin-tool-local-system/src/client/components/index.ts +2 -0
- package/packages/builtin-tool-local-system/src/executor/index.ts +435 -0
- package/packages/builtin-tool-web-browsing/src/client/Inspector/Search/index.tsx +32 -5
- package/packages/fetch-sse/src/__tests__/request.test.ts +608 -0
- package/packages/model-bank/src/aiModels/aihubmix.ts +44 -8
- package/packages/model-bank/src/aiModels/google.ts +49 -17
- package/packages/model-bank/src/aiModels/hunyuan.ts +20 -0
- package/packages/model-bank/src/aiModels/infiniai.ts +48 -7
- package/packages/model-bank/src/aiModels/lobehub.ts +13 -11
- package/packages/model-bank/src/aiModels/minimax.ts +46 -2
- package/packages/model-bank/src/aiModels/ollamacloud.ts +40 -5
- package/packages/model-bank/src/aiModels/openai.ts +6 -3
- package/packages/model-bank/src/aiModels/qwen.ts +1 -1
- package/packages/model-bank/src/aiModels/siliconcloud.ts +60 -0
- package/packages/model-bank/src/aiModels/vertexai.ts +77 -44
- package/packages/model-bank/src/aiModels/volcengine.ts +111 -2
- package/packages/model-bank/src/aiModels/zenmux.ts +19 -13
- package/packages/model-bank/src/aiModels/zhipu.ts +64 -2
- package/packages/model-bank/src/types/aiModel.ts +3 -0
- package/packages/model-runtime/src/core/contextBuilders/google.test.ts +84 -0
- package/packages/model-runtime/src/core/contextBuilders/google.ts +37 -1
- package/packages/model-runtime/src/providers/volcengine/index.ts +2 -1
- package/packages/model-runtime/src/providers/zhipu/index.test.ts +0 -27
- package/packages/model-runtime/src/providers/zhipu/index.ts +1 -1
- package/packages/model-runtime/src/utils/modelParse.ts +26 -21
- package/packages/types/src/agent/chatConfig.ts +6 -2
- package/src/features/ChatInput/ActionBar/Model/ControlsForm.tsx +40 -1
- package/src/features/ChatInput/ActionBar/Model/GPT52ProReasoningEffortSlider.tsx +59 -0
- package/src/features/ChatInput/ActionBar/Model/GPT52ReasoningEffortSlider.tsx +61 -0
- package/src/features/ChatInput/ActionBar/Model/TextVerbositySlider.tsx +1 -1
- package/src/features/ChatInput/ActionBar/Model/ThinkingLevel2Slider.tsx +58 -0
- package/src/features/ChatInput/ActionBar/Model/ThinkingLevelSlider.tsx +10 -8
- package/src/helpers/toolEngineering/index.ts +1 -1
- package/src/locales/default/plugin.ts +6 -0
- package/src/server/modules/Mecha/AgentToolsEngine/__tests__/index.test.ts +1 -1
- package/src/server/modules/Mecha/AgentToolsEngine/index.ts +1 -1
- package/src/services/chat/mecha/modelParamsResolver.ts +11 -0
- package/src/store/chat/slices/builtinTool/actions/index.ts +1 -11
- package/src/store/tool/slices/builtin/executors/index.ts +4 -0
- package/src/styles/text.ts +1 -1
- package/src/tools/executionRuntimes.ts +3 -8
- package/src/tools/identifiers.ts +1 -1
- package/src/tools/index.ts +1 -1
- package/src/tools/inspectors.ts +5 -0
- package/src/tools/renders.ts +6 -12
- package/packages/builtin-tool-local-system/src/client/Render/RenameLocalFile/index.tsx +0 -37
- package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +0 -201
- package/src/store/chat/slices/builtinTool/actions/knowledgeBase.ts +0 -163
- package/src/store/chat/slices/builtinTool/actions/localSystem.ts +0 -241
- package/src/tools/knowledge-base/ExecutionRuntime/index.ts +0 -25
- package/src/tools/knowledge-base/Render/ReadKnowledge/index.tsx +0 -29
- package/src/tools/knowledge-base/Render/SearchKnowledgeBase/index.tsx +0 -29
- package/src/tools/knowledge-base/Render/index.ts +0 -7
- package/src/tools/knowledge-base/index.ts +0 -12
- package/src/tools/local-system/ExecutionRuntime/index.ts +0 -9
- package/src/tools/local-system/systemRole.ts +0 -1
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { getRequestBody } from '../request';
|
|
4
|
+
|
|
5
|
+
describe('getRequestBody', () => {
|
|
6
|
+
describe('undefined or null input', () => {
|
|
7
|
+
it('should return undefined when body is undefined', async () => {
|
|
8
|
+
// Arrange & Act
|
|
9
|
+
const result = await getRequestBody(undefined);
|
|
10
|
+
|
|
11
|
+
// Assert
|
|
12
|
+
expect(result).toBeUndefined();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should return undefined when body is null', async () => {
|
|
16
|
+
// Arrange & Act
|
|
17
|
+
const result = await getRequestBody(null);
|
|
18
|
+
|
|
19
|
+
// Assert
|
|
20
|
+
expect(result).toBeUndefined();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should return undefined when body is not provided', async () => {
|
|
24
|
+
// Arrange & Act
|
|
25
|
+
const result = await getRequestBody();
|
|
26
|
+
|
|
27
|
+
// Assert
|
|
28
|
+
expect(result).toBeUndefined();
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('string input', () => {
|
|
33
|
+
it('should return string body as-is', async () => {
|
|
34
|
+
// Arrange
|
|
35
|
+
const body = 'test string body';
|
|
36
|
+
|
|
37
|
+
// Act
|
|
38
|
+
const result = await getRequestBody(body);
|
|
39
|
+
|
|
40
|
+
// Assert
|
|
41
|
+
expect(result).toBe('test string body');
|
|
42
|
+
expect(typeof result).toBe('string');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should return undefined for empty string (falsy check)', async () => {
|
|
46
|
+
// Arrange
|
|
47
|
+
const body = '';
|
|
48
|
+
|
|
49
|
+
// Act
|
|
50
|
+
const result = await getRequestBody(body);
|
|
51
|
+
|
|
52
|
+
// Assert
|
|
53
|
+
// Empty string is falsy, so the function returns undefined
|
|
54
|
+
expect(result).toBeUndefined();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should handle JSON string', async () => {
|
|
58
|
+
// Arrange
|
|
59
|
+
const body = JSON.stringify({ key: 'value', number: 123 });
|
|
60
|
+
|
|
61
|
+
// Act
|
|
62
|
+
const result = await getRequestBody(body);
|
|
63
|
+
|
|
64
|
+
// Assert
|
|
65
|
+
expect(result).toBe('{"key":"value","number":123}');
|
|
66
|
+
expect(typeof result).toBe('string');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should handle string with special characters', async () => {
|
|
70
|
+
// Arrange
|
|
71
|
+
const body = 'test\nstring\twith\rspecial\u0000chars';
|
|
72
|
+
|
|
73
|
+
// Act
|
|
74
|
+
const result = await getRequestBody(body);
|
|
75
|
+
|
|
76
|
+
// Assert
|
|
77
|
+
expect(result).toBe('test\nstring\twith\rspecial\u0000chars');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should handle very long string', async () => {
|
|
81
|
+
// Arrange
|
|
82
|
+
const body = 'a'.repeat(10000);
|
|
83
|
+
|
|
84
|
+
// Act
|
|
85
|
+
const result = await getRequestBody(body);
|
|
86
|
+
|
|
87
|
+
// Assert
|
|
88
|
+
expect(result).toBe(body);
|
|
89
|
+
expect(typeof result).toBe('string');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('ArrayBuffer input', () => {
|
|
94
|
+
it('should return ArrayBuffer as-is', async () => {
|
|
95
|
+
// Arrange
|
|
96
|
+
const buffer = new ArrayBuffer(8);
|
|
97
|
+
const view = new Uint8Array(buffer);
|
|
98
|
+
view[0] = 65; // 'A'
|
|
99
|
+
view[1] = 66; // 'B'
|
|
100
|
+
|
|
101
|
+
// Act
|
|
102
|
+
const result = await getRequestBody(buffer);
|
|
103
|
+
|
|
104
|
+
// Assert
|
|
105
|
+
expect(result).toBe(buffer);
|
|
106
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
107
|
+
expect((result as ArrayBuffer).byteLength).toBe(8);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should handle empty ArrayBuffer', async () => {
|
|
111
|
+
// Arrange
|
|
112
|
+
const buffer = new ArrayBuffer(0);
|
|
113
|
+
|
|
114
|
+
// Act
|
|
115
|
+
const result = await getRequestBody(buffer);
|
|
116
|
+
|
|
117
|
+
// Assert
|
|
118
|
+
expect(result).toBe(buffer);
|
|
119
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
120
|
+
expect((result as ArrayBuffer).byteLength).toBe(0);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should handle large ArrayBuffer', async () => {
|
|
124
|
+
// Arrange
|
|
125
|
+
const buffer = new ArrayBuffer(1024 * 1024); // 1MB
|
|
126
|
+
|
|
127
|
+
// Act
|
|
128
|
+
const result = await getRequestBody(buffer);
|
|
129
|
+
|
|
130
|
+
// Assert
|
|
131
|
+
expect(result).toBe(buffer);
|
|
132
|
+
expect((result as ArrayBuffer).byteLength).toBe(1024 * 1024);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('ArrayBufferView input (TypedArrays)', () => {
|
|
137
|
+
it('should convert Uint8Array to sliced ArrayBuffer', async () => {
|
|
138
|
+
// Arrange
|
|
139
|
+
const buffer = new ArrayBuffer(16);
|
|
140
|
+
const uint8View = new Uint8Array(buffer, 4, 8); // offset: 4, length: 8
|
|
141
|
+
uint8View[0] = 65;
|
|
142
|
+
uint8View[1] = 66;
|
|
143
|
+
|
|
144
|
+
// Act
|
|
145
|
+
const result = await getRequestBody(uint8View);
|
|
146
|
+
|
|
147
|
+
// Assert
|
|
148
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
149
|
+
expect((result as ArrayBuffer).byteLength).toBe(8);
|
|
150
|
+
expect(result).not.toBe(buffer); // Should be a new sliced buffer
|
|
151
|
+
|
|
152
|
+
// Verify the sliced data
|
|
153
|
+
const resultView = new Uint8Array(result as ArrayBuffer);
|
|
154
|
+
expect(resultView[0]).toBe(65);
|
|
155
|
+
expect(resultView[1]).toBe(66);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should convert Uint16Array to sliced ArrayBuffer', async () => {
|
|
159
|
+
// Arrange
|
|
160
|
+
const buffer = new ArrayBuffer(32);
|
|
161
|
+
const uint16View = new Uint16Array(buffer, 8, 4); // offset: 8 bytes, length: 4 elements
|
|
162
|
+
uint16View[0] = 256;
|
|
163
|
+
uint16View[1] = 512;
|
|
164
|
+
|
|
165
|
+
// Act
|
|
166
|
+
const result = await getRequestBody(uint16View);
|
|
167
|
+
|
|
168
|
+
// Assert
|
|
169
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
170
|
+
expect((result as ArrayBuffer).byteLength).toBe(8); // 4 elements * 2 bytes
|
|
171
|
+
expect(result).not.toBe(buffer);
|
|
172
|
+
|
|
173
|
+
// Verify data
|
|
174
|
+
const resultView = new Uint16Array(result as ArrayBuffer);
|
|
175
|
+
expect(resultView[0]).toBe(256);
|
|
176
|
+
expect(resultView[1]).toBe(512);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should convert Int32Array to sliced ArrayBuffer', async () => {
|
|
180
|
+
// Arrange
|
|
181
|
+
const buffer = new ArrayBuffer(64);
|
|
182
|
+
const int32View = new Int32Array(buffer, 16, 8); // offset: 16 bytes, length: 8 elements
|
|
183
|
+
int32View[0] = -12345;
|
|
184
|
+
int32View[7] = 67890;
|
|
185
|
+
|
|
186
|
+
// Act
|
|
187
|
+
const result = await getRequestBody(int32View);
|
|
188
|
+
|
|
189
|
+
// Assert
|
|
190
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
191
|
+
expect((result as ArrayBuffer).byteLength).toBe(32); // 8 elements * 4 bytes
|
|
192
|
+
expect(result).not.toBe(buffer);
|
|
193
|
+
|
|
194
|
+
// Verify data
|
|
195
|
+
const resultView = new Int32Array(result as ArrayBuffer);
|
|
196
|
+
expect(resultView[0]).toBe(-12345);
|
|
197
|
+
expect(resultView[7]).toBe(67890);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('should convert Float32Array to sliced ArrayBuffer', async () => {
|
|
201
|
+
// Arrange
|
|
202
|
+
const buffer = new ArrayBuffer(40);
|
|
203
|
+
const float32View = new Float32Array(buffer, 4, 5);
|
|
204
|
+
float32View[0] = 3.14159;
|
|
205
|
+
float32View[4] = 2.71828;
|
|
206
|
+
|
|
207
|
+
// Act
|
|
208
|
+
const result = await getRequestBody(float32View);
|
|
209
|
+
|
|
210
|
+
// Assert
|
|
211
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
212
|
+
expect((result as ArrayBuffer).byteLength).toBe(20); // 5 elements * 4 bytes
|
|
213
|
+
|
|
214
|
+
const resultView = new Float32Array(result as ArrayBuffer);
|
|
215
|
+
expect(resultView[0]).toBeCloseTo(3.14159);
|
|
216
|
+
expect(resultView[4]).toBeCloseTo(2.71828);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should convert DataView to sliced ArrayBuffer', async () => {
|
|
220
|
+
// Arrange
|
|
221
|
+
const buffer = new ArrayBuffer(24);
|
|
222
|
+
const dataView = new DataView(buffer, 8, 8); // offset: 8, length: 8
|
|
223
|
+
dataView.setUint8(0, 65);
|
|
224
|
+
dataView.setUint8(1, 66);
|
|
225
|
+
|
|
226
|
+
// Act
|
|
227
|
+
const result = await getRequestBody(dataView);
|
|
228
|
+
|
|
229
|
+
// Assert
|
|
230
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
231
|
+
expect((result as ArrayBuffer).byteLength).toBe(8);
|
|
232
|
+
|
|
233
|
+
const resultView = new Uint8Array(result as ArrayBuffer);
|
|
234
|
+
expect(resultView[0]).toBe(65);
|
|
235
|
+
expect(resultView[1]).toBe(66);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should handle TypedArray with zero offset', async () => {
|
|
239
|
+
// Arrange
|
|
240
|
+
const buffer = new ArrayBuffer(16);
|
|
241
|
+
const uint8View = new Uint8Array(buffer, 0, 16);
|
|
242
|
+
uint8View[0] = 100;
|
|
243
|
+
|
|
244
|
+
// Act
|
|
245
|
+
const result = await getRequestBody(uint8View);
|
|
246
|
+
|
|
247
|
+
// Assert
|
|
248
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
249
|
+
expect((result as ArrayBuffer).byteLength).toBe(16);
|
|
250
|
+
|
|
251
|
+
const resultView = new Uint8Array(result as ArrayBuffer);
|
|
252
|
+
expect(resultView[0]).toBe(100);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should handle empty TypedArray', async () => {
|
|
256
|
+
// Arrange
|
|
257
|
+
const buffer = new ArrayBuffer(16);
|
|
258
|
+
const uint8View = new Uint8Array(buffer, 8, 0); // zero length
|
|
259
|
+
|
|
260
|
+
// Act
|
|
261
|
+
const result = await getRequestBody(uint8View);
|
|
262
|
+
|
|
263
|
+
// Assert
|
|
264
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
265
|
+
expect((result as ArrayBuffer).byteLength).toBe(0);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe('Blob input', () => {
|
|
270
|
+
it('should convert Blob to ArrayBuffer', async () => {
|
|
271
|
+
// Arrange
|
|
272
|
+
const blobData = 'test blob content';
|
|
273
|
+
const blob = new Blob([blobData], { type: 'text/plain' });
|
|
274
|
+
|
|
275
|
+
// Act
|
|
276
|
+
const result = await getRequestBody(blob);
|
|
277
|
+
|
|
278
|
+
// Assert
|
|
279
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
280
|
+
|
|
281
|
+
// Verify content
|
|
282
|
+
const decoder = new TextDecoder();
|
|
283
|
+
const text = decoder.decode(result as ArrayBuffer);
|
|
284
|
+
expect(text).toBe('test blob content');
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('should convert Blob with binary data', async () => {
|
|
288
|
+
// Arrange
|
|
289
|
+
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]); // "Hello"
|
|
290
|
+
const blob = new Blob([uint8Array], { type: 'application/octet-stream' });
|
|
291
|
+
|
|
292
|
+
// Act
|
|
293
|
+
const result = await getRequestBody(blob);
|
|
294
|
+
|
|
295
|
+
// Assert
|
|
296
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
297
|
+
|
|
298
|
+
const decoder = new TextDecoder();
|
|
299
|
+
const text = decoder.decode(result as ArrayBuffer);
|
|
300
|
+
expect(text).toBe('Hello');
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should handle empty Blob', async () => {
|
|
304
|
+
// Arrange
|
|
305
|
+
const blob = new Blob([], { type: 'text/plain' });
|
|
306
|
+
|
|
307
|
+
// Act
|
|
308
|
+
const result = await getRequestBody(blob);
|
|
309
|
+
|
|
310
|
+
// Assert
|
|
311
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
312
|
+
expect((result as ArrayBuffer).byteLength).toBe(0);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('should convert File (subclass of Blob)', async () => {
|
|
316
|
+
// Arrange
|
|
317
|
+
const fileContent = 'file content';
|
|
318
|
+
const file = new File([fileContent], 'test.txt', { type: 'text/plain' });
|
|
319
|
+
|
|
320
|
+
// Act
|
|
321
|
+
const result = await getRequestBody(file);
|
|
322
|
+
|
|
323
|
+
// Assert
|
|
324
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
325
|
+
|
|
326
|
+
const decoder = new TextDecoder();
|
|
327
|
+
const text = decoder.decode(result as ArrayBuffer);
|
|
328
|
+
expect(text).toBe('file content');
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('should handle Blob with multiple chunks', async () => {
|
|
332
|
+
// Arrange
|
|
333
|
+
const blob = new Blob(['chunk1', 'chunk2', 'chunk3'], { type: 'text/plain' });
|
|
334
|
+
|
|
335
|
+
// Act
|
|
336
|
+
const result = await getRequestBody(blob);
|
|
337
|
+
|
|
338
|
+
// Assert
|
|
339
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
340
|
+
|
|
341
|
+
const decoder = new TextDecoder();
|
|
342
|
+
const text = decoder.decode(result as ArrayBuffer);
|
|
343
|
+
expect(text).toBe('chunk1chunk2chunk3');
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('should handle large Blob', async () => {
|
|
347
|
+
// Arrange
|
|
348
|
+
const largeData = 'x'.repeat(100000);
|
|
349
|
+
const blob = new Blob([largeData], { type: 'text/plain' });
|
|
350
|
+
|
|
351
|
+
// Act
|
|
352
|
+
const result = await getRequestBody(blob);
|
|
353
|
+
|
|
354
|
+
// Assert
|
|
355
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
356
|
+
expect((result as ArrayBuffer).byteLength).toBe(100000);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
describe('Unsupported types', () => {
|
|
361
|
+
it('should throw error for FormData', async () => {
|
|
362
|
+
// Arrange
|
|
363
|
+
const formData = new FormData();
|
|
364
|
+
formData.append('key', 'value');
|
|
365
|
+
|
|
366
|
+
// Spy on console.warn
|
|
367
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
368
|
+
|
|
369
|
+
// Act & Assert
|
|
370
|
+
await expect(getRequestBody(formData as any)).rejects.toThrow(
|
|
371
|
+
'Unsupported IPC proxy request body type',
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
expect(warnSpy).toHaveBeenCalledWith('Unsupported IPC proxy request body type:', 'object');
|
|
375
|
+
|
|
376
|
+
// Cleanup
|
|
377
|
+
warnSpy.mockRestore();
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('should throw error for URLSearchParams', async () => {
|
|
381
|
+
// Arrange
|
|
382
|
+
const params = new URLSearchParams({ key: 'value' });
|
|
383
|
+
|
|
384
|
+
// Spy on console.warn
|
|
385
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
386
|
+
|
|
387
|
+
// Act & Assert
|
|
388
|
+
await expect(getRequestBody(params as any)).rejects.toThrow(
|
|
389
|
+
'Unsupported IPC proxy request body type',
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
expect(warnSpy).toHaveBeenCalledWith('Unsupported IPC proxy request body type:', 'object');
|
|
393
|
+
|
|
394
|
+
// Cleanup
|
|
395
|
+
warnSpy.mockRestore();
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('should throw error for ReadableStream', async () => {
|
|
399
|
+
// Arrange
|
|
400
|
+
const stream = new ReadableStream({
|
|
401
|
+
start(controller) {
|
|
402
|
+
controller.enqueue('test');
|
|
403
|
+
controller.close();
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// Spy on console.warn
|
|
408
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
409
|
+
|
|
410
|
+
// Act & Assert
|
|
411
|
+
await expect(getRequestBody(stream as any)).rejects.toThrow(
|
|
412
|
+
'Unsupported IPC proxy request body type',
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
expect(warnSpy).toHaveBeenCalledWith('Unsupported IPC proxy request body type:', 'object');
|
|
416
|
+
|
|
417
|
+
// Cleanup
|
|
418
|
+
warnSpy.mockRestore();
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('should throw error for plain object', async () => {
|
|
422
|
+
// Arrange
|
|
423
|
+
const obj = { key: 'value' };
|
|
424
|
+
|
|
425
|
+
// Spy on console.warn
|
|
426
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
427
|
+
|
|
428
|
+
// Act & Assert
|
|
429
|
+
await expect(getRequestBody(obj as any)).rejects.toThrow(
|
|
430
|
+
'Unsupported IPC proxy request body type',
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
expect(warnSpy).toHaveBeenCalledWith('Unsupported IPC proxy request body type:', 'object');
|
|
434
|
+
|
|
435
|
+
// Cleanup
|
|
436
|
+
warnSpy.mockRestore();
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it('should throw error for number', async () => {
|
|
440
|
+
// Arrange
|
|
441
|
+
const num = 123;
|
|
442
|
+
|
|
443
|
+
// Spy on console.warn
|
|
444
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
445
|
+
|
|
446
|
+
// Act & Assert
|
|
447
|
+
await expect(getRequestBody(num as any)).rejects.toThrow(
|
|
448
|
+
'Unsupported IPC proxy request body type',
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
expect(warnSpy).toHaveBeenCalledWith('Unsupported IPC proxy request body type:', 'number');
|
|
452
|
+
|
|
453
|
+
// Cleanup
|
|
454
|
+
warnSpy.mockRestore();
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it('should throw error for boolean', async () => {
|
|
458
|
+
// Arrange
|
|
459
|
+
const bool = true;
|
|
460
|
+
|
|
461
|
+
// Spy on console.warn
|
|
462
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
463
|
+
|
|
464
|
+
// Act & Assert
|
|
465
|
+
await expect(getRequestBody(bool as any)).rejects.toThrow(
|
|
466
|
+
'Unsupported IPC proxy request body type',
|
|
467
|
+
);
|
|
468
|
+
|
|
469
|
+
expect(warnSpy).toHaveBeenCalledWith('Unsupported IPC proxy request body type:', 'boolean');
|
|
470
|
+
|
|
471
|
+
// Cleanup
|
|
472
|
+
warnSpy.mockRestore();
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
describe('Edge cases', () => {
|
|
477
|
+
it('should handle Uint8Array from actual buffer slice', async () => {
|
|
478
|
+
// Arrange - simulate real-world scenario where buffer is sliced
|
|
479
|
+
const originalBuffer = new ArrayBuffer(100);
|
|
480
|
+
const originalView = new Uint8Array(originalBuffer);
|
|
481
|
+
originalView.fill(42);
|
|
482
|
+
|
|
483
|
+
const slicedView = new Uint8Array(originalBuffer, 20, 30);
|
|
484
|
+
|
|
485
|
+
// Act
|
|
486
|
+
const result = await getRequestBody(slicedView);
|
|
487
|
+
|
|
488
|
+
// Assert
|
|
489
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
490
|
+
expect((result as ArrayBuffer).byteLength).toBe(30);
|
|
491
|
+
|
|
492
|
+
const resultView = new Uint8Array(result as ArrayBuffer);
|
|
493
|
+
expect(resultView.every((byte) => byte === 42)).toBe(true);
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
it('should preserve binary data integrity through TypedArray conversion', async () => {
|
|
497
|
+
// Arrange
|
|
498
|
+
const buffer = new ArrayBuffer(8);
|
|
499
|
+
const view = new Uint8Array(buffer);
|
|
500
|
+
// Set specific binary pattern
|
|
501
|
+
view[0] = 0xff;
|
|
502
|
+
view[1] = 0x00;
|
|
503
|
+
view[2] = 0xaa;
|
|
504
|
+
view[3] = 0x55;
|
|
505
|
+
view[4] = 0x12;
|
|
506
|
+
view[5] = 0x34;
|
|
507
|
+
view[6] = 0x56;
|
|
508
|
+
view[7] = 0x78;
|
|
509
|
+
|
|
510
|
+
// Act
|
|
511
|
+
const result = await getRequestBody(view);
|
|
512
|
+
|
|
513
|
+
// Assert
|
|
514
|
+
const resultView = new Uint8Array(result as ArrayBuffer);
|
|
515
|
+
expect(resultView[0]).toBe(0xff);
|
|
516
|
+
expect(resultView[1]).toBe(0x00);
|
|
517
|
+
expect(resultView[2]).toBe(0xaa);
|
|
518
|
+
expect(resultView[3]).toBe(0x55);
|
|
519
|
+
expect(resultView[4]).toBe(0x12);
|
|
520
|
+
expect(resultView[5]).toBe(0x34);
|
|
521
|
+
expect(resultView[6]).toBe(0x56);
|
|
522
|
+
expect(resultView[7]).toBe(0x78);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it('should handle Blob with non-ASCII characters', async () => {
|
|
526
|
+
// Arrange
|
|
527
|
+
const unicodeText = 'Hello 世界 🌍 مرحبا';
|
|
528
|
+
const blob = new Blob([unicodeText], { type: 'text/plain;charset=utf-8' });
|
|
529
|
+
|
|
530
|
+
// Act
|
|
531
|
+
const result = await getRequestBody(blob);
|
|
532
|
+
|
|
533
|
+
// Assert
|
|
534
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
535
|
+
|
|
536
|
+
const decoder = new TextDecoder('utf-8');
|
|
537
|
+
const text = decoder.decode(result as ArrayBuffer);
|
|
538
|
+
expect(text).toBe('Hello 世界 🌍 مرحبا');
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
describe('Real-world scenarios', () => {
|
|
543
|
+
it('should handle JSON API request body', async () => {
|
|
544
|
+
// Arrange
|
|
545
|
+
const apiPayload = JSON.stringify({
|
|
546
|
+
model: 'gpt-4',
|
|
547
|
+
messages: [{ role: 'user', content: 'Hello' }],
|
|
548
|
+
stream: true,
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
// Act
|
|
552
|
+
const result = await getRequestBody(apiPayload);
|
|
553
|
+
|
|
554
|
+
// Assert
|
|
555
|
+
expect(typeof result).toBe('string');
|
|
556
|
+
expect(result).toBe(apiPayload);
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
it('should handle file upload as Blob', async () => {
|
|
560
|
+
// Arrange
|
|
561
|
+
const imageData = new Uint8Array([
|
|
562
|
+
0x89,
|
|
563
|
+
0x50,
|
|
564
|
+
0x4e,
|
|
565
|
+
0x47,
|
|
566
|
+
0x0d,
|
|
567
|
+
0x0a,
|
|
568
|
+
0x1a,
|
|
569
|
+
0x0a, // PNG header
|
|
570
|
+
]);
|
|
571
|
+
const imageBlob = new Blob([imageData], { type: 'image/png' });
|
|
572
|
+
|
|
573
|
+
// Act
|
|
574
|
+
const result = await getRequestBody(imageBlob);
|
|
575
|
+
|
|
576
|
+
// Assert
|
|
577
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
578
|
+
const resultView = new Uint8Array(result as ArrayBuffer);
|
|
579
|
+
expect(resultView[0]).toBe(0x89);
|
|
580
|
+
expect(resultView[1]).toBe(0x50);
|
|
581
|
+
expect(resultView[2]).toBe(0x4e);
|
|
582
|
+
expect(resultView[3]).toBe(0x47);
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
it('should handle binary protocol buffer data', async () => {
|
|
586
|
+
// Arrange
|
|
587
|
+
const protobufData = new Uint8Array([
|
|
588
|
+
0x08, 0x96, 0x01, 0x12, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
|
|
589
|
+
]);
|
|
590
|
+
const buffer = protobufData.buffer;
|
|
591
|
+
|
|
592
|
+
// Act
|
|
593
|
+
const result = await getRequestBody(buffer);
|
|
594
|
+
|
|
595
|
+
// Assert
|
|
596
|
+
expect(result).toBe(buffer);
|
|
597
|
+
expect(result).toBeInstanceOf(ArrayBuffer);
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
it('should handle empty request (no body)', async () => {
|
|
601
|
+
// Arrange & Act
|
|
602
|
+
const result = await getRequestBody();
|
|
603
|
+
|
|
604
|
+
// Assert
|
|
605
|
+
expect(result).toBeUndefined();
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
});
|