mm_api 0.0.0-stage → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/com.json +5 -0
- package/config.tpl.json +41 -0
- package/examples/README.md +39 -0
- package/examples/basic.js +37 -0
- package/examples/hello_api/api.json +18 -0
- package/examples/hello_api/index.js +14 -0
- package/examples/helper.js +28 -0
- package/examples/param_api/api.json +17 -0
- package/examples/param_api/index.js +14 -0
- package/examples/param_api/param.json +19 -0
- package/examples/param_api/run.js +38 -0
- package/examples/sql_api/api.json +16 -0
- package/examples/sql_api/index.js +49 -0
- package/examples/sql_api/run.js +33 -0
- package/index.js +21 -0
- package/lib/api.js +927 -0
- package/lib/manager.js +251 -0
- package/lib/oauth.js +224 -0
- package/package.json +32 -4
- package/script.tpl.js +32 -0
- package/test.js +18 -0
- package/tests/api.test.js +167 -0
- package/tests/fixtures/echo.js +14 -0
- package/tests/fixtures/error.js +13 -0
- package/tests/fixtures/hello.js +13 -0
- package/tests/fixtures/param.json +19 -0
- package/tests/helper.js +57 -0
- package/tests/index.test.js +45 -0
- package/tests/manager.test.js +184 -0
- package/tests/oauth.test.js +107 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Api 接口驱动测试
|
|
5
|
+
* @description 覆盖脚本加载、请求执行、方法过滤、参数校验与异常路径
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const test = require('node:test');
|
|
9
|
+
const assert = require('node:assert/strict');
|
|
10
|
+
const Api = require('../lib/api.js');
|
|
11
|
+
const { makeCtx, makeOptions } = require('./helper.js');
|
|
12
|
+
|
|
13
|
+
test('load 加载脚本并挂载 main', () => {
|
|
14
|
+
const api = new Api({
|
|
15
|
+
name: 'hello',
|
|
16
|
+
path: '/hello',
|
|
17
|
+
dir: __dirname,
|
|
18
|
+
main: './fixtures/hello.js'
|
|
19
|
+
});
|
|
20
|
+
assert.strictEqual(api.isLoaded(), false, '初始未加载');
|
|
21
|
+
const mod = api.load();
|
|
22
|
+
assert.ok(mod, 'load 应返回脚本模块');
|
|
23
|
+
assert.strictEqual(api.isLoaded(), true, '加载后应标记已加载');
|
|
24
|
+
assert.strictEqual(typeof api.main, 'function', 'main 应挂载为函数');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('load 不存在的脚本返回 null', () => {
|
|
28
|
+
const api = new Api({
|
|
29
|
+
name: 'miss',
|
|
30
|
+
path: '/miss',
|
|
31
|
+
dir: __dirname,
|
|
32
|
+
main: './fixtures/not_exist.js'
|
|
33
|
+
});
|
|
34
|
+
const mod = api.load();
|
|
35
|
+
assert.strictEqual(mod, null);
|
|
36
|
+
assert.strictEqual(api.isLoaded(), false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('run 执行脚本返回 JSON 字符串', async () => {
|
|
40
|
+
const api = new Api({
|
|
41
|
+
name: 'hello',
|
|
42
|
+
path: '/hello',
|
|
43
|
+
method: 'ALL',
|
|
44
|
+
dir: __dirname,
|
|
45
|
+
main: './fixtures/hello.js'
|
|
46
|
+
});
|
|
47
|
+
assert.ok(api.load(), '应先加载脚本');
|
|
48
|
+
const ctx = makeCtx({ path: '/hello', method: 'GET' });
|
|
49
|
+
const ret = await api.run({}, ctx, makeOptions());
|
|
50
|
+
assert.ok(ret, 'run 应返回结果');
|
|
51
|
+
assert.deepStrictEqual(JSON.parse(ret), {
|
|
52
|
+
code: 0,
|
|
53
|
+
message: '成功',
|
|
54
|
+
data: { hello: 'world' }
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('run 回显查询参数与正文', async () => {
|
|
59
|
+
const api = new Api({
|
|
60
|
+
name: 'echo',
|
|
61
|
+
path: '/echo',
|
|
62
|
+
method: 'ALL',
|
|
63
|
+
dir: __dirname,
|
|
64
|
+
main: './fixtures/echo.js'
|
|
65
|
+
});
|
|
66
|
+
assert.ok(api.load(), '应先加载脚本');
|
|
67
|
+
const ctx = makeCtx({
|
|
68
|
+
path: '/echo',
|
|
69
|
+
method: 'POST',
|
|
70
|
+
query: { a: '1' },
|
|
71
|
+
body: { b: '2' }
|
|
72
|
+
});
|
|
73
|
+
const ret = await api.run({}, ctx, makeOptions());
|
|
74
|
+
assert.deepStrictEqual(JSON.parse(ret), {
|
|
75
|
+
query: { a: '1' },
|
|
76
|
+
body: { b: '2' }
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('run 方法不匹配返回 null', async () => {
|
|
81
|
+
const api = new Api({
|
|
82
|
+
name: 'hello',
|
|
83
|
+
path: '/hello',
|
|
84
|
+
method: 'POST',
|
|
85
|
+
dir: __dirname,
|
|
86
|
+
main: './fixtures/hello.js'
|
|
87
|
+
});
|
|
88
|
+
const ctx = makeCtx({ path: '/hello', method: 'GET' });
|
|
89
|
+
const ret = await api.run({}, ctx, makeOptions());
|
|
90
|
+
assert.strictEqual(ret, null);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('run 脚本抛错返回错误响应', async () => {
|
|
94
|
+
const api = new Api({
|
|
95
|
+
name: 'error',
|
|
96
|
+
path: '/error',
|
|
97
|
+
method: 'ALL',
|
|
98
|
+
dir: __dirname,
|
|
99
|
+
main: './fixtures/error.js'
|
|
100
|
+
});
|
|
101
|
+
const ctx = makeCtx({ path: '/error', method: 'GET' });
|
|
102
|
+
const ret = await api.run({}, ctx, makeOptions());
|
|
103
|
+
assert.ok(ret, 'run 应返回结果');
|
|
104
|
+
const obj = JSON.parse(ret);
|
|
105
|
+
assert.ok(obj.error, '应返回错误对象');
|
|
106
|
+
assert.strictEqual(obj.error.code, 10000);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('main 未定义脚本返回错误响应', async () => {
|
|
110
|
+
const api = new Api({
|
|
111
|
+
name: 'hello',
|
|
112
|
+
path: '/hello',
|
|
113
|
+
method: 'ALL',
|
|
114
|
+
dir: __dirname,
|
|
115
|
+
main: './fixtures/hello.js'
|
|
116
|
+
});
|
|
117
|
+
// 覆盖 main 为 null,模拟脚本无主函数
|
|
118
|
+
api.main = null;
|
|
119
|
+
const ctx = makeCtx({ path: '/hello', method: 'GET' });
|
|
120
|
+
const ret = await api.run({}, ctx, makeOptions());
|
|
121
|
+
assert.ok(ret);
|
|
122
|
+
const obj = JSON.parse(ret);
|
|
123
|
+
assert.ok(obj.error, '应返回错误对象');
|
|
124
|
+
assert.strictEqual(obj.error.code, 10000);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('加载参数配置并校验通过', () => {
|
|
128
|
+
const api = new Api({
|
|
129
|
+
name: 'param',
|
|
130
|
+
path: '/param',
|
|
131
|
+
method: 'ALL',
|
|
132
|
+
dir: __dirname,
|
|
133
|
+
param_path: './fixtures/param.json'
|
|
134
|
+
});
|
|
135
|
+
assert.ok(api.param, '应加载参数驱动');
|
|
136
|
+
const error = api.checkParam({ name: '张三' }, {}, 'GET');
|
|
137
|
+
assert.strictEqual(error, null, '参数正确时校验应通过');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('参数校验失败返回错误', () => {
|
|
141
|
+
const api = new Api({
|
|
142
|
+
name: 'param',
|
|
143
|
+
path: '/param',
|
|
144
|
+
method: 'ALL',
|
|
145
|
+
dir: __dirname,
|
|
146
|
+
param_path: './fixtures/param.json'
|
|
147
|
+
});
|
|
148
|
+
assert.ok(api.param, '应加载参数驱动');
|
|
149
|
+
const error = api.checkParam({}, {}, 'GET');
|
|
150
|
+
assert.ok(error, '缺少必填参数应返回错误');
|
|
151
|
+
assert.ok(error.error, '错误应为响应格式');
|
|
152
|
+
assert.strictEqual(error.error.code, 30001, '必填缺失错误码应为 30001');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('check 包含参数校验与身份校验', async () => {
|
|
156
|
+
const api = new Api({
|
|
157
|
+
name: 'hello',
|
|
158
|
+
path: '/hello',
|
|
159
|
+
method: 'ALL',
|
|
160
|
+
dir: __dirname,
|
|
161
|
+
main: './fixtures/hello.js',
|
|
162
|
+
check_param: false
|
|
163
|
+
});
|
|
164
|
+
const ctx = makeCtx({ path: '/hello', method: 'GET' });
|
|
165
|
+
const error = await api.check({}, ctx, makeOptions());
|
|
166
|
+
assert.strictEqual(error, null, '无需登录且无参数校验时应通过');
|
|
167
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
/**
|
|
5
|
+
* 接口主函数
|
|
6
|
+
* @param {object} ctx HTTP上下文
|
|
7
|
+
* @param {object} options 数据管理器
|
|
8
|
+
* @returns {object} 执行结果
|
|
9
|
+
*/
|
|
10
|
+
async main(params, ctx, options) {
|
|
11
|
+
const req = ctx.request;
|
|
12
|
+
return { query: req.query, body: req.body };
|
|
13
|
+
}
|
|
14
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
/**
|
|
5
|
+
* 接口主函数
|
|
6
|
+
* @param {object} ctx HTTP上下文
|
|
7
|
+
* @param {object} options 数据管理器
|
|
8
|
+
* @returns {object} 执行结果
|
|
9
|
+
*/
|
|
10
|
+
async main(params, ctx, options) {
|
|
11
|
+
return { code: 0, message: '成功', data: { hello: 'world' } };
|
|
12
|
+
}
|
|
13
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "param",
|
|
3
|
+
"title": "参数配置",
|
|
4
|
+
"get": {
|
|
5
|
+
"query": ["name"],
|
|
6
|
+
"query_required": ["name"]
|
|
7
|
+
},
|
|
8
|
+
"list": [
|
|
9
|
+
{
|
|
10
|
+
"name": "name",
|
|
11
|
+
"title": "名称",
|
|
12
|
+
"type": "string",
|
|
13
|
+
"string": {
|
|
14
|
+
"not_empty": true,
|
|
15
|
+
"max": 10
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
package/tests/helper.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 构造模拟 HTTP 请求上下文
|
|
5
|
+
* @param {object} cfg 配置项
|
|
6
|
+
* @param {string} cfg.path 请求路径
|
|
7
|
+
* @param {string} cfg.method 请求方法
|
|
8
|
+
* @param {object} cfg.query URL 参数
|
|
9
|
+
* @param {object} cfg.body 正文参数
|
|
10
|
+
* @param {object} cfg.headers 请求头
|
|
11
|
+
* @param {string} cfg.type 请求类型
|
|
12
|
+
* @returns {object} 请求上下文
|
|
13
|
+
*/
|
|
14
|
+
function makeCtx(cfg = {}) {
|
|
15
|
+
const req = {
|
|
16
|
+
path: cfg.path || '/',
|
|
17
|
+
method: cfg.method || 'GET',
|
|
18
|
+
query: cfg.query || {},
|
|
19
|
+
body: cfg.body || {},
|
|
20
|
+
headers: cfg.headers || {},
|
|
21
|
+
type: cfg.type || 'json',
|
|
22
|
+
url: cfg.url || (cfg.path || '/')
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
request: req,
|
|
26
|
+
response: { type: '' },
|
|
27
|
+
session: {},
|
|
28
|
+
headers: req.headers,
|
|
29
|
+
set(key, value) {
|
|
30
|
+
this.response[key] = value;
|
|
31
|
+
},
|
|
32
|
+
get status() {
|
|
33
|
+
return this.response.status;
|
|
34
|
+
},
|
|
35
|
+
set status(value) {
|
|
36
|
+
this.response.status = value;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 构造数据管理器
|
|
43
|
+
* @returns {object} 数据管理器
|
|
44
|
+
*/
|
|
45
|
+
function makeOptions() {
|
|
46
|
+
return {
|
|
47
|
+
ret: null,
|
|
48
|
+
new() {
|
|
49
|
+
return this;
|
|
50
|
+
},
|
|
51
|
+
getObj() {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { makeCtx, makeOptions };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* index.js 导出与工厂函数测试
|
|
5
|
+
* @description 覆盖模块入口的全部导出与 apiAdmin / admin 分支
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const test = require('node:test');
|
|
9
|
+
const assert = require('node:assert/strict');
|
|
10
|
+
const mod = require('../index.js');
|
|
11
|
+
|
|
12
|
+
test('导出结构', () => {
|
|
13
|
+
assert.ok(mod.Manager, '应导出 Manager');
|
|
14
|
+
assert.ok(mod.admin, '应导出 admin');
|
|
15
|
+
assert.strictEqual(typeof mod.apiAdmin, 'function', '应导出 apiAdmin 函数');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('apiAdmin 无参时使用 os 作用域', () => {
|
|
19
|
+
const inst = mod.apiAdmin();
|
|
20
|
+
assert.ok(inst instanceof mod.Manager, '应返回 Manager 实例');
|
|
21
|
+
assert.strictEqual(inst.config.name, 'os');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('apiAdmin 指定作用域创建实例', () => {
|
|
25
|
+
const inst = mod.apiAdmin('test_scope');
|
|
26
|
+
assert.ok(inst instanceof mod.Manager, '应返回 Manager 实例');
|
|
27
|
+
assert.strictEqual(inst.config.name, 'test_scope');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('apiAdmin 同作用域返回缓存实例', () => {
|
|
31
|
+
const first = mod.apiAdmin('cached_scope');
|
|
32
|
+
assert.strictEqual(mod.apiAdmin('cached_scope'), first, '同作用域应返回同一实例');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('apiAdmin 传入配置可覆盖标题', () => {
|
|
36
|
+
const inst = mod.apiAdmin('title_scope', { title: '自定义标题' });
|
|
37
|
+
assert.strictEqual(inst.config.name, 'title_scope');
|
|
38
|
+
assert.strictEqual(inst.config.title, '自定义标题');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('admin.get 返回 Manager 实例', () => {
|
|
42
|
+
const inst = mod.admin.get('admin_scope');
|
|
43
|
+
assert.ok(inst instanceof mod.Manager, 'admin.get 应返回 Manager 实例');
|
|
44
|
+
assert.strictEqual(inst.config.name, 'admin_scope');
|
|
45
|
+
});
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ApiManager 管理器测试
|
|
5
|
+
* @description 覆盖接口驱动的注册、注销、路径匹配、排序与路由执行
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const test = require('node:test');
|
|
9
|
+
const assert = require('node:assert/strict');
|
|
10
|
+
const { Manager, apiAdmin } = require('../index.js');
|
|
11
|
+
const { makeCtx, makeOptions } = require('./helper.js');
|
|
12
|
+
|
|
13
|
+
test('add 注册接口驱动', () => {
|
|
14
|
+
const mgr = apiAdmin('mgr_add');
|
|
15
|
+
const drive = mgr.add({
|
|
16
|
+
name: 'hello',
|
|
17
|
+
path: '/hello',
|
|
18
|
+
method: 'ALL',
|
|
19
|
+
dir: __dirname,
|
|
20
|
+
main: './fixtures/hello.js'
|
|
21
|
+
});
|
|
22
|
+
assert.ok(drive, 'add 应返回驱动实例');
|
|
23
|
+
assert.ok(mgr.has('hello'), '注册后应能查到驱动');
|
|
24
|
+
assert.strictEqual(mgr.size(), 1);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('get 获取接口驱动', () => {
|
|
28
|
+
const mgr = apiAdmin('mgr_get');
|
|
29
|
+
mgr.add({
|
|
30
|
+
name: 'hello',
|
|
31
|
+
path: '/hello',
|
|
32
|
+
method: 'ALL',
|
|
33
|
+
dir: __dirname,
|
|
34
|
+
main: './fixtures/hello.js'
|
|
35
|
+
});
|
|
36
|
+
const drive = mgr.get('hello');
|
|
37
|
+
assert.ok(drive, 'get 应返回驱动实例');
|
|
38
|
+
assert.strictEqual(drive.config.name, 'hello');
|
|
39
|
+
assert.strictEqual(drive.config.path, '/hello');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('del 注销接口驱动', async () => {
|
|
43
|
+
const mgr = apiAdmin('mgr_del');
|
|
44
|
+
mgr.add({
|
|
45
|
+
name: 'hello',
|
|
46
|
+
path: '/hello',
|
|
47
|
+
method: 'ALL',
|
|
48
|
+
dir: __dirname,
|
|
49
|
+
main: './fixtures/hello.js'
|
|
50
|
+
});
|
|
51
|
+
const bl = await mgr.del('hello');
|
|
52
|
+
assert.strictEqual(bl, true, 'del 应返回 true');
|
|
53
|
+
assert.strictEqual(mgr.has('hello'), false, '注销后驱动应不存在');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('isMatch 路径匹配', () => {
|
|
57
|
+
const mgr = new Manager({ name: 'isMatch' });
|
|
58
|
+
// 精确匹配
|
|
59
|
+
assert.strictEqual(mgr.isMatch('/hello', '/hello'), true);
|
|
60
|
+
// 尾通配:/api/* 匹配 /api/hello
|
|
61
|
+
assert.strictEqual(mgr.isMatch('/api/hello', '/api/*'), true);
|
|
62
|
+
// 头通配:*hello 匹配 /hello
|
|
63
|
+
assert.strictEqual(mgr.isMatch('/hello', '*hello'), true);
|
|
64
|
+
// 头尾通配:*hello* 包含匹配
|
|
65
|
+
assert.strictEqual(mgr.isMatch('/api/hello', '*hello*'), true);
|
|
66
|
+
// 不匹配
|
|
67
|
+
assert.strictEqual(mgr.isMatch('/other', '/hello'), false);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('_sortedDrives 按路径长度降序排序', () => {
|
|
71
|
+
const mgr = apiAdmin('mgr_sort');
|
|
72
|
+
mgr.add({
|
|
73
|
+
name: 'short',
|
|
74
|
+
path: '/a',
|
|
75
|
+
method: 'ALL',
|
|
76
|
+
state: 1,
|
|
77
|
+
sort: 1,
|
|
78
|
+
dir: __dirname,
|
|
79
|
+
main: './fixtures/hello.js'
|
|
80
|
+
});
|
|
81
|
+
mgr.add({
|
|
82
|
+
name: 'long',
|
|
83
|
+
path: '/a/b',
|
|
84
|
+
method: 'ALL',
|
|
85
|
+
state: 1,
|
|
86
|
+
sort: 1,
|
|
87
|
+
dir: __dirname,
|
|
88
|
+
main: './fixtures/hello.js'
|
|
89
|
+
});
|
|
90
|
+
const drives = mgr._sortedDrives();
|
|
91
|
+
assert.strictEqual(drives[0].config.name, 'long', '路径长的应优先');
|
|
92
|
+
assert.strictEqual(drives[1].config.name, 'short');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('_sortedDrives 过滤未启用驱动', () => {
|
|
96
|
+
const mgr = apiAdmin('mgr_state');
|
|
97
|
+
mgr.add({
|
|
98
|
+
name: 'disabled',
|
|
99
|
+
path: '/a',
|
|
100
|
+
method: 'ALL',
|
|
101
|
+
state: 0,
|
|
102
|
+
dir: __dirname,
|
|
103
|
+
main: './fixtures/hello.js'
|
|
104
|
+
});
|
|
105
|
+
mgr.add({
|
|
106
|
+
name: 'enabled',
|
|
107
|
+
path: '/b',
|
|
108
|
+
method: 'ALL',
|
|
109
|
+
state: 1,
|
|
110
|
+
dir: __dirname,
|
|
111
|
+
main: './fixtures/hello.js'
|
|
112
|
+
});
|
|
113
|
+
const drives = mgr._sortedDrives();
|
|
114
|
+
assert.strictEqual(drives.length, 1);
|
|
115
|
+
assert.strictEqual(drives[0].config.name, 'enabled');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('run 匹配路由并返回脚本结果', async () => {
|
|
119
|
+
const mgr = apiAdmin('mgr_run');
|
|
120
|
+
mgr.add({
|
|
121
|
+
name: 'hello',
|
|
122
|
+
path: '/hello',
|
|
123
|
+
method: 'ALL',
|
|
124
|
+
dir: __dirname,
|
|
125
|
+
main: './fixtures/hello.js'
|
|
126
|
+
});
|
|
127
|
+
const ctx = makeCtx({ path: '/hello', method: 'GET' });
|
|
128
|
+
const options = makeOptions();
|
|
129
|
+
const ret = await mgr.run(ctx, options);
|
|
130
|
+
assert.ok(ret, 'run 应返回结果');
|
|
131
|
+
assert.deepStrictEqual(JSON.parse(ret), {
|
|
132
|
+
code: 0,
|
|
133
|
+
message: '成功',
|
|
134
|
+
data: { hello: 'world' }
|
|
135
|
+
});
|
|
136
|
+
assert.strictEqual(options.ret, ret, '结果应写入数据管理器 ret');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('run 方法不匹配时返回 null', async () => {
|
|
140
|
+
const mgr = apiAdmin('mgr_method');
|
|
141
|
+
mgr.add({
|
|
142
|
+
name: 'hello',
|
|
143
|
+
path: '/hello',
|
|
144
|
+
method: 'POST',
|
|
145
|
+
dir: __dirname,
|
|
146
|
+
main: './fixtures/hello.js'
|
|
147
|
+
});
|
|
148
|
+
const ctx = makeCtx({ path: '/hello', method: 'GET' });
|
|
149
|
+
const ret = await mgr.run(ctx, makeOptions());
|
|
150
|
+
assert.strictEqual(ret, null);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('run 路径不匹配时返回 null', async () => {
|
|
154
|
+
const mgr = apiAdmin('mgr_path');
|
|
155
|
+
mgr.add({
|
|
156
|
+
name: 'hello',
|
|
157
|
+
path: '/hello',
|
|
158
|
+
method: 'ALL',
|
|
159
|
+
dir: __dirname,
|
|
160
|
+
main: './fixtures/hello.js'
|
|
161
|
+
});
|
|
162
|
+
const ctx = makeCtx({ path: '/other', method: 'GET' });
|
|
163
|
+
const ret = await mgr.run(ctx, makeOptions());
|
|
164
|
+
assert.strictEqual(ret, null);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('reload 重启接口驱动', async () => {
|
|
168
|
+
const mgr = apiAdmin('mgr_reload');
|
|
169
|
+
mgr.add({
|
|
170
|
+
name: 'hello',
|
|
171
|
+
path: '/hello',
|
|
172
|
+
method: 'ALL',
|
|
173
|
+
dir: __dirname,
|
|
174
|
+
main: './fixtures/hello.js'
|
|
175
|
+
});
|
|
176
|
+
const bl = await mgr.reload('hello');
|
|
177
|
+
assert.strictEqual(bl, true, 'reload 应返回 true');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('reload 不存在的驱动返回 false', async () => {
|
|
181
|
+
const mgr = apiAdmin('mgr_reload_miss');
|
|
182
|
+
const bl = await mgr.reload('not_exist');
|
|
183
|
+
assert.strictEqual(bl, false);
|
|
184
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Oauth 身份验证测试
|
|
5
|
+
* @description 覆盖免登录、登录校验与权限级别校验
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const test = require('node:test');
|
|
9
|
+
const assert = require('node:assert/strict');
|
|
10
|
+
const Oauth = require('../lib/oauth.js');
|
|
11
|
+
const { makeCtx } = require('./helper.js');
|
|
12
|
+
|
|
13
|
+
test('无需登录时 check 返回 null', async () => {
|
|
14
|
+
const oauth = new Oauth({ sign_in: false });
|
|
15
|
+
const ctx = makeCtx({ path: '/hello', method: 'GET' });
|
|
16
|
+
const ret = await oauth.check(ctx);
|
|
17
|
+
assert.strictEqual(ret, null);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('需登录但未登录时返回 60000', async () => {
|
|
21
|
+
const oauth = new Oauth({ sign_in: true });
|
|
22
|
+
const ctx = makeCtx({ path: '/hello', method: 'GET' });
|
|
23
|
+
const ret = await oauth.check(ctx);
|
|
24
|
+
assert.ok(ret, '应返回错误对象');
|
|
25
|
+
assert.strictEqual(ret.code, 60000);
|
|
26
|
+
assert.strictEqual(ret.message, '账户未登录!');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('已登录且权限足够时返回 null', async () => {
|
|
30
|
+
const oauth = new Oauth({
|
|
31
|
+
sign_in: true,
|
|
32
|
+
vip: 1,
|
|
33
|
+
gm: 1,
|
|
34
|
+
mc: 1,
|
|
35
|
+
user_group: [],
|
|
36
|
+
user_admin: []
|
|
37
|
+
});
|
|
38
|
+
const ctx = makeCtx({ path: '/hello', method: 'GET' });
|
|
39
|
+
ctx.session.user = {
|
|
40
|
+
user_id: 1,
|
|
41
|
+
vip: 9,
|
|
42
|
+
gm: 9,
|
|
43
|
+
mc: 9,
|
|
44
|
+
user_group: '',
|
|
45
|
+
user_admin: ''
|
|
46
|
+
};
|
|
47
|
+
const ret = await oauth.check(ctx);
|
|
48
|
+
assert.strictEqual(ret, null, '权限足够时应通过');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('权限不足返回 70000', async () => {
|
|
52
|
+
const oauth = new Oauth({
|
|
53
|
+
sign_in: true,
|
|
54
|
+
gm: 5
|
|
55
|
+
});
|
|
56
|
+
const ctx = makeCtx({ path: '/hello', method: 'GET' });
|
|
57
|
+
ctx.session.user = {
|
|
58
|
+
user_id: 1,
|
|
59
|
+
vip: 9,
|
|
60
|
+
gm: 1,
|
|
61
|
+
mc: 9,
|
|
62
|
+
user_group: '',
|
|
63
|
+
user_admin: ''
|
|
64
|
+
};
|
|
65
|
+
const ret = await oauth.check(ctx);
|
|
66
|
+
assert.ok(ret, '应返回错误对象');
|
|
67
|
+
assert.strictEqual(ret.code, 70000);
|
|
68
|
+
assert.strictEqual(ret.message, '没有访问权限');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('用户组不匹配返回 70000', async () => {
|
|
72
|
+
const oauth = new Oauth({
|
|
73
|
+
sign_in: true,
|
|
74
|
+
user_group: ['1', '5']
|
|
75
|
+
});
|
|
76
|
+
const ctx = makeCtx({ path: '/hello', method: 'GET' });
|
|
77
|
+
ctx.session.user = {
|
|
78
|
+
user_id: 1,
|
|
79
|
+
vip: 9,
|
|
80
|
+
gm: 9,
|
|
81
|
+
mc: 9,
|
|
82
|
+
user_group: '2,3',
|
|
83
|
+
user_admin: ''
|
|
84
|
+
};
|
|
85
|
+
const ret = await oauth.check(ctx);
|
|
86
|
+
assert.ok(ret, '应返回错误对象');
|
|
87
|
+
assert.strictEqual(ret.code, 70000);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('管理组不匹配返回 70000', async () => {
|
|
91
|
+
const oauth = new Oauth({
|
|
92
|
+
sign_in: true,
|
|
93
|
+
user_admin: ['1', '5']
|
|
94
|
+
});
|
|
95
|
+
const ctx = makeCtx({ path: '/hello', method: 'GET' });
|
|
96
|
+
ctx.session.user = {
|
|
97
|
+
user_id: 1,
|
|
98
|
+
vip: 9,
|
|
99
|
+
gm: 9,
|
|
100
|
+
mc: 9,
|
|
101
|
+
user_group: '',
|
|
102
|
+
user_admin: '2,3'
|
|
103
|
+
};
|
|
104
|
+
const ret = await oauth.check(ctx);
|
|
105
|
+
assert.ok(ret, '应返回错误对象');
|
|
106
|
+
assert.strictEqual(ret.code, 70000);
|
|
107
|
+
});
|