feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,144 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { describe, expect, it } from 'vitest';
import { renderHook } from '@testing-library/react-hooks';
import { MockDataStatus, MockDataValueType } from '../../../src/util/typings';
import {
BranchType,
useGenTreeBranch,
} from '../../../src/hook/use-gen-tree-branch';
describe('useGenTreeBranch', () => {
it('should return an empty object when no mockData is provided', () => {
const { result } = renderHook(() => useGenTreeBranch());
expect(result.current.branchInfo).toEqual({});
expect(result.current.prunedData).toBeUndefined();
});
it('should prune the tree correctly and generate branchInfo', () => {
const mockData = {
label: 'label',
type: MockDataValueType.OBJECT,
status: MockDataStatus.DEFAULT,
key: 'root',
isRequired: false,
children: [
{
label: 'label',
isRequired: false,
type: MockDataValueType.ARRAY,
status: MockDataStatus.ADDED,
key: 'array1',
children: [
{
label: 'label',
isRequired: false,
type: MockDataValueType.STRING,
status: MockDataStatus.DEFAULT,
key: 'str1',
},
{
label: 'label',
type: MockDataValueType.OBJECT,
status: MockDataStatus.ADDED,
isRequired: false,
key: 'obj1',
children: [
{
label: 'label',
isRequired: false,
type: MockDataValueType.NUMBER,
status: MockDataStatus.DEFAULT,
key: 'num1',
},
],
},
],
},
{
label: 'label',
isRequired: false,
type: MockDataValueType.OBJECT,
status: MockDataStatus.DEFAULT,
key: 'obj2',
children: [
{
label: 'label',
isRequired: false,
type: MockDataValueType.BOOLEAN,
status: MockDataStatus.DEFAULT,
key: 'bool1',
},
],
},
],
};
const { result } = renderHook(() => useGenTreeBranch(mockData));
expect(result.current.prunedData).toEqual({
label: 'label',
type: 'object',
status: 'default',
key: 'root',
isRequired: false,
children: [
{
label: 'label',
isRequired: false,
type: 'array',
status: 'added',
key: 'array1',
children: undefined,
},
{
label: 'label',
isRequired: false,
type: 'object',
status: 'default',
key: 'obj2',
children: [
{
isRequired: false,
key: 'bool1',
label: 'label',
status: 'default',
type: 'boolean',
},
],
},
],
});
expect(result.current.branchInfo).toEqual({
array1: {
isLast: false,
v: [],
},
obj2: {
isLast: true,
v: [],
},
bool1: {
isLast: true,
v: [BranchType.HALF],
},
});
});
});

View File

