@sfmc-bds/db-server 0.2.0-beta.13 → 0.2.0-beta.15

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 (88) hide show
  1. package/dist/domain/bridge.d.ts +1 -5
  2. package/dist/domain/bridge.d.ts.map +1 -1
  3. package/dist/domain/bridge.js +15 -14
  4. package/dist/domain/bridge.js.map +2 -2
  5. package/dist/domain/installed-content.d.ts +84 -0
  6. package/dist/domain/installed-content.d.ts.map +1 -0
  7. package/dist/domain/installed-content.js +149 -0
  8. package/dist/domain/installed-content.js.map +7 -0
  9. package/dist/domain/qq-account-profile.d.ts +51 -0
  10. package/dist/domain/qq-account-profile.d.ts.map +1 -0
  11. package/dist/domain/qq-account-profile.js +129 -0
  12. package/dist/domain/qq-account-profile.js.map +7 -0
  13. package/dist/domain/qq-events.d.ts +2 -2
  14. package/dist/domain/qq-events.d.ts.map +1 -1
  15. package/dist/domain/qq-events.js +6 -3
  16. package/dist/domain/qq-events.js.map +2 -2
  17. package/dist/domain/schema.d.ts.map +1 -1
  18. package/dist/domain/schema.js +13 -0
  19. package/dist/domain/schema.js.map +2 -2
  20. package/dist/domain/system-status.d.ts +3 -0
  21. package/dist/domain/system-status.d.ts.map +1 -1
  22. package/dist/domain/system-status.js +27 -12
  23. package/dist/domain/system-status.js.map +2 -2
  24. package/dist/env.d.ts +0 -2
  25. package/dist/env.d.ts.map +1 -1
  26. package/dist/env.js +12 -24
  27. package/dist/env.js.map +2 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +73 -21
  30. package/dist/index.js.map +3 -3
  31. package/dist/routes/config.d.ts +1 -1
  32. package/dist/routes/config.d.ts.map +1 -1
  33. package/dist/routes/config.js +0 -4
  34. package/dist/routes/config.js.map +2 -2
  35. package/dist/routes/content.d.ts +23 -0
  36. package/dist/routes/content.d.ts.map +1 -0
  37. package/dist/routes/content.js +19 -0
  38. package/dist/routes/content.js.map +7 -0
  39. package/dist/routes/messages.d.ts +14 -6
  40. package/dist/routes/messages.d.ts.map +1 -1
  41. package/dist/routes/messages.js +22 -8
  42. package/dist/routes/messages.js.map +2 -2
  43. package/dist/routes/qq-bind.d.ts.map +1 -1
  44. package/dist/routes/qq-bind.js +29 -1
  45. package/dist/routes/qq-bind.js.map +2 -2
  46. package/dist/routes/qq-events.d.ts +6 -2
  47. package/dist/routes/qq-events.d.ts.map +1 -1
  48. package/dist/routes/qq-events.js +35 -1
  49. package/dist/routes/qq-events.js.map +3 -3
  50. package/dist/routes/qq-join.js +1 -1
  51. package/dist/routes/qq-join.js.map +2 -2
  52. package/dist/routes/status.d.ts +3 -11
  53. package/dist/routes/status.d.ts.map +1 -1
  54. package/dist/routes/status.js +39 -49
  55. package/dist/routes/status.js.map +2 -2
  56. package/package.json +4 -5
  57. package/dist/domain/bridge.test.d.ts +0 -2
  58. package/dist/domain/bridge.test.d.ts.map +0 -1
  59. package/dist/domain/bridge.test.js +0 -87
  60. package/dist/domain/bridge.test.js.map +0 -7
  61. package/dist/domain/messages-bridge-filter.test.d.ts +0 -5
  62. package/dist/domain/messages-bridge-filter.test.d.ts.map +0 -1
  63. package/dist/domain/messages-bridge-filter.test.js +0 -20
  64. package/dist/domain/messages-bridge-filter.test.js.map +0 -7
  65. package/dist/domain/player-session.test.d.ts +0 -2
  66. package/dist/domain/player-session.test.d.ts.map +0 -1
  67. package/dist/domain/player-session.test.js +0 -33
  68. package/dist/domain/player-session.test.js.map +0 -7
  69. package/dist/domain/qq-bind-status.test.d.ts +0 -2
  70. package/dist/domain/qq-bind-status.test.d.ts.map +0 -1
  71. package/dist/domain/qq-bind-status.test.js +0 -211
  72. package/dist/domain/qq-bind-status.test.js.map +0 -7
  73. package/dist/domain/qq-events.test.d.ts +0 -5
  74. package/dist/domain/qq-events.test.d.ts.map +0 -1
  75. package/dist/domain/qq-events.test.js +0 -170
  76. package/dist/domain/qq-events.test.js.map +0 -7
  77. package/dist/domain/qq-join.test.d.ts +0 -2
  78. package/dist/domain/qq-join.test.d.ts.map +0 -1
  79. package/dist/domain/qq-join.test.js +0 -353
  80. package/dist/domain/qq-join.test.js.map +0 -7
  81. package/dist/domain/system-status.test.d.ts +0 -5
  82. package/dist/domain/system-status.test.d.ts.map +0 -1
  83. package/dist/domain/system-status.test.js +0 -19
  84. package/dist/domain/system-status.test.js.map +0 -7
  85. package/dist/runtime.test.d.ts +0 -2
  86. package/dist/runtime.test.d.ts.map +0 -1
  87. package/dist/runtime.test.js +0 -477
  88. package/dist/runtime.test.js.map +0 -7
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/domain/qq-bind-status.test.ts"],
4
- "sourcesContent": ["/**\n * domain/qq-bind-status.test.ts \u2014 bind \u8868 + status \u67E5\u8BE2\u5F62\u72B6\n */\nimport assert from \"node:assert/strict\";\nimport test from \"node:test\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport { SQL } from \"sql-template-strings\";\nimport { initSchema } from \"./schema.js\";\nimport { createQuery } from \"../lib/sqlite.js\";\nimport { createStatusRoutes, FRESH_MS } from \"../routes/status.js\";\nimport { createQqBindRoutes } from \"../routes/qq-bind.js\";\n\nfunction memDb() {\n const db = new DatabaseSync(\":memory:\");\n initSchema(db);\n return { db, query: createQuery(db) };\n}\n\nfunction mockRes() {\n let status = 200;\n let body: Record<string, unknown> = {};\n return {\n res: {\n writeHead(code: number) {\n status = code;\n },\n end(s: string) {\n body = JSON.parse(s) as Record<string, unknown>;\n },\n setHeader() {},\n } as unknown as import(\"http\").ServerResponse,\n get status() {\n return status;\n },\n get body() {\n return body;\n },\n };\n}\n\ntest(\"initSchema \u542B qq bind \u8868\", () => {\n const { db } = memDb();\n const tables = db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'sfmc_qq_%' ORDER BY name\")\n .all() as Array<{ name: string }>;\n const names = tables.map((t) => t.name);\n assert.ok(names.includes(\"sfmc_qq_bind_pending\"));\n assert.ok(names.includes(\"sfmc_qq_bindings\"));\n assert.ok(names.includes(\"sfmc_qq_join_requests\"));\n assert.ok(names.includes(\"sfmc_qq_admin_actions\"));\n assert.ok(FRESH_MS > 0);\n});\n\ntest(\"GET /api/sfmc/status \u7A7A\u8868\u4E0D\u629B\u9519\", async () => {\n const { query } = memDb();\n const handle = createStatusRoutes({\n query,\n collectSystem: async () => ({\n host: {\n hostname: \"test-host\",\n platform: \"win32\",\n arch: \"x64\",\n release: \"10.0\",\n uptimeSec: 3600,\n uptimeText: \"1\u65F60\u5206\",\n cpu: { model: \"Test CPU\", cores: 4 },\n memory: { totalMb: 8192, freeMb: 4096, usedMb: 4096, usedPercent: 50 },\n loadavg: [0, 0, 0],\n },\n db: { pid: 1, running: true, uptimeSec: 10, uptimeText: \"10\u79D2\" },\n bds: { state: \"stopped\", running: false, pid: 0, uptimeSec: null, uptimeText: \"\u672A\u8FD0\u884C\" },\n }),\n });\n const m = mockRes();\n const ok = await handle({\n path: \"/api/sfmc/status\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(ok, true);\n assert.equal(m.status, 200);\n assert.ok(Array.isArray(m.body.online));\n assert.equal((m.body.online as unknown[]).length, 0);\n assert.equal((m.body.host as { hostname: string }).hostname, \"test-host\");\n assert.equal((m.body.processes as { bds: { state: string } }).bds.state, \"stopped\");\n});\n\ntest(\"bind request \u2192 confirm \u2192 me \u2192 unbind + \u51B2\u7A81\", async () => {\n const { query } = memDb();\n let bodyData: Record<string, unknown> = {};\n const handle = createQqBindRoutes({\n query,\n body: async () => bodyData,\n json: (res, data, status = 200) => {\n (res as unknown as { writeHead: (c: number) => void }).writeHead(status);\n (res as unknown as { end: (s: string) => void }).end(JSON.stringify(data));\n },\n });\n\n bodyData = { openid: \"oa\", qq_backend: \"official\" };\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/bind/request\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n const code = String(m.body.code);\n assert.match(code, /^\\d{6}$/);\n\n bodyData = { code, xuid: \"x1\", name: \"Steve\" };\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/bind/confirm\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/bind/me\",\n method: \"GET\",\n params: new URLSearchParams(\"openid=oa\"),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.bound, true);\n assert.equal((m.body.binding as { player_name: string }).player_name, \"Steve\");\n\n // \u51B2\u7A81\uFF1A\u53E6\u4E00 QQ \u7ED1\u540C\u4E00 xuid\n bodyData = { openid: \"ob\", qq_backend: \"official\" };\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/bind/request\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n const code2 = String(m.body.code);\n bodyData = { code: code2, xuid: \"x1\", name: \"Alex\" };\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/bind/confirm\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, false);\n assert.equal(m.body.error, \"player_already_bound\");\n\n bodyData = { openid: \"oa\" };\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/bind/unbind\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.unbound, true);\n\n // \u5DF2\u7ED1\u5B9A\u518D request\n bodyData = { openid: \"oa\", qq_backend: \"official\" };\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/bind/request\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n // \u89E3\u7ED1\u540E\u5E94\u80FD\u518D\u7533\u8BF7\n assert.equal(m.body.success, true);\n});\n\ntest(\"status \u65B0\u9C9C\u73A9\u5BB6\u8BA1\u5165 online\", async () => {\n const { query } = memDb();\n const now = Date.now();\n query(\n SQL`INSERT INTO sfmc_players (id, name, updated_at) VALUES ('p1', 'Steve', ${now})`\n );\n query(\n SQL`INSERT INTO sfmc_world (day, difficulty, updated_at) VALUES (3, 'normal', ${String(now)})`\n );\n const handle = createStatusRoutes({\n query,\n collectSystem: async () => ({\n host: {\n hostname: \"h\",\n platform: \"linux\",\n arch: \"x64\",\n release: \"6\",\n uptimeSec: 1,\n uptimeText: \"1\u79D2\",\n cpu: { model: \"c\", cores: 2 },\n memory: { totalMb: 1, freeMb: 1, usedMb: 0, usedPercent: 0 },\n loadavg: [0, 0, 0],\n },\n db: { pid: 1, running: true, uptimeSec: 1, uptimeText: \"1\u79D2\" },\n bds: {\n state: \"running\",\n running: true,\n pid: 99,\n uptimeSec: 120,\n uptimeText: \"2\u52060\u79D2\",\n },\n }),\n });\n const m = mockRes();\n await handle({\n path: \"/api/sfmc/status\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal((m.body.online as unknown[]).length, 1);\n assert.equal((m.body.world as { day: number }).day, 3);\n assert.equal((m.body.processes as { bds: { pid: number } }).bds.pid, 99);\n});\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,5 +0,0 @@
1
- /**
2
- * qq-events 聚合器单测
3
- */
4
- export {};
5
- //# sourceMappingURL=qq-events.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"qq-events.test.d.ts","sourceRoot":"","sources":["../../src/domain/qq-events.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -1,170 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import {
4
- createQqEventsAggregator,
5
- formatImmediateBody,
6
- formatWindowBody,
7
- localizeCause,
8
- MAX_WINDOW_EVENTS,
9
- normalizeEventPayload,
10
- resolveQqEventsConfig
11
- } from "./qq-events.js";
12
- const ALL_ON = {
13
- enabled: true,
14
- window_sec: 60,
15
- join: true,
16
- leave: true,
17
- death: true,
18
- crash: true,
19
- start: true
20
- };
21
- const fakeOutbound = {
22
- backend: "official",
23
- creds: { appId: "a", appSecret: "s" },
24
- groupOpenid: "G",
25
- prefix: "[MC]"
26
- };
27
- test("resolveQqEventsConfig \u7F3A\u7701\u4E0E\u603B\u5F00\u5173", () => {
28
- const d = resolveQqEventsConfig(void 0);
29
- assert.equal(d.enabled, true);
30
- assert.equal(d.window_sec, 60);
31
- assert.equal(d.join, true);
32
- const off = resolveQqEventsConfig({ enabled: false, join: false });
33
- assert.equal(off.enabled, false);
34
- assert.equal(off.join, false);
35
- assert.equal(off.death, true);
36
- });
37
- test("localizeCause \u6620\u5C04\u4E0E\u539F\u6587\u56DE\u9000", () => {
38
- assert.equal(localizeCause("fall"), "\u5760\u843D");
39
- assert.equal(localizeCause("drowning"), "\u6EBA\u6C34");
40
- assert.equal(localizeCause("custom_xyz"), "custom_xyz");
41
- assert.equal(localizeCause(""), "");
42
- });
43
- test("normalizeEventPayload \u8FC7\u6EE4\u975E\u6CD5 type", () => {
44
- assert.equal(normalizeEventPayload({ type: "chat" }), null);
45
- assert.deepEqual(normalizeEventPayload({ type: "join", player: "Steve" }), {
46
- type: "join",
47
- player: "Steve"
48
- });
49
- });
50
- test("formatWindowBody \u805A\u5408\u6587\u6848", () => {
51
- const text = formatWindowBody([
52
- { type: "join", player: "Steve" },
53
- { type: "join", player: "Alex" },
54
- { type: "leave", player: "Bob" },
55
- { type: "death", player: "Steve", cause: "fall" }
56
- ]);
57
- assert.match(text, /^\[MC事件\]/);
58
- assert.match(text, /上线:Steve、Alex/);
59
- assert.match(text, /下线:Bob/);
60
- assert.match(text, /死亡:Steve(坠落)/);
61
- });
62
- test("formatImmediateBody crash/start", () => {
63
- assert.equal(formatImmediateBody({ type: "crash", detail: "code=1" }), "[MC\u4E8B\u4EF6] BDS \u610F\u5916\u9000\u51FA (code=1)");
64
- assert.equal(formatImmediateBody({ type: "start", detail: "pid=9" }), "[MC\u4E8B\u4EF6] BDS \u5DF2\u542F\u52A8 (pid=9)");
65
- });
66
- test("\u7A97\u53E3\u5185 join+death \u5230\u671F flush \u6210\u4E00\u6761", () => {
67
- const sent = [];
68
- const timers = [];
69
- const agg = createQqEventsAggregator({
70
- getConfig: () => ({ ...ALL_ON, window_sec: 60 }),
71
- getOutbound: () => fakeOutbound,
72
- send: (t) => sent.push(t),
73
- setTimeoutFn: (fn, ms) => {
74
- timers.push({ fn, ms });
75
- return timers.length;
76
- },
77
- clearTimeoutFn: () => {
78
- }
79
- });
80
- agg.ingestOne({ type: "join", player: "A" });
81
- agg.ingestOne({ type: "join", player: "B" });
82
- agg.ingestOne({ type: "death", player: "A", cause: "fall" });
83
- assert.equal(sent.length, 0);
84
- assert.equal(agg.pendingCount(), 3);
85
- assert.equal(timers.length, 1);
86
- assert.equal(timers[0].ms, 6e4);
87
- timers[0].fn();
88
- assert.equal(sent.length, 1);
89
- assert.match(sent[0], /上线:A、B/);
90
- assert.match(sent[0], /死亡:A(坠落)/);
91
- assert.equal(agg.pendingCount(), 0);
92
- agg.dispose();
93
- });
94
- test("crash \u7ACB\u5373\u63A8\u4E14\u5148\u51B2\u5237\u7A97\u53E3", () => {
95
- const sent = [];
96
- const timers = [];
97
- const agg = createQqEventsAggregator({
98
- getConfig: () => ALL_ON,
99
- getOutbound: () => fakeOutbound,
100
- send: (t) => sent.push(t),
101
- setTimeoutFn: (fn) => {
102
- timers.push({ fn });
103
- return 1;
104
- },
105
- clearTimeoutFn: () => {
106
- }
107
- });
108
- agg.ingestOne({ type: "join", player: "Steve" });
109
- agg.ingestOne({ type: "crash", detail: "code=1" });
110
- assert.equal(sent.length, 2);
111
- assert.match(sent[0], /上线:Steve/);
112
- assert.match(sent[1], /BDS 意外退出 \(code=1\)/);
113
- assert.equal(agg.pendingCount(), 0);
114
- agg.dispose();
115
- });
116
- test("enabled:false \u5168\u4E22", () => {
117
- const sent = [];
118
- const agg = createQqEventsAggregator({
119
- getConfig: () => ({ ...ALL_ON, enabled: false }),
120
- getOutbound: () => fakeOutbound,
121
- send: (t) => sent.push(t)
122
- });
123
- const r = agg.ingestMany([
124
- { type: "join", player: "A" },
125
- { type: "crash", detail: "x" }
126
- ]);
127
- assert.equal(r.accepted, 0);
128
- assert.equal(r.rejected, 2);
129
- assert.equal(sent.length, 0);
130
- agg.dispose();
131
- });
132
- test("\u5355\u7C7B\u5173\u95ED\uFF1Adeath \u5173\u5219\u4E22\u5F03", () => {
133
- const sent = [];
134
- const timers = [];
135
- const agg = createQqEventsAggregator({
136
- getConfig: () => ({ ...ALL_ON, death: false }),
137
- getOutbound: () => fakeOutbound,
138
- send: (t) => sent.push(t),
139
- setTimeoutFn: (fn) => {
140
- timers.push({ fn });
141
- return 1;
142
- },
143
- clearTimeoutFn: () => {
144
- }
145
- });
146
- assert.equal(agg.ingestOne({ type: "death", player: "A", cause: "fall" }).accepted, false);
147
- assert.equal(agg.ingestOne({ type: "join", player: "A" }).accepted, true);
148
- timers[0].fn();
149
- assert.equal(sent.length, 1);
150
- assert.doesNotMatch(sent[0], /死亡/);
151
- agg.dispose();
152
- });
153
- test("\u7A97\u53E3\u6EE1 MAX_WINDOW_EVENTS \u63D0\u524D flush", () => {
154
- const sent = [];
155
- const agg = createQqEventsAggregator({
156
- getConfig: () => ({ ...ALL_ON, window_sec: 600 }),
157
- getOutbound: () => fakeOutbound,
158
- send: (t) => sent.push(t),
159
- setTimeoutFn: () => 1,
160
- clearTimeoutFn: () => {
161
- }
162
- });
163
- for (let i = 0; i < MAX_WINDOW_EVENTS; i++) {
164
- agg.ingestOne({ type: "join", player: `P${i}` });
165
- }
166
- assert.equal(sent.length, 1);
167
- assert.equal(agg.pendingCount(), 0);
168
- agg.dispose();
169
- });
170
- //# sourceMappingURL=qq-events.test.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/domain/qq-events.test.ts"],
4
- "sourcesContent": ["/**\n * qq-events \u805A\u5408\u5668\u5355\u6D4B\n */\n\nimport assert from \"node:assert/strict\";\nimport test from \"node:test\";\nimport {\n createQqEventsAggregator,\n formatImmediateBody,\n formatWindowBody,\n localizeCause,\n MAX_WINDOW_EVENTS,\n normalizeEventPayload,\n resolveQqEventsConfig,\n type ResolvedQqEventsConfig,\n} from \"./qq-events.js\";\nimport type { OutboundConfig } from \"./bridge.js\";\n\nconst ALL_ON: ResolvedQqEventsConfig = {\n enabled: true,\n window_sec: 60,\n join: true,\n leave: true,\n death: true,\n crash: true,\n start: true,\n};\n\nconst fakeOutbound: OutboundConfig = {\n backend: \"official\",\n creds: { appId: \"a\", appSecret: \"s\" },\n groupOpenid: \"G\",\n prefix: \"[MC]\",\n};\n\ntest(\"resolveQqEventsConfig \u7F3A\u7701\u4E0E\u603B\u5F00\u5173\", () => {\n const d = resolveQqEventsConfig(undefined);\n assert.equal(d.enabled, true);\n assert.equal(d.window_sec, 60);\n assert.equal(d.join, true);\n\n const off = resolveQqEventsConfig({ enabled: false, join: false });\n assert.equal(off.enabled, false);\n assert.equal(off.join, false);\n assert.equal(off.death, true);\n});\n\ntest(\"localizeCause \u6620\u5C04\u4E0E\u539F\u6587\u56DE\u9000\", () => {\n assert.equal(localizeCause(\"fall\"), \"\u5760\u843D\");\n assert.equal(localizeCause(\"drowning\"), \"\u6EBA\u6C34\");\n assert.equal(localizeCause(\"custom_xyz\"), \"custom_xyz\");\n assert.equal(localizeCause(\"\"), \"\");\n});\n\ntest(\"normalizeEventPayload \u8FC7\u6EE4\u975E\u6CD5 type\", () => {\n assert.equal(normalizeEventPayload({ type: \"chat\" }), null);\n assert.deepEqual(normalizeEventPayload({ type: \"join\", player: \"Steve\" }), {\n type: \"join\",\n player: \"Steve\",\n });\n});\n\ntest(\"formatWindowBody \u805A\u5408\u6587\u6848\", () => {\n const text = formatWindowBody([\n { type: \"join\", player: \"Steve\" },\n { type: \"join\", player: \"Alex\" },\n { type: \"leave\", player: \"Bob\" },\n { type: \"death\", player: \"Steve\", cause: \"fall\" },\n ]);\n assert.match(text, /^\\[MC\u4E8B\u4EF6\\]/);\n assert.match(text, /\u4E0A\u7EBF\uFF1ASteve\u3001Alex/);\n assert.match(text, /\u4E0B\u7EBF\uFF1ABob/);\n assert.match(text, /\u6B7B\u4EA1\uFF1ASteve\uFF08\u5760\u843D\uFF09/);\n});\n\ntest(\"formatImmediateBody crash/start\", () => {\n assert.equal(formatImmediateBody({ type: \"crash\", detail: \"code=1\" }), \"[MC\u4E8B\u4EF6] BDS \u610F\u5916\u9000\u51FA (code=1)\");\n assert.equal(formatImmediateBody({ type: \"start\", detail: \"pid=9\" }), \"[MC\u4E8B\u4EF6] BDS \u5DF2\u542F\u52A8 (pid=9)\");\n});\n\ntest(\"\u7A97\u53E3\u5185 join+death \u5230\u671F flush \u6210\u4E00\u6761\", () => {\n const sent: string[] = [];\n const timers: Array<{ fn: () => void; ms: number }> = [];\n const agg = createQqEventsAggregator({\n getConfig: () => ({ ...ALL_ON, window_sec: 60 }),\n getOutbound: () => fakeOutbound,\n send: (t) => sent.push(t),\n setTimeoutFn: (fn, ms) => {\n timers.push({ fn, ms });\n return timers.length as unknown as ReturnType<typeof setTimeout>;\n },\n clearTimeoutFn: () => {},\n });\n\n agg.ingestOne({ type: \"join\", player: \"A\" });\n agg.ingestOne({ type: \"join\", player: \"B\" });\n agg.ingestOne({ type: \"death\", player: \"A\", cause: \"fall\" });\n assert.equal(sent.length, 0);\n assert.equal(agg.pendingCount(), 3);\n assert.equal(timers.length, 1);\n assert.equal(timers[0]!.ms, 60_000);\n\n timers[0]!.fn();\n assert.equal(sent.length, 1);\n assert.match(sent[0]!, /\u4E0A\u7EBF\uFF1AA\u3001B/);\n assert.match(sent[0]!, /\u6B7B\u4EA1\uFF1AA\uFF08\u5760\u843D\uFF09/);\n assert.equal(agg.pendingCount(), 0);\n agg.dispose();\n});\n\ntest(\"crash \u7ACB\u5373\u63A8\u4E14\u5148\u51B2\u5237\u7A97\u53E3\", () => {\n const sent: string[] = [];\n const timers: Array<{ fn: () => void }> = [];\n const agg = createQqEventsAggregator({\n getConfig: () => ALL_ON,\n getOutbound: () => fakeOutbound,\n send: (t) => sent.push(t),\n setTimeoutFn: (fn) => {\n timers.push({ fn });\n return 1 as unknown as ReturnType<typeof setTimeout>;\n },\n clearTimeoutFn: () => {},\n });\n\n agg.ingestOne({ type: \"join\", player: \"Steve\" });\n agg.ingestOne({ type: \"crash\", detail: \"code=1\" });\n assert.equal(sent.length, 2);\n assert.match(sent[0]!, /\u4E0A\u7EBF\uFF1ASteve/);\n assert.match(sent[1]!, /BDS \u610F\u5916\u9000\u51FA \\(code=1\\)/);\n assert.equal(agg.pendingCount(), 0);\n agg.dispose();\n});\n\ntest(\"enabled:false \u5168\u4E22\", () => {\n const sent: string[] = [];\n const agg = createQqEventsAggregator({\n getConfig: () => ({ ...ALL_ON, enabled: false }),\n getOutbound: () => fakeOutbound,\n send: (t) => sent.push(t),\n });\n const r = agg.ingestMany([\n { type: \"join\", player: \"A\" },\n { type: \"crash\", detail: \"x\" },\n ]);\n assert.equal(r.accepted, 0);\n assert.equal(r.rejected, 2);\n assert.equal(sent.length, 0);\n agg.dispose();\n});\n\ntest(\"\u5355\u7C7B\u5173\u95ED\uFF1Adeath \u5173\u5219\u4E22\u5F03\", () => {\n const sent: string[] = [];\n const timers: Array<{ fn: () => void }> = [];\n const agg = createQqEventsAggregator({\n getConfig: () => ({ ...ALL_ON, death: false }),\n getOutbound: () => fakeOutbound,\n send: (t) => sent.push(t),\n setTimeoutFn: (fn) => {\n timers.push({ fn });\n return 1 as unknown as ReturnType<typeof setTimeout>;\n },\n clearTimeoutFn: () => {},\n });\n assert.equal(agg.ingestOne({ type: \"death\", player: \"A\", cause: \"fall\" }).accepted, false);\n assert.equal(agg.ingestOne({ type: \"join\", player: \"A\" }).accepted, true);\n timers[0]!.fn();\n assert.equal(sent.length, 1);\n assert.doesNotMatch(sent[0]!, /\u6B7B\u4EA1/);\n agg.dispose();\n});\n\ntest(\"\u7A97\u53E3\u6EE1 MAX_WINDOW_EVENTS \u63D0\u524D flush\", () => {\n const sent: string[] = [];\n const agg = createQqEventsAggregator({\n getConfig: () => ({ ...ALL_ON, window_sec: 600 }),\n getOutbound: () => fakeOutbound,\n send: (t) => sent.push(t),\n setTimeoutFn: () => 1 as unknown as ReturnType<typeof setTimeout>,\n clearTimeoutFn: () => {},\n });\n for (let i = 0; i < MAX_WINDOW_EVENTS; i++) {\n agg.ingestOne({ type: \"join\", player: `P${i}` });\n }\n assert.equal(sent.length, 1);\n assert.equal(agg.pendingCount(), 0);\n agg.dispose();\n});\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=qq-join.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"qq-join.test.d.ts","sourceRoot":"","sources":["../../src/domain/qq-join.test.ts"],"names":[],"mappings":""}
@@ -1,353 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { DatabaseSync } from "node:sqlite";
4
- import { initSchema } from "./schema.js";
5
- import { createQuery } from "../lib/sqlite.js";
6
- import { createQqJoinRoutes } from "../routes/qq-join.js";
7
- function memDb() {
8
- const db = new DatabaseSync(":memory:");
9
- initSchema(db);
10
- return { db, query: createQuery(db) };
11
- }
12
- function mockRes() {
13
- let status = 200;
14
- let body = {};
15
- return {
16
- res: {
17
- writeHead(code) {
18
- status = code;
19
- },
20
- end(s) {
21
- body = JSON.parse(s);
22
- }
23
- },
24
- get status() {
25
- return status;
26
- },
27
- get body() {
28
- return body;
29
- }
30
- };
31
- }
32
- function makeHandle(admins = ["admin1"], flags = {}) {
33
- const { query } = memDb();
34
- let bodyData = {};
35
- let joinFlags = {
36
- allowlistEnabled: flags.allowlistEnabled !== false,
37
- requireApproval: flags.requireApproval !== false,
38
- treatGroupAdminsAsAdmins: flags.treatGroupAdminsAsAdmins === true
39
- };
40
- const handle = createQqJoinRoutes({
41
- query,
42
- body: async () => bodyData,
43
- json: (res, data, status = 200) => {
44
- res.writeHead(status);
45
- res.end(JSON.stringify(data));
46
- },
47
- getAdminOpenids: () => admins,
48
- getJoinFlags: () => ({ ...joinFlags }),
49
- setJoinFlags: (partial) => {
50
- joinFlags = { ...joinFlags, ...partial };
51
- return { ...joinFlags };
52
- }
53
- });
54
- return {
55
- query,
56
- setBody(d) {
57
- bodyData = d;
58
- },
59
- handle,
60
- getFlags: () => ({ ...joinFlags })
61
- };
62
- }
63
- test("initSchema \u542B join / admin_actions \u8868", () => {
64
- const { db } = memDb();
65
- const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'sfmc_qq_%' ORDER BY name").all();
66
- assert.ok(tables.some((t) => t.name === "sfmc_qq_join_requests"));
67
- assert.ok(tables.some((t) => t.name === "sfmc_qq_admin_actions"));
68
- });
69
- test("join request \u2192 decide approve \u2192 apply-queue \u2192 applied", async () => {
70
- const { setBody, handle } = makeHandle(["admin1"]);
71
- setBody({ openid: "user1", player_name: "Steve", qq_backend: "official" });
72
- let m = mockRes();
73
- await handle({
74
- path: "/api/sfmc/qq/join/request",
75
- method: "POST",
76
- params: new URLSearchParams(),
77
- req: {},
78
- res: m.res
79
- });
80
- assert.equal(m.body.success, true);
81
- const id = String(m.body.id);
82
- assert.match(id, /^join_/);
83
- setBody({ id, decision: "approve", decided_by: "stranger" });
84
- m = mockRes();
85
- await handle({
86
- path: "/api/sfmc/qq/join/decide",
87
- method: "POST",
88
- params: new URLSearchParams(),
89
- req: {},
90
- res: m.res
91
- });
92
- assert.equal(m.status, 403);
93
- assert.equal(m.body.error, "not_admin");
94
- setBody({ id, decision: "approve", decided_by: "admin1" });
95
- m = mockRes();
96
- await handle({
97
- path: "/api/sfmc/qq/join/decide",
98
- method: "POST",
99
- params: new URLSearchParams(),
100
- req: {},
101
- res: m.res
102
- });
103
- assert.equal(m.body.success, true);
104
- assert.equal(m.body.status, "approved");
105
- m = mockRes();
106
- await handle({
107
- path: "/api/sfmc/qq/join/apply-queue",
108
- method: "GET",
109
- params: new URLSearchParams(),
110
- req: {},
111
- res: m.res
112
- });
113
- assert.equal(m.body.success, true);
114
- const queue = m.body.queue;
115
- assert.equal(queue.length, 1);
116
- assert.equal(queue[0].player_name, "Steve");
117
- setBody({ id, ok: true });
118
- m = mockRes();
119
- await handle({
120
- path: "/api/sfmc/qq/join/applied",
121
- method: "POST",
122
- params: new URLSearchParams(),
123
- req: {},
124
- res: m.res
125
- });
126
- assert.equal(m.body.success, true);
127
- m = mockRes();
128
- await handle({
129
- path: "/api/sfmc/qq/join/apply-queue",
130
- method: "GET",
131
- params: new URLSearchParams(),
132
- req: {},
133
- res: m.res
134
- });
135
- assert.equal(m.body.queue.length, 0);
136
- });
137
- test("join reject \u4E0D\u8FDB apply-queue", async () => {
138
- const { setBody, handle } = makeHandle(["admin1"]);
139
- setBody({ openid: "u2", player_name: "Alex" });
140
- let m = mockRes();
141
- await handle({
142
- path: "/api/sfmc/qq/join/request",
143
- method: "POST",
144
- params: new URLSearchParams(),
145
- req: {},
146
- res: m.res
147
- });
148
- const id = String(m.body.id);
149
- setBody({ id, decision: "reject", decided_by: "admin1" });
150
- m = mockRes();
151
- await handle({
152
- path: "/api/sfmc/qq/join/decide",
153
- method: "POST",
154
- params: new URLSearchParams(),
155
- req: {},
156
- res: m.res
157
- });
158
- assert.equal(m.body.status, "rejected");
159
- m = mockRes();
160
- await handle({
161
- path: "/api/sfmc/qq/join/apply-queue",
162
- method: "GET",
163
- params: new URLSearchParams(),
164
- req: {},
165
- res: m.res
166
- });
167
- assert.equal(m.body.queue.length, 0);
168
- });
169
- test("kick \u2192 action-queue \u2192 action-done", async () => {
170
- const { setBody, handle } = makeHandle(["admin1"]);
171
- setBody({ openid: "admin1", target_name: "Steve", reason: "test" });
172
- let m = mockRes();
173
- await handle({
174
- path: "/api/sfmc/qq/admin/kick",
175
- method: "POST",
176
- params: new URLSearchParams(),
177
- req: {},
178
- res: m.res
179
- });
180
- assert.equal(m.body.success, true);
181
- const id = String(m.body.id);
182
- m = mockRes();
183
- await handle({
184
- path: "/api/sfmc/qq/admin/action-queue",
185
- method: "GET",
186
- params: new URLSearchParams(),
187
- req: {},
188
- res: m.res
189
- });
190
- const queue = m.body.queue;
191
- assert.equal(queue.length, 1);
192
- assert.equal(queue[0].kind, "kick");
193
- assert.equal(queue[0].target_name, "Steve");
194
- setBody({ id, ok: true });
195
- m = mockRes();
196
- await handle({
197
- path: "/api/sfmc/qq/admin/action-done",
198
- method: "POST",
199
- params: new URLSearchParams(),
200
- req: {},
201
- res: m.res
202
- });
203
- assert.equal(m.body.success, true);
204
- m = mockRes();
205
- await handle({
206
- path: "/api/sfmc/qq/admin/action-queue",
207
- method: "GET",
208
- params: new URLSearchParams(),
209
- req: {},
210
- res: m.res
211
- });
212
- assert.equal(m.body.queue.length, 0);
213
- });
214
- test("\u767D\u540D\u5355\u5173\u95ED\u65F6\u62D2\u7EDD\u7533\u8BF7\u4E14 apply-queue \u7A7A", async () => {
215
- const { setBody, handle } = makeHandle(["admin1"], { allowlistEnabled: false });
216
- setBody({ openid: "u", player_name: "Steve" });
217
- let m = mockRes();
218
- await handle({
219
- path: "/api/sfmc/qq/join/request",
220
- method: "POST",
221
- params: new URLSearchParams(),
222
- req: {},
223
- res: m.res
224
- });
225
- assert.equal(m.status, 403);
226
- assert.equal(m.body.error, "join_allowlist_disabled");
227
- m = mockRes();
228
- await handle({
229
- path: "/api/sfmc/qq/join/apply-queue",
230
- method: "GET",
231
- params: new URLSearchParams(),
232
- req: {},
233
- res: m.res
234
- });
235
- assert.equal(m.body.queue.length, 0);
236
- });
237
- test("\u5173\u95ED\u5BA1\u6279\u65F6\u7533\u8BF7\u76F4\u63A5 approved", async () => {
238
- const { setBody, handle } = makeHandle(["admin1"], { requireApproval: false });
239
- setBody({ openid: "u", player_name: "Alex" });
240
- let m = mockRes();
241
- await handle({
242
- path: "/api/sfmc/qq/join/request",
243
- method: "POST",
244
- params: new URLSearchParams(),
245
- req: {},
246
- res: m.res
247
- });
248
- assert.equal(m.body.success, true);
249
- assert.equal(m.body.status, "approved");
250
- assert.equal(m.body.auto_approved, true);
251
- m = mockRes();
252
- await handle({
253
- path: "/api/sfmc/qq/join/apply-queue",
254
- method: "GET",
255
- params: new URLSearchParams(),
256
- req: {},
257
- res: m.res
258
- });
259
- assert.equal(m.body.queue.length, 1);
260
- });
261
- test("\u7BA1\u7406\u5458\u53EF\u6539 settings", async () => {
262
- const { setBody, handle, getFlags } = makeHandle(["admin1"]);
263
- let m = mockRes();
264
- await handle({
265
- path: "/api/sfmc/qq/join/settings",
266
- method: "GET",
267
- params: new URLSearchParams(),
268
- req: {},
269
- res: m.res
270
- });
271
- assert.equal(m.body.allowlist_enabled, true);
272
- assert.equal(m.body.require_approval, true);
273
- assert.equal(m.body.treat_group_admins_as_admins, false);
274
- setBody({ openid: "stranger", require_approval: false });
275
- m = mockRes();
276
- await handle({
277
- path: "/api/sfmc/qq/join/settings",
278
- method: "POST",
279
- params: new URLSearchParams(),
280
- req: {},
281
- res: m.res
282
- });
283
- assert.equal(m.status, 403);
284
- setBody({ openid: "admin1", require_approval: false, allowlist_enabled: true });
285
- m = mockRes();
286
- await handle({
287
- path: "/api/sfmc/qq/join/settings",
288
- method: "POST",
289
- params: new URLSearchParams(),
290
- req: {},
291
- res: m.res
292
- });
293
- assert.equal(m.body.success, true);
294
- assert.equal(m.body.require_approval, false);
295
- assert.equal(getFlags().requireApproval, false);
296
- });
297
- test("treat_group_admins_as_admins \u53EA\u8BFB\uFF1APOST \u6539\u5199\u88AB\u62D2\uFF1B\u7FA4\u7BA1\u53EF\u9274\u6743", async () => {
298
- const { setBody, handle, getFlags } = makeHandle([], { treatGroupAdminsAsAdmins: true });
299
- let m = mockRes();
300
- await handle({
301
- path: "/api/sfmc/qq/join/settings",
302
- method: "GET",
303
- params: new URLSearchParams(),
304
- req: {},
305
- res: m.res
306
- });
307
- assert.equal(m.body.treat_group_admins_as_admins, true);
308
- setBody({ openid: "gadmin", treat_group_admins_as_admins: false, as_group_admin: true });
309
- m = mockRes();
310
- await handle({
311
- path: "/api/sfmc/qq/join/settings",
312
- method: "POST",
313
- params: new URLSearchParams(),
314
- req: {},
315
- res: m.res
316
- });
317
- assert.equal(m.status, 400);
318
- assert.equal(m.body.error, "immutable_field");
319
- assert.equal(getFlags().treatGroupAdminsAsAdmins, true);
320
- setBody({ openid: "gadmin", player_name: "Alex", qq_backend: "official" });
321
- m = mockRes();
322
- await handle({
323
- path: "/api/sfmc/qq/join/request",
324
- method: "POST",
325
- params: new URLSearchParams(),
326
- req: {},
327
- res: m.res
328
- });
329
- const id = String(m.body.id);
330
- setBody({ id, decision: "approve", decided_by: "gadmin", as_group_admin: true });
331
- m = mockRes();
332
- await handle({
333
- path: "/api/sfmc/qq/join/decide",
334
- method: "POST",
335
- params: new URLSearchParams(),
336
- req: {},
337
- res: m.res
338
- });
339
- assert.equal(m.body.success, true);
340
- assert.equal(m.body.status, "approved");
341
- const denied = makeHandle([], { treatGroupAdminsAsAdmins: false });
342
- denied.setBody({ id: "x", decision: "approve", decided_by: "gadmin", as_group_admin: true });
343
- m = mockRes();
344
- await denied.handle({
345
- path: "/api/sfmc/qq/join/decide",
346
- method: "POST",
347
- params: new URLSearchParams(),
348
- req: {},
349
- res: m.res
350
- });
351
- assert.equal(m.status, 403);
352
- });
353
- //# sourceMappingURL=qq-join.test.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/domain/qq-join.test.ts"],
4
- "sourcesContent": ["/**\n * domain/qq-join.test.ts \u2014 \u5165\u670D\u5BA1\u6279\u72B6\u6001\u673A + \u8E22\u4EBA\u961F\u5217\n */\nimport assert from \"node:assert/strict\";\nimport test from \"node:test\";\nimport { DatabaseSync } from \"node:sqlite\";\nimport { initSchema } from \"./schema.js\";\nimport { createQuery } from \"../lib/sqlite.js\";\nimport { createQqJoinRoutes } from \"../routes/qq-join.js\";\n\nfunction memDb() {\n const db = new DatabaseSync(\":memory:\");\n initSchema(db);\n return { db, query: createQuery(db) };\n}\n\nfunction mockRes() {\n let status = 200;\n let body: Record<string, unknown> = {};\n return {\n res: {\n writeHead(code: number) {\n status = code;\n },\n end(s: string) {\n body = JSON.parse(s) as Record<string, unknown>;\n },\n } as unknown as import(\"http\").ServerResponse,\n get status() {\n return status;\n },\n get body() {\n return body;\n },\n };\n}\n\nfunction makeHandle(\n admins: string[] = [\"admin1\"],\n flags: {\n allowlistEnabled?: boolean;\n requireApproval?: boolean;\n treatGroupAdminsAsAdmins?: boolean;\n } = {}\n) {\n const { query } = memDb();\n let bodyData: Record<string, unknown> = {};\n let joinFlags = {\n allowlistEnabled: flags.allowlistEnabled !== false,\n requireApproval: flags.requireApproval !== false,\n treatGroupAdminsAsAdmins: flags.treatGroupAdminsAsAdmins === true,\n };\n const handle = createQqJoinRoutes({\n query,\n body: async () => bodyData,\n json: (res, data, status = 200) => {\n (res as unknown as { writeHead: (c: number) => void }).writeHead(status);\n (res as unknown as { end: (s: string) => void }).end(JSON.stringify(data));\n },\n getAdminOpenids: () => admins,\n getJoinFlags: () => ({ ...joinFlags }),\n setJoinFlags: (partial) => {\n joinFlags = { ...joinFlags, ...partial };\n return { ...joinFlags };\n },\n });\n return {\n query,\n setBody(d: Record<string, unknown>) {\n bodyData = d;\n },\n handle,\n getFlags: () => ({ ...joinFlags }),\n };\n}\n\ntest(\"initSchema \u542B join / admin_actions \u8868\", () => {\n const { db } = memDb();\n const tables = db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'sfmc_qq_%' ORDER BY name\")\n .all() as Array<{ name: string }>;\n assert.ok(tables.some((t) => t.name === \"sfmc_qq_join_requests\"));\n assert.ok(tables.some((t) => t.name === \"sfmc_qq_admin_actions\"));\n});\n\ntest(\"join request \u2192 decide approve \u2192 apply-queue \u2192 applied\", async () => {\n const { setBody, handle } = makeHandle([\"admin1\"]);\n\n setBody({ openid: \"user1\", player_name: \"Steve\", qq_backend: \"official\" });\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/request\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n const id = String(m.body.id);\n assert.match(id, /^join_/);\n\n setBody({ id, decision: \"approve\", decided_by: \"stranger\" });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/decide\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.status, 403);\n assert.equal(m.body.error, \"not_admin\");\n\n setBody({ id, decision: \"approve\", decided_by: \"admin1\" });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/decide\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n assert.equal(m.body.status, \"approved\");\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/apply-queue\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n const queue = m.body.queue as Array<{ id: string; player_name: string }>;\n assert.equal(queue.length, 1);\n assert.equal(queue[0]!.player_name, \"Steve\");\n\n setBody({ id, ok: true });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/applied\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/apply-queue\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal((m.body.queue as unknown[]).length, 0);\n});\n\ntest(\"join reject \u4E0D\u8FDB apply-queue\", async () => {\n const { setBody, handle } = makeHandle([\"admin1\"]);\n setBody({ openid: \"u2\", player_name: \"Alex\" });\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/request\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n const id = String(m.body.id);\n\n setBody({ id, decision: \"reject\", decided_by: \"admin1\" });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/decide\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.status, \"rejected\");\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/apply-queue\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal((m.body.queue as unknown[]).length, 0);\n});\n\ntest(\"kick \u2192 action-queue \u2192 action-done\", async () => {\n const { setBody, handle } = makeHandle([\"admin1\"]);\n\n setBody({ openid: \"admin1\", target_name: \"Steve\", reason: \"test\" });\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/admin/kick\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n const id = String(m.body.id);\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/admin/action-queue\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n const queue = m.body.queue as Array<{ id: string; kind: string; target_name: string }>;\n assert.equal(queue.length, 1);\n assert.equal(queue[0]!.kind, \"kick\");\n assert.equal(queue[0]!.target_name, \"Steve\");\n\n setBody({ id, ok: true });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/admin/action-done\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/admin/action-queue\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal((m.body.queue as unknown[]).length, 0);\n});\n\ntest(\"\u767D\u540D\u5355\u5173\u95ED\u65F6\u62D2\u7EDD\u7533\u8BF7\u4E14 apply-queue \u7A7A\", async () => {\n const { setBody, handle } = makeHandle([\"admin1\"], { allowlistEnabled: false });\n setBody({ openid: \"u\", player_name: \"Steve\" });\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/request\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.status, 403);\n assert.equal(m.body.error, \"join_allowlist_disabled\");\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/apply-queue\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal((m.body.queue as unknown[]).length, 0);\n});\n\ntest(\"\u5173\u95ED\u5BA1\u6279\u65F6\u7533\u8BF7\u76F4\u63A5 approved\", async () => {\n const { setBody, handle } = makeHandle([\"admin1\"], { requireApproval: false });\n setBody({ openid: \"u\", player_name: \"Alex\" });\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/request\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n assert.equal(m.body.status, \"approved\");\n assert.equal(m.body.auto_approved, true);\n\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/apply-queue\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal((m.body.queue as unknown[]).length, 1);\n});\n\ntest(\"\u7BA1\u7406\u5458\u53EF\u6539 settings\", async () => {\n const { setBody, handle, getFlags } = makeHandle([\"admin1\"]);\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/settings\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.allowlist_enabled, true);\n assert.equal(m.body.require_approval, true);\n assert.equal(m.body.treat_group_admins_as_admins, false);\n\n setBody({ openid: \"stranger\", require_approval: false });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/settings\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.status, 403);\n\n setBody({ openid: \"admin1\", require_approval: false, allowlist_enabled: true });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/settings\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n assert.equal(m.body.require_approval, false);\n assert.equal(getFlags().requireApproval, false);\n});\n\ntest(\"treat_group_admins_as_admins \u53EA\u8BFB\uFF1APOST \u6539\u5199\u88AB\u62D2\uFF1B\u7FA4\u7BA1\u53EF\u9274\u6743\", async () => {\n const { setBody, handle, getFlags } = makeHandle([], { treatGroupAdminsAsAdmins: true });\n\n let m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/settings\",\n method: \"GET\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.treat_group_admins_as_admins, true);\n\n setBody({ openid: \"gadmin\", treat_group_admins_as_admins: false, as_group_admin: true });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/settings\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.status, 400);\n assert.equal(m.body.error, \"immutable_field\");\n assert.equal(getFlags().treatGroupAdminsAsAdmins, true);\n\n setBody({ openid: \"gadmin\", player_name: \"Alex\", qq_backend: \"official\" });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/request\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n const id = String(m.body.id);\n\n setBody({ id, decision: \"approve\", decided_by: \"gadmin\", as_group_admin: true });\n m = mockRes();\n await handle({\n path: \"/api/sfmc/qq/join/decide\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.body.success, true);\n assert.equal(m.body.status, \"approved\");\n\n const denied = makeHandle([], { treatGroupAdminsAsAdmins: false });\n denied.setBody({ id: \"x\", decision: \"approve\", decided_by: \"gadmin\", as_group_admin: true });\n m = mockRes();\n await denied.handle({\n path: \"/api/sfmc/qq/join/decide\",\n method: \"POST\",\n params: new URLSearchParams(),\n req: {} as import(\"http\").IncomingMessage,\n res: m.res,\n });\n assert.equal(m.status, 403);\n});\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,5 +0,0 @@
1
- /**
2
- * domain/system-status.test.ts — formatUptime / host 快照形状
3
- */
4
- export {};
5
- //# sourceMappingURL=system-status.test.d.ts.map