typespeed 2.5.0 → 2.5.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.
Files changed (96) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.en.md +169 -0
  3. package/README.md +123 -37
  4. package/dist/default/rabbitmq.class.js +18 -1
  5. package/dist/default/redis.class.js +14 -1
  6. package/dist/default/socket-io.class.js +53 -4
  7. package/dist/typespeed.d.ts +7 -7
  8. package/package.json +7 -2
  9. package/.github/workflows/test.yml +0 -50
  10. package/app/nodemon.json +0 -5
  11. package/app/package.json +0 -17
  12. package/app/src/aop-test.class.ts +0 -29
  13. package/app/src/config-development.json +0 -26
  14. package/app/src/config-production.json +0 -15
  15. package/app/src/config.json +0 -50
  16. package/app/src/custom-log.class.ts +0 -33
  17. package/app/src/entities/mutil-users.class.ts +0 -5
  18. package/app/src/entities/user-dto.class.ts +0 -3
  19. package/app/src/first-page.class.ts +0 -46
  20. package/app/src/jwt-authentication.class.ts +0 -30
  21. package/app/src/main.ts +0 -24
  22. package/app/src/second-page.class.ts +0 -43
  23. package/app/src/test-bind.class.ts +0 -41
  24. package/app/src/test-database.class.ts +0 -81
  25. package/app/src/test-log.class.ts +0 -11
  26. package/app/src/test-mq.class.ts +0 -21
  27. package/app/src/test-orm.class.ts +0 -63
  28. package/app/src/test-redis.class.ts +0 -45
  29. package/app/src/test-request.class.ts +0 -37
  30. package/app/src/test-socket.class.ts +0 -56
  31. package/app/src/user-model.class.ts +0 -50
  32. package/app/src/views/index.html +0 -1
  33. package/app/src/views/socket.html +0 -23
  34. package/app/src/views/upload.html +0 -14
  35. package/app/static/favicon.ico +0 -0
  36. package/app/static/k.jpg +0 -0
  37. package/app/tsconfig.json +0 -20
  38. package/introduction/decorator/example-import.class.ts +0 -6
  39. package/introduction/decorator/main-app.ts +0 -6
  40. package/introduction/decorator/main-decorator.ts +0 -108
  41. package/introduction/decorator/main-import.ts +0 -1
  42. package/introduction/decorator/main-reflect.ts +0 -25
  43. package/introduction/decorator-next/dist/main.js +0 -116
  44. package/introduction/decorator-next/package.json +0 -14
  45. package/introduction/decorator-next/src/main.ts +0 -64
  46. package/introduction/decorator-next/tsconfig.json +0 -17
  47. package/introduction/decorator-next//346/225/231/347/250/213.md +0 -384
  48. package/introduction/tsconfig.json +0 -23
  49. package/introduction/web-services/db-init.ts +0 -39
  50. package/introduction/web-services/db-singleton.ts +0 -59
  51. package/introduction/web-services/http-if.ts +0 -10
  52. package/introduction/web-services/http-init.ts +0 -6
  53. package/introduction/web-services/http-router.ts +0 -28
  54. package/src/bind.decorator.ts +0 -50
  55. package/src/core.decorator.ts +0 -184
  56. package/src/database.decorator.ts +0 -446
  57. package/src/decorator-utils.ts +0 -53
  58. package/src/default/default-authentication.class.ts +0 -10
  59. package/src/default/express-server.class.ts +0 -150
  60. package/src/default/log-default.class.ts +0 -9
  61. package/src/default/node-cache.class.ts +0 -40
  62. package/src/default/pages/404.html +0 -226
  63. package/src/default/pages/500.html +0 -713
  64. package/src/default/rabbitmq.class.ts +0 -61
  65. package/src/default/read-write-db.class.ts +0 -53
  66. package/src/default/redis.class.ts +0 -79
  67. package/src/default/socket-io.class.ts +0 -81
  68. package/src/factory/authentication-factory.class.ts +0 -9
  69. package/src/factory/cache-factory.class.ts +0 -7
  70. package/src/factory/data-source-factory.class.ts +0 -4
  71. package/src/factory/log-factory.class.ts +0 -8
  72. package/src/factory/server-factory.class.ts +0 -6
  73. package/src/route.decorator.ts +0 -334
  74. package/src/scaffold/command.ts +0 -40
  75. package/src/scaffold/templates/front-page.class.ts.tpl +0 -12
  76. package/src/scaffold/templates/main.ts.tpl +0 -13
  77. package/src/scaffold/templates/nodemon.json.tpl +0 -7
  78. package/src/scaffold/templates/package.json.tpl +0 -16
  79. package/src/scaffold/templates/tsconfig.json.tpl +0 -17
  80. package/src/typespeed.d.ts +0 -403
  81. package/src/typespeed.ts +0 -139
  82. package/test/bind.test.ts +0 -28
  83. package/test/database.test.ts +0 -76
  84. package/test/decorator-utils.test.ts +0 -42
  85. package/test/first-page.test.ts +0 -42
  86. package/test/hooks.ts +0 -13
  87. package/test/mq.test.ts +0 -17
  88. package/test/orm.test.ts +0 -66
  89. package/test/redis.test.ts +0 -48
  90. package/test/request.test.ts +0 -51
  91. package/test/second-page.test.ts +0 -50
  92. package/test/socket.test.ts +0 -60
  93. package/test-env/docker-compose.test.yml +0 -31
  94. package/test-env/mysql-init/init.sql +0 -8
  95. package/test-env//346/234/254/345/234/260Docker/346/265/213/350/257/225/346/226/271/346/241/210.md +0 -69
  96. package/tsconfig.json +0 -25