@@ -0,0 +1,284 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { describe, expect, it, vi } from 'vitest';
import { renderHook, act } from '@testing-library/react-hooks';
import { SpaceType } from '@coze-arch/bot-api/developer_api';
import { useMockSetInSettingModalController } from '../../../src/hook/use-mock-set-in-setting-modal';
vi.stubGlobal('IS_PROD', false);
vi.mock('@coze-arch/bot-api', () => ({
debuggerApi: {
BindMockSet: vi.fn().mockResolvedValue(null),
GetMockSetUsageInfo: vi.fn().mockResolvedValue({ usersUsageCount: 0 }),
MGetMockSet: vi
.fn()
.mockResolvedValueOnce({ mockSets: [] })
.mockResolvedValue({
mockSets: [
{
createTimeInSec: '1717510112',
creator: {
ID: '1735475817351321',
avatarUrl:
'https://p3-passport.byteacctimg.com/obj/user-avatar/assets/e7b19241fb224cea967dfaea35448102_1080_1080.png',
name: 'minalwws8888',
},
description: '',
id: '7376649762918891521',
mockRuleQuantity: 1,
name: 'getNews mockset84',
schemaIncompatible: false,
updateTimeInSec: '1718180157',
},
{
createTimeInSec: '1718179289',
creator: {
ID: '1735475817351321',
avatarUrl:
'https://p3-passport.byteacctimg.com/obj/user-avatar/assets/e7b19241fb224cea967dfaea35448102_1080_1080.png',
name: 'minalwws8888',
},
description: 'getNews mockset76',
id: '7379523858379833345',
mockRuleQuantity: 1,
name: 'getNews mockset76',
schemaIncompatible: false,
updateTimeInSec: '1718179317',
},
{
createTimeInSec: '1717596210',
creator: {
ID: '1735475817351321',
avatarUrl:
'https://p3-passport.byteacctimg.com/obj/user-avatar/assets/e7b19241fb224cea967dfaea35448102_1080_1080.png',
name: 'minalwws8888',
},
description: '这是一个描述',
id: '7377019552296599553',
mockRuleQuantity: 1,
name: 'getNews mockset51',
schemaIncompatible: false,
updateTimeInSec: '1717596219',
},
{
createTimeInSec: '1717586096',
creator: {
ID: '1735475817351321',
avatarUrl:
'https://p3-passport.byteacctimg.com/obj/user-avatar/assets/e7b19241fb224cea967dfaea35448102_1080_1080.png',
name: 'minalwws8888',
},
description: '',
id: '7376976111470641153',
mockRuleQuantity: 1,
name: 'getNews mockset44',
schemaIncompatible: false,
updateTimeInSec: '1717586102',
},
{
createTimeInSec: '1717422094',
creator: {
ID: '1735475817351321',
avatarUrl:
'https://p3-passport.byteacctimg.com/obj/user-avatar/assets/e7b19241fb224cea967dfaea35448102_1080_1080.png',
name: 'minalwws8888',
},
description: '',
id: '7376271731066929153',
mockRuleQuantity: 8,
name: 'getNews mockset74',
schemaIncompatible: false,
updateTimeInSec: '1717448382',
},
{
createTimeInSec: '1717424229',
creator: {
ID: '1735475817351321',
avatarUrl:
'https://p3-passport.byteacctimg.com/obj/user-avatar/assets/e7b19241fb224cea967dfaea35448102_1080_1080.png',
name: 'minalwws8888',
},
description: '',
id: '7376280897617657858',
mockRuleQuantity: 3,
name: 'getNews mockset80',
schemaIncompatible: false,
updateTimeInSec: '1717424242',
},
{
createTimeInSec: '1717423923',
creator: {
ID: '1735475817351321',
avatarUrl:
'https://p3-passport.byteacctimg.com/obj/user-avatar/assets/e7b19241fb224cea967dfaea35448102_1080_1080.png',
name: 'minalwws8888',
},
description: '',
id: '7376279584485933058',
mockRuleQuantity: 1,
name: 'getNews mockset93',
schemaIncompatible: false,
updateTimeInSec: '1717423928',
},
],
}),
MGetMockSetBinding: vi
.fn()
.mockResolvedValueOnce({ mockSetBindings: [], mockSetDetails: {} })
.mockResolvedValue({
mockSetBindings: [
{
bizCtx: {
bizSpaceID: '7313780910216708140',
connectorID: '10000010',
connectorUID: '1735475817351321',
ext: {
mockSubjectInfo:
'{"componentID":"7373521805258047532","componentType":10001,"parentComponentID":"7373521805258014764","parentComponentType":10000}',
},
trafficCallerID: '7340867364105633836',
trafficScene: 10000,
},
mockSetID: '7376649762918891521',
mockSubject: {
componentID: '7373521805258047532',
componentType: 10001,
parentComponentID: '7373521805258014764',
parentComponentType: 10000,
},
},
],
mockSetDetails: {
'7376649762918891521': {
createTimeInSec: '1717510112',
creator: {
ID: '1735475817351321',
},
description: '',
id: '7376649762918891521',
mockRuleQuantity: 1,
mockSubject: {
componentID: '7373521805258047532',
componentType: 10001,
parentComponentID: '7373521805258014764',
parentComponentType: 10000,
},
name: 'getNews mockset84',
schemaIncompatible: false,
updateTimeInSec: '1718180157',
},
},
}),
DeleteMockSet: vi.fn().mockResolvedValue(null),
},
}));
vi.mock('@coze-arch/bot-studio-store', () => ({
useSpaceStore: vi.fn().mockReturnValue(SpaceType.Personal),
}));
vi.mock('@coze-studio/user-store', () => ({
userStoreService: {
useUserInfo: vi.fn().mockReturnValue({
user_id_str: 'test-user-id',
}),
},
}));
vi.mock('@/util', () => ({
getEnvironment: vi.fn(),
getMockSubjectInfo: vi.fn(),
getPluginInfo: vi.fn().mockReturnValue({
spaceID: 'spaceID',
toolID: 'toolID',
pluginID: 'pluginID',
}),
getUsedScene: vi.fn(),
isCurrent: vi.fn().mockReturnValue(true),
MockTrafficEnabled: {
DISABLE: 0,
ENABLE: 1,
},
}));
vi.mock('@coze-arch/logger', () => ({
logger: {
createLoggerWith: vi.fn(),
error: vi.fn(),
},
reporter: {
createReporterWithPreset: vi.fn(),
},
}));
const mockProps = {
bindSubjectInfo: {
componentID: 'test-subject-id',
componentType: 10001,
parentComponentID: 'parentComponentID',
parentComponentType: 10000,
},
bizCtx: {
connectorID: 'connectorID',
connectorUID: 'connectorUID',
trafficScene: 10000,
trafficCallerID: 'trafficCallerID',
bizSpaceID: 'bizSpaceID',
},
};
describe('useMockSetInSettingModalController', () => {
it('should initialize correctly', async () => {
const { result, waitForNextUpdate } = renderHook(() =>
useMockSetInSettingModalController(mockProps),
);
await waitForNextUpdate();
expect(result.current.isEnabled).toBeFalsy(); // 初始状态应该是不启用
expect(result.current.mockSetData).toEqual([]); // 初始mockSetData应该是空数组
});
it('should act export action', async () => {
const { result, waitForNextUpdate } = renderHook(() =>
useMockSetInSettingModalController(mockProps),
);
await waitForNextUpdate();
expect(result.current.isEnabled).toBeTruthy(); // 有选中的mock数据的前提下会自动启用
expect(result.current.mockSetData.length).toEqual(7); // mockSetData 数据是 7
act(() => result.current.doSetCreateModal(true));
expect(result.current.showCreateModal).toBeTruthy(); // 打开创建modal
act(() => result.current.doHandleView({ id: 'record-id' })); // 点击查看
act(() => result.current.doEnabled()); // 关闭
expect(result.current.isEnabled).toBeFalsy(); // 禁用
act(() => result.current.doSetDeleteId('record-id')); // 删除
expect(result.current.deleteRenderTitle).toBe(
'Translated: delete_the_mockset {}',
); // 删除后应该显示删除的title
act(() => result.current.doConfirmDelete()); // 确认删除
act(() => result.current.doChangeMock({ id: 'change-mock' })); // 修改mock
expect(result.current.selectedMockSet).toStrictEqual({ id: 'change-mock' }); // 修改mock后应该显示新的mock
});
});

