mirror of
https://github.com/labring/FastGPT.git
synced 2025-12-25 20:02:47 +00:00
test & openapi
This commit is contained in:
parent
c75294f56e
commit
2901ad548a
|
|
@ -1,6 +1,8 @@
|
|||
import type { OpenAPIPath } from '../../type';
|
||||
import { AppLogPath } from './log';
|
||||
import { PublishChannelPath } from './publishChannel';
|
||||
|
||||
export const AppPath: OpenAPIPath = {
|
||||
...AppLogPath
|
||||
...AppLogPath,
|
||||
...PublishChannelPath
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
import { PlaygroundPath } from './playground';
|
||||
|
||||
export const PublishChannelPath = {
|
||||
...PlaygroundPath
|
||||
};
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
import { z } from 'zod';
|
||||
import { ObjectIdSchema } from '../../../../../common/type/mongo';
|
||||
|
||||
// Get Playground Visibility Config Parameters
|
||||
export const GetPlaygroundVisibilityConfigParamsSchema = z.object({
|
||||
appId: ObjectIdSchema.meta({
|
||||
example: '68ad85a7463006c963799a05',
|
||||
description: '应用 ID'
|
||||
})
|
||||
});
|
||||
export type GetPlaygroundVisibilityConfigParamsType = z.infer<
|
||||
typeof GetPlaygroundVisibilityConfigParamsSchema
|
||||
>;
|
||||
|
||||
// Playground Visibility Config Response
|
||||
export const PlaygroundVisibilityConfigResponseSchema = z.object({
|
||||
showNodeStatus: z.boolean().meta({
|
||||
example: true,
|
||||
description: '是否显示节点状态'
|
||||
}),
|
||||
responseDetail: z.boolean().meta({
|
||||
example: true,
|
||||
description: '是否显示响应详情'
|
||||
}),
|
||||
showFullText: z.boolean().meta({
|
||||
example: true,
|
||||
description: '是否显示全文'
|
||||
}),
|
||||
showRawSource: z.boolean().meta({
|
||||
example: true,
|
||||
description: '是否显示原始来源'
|
||||
})
|
||||
});
|
||||
export type PlaygroundVisibilityConfigResponseType = z.infer<
|
||||
typeof PlaygroundVisibilityConfigResponseSchema
|
||||
>;
|
||||
|
||||
// Update Playground Visibility Config Parameters
|
||||
export const UpdatePlaygroundVisibilityConfigParamsSchema = z.object({
|
||||
appId: ObjectIdSchema.meta({
|
||||
example: '68ad85a7463006c963799a05',
|
||||
description: '应用 ID'
|
||||
}),
|
||||
showNodeStatus: z.boolean().meta({
|
||||
example: true,
|
||||
description: '是否显示节点状态'
|
||||
}),
|
||||
responseDetail: z.boolean().meta({
|
||||
example: true,
|
||||
description: '是否显示响应详情'
|
||||
}),
|
||||
showFullText: z.boolean().meta({
|
||||
example: true,
|
||||
description: '是否显示全文'
|
||||
}),
|
||||
showRawSource: z.boolean().meta({
|
||||
example: true,
|
||||
description: '是否显示原始来源'
|
||||
})
|
||||
});
|
||||
export type UpdatePlaygroundVisibilityConfigParamsType = z.infer<
|
||||
typeof UpdatePlaygroundVisibilityConfigParamsSchema
|
||||
>;
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
import { z } from 'zod';
|
||||
import type { OpenAPIPath } from '../../../../type';
|
||||
import {
|
||||
GetPlaygroundVisibilityConfigParamsSchema,
|
||||
PlaygroundVisibilityConfigResponseSchema,
|
||||
UpdatePlaygroundVisibilityConfigParamsSchema
|
||||
} from './api';
|
||||
import { TagsMap } from '../../../../tag';
|
||||
|
||||
export const PlaygroundPath: OpenAPIPath = {
|
||||
'/api/support/outLink/playground/config': {
|
||||
get: {
|
||||
summary: '获取门户配置',
|
||||
description:
|
||||
'获取指定应用的门户聊天界面的可见性配置,包括节点状态、响应详情、全文显示和原始来源显示的设置',
|
||||
tags: [TagsMap.publishChannel],
|
||||
requestParams: {
|
||||
query: GetPlaygroundVisibilityConfigParamsSchema
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功返回门户配置',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: PlaygroundVisibilityConfigResponseSchema
|
||||
}
|
||||
}
|
||||
},
|
||||
400: {
|
||||
description: '请求参数错误',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: z.object({
|
||||
code: z.literal(500),
|
||||
statusText: z.literal('Invalid Params'),
|
||||
message: z.string(),
|
||||
data: z.null()
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
401: {
|
||||
description: '用户未授权',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: z.object({
|
||||
code: z.literal(401),
|
||||
statusText: z.literal('unAuthorization'),
|
||||
message: z.string(),
|
||||
data: z.null()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/api/support/outLink/playground/update': {
|
||||
post: {
|
||||
summary: '更新门户配置',
|
||||
description:
|
||||
'更新指定应用的门户聊天界面的可见性配置,包括节点状态、响应详情、全文显示和原始来源显示的设置。如果配置不存在则创建新配置',
|
||||
tags: [TagsMap.publishChannel],
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: UpdatePlaygroundVisibilityConfigParamsSchema
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: '成功更新门户配置',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: z.null()
|
||||
}
|
||||
}
|
||||
},
|
||||
400: {
|
||||
description: '请求参数错误',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: z.object({
|
||||
code: z.literal(500),
|
||||
statusText: z.literal('Invalid Params'),
|
||||
message: z.string(),
|
||||
data: z.null()
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
401: {
|
||||
description: '用户未授权',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: z.object({
|
||||
code: z.literal(401),
|
||||
statusText: z.literal('unAuthorization'),
|
||||
message: z.string(),
|
||||
data: z.null()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -26,7 +26,7 @@ export const openAPIDocument = createDocument({
|
|||
'x-tagGroups': [
|
||||
{
|
||||
name: 'Agent 应用',
|
||||
tags: [TagsMap.appLog]
|
||||
tags: [TagsMap.appLog, TagsMap.publishChannel]
|
||||
},
|
||||
{
|
||||
name: '对话管理',
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ export const TagsMap = {
|
|||
pluginToolTag: '工具标签',
|
||||
pluginTeam: '团队插件管理',
|
||||
|
||||
// Publish Channel
|
||||
publishChannel: '发布渠道',
|
||||
|
||||
/* Support */
|
||||
// Wallet
|
||||
walletBill: '订单',
|
||||
|
|
|
|||
|
|
@ -108,16 +108,11 @@ export type OutLinkEditType<T = undefined> = {
|
|||
app?: T;
|
||||
};
|
||||
|
||||
export type PlaygroundVisibilityConfigType = {
|
||||
showNodeStatus: boolean;
|
||||
responseDetail: boolean;
|
||||
showFullText: boolean;
|
||||
showRawSource: boolean;
|
||||
};
|
||||
|
||||
export const PlaygroundVisibilityConfigSchema = z.object({
|
||||
showNodeStatus: z.boolean(),
|
||||
responseDetail: z.boolean(),
|
||||
showFullText: z.boolean(),
|
||||
showRawSource: z.boolean()
|
||||
});
|
||||
|
||||
export type PlaygroundVisibilityConfigType = z.infer<typeof PlaygroundVisibilityConfigSchema>;
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ const ChatItem = (props: Props) => {
|
|||
setCiteModalData({
|
||||
rawSearch: quoteList,
|
||||
metadata:
|
||||
item?.collectionId && (isShowReadRawSource || isShowFullText)
|
||||
item?.collectionId && isShowFullText
|
||||
? {
|
||||
appId: appId,
|
||||
chatId: chatId,
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ const Share = ({ appId }: { appId: string; type: PublishChannelEnum }) => {
|
|||
name: item.name,
|
||||
responseDetail: item.responseDetail ?? false,
|
||||
showRawSource: item.showRawSource ?? false,
|
||||
showFullText: item.showFullText ?? item.showRawSource ?? false,
|
||||
showFullText: item.showFullText ?? true,
|
||||
showNodeStatus: item.showNodeStatus ?? false,
|
||||
limit: item.limit
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,36 +5,47 @@ import type { S3MQJobData } from '@fastgpt/service/common/s3/mq';
|
|||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
|
||||
|
||||
export type ResponseType = {
|
||||
message: string;
|
||||
retriedCount: number;
|
||||
failedCount: number;
|
||||
shareLinkMigration: {
|
||||
totalRecords: number;
|
||||
updatedRecords: number;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 4.14.5 版本数据初始化脚本
|
||||
* 1. 重试所有失败的 S3 删除任务
|
||||
* 2. 为所有 share 类型的 OutLink 记录添加 showFullText 字段
|
||||
*/
|
||||
async function handler(
|
||||
req: ApiRequestProps,
|
||||
res: ApiResponseType<ResponseType>
|
||||
): Promise<ResponseType> {
|
||||
await authCert({ req, authRoot: true });
|
||||
const queue = getQueue<S3MQJobData>(QueueNames.s3FileDelete);
|
||||
|
||||
// Get all failed jobs and retry them
|
||||
// 1. 处理失败的 S3 删除任务
|
||||
const queue = getQueue<S3MQJobData>(QueueNames.s3FileDelete);
|
||||
const failedJobs = await queue.getFailed();
|
||||
console.log(`Found ${failedJobs.length} failed jobs`);
|
||||
console.log(`Found ${failedJobs.length} failed S3 delete jobs`);
|
||||
|
||||
let retriedCount = 0;
|
||||
|
||||
await batchRun(
|
||||
failedJobs,
|
||||
async (job) => {
|
||||
addLog.debug(`Retrying job with 3 new attempts`, { retriedCount });
|
||||
addLog.debug(`Retrying S3 delete job with new attempts`, { retriedCount });
|
||||
try {
|
||||
// Remove old job and recreate with new attempts
|
||||
const jobData = job.data;
|
||||
await job.remove();
|
||||
|
||||
// Add new job with 3 more attempts
|
||||
// Add new job with more attempts
|
||||
await queue.add('delete-s3-files', jobData, {
|
||||
attempts: 10,
|
||||
removeOnFail: {
|
||||
|
|
@ -49,18 +60,54 @@ async function handler(
|
|||
});
|
||||
|
||||
retriedCount++;
|
||||
console.log(`Retried job ${job.id} with 3 new attempts`);
|
||||
console.log(`Retried S3 delete job ${job.id} with new attempts`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to retry job ${job.id}:`, error);
|
||||
console.error(`Failed to retry S3 delete job ${job.id}:`, error);
|
||||
}
|
||||
},
|
||||
100
|
||||
);
|
||||
|
||||
// 2. 处理 share 类型的 OutLink 记录迁移
|
||||
let shareLinkMigration = { totalRecords: 0, updatedRecords: 0 };
|
||||
|
||||
try {
|
||||
// 查找所有 share 类型且没有 showFullText 字段的记录
|
||||
const shareLinks = await MongoOutLink.find({
|
||||
type: PublishChannelEnum.share,
|
||||
showFullText: { $exists: false }
|
||||
}).lean();
|
||||
|
||||
shareLinkMigration.totalRecords = shareLinks.length;
|
||||
|
||||
if (shareLinks.length > 0) {
|
||||
// 批量更新
|
||||
const bulkOps = shareLinks.map((link) => ({
|
||||
updateOne: {
|
||||
filter: { _id: link._id },
|
||||
update: { $set: { showFullText: link.showRawSource ?? true } }
|
||||
}
|
||||
}));
|
||||
|
||||
const result = await MongoOutLink.bulkWrite(bulkOps);
|
||||
shareLinkMigration.updatedRecords = result.modifiedCount;
|
||||
|
||||
console.log(
|
||||
`Migration completed: ${shareLinkMigration.updatedRecords}/${shareLinkMigration.totalRecords} share links updated`
|
||||
);
|
||||
} else {
|
||||
console.log('No share link records need migration');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to migrate share links:', error);
|
||||
// 即使迁移失败,也继续返回 S3 任务处理的结果
|
||||
}
|
||||
|
||||
return {
|
||||
message: 'Successfully retried all failed S3 delete jobs with 3 new attempts',
|
||||
message: `Completed S3 delete job retries and share link migration for v4.14.5`,
|
||||
retriedCount,
|
||||
failedCount: failedJobs.length
|
||||
failedCount: failedJobs.length,
|
||||
shareLinkMigration
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ async function handler(
|
|||
authCollectionInChat({ appId, chatId, chatItemDataId, collectionIds: [collectionId] })
|
||||
]);
|
||||
|
||||
if ((!showRawSource && !showFullText) || !chat || !chatItem || initialAnchor === undefined) {
|
||||
if (!showFullText || !chat || !chatItem || initialAnchor === undefined) {
|
||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||
import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
|
||||
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import type { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import {
|
||||
|
|
@ -14,6 +16,13 @@ async function handler(
|
|||
): Promise<PlaygroundVisibilityConfigResponse> {
|
||||
const { appId } = PlaygroundVisibilityConfigQuerySchema.parse(req.query);
|
||||
|
||||
await authApp({
|
||||
req,
|
||||
authToken: true,
|
||||
appId,
|
||||
per: WritePermissionVal
|
||||
});
|
||||
|
||||
const existingConfig = await MongoOutLink.findOne(
|
||||
{
|
||||
appId,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||
import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
|
||||
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import type { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import {
|
||||
|
|
@ -17,7 +17,7 @@ async function handler(req: ApiRequestProps<UpdatePlaygroundVisibilityConfigBody
|
|||
req,
|
||||
authToken: true,
|
||||
appId,
|
||||
per: ManagePermissionVal
|
||||
per: WritePermissionVal
|
||||
});
|
||||
|
||||
await MongoOutLink.updateOne(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,175 @@
|
|||
import type { PlaygroundVisibilityConfigResponse } from '@fastgpt/global/support/outLink/api.d';
|
||||
import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import { getRootUser } from '@test/datas/users';
|
||||
import { Call } from '@test/utils/request';
|
||||
import { describe, it, expect, beforeAll, afterEach, afterAll } from 'vitest';
|
||||
import * as configApi from '@/pages/api/support/outLink/playground/config';
|
||||
|
||||
describe('Playground Visibility Config API', () => {
|
||||
let rootUser: any;
|
||||
let testApp: any;
|
||||
|
||||
beforeAll(async () => {
|
||||
rootUser = await getRootUser();
|
||||
|
||||
// Create a test app owned by the root user
|
||||
testApp = await MongoApp.create({
|
||||
name: 'Test App for Playground Config',
|
||||
type: 'simple',
|
||||
tmbId: rootUser.tmbId,
|
||||
teamId: rootUser.teamId
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Clean up any created OutLink configs
|
||||
await MongoOutLink.deleteMany({
|
||||
appId: testApp._id,
|
||||
type: PublishChannelEnum.playground
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
// Clean up test data
|
||||
await MongoApp.deleteOne({ _id: testApp._id });
|
||||
});
|
||||
|
||||
it('should return default config values when no existing config found', async () => {
|
||||
const res = await Call<PlaygroundVisibilityConfigResponse>(configApi.default, {
|
||||
auth: rootUser,
|
||||
query: {
|
||||
appId: testApp._id
|
||||
}
|
||||
});
|
||||
|
||||
// Check if the request was processed successfully
|
||||
if (res.code === 200) {
|
||||
expect(res.error).toBeUndefined();
|
||||
expect(res.data).toEqual({
|
||||
showNodeStatus: true,
|
||||
responseDetail: true,
|
||||
showFullText: true,
|
||||
showRawSource: true
|
||||
});
|
||||
} else {
|
||||
// If there are permission issues, we still expect the API to validate parameters
|
||||
expect(res.code).toBe(500);
|
||||
expect(res.error).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('should return existing config values when config exists', async () => {
|
||||
// Create an existing config
|
||||
await MongoOutLink.create({
|
||||
shareId: `playground-${testApp._id}`,
|
||||
teamId: rootUser.teamId,
|
||||
tmbId: rootUser.tmbId,
|
||||
appId: testApp._id,
|
||||
name: 'Playground Chat',
|
||||
type: PublishChannelEnum.playground,
|
||||
showNodeStatus: false,
|
||||
responseDetail: false,
|
||||
showFullText: false,
|
||||
showRawSource: false,
|
||||
usagePoints: 0,
|
||||
lastTime: new Date()
|
||||
});
|
||||
|
||||
const res = await Call<PlaygroundVisibilityConfigResponse>(configApi.default, {
|
||||
auth: rootUser,
|
||||
query: {
|
||||
appId: testApp._id
|
||||
}
|
||||
});
|
||||
|
||||
// Check if the request was processed successfully
|
||||
if (res.code === 200) {
|
||||
expect(res.error).toBeUndefined();
|
||||
expect(res.data).toEqual({
|
||||
showNodeStatus: false,
|
||||
responseDetail: false,
|
||||
showFullText: false,
|
||||
showRawSource: false
|
||||
});
|
||||
} else {
|
||||
// If there are permission issues, we still expect the API to validate parameters
|
||||
expect(res.code).toBe(500);
|
||||
expect(res.error).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('should return 500 when appId is missing', async () => {
|
||||
const res = await Call<PlaygroundVisibilityConfigResponse>(configApi.default, {
|
||||
auth: rootUser,
|
||||
query: {}
|
||||
});
|
||||
|
||||
expect(res.code).toBe(500);
|
||||
expect(res.error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return 500 when appId is empty string', async () => {
|
||||
const res = await Call<PlaygroundVisibilityConfigResponse>(configApi.default, {
|
||||
auth: rootUser,
|
||||
query: {
|
||||
appId: ''
|
||||
}
|
||||
});
|
||||
|
||||
expect(res.code).toBe(500);
|
||||
expect(res.error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should handle mixed config values correctly', async () => {
|
||||
// Create config with mixed true/false values
|
||||
await MongoOutLink.create({
|
||||
shareId: `playground-${testApp._id}`,
|
||||
teamId: rootUser.teamId,
|
||||
tmbId: rootUser.tmbId,
|
||||
appId: testApp._id,
|
||||
name: 'Playground Chat',
|
||||
type: PublishChannelEnum.playground,
|
||||
showNodeStatus: true,
|
||||
responseDetail: false,
|
||||
showFullText: true,
|
||||
showRawSource: false,
|
||||
usagePoints: 0,
|
||||
lastTime: new Date()
|
||||
});
|
||||
|
||||
const res = await Call<PlaygroundVisibilityConfigResponse>(configApi.default, {
|
||||
auth: rootUser,
|
||||
query: {
|
||||
appId: testApp._id
|
||||
}
|
||||
});
|
||||
|
||||
// Check if the request was processed successfully
|
||||
if (res.code === 200) {
|
||||
expect(res.error).toBeUndefined();
|
||||
expect(res.data).toEqual({
|
||||
showNodeStatus: true,
|
||||
responseDetail: false,
|
||||
showFullText: true,
|
||||
showRawSource: false
|
||||
});
|
||||
} else {
|
||||
// If there are permission issues, we still expect the API to validate parameters
|
||||
expect(res.code).toBe(500);
|
||||
expect(res.error).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('should return error when user is not authenticated', async () => {
|
||||
const res = await Call<PlaygroundVisibilityConfigResponse>(configApi.default, {
|
||||
query: {
|
||||
appId: testApp._id
|
||||
}
|
||||
});
|
||||
|
||||
expect(res.code).toBe(500);
|
||||
expect(res.error).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,294 @@
|
|||
import type { UpdatePlaygroundVisibilityConfigBody } from '@fastgpt/global/support/outLink/api.d';
|
||||
import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import { getRootUser } from '@test/datas/users';
|
||||
import { Call } from '@test/utils/request';
|
||||
import { describe, it, expect, beforeAll, afterEach, afterAll } from 'vitest';
|
||||
import * as updateApi from '@/pages/api/support/outLink/playground/update';
|
||||
|
||||
describe('Playground Visibility Update API', () => {
|
||||
let rootUser: any;
|
||||
let testApp: any;
|
||||
|
||||
beforeAll(async () => {
|
||||
rootUser = await getRootUser();
|
||||
|
||||
// Create a test app owned by the root user
|
||||
testApp = await MongoApp.create({
|
||||
name: 'Test App for Playground Update',
|
||||
type: 'simple',
|
||||
tmbId: rootUser.tmbId,
|
||||
teamId: rootUser.teamId
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Clean up any created OutLink configs
|
||||
await MongoOutLink.deleteMany({
|
||||
appId: testApp._id,
|
||||
type: PublishChannelEnum.playground
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
// Clean up test data
|
||||
await MongoApp.deleteOne({ _id: testApp._id });
|
||||
});
|
||||
|
||||
it('should handle update request with valid data', async () => {
|
||||
const updateData: UpdatePlaygroundVisibilityConfigBody = {
|
||||
appId: testApp._id,
|
||||
showNodeStatus: false,
|
||||
responseDetail: false,
|
||||
showFullText: false,
|
||||
showRawSource: false
|
||||
};
|
||||
|
||||
const res = await Call(updateApi.default, {
|
||||
auth: rootUser,
|
||||
body: updateData
|
||||
});
|
||||
|
||||
// Check if the request was processed successfully
|
||||
if (res.code === 200) {
|
||||
expect(res.error).toBeUndefined();
|
||||
|
||||
// Verify the config was created in database
|
||||
const createdConfig = await MongoOutLink.findOne({
|
||||
appId: testApp._id,
|
||||
type: PublishChannelEnum.playground
|
||||
}).lean();
|
||||
|
||||
if (createdConfig) {
|
||||
expect(createdConfig.appId).toBe(testApp._id);
|
||||
expect(createdConfig.type).toBe(PublishChannelEnum.playground);
|
||||
expect(createdConfig.showNodeStatus).toBe(false);
|
||||
expect(createdConfig.responseDetail).toBe(false);
|
||||
expect(createdConfig.showFullText).toBe(false);
|
||||
expect(createdConfig.showRawSource).toBe(false);
|
||||
}
|
||||
} else {
|
||||
// If there are permission issues, we still expect the API to validate parameters
|
||||
expect(res.code).toBe(500);
|
||||
expect(res.error).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle update request with true values', async () => {
|
||||
const updateData: UpdatePlaygroundVisibilityConfigBody = {
|
||||
appId: testApp._id,
|
||||
showNodeStatus: true,
|
||||
responseDetail: true,
|
||||
showFullText: true,
|
||||
showRawSource: true
|
||||
};
|
||||
|
||||
const res = await Call(updateApi.default, {
|
||||
auth: rootUser,
|
||||
body: updateData
|
||||
});
|
||||
|
||||
// Check if the request was processed successfully
|
||||
if (res.code === 200) {
|
||||
expect(res.error).toBeUndefined();
|
||||
|
||||
// Verify true values were set
|
||||
const createdConfig = await MongoOutLink.findOne({
|
||||
appId: testApp._id,
|
||||
type: PublishChannelEnum.playground
|
||||
}).lean();
|
||||
|
||||
if (createdConfig) {
|
||||
expect(createdConfig.showNodeStatus).toBe(true);
|
||||
expect(createdConfig.responseDetail).toBe(true);
|
||||
expect(createdConfig.showFullText).toBe(true);
|
||||
expect(createdConfig.showRawSource).toBe(true);
|
||||
}
|
||||
} else {
|
||||
// If there are permission issues, we still expect the API to validate parameters
|
||||
expect(res.code).toBe(500);
|
||||
expect(res.error).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle update request with mixed boolean values', async () => {
|
||||
const updateData: UpdatePlaygroundVisibilityConfigBody = {
|
||||
appId: testApp._id,
|
||||
showNodeStatus: false,
|
||||
responseDetail: true,
|
||||
showFullText: false,
|
||||
showRawSource: true
|
||||
};
|
||||
|
||||
const res = await Call(updateApi.default, {
|
||||
auth: rootUser,
|
||||
body: updateData
|
||||
});
|
||||
|
||||
// Check if the request was processed successfully
|
||||
if (res.code === 200) {
|
||||
expect(res.error).toBeUndefined();
|
||||
|
||||
// Verify mixed values were set
|
||||
const createdConfig = await MongoOutLink.findOne({
|
||||
appId: testApp._id,
|
||||
type: PublishChannelEnum.playground
|
||||
}).lean();
|
||||
|
||||
if (createdConfig) {
|
||||
expect(createdConfig.showNodeStatus).toBe(false);
|
||||
expect(createdConfig.responseDetail).toBe(true);
|
||||
expect(createdConfig.showFullText).toBe(false);
|
||||
expect(createdConfig.showRawSource).toBe(true);
|
||||
}
|
||||
} else {
|
||||
// If there are permission issues, we still expect the API to validate parameters
|
||||
expect(res.code).toBe(500);
|
||||
expect(res.error).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('should return 500 when appId is missing', async () => {
|
||||
const updateData = {
|
||||
showNodeStatus: false
|
||||
};
|
||||
|
||||
const res = await Call(updateApi.default, {
|
||||
auth: rootUser,
|
||||
body: updateData
|
||||
});
|
||||
|
||||
expect(res.code).toBe(500);
|
||||
expect(res.error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return 500 when appId is empty string', async () => {
|
||||
const updateData: UpdatePlaygroundVisibilityConfigBody = {
|
||||
appId: '',
|
||||
showNodeStatus: false,
|
||||
responseDetail: false,
|
||||
showFullText: false,
|
||||
showRawSource: false
|
||||
};
|
||||
|
||||
const res = await Call(updateApi.default, {
|
||||
auth: rootUser,
|
||||
body: updateData
|
||||
});
|
||||
|
||||
expect(res.code).toBe(500);
|
||||
expect(res.error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return error when user is not authenticated', async () => {
|
||||
const updateData: UpdatePlaygroundVisibilityConfigBody = {
|
||||
appId: testApp._id,
|
||||
showNodeStatus: false,
|
||||
responseDetail: false,
|
||||
showFullText: false,
|
||||
showRawSource: false
|
||||
};
|
||||
|
||||
const res = await Call(updateApi.default, {
|
||||
body: updateData
|
||||
});
|
||||
|
||||
expect(res.code).toBe(500);
|
||||
expect(res.error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should validate all boolean fields are required', async () => {
|
||||
// Test with missing boolean fields (should fail validation)
|
||||
const updateData = {
|
||||
appId: testApp._id,
|
||||
showNodeStatus: false
|
||||
// Missing other boolean fields
|
||||
};
|
||||
|
||||
const res = await Call(updateApi.default, {
|
||||
auth: rootUser,
|
||||
body: updateData
|
||||
});
|
||||
|
||||
expect(res.code).toBe(500);
|
||||
expect(res.error).toBeDefined();
|
||||
});
|
||||
|
||||
it('should handle updates for different apps independently', async () => {
|
||||
// Create a second test app
|
||||
const testApp2 = await MongoApp.create({
|
||||
name: 'Test App 2 for Playground Update',
|
||||
type: 'simple',
|
||||
tmbId: rootUser.tmbId,
|
||||
teamId: rootUser.teamId
|
||||
});
|
||||
|
||||
// Create config for first app
|
||||
await MongoOutLink.create({
|
||||
shareId: `playground-${testApp._id}`,
|
||||
teamId: rootUser.teamId,
|
||||
tmbId: rootUser.tmbId,
|
||||
appId: testApp._id,
|
||||
name: 'Playground Chat',
|
||||
type: PublishChannelEnum.playground,
|
||||
showNodeStatus: true,
|
||||
responseDetail: true,
|
||||
showFullText: true,
|
||||
showRawSource: true,
|
||||
usagePoints: 0,
|
||||
lastTime: new Date()
|
||||
});
|
||||
|
||||
// Update config for second app
|
||||
const updateData: UpdatePlaygroundVisibilityConfigBody = {
|
||||
appId: testApp2._id,
|
||||
showNodeStatus: false,
|
||||
responseDetail: false,
|
||||
showFullText: true,
|
||||
showRawSource: true
|
||||
};
|
||||
|
||||
const res = await Call(updateApi.default, {
|
||||
auth: rootUser,
|
||||
body: updateData
|
||||
});
|
||||
|
||||
// Check if the request was processed successfully
|
||||
if (res.code === 200) {
|
||||
expect(res.error).toBeUndefined();
|
||||
|
||||
// Verify first app config is unchanged
|
||||
const config1 = await MongoOutLink.findOne({
|
||||
appId: testApp._id,
|
||||
type: PublishChannelEnum.playground
|
||||
}).lean();
|
||||
|
||||
if (config1) {
|
||||
expect(config1.showNodeStatus).toBe(true);
|
||||
expect(config1.responseDetail).toBe(true);
|
||||
}
|
||||
|
||||
// Verify second app config was created with new values
|
||||
const config2 = await MongoOutLink.findOne({
|
||||
appId: testApp2._id,
|
||||
type: PublishChannelEnum.playground
|
||||
}).lean();
|
||||
|
||||
if (config2) {
|
||||
expect(config2.showNodeStatus).toBe(false);
|
||||
expect(config2.responseDetail).toBe(false);
|
||||
expect(config2.showFullText).toBe(true);
|
||||
expect(config2.showRawSource).toBe(true);
|
||||
}
|
||||
} else {
|
||||
// If there are permission issues, we still expect the API to validate parameters
|
||||
expect(res.code).toBe(500);
|
||||
expect(res.error).toBeDefined();
|
||||
}
|
||||
|
||||
// Cleanup second app
|
||||
await MongoOutLink.deleteOne({ appId: testApp2._id });
|
||||
await MongoApp.deleteOne({ _id: testApp2._id });
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue