Skip to content

Commit b0297d2

Browse files
Add test (#4721)
* Add unit tests for Markdown utility functions and CodeClassNameEnum. (#4716) Co-authored-by: gru-agent[bot] <185149714+gru-agent[bot]@users.noreply.github.com> * Add unit tests for authChatCrud and authCollectionInChat functions in chat service. (#4718) Co-authored-by: gru-agent[bot] <185149714+gru-agent[bot]@users.noreply.github.com> --------- Co-authored-by: gru-agent[bot] <185149714+gru-agent[bot]@users.noreply.github.com>
1 parent 5023da4 commit b0297d2

File tree

3 files changed

+315
-0
lines changed

3 files changed

+315
-0
lines changed

projects/app/src/service/support/permission/auth/chat.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,5 @@ export const authCollectionInChat = async ({
244244
} catch (error) {}
245245
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
246246
};
247+
248+
export { defaultResponseShow };
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { mdTextFormat, CodeClassNameEnum } from '@/components/Markdown/utils';
3+
4+
describe('Markdown utils', () => {
5+
describe('mdTextFormat', () => {
6+
it('should format latex expressions correctly', () => {
7+
const input = 'Here is some math: \\[x^2 + y^2 = z^2\\] and inline \\(a+b=c\\)';
8+
const expected = 'Here is some math: $$x^2 + y^2 = z^2$$ and inline $a+b=c$';
9+
expect(mdTextFormat(input)).toBe(expected);
10+
});
11+
12+
it('should not format latex expressions inside code blocks', () => {
13+
const input = '```math\n\\[x^2\\]\n```\n`\\[y^2\\]`';
14+
expect(mdTextFormat(input)).toBe(input);
15+
});
16+
17+
it('should convert quote references to proper markdown links', () => {
18+
const input = '[123456789012345678901234]';
19+
const expected = '[123456789012345678901234](QUOTE)';
20+
expect(mdTextFormat(input)).toBe(expected);
21+
});
22+
23+
it('should not convert invalid quote references', () => {
24+
const input = '[12345] [abcdef] [123456789012345678901234](test)';
25+
expect(mdTextFormat(input)).toBe(input);
26+
});
27+
28+
it('should add spaces between URLs and Chinese punctuation', () => {
29+
const input = 'Check https://example.com,here。';
30+
const expected = 'Check https://example.com ,here。';
31+
expect(mdTextFormat(input)).toBe(expected);
32+
});
33+
34+
it('should handle complex text with multiple patterns', () => {
35+
const input =
36+
'Math \\[x^2\\] with link https://test.com,and quote [123456789012345678901234]';
37+
const expected =
38+
'Math $$x^2$$ with link https://test.com ,and quote [123456789012345678901234](QUOTE)';
39+
expect(mdTextFormat(input)).toBe(expected);
40+
});
41+
});
42+
43+
describe('CodeClassNameEnum', () => {
44+
it('should have correct enum values', () => {
45+
expect(CodeClassNameEnum.guide).toBe('guide');
46+
expect(CodeClassNameEnum.questionguide).toBe('questionguide');
47+
expect(CodeClassNameEnum.mermaid).toBe('mermaid');
48+
expect(CodeClassNameEnum.echarts).toBe('echarts');
49+
expect(CodeClassNameEnum.quote).toBe('quote');
50+
expect(CodeClassNameEnum.files).toBe('files');
51+
expect(CodeClassNameEnum.latex).toBe('latex');
52+
expect(CodeClassNameEnum.iframe).toBe('iframe');
53+
expect(CodeClassNameEnum.html).toBe('html');
54+
expect(CodeClassNameEnum.svg).toBe('svg');
55+
expect(CodeClassNameEnum.video).toBe('video');
56+
expect(CodeClassNameEnum.audio).toBe('audio');
57+
});
58+
});
59+
});
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
import { describe, expect, it, vi } from 'vitest';
2+
import { authChatCrud, authCollectionInChat } from '@/service/support/permission/auth/chat';
3+
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
4+
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
5+
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
6+
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
7+
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
8+
import { authApp } from '@fastgpt/service/support/permission/app/auth';
9+
import { authOutLink } from '@/service/support/permission/auth/outLink';
10+
11+
vi.mock('@fastgpt/service/core/chat/chatSchema', () => ({
12+
MongoChat: {
13+
findOne: vi.fn()
14+
}
15+
}));
16+
17+
vi.mock('@fastgpt/service/core/chat/chatItemSchema', () => ({
18+
MongoChatItem: {
19+
findOne: vi.fn()
20+
}
21+
}));
22+
23+
vi.mock('@fastgpt/service/support/permission/app/auth');
24+
vi.mock('@/service/support/permission/auth/outLink');
25+
26+
describe('authChatCrud', () => {
27+
it('should reject if no appId provided', async () => {
28+
await expect(authChatCrud({ appId: '' })).rejects.toBe(ChatErrEnum.unAuthChat);
29+
});
30+
31+
it('should auth outLink without chatId', async () => {
32+
vi.mocked(authOutLink).mockResolvedValue({
33+
outLinkConfig: {
34+
teamId: 'team1',
35+
tmbId: 'tmb1',
36+
responseDetail: true,
37+
showNodeStatus: true,
38+
showRawSource: true
39+
},
40+
uid: 'user1',
41+
appId: 'app1'
42+
});
43+
44+
const result = await authChatCrud({
45+
appId: 'app1',
46+
shareId: 'share1',
47+
outLinkUid: 'user1'
48+
});
49+
50+
expect(result).toMatchObject({
51+
teamId: 'team1',
52+
tmbId: 'tmb1',
53+
uid: 'user1',
54+
responseDetail: true,
55+
showNodeStatus: true,
56+
showRawSource: true,
57+
authType: AuthUserTypeEnum.outLink
58+
});
59+
});
60+
61+
it('should auth outLink with chatId', async () => {
62+
const mockChat = {
63+
appId: 'app1',
64+
outLinkUid: 'user1'
65+
};
66+
67+
vi.mocked(authOutLink).mockResolvedValue({
68+
outLinkConfig: {
69+
teamId: 'team1',
70+
tmbId: 'tmb1',
71+
responseDetail: true,
72+
showNodeStatus: true,
73+
showRawSource: true
74+
},
75+
uid: 'user1',
76+
appId: 'app1'
77+
});
78+
79+
vi.mocked(MongoChat.findOne).mockReturnValue({
80+
lean: () => mockChat
81+
} as any);
82+
83+
const result = await authChatCrud({
84+
appId: 'app1',
85+
chatId: 'chat1',
86+
shareId: 'share1',
87+
outLinkUid: 'user1'
88+
});
89+
90+
expect(result).toMatchObject({
91+
teamId: 'team1',
92+
tmbId: 'tmb1',
93+
uid: 'user1',
94+
chat: mockChat,
95+
responseDetail: true,
96+
showNodeStatus: true,
97+
showRawSource: true,
98+
authType: AuthUserTypeEnum.outLink
99+
});
100+
});
101+
102+
it('should reject if outLink appId does not match', async () => {
103+
vi.mocked(authOutLink).mockResolvedValue({
104+
outLinkConfig: {
105+
teamId: 'team1',
106+
tmbId: 'tmb1'
107+
},
108+
uid: 'user1',
109+
appId: 'different-app'
110+
});
111+
112+
await expect(
113+
authChatCrud({
114+
appId: 'app1',
115+
shareId: 'share1',
116+
outLinkUid: 'user1'
117+
})
118+
).rejects.toBe(ChatErrEnum.unAuthChat);
119+
});
120+
121+
it('should auth with cookie', async () => {
122+
vi.mocked(authApp).mockResolvedValue({
123+
teamId: 'team1',
124+
tmbId: 'tmb1',
125+
permission: {
126+
hasManagePer: true
127+
},
128+
authType: AuthUserTypeEnum.team
129+
});
130+
131+
const result = await authChatCrud({
132+
appId: 'app1',
133+
req: {} as any
134+
});
135+
136+
expect(result).toEqual({
137+
teamId: 'team1',
138+
tmbId: 'tmb1',
139+
uid: 'tmb1',
140+
responseDetail: true,
141+
showNodeStatus: true,
142+
showRawSource: true,
143+
authType: AuthUserTypeEnum.team
144+
});
145+
});
146+
});
147+
148+
describe('authCollectionInChat', () => {
149+
it('should reject if chat item not found', async () => {
150+
vi.mocked(MongoChatItem.findOne).mockReturnValue({
151+
lean: () => null
152+
} as any);
153+
154+
await expect(
155+
authCollectionInChat({
156+
collectionIds: ['col1'],
157+
appId: 'app1',
158+
chatId: 'chat1',
159+
chatItemDataId: 'item1'
160+
})
161+
).rejects.toBe(DatasetErrEnum.unAuthDatasetCollection);
162+
});
163+
164+
it('should auth collection ids in chat item', async () => {
165+
const mockChatItem = {
166+
time: new Date(),
167+
responseData: [
168+
{
169+
quoteList: [{ collectionId: 'col1' }, { collectionId: 'col2' }]
170+
}
171+
]
172+
};
173+
174+
vi.mocked(MongoChatItem.findOne).mockReturnValue({
175+
lean: () => mockChatItem
176+
} as any);
177+
178+
const result = await authCollectionInChat({
179+
collectionIds: ['col1', 'col2'],
180+
appId: 'app1',
181+
chatId: 'chat1',
182+
chatItemDataId: 'item1'
183+
});
184+
185+
expect(result).toEqual({
186+
chatItem: mockChatItem
187+
});
188+
});
189+
190+
it('should handle plugin, tool and loop details in response data', async () => {
191+
const mockChatItem = {
192+
time: new Date(),
193+
responseData: [
194+
{
195+
quoteList: [{ collectionId: 'col1' }],
196+
pluginDetail: [
197+
{
198+
quoteList: [{ collectionId: 'col2' }]
199+
}
200+
],
201+
toolDetail: [
202+
{
203+
quoteList: [{ collectionId: 'col3' }]
204+
}
205+
],
206+
loopDetail: [
207+
{
208+
quoteList: [{ collectionId: 'col4' }]
209+
}
210+
]
211+
}
212+
]
213+
};
214+
215+
vi.mocked(MongoChatItem.findOne).mockReturnValue({
216+
lean: () => mockChatItem
217+
} as any);
218+
219+
const result = await authCollectionInChat({
220+
collectionIds: ['col1', 'col2', 'col3', 'col4'],
221+
appId: 'app1',
222+
chatId: 'chat1',
223+
chatItemDataId: 'item1'
224+
});
225+
226+
expect(result).toEqual({
227+
chatItem: mockChatItem
228+
});
229+
});
230+
231+
it('should reject if collection ids not found in quotes', async () => {
232+
const mockChatItem = {
233+
time: new Date(),
234+
responseData: [
235+
{
236+
quoteList: [{ collectionId: 'col1' }]
237+
}
238+
]
239+
};
240+
241+
vi.mocked(MongoChatItem.findOne).mockReturnValue({
242+
lean: () => mockChatItem
243+
} as any);
244+
245+
await expect(
246+
authCollectionInChat({
247+
collectionIds: ['col2'],
248+
appId: 'app1',
249+
chatId: 'chat1',
250+
chatItemDataId: 'item1'
251+
})
252+
).rejects.toBe(DatasetErrEnum.unAuthDatasetFile);
253+
});
254+
});

0 commit comments

Comments
 (0)