View File

@@ -0,0 +1,116 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { describe, expect, it, vi } from 'vitest';
import { renderHook, act } from '@testing-library/react-hooks';
import { useSaveMockData } from '../../../src/hook/use-save-mock-data';
vi.mock('@coze-arch/coze-design', () => import('@coze-arch/bot-semi'));
const mockBizCtx = {};
vi.mock('@coze-arch/bot-api', () => ({
debuggerApi: {
ResponseExpectType: {
Undefined: 0,
JSON: 1,
},
MockRule: {
id: 'id',
},
BizCtx: {
connectorID: 'connectorID',
},
SaveMockRule: vi
.fn()
.mockResolvedValueOnce({
id: '123',
status: 'success',
})
.mockResolvedValueOnce({
id: '123',
status: 'success',
})
.mockRejectedValueOnce({
error: { message: 'Failed to save mock data' },
})
.mockRejectedValueOnce({
error: { message: 'Failed to save mock data' },
}),
},
}));
describe('useSaveMockData', () => {
it('should call onSuccess when all rules are saved successfully', async () => {
const onSuccess = vi.fn();
const onError = vi.fn();
const mockSetId = 'mockSetId';
const mockRules = ['rule1', 'rule2'];
const { result } = renderHook(() =>
useSaveMockData({
mockSetId,
basicParams: {
environment: 'environment',
workspace_id: 'workspace_id',
workspace_type: 'personal_workspace',
tool_id: 'tool_id',
mock_set_id: 'mock_set_id',
},
bizCtx: mockBizCtx,
onSuccess,
onError,
}),
);
await act(() => {
result.current.save(mockRules);
});
expect(onSuccess).toHaveBeenCalledTimes(1);
expect(onError).not.toHaveBeenCalled();
});
it('should call onError and display a toast when all rules fail to save', async () => {
const onSuccess = vi.fn();
const onError = vi.fn();
const mockRules = ['rule1', 'rule2'];
const { result } = renderHook(() =>
useSaveMockData({
mockSetId: undefined, // 测试没有mockSetId的情况
basicParams: {
environment: 'environment',
workspace_id: 'workspace_id',
workspace_type: 'personal_workspace',
tool_id: 'tool_id',
mock_set_id: 'mock_set_id',
},
bizCtx: mockBizCtx,
onSuccess,
onError,
}),
);
await act(() => {
result.current.save(mockRules);
});
expect(onSuccess).not.toHaveBeenCalled();
expect(onError).toHaveBeenCalledTimes(1);
});
});

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { renderHook } from '@testing-library/react-hooks';
import {
type BizCtx,
ComponentType,
TrafficScene,
} from '@coze-arch/bot-api/debugger_api';
import { type BasicMockSetInfo } from '../../src/component/interface';
import { useInitialGetEnabledMockSet } from '../../src/component/hooks/use-get-mockset';
vi.mock('@coze-arch/bot-utils', () => ({
safeJSONParse: JSON.parse,
}));
vi.mock('../const', () => ({
IS_OVERSEA: true,
REAL_DATA_MOCKSET: {
id: '0',
},
}));
vi.mock('@coze-arch/logger', () => ({
logger: {
createLoggerWith: vi.fn(),
},
reporter: {
createReporterWithPreset: vi.fn(),
},
}));
vi.mock('@coze-arch/bot-api', () => ({
debuggerApi: {
MGetMockSetBinding: vi.fn().mockResolvedValue({
code: 0,
msg: '',
mockSetBindings: [
{
mockSetID: '1',
mockSubject: {
componentID: 'tool1',
componentType: ComponentType.CozeTool,
parentComponentID: 'plugin1',
parentComponentType: ComponentType.CozePlugin,
},
bizCtx: {
bizSpaceID: '1',
trafficCallerID: '1',
trafficScene: TrafficScene.CozeSingleAgentDebug,
connectorID: '1',
connectorUID: '2',
},
},
],
mockSetDetails: {
'1': {
id: '1',
name: 'test-detail',
mockSubject: {
componentID: 'tool1',
componentType: ComponentType.CozeTool,
parentComponentID: 'plugin1',
parentComponentType: ComponentType.CozePlugin,
},
},
},
}),
},
}));
describe('mock-set-hooks', () => {
it('fetch-mock-list', async () => {
const TEST_COMMON_BIZ = {
connectorID: '1',
connectorUID: '2',
};
const TEST_BIZ_CTX1: BizCtx = {
bizSpaceID: '1',
trafficCallerID: '1',
trafficScene: TrafficScene.CozeSingleAgentDebug,
};
const TEST_MOCK_SUBJECT = {
componentID: 'tool1',
componentType: ComponentType.CozeTool,
parentComponentID: 'plugin1',
parentComponentType: ComponentType.CozePlugin,
};
const singleAgentToolItem: BasicMockSetInfo = {
bindSubjectInfo: TEST_MOCK_SUBJECT,
bizCtx: {
...TEST_COMMON_BIZ,
...TEST_BIZ_CTX1,
},
};
const { result } = renderHook(() =>
useInitialGetEnabledMockSet({
bizCtx: singleAgentToolItem.bizCtx,
pollingInterval: 0,
}),
);
const { start } = result.current;
await start();
const { data } = result.current;
expect(data[0]?.mockSetBinding.mockSetID).toEqual('1');
expect(data[0]?.mockSetDetail?.id).toEqual('1');
});
});