@@ -1,76 +0,0 @@
1
-
2
- const chaiObj = require('chai');
3
- chaiObj.use(require("chai-http"));
4
- const expect = chaiObj.expect;
5
- describe("Test Database", () => {
6
- const randomId = random(3000, 5000);
7
- const testAddr = `http://${process.env.LOCAL_HOST || "localhost"}:8081`;
8
- const testDatabase = [
9
- {
10
- "url": "/db/insert?id=" + randomId,
11
- "expect": "Insert success: " + randomId,
12
- },
13
- {
14
- "url": "/db/insert2?id=" + (randomId + 1),
15
- "expect": "Insert success: " + (randomId + 1),
16
- },
17
- {
18
- "url": "/db/update?id=" + randomId,
19
- "expect": "update success: 1",
20
- },
21
- {
22
- "url": "/db/set-cache?value=zzz",
23
- "expect": "set cache success",
24
- },
25
- {
26
- "url": "/db/get-cache",
27
- "expect": "zzz",
28
- }
29
- ]
30
- testDatabase.forEach((testRequest) => {
31
- it(testRequest.url, (done) => {
32
- chaiObj.request(testAddr).get(testRequest.url).end((err, res) => {
33
- chaiObj.assert.equal(testRequest.expect, res.text);
34
- return done();
35
- });
36
- });
37
- });
38
- it("/db/select-row", (done) => {
39
- chaiObj.request(testAddr).get("/db/select-row?id=" + randomId).end((err, res) => {
40
- const dataList = JSON.parse(res.text);
41
- expect(dataList).to.be.an('array');
42
- expect(dataList[0]).to.have.property("id").which.is.a("number").equal(randomId);
43
- done();
44
- });
45
- });
46
- it("/db/select-row twice", (done) => {
47
- chaiObj.request(testAddr).get("/db/select-row?id=" + randomId).end((err, res) => {
48
- const dataList = JSON.parse(res.text);
49
- expect(dataList).to.be.an('array');
50
- expect(dataList[0]).to.have.property("id").which.is.a("number").equal(randomId);
51
- done();
52
- });
53
- });
54
- it("/db/select", (done) => {
55
- chaiObj.request(testAddr).get("/db/select").end((err, res) => {
56
- const dataList = JSON.parse(res.text);
57
- expect(dataList).to.be.an('array');
58
- expect(dataList[0]).to.have.property('id');
59
- done();
60
- });
61
- });
62
- it("/db/select-user", (done) => {
63
- chaiObj.request(testAddr).get("/db/select-user").end((err, res) => {
64
- const dataList = JSON.parse(res.text);
65
- expect(dataList).to.be.an('array');
66
- expect(dataList[0]).to.have.property('id');
67
- done();
68
- });
69
- });
70
- });
71
-
72
- function random(min, max) {
73
- return Math.round(Math.random() * (max - min)) + min;
74
- }
75
-
76
- export {};
@@ -1,42 +0,0 @@
1
- import { expect } from "chai";
2
- import { isStd, getStdArgs } from "../src/decorator-utils";
3
-
4
- describe("decorator-utils · isStd 双签名判据", () => {
5
- it("legacy 类装饰器(1 参)→ false", () => {
6
- expect(isStd([class Foo {}])).to.equal(false);
7
- });
8
-
9
- it("legacy 方法/属性装饰器(2 参,第 2 参是 string key)→ false", () => {
10
- expect(isStd([{}, "methodName"])).to.equal(false);
11
- });
12
-
13
- it("legacy 带 descriptor 方法 / 参数装饰器(3 参)→ false", () => {
14
- expect(isStd([{}, "methodName", {}])).to.equal(false);
15
- expect(isStd([{}, "methodName", 0])).to.equal(false);
16
- });
17
-
18
- it("标准类/方法/field 装饰器(2 参,第 2 参带 string kind)→ true", () => {
19
- expect(isStd([function () {}, { kind: "class" }])).to.equal(true);
20
- expect(isStd([function () {}, { kind: "method" }])).to.equal(true);
21
- expect(isStd([undefined, { kind: "field" }])).to.equal(true);
22
- expect(isStd([{}, { kind: "accessor" }])).to.equal(true);
23
- });
24
-
25
- it("第 2 参是对象但无 kind → false", () => {
26
- expect(isStd([{}, {}])).to.equal(false);
27
- });
28
-
29
- it("第 2 参是 null / 非对象 → false", () => {
30
- expect(isStd([{}, null])).to.equal(false);
31
- expect(isStd([{}, 42])).to.equal(false);
32
- });
33
-
34
- it("getStdArgs 解出 (value, context)", () => {
35
- const ctx = { kind: "method", name: "foo" };
36
- const [value, context] = getStdArgs([function bar() {}, ctx]);
37
- expect(typeof value).to.equal("function");
38
- expect(context).to.equal(ctx);
39
- });
40
- });
41
-
42
- export {};
@@ -1,42 +0,0 @@
1
- const chaiObj = require('chai');
2
- chaiObj.use(require("chai-http"));
3
-
4
- describe("Test First page", () => {
5
- const testAddr = `http://${process.env.LOCAL_HOST || "localhost"}:8081`;
6
- const firstPageRequests = [
7
- {
8
- "url": "/first",
9
- "expect": "FirstPage index running",
10
- },
11
- {
12
- "url": "/first/sendJson",
13
- "expect": JSON.stringify({
14
- "from": "sendJson",
15
- "to": "Browser"
16
- }),
17
- },
18
- {
19
- "url": "/first/sendResult",
20
- "expect": "sendResult",
21
- },
22
- {
23
- "url": "/first/renderTest",
24
- "expect": "Hello zzz!",
25
- },
26
- {
27
- "url": "/test/error",
28
- "expect": "This is a error log",
29
- }
30
- ]
31
- firstPageRequests.forEach((testRequest) => {
32
- it(testRequest.url, (done) => {
33
- chaiObj.request(testAddr).get(testRequest.url).end((err, res) => {
34
- chaiObj.assert.equal(testRequest.expect, res.text);
35
- return done();
36
- });
37
- });
38
- });
39
-
40
- });
41
-
42
- export {};
package/test/hooks.ts DELETED
@@ -1,13 +0,0 @@
1
- let appClose;
2
- before(function () {
3
- this.timeout(50000);
4
- process.env["LOG"] = "CLOSE";
5
- appClose = require("../app/src/main");
6
- });
7
-
8
- after((done) => {
9
- if(appClose != null){
10
- appClose.default();
11
- }
12
- done();
13
- });
package/test/mq.test.ts DELETED
@@ -1,17 +0,0 @@
1
- const chaiObj = require('chai');
2
-
3
- chaiObj.use(require("chai-http"));
4
-
5
- const expect = chaiObj.expect;
6
-
7
- describe("Test RabbitMQ", () => {
8
- const testAddr = `http://${process.env.LOCAL_HOST || "localhost"}:8081`;
9
- it("/mq/sendByMQClass", (done) => {
10
- chaiObj.request(testAddr).get("/mq/sendByMQClass").end((err, res) => {
11
- expect(res.text).to.be.equal("Sent by MQClass");
12
- done();
13
- });
14
- });
15
- });
16
-
17
- export { };
package/test/orm.test.ts DELETED
@@ -1,66 +0,0 @@
1
- const chaiObj = require('chai');
2
- chaiObj.use(require("chai-http"));
3
- const expect = chaiObj.expect;
4
-
5
- describe("Test Request Paramters", () => {
6
- const testAddr = `http://${process.env.LOCAL_HOST || "localhost"}:8081`;
7
- it("/redis", (done) => {
8
- chaiObj.request(testAddr).get("/redis").end((err, res) => {
9
- expect(res.text).to.be.equal("get from redis: " + "Hello World");
10
- done();
11
- });
12
- });
13
- it("/orm/first", (done) => {
14
- chaiObj.request(testAddr).get("/orm/first").end((err, res) => {
15
- expect(res.text).to.be.equal("first test, to getUsers");
16
- done();
17
- });
18
- });
19
- it("/orm/one", (done) => {
20
- chaiObj.request(testAddr).get("/orm/one?id=1").end((err, res) => {
21
- expect(res.text).to.be.equal("find one test, to getUser");
22
- done();
23
- });
24
- });
25
- it("/orm/delete", (done) => {
26
- chaiObj.request(testAddr).get("/orm/delete?id=0").end((err, res) => {
27
- expect(res.text).to.be.equal("remove user, results: remove rows: 0");
28
- done();
29
- });
30
- });
31
- it("/orm/count", (done) => {
32
- chaiObj.request(testAddr).get("/orm/count").end((err, res) => {
33
- expect(JSON.parse(res.text)).to.have.property("count").which.is.a("number").above(0);
34
- done();
35
- });
36
- });
37
- it("/orm/new", (done) => {
38
- chaiObj.request(testAddr).get("/orm/new").end((err, res) => {
39
- expect(JSON.parse(res.text)).to.have.property("newId").which.is.a("number").above(0);
40
- done();
41
- });
42
- });
43
- it("/orm/page/calculate", (done) => {
44
- chaiObj.request(testAddr).get("/orm/page/calculate").end((err, res) => {
45
- expect(JSON.parse(res.text)).to.have.property("totalPage").which.is.a("number").equal(38);
46
- done();
47
- });
48
- });
49
- it("/orm/pages", (done) => {
50
- chaiObj.request(testAddr).get("/orm/pages/1").end((err, res) => {
51
- expect(JSON.parse(res.text)).is.a("array").lengthOf(3);
52
- done();
53
- });
54
- });
55
- it("/orm/edit", (done) => {
56
- chaiObj.request(testAddr).post("/orm/edit")
57
- .type("form")
58
- .send({ id: 1, name: "new name" })
59
- .end((err, res) => {
60
- expect(JSON.parse(res.text)).to.have.property("effectRows").which.to.be.oneOf([0, 1]);
61
- done();
62
- });
63
- });
64
- });
65
-
66
- export { };
@@ -1,48 +0,0 @@
1
- const chaiObj = require('chai');
2
- chaiObj.use(require("chai-http"));
3
- const expect = chaiObj.expect;
4
-
5
- describe("Test Redis", () => {
6
- const testAddr = `http://${process.env.LOCAL_HOST || "localhost"}:8081`;
7
- const ranking = {
8
- "zhangsan": 93, "lisi": 99, "wangwu": 96, "zhaoliu": 97, "qianqi": 98, "sunba": 92, "zhoujiu": 94, "wushi": 95
9
- };
10
- it("/redis", (done) => {
11
- chaiObj.request(testAddr).get("/redis").end((err, res) => {
12
- expect(res.text).to.be.equal("get from redis: " + "Hello World");
13
- done();
14
- });
15
- });
16
- it("/redis/publish", (done) => {
17
- chaiObj.request(testAddr).get("/redis/publish").end((err, res) => {
18
- expect(res.text).to.be.equal("Published!");
19
- done();
20
- });
21
- });
22
- Object.keys(ranking).forEach((item) => {
23
- it("/redis/add " + item, (done) => {
24
- chaiObj.request(testAddr).get(`/redis/add?name=${item}&score=${ranking[item]}`).end((err, res) => {
25
- expect(res.text).to.be.equal("add zset success");
26
- done();
27
- });
28
- });
29
- });
30
- it("/redis/list", (done) => {
31
- chaiObj.request(testAddr).get("/redis/list").end((err, res) => {
32
- const dataList = JSON.parse(res.text);
33
- const rankingAsc = Object.fromEntries(Object.entries(ranking).sort((a, b) => a[1] - b[1]));
34
- expect(JSON.stringify(dataList)).to.be.equal(JSON.stringify(rankingAsc));
35
- done();
36
- });
37
- });
38
- it("/redis/ranking", (done) => {
39
- chaiObj.request(testAddr).get("/redis/ranking").end((err, res) => {
40
- const dataList = JSON.parse(res.text);
41
- const rankingDesc = Object.fromEntries(Object.entries(ranking).sort((a, b) => b[1] - a[1]));
42
- expect(JSON.stringify(dataList)).to.be.equal(JSON.stringify(rankingDesc));
43
- done();
44
- });
45
- });
46
- });
47
-
48
- export { };
@@ -1,51 +0,0 @@
1
- import MutilUsers from "../app/src/entities/mutil-users.class";
2
- import UserDto from "../app/src/entities/user-dto.class";
3
- const chaiObj = require('chai');
4
- chaiObj.use(require("chai-http"));
5
- const expect = chaiObj.expect;
6
-
7
- describe("Test Request Paramters", () => {
8
- const testAddr = `http://${process.env.LOCAL_HOST || "localhost"}:8081`;
9
- it("/request/res", (done) => {
10
- chaiObj.request(testAddr).get("/request/res").end((err, res) => {
11
- expect(res.text).to.be.equal("test res");
12
- done();
13
- });
14
- });
15
- it("/request/query", (done) => {
16
- chaiObj.request(testAddr).get("/request/query?id=100").end((err, res) => {
17
- expect(res.text).to.be.equal(JSON.stringify(
18
- new MutilUsers("group", [new UserDto(1, "name"), new UserDto(2, "name")]
19
- )));
20
- done();
21
- });
22
- });
23
- it("/request/body", (done) => {
24
- const sendUser = new UserDto(100, "name100");
25
- chaiObj.request(testAddr).post("/request/body")
26
- .send(sendUser)
27
- .end((err, res) => {
28
- expect(res.text).to.be.equal(JSON.stringify(new MutilUsers("group", [sendUser])));
29
- done();
30
- });
31
- });
32
- it("/request/form", (done) => {
33
- const sendName = "name200";
34
- chaiObj.request(testAddr).post("/request/form")
35
- .type("form")
36
- .send({ name: sendName })
37
- .end((err, res) => {
38
- expect(res.text).to.be.equal("Got name: " + sendName);
39
- done();
40
- });
41
- });
42
- it("/request/param", (done) => {
43
- const sendId = 200;
44
- chaiObj.request(testAddr).get("/request/param/" + sendId).end((err, res) => {
45
- expect(res.text).to.be.equal("test param: " + sendId);
46
- done();
47
- });
48
- });
49
- });
50
-
51
- export {};
@@ -1,50 +0,0 @@
1
- import * as jwttoken from "jsonwebtoken";
2
- import * as fs from "fs";
3
- const chaiObj = require('chai');
4
- chaiObj.use(require("chai-http"));
5
-
6
- describe("Test Sencond Page", () => {
7
- const testAddr = `http://${process.env.LOCAL_HOST || "localhost"}:8081`;
8
- it("/second/setCookie", (done) => {
9
- chaiObj.request(testAddr).get("/second/setCookie").end((err, res) => {
10
- chaiObj.assert.equal(res.headers["set-cookie"].includes("name=zzz; Path=/"), true);
11
- done();
12
- });
13
- });
14
- it("/second/getCookie", (done) => {
15
- const cookieSet = "mycookie"
16
- chaiObj.request(testAddr).get("/second/getCookie")
17
- .set("Cookie", "name=" + cookieSet).end((err, res) => {
18
- chaiObj.assert.equal(res.text, "getCookie: " + cookieSet);
19
- done();
20
- });
21
- });
22
- it("/second/testSession", (done) => {
23
- chaiObj.request(testAddr).get("/second/testSession").end((err, res) => {
24
- chaiObj.assert.equal(res.text, "testForSession: 1");
25
- done();
26
- });
27
- });
28
- it("/upload", (done) => {
29
- const uploadfile = "./app/static/k.jpg";
30
- chaiObj.request(testAddr).post("/upload")
31
- .set('Content-Type', 'multipart/form-data')
32
- .attach("file", uploadfile)
33
- .end((err, res) => {
34
- chaiObj.assert.equal(res.text, "upload success");
35
- done();
36
- });
37
- });
38
- it("/form", (done) => {
39
- const token = jwttoken.sign({ foo: 'bar' }, 'shhhhhhared-secret');
40
- const fileContents = fs.readFileSync("./app/src/views/upload.html", "utf8");
41
- chaiObj.request(testAddr).post("/form")
42
- .set({ "Authorization": `Bearer ${token}` })
43
- .end((err, res) => {
44
- chaiObj.assert.equal(res.text, fileContents);
45
- done();
46
- });
47
- });
48
- });
49
-
50
- export { };
@@ -1,60 +0,0 @@
1
- const chaiObj = require('chai');
2
- chaiObj.use(require("chai-http"));
3
- import { io as Client } from "socket.io-client";
4
- const expect = chaiObj.expect;
5
-
6
- describe("Test Socket IO", () => {
7
- const testAddr = `http://${process.env.LOCAL_HOST || "localhost"}:8081`;
8
- let clientHanMeiMei, clientLiLei;
9
- before((done) => {
10
- clientHanMeiMei = Client(testAddr);
11
- clientLiLei = Client(testAddr);
12
- clientHanMeiMei.on("connect", done);
13
- });
14
- after(() => {
15
- clientHanMeiMei.close();
16
- clientLiLei.close();
17
- });
18
- it("send and receive message", (done) => {
19
- clientHanMeiMei.on("all", (arg) => {
20
- expect(arg).to.be.include("LiLei");
21
- clientHanMeiMei.removeAllListeners("all");
22
- done();
23
- });
24
- clientLiLei.emit("say", "test-from-client-1");
25
- });
26
- it("test join room", (done) => {
27
- clientHanMeiMei.emit("join", "");
28
- clientHanMeiMei.on("all", (arg) => {
29
- expect(arg).to.be.include("joined private-room");
30
- clientHanMeiMei.removeAllListeners("all");
31
- done();
32
- });
33
- });
34
- it("test say in room", (done) => {
35
- const message = "I said in Room";
36
- clientHanMeiMei.on("all", (arg) => {
37
- expect(arg).to.be.include(message);
38
- clientHanMeiMei.removeAllListeners("all");
39
- done();
40
- });
41
- clientHanMeiMei.emit("say-inroom", message);
42
- });
43
- it("test error catching", (done) => {
44
- clientHanMeiMei.on("all", (arg) => {
45
- expect(arg).to.be.include("We have a problem!");
46
- clientHanMeiMei.removeAllListeners("all");
47
- done();
48
- });
49
- clientHanMeiMei.emit("test-error", "");
50
- });
51
- it("test disconnecting", (done) => {
52
- clientLiLei.on("all", (arg) => {
53
- expect(arg).to.be.include("lost a member");
54
- clientLiLei.removeAllListeners("all");
55
- done();
56
- });
57
- clientHanMeiMei.disconnect();
58
- });
59
- });
60
-
@@ -1,31 +0,0 @@
1
- services:
2
- mysql:
3
- image: mysql:8.0
4
- container_name: typespeed-test-mysql
5
- environment:
6
- MYSQL_ROOT_PASSWORD: root
7
- MYSQL_DATABASE: test
8
- ports:
9
- - "3306:3306"
10
- volumes:
11
- - ./mysql-init:/docker-entrypoint-initdb.d
12
- healthcheck:
13
- test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-proot"]
14
- interval: 3s
15
- timeout: 3s
16
- retries: 20
17
-
18
- redis:
19
- image: redis:7
20
- container_name: typespeed-test-redis
21
- ports:
22
- - "6379:6379"
23
-
24
- rabbitmq:
25
- image: rabbitmq:3.8
26
- container_name: typespeed-test-rabbitmq
27
- environment:
28
- RABBITMQ_DEFAULT_USER: guest
29
- RABBITMQ_DEFAULT_PASS: guest
30
- ports:
31
- - "5672:5672"
@@ -1,8 +0,0 @@
1
- -- 与 CI 的 Setup MySQL 步骤一致:建 test 库 + user 表
2
- CREATE DATABASE IF NOT EXISTS test DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
3
- USE test;
4
- CREATE TABLE IF NOT EXISTS `user` (
5
- `id` int(11) NOT NULL AUTO_INCREMENT,
6
- `name` varchar(100) DEFAULT NULL,
7
- PRIMARY KEY (`id`)
8
- ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -1,69 +0,0 @@
1
- # 本地 Docker 测试方案
2
-
3
- typespeed 测试依赖三个外部服务(MySQL / Redis / RabbitMQ),本机没有常驻服务时用 Docker 起一套与 CI 一致的环境来跑测试。
4
-
5
- ## 一、前置条件
6
-
7
- - 本机已安装 **OrbStack**(或 Docker Desktop),Docker CLI 可用:`docker --version`
8
- - 若 OrbStack 未运行,先启动它(菜单栏点开即可,或 `open -a OrbStack`)
9
- - 端口占用:默认映射 `3306 / 6379 / 5672`,起容器前确认空闲(`nc -z localhost 3306` 等)
10
-
11
- ## 二、一键启动测试依赖
12
-
13
- ```bash
14
- cd /Users/zzz/book/typespeed
15
- docker compose -f test-env/docker-compose.test.yml up -d
16
- ```
17
-
18
- 启动三个容器:
19
-
20
- | 服务 | 容器名 | 端口 | 账号 |
21
- |------|--------|------|------|
22
- | MySQL 8.0 | typespeed-test-mysql | 127.0.0.1:3306 | root / root,库 `test` |
23
- | Redis 7 | typespeed-test-redis | 127.0.0.1:6379 | 无 |
24
- | RabbitMQ 3.8 | typespeed-test-rabbitmq | 127.0.0.1:5672 | guest / guest |
25
-
26
- MySQL 首次启动会自动执行 `test-env/mysql-init/init.sql`(建 `test` 库 + `user` 表,与 CI 的 Setup MySQL 步骤一致)。
27
-
28
- 检查就绪状态:
29
-
30
- ```bash
31
- docker compose -f test-env/docker-compose.test.yml ps # 三容器 Up 且 mysql 为 healthy
32
- docker exec typespeed-test-mysql mysql -uroot -proot -e "USE test; SHOW TABLES;"
33
- docker exec typespeed-test-redis redis-cli ping # PONG
34
- docker exec typespeed-test-rabbitmq rabbitmq-diagnostics -q ping # Ping succeeded
35
- ```
36
-
37
- ## 三、跑测试
38
-
39
- ```bash
40
- npm test
41
- ```
42
-
43
- 预期:`51 passing`。
44
-
45
- > 说明:`package.json` 的 test 脚本已加 `NODE_OPTIONS=--no-experimental-strip-types`,这是 Node 22 下 mocha 加载 CJS 风格 .ts 测试的必需项(否则报 `require is not defined`)。
46
-
47
- ## 四、收尾
48
-
49
- ```bash
50
- docker compose -f test-env/docker-compose.test.yml down # 停容器(保留数据卷需加 -v)
51
- ```
52
-
53
- ## 五、本次排障记录(2026-09-06)
54
-
55
- 本地 `npm test` 曾长期 0 passing / 47 failing,所有请求返回 404 错误页(`src/default/pages/404.html`,即 MIT License 模板页)。曾误判为"缺本地服务"(环境差异),用 Docker 起齐服务后依然全挂,最终定位为**框架根因**:
56
-
57
- 1. `src/typespeed.ts` 的 `getRootPath()` 靠匹配调用栈推断应用根目录(`mainPath`),原匹配写死旧版 Node 栈帧格式 `at Function.Module._load` 和 `at Object.<anonymous>`:
58
- - Node 22 的帧是 `at Function._load`(少了 `Module.`);
59
- - mocha 下 hooks 的帧是 `at Context.<anonymous>`(不是 `Object.`),且 `Function._load` 与 `Module.require` 之间会插入 `wrapModuleLoad` 帧。
60
- - 匹配全部落空 → `mainPath` 回退成 mocha 的 bin 目录 → config 不加载、app/src 下组件不加载、路由不注册 → 一切请求 404。
61
- 2. 修复:`getRootPath` 改为**按顺序出现匹配**(不要求帧严格相邻),帧格式用正则兼容新旧 Node 与 mocha。改动见 `src/typespeed.ts`(未提交)。
62
- 3. 修复后本地 `npm test` 全绿 **51 passing (3s)**。
63
- 4. 连带影响:CI 若从 node 16 升到 node 22(见 0.4 改动),旧的栈匹配同样会失效——本修复对 CI 也是必需的。
64
-
65
- ## 六、附:docker-compose.test.yml 内容要点
66
-
67
- - 服务定义与 CI(`.github/workflows/test.yml`)的 services 保持一致:mysql root/root + `test` 库、redis、rabbitmq guest/guest。
68
- - MySQL 加了 healthcheck,避免测试启动时库还没就绪。
69
- - 初始化 SQL 挂在 `test-env/mysql-init/init.sql`,与 CI 建表语句一致。
package/tsconfig.json DELETED
@@ -1,25 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "module": "CommonJS",
4
- "emitDecoratorMetadata": true,
5
- "experimentalDecorators": true,
6
- "allowSyntheticDefaultImports": true,
7
- "forceConsistentCasingInFileNames": true,
8
- "target": "ES2017",
9
- "outDir": "./dist",
10
- "baseUrl": "./",
11
- "typeRoots": [
12
- "node_modules/@types"
13
- ],
14
- },
15
- "include": [
16
- "./src/**/*.ts",
17
- "./src/typespeed.d.ts"
18
- ],
19
- "exclude": [
20
- "node_modules",
21
- "dist",
22
- "test",
23
- "app"
24
- ]
25
- }