calc-mcp-server 0.1.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/.claude/commands/opsx/apply.md +152 -0
- package/.claude/commands/opsx/archive.md +157 -0
- package/.claude/commands/opsx/bulk-archive.md +242 -0
- package/.claude/commands/opsx/continue.md +114 -0
- package/.claude/commands/opsx/explore.md +174 -0
- package/.claude/commands/opsx/ff.md +94 -0
- package/.claude/commands/opsx/new.md +69 -0
- package/.claude/commands/opsx/onboard.md +534 -0
- package/.claude/commands/opsx/sync.md +134 -0
- package/.claude/commands/opsx/verify.md +164 -0
- package/.claude/settings.local.json +8 -0
- package/.claude/skills/npm-publish/SKILL.md +164 -0
- package/.claude/skills/openspec-apply-change/SKILL.md +156 -0
- package/.claude/skills/openspec-archive-change/SKILL.md +161 -0
- package/.claude/skills/openspec-bulk-archive-change/SKILL.md +246 -0
- package/.claude/skills/openspec-continue-change/SKILL.md +118 -0
- package/.claude/skills/openspec-explore/SKILL.md +289 -0
- package/.claude/skills/openspec-ff-change/SKILL.md +101 -0
- package/.claude/skills/openspec-new-change/SKILL.md +74 -0
- package/.claude/skills/openspec-onboard/SKILL.md +538 -0
- package/.claude/skills/openspec-sync-specs/SKILL.md +138 -0
- package/.claude/skills/openspec-verify-change/SKILL.md +168 -0
- package/CLAUDE.md +92 -0
- package/README.md +319 -0
- package/build/engines/decimal.d.ts +10 -0
- package/build/engines/decimal.d.ts.map +1 -0
- package/build/engines/decimal.js +61 -0
- package/build/engines/decimal.js.map +1 -0
- package/build/engines/programmer.d.ts +18 -0
- package/build/engines/programmer.d.ts.map +1 -0
- package/build/engines/programmer.js +103 -0
- package/build/engines/programmer.js.map +1 -0
- package/build/errors/handler.d.ts +10 -0
- package/build/errors/handler.d.ts.map +1 -0
- package/build/errors/handler.js +37 -0
- package/build/errors/handler.js.map +1 -0
- package/build/errors/types.d.ts +25 -0
- package/build/errors/types.d.ts.map +1 -0
- package/build/errors/types.js +2 -0
- package/build/errors/types.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +16 -0
- package/build/index.js.map +1 -0
- package/build/mcp/server.d.ts +3 -0
- package/build/mcp/server.d.ts.map +1 -0
- package/build/mcp/server.js +270 -0
- package/build/mcp/server.js.map +1 -0
- package/build/mcp/tools/ascii.d.ts +11 -0
- package/build/mcp/tools/ascii.d.ts.map +1 -0
- package/build/mcp/tools/ascii.js +93 -0
- package/build/mcp/tools/ascii.js.map +1 -0
- package/build/mcp/tools/basic.d.ts +6 -0
- package/build/mcp/tools/basic.d.ts.map +1 -0
- package/build/mcp/tools/basic.js +34 -0
- package/build/mcp/tools/basic.js.map +1 -0
- package/build/mcp/tools/conversion.d.ts +8 -0
- package/build/mcp/tools/conversion.d.ts.map +1 -0
- package/build/mcp/tools/conversion.js +81 -0
- package/build/mcp/tools/conversion.js.map +1 -0
- package/build/mcp/tools/programmer.d.ts +6 -0
- package/build/mcp/tools/programmer.d.ts.map +1 -0
- package/build/mcp/tools/programmer.js +29 -0
- package/build/mcp/tools/programmer.js.map +1 -0
- package/build/parser/ast.d.ts +47 -0
- package/build/parser/ast.d.ts.map +1 -0
- package/build/parser/ast.js +2 -0
- package/build/parser/ast.js.map +1 -0
- package/build/parser/lexer.d.ts +24 -0
- package/build/parser/lexer.d.ts.map +1 -0
- package/build/parser/lexer.js +168 -0
- package/build/parser/lexer.js.map +1 -0
- package/build/parser/parser.d.ts +14 -0
- package/build/parser/parser.d.ts.map +1 -0
- package/build/parser/parser.js +115 -0
- package/build/parser/parser.js.map +1 -0
- package/docs/plans/2025-02-24-mcp-calculator-design.md +344 -0
- package/docs/plans/2025-02-24-mcp-calculator-implementation.md +2626 -0
- package/openspec/changes/archive/2026-02-24-simplify-ascii-tools/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-02-24-simplify-ascii-tools/design.md +46 -0
- package/openspec/changes/archive/2026-02-24-simplify-ascii-tools/proposal.md +21 -0
- package/openspec/changes/archive/2026-02-24-simplify-ascii-tools/specs/ascii-conversion/spec.md +22 -0
- package/openspec/changes/archive/2026-02-24-simplify-ascii-tools/tasks.md +24 -0
- package/openspec/config.yaml +20 -0
- package/openspec/specs/ascii-conversion/spec.md +43 -0
- package/package.json +40 -0
- package/src/engines/decimal.ts +69 -0
- package/src/engines/programmer.ts +112 -0
- package/src/errors/handler.ts +55 -0
- package/src/errors/types.ts +37 -0
- package/src/index.ts +20 -0
- package/src/mcp/server.ts +287 -0
- package/src/mcp/tools/ascii.ts +116 -0
- package/src/mcp/tools/basic.ts +44 -0
- package/src/mcp/tools/conversion.ts +95 -0
- package/src/mcp/tools/programmer.ts +36 -0
- package/src/parser/ast.ts +51 -0
- package/src/parser/lexer.ts +216 -0
- package/src/parser/parser.ts +154 -0
- package/test/integration/ascii.test.ts +450 -0
- package/test/integration/basic-calculate.test.ts +272 -0
- package/test/integration/conversion.test.ts +357 -0
- package/test/integration/programmer-calculate.test.ts +363 -0
- package/test/unit/decimal-engine.test.ts +134 -0
- package/test/unit/error-handler.test.ts +173 -0
- package/test/unit/lexer.test.ts +176 -0
- package/test/unit/parser.test.ts +197 -0
- package/test/unit/programmer-engine.test.ts +234 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { basicCalculate } from '../../src/mcp/tools/basic.js';
|
|
3
|
+
|
|
4
|
+
describe('basicCalculate', () => {
|
|
5
|
+
describe('Basic arithmetic operations', () => {
|
|
6
|
+
it('should add two numbers', () => {
|
|
7
|
+
const result = basicCalculate({ expression: '2 + 3' });
|
|
8
|
+
|
|
9
|
+
expect(result.success).toBe(true);
|
|
10
|
+
if (result.success) {
|
|
11
|
+
expect(result.result).toBe('5');
|
|
12
|
+
expect(result.details?.expression).toBe('2 + 3');
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should subtract two numbers', () => {
|
|
17
|
+
const result = basicCalculate({ expression: '10 - 4' });
|
|
18
|
+
|
|
19
|
+
expect(result.success).toBe(true);
|
|
20
|
+
if (result.success) {
|
|
21
|
+
expect(result.result).toBe('6');
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should multiply two numbers', () => {
|
|
26
|
+
const result = basicCalculate({ expression: '6 * 7' });
|
|
27
|
+
|
|
28
|
+
expect(result.success).toBe(true);
|
|
29
|
+
if (result.success) {
|
|
30
|
+
expect(result.result).toBe('42');
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should divide two numbers', () => {
|
|
35
|
+
const result = basicCalculate({ expression: '15 / 3' });
|
|
36
|
+
|
|
37
|
+
expect(result.success).toBe(true);
|
|
38
|
+
if (result.success) {
|
|
39
|
+
expect(result.result).toBe('5');
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should calculate modulo', () => {
|
|
44
|
+
const result = basicCalculate({ expression: '17 % 5' });
|
|
45
|
+
|
|
46
|
+
expect(result.success).toBe(true);
|
|
47
|
+
if (result.success) {
|
|
48
|
+
expect(result.result).toBe('2');
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('Floating point arithmetic', () => {
|
|
54
|
+
it('should handle 0.1 + 0.2 correctly', () => {
|
|
55
|
+
const result = basicCalculate({ expression: '0.1 + 0.2' });
|
|
56
|
+
|
|
57
|
+
expect(result.success).toBe(true);
|
|
58
|
+
if (result.success) {
|
|
59
|
+
expect(result.result).toBe('0.3');
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should handle 1 / 3 with decimal precision', () => {
|
|
64
|
+
const result = basicCalculate({ expression: '1 / 3' });
|
|
65
|
+
|
|
66
|
+
expect(result.success).toBe(true);
|
|
67
|
+
if (result.success) {
|
|
68
|
+
expect(parseFloat(result.result)).toBeCloseTo(0.333, 2);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should handle 3.14159 * 2', () => {
|
|
73
|
+
const result = basicCalculate({ expression: '3.14159 * 2' });
|
|
74
|
+
|
|
75
|
+
expect(result.success).toBe(true);
|
|
76
|
+
if (result.success) {
|
|
77
|
+
expect(result.result).toBe('6.28318');
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('Operator precedence', () => {
|
|
83
|
+
it('should respect multiplication before addition', () => {
|
|
84
|
+
const result = basicCalculate({ expression: '2 + 3 * 4' });
|
|
85
|
+
|
|
86
|
+
expect(result.success).toBe(true);
|
|
87
|
+
if (result.success) {
|
|
88
|
+
expect(result.result).toBe('14');
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should respect parentheses', () => {
|
|
93
|
+
const result = basicCalculate({ expression: '(2 + 3) * 4' });
|
|
94
|
+
|
|
95
|
+
expect(result.success).toBe(true);
|
|
96
|
+
if (result.success) {
|
|
97
|
+
expect(result.result).toBe('20');
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should handle nested parentheses', () => {
|
|
102
|
+
const result = basicCalculate({ expression: '((2 + 3) * (4 - 1)) / 5' });
|
|
103
|
+
|
|
104
|
+
expect(result.success).toBe(true);
|
|
105
|
+
if (result.success) {
|
|
106
|
+
expect(result.result).toBe('3');
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('Complex expressions', () => {
|
|
112
|
+
it('should handle 2.5 * 3 + (10 - 4) / 2', () => {
|
|
113
|
+
const result = basicCalculate({ expression: '2.5 * 3 + (10 - 4) / 2' });
|
|
114
|
+
|
|
115
|
+
expect(result.success).toBe(true);
|
|
116
|
+
if (result.success) {
|
|
117
|
+
expect(result.result).toBe('10.5');
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should handle (10 + 5) * (8 - 3) / 5', () => {
|
|
122
|
+
const result = basicCalculate({ expression: '(10 + 5) * (8 - 3) / 5' });
|
|
123
|
+
|
|
124
|
+
expect(result.success).toBe(true);
|
|
125
|
+
if (result.success) {
|
|
126
|
+
expect(result.result).toBe('15');
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should handle 100 / 4 / 5', () => {
|
|
131
|
+
const result = basicCalculate({ expression: '100 / 4 / 5' });
|
|
132
|
+
|
|
133
|
+
expect(result.success).toBe(true);
|
|
134
|
+
if (result.success) {
|
|
135
|
+
expect(result.result).toBe('5');
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('Negative numbers', () => {
|
|
141
|
+
it('should handle -5 + 3', () => {
|
|
142
|
+
const result = basicCalculate({ expression: '-5 + 3' });
|
|
143
|
+
|
|
144
|
+
expect(result.success).toBe(true);
|
|
145
|
+
if (result.success) {
|
|
146
|
+
expect(result.result).toBe('-2');
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should handle -10 * -2', () => {
|
|
151
|
+
const result = basicCalculate({ expression: '-10 * -2' });
|
|
152
|
+
|
|
153
|
+
expect(result.success).toBe(true);
|
|
154
|
+
if (result.success) {
|
|
155
|
+
expect(result.result).toBe('20');
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should handle 5 + -3', () => {
|
|
160
|
+
const result = basicCalculate({ expression: '5 + -3' });
|
|
161
|
+
|
|
162
|
+
expect(result.success).toBe(true);
|
|
163
|
+
if (result.success) {
|
|
164
|
+
expect(result.result).toBe('2');
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('Error handling', () => {
|
|
170
|
+
it('should return error for empty expression', () => {
|
|
171
|
+
const result = basicCalculate({ expression: '' });
|
|
172
|
+
|
|
173
|
+
expect(result.success).toBe(false);
|
|
174
|
+
if (!result.success) {
|
|
175
|
+
expect(result.error.type).toBe('EmptyInput');
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should return error for whitespace only', () => {
|
|
180
|
+
const result = basicCalculate({ expression: ' ' });
|
|
181
|
+
|
|
182
|
+
expect(result.success).toBe(false);
|
|
183
|
+
if (!result.success) {
|
|
184
|
+
expect(result.error.type).toBe('EmptyInput');
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should return error for division by zero', () => {
|
|
189
|
+
const result = basicCalculate({ expression: '5 / 0' });
|
|
190
|
+
|
|
191
|
+
expect(result.success).toBe(false);
|
|
192
|
+
if (!result.success) {
|
|
193
|
+
expect(result.error.type).toBe('DivisionByZero');
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should return error for modulo by zero', () => {
|
|
198
|
+
const result = basicCalculate({ expression: '5 % 0' });
|
|
199
|
+
|
|
200
|
+
expect(result.success).toBe(false);
|
|
201
|
+
if (!result.success) {
|
|
202
|
+
expect(result.error.type).toBe('DivisionByZero');
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should return error for unmatched opening parenthesis', () => {
|
|
207
|
+
const result = basicCalculate({ expression: '(2 + 3' });
|
|
208
|
+
|
|
209
|
+
expect(result.success).toBe(false);
|
|
210
|
+
if (!result.success) {
|
|
211
|
+
expect(result.error.type).toBe('SyntaxError');
|
|
212
|
+
expect(result.error.message).toContain('Unmatched');
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should return error for unmatched closing parenthesis', () => {
|
|
217
|
+
const result = basicCalculate({ expression: '2 + 3)' });
|
|
218
|
+
|
|
219
|
+
expect(result.success).toBe(false);
|
|
220
|
+
if (!result.success) {
|
|
221
|
+
expect(result.error.type).toBe('SyntaxError');
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should return error for invalid expression', () => {
|
|
226
|
+
const result = basicCalculate({ expression: '2 + + 3' });
|
|
227
|
+
|
|
228
|
+
expect(result.success).toBe(false);
|
|
229
|
+
if (!result.success) {
|
|
230
|
+
expect(result.error.type).toBe('SyntaxError');
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
describe('Edge cases', () => {
|
|
236
|
+
it('should handle 0 + 0', () => {
|
|
237
|
+
const result = basicCalculate({ expression: '0 + 0' });
|
|
238
|
+
|
|
239
|
+
expect(result.success).toBe(true);
|
|
240
|
+
if (result.success) {
|
|
241
|
+
expect(result.result).toBe('0');
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should handle single number', () => {
|
|
246
|
+
const result = basicCalculate({ expression: '42' });
|
|
247
|
+
|
|
248
|
+
expect(result.success).toBe(true);
|
|
249
|
+
if (result.success) {
|
|
250
|
+
expect(result.result).toBe('42');
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('should handle expression with whitespace', () => {
|
|
255
|
+
const result = basicCalculate({ expression: ' 2 + 3 ' });
|
|
256
|
+
|
|
257
|
+
expect(result.success).toBe(true);
|
|
258
|
+
if (result.success) {
|
|
259
|
+
expect(result.result).toBe('5');
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should handle very large numbers', () => {
|
|
264
|
+
const result = basicCalculate({ expression: '999999999 * 999999999' });
|
|
265
|
+
|
|
266
|
+
expect(result.success).toBe(true);
|
|
267
|
+
if (result.success) {
|
|
268
|
+
expect(result.result).toBe('999999998000000001');
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
});
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { convertBase } from '../../src/mcp/tools/conversion.js';
|
|
3
|
+
|
|
4
|
+
describe('convertBase', () => {
|
|
5
|
+
describe('Binary conversions (base 2)', () => {
|
|
6
|
+
it('should convert binary to decimal', () => {
|
|
7
|
+
const result = convertBase({ value: '1010', fromBase: 2, toBase: 10 });
|
|
8
|
+
|
|
9
|
+
expect(result.success).toBe(true);
|
|
10
|
+
if (result.success) {
|
|
11
|
+
expect(result.result).toBe('10');
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should convert binary to hexadecimal', () => {
|
|
16
|
+
const result = convertBase({ value: '11111111', fromBase: 2, toBase: 16 });
|
|
17
|
+
|
|
18
|
+
expect(result.success).toBe(true);
|
|
19
|
+
if (result.success) {
|
|
20
|
+
expect(result.result).toBe('FF');
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should convert binary to octal', () => {
|
|
25
|
+
const result = convertBase({ value: '101010', fromBase: 2, toBase: 8 });
|
|
26
|
+
|
|
27
|
+
expect(result.success).toBe(true);
|
|
28
|
+
if (result.success) {
|
|
29
|
+
expect(result.result).toBe('52');
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should convert binary to binary (identity)', () => {
|
|
34
|
+
const result = convertBase({ value: '1100100', fromBase: 2, toBase: 2 });
|
|
35
|
+
|
|
36
|
+
expect(result.success).toBe(true);
|
|
37
|
+
if (result.success) {
|
|
38
|
+
expect(result.result).toBe('1100100');
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('Octal conversions (base 8)', () => {
|
|
44
|
+
it('should convert octal to decimal', () => {
|
|
45
|
+
const result = convertBase({ value: '755', fromBase: 8, toBase: 10 });
|
|
46
|
+
|
|
47
|
+
expect(result.success).toBe(true);
|
|
48
|
+
if (result.success) {
|
|
49
|
+
expect(result.result).toBe('493');
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should convert octal to binary', () => {
|
|
54
|
+
const result = convertBase({ value: '10', fromBase: 8, toBase: 2 });
|
|
55
|
+
|
|
56
|
+
expect(result.success).toBe(true);
|
|
57
|
+
if (result.success) {
|
|
58
|
+
expect(result.result).toBe('1000');
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should convert octal to hexadecimal', () => {
|
|
63
|
+
const result = convertBase({ value: '377', fromBase: 8, toBase: 16 });
|
|
64
|
+
|
|
65
|
+
expect(result.success).toBe(true);
|
|
66
|
+
if (result.success) {
|
|
67
|
+
expect(result.result).toBe('FF');
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should convert octal to octal (identity)', () => {
|
|
72
|
+
const result = convertBase({ value: '12345670', fromBase: 8, toBase: 8 });
|
|
73
|
+
|
|
74
|
+
expect(result.success).toBe(true);
|
|
75
|
+
if (result.success) {
|
|
76
|
+
expect(result.result).toBe('12345670');
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('Decimal conversions (base 10)', () => {
|
|
82
|
+
it('should convert decimal to binary', () => {
|
|
83
|
+
const result = convertBase({ value: '42', fromBase: 10, toBase: 2 });
|
|
84
|
+
|
|
85
|
+
expect(result.success).toBe(true);
|
|
86
|
+
if (result.success) {
|
|
87
|
+
expect(result.result).toBe('101010');
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should convert decimal to hexadecimal', () => {
|
|
92
|
+
const result = convertBase({ value: '255', fromBase: 10, toBase: 16 });
|
|
93
|
+
|
|
94
|
+
expect(result.success).toBe(true);
|
|
95
|
+
if (result.success) {
|
|
96
|
+
expect(result.result).toBe('FF');
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should convert decimal to octal', () => {
|
|
101
|
+
const result = convertBase({ value: '64', fromBase: 10, toBase: 8 });
|
|
102
|
+
|
|
103
|
+
expect(result.success).toBe(true);
|
|
104
|
+
if (result.success) {
|
|
105
|
+
expect(result.result).toBe('100');
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should convert decimal to decimal (identity)', () => {
|
|
110
|
+
const result = convertBase({ value: '12345', fromBase: 10, toBase: 10 });
|
|
111
|
+
|
|
112
|
+
expect(result.success).toBe(true);
|
|
113
|
+
if (result.success) {
|
|
114
|
+
expect(result.result).toBe('12345');
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should handle negative decimal numbers', () => {
|
|
119
|
+
const result = convertBase({ value: '-42', fromBase: 10, toBase: 16 });
|
|
120
|
+
|
|
121
|
+
expect(result.success).toBe(true);
|
|
122
|
+
if (result.success) {
|
|
123
|
+
expect(result.result).toBe('2A');
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('Hexadecimal conversions (base 16)', () => {
|
|
129
|
+
it('should convert hex to decimal', () => {
|
|
130
|
+
const result = convertBase({ value: 'FF', fromBase: 16, toBase: 10 });
|
|
131
|
+
|
|
132
|
+
expect(result.success).toBe(true);
|
|
133
|
+
if (result.success) {
|
|
134
|
+
expect(result.result).toBe('255');
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should convert hex to binary', () => {
|
|
139
|
+
const result = convertBase({ value: 'A', fromBase: 16, toBase: 2 });
|
|
140
|
+
|
|
141
|
+
expect(result.success).toBe(true);
|
|
142
|
+
if (result.success) {
|
|
143
|
+
expect(result.result).toBe('1010');
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should convert hex to octal', () => {
|
|
148
|
+
const result = convertBase({ value: '10', fromBase: 16, toBase: 8 });
|
|
149
|
+
|
|
150
|
+
expect(result.success).toBe(true);
|
|
151
|
+
if (result.success) {
|
|
152
|
+
expect(result.result).toBe('20');
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should convert hex to hex (identity)', () => {
|
|
157
|
+
const result = convertBase({ value: 'ABCDEF', fromBase: 16, toBase: 16 });
|
|
158
|
+
|
|
159
|
+
expect(result.success).toBe(true);
|
|
160
|
+
if (result.success) {
|
|
161
|
+
expect(result.result).toBe('ABCDEF');
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should handle lowercase hex input', () => {
|
|
166
|
+
const result = convertBase({ value: 'ff', fromBase: 16, toBase: 10 });
|
|
167
|
+
|
|
168
|
+
expect(result.success).toBe(true);
|
|
169
|
+
if (result.success) {
|
|
170
|
+
expect(result.result).toBe('255');
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should output uppercase hex', () => {
|
|
175
|
+
const result = convertBase({ value: 'ff', fromBase: 16, toBase: 16 });
|
|
176
|
+
|
|
177
|
+
expect(result.success).toBe(true);
|
|
178
|
+
if (result.success) {
|
|
179
|
+
expect(result.result).toBe('FF');
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe('Large number conversions', () => {
|
|
185
|
+
it('should convert large decimal to hex', () => {
|
|
186
|
+
const result = convertBase({ value: '4294967295', fromBase: 10, toBase: 16 });
|
|
187
|
+
|
|
188
|
+
expect(result.success).toBe(true);
|
|
189
|
+
if (result.success) {
|
|
190
|
+
expect(result.result).toBe('FFFFFFFF');
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should convert large hex to decimal', () => {
|
|
195
|
+
const result = convertBase({ value: 'FFFFFFFF', fromBase: 16, toBase: 10 });
|
|
196
|
+
|
|
197
|
+
expect(result.success).toBe(true);
|
|
198
|
+
if (result.success) {
|
|
199
|
+
expect(result.result).toBe('4294967295');
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should convert large binary to hex', () => {
|
|
204
|
+
const result = convertBase({ value: '11111111111111111111111111111111', fromBase: 2, toBase: 16 });
|
|
205
|
+
|
|
206
|
+
expect(result.success).toBe(true);
|
|
207
|
+
if (result.success) {
|
|
208
|
+
expect(result.result).toBe('FFFFFFFF');
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe('Edge cases', () => {
|
|
214
|
+
it('should handle zero in all bases', () => {
|
|
215
|
+
const bases = [2, 8, 10, 16];
|
|
216
|
+
for (const fromBase of bases) {
|
|
217
|
+
for (const toBase of bases) {
|
|
218
|
+
const result = convertBase({ value: '0', fromBase, toBase });
|
|
219
|
+
expect(result.success).toBe(true);
|
|
220
|
+
if (result.success) {
|
|
221
|
+
expect(result.result).toBe('0');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should handle single digit conversions', () => {
|
|
228
|
+
const result = convertBase({ value: '7', fromBase: 10, toBase: 2 });
|
|
229
|
+
expect(result.success).toBe(true);
|
|
230
|
+
if (result.success) {
|
|
231
|
+
expect(result.result).toBe('111');
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('should include steps in details', () => {
|
|
236
|
+
const result = convertBase({ value: 'FF', fromBase: 16, toBase: 10 });
|
|
237
|
+
|
|
238
|
+
expect(result.success).toBe(true);
|
|
239
|
+
if (result.success) {
|
|
240
|
+
expect(result.details?.steps).toBeDefined();
|
|
241
|
+
expect(result.details?.steps?.length).toBe(3);
|
|
242
|
+
expect(result.details?.steps?.[0]).toContain('FF');
|
|
243
|
+
expect(result.details?.steps?.[1]).toContain('255');
|
|
244
|
+
expect(result.details?.steps?.[2]).toContain('base 10');
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe('Error handling', () => {
|
|
250
|
+
it('should return error for empty value', () => {
|
|
251
|
+
const result = convertBase({ value: '', fromBase: 10, toBase: 2 });
|
|
252
|
+
|
|
253
|
+
expect(result.success).toBe(false);
|
|
254
|
+
if (!result.success) {
|
|
255
|
+
expect(result.error.type).toBe('EmptyInput');
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('should return error for invalid source base', () => {
|
|
260
|
+
const result = convertBase({ value: '101', fromBase: 3, toBase: 10 });
|
|
261
|
+
|
|
262
|
+
expect(result.success).toBe(false);
|
|
263
|
+
if (!result.success) {
|
|
264
|
+
expect(result.error.type).toBe('InvalidBase');
|
|
265
|
+
expect(result.error.message).toContain('source base');
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('should return error for invalid target base', () => {
|
|
270
|
+
const result = convertBase({ value: '101', fromBase: 2, toBase: 5 });
|
|
271
|
+
|
|
272
|
+
expect(result.success).toBe(false);
|
|
273
|
+
if (!result.success) {
|
|
274
|
+
expect(result.error.type).toBe('InvalidBase');
|
|
275
|
+
expect(result.error.message).toContain('target base');
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should return error for invalid binary number', () => {
|
|
280
|
+
const result = convertBase({ value: '102', fromBase: 2, toBase: 10 });
|
|
281
|
+
|
|
282
|
+
expect(result.success).toBe(false);
|
|
283
|
+
if (!result.success) {
|
|
284
|
+
expect(result.error.type).toBe('InvalidNumber');
|
|
285
|
+
expect(result.error.message).toContain('binary');
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('should return error for invalid octal number', () => {
|
|
290
|
+
const result = convertBase({ value: '89', fromBase: 8, toBase: 10 });
|
|
291
|
+
|
|
292
|
+
expect(result.success).toBe(false);
|
|
293
|
+
if (!result.success) {
|
|
294
|
+
expect(result.error.type).toBe('InvalidNumber');
|
|
295
|
+
expect(result.error.message).toContain('octal');
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('should return error for invalid decimal number', () => {
|
|
300
|
+
const result = convertBase({ value: '12.5', fromBase: 10, toBase: 2 });
|
|
301
|
+
|
|
302
|
+
expect(result.success).toBe(false);
|
|
303
|
+
if (!result.success) {
|
|
304
|
+
expect(result.error.type).toBe('InvalidNumber');
|
|
305
|
+
expect(result.error.message).toContain('decimal');
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('should return error for invalid hexadecimal number', () => {
|
|
310
|
+
const result = convertBase({ value: 'GHI', fromBase: 16, toBase: 10 });
|
|
311
|
+
|
|
312
|
+
expect(result.success).toBe(false);
|
|
313
|
+
if (!result.success) {
|
|
314
|
+
expect(result.error.type).toBe('InvalidNumber');
|
|
315
|
+
expect(result.error.message).toContain('hexadecimal');
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('should return error for whitespace only value', () => {
|
|
320
|
+
const result = convertBase({ value: ' ', fromBase: 10, toBase: 2 });
|
|
321
|
+
|
|
322
|
+
expect(result.success).toBe(false);
|
|
323
|
+
if (!result.success) {
|
|
324
|
+
expect(result.error.type).toBe('EmptyInput');
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
describe('Special values', () => {
|
|
330
|
+
it('should convert power of 2 values', () => {
|
|
331
|
+
const result = convertBase({ value: '256', fromBase: 10, toBase: 2 });
|
|
332
|
+
|
|
333
|
+
expect(result.success).toBe(true);
|
|
334
|
+
if (result.success) {
|
|
335
|
+
expect(result.result).toBe('100000000');
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('should convert power of 16 values', () => {
|
|
340
|
+
const result = convertBase({ value: '10000', fromBase: 16, toBase: 10 });
|
|
341
|
+
|
|
342
|
+
expect(result.success).toBe(true);
|
|
343
|
+
if (result.success) {
|
|
344
|
+
expect(result.result).toBe('65536');
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('should handle maximum 32-bit unsigned', () => {
|
|
349
|
+
const result = convertBase({ value: '4294967295', fromBase: 10, toBase: 2 });
|
|
350
|
+
|
|
351
|
+
expect(result.success).toBe(true);
|
|
352
|
+
if (result.success) {
|
|
353
|
+
expect(result.result).toBe('11111111111111111111111111111111');
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
});
|