View File

@@ -0,0 +1,335 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
type BizCtx,
ComponentType,
TrafficScene,
} from '@coze-arch/bot-api/debugger_api';
import {
getPluginInfo,
getMockSubjectInfo,
type BasicMockSetInfo,
} from '@coze-studio/mockset-shared';
import {
isRealData,
isCurrent,
isSameWorkflowTool,
isSameScene,
getUsedScene,
} from '../../src/util';
vi.mock('@coze-arch/bot-utils', () => ({
safeJSONParse: JSON.parse,
}));
vi.mock('../const', () => ({
REAL_DATA_MOCKSET: {
id: '0',
},
}));
vi.mock('@coze-arch/logger', () => ({
logger: {
createLoggerWith: vi.fn(),
error: vi.fn(),
},
reporter: {
createReporterWithPreset: vi.fn(),
},
}));
const TEST_COMMON_BIZ = {
connectorID: '1',
connectorUID: '2',
};
const TEST_BIZ_CTX1: BizCtx = {
bizSpaceID: '1',
trafficCallerID: '1',
trafficScene: TrafficScene.CozeSingleAgentDebug,
};
const TEST_BIZ_CTX2: BizCtx = {
bizSpaceID: '1',
trafficCallerID: '2',
trafficScene: TrafficScene.CozeSingleAgentDebug,
};
const TEST_BIZ_CTX3: BizCtx = {
bizSpaceID: '1',
trafficCallerID: '1',
trafficScene: TrafficScene.CozeWorkflowDebug,
};
const TEST_BIZ_CTX4: BizCtx = {
bizSpaceID: '2',
trafficCallerID: '1',
trafficScene: TrafficScene.CozeWorkflowDebug,
};
const TEST_MOCK_SUBJECT1 = {
componentID: 'tool1',
componentType: ComponentType.CozeTool,
parentComponentID: 'plugin1',
parentComponentType: ComponentType.CozePlugin,
};
const TEST_MOCK_SUBJECT2 = {
componentID: 'tool2',
componentType: ComponentType.CozeTool,
parentComponentID: 'plugin1',
parentComponentType: ComponentType.CozePlugin,
};
const TEST_MOCK_WORKFLOW_NODE1 = {
componentID: 'node1',
componentType: ComponentType.CozeToolNode,
parentComponentID: 'workflow1',
parentComponentType: ComponentType.CozeWorkflow,
};
const TEST_MOCK_WORKFLOW_NODE2 = {
componentID: 'node2',
componentType: ComponentType.CozeToolNode,
parentComponentID: 'workflow1',
parentComponentType: ComponentType.CozeWorkflow,
};
const singleAgentToolItem1: BasicMockSetInfo = {
bindSubjectInfo: TEST_MOCK_SUBJECT1,
bizCtx: {
...TEST_COMMON_BIZ,
...TEST_BIZ_CTX1,
},
};
const singleAgentToolItem2: BasicMockSetInfo = {
bindSubjectInfo: TEST_MOCK_SUBJECT2,
bizCtx: {
...TEST_COMMON_BIZ,
...TEST_BIZ_CTX2,
},
};
const multiAgentToolItem: BasicMockSetInfo = {
bindSubjectInfo: TEST_MOCK_SUBJECT1,
bizCtx: {
...TEST_COMMON_BIZ,
...TEST_BIZ_CTX1,
trafficScene: TrafficScene.CozeMultiAgentDebug,
},
};
const workflowToolItem1: BasicMockSetInfo = {
bindSubjectInfo: TEST_MOCK_WORKFLOW_NODE1,
bizCtx: {
...TEST_COMMON_BIZ,
...TEST_BIZ_CTX3,
ext: { mockSubjectInfo: JSON.stringify(TEST_MOCK_SUBJECT1) },
},
};
const workflowToolItem2: BasicMockSetInfo = {
bindSubjectInfo: TEST_MOCK_WORKFLOW_NODE1,
bizCtx: {
...TEST_COMMON_BIZ,
...TEST_BIZ_CTX3,
ext: { mockSubjectInfo: JSON.stringify(TEST_MOCK_SUBJECT2) },
},
};
const workflowToolItem3: BasicMockSetInfo = {
bindSubjectInfo: TEST_MOCK_WORKFLOW_NODE2,
bizCtx: {
...TEST_COMMON_BIZ,
...TEST_BIZ_CTX3,
ext: { mockSubjectInfo: JSON.stringify(TEST_MOCK_SUBJECT1) },
},
};
describe('mock-set-utils-isRealData', () => {
it('real data', () => {
const res = isRealData({ id: '0' });
expect(res).toEqual(true);
});
it('not real data', () => {
const res = isRealData({ id: '123' });
expect(res).toEqual(false);
});
});
describe('mock-set-utils-isCurrent', () => {
it('same single agent mock tool', () => {
const res = isCurrent(singleAgentToolItem1, singleAgentToolItem1);
expect(res).toEqual(true);
});
it('same agent different mock tool', () => {
const res = isCurrent(singleAgentToolItem1, singleAgentToolItem2);
expect(res).toEqual(false);
});
it('different single multi agent mock tool', () => {
const res = isCurrent(singleAgentToolItem1, multiAgentToolItem);
expect(res).toEqual(false);
});
it('different single agent workflow mock tool', () => {
const res = isCurrent(singleAgentToolItem1, workflowToolItem1);
expect(res).toEqual(false);
});
it('same workflow mock tool', () => {
const res = isCurrent(workflowToolItem1, workflowToolItem1);
expect(res).toEqual(true);
});
it('different workflow mock tool', () => {
const res = isCurrent(workflowToolItem1, workflowToolItem2);
expect(res).toEqual(false);
});
it('different workflow mock tool', () => {
const res = isCurrent(workflowToolItem1, workflowToolItem2);
expect(res).toEqual(false);
});
it('different workflow mock node', () => {
const res = isCurrent(workflowToolItem1, workflowToolItem3);
expect(res).toEqual(false);
});
});
describe('mock-set-utils-isSameWorkflowTool', () => {
it('workflow same mock subject', () => {
const res = isSameWorkflowTool(
workflowToolItem1.bizCtx.ext?.mockSubjectInfo || '',
workflowToolItem1.bizCtx.ext?.mockSubjectInfo || '',
);
expect(res).toEqual(true);
});
it('different mock subject', () => {
const res = isSameWorkflowTool(
workflowToolItem1.bizCtx.ext?.mockSubjectInfo || '',
workflowToolItem2.bizCtx.ext?.mockSubjectInfo || '',
);
expect(res).toEqual(false);
});
});
describe('mock-set-utils-isSameScene', () => {
it('isSameScene', () => {
const res = isSameScene(TEST_BIZ_CTX1, TEST_BIZ_CTX1);
expect(res).toEqual(true);
});
it('different caller id', () => {
const res = isSameScene(TEST_BIZ_CTX1, TEST_BIZ_CTX2);
expect(res).toEqual(false);
});
it('different used scene', () => {
const res = isSameScene(TEST_BIZ_CTX1, TEST_BIZ_CTX3);
expect(res).toEqual(false);
});
it('different space id', () => {
const res = isSameScene(TEST_BIZ_CTX1, TEST_BIZ_CTX4);
expect(res).toEqual(false);
});
});
describe('mock-set-utils-getPluginInfo', () => {
it('get agent tool info', () => {
const res = getPluginInfo(TEST_BIZ_CTX1, TEST_MOCK_SUBJECT1);
expect(res.pluginID).toEqual(TEST_MOCK_SUBJECT1.parentComponentID);
expect(res.toolID).toEqual(TEST_MOCK_SUBJECT1.componentID);
expect(res.spaceID).toEqual(TEST_BIZ_CTX1.bizSpaceID);
});
it('get workflow tool info', () => {
const res = getPluginInfo(
workflowToolItem1.bizCtx,
workflowToolItem1.bindSubjectInfo,
);
expect(res.pluginID).toEqual(TEST_MOCK_SUBJECT1.parentComponentID);
expect(res.toolID).toEqual(TEST_MOCK_SUBJECT1.componentID);
expect(res.spaceID).toEqual(TEST_BIZ_CTX3.bizSpaceID);
});
});
describe('mock-set-utils-getMockSubjectInfo', () => {
it('get workflow mock subject info', () => {
const res = getMockSubjectInfo(
workflowToolItem1.bizCtx,
workflowToolItem1.bindSubjectInfo,
);
expect(res.componentID).toEqual(TEST_MOCK_SUBJECT1.componentID);
expect(res.componentType).toEqual(TEST_MOCK_SUBJECT1.componentType);
expect(res.parentComponentID).toEqual(TEST_MOCK_SUBJECT1.parentComponentID);
expect(res.parentComponentType).toEqual(
TEST_MOCK_SUBJECT1.parentComponentType,
);
});
it('get agent subject info', () => {
const res = getMockSubjectInfo(
singleAgentToolItem1.bizCtx,
singleAgentToolItem1.bindSubjectInfo,
);
expect(res.componentID).toEqual(TEST_MOCK_SUBJECT1.componentID);
expect(res.componentType).toEqual(TEST_MOCK_SUBJECT1.componentType);
expect(res.parentComponentID).toEqual(TEST_MOCK_SUBJECT1.parentComponentID);
expect(res.parentComponentType).toEqual(
TEST_MOCK_SUBJECT1.parentComponentType,
);
});
});
describe('mock-set-utils-getMockSubjectInfo', () => {
it('get single agent', () => {
const res = getUsedScene(TrafficScene.CozeSingleAgentDebug);
expect(res).toEqual('bot');
});
it('get multi agent', () => {
const res = getUsedScene(TrafficScene.CozeMultiAgentDebug);
expect(res).toEqual('agent');
});
it('get workflow', () => {
const res = getUsedScene(TrafficScene.CozeWorkflowDebug);
expect(res).toEqual('flow');
});
});

