优化
This commit is contained in:
13
server/test/demo/getui/index.ts
Normal file
13
server/test/demo/getui/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import * as dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
import { GetuiClient } from '../../../plugins/com.msgbyte.getui/lib/GetuiClient';
|
||||
|
||||
const client = new GetuiClient(
|
||||
process.env.GETUI_APPID,
|
||||
process.env.GETUI_APPKEY,
|
||||
process.env.GETUI_MASTERSECRET
|
||||
);
|
||||
|
||||
client.allPush('title', 'body', {}).then((res) => {
|
||||
console.log('res', res);
|
||||
});
|
||||
11
server/test/demo/openapi-client-simple/app.html
Normal file
11
server/test/demo/openapi-client-simple/app.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>OIDC Client Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<a href="<API>/open/auth?client_id=<clientId>&redirect_uri=<clientUrl>/cb&scope=openid profile&response_type=code&state=45535116436">登录</a>
|
||||
</body>
|
||||
</html>
|
||||
86
server/test/demo/openapi-client-simple/index.ts
Normal file
86
server/test/demo/openapi-client-simple/index.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import express from 'express';
|
||||
import path from 'path';
|
||||
import axios from 'axios';
|
||||
import fs from 'fs-extra';
|
||||
const app = express();
|
||||
const port = process.env.PORT || 8080;
|
||||
|
||||
const API = process.env.API || 'https://tailchat-nightly.moonrailgun.com'; // dev environment is 'http://localhost:11001'
|
||||
const clientUrl = `http://localhost:${port}`;
|
||||
const clientId = process.env.ID || 'tc_649aa2179e97b8b3b2d1004f';
|
||||
const clientSecret = process.env.SECRET || '4Pt4lccOaztJROs-VhmQf8XBU89-z8rr';
|
||||
|
||||
console.log('config:', {
|
||||
API,
|
||||
clientUrl,
|
||||
clientId,
|
||||
});
|
||||
|
||||
const request = axios.create({
|
||||
baseURL: API,
|
||||
transformRequest: [
|
||||
function (data) {
|
||||
let ret = '';
|
||||
for (const it in data) {
|
||||
ret +=
|
||||
encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&';
|
||||
}
|
||||
ret = ret.substring(0, ret.lastIndexOf('&'));
|
||||
return ret;
|
||||
},
|
||||
],
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
|
||||
app.get('/', async (req, res) => {
|
||||
let html = (
|
||||
await fs.readFile(path.resolve(__dirname, './app.html'))
|
||||
).toString();
|
||||
html = html
|
||||
.replace('<API>', API)
|
||||
.replace('<clientId>', clientId)
|
||||
.replace('<clientUrl>', clientUrl);
|
||||
|
||||
res.send(html);
|
||||
});
|
||||
|
||||
app.get('/cb', async (req, res, next) => {
|
||||
try {
|
||||
const { code, state } = req.query;
|
||||
|
||||
console.log('code', code);
|
||||
|
||||
// 根据获取到的code获取授权码
|
||||
const { data: tokenInfo } = await request.post('/open/token', {
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
redirect_uri: `${clientUrl}/cb`,
|
||||
code,
|
||||
grant_type: 'authorization_code',
|
||||
});
|
||||
|
||||
console.log('tokenInfo', tokenInfo);
|
||||
|
||||
const { access_token, expires_in, id_token, scope, token_type } = tokenInfo;
|
||||
|
||||
console.log('access_token', access_token);
|
||||
|
||||
const { data: userInfo } = await request.post('/open/me', {
|
||||
access_token,
|
||||
});
|
||||
|
||||
res.json({ userInfo });
|
||||
} catch (err) {
|
||||
console.error(err.response.data);
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(
|
||||
`Please ensure that the third-party login function is enabled and the callback has been registered in the whitelist of the OIDC server: ${clientUrl}/cb`
|
||||
);
|
||||
console.log(`Test Server Address: http://127.0.0.1:${port}`);
|
||||
});
|
||||
21
server/test/demo/openapi-client-simple/package.json
Normal file
21
server/test/demo/openapi-client-simple/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "openapi-client",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "ts-node ./index.ts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^0.26.0",
|
||||
"express": "^4.17.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.15",
|
||||
"ts-node": "10.9.1"
|
||||
}
|
||||
}
|
||||
101
server/test/demo/openapi-client/index.ts
Normal file
101
server/test/demo/openapi-client/index.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { Issuer } from 'openid-client';
|
||||
import express from 'express';
|
||||
import axios from 'axios';
|
||||
|
||||
const app = express();
|
||||
const port = 8080;
|
||||
|
||||
const API = process.env.API || 'http://localhost:11001';
|
||||
const clientUrl = `http://localhost:${port}`;
|
||||
const clientId = process.env.ID || 'tc_649aa2179e97b8b3b2d1004f';
|
||||
const clientSecret = process.env.SECRET || '4Pt4lccOaztJROs-VhmQf8XBU89-z8rr';
|
||||
|
||||
console.log('config:', {
|
||||
API,
|
||||
clientUrl,
|
||||
clientId,
|
||||
});
|
||||
|
||||
const request = axios.create({
|
||||
baseURL: API,
|
||||
transformRequest: [
|
||||
function (data) {
|
||||
let ret = '';
|
||||
for (const it in data) {
|
||||
ret +=
|
||||
encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&';
|
||||
}
|
||||
ret = ret.substring(0, ret.lastIndexOf('&'));
|
||||
return ret;
|
||||
},
|
||||
],
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
|
||||
(async () => {
|
||||
const tailchatIssuer = await Issuer.discover(
|
||||
'https://tailchat-nightly.moonrailgun.com/open/'
|
||||
);
|
||||
console.log(
|
||||
'Discovered issuer',
|
||||
tailchatIssuer.issuer,
|
||||
tailchatIssuer.metadata
|
||||
);
|
||||
|
||||
const client = new tailchatIssuer.Client({
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
redirect_uris: [`${clientUrl}/cb`],
|
||||
response_types: ['code'],
|
||||
// id_token_signed_response_alg (default "RS256")
|
||||
// token_endpoint_auth_method (default "client_secret_basic")
|
||||
});
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
const url = client.authorizationUrl({
|
||||
scope: 'openid profile',
|
||||
// response_mode: 'form_post',
|
||||
nonce: Math.random().toString(),
|
||||
});
|
||||
res.send(`<a href="${url}">登录</a>`);
|
||||
});
|
||||
|
||||
app.get('/cb', async (req, res, next) => {
|
||||
try {
|
||||
const { code } = req.query;
|
||||
|
||||
// 根据获取到的code获取授权码
|
||||
const { data: tokenInfo } = await request.post('/open/token', {
|
||||
// client_id: 'foo',
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
redirect_uri: `${clientUrl}/cb`,
|
||||
code,
|
||||
grant_type: 'authorization_code',
|
||||
});
|
||||
|
||||
console.log('tokenInfo', tokenInfo);
|
||||
|
||||
const { access_token, expires_in, id_token, scope, token_type } =
|
||||
tokenInfo;
|
||||
|
||||
console.log('access_token', access_token);
|
||||
|
||||
const { data: userInfo } = await request.post('/open/me', {
|
||||
access_token,
|
||||
});
|
||||
|
||||
res.json({ userInfo });
|
||||
} catch (err) {
|
||||
console.error(err.response.data);
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`请确保回调已经被注册在OIDC服务端的白名单中: ${clientUrl}/cb`);
|
||||
console.log(`测试服务地址: http://127.0.0.1:${port}`);
|
||||
});
|
||||
})();
|
||||
21
server/test/demo/openapi-client/package.json
Normal file
21
server/test/demo/openapi-client/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "openapi-client",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "ts-node ./index.ts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^0.26.0",
|
||||
"express": "^4.17.2",
|
||||
"openid-client": "^5.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ts-node": "10.9.1"
|
||||
}
|
||||
}
|
||||
67
server/test/demo/openapi-oidc-page/index.ts
Normal file
67
server/test/demo/openapi-oidc-page/index.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import express from 'express';
|
||||
import path from 'path';
|
||||
import ejs from 'ejs';
|
||||
|
||||
const app = express();
|
||||
const port = process.env.PORT || 8080;
|
||||
|
||||
const publicDir = path.resolve(__dirname, '../../../public');
|
||||
const viewRootDir = path.resolve(
|
||||
__dirname,
|
||||
'../../../services/openapi/oidc/views'
|
||||
);
|
||||
|
||||
app.use(express.static(publicDir));
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.send(
|
||||
`<ul>${['/login', '/authorize', '/error']
|
||||
.map((p) => `<li><a href=${p}>${p}</a></li>`)
|
||||
.join('\n')}</ul>`
|
||||
);
|
||||
});
|
||||
|
||||
app.get('/login', async (req, res) => {
|
||||
const data = {
|
||||
uid: 'fooooooooo',
|
||||
};
|
||||
const html = await ejs.renderFile(
|
||||
path.resolve(viewRootDir, './login.ejs'),
|
||||
data
|
||||
);
|
||||
|
||||
res.send(html);
|
||||
});
|
||||
|
||||
app.get('/error', async (req, res) => {
|
||||
const data = {
|
||||
text: 'fooooooooo',
|
||||
};
|
||||
const html = await ejs.renderFile(
|
||||
path.resolve(viewRootDir, './error.ejs'),
|
||||
data
|
||||
);
|
||||
|
||||
res.send(html);
|
||||
});
|
||||
|
||||
app.get('/authorize', async (req, res) => {
|
||||
const data = {
|
||||
logoUri: 'loginUrl',
|
||||
clientName: 'Test',
|
||||
uid: 'foooo',
|
||||
details: {},
|
||||
params: {},
|
||||
session: '',
|
||||
};
|
||||
const html = await ejs.renderFile(
|
||||
path.resolve(viewRootDir, './authorize.ejs'),
|
||||
data
|
||||
);
|
||||
|
||||
res.send(html);
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server: http://127.0.0.1:${port}`);
|
||||
});
|
||||
22
server/test/demo/openapi-oidc-page/package.json
Normal file
22
server/test/demo/openapi-oidc-page/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "openapi-oidc-page",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "ts-node ./index.ts",
|
||||
"dev": "nodemon --watch \"index.ts\" --watch \"../../../services/openapi/oidc/views/**\" --ext \"ts,ejs\" --exec \"ts-node --transpile-only ./index.ts\"",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"express": "^4.17.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.15",
|
||||
"nodemon": "^3.0.1",
|
||||
"ts-node": "10.9.1"
|
||||
}
|
||||
}
|
||||
38
server/test/integration/chat/ack.spec.ts
Normal file
38
server/test/integration/chat/ack.spec.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { createTestServiceBroker } from '../../utils';
|
||||
import AckService from '../../../services/core/chat/ack.service';
|
||||
import { Types } from 'mongoose';
|
||||
import _ from 'lodash';
|
||||
|
||||
describe('Test "chat.message" service', () => {
|
||||
const { broker, service, insertTestData } =
|
||||
createTestServiceBroker<AckService>(AckService);
|
||||
|
||||
test('Test "chat.ack.update"', async () => {
|
||||
const converseId = new Types.ObjectId();
|
||||
const userId = new Types.ObjectId();
|
||||
const lastMessageId = new Types.ObjectId();
|
||||
|
||||
await broker.call(
|
||||
'chat.ack.update',
|
||||
{
|
||||
converseId: String(converseId),
|
||||
lastMessageId: String(lastMessageId),
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const record = await service.adapter.model.findOne({
|
||||
userId,
|
||||
converseId,
|
||||
});
|
||||
try {
|
||||
expect(String(record.lastMessageId)).toBe(String(lastMessageId));
|
||||
} finally {
|
||||
await record.deleteOne();
|
||||
}
|
||||
});
|
||||
});
|
||||
180
server/test/integration/chat/message.spec.ts
Normal file
180
server/test/integration/chat/message.spec.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import { createTestServiceBroker } from '../../utils';
|
||||
import MessageService from '../../../services/core/chat/message.service';
|
||||
import type { MessageDocument } from '../../../models/chat/message';
|
||||
import { Types } from 'mongoose';
|
||||
import _ from 'lodash';
|
||||
|
||||
function createTestMessage(converseId: Types.ObjectId, content = 'bar') {
|
||||
return {
|
||||
content,
|
||||
// author: '',
|
||||
// groupId: '',
|
||||
avatar: null,
|
||||
converseId,
|
||||
};
|
||||
}
|
||||
|
||||
describe('Test "chat.message" service', () => {
|
||||
const { broker, service, insertTestData } =
|
||||
createTestServiceBroker<MessageService>(MessageService);
|
||||
|
||||
describe('Test "chat.message.fetchConverseMessage"', () => {
|
||||
test('single message', async () => {
|
||||
const converseId = new Types.ObjectId();
|
||||
const testDoc = await insertTestData(createTestMessage(converseId));
|
||||
|
||||
const res: MessageDocument = await broker.call(
|
||||
'chat.message.fetchConverseMessage',
|
||||
{
|
||||
converseId: String(converseId),
|
||||
}
|
||||
);
|
||||
|
||||
expect(res).not.toBe(null);
|
||||
expect(Array.isArray(res)).toBe(true);
|
||||
expect(_.get(res, [0, '_id'])).toBe(String(testDoc._id));
|
||||
});
|
||||
|
||||
test('limit should be ok', async () => {
|
||||
const converseId = new Types.ObjectId();
|
||||
const docs = await Promise.all(
|
||||
Array(60)
|
||||
.fill(null)
|
||||
.map(() => insertTestData(createTestMessage(converseId)))
|
||||
);
|
||||
|
||||
const res: MessageDocument[] = await broker.call(
|
||||
'chat.message.fetchConverseMessage',
|
||||
{
|
||||
converseId: String(converseId),
|
||||
}
|
||||
);
|
||||
|
||||
expect(res).not.toBe(null);
|
||||
expect(Array.isArray(res)).toBe(true);
|
||||
expect(res.length).toBe(50);
|
||||
});
|
||||
|
||||
test('startId should be ok', async () => {
|
||||
const converseId = new Types.ObjectId();
|
||||
const docs = await Promise.all(
|
||||
Array(60)
|
||||
.fill(null)
|
||||
.map(() => insertTestData(createTestMessage(converseId)))
|
||||
);
|
||||
|
||||
const startId = docs[20]._id; // 这是第21条数据
|
||||
|
||||
const res: MessageDocument[] = await broker.call(
|
||||
'chat.message.fetchConverseMessage',
|
||||
{
|
||||
converseId: String(converseId),
|
||||
startId: String(startId),
|
||||
}
|
||||
);
|
||||
|
||||
expect(res).not.toBe(null);
|
||||
expect(Array.isArray(res)).toBe(true);
|
||||
expect(res.length).toBe(20); // 因为是倒序排列, 所以会拿到前20条
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test message reaction"', () => {
|
||||
test('chat.message.addReaction', async () => {
|
||||
const converseId = new Types.ObjectId();
|
||||
const userId = new Types.ObjectId();
|
||||
const emoji = ':any:';
|
||||
const message = await insertTestData(createTestMessage(converseId));
|
||||
|
||||
const res: MessageDocument[] = await broker.call(
|
||||
'chat.message.addReaction',
|
||||
{
|
||||
messageId: String(message._id),
|
||||
emoji,
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(res).toBe(true);
|
||||
|
||||
const _message = await service.adapter.findById(String(message._id));
|
||||
expect(_message.reactions.length).toBe(1);
|
||||
expect(_message.reactions[0].name).toBe(emoji);
|
||||
expect(String(_message.reactions[0].author)).toBe(String(userId));
|
||||
});
|
||||
|
||||
describe('chat.message.removeReaction', () => {
|
||||
test('remove exist reaction', async () => {
|
||||
const converseId = new Types.ObjectId();
|
||||
const userId = new Types.ObjectId();
|
||||
const emoji = ':any:';
|
||||
const message = await insertTestData({
|
||||
...createTestMessage(converseId),
|
||||
reactions: [
|
||||
{
|
||||
author: userId,
|
||||
name: emoji,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const res: MessageDocument[] = await broker.call(
|
||||
'chat.message.removeReaction',
|
||||
{
|
||||
messageId: String(message._id),
|
||||
emoji,
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(res).toBe(true);
|
||||
|
||||
const _message = await service.adapter.findById(String(message._id));
|
||||
expect(_message.reactions.length).toBe(0);
|
||||
});
|
||||
|
||||
test('remove non-exist reaction', async () => {
|
||||
const converseId = new Types.ObjectId();
|
||||
const userId = new Types.ObjectId();
|
||||
const emoji = ':any:';
|
||||
const message = await insertTestData({
|
||||
...createTestMessage(converseId),
|
||||
reactions: [
|
||||
{
|
||||
author: userId,
|
||||
name: emoji,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const res: MessageDocument[] = await broker.call(
|
||||
'chat.message.removeReaction',
|
||||
{
|
||||
messageId: String(message._id),
|
||||
emoji: ':none:',
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(res).toBe(true);
|
||||
|
||||
const _message = await service.adapter.findById(String(message._id));
|
||||
expect(_message.reactions.length).toBe(1);
|
||||
expect(_message.reactions[0].name).toBe(emoji);
|
||||
expect(String(_message.reactions[0].author)).toBe(String(userId));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
600
server/test/integration/group/group.spec.ts
Normal file
600
server/test/integration/group/group.spec.ts
Normal file
@@ -0,0 +1,600 @@
|
||||
import { createTestServiceBroker } from '../../utils';
|
||||
import GroupService from '../../../services/core/group/group.service';
|
||||
import { Types } from 'mongoose';
|
||||
import type { Group } from '../../../models/group/group';
|
||||
import { generateRandomStr } from '../../../lib/utils';
|
||||
import _ from 'lodash';
|
||||
import { GroupPanelType, PERMISSION } from 'tailchat-server-sdk';
|
||||
|
||||
function createTestGroup(
|
||||
userId: Types.ObjectId = new Types.ObjectId(),
|
||||
groupInfo?: Partial<Group>
|
||||
): Partial<Group> {
|
||||
return {
|
||||
name: 'test',
|
||||
owner: userId,
|
||||
members: [
|
||||
{
|
||||
roles: [],
|
||||
userId: userId,
|
||||
},
|
||||
],
|
||||
panels: [],
|
||||
...groupInfo,
|
||||
};
|
||||
}
|
||||
|
||||
function createTestRole(
|
||||
name: string = generateRandomStr(),
|
||||
permissions: string[] = []
|
||||
) {
|
||||
const roleId = new Types.ObjectId();
|
||||
return {
|
||||
_id: roleId,
|
||||
id: String(roleId),
|
||||
name,
|
||||
permissions,
|
||||
};
|
||||
}
|
||||
|
||||
describe('Test "group" service', () => {
|
||||
const { broker, service, insertTestData } =
|
||||
createTestServiceBroker<GroupService>(GroupService, {
|
||||
contextCallMockFn(actionName) {
|
||||
if (actionName === 'group.getUserAllPermissions') {
|
||||
return [PERMISSION.core.owner];
|
||||
}
|
||||
if (actionName === 'user.getUserInfo') {
|
||||
return { nickname: 'test-nickname' };
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
test('Test "group.createGroup"', async () => {
|
||||
const userId = String(new Types.ObjectId());
|
||||
|
||||
const res: Group = await broker.call(
|
||||
'group.createGroup',
|
||||
{
|
||||
name: 'test',
|
||||
panels: [
|
||||
{
|
||||
id: '00',
|
||||
name: '频道1',
|
||||
type: GroupPanelType.TEXT,
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
name: '频道分组',
|
||||
type: GroupPanelType.GROUP,
|
||||
},
|
||||
{
|
||||
id: '11',
|
||||
name: '子频道',
|
||||
parentId: '10',
|
||||
type: GroupPanelType.TEXT,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
expect(res).toHaveProperty('name', 'test');
|
||||
expect(res).toHaveProperty('panels');
|
||||
expect(res).toHaveProperty('owner');
|
||||
expect(res.members.length).toBe(1);
|
||||
|
||||
// 面板ID会被自动转换
|
||||
const panels = res.panels;
|
||||
expect(panels[0].id).toHaveLength(24);
|
||||
expect(panels[1].id).toBe(panels[2].parentId);
|
||||
expect(res.roles).toEqual([]);
|
||||
} finally {
|
||||
await service.adapter.model.findByIdAndRemove(res._id);
|
||||
}
|
||||
});
|
||||
|
||||
test('Test "group.getUserGroups"', async () => {
|
||||
const userId = new Types.ObjectId();
|
||||
const testGroup = await insertTestData(createTestGroup(userId));
|
||||
|
||||
const res: Group[] = await broker.call(
|
||||
'group.getUserGroups',
|
||||
{},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(res.length).toBe(1);
|
||||
expect(res[0]._id).toBe(String(testGroup._id));
|
||||
});
|
||||
|
||||
test('Test "group.joinGroup"', async () => {
|
||||
const userId = new Types.ObjectId();
|
||||
const testGroup = await insertTestData(createTestGroup(userId));
|
||||
|
||||
expect(
|
||||
[...(testGroup.members ?? [])].map((v) =>
|
||||
service.adapter.entityToObject(v)
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
roles: [],
|
||||
userId,
|
||||
},
|
||||
]);
|
||||
|
||||
const newMemberUserId = new Types.ObjectId();
|
||||
|
||||
const res: Group = await broker.call(
|
||||
'group.joinGroup',
|
||||
{
|
||||
groupId: String(testGroup._id),
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(newMemberUserId),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const newMembers = [...res.members];
|
||||
expect(newMembers).toEqual([
|
||||
{
|
||||
roles: [],
|
||||
userId,
|
||||
},
|
||||
{
|
||||
roles: [],
|
||||
userId: newMemberUserId,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Test "group.modifyGroupPanel"', async () => {
|
||||
const testGroupPanels = [
|
||||
{
|
||||
id: String(new Types.ObjectId()),
|
||||
name: generateRandomStr(),
|
||||
type: 1,
|
||||
},
|
||||
{
|
||||
id: String(new Types.ObjectId()),
|
||||
name: generateRandomStr(),
|
||||
type: 1,
|
||||
},
|
||||
{
|
||||
id: String(new Types.ObjectId()),
|
||||
name: generateRandomStr(),
|
||||
type: 1,
|
||||
},
|
||||
];
|
||||
const testGroup = await insertTestData(
|
||||
createTestGroup(new Types.ObjectId(), {
|
||||
panels: [...testGroupPanels],
|
||||
})
|
||||
);
|
||||
|
||||
const newPanelName = generateRandomStr();
|
||||
|
||||
const res: Group = await broker.call(
|
||||
'group.modifyGroupPanel',
|
||||
{
|
||||
groupId: String(testGroup._id),
|
||||
panelId: String(testGroupPanels[1].id),
|
||||
name: newPanelName,
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(testGroup.owner),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const expectedPanels = [
|
||||
testGroupPanels[0],
|
||||
{ ...testGroupPanels[1], name: newPanelName },
|
||||
testGroupPanels[2],
|
||||
];
|
||||
expect(res.panels).toEqual(expectedPanels);
|
||||
expect(_.omit(res, 'updatedAt')).toEqual(
|
||||
_.omit(
|
||||
{
|
||||
...testGroup.toJSON(),
|
||||
_id: String(testGroup._id),
|
||||
panels: expectedPanels,
|
||||
},
|
||||
'updatedAt'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
describe('Test "group.deleteGroupPanel"', () => {
|
||||
const groupPanelId = new Types.ObjectId();
|
||||
const textPanelId = new Types.ObjectId();
|
||||
|
||||
const sampleGroupInfo = {
|
||||
panels: [
|
||||
{
|
||||
id: String(groupPanelId),
|
||||
name: '文字频道',
|
||||
type: 1,
|
||||
},
|
||||
{
|
||||
id: String(textPanelId),
|
||||
name: '大厅',
|
||||
parentId: String(groupPanelId),
|
||||
type: 0,
|
||||
},
|
||||
{
|
||||
id: String(new Types.ObjectId()),
|
||||
name: '其他面板',
|
||||
type: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
test('delete single panel', async () => {
|
||||
const userId = new Types.ObjectId();
|
||||
const testGroup = await insertTestData(
|
||||
createTestGroup(userId, sampleGroupInfo)
|
||||
);
|
||||
|
||||
const res: Group = await broker.call(
|
||||
'group.deleteGroupPanel',
|
||||
{
|
||||
groupId: String(testGroup._id),
|
||||
panelId: String(textPanelId),
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(res.panels.length).toBe(2);
|
||||
});
|
||||
test('delete group panel', async () => {
|
||||
const userId = new Types.ObjectId();
|
||||
const testGroup = await insertTestData(
|
||||
createTestGroup(userId, sampleGroupInfo)
|
||||
);
|
||||
|
||||
const res: Group = await broker.call(
|
||||
'group.deleteGroupPanel',
|
||||
{
|
||||
groupId: String(testGroup._id),
|
||||
panelId: String(groupPanelId),
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(res.panels.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Group Roles Controllers', () => {
|
||||
test('Test "group.createGroupRole"', async () => {
|
||||
const userId = new Types.ObjectId();
|
||||
const testGroup = await insertTestData(createTestGroup(userId));
|
||||
|
||||
const res: Group = await broker.call(
|
||||
'group.createGroupRole',
|
||||
{
|
||||
groupId: String(testGroup.id),
|
||||
roleName: 'testRole',
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect((res.roles ?? []).length).toBe(1);
|
||||
expect(res.roles).toMatchObject([
|
||||
{
|
||||
name: 'testRole',
|
||||
permissions: [],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Test "group.deleteGroupRole"', async () => {
|
||||
const userId = new Types.ObjectId();
|
||||
const role1 = createTestRole('TestRole1', ['permission1', 'permission2']);
|
||||
const role2 = createTestRole('TestRole2', ['permission1', 'permission2']);
|
||||
const testGroup = await insertTestData(
|
||||
createTestGroup(userId, {
|
||||
roles: [role1, role2],
|
||||
members: [
|
||||
{
|
||||
userId,
|
||||
roles: [role1.id, role2.id],
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
expect(testGroup.roles?.length).toBe(2);
|
||||
expect(testGroup.roles).toMatchObject([role1, role2]);
|
||||
|
||||
const res: Group = await broker.call(
|
||||
'group.deleteGroupRole',
|
||||
{
|
||||
groupId: String(testGroup.id),
|
||||
roleId: String(role1.id),
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(res.roles?.length).toBe(1);
|
||||
expect(res.roles).toMatchObject([
|
||||
{
|
||||
name: 'TestRole2',
|
||||
permissions: ['permission1', 'permission2'],
|
||||
},
|
||||
]);
|
||||
expect(res.members).toMatchObject([
|
||||
{
|
||||
userId,
|
||||
roles: [role2.id],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Test "group.updateGroupRolePermission"', async () => {
|
||||
const userId = new Types.ObjectId();
|
||||
const role1 = createTestRole('TestRole1', ['permission1', 'permission2']);
|
||||
const role2 = createTestRole('TestRole2', ['permission1', 'permission2']);
|
||||
const testGroup = await insertTestData(
|
||||
createTestGroup(userId, {
|
||||
roles: [role1, role2],
|
||||
})
|
||||
);
|
||||
|
||||
const res: Group = await broker.call(
|
||||
'group.updateGroupRolePermission',
|
||||
{
|
||||
groupId: String(testGroup.id),
|
||||
roleName: 'TestRole1',
|
||||
permissions: ['foo'],
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect((res.roles ?? []).length).toBe(2);
|
||||
expect(res.roles).toMatchObject([
|
||||
{
|
||||
name: 'TestRole1',
|
||||
permissions: ['foo'],
|
||||
},
|
||||
{
|
||||
name: 'TestRole2',
|
||||
permissions: ['permission1', 'permission2'],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Test "group.getPermissions"', async () => {
|
||||
const userId = new Types.ObjectId();
|
||||
const role1 = createTestRole('TestRole1', ['permission1', 'permission2']);
|
||||
const role2 = createTestRole('TestRole2', ['permission2', 'permission3']);
|
||||
const testGroup = await insertTestData(
|
||||
createTestGroup(userId, {
|
||||
members: [
|
||||
{
|
||||
userId,
|
||||
roles: [role1.id, role2.id],
|
||||
},
|
||||
],
|
||||
roles: [role1, role2],
|
||||
})
|
||||
);
|
||||
|
||||
const res: string[] = await broker.call(
|
||||
'group.getPermissions',
|
||||
{
|
||||
groupId: String(testGroup.id),
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
},
|
||||
}
|
||||
);
|
||||
expect(res).toEqual(['permission1', 'permission2', 'permission3']);
|
||||
});
|
||||
|
||||
test('Test "group.appendGroupMemberRoles"', async () => {
|
||||
const userId = new Types.ObjectId();
|
||||
const role1 = createTestRole('TestRole1', ['permission1', 'permission2']);
|
||||
const role2 = createTestRole('TestRole2', ['permission2', 'permission3']);
|
||||
const testGroup = await insertTestData(
|
||||
createTestGroup(userId, {
|
||||
members: [
|
||||
{
|
||||
userId,
|
||||
roles: [role1.id],
|
||||
},
|
||||
],
|
||||
roles: [role1, role2],
|
||||
})
|
||||
);
|
||||
|
||||
await broker.call(
|
||||
'group.appendGroupMemberRoles',
|
||||
{
|
||||
groupId: String(testGroup.id),
|
||||
memberIds: [String(userId)],
|
||||
roles: [role2.id],
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(_.last(service.cleanActionCache.mock.calls)).toEqual([
|
||||
'getGroupInfo',
|
||||
[String(testGroup.id)],
|
||||
]);
|
||||
const notifiedGroupId = _.last(service.roomcastNotify.mock.calls)[1];
|
||||
const notifiedGroupInfo: Group = _.last(
|
||||
service.roomcastNotify.mock.calls
|
||||
)[3];
|
||||
|
||||
expect(notifiedGroupId).toEqual(String(testGroup.id));
|
||||
expect(notifiedGroupInfo.members).toEqual([
|
||||
{
|
||||
roles: [role1.id, role2.id],
|
||||
userId,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('Test "group.removeGroupMemberRoles"', async () => {
|
||||
const userId = new Types.ObjectId();
|
||||
const role1 = createTestRole('TestRole1', ['permission1', 'permission2']);
|
||||
const role2 = createTestRole('TestRole2', ['permission2', 'permission3']);
|
||||
const testGroup = await insertTestData(
|
||||
createTestGroup(userId, {
|
||||
members: [
|
||||
{
|
||||
userId,
|
||||
roles: [role1.id],
|
||||
},
|
||||
],
|
||||
roles: [role1, role2],
|
||||
})
|
||||
);
|
||||
|
||||
await broker.call(
|
||||
'group.removeGroupMemberRoles',
|
||||
{
|
||||
groupId: String(testGroup.id),
|
||||
memberIds: [String(userId)],
|
||||
roles: [role1.id],
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(_.last(service.cleanActionCache.mock.calls)).toEqual([
|
||||
'getGroupInfo',
|
||||
[String(testGroup.id)],
|
||||
]);
|
||||
const notifiedGroupId = _.last(service.roomcastNotify.mock.calls)[1];
|
||||
const notifiedGroupInfo: Group = _.last(
|
||||
service.roomcastNotify.mock.calls
|
||||
)[3];
|
||||
|
||||
expect(notifiedGroupId).toEqual(String(testGroup.id));
|
||||
expect(notifiedGroupInfo.members).toEqual([
|
||||
{
|
||||
roles: [],
|
||||
userId,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
test('Test "group.muteGroupMember"', async () => {
|
||||
const userId = new Types.ObjectId();
|
||||
const testGroup = await insertTestData(createTestGroup(userId));
|
||||
|
||||
const muteUntil = new Date().valueOf() + 1000 * 60 * 60 * 10;
|
||||
|
||||
await broker.call(
|
||||
'group.muteGroupMember',
|
||||
{
|
||||
groupId: String(testGroup._id),
|
||||
memberId: String(userId),
|
||||
muteMs: 1000 * 60 * 60 * 10,
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
user: { nickname: 'foo' },
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const finalGroup = await service.adapter.model.findById(testGroup._id);
|
||||
|
||||
expect(new Date(finalGroup?.members[0].muteUntil ?? 0).valueOf()).toBe(
|
||||
muteUntil
|
||||
);
|
||||
});
|
||||
|
||||
test('Test "group.deleteGroupMember"', async () => {
|
||||
const userId = new Types.ObjectId();
|
||||
const userId2 = new Types.ObjectId();
|
||||
const testGroup = await insertTestData(
|
||||
createTestGroup(userId, {
|
||||
members: [
|
||||
{
|
||||
roles: [],
|
||||
userId: userId,
|
||||
},
|
||||
{
|
||||
roles: [],
|
||||
userId: userId2,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
const beforeGroup = await service.adapter.model.findById(testGroup._id);
|
||||
|
||||
expect(beforeGroup.members.map((m) => String(m.userId))).toEqual([
|
||||
String(userId),
|
||||
String(userId2),
|
||||
]);
|
||||
|
||||
await broker.call(
|
||||
'group.deleteGroupMember',
|
||||
{
|
||||
groupId: String(testGroup._id),
|
||||
memberId: String(userId2),
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(userId),
|
||||
user: { nickname: 'foo' },
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const finalGroup = await service.adapter.model.findById(testGroup._id);
|
||||
|
||||
expect(finalGroup.members.map((m) => String(m.userId))).toEqual([
|
||||
String(userId),
|
||||
]);
|
||||
});
|
||||
});
|
||||
77
server/test/integration/openapi/app.spec.ts
Normal file
77
server/test/integration/openapi/app.spec.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { createTestServiceBroker } from '../../utils';
|
||||
import OpenAppService from '../../../services/openapi/app.service';
|
||||
import { Types } from 'mongoose';
|
||||
import _ from 'lodash';
|
||||
import { generateRandomStr } from '../../../lib/utils';
|
||||
import type { OpenApp } from '../../../models/openapi/app';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
/**
|
||||
* 创建测试应用
|
||||
*/
|
||||
|
||||
function createTestOpenApp(
|
||||
userId: Types.ObjectId = new Types.ObjectId()
|
||||
): Partial<OpenApp> {
|
||||
return {
|
||||
owner: userId,
|
||||
appId: `tc_${new Types.ObjectId().toString()}`,
|
||||
appSecret: nanoid(32),
|
||||
appName: generateRandomStr(),
|
||||
appDesc: generateRandomStr(),
|
||||
appIcon: null,
|
||||
};
|
||||
}
|
||||
|
||||
describe('Test "openapi.app" service', () => {
|
||||
const { broker, service, insertTestData } =
|
||||
createTestServiceBroker<OpenAppService>(OpenAppService);
|
||||
|
||||
test('Test "openapi.app.create"', async () => {
|
||||
const userId = String(new Types.ObjectId());
|
||||
const name = generateRandomStr();
|
||||
|
||||
const res: OpenApp = await broker.call(
|
||||
'openapi.app.create',
|
||||
{
|
||||
appName: name,
|
||||
appDesc: '',
|
||||
appIcon: '',
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
expect(res).toHaveProperty('owner');
|
||||
expect(res.appId).toHaveLength(27);
|
||||
expect(res.appSecret).toHaveLength(32);
|
||||
expect(res.appName).toBe(name);
|
||||
} finally {
|
||||
await service.adapter.model.findByIdAndRemove(res._id);
|
||||
}
|
||||
});
|
||||
|
||||
test('Test "openapi.app.setAppOAuthInfo"', async () => {
|
||||
const { _id, appId, owner } = await insertTestData(createTestOpenApp());
|
||||
await broker.call(
|
||||
'openapi.app.setAppOAuthInfo',
|
||||
{
|
||||
appId,
|
||||
fieldName: 'redirectUrls',
|
||||
fieldValue: ['http://example.com'],
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(owner),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const openapp = await service.adapter.findById(_id);
|
||||
expect(openapp.oauth.redirectUrls).toEqual(['http://example.com']);
|
||||
});
|
||||
});
|
||||
177
server/test/integration/user/dmlist.spec.ts
Normal file
177
server/test/integration/user/dmlist.spec.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
import { createTestServiceBroker } from '../../utils';
|
||||
import UserDMListService from '../../../services/core/user/dmlist.service';
|
||||
import { Types } from 'mongoose';
|
||||
import type { UserDMList } from '../../../models/user/dmList';
|
||||
|
||||
describe('Test "dmlist" service', () => {
|
||||
const { broker, service, insertTestData } =
|
||||
createTestServiceBroker<UserDMListService>(UserDMListService);
|
||||
|
||||
describe('Test "user.dmlist.addConverse"', () => {
|
||||
test('addConverse should be ok', async () => {
|
||||
const userId = String(new Types.ObjectId());
|
||||
const converseId = String(new Types.ObjectId());
|
||||
|
||||
await broker.call(
|
||||
'user.dmlist.addConverse',
|
||||
{
|
||||
converseId,
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
const res = await service.adapter.model.findOne({
|
||||
userId,
|
||||
});
|
||||
|
||||
expect(res.converseIds.map((r) => String(r))).toEqual([converseId]); // 应该被成功插入
|
||||
} finally {
|
||||
await service.adapter.model.deleteOne({
|
||||
userId,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('addConverse should not be repeat', async () => {
|
||||
const userId = String(new Types.ObjectId());
|
||||
const converseId = String(new Types.ObjectId());
|
||||
|
||||
await broker.call(
|
||||
'user.dmlist.addConverse',
|
||||
{
|
||||
converseId,
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await broker.call(
|
||||
'user.dmlist.addConverse',
|
||||
{
|
||||
converseId,
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
const res = await service.adapter.model.findOne({
|
||||
userId,
|
||||
});
|
||||
|
||||
expect(res.converseIds.map((r) => String(r))).toEqual([converseId]); // 应该被成功插入
|
||||
} finally {
|
||||
await service.adapter.model.deleteOne({
|
||||
userId,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('addConverse can be add more', async () => {
|
||||
const userId = String(new Types.ObjectId());
|
||||
const converseId = String(new Types.ObjectId());
|
||||
const converseId2 = String(new Types.ObjectId());
|
||||
|
||||
await broker.call(
|
||||
'user.dmlist.addConverse',
|
||||
{
|
||||
converseId,
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await broker.call(
|
||||
'user.dmlist.addConverse',
|
||||
{
|
||||
converseId: converseId2,
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
const res = await service.adapter.model.findOne({
|
||||
userId,
|
||||
});
|
||||
|
||||
expect(res.converseIds.map((r) => String(r))).toEqual([
|
||||
converseId,
|
||||
converseId2,
|
||||
]);
|
||||
} finally {
|
||||
await service.adapter.model.deleteOne({
|
||||
userId,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('Test "user.dmlist.removeConverse"', async () => {
|
||||
const userId = String(new Types.ObjectId());
|
||||
const converseId = new Types.ObjectId();
|
||||
|
||||
await insertTestData({
|
||||
userId,
|
||||
converseIds: [converseId],
|
||||
});
|
||||
|
||||
expect(
|
||||
(await service.adapter.model.findOne({ userId })).converseIds.length
|
||||
).toBe(1);
|
||||
|
||||
await broker.call(
|
||||
'user.dmlist.removeConverse',
|
||||
{
|
||||
converseId: String(converseId),
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(
|
||||
(await service.adapter.model.findOne({ userId })).converseIds.length
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
test('Test "user.dmlist.getAllConverse"', async () => {
|
||||
const userId = String(new Types.ObjectId());
|
||||
|
||||
const testData = await insertTestData({
|
||||
userId,
|
||||
converseIds: [new Types.ObjectId()],
|
||||
});
|
||||
|
||||
const converseIds: UserDMList = await broker.call(
|
||||
'user.dmlist.getAllConverse',
|
||||
{},
|
||||
{
|
||||
meta: {
|
||||
userId,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(converseIds).toEqual([...testData.converseIds]);
|
||||
});
|
||||
});
|
||||
194
server/test/integration/user/user.spec.ts
Normal file
194
server/test/integration/user/user.spec.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
import { generateRandomStr, getEmailAddress } from '../../../lib/utils';
|
||||
import UserService from '../../../services/core/user/user.service';
|
||||
import { createTestServiceBroker } from '../../utils';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import type { UserDocument } from '../../../models/user/user';
|
||||
|
||||
/**
|
||||
* 创建测试用户
|
||||
*/
|
||||
function createTestUser(email = 'foo@bar.com') {
|
||||
return {
|
||||
email,
|
||||
nickname: getEmailAddress(email),
|
||||
password: bcrypt.hashSync('123456'),
|
||||
avatar: null,
|
||||
discriminator: '0000',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建正常用户
|
||||
*/
|
||||
function createTestTemporaryUser() {
|
||||
return {
|
||||
email: `${generateRandomStr()}.temporary@msgbyte.com`,
|
||||
nickname: generateRandomStr(),
|
||||
password: bcrypt.hashSync('123456'),
|
||||
avatar: null,
|
||||
discriminator: '0000',
|
||||
temporary: true,
|
||||
};
|
||||
}
|
||||
|
||||
describe('Test "user" service', () => {
|
||||
const { broker, service, insertTestData } =
|
||||
createTestServiceBroker<UserService>(UserService);
|
||||
|
||||
test('Test "user.register"', async () => {
|
||||
const params = {
|
||||
email: 'test@example.com',
|
||||
password: '123456',
|
||||
};
|
||||
const user: any = await broker.call('user.register', params);
|
||||
|
||||
try {
|
||||
expect(user.email).toBe(params.email);
|
||||
expect(user.avatar).toBe(null);
|
||||
expect(user.nickname).toBe(getEmailAddress(params.email));
|
||||
} finally {
|
||||
await service.adapter.removeById(user._id);
|
||||
}
|
||||
});
|
||||
|
||||
test('Test "user.createTemporaryUser"', async () => {
|
||||
const nickname = generateRandomStr();
|
||||
const params = {
|
||||
nickname,
|
||||
};
|
||||
const user: any = await broker.call('user.createTemporaryUser', params);
|
||||
|
||||
try {
|
||||
expect(user).toHaveProperty('nickname', nickname);
|
||||
expect(user).toHaveProperty('discriminator');
|
||||
expect(user).toHaveProperty('token');
|
||||
expect(user).toHaveProperty('temporary', true);
|
||||
expect(String(user.email).endsWith('.temporary@msgbyte.com'));
|
||||
} finally {
|
||||
await service.adapter.removeById(user._id);
|
||||
}
|
||||
});
|
||||
|
||||
test('Test "user.claimTemporaryUser"', async () => {
|
||||
const testDoc = await insertTestData(createTestTemporaryUser());
|
||||
const email = `${generateRandomStr()}@msgbyte.com`;
|
||||
const password = '654321';
|
||||
|
||||
const newUser: any = await broker.call('user.claimTemporaryUser', {
|
||||
userId: String(testDoc._id),
|
||||
email,
|
||||
password,
|
||||
});
|
||||
|
||||
expect(newUser).toHaveProperty('nickname', testDoc.nickname); // 昵称不变
|
||||
expect(newUser).toHaveProperty('email', email);
|
||||
expect(newUser).toHaveProperty('password');
|
||||
expect(bcrypt.compareSync(password, newUser.password)).toBe(true); // 校验密码修改是否正确
|
||||
expect(newUser).toHaveProperty('token');
|
||||
expect(newUser).toHaveProperty('temporary', false);
|
||||
});
|
||||
|
||||
test('Test "user.searchUserWithUniqueName"', async () => {
|
||||
const testDoc = await insertTestData(createTestUser());
|
||||
|
||||
const res: UserDocument = await broker.call(
|
||||
'user.searchUserWithUniqueName',
|
||||
{
|
||||
uniqueName: testDoc.nickname + '#' + testDoc.discriminator,
|
||||
}
|
||||
);
|
||||
|
||||
expect(res).not.toBe(null);
|
||||
expect(res.nickname).toBe(testDoc.nickname);
|
||||
expect(res).not.toHaveProperty('password');
|
||||
});
|
||||
|
||||
test('Test "user.updateUserExtra"', async () => {
|
||||
const testUser = await insertTestData(createTestUser());
|
||||
|
||||
const res = await broker.call(
|
||||
'user.updateUserExtra',
|
||||
{
|
||||
fieldName: 'foo',
|
||||
fieldValue: 'bar',
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(testUser._id),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(res).toMatchObject({
|
||||
extra: {
|
||||
foo: 'bar',
|
||||
},
|
||||
});
|
||||
|
||||
const res2 = await broker.call(
|
||||
'user.updateUserExtra',
|
||||
{
|
||||
fieldName: 'foo',
|
||||
fieldValue: 'baz',
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(testUser._id),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(res2).toMatchObject({
|
||||
extra: {
|
||||
foo: 'baz',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('Test "user.setUserSettings"', async () => {
|
||||
const testUser = await insertTestData(createTestUser());
|
||||
|
||||
const res = await broker.call(
|
||||
'user.setUserSettings',
|
||||
{
|
||||
settings: {
|
||||
foo: 'aaa',
|
||||
bar: 233,
|
||||
},
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(testUser._id),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(res).toEqual({
|
||||
foo: 'aaa',
|
||||
bar: 233,
|
||||
});
|
||||
|
||||
// and can be merge
|
||||
|
||||
const res2 = await broker.call(
|
||||
'user.setUserSettings',
|
||||
{
|
||||
settings: {
|
||||
foo: 'bbb',
|
||||
baz: 963,
|
||||
},
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
userId: String(testUser._id),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
expect(res2).toEqual({
|
||||
foo: 'bbb',
|
||||
bar: 233,
|
||||
baz: 963,
|
||||
});
|
||||
});
|
||||
});
|
||||
6
server/test/setup.ts
Normal file
6
server/test/setup.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
/**
|
||||
* 读取.env环境变量配置文件
|
||||
*/
|
||||
dotenv.config();
|
||||
110
server/test/utils.ts
Normal file
110
server/test/utils.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
import type { DocumentType } from '@typegoose/typegoose';
|
||||
import { config, TcService, TcBroker } from 'tailchat-server-sdk';
|
||||
|
||||
interface TestServiceBrokerOptions {
|
||||
contextCallMockFn?: (actionName: string, params: any, opts?: any) => void;
|
||||
}
|
||||
|
||||
type MockedService<T extends TcService> = T & {
|
||||
roomcastNotify: jest.Mock;
|
||||
cleanActionCache: jest.Mock;
|
||||
};
|
||||
|
||||
/**
|
||||
* 常见一个测试微服务节点
|
||||
* @param serviceCls 微服务类
|
||||
*/
|
||||
export function createTestServiceBroker<T extends TcService = TcService>(
|
||||
serviceCls: typeof TcService,
|
||||
options?: TestServiceBrokerOptions
|
||||
): {
|
||||
broker: TcBroker;
|
||||
contextCallMock: jest.Mock;
|
||||
service: MockedService<T>;
|
||||
insertTestData: <E, R extends E = E>(
|
||||
entity: E
|
||||
) => Promise<DocumentType<R & { _id: string }>>;
|
||||
} {
|
||||
const broker = new TcBroker({ logger: false });
|
||||
const service = broker.createService(serviceCls) as MockedService<T>;
|
||||
const testDataStack = [];
|
||||
const contextCallMock = jest.fn(options?.contextCallMockFn);
|
||||
|
||||
broker.ContextFactory = class extends broker.ContextFactory {
|
||||
call = contextCallMock as any;
|
||||
} as any;
|
||||
|
||||
// Mock
|
||||
service.roomcastNotify = jest.fn();
|
||||
service.cleanActionCache = jest.fn();
|
||||
|
||||
beforeAll(async () => {
|
||||
await broker.start();
|
||||
});
|
||||
afterAll(async () => {
|
||||
await Promise.all(
|
||||
testDataStack.map((item) => {
|
||||
if (typeof service.adapter !== 'object') {
|
||||
throw new Error('无法调用 insertTestData');
|
||||
}
|
||||
|
||||
return service.adapter.removeById(item._id);
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
console.log(`已清理 ${testDataStack.length} 条测试数据`);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('测试数据清理失败:', err);
|
||||
});
|
||||
|
||||
await broker.stop();
|
||||
});
|
||||
|
||||
const insertTestData = async (entity: any) => {
|
||||
if (typeof service.adapter !== 'object') {
|
||||
throw new Error('无法调用 insertTestData');
|
||||
}
|
||||
const doc = await service.adapter.insert(entity);
|
||||
testDataStack.push(doc);
|
||||
return doc;
|
||||
};
|
||||
|
||||
return {
|
||||
broker,
|
||||
contextCallMock,
|
||||
service,
|
||||
insertTestData,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建用户Token
|
||||
*/
|
||||
export function createTestUserToken(
|
||||
user: {
|
||||
_id: string;
|
||||
username: string;
|
||||
email: string;
|
||||
avatar: string;
|
||||
} = {
|
||||
_id: '',
|
||||
username: 'test',
|
||||
email: 'test',
|
||||
avatar: '',
|
||||
}
|
||||
): string {
|
||||
return jwt.sign(
|
||||
{
|
||||
_id: user._id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
avatar: user.avatar,
|
||||
},
|
||||
config.secret,
|
||||
{
|
||||
expiresIn: '30d',
|
||||
}
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user