@mdrv/opencode-quota 262.0.0
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/LICENSE +21 -0
- package/README.md +189 -0
- package/bin/copilot-quota.ts +374 -0
- package/bin/glm-quota.ts +467 -0
- package/bin/install.js +439 -0
- package/bin/kimi-quota.ts +314 -0
- package/dist/bin/copilot-quota.d.ts +8 -0
- package/dist/bin/copilot-quota.d.ts.map +1 -0
- package/dist/bin/copilot-quota.js +298 -0
- package/dist/bin/copilot-quota.js.map +1 -0
- package/dist/bin/glm-quota.d.ts +8 -0
- package/dist/bin/glm-quota.d.ts.map +1 -0
- package/dist/bin/glm-quota.js +367 -0
- package/dist/bin/glm-quota.js.map +1 -0
- package/dist/bin/kimi-quota.d.ts +3 -0
- package/dist/bin/kimi-quota.d.ts.map +1 -0
- package/dist/bin/kimi-quota.js +241 -0
- package/dist/bin/kimi-quota.js.map +1 -0
- package/dist/src/api/client.d.ts +76 -0
- package/dist/src/api/client.d.ts.map +1 -0
- package/dist/src/api/client.js +203 -0
- package/dist/src/api/client.js.map +1 -0
- package/dist/src/api/endpoints.d.ts +22 -0
- package/dist/src/api/endpoints.d.ts.map +1 -0
- package/dist/src/api/endpoints.js +41 -0
- package/dist/src/api/endpoints.js.map +1 -0
- package/dist/src/api/platforms.d.ts +20 -0
- package/dist/src/api/platforms.d.ts.map +1 -0
- package/dist/src/api/platforms.js +38 -0
- package/dist/src/api/platforms.js.map +1 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +723 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/shared/logging.d.ts +7 -0
- package/dist/src/shared/logging.d.ts.map +1 -0
- package/dist/src/shared/logging.js +29 -0
- package/dist/src/shared/logging.js.map +1 -0
- package/dist/src/utils/box-constants.d.ts +43 -0
- package/dist/src/utils/box-constants.d.ts.map +1 -0
- package/dist/src/utils/box-constants.js +43 -0
- package/dist/src/utils/box-constants.js.map +1 -0
- package/dist/src/utils/date-formatter.d.ts +17 -0
- package/dist/src/utils/date-formatter.d.ts.map +1 -0
- package/dist/src/utils/date-formatter.js +33 -0
- package/dist/src/utils/date-formatter.js.map +1 -0
- package/dist/src/utils/error-formatter.d.ts +17 -0
- package/dist/src/utils/error-formatter.d.ts.map +1 -0
- package/dist/src/utils/error-formatter.js +60 -0
- package/dist/src/utils/error-formatter.js.map +1 -0
- package/dist/src/utils/progress-bar.d.ts +35 -0
- package/dist/src/utils/progress-bar.d.ts.map +1 -0
- package/dist/src/utils/progress-bar.js +43 -0
- package/dist/src/utils/progress-bar.js.map +1 -0
- package/dist/src/utils/reset-timer.d.ts +11 -0
- package/dist/src/utils/reset-timer.d.ts.map +1 -0
- package/dist/src/utils/reset-timer.js +32 -0
- package/dist/src/utils/reset-timer.js.map +1 -0
- package/dist/src/utils/time-window.d.ts +30 -0
- package/dist/src/utils/time-window.d.ts.map +1 -0
- package/dist/src/utils/time-window.js +34 -0
- package/dist/src/utils/time-window.js.map +1 -0
- package/dist/tests/error-handling/api-errors.test.d.ts +7 -0
- package/dist/tests/error-handling/api-errors.test.d.ts.map +1 -0
- package/dist/tests/error-handling/api-errors.test.js +110 -0
- package/dist/tests/error-handling/api-errors.test.js.map +1 -0
- package/dist/tests/error-handling/auth-errors.test.d.ts +7 -0
- package/dist/tests/error-handling/auth-errors.test.d.ts.map +1 -0
- package/dist/tests/error-handling/auth-errors.test.js +110 -0
- package/dist/tests/error-handling/auth-errors.test.js.map +1 -0
- package/dist/tests/error-handling/network-errors.test.d.ts +7 -0
- package/dist/tests/error-handling/network-errors.test.d.ts.map +1 -0
- package/dist/tests/error-handling/network-errors.test.js +94 -0
- package/dist/tests/error-handling/network-errors.test.js.map +1 -0
- package/dist/tests/error-handling/parse-errors.test.d.ts +7 -0
- package/dist/tests/error-handling/parse-errors.test.d.ts.map +1 -0
- package/dist/tests/error-handling/parse-errors.test.js +87 -0
- package/dist/tests/error-handling/parse-errors.test.js.map +1 -0
- package/dist/tests/error-handling/token-sanitization.test.d.ts +2 -0
- package/dist/tests/error-handling/token-sanitization.test.d.ts.map +1 -0
- package/dist/tests/error-handling/token-sanitization.test.js +59 -0
- package/dist/tests/error-handling/token-sanitization.test.js.map +1 -0
- package/dist/tests/functional/date-formatter.test.d.ts +5 -0
- package/dist/tests/functional/date-formatter.test.d.ts.map +1 -0
- package/dist/tests/functional/date-formatter.test.js +46 -0
- package/dist/tests/functional/date-formatter.test.js.map +1 -0
- package/dist/tests/functional/progress-bar.test.d.ts +5 -0
- package/dist/tests/functional/progress-bar.test.d.ts.map +1 -0
- package/dist/tests/functional/progress-bar.test.js +82 -0
- package/dist/tests/functional/progress-bar.test.js.map +1 -0
- package/dist/tests/functional/reset-timer.test.d.ts +6 -0
- package/dist/tests/functional/reset-timer.test.d.ts.map +1 -0
- package/dist/tests/functional/reset-timer.test.js +67 -0
- package/dist/tests/functional/reset-timer.test.js.map +1 -0
- package/dist/tests/functional/time-window.test.d.ts +5 -0
- package/dist/tests/functional/time-window.test.d.ts.map +1 -0
- package/dist/tests/functional/time-window.test.js +46 -0
- package/dist/tests/functional/time-window.test.js.map +1 -0
- package/dist/tests/integration/box-alignment.test.d.ts +8 -0
- package/dist/tests/integration/box-alignment.test.d.ts.map +1 -0
- package/dist/tests/integration/box-alignment.test.js +238 -0
- package/dist/tests/integration/box-alignment.test.js.map +1 -0
- package/dist/tests/integration/error-handling.test.d.ts +2 -0
- package/dist/tests/integration/error-handling.test.d.ts.map +1 -0
- package/dist/tests/integration/error-handling.test.js +36 -0
- package/dist/tests/integration/error-handling.test.js.map +1 -0
- package/dist/tests/integration/installer-config.test.d.ts +2 -0
- package/dist/tests/integration/installer-config.test.d.ts.map +1 -0
- package/dist/tests/integration/installer-config.test.js +65 -0
- package/dist/tests/integration/installer-config.test.js.map +1 -0
- package/dist/tests/integration/plugin-catch-block.test.d.ts +2 -0
- package/dist/tests/integration/plugin-catch-block.test.d.ts.map +1 -0
- package/dist/tests/integration/plugin-catch-block.test.js +134 -0
- package/dist/tests/integration/plugin-catch-block.test.js.map +1 -0
- package/dist/tests/integration/reset-time-display.test.d.ts +6 -0
- package/dist/tests/integration/reset-time-display.test.d.ts.map +1 -0
- package/dist/tests/integration/reset-time-display.test.js +138 -0
- package/dist/tests/integration/reset-time-display.test.js.map +1 -0
- package/dist/tests/module/http-client.test.d.ts +2 -0
- package/dist/tests/module/http-client.test.d.ts.map +1 -0
- package/dist/tests/module/http-client.test.js +49 -0
- package/dist/tests/module/http-client.test.js.map +1 -0
- package/dist/tests/module/platform-detection.test.d.ts +5 -0
- package/dist/tests/module/platform-detection.test.d.ts.map +1 -0
- package/dist/tests/module/platform-detection.test.js +48 -0
- package/dist/tests/module/platform-detection.test.js.map +1 -0
- package/integration/agents/copilot-quota-exec.md +20 -0
- package/integration/agents/glm-quota-exec.md +20 -0
- package/integration/command/copilot_quota.md +6 -0
- package/integration/command/glm_quota.md +6 -0
- package/integration/skills/copilot-quota/SKILL.md +11 -0
- package/integration/skills/glm-quota/SKILL.md +11 -0
- package/package.json +69 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functional tests for progress bar module
|
|
3
|
+
*/
|
|
4
|
+
import assert from 'node:assert';
|
|
5
|
+
import { describe, test } from 'node:test';
|
|
6
|
+
import { createProgressBar, formatPercentage, formatProgressLine } from '../../src/utils/progress-bar.js';
|
|
7
|
+
describe('createProgressBar', () => {
|
|
8
|
+
test('creates full bar at 100%', () => {
|
|
9
|
+
const result = createProgressBar(100);
|
|
10
|
+
assert.strictEqual(result.length, 30);
|
|
11
|
+
assert.ok(result.includes('#'));
|
|
12
|
+
assert.ok(!result.includes('-'));
|
|
13
|
+
});
|
|
14
|
+
test('creates empty bar at 0%', () => {
|
|
15
|
+
const result = createProgressBar(0);
|
|
16
|
+
assert.strictEqual(result.length, 30);
|
|
17
|
+
assert.ok(!result.includes('#'));
|
|
18
|
+
assert.ok(result.includes('-'));
|
|
19
|
+
});
|
|
20
|
+
test('creates half-filled bar at 50%', () => {
|
|
21
|
+
const result = createProgressBar(50);
|
|
22
|
+
assert.strictEqual(result.length, 30);
|
|
23
|
+
const filledCount = (result.match(/#/g) || []).length;
|
|
24
|
+
const emptyCount = (result.match(/-/g) || []).length;
|
|
25
|
+
assert.strictEqual(filledCount, 15);
|
|
26
|
+
assert.strictEqual(emptyCount, 15);
|
|
27
|
+
});
|
|
28
|
+
test('clamps percentage to 100', () => {
|
|
29
|
+
const result = createProgressBar(150);
|
|
30
|
+
assert.strictEqual(result.length, 30);
|
|
31
|
+
assert.ok(result.includes('#'));
|
|
32
|
+
assert.ok(!result.includes('-'));
|
|
33
|
+
});
|
|
34
|
+
test('clamps percentage to 0', () => {
|
|
35
|
+
const result = createProgressBar(-50);
|
|
36
|
+
assert.strictEqual(result.length, 30);
|
|
37
|
+
assert.ok(!result.includes('#'));
|
|
38
|
+
assert.ok(result.includes('-'));
|
|
39
|
+
});
|
|
40
|
+
test('uses custom width', () => {
|
|
41
|
+
const result = createProgressBar(50, { width: 10 });
|
|
42
|
+
assert.strictEqual(result.length, 10);
|
|
43
|
+
});
|
|
44
|
+
test('uses custom characters', () => {
|
|
45
|
+
const result = createProgressBar(50, {
|
|
46
|
+
filledChar: '■',
|
|
47
|
+
emptyChar: '□',
|
|
48
|
+
});
|
|
49
|
+
assert.ok(result.includes('■'));
|
|
50
|
+
assert.ok(result.includes('□'));
|
|
51
|
+
assert.ok(!result.includes('#'));
|
|
52
|
+
assert.ok(!result.includes('-'));
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe('formatPercentage', () => {
|
|
56
|
+
test('formats with one decimal', () => {
|
|
57
|
+
assert.strictEqual(formatPercentage(40.5), '40.5%');
|
|
58
|
+
});
|
|
59
|
+
test('formats whole number', () => {
|
|
60
|
+
assert.strictEqual(formatPercentage(100), '100.0%');
|
|
61
|
+
});
|
|
62
|
+
test('formats zero', () => {
|
|
63
|
+
assert.strictEqual(formatPercentage(0), '0.0%');
|
|
64
|
+
});
|
|
65
|
+
test('formats with custom decimals', () => {
|
|
66
|
+
assert.strictEqual(formatPercentage(33.333, 2), '33.33%');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe('formatProgressLine', () => {
|
|
70
|
+
test('formats complete line', () => {
|
|
71
|
+
const result = formatProgressLine('Test Label', 75);
|
|
72
|
+
assert.ok(result.includes('Test Label'));
|
|
73
|
+
assert.ok(result.includes('75.0%'));
|
|
74
|
+
assert.ok(result.includes('#'));
|
|
75
|
+
assert.ok(result.includes('-'));
|
|
76
|
+
});
|
|
77
|
+
test('pads label to 20 characters', () => {
|
|
78
|
+
const result = formatProgressLine('Short', 50);
|
|
79
|
+
assert.ok(result.startsWith('Short'));
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=progress-bar.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress-bar.test.js","sourceRoot":"","sources":["../../../tests/functional/progress-bar.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reset-timer.test.d.ts","sourceRoot":"","sources":["../../../tests/functional/reset-timer.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for reset timer utility
|
|
3
|
+
* Test formatTimeUntilReset() function following TDD
|
|
4
|
+
*/
|
|
5
|
+
import assert from 'node:assert';
|
|
6
|
+
import { afterEach, beforeEach, describe, test } from 'node:test';
|
|
7
|
+
import { formatTimeUntilReset } from '../../src/utils/reset-timer.js';
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// TESTS FOR formatTimeUntilReset()
|
|
10
|
+
// ============================================================================
|
|
11
|
+
describe('formatTimeUntilReset', () => {
|
|
12
|
+
const fixedNow = 1737763200000;
|
|
13
|
+
const originalNow = Date.now;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
Date.now = () => fixedNow;
|
|
16
|
+
});
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
Date.now = originalNow;
|
|
19
|
+
});
|
|
20
|
+
test('should return empty string for null timestamp', () => {
|
|
21
|
+
const result = formatTimeUntilReset(null);
|
|
22
|
+
assert.strictEqual(result, '');
|
|
23
|
+
});
|
|
24
|
+
test('should return empty string for undefined timestamp', () => {
|
|
25
|
+
const result = formatTimeUntilReset(undefined);
|
|
26
|
+
assert.strictEqual(result, '');
|
|
27
|
+
});
|
|
28
|
+
test('should format 1 hour 30 minutes', () => {
|
|
29
|
+
// Current time + 1.5 hours
|
|
30
|
+
const resetTime = fixedNow + (90 * 60 * 1000);
|
|
31
|
+
const result = formatTimeUntilReset(resetTime);
|
|
32
|
+
assert.strictEqual(result, 'Resets in 1 hours 30 minutes');
|
|
33
|
+
});
|
|
34
|
+
test('should format 4 hours 42 minutes', () => {
|
|
35
|
+
// Current time + 4h 42m
|
|
36
|
+
const resetTime = fixedNow + (4 * 60 * 60 * 1000) + (42 * 60 * 1000);
|
|
37
|
+
const result = formatTimeUntilReset(resetTime);
|
|
38
|
+
assert.strictEqual(result, 'Resets in 4 hours 42 minutes');
|
|
39
|
+
});
|
|
40
|
+
test('should format only hours when minutes is 0', () => {
|
|
41
|
+
// Current time + 5 hours
|
|
42
|
+
const resetTime = fixedNow + (5 * 60 * 60 * 1000);
|
|
43
|
+
const result = formatTimeUntilReset(resetTime);
|
|
44
|
+
assert.strictEqual(result, 'Resets in 5 hours 0 minutes');
|
|
45
|
+
});
|
|
46
|
+
test('should format only minutes when hours is 0', () => {
|
|
47
|
+
// Current time + 45 minutes
|
|
48
|
+
const resetTime = fixedNow + (45 * 60 * 1000);
|
|
49
|
+
const result = formatTimeUntilReset(resetTime);
|
|
50
|
+
assert.strictEqual(result, 'Resets in 0 hours 45 minutes');
|
|
51
|
+
});
|
|
52
|
+
test('should return empty string for past timestamp', () => {
|
|
53
|
+
// Current time - 1 hour (already passed)
|
|
54
|
+
const resetTime = fixedNow - (60 * 60 * 1000);
|
|
55
|
+
const result = formatTimeUntilReset(resetTime);
|
|
56
|
+
assert.strictEqual(result, '');
|
|
57
|
+
});
|
|
58
|
+
test('should return empty string for invalid timestamp', () => {
|
|
59
|
+
const result = formatTimeUntilReset(-1);
|
|
60
|
+
assert.strictEqual(result, '');
|
|
61
|
+
});
|
|
62
|
+
test('should return empty string for non-numeric timestamp', () => {
|
|
63
|
+
const result = formatTimeUntilReset('invalid');
|
|
64
|
+
assert.strictEqual(result, '');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=reset-timer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reset-timer.test.js","sourceRoot":"","sources":["../../../tests/functional/reset-timer.test.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,MAAM,MAAM,aAAa,CAAA;AAChC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAA;AAErE,+EAA+E;AAC/E,mCAAmC;AACnC,+EAA+E;AAE/E,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACrC,MAAM,QAAQ,GAAG,aAAa,CAAA;IAC9B,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAA;IAE5B,UAAU,CAAC,GAAG,EAAE;QACf,IAAI,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAA;IAC1B,CAAC,CAAC,CAAA;IAEF,SAAS,CAAC,GAAG,EAAE;QACd,IAAI,CAAC,GAAG,GAAG,WAAW,CAAA;IACvB,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;QAC1D,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAA;QACzC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC/D,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC9C,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC5C,2BAA2B;QAC3B,MAAM,SAAS,GAAG,QAAQ,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;QAC7C,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC9C,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,8BAA8B,CAAC,CAAA;IAC3D,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC7C,wBAAwB;QACxB,MAAM,SAAS,GAAG,QAAQ,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;QACpE,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC9C,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,8BAA8B,CAAC,CAAA;IAC3D,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACvD,yBAAyB;QACzB,MAAM,SAAS,GAAG,QAAQ,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;QACjD,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC9C,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,6BAA6B,CAAC,CAAA;IAC1D,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACvD,4BAA4B;QAC5B,MAAM,SAAS,GAAG,QAAQ,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;QAC7C,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC9C,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,8BAA8B,CAAC,CAAA;IAC3D,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;QAC1D,yCAAyC;QACzC,MAAM,SAAS,GAAG,QAAQ,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;QAC7C,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC9C,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC7D,MAAM,MAAM,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAA;QACvC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,sDAAsD,EAAE,GAAG,EAAE;QACjE,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAA8B,CAAC,CAAA;QACnE,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time-window.test.d.ts","sourceRoot":"","sources":["../../../tests/functional/time-window.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functional tests for time window module
|
|
3
|
+
*/
|
|
4
|
+
import assert from 'node:assert';
|
|
5
|
+
import { describe, test } from 'node:test';
|
|
6
|
+
import { getTimeWindow, getTimeWindowQueryParams } from '../../src/utils/time-window.js';
|
|
7
|
+
describe('getTimeWindow', () => {
|
|
8
|
+
test('returns 24-hour rolling window', () => {
|
|
9
|
+
// Mock Date to 2026-01-18 14:30:00
|
|
10
|
+
const mockNow = new Date(2026, 0, 18, 14, 30, 0, 0);
|
|
11
|
+
const { startTime, endTime } = getTimeWindow(mockNow);
|
|
12
|
+
assert.strictEqual(startTime, '2026-01-17 14:00:00');
|
|
13
|
+
assert.strictEqual(endTime, '2026-01-18 14:59:59');
|
|
14
|
+
});
|
|
15
|
+
test('start is yesterday at same hour', () => {
|
|
16
|
+
const mockNow = new Date(2026, 5, 15, 10, 0, 0, 0);
|
|
17
|
+
const { startTime, endTime } = getTimeWindow(mockNow);
|
|
18
|
+
assert.ok(startTime.endsWith('10:00:00'));
|
|
19
|
+
assert.ok(startTime.startsWith('2026-06-14'));
|
|
20
|
+
});
|
|
21
|
+
test('end is today at 59:59:999', () => {
|
|
22
|
+
const mockNow = new Date(2026, 5, 15, 10, 0, 0, 0);
|
|
23
|
+
const { startTime, endTime } = getTimeWindow(mockNow);
|
|
24
|
+
assert.ok(endTime.endsWith('10:59:59'));
|
|
25
|
+
assert.ok(endTime.startsWith('2026-06-15'));
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('getTimeWindowQueryParams', () => {
|
|
29
|
+
test('returns URL-encoded query string', () => {
|
|
30
|
+
const mockNow = new Date(2026, 0, 18, 14, 30, 0, 0);
|
|
31
|
+
const result = getTimeWindowQueryParams(mockNow);
|
|
32
|
+
assert.ok(result.startsWith('startTime='));
|
|
33
|
+
assert.ok(result.includes('&endTime='));
|
|
34
|
+
assert.ok(result.includes('2026-01-17'));
|
|
35
|
+
assert.ok(result.includes('2026-01-18'));
|
|
36
|
+
});
|
|
37
|
+
test('contains encoded spaces and colons', () => {
|
|
38
|
+
const mockNow = new Date(2026, 0, 18, 14, 30, 0, 0);
|
|
39
|
+
const result = getTimeWindowQueryParams(mockNow);
|
|
40
|
+
// Spaces should be encoded as %20
|
|
41
|
+
assert.ok(result.includes('%20'));
|
|
42
|
+
// Colons should be encoded as %3A
|
|
43
|
+
assert.ok(result.includes('%3A'));
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
//# sourceMappingURL=time-window.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time-window.test.js","sourceRoot":"","sources":["../../../tests/functional/time-window.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"box-alignment.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/box-alignment.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Box Alignment Validation Tests
|
|
3
|
+
*
|
|
4
|
+
* Ensures all output lines maintain consistent 60-character width
|
|
5
|
+
* and proper box drawing alignment.
|
|
6
|
+
*/
|
|
7
|
+
import * as assert from 'node:assert';
|
|
8
|
+
import { describe, it } from 'node:test';
|
|
9
|
+
// Mock data for testing
|
|
10
|
+
const mockQuotaData = {
|
|
11
|
+
limits: [
|
|
12
|
+
{
|
|
13
|
+
type: 'MCP usage(1 Month)',
|
|
14
|
+
percentage: 1.0,
|
|
15
|
+
currentValue: 4,
|
|
16
|
+
total: 1000,
|
|
17
|
+
usageDetails: [
|
|
18
|
+
{ modelCode: 'search-prime', usage: 0 },
|
|
19
|
+
{ modelCode: 'web-reader', usage: 0 },
|
|
20
|
+
{ modelCode: 'zread', usage: 4 },
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
type: 'Token usage(5 Hour)',
|
|
25
|
+
percentage: 1.0,
|
|
26
|
+
nextResetTime: Date.now() + 5 * 60 * 60 * 1000, // 5 hours from now
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
const mockModelData = {
|
|
31
|
+
totalUsage: {
|
|
32
|
+
totalModelCallCount: 0,
|
|
33
|
+
totalTokensUsage: 0,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
const mockToolData = {
|
|
37
|
+
totalUsage: {
|
|
38
|
+
totalNetworkSearchCount: 0,
|
|
39
|
+
totalWebReadMcpCount: 0,
|
|
40
|
+
totalZreadMcpCount: 4,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Get display width of a string (handles Unicode)
|
|
45
|
+
*/
|
|
46
|
+
function getDisplayWidth(text) {
|
|
47
|
+
let width = 0;
|
|
48
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
49
|
+
const codePoint = text.codePointAt(i);
|
|
50
|
+
if (codePoint === undefined) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (codePoint > 0xffff) {
|
|
54
|
+
i += 1;
|
|
55
|
+
}
|
|
56
|
+
if (isControlCodePoint(codePoint) || isZeroWidthCodePoint(codePoint)) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
width += isEmojiCodePoint(codePoint) || isFullWidthCodePoint(codePoint) ? 2 : 1;
|
|
60
|
+
}
|
|
61
|
+
return width;
|
|
62
|
+
}
|
|
63
|
+
function isControlCodePoint(codePoint) {
|
|
64
|
+
return codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f);
|
|
65
|
+
}
|
|
66
|
+
function isZeroWidthCodePoint(codePoint) {
|
|
67
|
+
return (codePoint === 0x200d
|
|
68
|
+
|| codePoint === 0xfe0f
|
|
69
|
+
|| (codePoint >= 0xfe00 && codePoint <= 0xfe0f));
|
|
70
|
+
}
|
|
71
|
+
function isEmojiCodePoint(codePoint) {
|
|
72
|
+
return ((codePoint >= 0x1f300 && codePoint <= 0x1f5ff)
|
|
73
|
+
|| (codePoint >= 0x1f600 && codePoint <= 0x1f64f)
|
|
74
|
+
|| (codePoint >= 0x1f680 && codePoint <= 0x1f6ff)
|
|
75
|
+
|| (codePoint >= 0x1f700 && codePoint <= 0x1f77f)
|
|
76
|
+
|| (codePoint >= 0x1f780 && codePoint <= 0x1f7ff)
|
|
77
|
+
|| (codePoint >= 0x1f800 && codePoint <= 0x1f8ff)
|
|
78
|
+
|| (codePoint >= 0x1f900 && codePoint <= 0x1f9ff)
|
|
79
|
+
|| (codePoint >= 0x1fa00 && codePoint <= 0x1faff)
|
|
80
|
+
|| (codePoint >= 0x2600 && codePoint <= 0x26ff)
|
|
81
|
+
|| (codePoint >= 0x2700 && codePoint <= 0x27bf));
|
|
82
|
+
}
|
|
83
|
+
function isFullWidthCodePoint(codePoint) {
|
|
84
|
+
return (codePoint >= 0x1100 && (codePoint <= 0x115f
|
|
85
|
+
|| codePoint === 0x2329
|
|
86
|
+
|| codePoint === 0x232a
|
|
87
|
+
|| (codePoint >= 0x2e80 && codePoint <= 0x3247 && codePoint !== 0x303f)
|
|
88
|
+
|| (codePoint >= 0x3250 && codePoint <= 0x4dbf)
|
|
89
|
+
|| (codePoint >= 0x4e00 && codePoint <= 0xa4c6)
|
|
90
|
+
|| (codePoint >= 0xa960 && codePoint <= 0xa97c)
|
|
91
|
+
|| (codePoint >= 0xac00 && codePoint <= 0xd7a3)
|
|
92
|
+
|| (codePoint >= 0xf900 && codePoint <= 0xfaff)
|
|
93
|
+
|| (codePoint >= 0xfe10 && codePoint <= 0xfe19)
|
|
94
|
+
|| (codePoint >= 0xfe30 && codePoint <= 0xfe6b)
|
|
95
|
+
|| (codePoint >= 0xff01 && codePoint <= 0xff60)
|
|
96
|
+
|| (codePoint >= 0xffe0 && codePoint <= 0xffe6)
|
|
97
|
+
|| (codePoint >= 0x1b000 && codePoint <= 0x1b001)
|
|
98
|
+
|| (codePoint >= 0x1f200 && codePoint <= 0x1f251)
|
|
99
|
+
|| (codePoint >= 0x20000 && codePoint <= 0x3fffd)));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Generate test output using the plugin
|
|
103
|
+
*/
|
|
104
|
+
async function generateTestOutput() {
|
|
105
|
+
// Import the plugin dynamically
|
|
106
|
+
const plugin = await import('../../src/index.js');
|
|
107
|
+
const instance = await plugin.GlmQuotaPlugin(null);
|
|
108
|
+
// The tool.execute function signature requires args and context
|
|
109
|
+
// We pass empty objects since our tool doesn't use them
|
|
110
|
+
const execute = instance.tool.glm_quota.execute;
|
|
111
|
+
const result = await execute({}, {});
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
describe('Box Alignment Validation', () => {
|
|
115
|
+
it('all output lines are exactly 60 characters wide', async () => {
|
|
116
|
+
const output = await generateTestOutput();
|
|
117
|
+
const lines = output.split('\n').filter(line => line.length > 0);
|
|
118
|
+
for (const line of lines) {
|
|
119
|
+
const displayWidth = getDisplayWidth(line);
|
|
120
|
+
assert.strictEqual(displayWidth, 60, `Line has incorrect width (${displayWidth}): "${line}"`);
|
|
121
|
+
}
|
|
122
|
+
// Ensure we have output
|
|
123
|
+
assert.ok(lines.length > 0, 'Output should contain lines');
|
|
124
|
+
});
|
|
125
|
+
it('top border matches bottom border width', async () => {
|
|
126
|
+
const output = await generateTestOutput();
|
|
127
|
+
const lines = output.split('\n').filter(line => line.length > 0);
|
|
128
|
+
const topBorder = lines[0];
|
|
129
|
+
const bottomBorder = lines[lines.length - 1];
|
|
130
|
+
assert.ok(topBorder.startsWith('╔'), 'First line should be top border');
|
|
131
|
+
assert.ok(bottomBorder.startsWith('╚'), 'Last line should be bottom border');
|
|
132
|
+
assert.strictEqual(getDisplayWidth(topBorder), getDisplayWidth(bottomBorder), 'Top and bottom borders should have same width');
|
|
133
|
+
});
|
|
134
|
+
it('all section dividers are 60 characters', async () => {
|
|
135
|
+
const output = await generateTestOutput();
|
|
136
|
+
const lines = output.split('\n').filter(line => line.length > 0);
|
|
137
|
+
const dividers = lines.filter(line => line.startsWith('╠') || line.startsWith('╟'));
|
|
138
|
+
for (const divider of dividers) {
|
|
139
|
+
const displayWidth = getDisplayWidth(divider);
|
|
140
|
+
assert.strictEqual(displayWidth, 60, `Divider has incorrect width: "${divider}"`);
|
|
141
|
+
}
|
|
142
|
+
// Dividers are optional (may not exist in error message output)
|
|
143
|
+
// Just ensure if they exist, they're correctly sized
|
|
144
|
+
});
|
|
145
|
+
it('content lines maintain consistent padding', async () => {
|
|
146
|
+
const output = await generateTestOutput();
|
|
147
|
+
const lines = output.split('\n').filter(line => line.length > 0);
|
|
148
|
+
const contentLines = lines.filter(line => line.startsWith('║') && !line.startsWith('╠') && !line.startsWith('╟'));
|
|
149
|
+
for (const line of contentLines) {
|
|
150
|
+
assert.ok(line.startsWith('║'), 'Content line should start with ║');
|
|
151
|
+
assert.ok(line.endsWith('║'), 'Content line should end with ║');
|
|
152
|
+
const displayWidth = getDisplayWidth(line);
|
|
153
|
+
assert.strictEqual(displayWidth, 60, `Content line has incorrect width: "${line}"`);
|
|
154
|
+
}
|
|
155
|
+
// Ensure we have content lines
|
|
156
|
+
assert.ok(contentLines.length > 0, 'Output should contain content lines');
|
|
157
|
+
});
|
|
158
|
+
it('progress bar lines fit within box width', async () => {
|
|
159
|
+
const output = await generateTestOutput();
|
|
160
|
+
const lines = output.split('\n').filter(line => line.length > 0);
|
|
161
|
+
const progressLines = lines.filter(line => line.includes('[') && line.includes(']'));
|
|
162
|
+
for (const line of progressLines) {
|
|
163
|
+
const displayWidth = getDisplayWidth(line);
|
|
164
|
+
assert.strictEqual(displayWidth, 60, `Progress bar line has incorrect width: "${line}"`);
|
|
165
|
+
}
|
|
166
|
+
// Progress bars are optional (may not exist in error message output)
|
|
167
|
+
// Just ensure if they exist, they're correctly sized
|
|
168
|
+
});
|
|
169
|
+
it('handles empty sections with proper alignment', async () => {
|
|
170
|
+
// This test uses actual output which might have data
|
|
171
|
+
// We're verifying that even with varying data, alignment is maintained
|
|
172
|
+
const output = await generateTestOutput();
|
|
173
|
+
const lines = output.split('\n').filter(line => line.length > 0);
|
|
174
|
+
// All lines should be exactly 60 characters regardless of content
|
|
175
|
+
for (const line of lines) {
|
|
176
|
+
const displayWidth = getDisplayWidth(line);
|
|
177
|
+
assert.strictEqual(displayWidth, 60, `Line with varying content has incorrect width: "${line}"`);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
it('box structure is complete and valid', async () => {
|
|
181
|
+
const output = await generateTestOutput();
|
|
182
|
+
const lines = output.split('\n').filter(line => line.length > 0);
|
|
183
|
+
// Check box structure
|
|
184
|
+
assert.ok(lines[0].startsWith('╔'), 'Should start with top border');
|
|
185
|
+
assert.ok(lines[lines.length - 1].startsWith('╚'), 'Should end with bottom border');
|
|
186
|
+
// Count section markers (only check if they exist - error messages have fewer/no dividers)
|
|
187
|
+
const sectionDividers = lines.filter(line => line.startsWith('╠')).length;
|
|
188
|
+
const subsectionDividers = lines.filter(line => line.startsWith('╟')).length;
|
|
189
|
+
// If we have dividers, they should be valid (but they're optional for error messages)
|
|
190
|
+
// Just ensure the box is complete with top and bottom borders
|
|
191
|
+
assert.ok(lines.length >= 3, 'Should have at least top border, content, and bottom border');
|
|
192
|
+
});
|
|
193
|
+
it('unicode box-drawing characters render correctly', async () => {
|
|
194
|
+
const output = await generateTestOutput();
|
|
195
|
+
// Ensure all expected box-drawing characters are present
|
|
196
|
+
assert.ok(output.includes('╔'), 'Should contain top-left corner');
|
|
197
|
+
assert.ok(output.includes('╗'), 'Should contain top-right corner');
|
|
198
|
+
assert.ok(output.includes('╚'), 'Should contain bottom-left corner');
|
|
199
|
+
assert.ok(output.includes('╝'), 'Should contain bottom-right corner');
|
|
200
|
+
assert.ok(output.includes('║'), 'Should contain vertical lines');
|
|
201
|
+
assert.ok(output.includes('═'), 'Should contain horizontal lines');
|
|
202
|
+
// T-junctions are optional in error output
|
|
203
|
+
if (output.includes('╠') || output.includes('╟')) {
|
|
204
|
+
assert.ok(output.includes('╠'), 'Should contain left T-junction');
|
|
205
|
+
assert.ok(output.includes('╣'), 'Should contain right T-junction');
|
|
206
|
+
assert.ok(output.includes('╟'), 'Should contain left T-junction (dashed)');
|
|
207
|
+
assert.ok(output.includes('╢'), 'Should contain right T-junction (dashed)');
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
it('progress bar characters do not break alignment', async () => {
|
|
211
|
+
const output = await generateTestOutput();
|
|
212
|
+
const lines = output.split('\n').filter(line => line.length > 0);
|
|
213
|
+
// Find lines with progress bars (contain █ or ░)
|
|
214
|
+
const progressLines = lines.filter(line => line.includes('█') || line.includes('░'));
|
|
215
|
+
for (const line of progressLines) {
|
|
216
|
+
const displayWidth = getDisplayWidth(line);
|
|
217
|
+
assert.strictEqual(displayWidth, 60, `Progress bar with special chars has incorrect width: "${line}"`);
|
|
218
|
+
}
|
|
219
|
+
// Progress bars are optional in error output
|
|
220
|
+
// Just ensure if they exist, they're correctly aligned
|
|
221
|
+
});
|
|
222
|
+
it('all sections are present in output', async () => {
|
|
223
|
+
const output = await generateTestOutput();
|
|
224
|
+
// Check if this is quota data or error message
|
|
225
|
+
// Both are valid outputs with correct alignment
|
|
226
|
+
const isQuotaData = output.includes('QUOTA LIMITS');
|
|
227
|
+
const isErrorMessage = output.includes('Credentials Not Found');
|
|
228
|
+
assert.ok(isQuotaData || isErrorMessage, 'Output should be either quota data or error message');
|
|
229
|
+
if (isQuotaData) {
|
|
230
|
+
// If we have quota data, verify expected sections
|
|
231
|
+
assert.ok(output.includes('MODEL USAGE'), 'Quota output should contain MODEL USAGE section');
|
|
232
|
+
assert.ok(output.includes('TOOL/MCP USAGE'), 'Quota output should contain TOOL/MCP USAGE section');
|
|
233
|
+
assert.ok(output.includes('Platform:'), 'Quota output should contain platform information');
|
|
234
|
+
assert.ok(output.includes('Period:'), 'Quota output should contain period information');
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
//# sourceMappingURL=box-alignment.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"box-alignment.test.js","sourceRoot":"","sources":["../../../tests/integration/box-alignment.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-handling.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/error-handling.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as assert from 'node:assert';
|
|
2
|
+
import { describe, test } from 'node:test';
|
|
3
|
+
import { GlmQuotaPlugin } from '../../src/index.js';
|
|
4
|
+
import { BOX_WIDTH } from '../../src/utils/box-constants.js';
|
|
5
|
+
describe('Integration: Error Handling in src/index.ts', () => {
|
|
6
|
+
test('should return boxed error message when Date constructor throws', async () => {
|
|
7
|
+
// Setup credentials to reach queryAllUsage
|
|
8
|
+
process.env.ZAI_API_KEY = 'test-token';
|
|
9
|
+
// Mock Date to throw
|
|
10
|
+
const originalDate = global.Date;
|
|
11
|
+
try {
|
|
12
|
+
class ThrowingDate extends Date {
|
|
13
|
+
constructor(...args) {
|
|
14
|
+
super(...args);
|
|
15
|
+
throw new Error('Date failure');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
global.Date = ThrowingDate;
|
|
19
|
+
// Create plugin instance
|
|
20
|
+
const plugin = await GlmQuotaPlugin({});
|
|
21
|
+
const tool = plugin.tool.glm_quota;
|
|
22
|
+
const result = await tool.execute();
|
|
23
|
+
const lines = result.split('\n');
|
|
24
|
+
assert.ok(result.includes('╔'), 'Output should contain top border');
|
|
25
|
+
assert.ok(result.includes('╚'), 'Output should contain bottom border');
|
|
26
|
+
assert.ok(result.includes('Date failure'), `Output should contain error message. Got: ${result}`);
|
|
27
|
+
assert.strictEqual(lines[0].length, BOX_WIDTH.TOTAL, 'Box width should match');
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
// Cleanup
|
|
31
|
+
global.Date = originalDate;
|
|
32
|
+
delete process.env.ZAI_API_KEY;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
//# sourceMappingURL=error-handling.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-handling.test.js","sourceRoot":"","sources":["../../../tests/integration/error-handling.test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAA;AAO5D,QAAQ,CAAC,6CAA6C,EAAE,GAAG,EAAE;IAC5D,IAAI,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QACjF,2CAA2C;QAC3C,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,YAAY,CAAA;QAEtC,qBAAqB;QACrB,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAA;QAEhC,IAAI,CAAC;YACJ,MAAM,YAAa,SAAQ,IAAI;gBAC9B,YAAY,GAAG,IAAwC;oBACtD,KAAK,CAAC,GAAG,IAAI,CAAC,CAAA;oBACd,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAA;gBAChC,CAAC;aACD;YAED,MAAM,CAAC,IAAI,GAAG,YAA+B,CAAA;YAE7C,yBAAyB;YACzB,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,EAA8B,CAAC,CAAA;YACnE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAK,CAAC,SAAS,CAAA;YAEnC,MAAM,MAAM,GAAG,MAAO,IAAgC,CAAC,OAAO,EAAE,CAAA;YAEhE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YAChC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,kCAAkC,CAAC,CAAA;YACnE,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,qCAAqC,CAAC,CAAA;YACtE,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,6CAA6C,MAAM,EAAE,CAAC,CAAA;YACjG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,KAAK,EAAE,wBAAwB,CAAC,CAAA;QAC/E,CAAC;gBAAS,CAAC;YACV,UAAU;YACV,MAAM,CAAC,IAAI,GAAG,YAAY,CAAA;YAC1B,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,CAAA;QAC/B,CAAC;IACF,CAAC,CAAC,CAAA;AACH,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installer-config.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/installer-config.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import { afterEach, describe, test } from 'node:test';
|
|
7
|
+
const PLUGIN_NAME = 'opencode-glm-quota';
|
|
8
|
+
const createdHomes = [];
|
|
9
|
+
function createTempHome() {
|
|
10
|
+
const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'glm-quota-installer-'));
|
|
11
|
+
createdHomes.push(tempHome);
|
|
12
|
+
return tempHome;
|
|
13
|
+
}
|
|
14
|
+
function runInstaller(tempHome) {
|
|
15
|
+
const result = spawnSync('node', ['bin/install.js', '--force'], {
|
|
16
|
+
cwd: process.cwd(),
|
|
17
|
+
encoding: 'utf-8',
|
|
18
|
+
env: {
|
|
19
|
+
...process.env,
|
|
20
|
+
HOME: tempHome,
|
|
21
|
+
USERPROFILE: tempHome,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
assert.strictEqual(result.status, 0, `Installer failed.\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`);
|
|
25
|
+
}
|
|
26
|
+
function readConfig(tempHome) {
|
|
27
|
+
const configPath = path.join(tempHome, '.config', 'opencode', 'opencode.json');
|
|
28
|
+
assert.ok(fs.existsSync(configPath), `Expected config file to exist: ${configPath}`);
|
|
29
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
30
|
+
return JSON.parse(content);
|
|
31
|
+
}
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
while (createdHomes.length > 0) {
|
|
34
|
+
const tempHome = createdHomes.pop();
|
|
35
|
+
if (!tempHome) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
fs.rmSync(tempHome, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
describe('Installer config key handling', () => {
|
|
42
|
+
test('fresh install writes plugin key (not plugins)', () => {
|
|
43
|
+
const tempHome = createTempHome();
|
|
44
|
+
runInstaller(tempHome);
|
|
45
|
+
const config = readConfig(tempHome);
|
|
46
|
+
const pluginArray = config.plugin;
|
|
47
|
+
assert.ok(Array.isArray(pluginArray), 'Expected "plugin" to be an array');
|
|
48
|
+
assert.ok(pluginArray.includes(PLUGIN_NAME), `Expected plugin array to include ${PLUGIN_NAME}`);
|
|
49
|
+
assert.strictEqual(Object.prototype.hasOwnProperty.call(config, 'plugins'), false, 'Expected "plugins" key to be absent');
|
|
50
|
+
});
|
|
51
|
+
test('migrates legacy plugins key into plugin key', () => {
|
|
52
|
+
const tempHome = createTempHome();
|
|
53
|
+
const configDir = path.join(tempHome, '.config', 'opencode');
|
|
54
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
55
|
+
fs.writeFileSync(path.join(configDir, 'opencode.json'), JSON.stringify({ plugins: ['existing-plugin'] }, null, 2) + '\n');
|
|
56
|
+
runInstaller(tempHome);
|
|
57
|
+
const config = readConfig(tempHome);
|
|
58
|
+
const pluginArray = config.plugin;
|
|
59
|
+
assert.ok(Array.isArray(pluginArray), 'Expected "plugin" to be an array');
|
|
60
|
+
assert.ok(pluginArray.includes('existing-plugin'), 'Expected legacy plugin value to be preserved');
|
|
61
|
+
assert.ok(pluginArray.includes(PLUGIN_NAME), `Expected plugin array to include ${PLUGIN_NAME}`);
|
|
62
|
+
assert.strictEqual(Object.prototype.hasOwnProperty.call(config, 'plugins'), false, 'Expected legacy "plugins" key to be removed');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=installer-config.test.js.map
|