View File

@@ -0,0 +1,283 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { isEqual } from 'lodash-es';
import { type JSONSchema7 } from 'json-schema';
import {
ROOT_KEY,
getArrayItemKey,
transDataWithStatus2Object,
transSchema2DataWithStatus,
} from '@coze-studio/mockset-shared';
import {
getMergedDataWithStatus,
mergeDataWithStatus,
safeJSONParse,
transMockData2DataWithStatus,
} from '../src/util/utils';
import {
MockDataStatus,
type MockDataValueType,
type MockDataWithStatus,
} from '../src/util/typings';
const testSchema: JSONSchema7 = {
$schema: 'https://json-schema.org/draft-07/schema',
required: ['num', 'str', 'bool'],
properties: {
bool: {
additionalProperties: false,
type: ['boolean'],
},
int: {
additionalProperties: false,
type: ['integer'],
},
num: {
additionalProperties: false,
type: ['number'],
},
str: {
additionalProperties: false,
type: ['string'],
},
},
additionalProperties: false,
type: ['object'],
};
const testMockA: MockDataWithStatus = {
children: [
{
description: undefined,
displayValue: 'false',
isRequired: true,
key: 'mock-bool',
label: 'bool',
status: 'added' as MockDataStatus,
type: 'boolean' as MockDataValueType,
realValue: false,
},
{
description: undefined,
displayValue: '0',
isRequired: false,
key: 'mock-int',
label: 'int',
status: 'added' as MockDataStatus,
type: 'integer' as MockDataValueType,
realValue: 0,
},
{
description: undefined,
displayValue: '0',
isRequired: true,
key: 'mock-num',
label: 'num',
status: 'added' as MockDataStatus,
type: 'number' as MockDataValueType,
realValue: 0,
},
{
description: undefined,
displayValue: '""',
isRequired: true,
key: 'mock-str',
label: 'str',
status: 'added' as MockDataStatus,
type: 'string' as MockDataValueType,
realValue: '',
},
],
description: undefined,
displayValue: undefined,
isRequired: false,
key: 'mock',
label: 'mock',
status: 'added' as MockDataStatus,
type: 'object' as MockDataValueType,
realValue: undefined,
};
const testMockAObj = { mock: { bool: false, int: 0, num: 0, str: '' } };
const testMockBStr = '{"bool":true,"num":2,"str":"hello","extra":"extra"}';
const testMockB: MockDataWithStatus = {
key: 'mock',
label: 'mock',
realValue: undefined,
displayValue: undefined,
isRequired: false,
type: 'object' as MockDataValueType,
status: 'removed' as MockDataStatus,
children: [
{
key: 'mock-bool',
label: 'bool',
realValue: true,
displayValue: 'true',
isRequired: false,
type: 'boolean' as MockDataValueType,
status: 'removed' as MockDataStatus,
},
{
key: 'mock-num',
label: 'num',
realValue: 2,
displayValue: '2',
isRequired: false,
type: 'number' as MockDataValueType,
status: 'removed' as MockDataStatus,
},
{
key: 'mock-str',
label: 'str',
realValue: 'hello',
displayValue: '"hello"',
isRequired: false,
type: 'string' as MockDataValueType,
status: 'removed' as MockDataStatus,
},
{
key: 'mock-extra',
label: 'extra',
realValue: 'extra',
displayValue: '"extra"',
isRequired: false,
type: 'string' as MockDataValueType,
status: 'removed' as MockDataStatus,
},
],
};
const testMergedMockData: MockDataWithStatus = {
key: 'mock',
label: 'mock',
description: undefined,
realValue: undefined,
displayValue: undefined,
isRequired: false,
type: 'object' as MockDataValueType,
status: 'added' as MockDataStatus,
children: [
{
description: undefined,
displayValue: 'true',
isRequired: true,
key: 'mock-bool',
label: 'bool',
status: 'default' as MockDataStatus,
type: 'boolean' as MockDataValueType,
realValue: true,
},
{
description: undefined,
displayValue: '0',
isRequired: false,
key: 'mock-int',
label: 'int',
status: 'added' as MockDataStatus,
type: 'integer' as MockDataValueType,
realValue: 0,
},
{
description: undefined,
displayValue: '2',
isRequired: true,
key: 'mock-num',
label: 'num',
status: 'default' as MockDataStatus,
type: 'number' as MockDataValueType,
realValue: 2,
},
{
description: undefined,
displayValue: '"hello"',
isRequired: true,
key: 'mock-str',
label: 'str',
status: 'default' as MockDataStatus,
type: 'string' as MockDataValueType,
realValue: 'hello',
},
{
key: 'mock-extra',
label: 'extra',
realValue: 'extra',
displayValue: '"extra"',
isRequired: false,
type: 'string' as MockDataValueType,
status: 'removed' as MockDataStatus,
},
],
};
describe('plugin-mock-data-utils', () => {
it('getArrayItemKey', () => {
const key = getArrayItemKey(1);
expect(key).toEqual('item_1');
});
it('transSchema2DataWithStatus', () => {
const data = transSchema2DataWithStatus(ROOT_KEY, testSchema);
const compareResult = isEqual(data, testMockA);
expect(compareResult).toEqual(true);
});
it('transDataWithStatus2Object', () => {
const data = transDataWithStatus2Object(testMockA);
const compareResult = isEqual(data, testMockAObj);
expect(compareResult).toEqual(true);
});
it('transMockData2DataWithStatus', () => {
const obj = safeJSONParse(testMockBStr);
const parsedMockData = transMockData2DataWithStatus(ROOT_KEY, obj, {
defaultStatus: MockDataStatus.REMOVED,
});
const compareResult = isEqual(parsedMockData, testMockB);
expect(compareResult).toEqual(true);
});
it('mergeDataWithStatus', () => {
const { merged: mergedMockData, incompatible } = mergeDataWithStatus(
testMockA.children,
testMockB.children,
);
const compareResult = isEqual(mergedMockData, testMergedMockData.children);
expect(compareResult).toEqual(true);
expect(incompatible).toEqual(true);
});
it('getMergedDataWithStatus', () => {
const { result: mergedMockData, incompatible } = getMergedDataWithStatus(
testMockA,
testMockBStr,
);
const compareResult = isEqual(mergedMockData, testMergedMockData);
expect(compareResult).toEqual(true);
expect(incompatible).toEqual(true);
});
});

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { vi } from 'vitest';
vi.stubGlobal('IS_DEV_MODE', false);
vi.mock('@coze-arch/logger', () => ({
useErrorHandler: vi.fn().mockReturnValue(vi.fn()),
logger: {
error: vi.fn(),
info: vi.fn(),
},
reporter: {
tracer: vi.fn().mockReturnValue(vi.fn()),
event: vi.fn(),
errorEvent: vi.fn(),
},
}));
vi.mock('@coze-studio/bot-plugin-store', () => ({
usePluginStore: vi.fn().mockReturnValue({
pluginInfo: {
plugin_id: 'plugin_id',
canEdit: !0,
},
}),
}));
vi.mock('@coze-arch/i18n', () => ({
I18n: {
t: (key, params = {}) => `Translated: ${key} ${JSON.stringify(params)}`,
},
}));
vi.mock('@coze-arch/bot-tea', () => ({
sendTeaEvent: vi.fn(),
EVENT_NAMES: {
use_mockset_front: 'use_mockset_front',
del_mockset_front: 'del_mockset_front',
},
ParamsTypeDefine: {},
PluginMockDataGenerateMode: {
MANUAL: 0, // 手动创建
RANDOM: 1, // 随机生成
LLM: 2,
},
}));
vi.mock('@coze-arch/bot-hooks', () => ({
SceneType: {
BOT__VIEW__WORKFLOW: 'botViewWorkflow',
/** bot 详情页查看 workflow或新建 workflow 但未发布,点击返回 */
WORKFLOW__BACK__BOT: 'workflowBackBot',
/** bot 详情页创建 workflow在 workflow 发布后返回 */
WORKFLOW_PUBLISHED__BACK__BOT: 'workflowPublishedBackBot',
/** bot 详情页进入 mock data 页面 */
BOT__TO__PLUGIN_MOCK_DATA: 'botToPluginMockData',
/** workflow 详情页进入 mock data 页面 */
WORKFLOW__TO__PLUGIN_MOCK_DATA: 'workflowToPluginMockData',
/** mock set 页进入 mock data 页面 */
PLUGIN_MOCK_SET__TO__PLUGIN_MOCK_DATA: 'pluginMockSetToPluginMockData',
/** bot 详情页进入 knowledge 页面 */
BOT__VIEW__KNOWLEDGE: 'botViewKnowledge',
/** knowledge 页面点击退出返回 bot 详情页(未点击添加) */
KNOWLEDGE__BACK__BOT: 'knowledgeBackBot',
/** knowledge 页面点击返回 bot 详情页,并添加到 bot */
KNOWLEDGE__ADD_TO__BOT: 'knowledgeAddToBot',
},
usePageJumpService: vi.fn().mockReturnValue({
jump: vi.fn(),
}),
}));
vi.mock('@coze-arch/bot-studio-store', () => ({
useSpaceStore: {
getState: vi.fn().mockReturnValue({
getSpaceId: vi.fn().mockReturnValue('spaceId'),
}),
},
}));
vi.mock('@coze-arch/report-events', () => ({
REPORT_EVENTS: {
pluginIdeInitTrace: 'pluginIdeInitTrace',
pluginIdeInit: 'pluginIdeInit',
},
}));
vi.mock('@coze-arch/bot-error', () => ({
CustomError: vi.fn(),
}));
vi.mock('react-router-dom', () => ({
useNavigate: vi.fn().mockReturnValue(vi.fn()),
useParams: vi.fn().mockReturnValue({
space_id: 'space_id',
plugin_id: 'plugin_id',
}),
}));

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { renderHook } from '@testing-library/react-hooks';
import { useTransSchema } from '../src/hook/use-trans-schema';
vi.mock('@coze-arch/logger', () => ({
logger: {
createLoggerWith: vi.fn(),
},
}));
vi.mock('@coze-arch/bot-utils', () => ({
safeJSONParse: JSON.parse,
}));
describe('plugin-mock-data-hooks', () => {
it('useTransSchema - compatible case 1 ', () => {
const { result } = renderHook(() =>
useTransSchema(
'{"$schema":"https://json-schema.org/draft-07/schema","type":["object"],"additionalProperties":false}',
'{}',
),
);
const { incompatible } = result.current;
expect(incompatible).toEqual(false);
});
it('useTransSchema - compatible case 2', () => {
const { result } = renderHook(() =>
useTransSchema(
'{"$schema":"https://json-schema.org/draft-07/schema","required":["num","str","bool"],"properties":{"bool":{"additionalProperties":false,"type":["boolean"]},"int":{"additionalProperties":false,"type":["integer"]},"num":{"additionalProperties":false,"type":["number"]},"str":{"additionalProperties":false,"type":["string"]}},"additionalProperties":false,"type":["object"]}',
'{"int": 1,"num": 1.11,"str": "test","bool": true\n}',
),
);
const { incompatible } = result.current;
expect(incompatible).toEqual(false);
});
it('useTransSchema - testValueValid', () => {
const { result } = renderHook(() =>
useTransSchema(
'{"$schema":"https://json-schema.org/draft-07/schema","properties":{"response_for_model":{"additionalProperties":false,"type":"string"},"str":{"additionalProperties":false,"type":["string"]}},"additionalProperties":false,"type":["object"]}',
),
);
const { testValueValid } = result.current;
const testPass = testValueValid('{"response_for_model": "xxx"}');
const testFail1 = testValueValid(
'{"response_for_model": "", "str": "hello"}',
);
const testFail2 = testValueValid('{}');
expect(testPass).toEqual(true);
expect(testFail1).toEqual(false);
expect(testFail2).toEqual(false);
});
});

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { describe, expect, it, vi } from 'vitest';
import { getMockSetReqOptions } from '../../../src/util/get-mock-set-options';
vi.mock('@coze-arch/bot-flags', () => ({
getFlags: vi
.fn()
.mockReturnValueOnce({
'bot.devops.plugin_mockset': true,
})
.mockReturnValueOnce({
'bot.devops.plugin_mockset': false,
}),
}));
const baseBotInfo = {
mode: 0,
botId: 'testBotId',
botMarketStatus: 1,
space_id: 'testSpaceId',
};
describe('getMockSetReqOptions', () => {
it('should return mock headers when plugin_mockset flag is true', () => {
const result = getMockSetReqOptions(baseBotInfo);
expect(result).toEqual({
headers: {
'rpc-persist-mock-traffic-scene': 10000,
'rpc-persist-mock-traffic-caller-id': 'testBotId',
'rpc-persist-mock-space-id': 'testSpaceId',
'rpc-persist-mock-traffic-enable': 1,
},
});
});
it('should return empty object when plugin_mockset flag is false', () => {
const result = getMockSetReqOptions(baseBotInfo);
expect(result).toEqual({});
});
});