mm_api 1.1.0 → 1.1.2
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/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 +2 -2
- package/lib/{drive.js → api.js} +74 -131
- package/lib/manager.js +73 -42
- package/lib/oauth.js +3 -56
- package/package.json +33 -27
- package/script.tpl.js +1 -1
- 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,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
|
+
});
|