@nahisaho/musubix-core 1.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.
Files changed (214) hide show
  1. package/bin/musubix.js +18 -0
  2. package/dist/__tests__/index.test.d.ts +2 -0
  3. package/dist/__tests__/index.test.d.ts.map +1 -0
  4. package/dist/__tests__/index.test.js +27 -0
  5. package/dist/__tests__/index.test.js.map +1 -0
  6. package/dist/auth/auth-manager.d.ts +320 -0
  7. package/dist/auth/auth-manager.d.ts.map +1 -0
  8. package/dist/auth/auth-manager.js +580 -0
  9. package/dist/auth/auth-manager.js.map +1 -0
  10. package/dist/cli/base.d.ts +58 -0
  11. package/dist/cli/base.d.ts.map +1 -0
  12. package/dist/cli/base.js +93 -0
  13. package/dist/cli/base.js.map +1 -0
  14. package/dist/cli/commands/help.d.ts +17 -0
  15. package/dist/cli/commands/help.d.ts.map +1 -0
  16. package/dist/cli/commands/help.js +228 -0
  17. package/dist/cli/commands/help.js.map +1 -0
  18. package/dist/cli/commands/index.d.ts +14 -0
  19. package/dist/cli/commands/index.d.ts.map +1 -0
  20. package/dist/cli/commands/index.js +25 -0
  21. package/dist/cli/commands/index.js.map +1 -0
  22. package/dist/cli/commands/init.d.ts +38 -0
  23. package/dist/cli/commands/init.d.ts.map +1 -0
  24. package/dist/cli/commands/init.js +258 -0
  25. package/dist/cli/commands/init.js.map +1 -0
  26. package/dist/cli/index.d.ts +9 -0
  27. package/dist/cli/index.d.ts.map +1 -0
  28. package/dist/cli/index.js +9 -0
  29. package/dist/cli/index.js.map +1 -0
  30. package/dist/codegen/coding-standards.d.ts +250 -0
  31. package/dist/codegen/coding-standards.d.ts.map +1 -0
  32. package/dist/codegen/coding-standards.js +976 -0
  33. package/dist/codegen/coding-standards.js.map +1 -0
  34. package/dist/codegen/coverage-reporter.d.ts +264 -0
  35. package/dist/codegen/coverage-reporter.d.ts.map +1 -0
  36. package/dist/codegen/coverage-reporter.js +697 -0
  37. package/dist/codegen/coverage-reporter.js.map +1 -0
  38. package/dist/codegen/dependency-analyzer.d.ts +271 -0
  39. package/dist/codegen/dependency-analyzer.d.ts.map +1 -0
  40. package/dist/codegen/dependency-analyzer.js +661 -0
  41. package/dist/codegen/dependency-analyzer.js.map +1 -0
  42. package/dist/codegen/generator.d.ts +275 -0
  43. package/dist/codegen/generator.d.ts.map +1 -0
  44. package/dist/codegen/generator.js +781 -0
  45. package/dist/codegen/generator.js.map +1 -0
  46. package/dist/codegen/index.d.ts +18 -0
  47. package/dist/codegen/index.d.ts.map +1 -0
  48. package/dist/codegen/index.js +27 -0
  49. package/dist/codegen/index.js.map +1 -0
  50. package/dist/codegen/integration-test-generator.d.ts +312 -0
  51. package/dist/codegen/integration-test-generator.d.ts.map +1 -0
  52. package/dist/codegen/integration-test-generator.js +765 -0
  53. package/dist/codegen/integration-test-generator.js.map +1 -0
  54. package/dist/codegen/pattern-conformance.d.ts +309 -0
  55. package/dist/codegen/pattern-conformance.d.ts.map +1 -0
  56. package/dist/codegen/pattern-conformance.js +590 -0
  57. package/dist/codegen/pattern-conformance.js.map +1 -0
  58. package/dist/codegen/quality-metrics.d.ts +235 -0
  59. package/dist/codegen/quality-metrics.d.ts.map +1 -0
  60. package/dist/codegen/quality-metrics.js +439 -0
  61. package/dist/codegen/quality-metrics.js.map +1 -0
  62. package/dist/codegen/security-scanner.d.ts +179 -0
  63. package/dist/codegen/security-scanner.d.ts.map +1 -0
  64. package/dist/codegen/security-scanner.js +495 -0
  65. package/dist/codegen/security-scanner.js.map +1 -0
  66. package/dist/codegen/static-analyzer.d.ts +188 -0
  67. package/dist/codegen/static-analyzer.d.ts.map +1 -0
  68. package/dist/codegen/static-analyzer.js +490 -0
  69. package/dist/codegen/static-analyzer.js.map +1 -0
  70. package/dist/codegen/unit-test-generator.d.ts +289 -0
  71. package/dist/codegen/unit-test-generator.d.ts.map +1 -0
  72. package/dist/codegen/unit-test-generator.js +634 -0
  73. package/dist/codegen/unit-test-generator.js.map +1 -0
  74. package/dist/design/adr-generator.d.ts +227 -0
  75. package/dist/design/adr-generator.d.ts.map +1 -0
  76. package/dist/design/adr-generator.js +423 -0
  77. package/dist/design/adr-generator.js.map +1 -0
  78. package/dist/design/c4-generator.d.ts +267 -0
  79. package/dist/design/c4-generator.d.ts.map +1 -0
  80. package/dist/design/c4-generator.js +453 -0
  81. package/dist/design/c4-generator.js.map +1 -0
  82. package/dist/design/framework-optimizer.d.ts +190 -0
  83. package/dist/design/framework-optimizer.d.ts.map +1 -0
  84. package/dist/design/framework-optimizer.js +589 -0
  85. package/dist/design/framework-optimizer.js.map +1 -0
  86. package/dist/design/index.d.ts +12 -0
  87. package/dist/design/index.d.ts.map +1 -0
  88. package/dist/design/index.js +13 -0
  89. package/dist/design/index.js.map +1 -0
  90. package/dist/design/pattern-detector.d.ts +270 -0
  91. package/dist/design/pattern-detector.d.ts.map +1 -0
  92. package/dist/design/pattern-detector.js +621 -0
  93. package/dist/design/pattern-detector.js.map +1 -0
  94. package/dist/design/solid-validator.d.ts +188 -0
  95. package/dist/design/solid-validator.d.ts.map +1 -0
  96. package/dist/design/solid-validator.js +579 -0
  97. package/dist/design/solid-validator.js.map +1 -0
  98. package/dist/error/data-persistence.d.ts +311 -0
  99. package/dist/error/data-persistence.d.ts.map +1 -0
  100. package/dist/error/data-persistence.js +586 -0
  101. package/dist/error/data-persistence.js.map +1 -0
  102. package/dist/error/graceful-degradation.d.ts +309 -0
  103. package/dist/error/graceful-degradation.d.ts.map +1 -0
  104. package/dist/error/graceful-degradation.js +510 -0
  105. package/dist/error/graceful-degradation.js.map +1 -0
  106. package/dist/error/index.d.ts +11 -0
  107. package/dist/error/index.d.ts.map +1 -0
  108. package/dist/error/index.js +19 -0
  109. package/dist/error/index.js.map +1 -0
  110. package/dist/explanation/explanation-generator.d.ts +228 -0
  111. package/dist/explanation/explanation-generator.d.ts.map +1 -0
  112. package/dist/explanation/explanation-generator.js +662 -0
  113. package/dist/explanation/explanation-generator.js.map +1 -0
  114. package/dist/explanation/index.d.ts +11 -0
  115. package/dist/explanation/index.d.ts.map +1 -0
  116. package/dist/explanation/index.js +19 -0
  117. package/dist/explanation/index.js.map +1 -0
  118. package/dist/explanation/reasoning-chain.d.ts +314 -0
  119. package/dist/explanation/reasoning-chain.d.ts.map +1 -0
  120. package/dist/explanation/reasoning-chain.js +414 -0
  121. package/dist/explanation/reasoning-chain.js.map +1 -0
  122. package/dist/explanation/visual-explanation.d.ts +315 -0
  123. package/dist/explanation/visual-explanation.d.ts.map +1 -0
  124. package/dist/explanation/visual-explanation.js +667 -0
  125. package/dist/explanation/visual-explanation.js.map +1 -0
  126. package/dist/index.d.ts +33 -0
  127. package/dist/index.d.ts.map +1 -0
  128. package/dist/index.js +47 -0
  129. package/dist/index.js.map +1 -0
  130. package/dist/requirements/decomposer.d.ts +235 -0
  131. package/dist/requirements/decomposer.d.ts.map +1 -0
  132. package/dist/requirements/decomposer.js +587 -0
  133. package/dist/requirements/decomposer.js.map +1 -0
  134. package/dist/requirements/related-finder.d.ts +261 -0
  135. package/dist/requirements/related-finder.d.ts.map +1 -0
  136. package/dist/requirements/related-finder.js +629 -0
  137. package/dist/requirements/related-finder.js.map +1 -0
  138. package/dist/traceability/impact.d.ts +196 -0
  139. package/dist/traceability/impact.d.ts.map +1 -0
  140. package/dist/traceability/impact.js +438 -0
  141. package/dist/traceability/impact.js.map +1 -0
  142. package/dist/traceability/index.d.ts +9 -0
  143. package/dist/traceability/index.d.ts.map +1 -0
  144. package/dist/traceability/index.js +10 -0
  145. package/dist/traceability/index.js.map +1 -0
  146. package/dist/traceability/manager.d.ts +266 -0
  147. package/dist/traceability/manager.d.ts.map +1 -0
  148. package/dist/traceability/manager.js +412 -0
  149. package/dist/traceability/manager.js.map +1 -0
  150. package/dist/types/common.d.ts +294 -0
  151. package/dist/types/common.d.ts.map +1 -0
  152. package/dist/types/common.js +15 -0
  153. package/dist/types/common.js.map +1 -0
  154. package/dist/types/ears.d.ts +158 -0
  155. package/dist/types/ears.d.ts.map +1 -0
  156. package/dist/types/ears.js +33 -0
  157. package/dist/types/ears.js.map +1 -0
  158. package/dist/types/errors.d.ts +176 -0
  159. package/dist/types/errors.d.ts.map +1 -0
  160. package/dist/types/errors.js +55 -0
  161. package/dist/types/errors.js.map +1 -0
  162. package/dist/types/index.d.ts +10 -0
  163. package/dist/types/index.d.ts.map +1 -0
  164. package/dist/types/index.js +10 -0
  165. package/dist/types/index.js.map +1 -0
  166. package/dist/utils/data-protector.d.ts +122 -0
  167. package/dist/utils/data-protector.d.ts.map +1 -0
  168. package/dist/utils/data-protector.js +275 -0
  169. package/dist/utils/data-protector.js.map +1 -0
  170. package/dist/utils/error-handler.d.ts +101 -0
  171. package/dist/utils/error-handler.d.ts.map +1 -0
  172. package/dist/utils/error-handler.js +324 -0
  173. package/dist/utils/error-handler.js.map +1 -0
  174. package/dist/utils/i18n-manager.d.ts +259 -0
  175. package/dist/utils/i18n-manager.d.ts.map +1 -0
  176. package/dist/utils/i18n-manager.js +554 -0
  177. package/dist/utils/i18n-manager.js.map +1 -0
  178. package/dist/utils/index.d.ts +10 -0
  179. package/dist/utils/index.d.ts.map +1 -0
  180. package/dist/utils/index.js +10 -0
  181. package/dist/utils/index.js.map +1 -0
  182. package/dist/utils/logger.d.ts +120 -0
  183. package/dist/utils/logger.d.ts.map +1 -0
  184. package/dist/utils/logger.js +237 -0
  185. package/dist/utils/logger.js.map +1 -0
  186. package/dist/utils/performance-profiler.d.ts +251 -0
  187. package/dist/utils/performance-profiler.d.ts.map +1 -0
  188. package/dist/utils/performance-profiler.js +458 -0
  189. package/dist/utils/performance-profiler.js.map +1 -0
  190. package/dist/utils/scalability-optimizer.d.ts +294 -0
  191. package/dist/utils/scalability-optimizer.d.ts.map +1 -0
  192. package/dist/utils/scalability-optimizer.js +606 -0
  193. package/dist/utils/scalability-optimizer.js.map +1 -0
  194. package/dist/utils/structured-logger.d.ts +294 -0
  195. package/dist/utils/structured-logger.d.ts.map +1 -0
  196. package/dist/utils/structured-logger.js +630 -0
  197. package/dist/utils/structured-logger.js.map +1 -0
  198. package/dist/utils/version-compatibility.d.ts +217 -0
  199. package/dist/utils/version-compatibility.d.ts.map +1 -0
  200. package/dist/utils/version-compatibility.js +443 -0
  201. package/dist/utils/version-compatibility.js.map +1 -0
  202. package/dist/validators/ears-validator.d.ts +182 -0
  203. package/dist/validators/ears-validator.d.ts.map +1 -0
  204. package/dist/validators/ears-validator.js +357 -0
  205. package/dist/validators/ears-validator.js.map +1 -0
  206. package/dist/validators/index.d.ts +8 -0
  207. package/dist/validators/index.d.ts.map +1 -0
  208. package/dist/validators/index.js +9 -0
  209. package/dist/validators/index.js.map +1 -0
  210. package/dist/version.d.ts +8 -0
  211. package/dist/version.d.ts.map +1 -0
  212. package/dist/version.js +8 -0
  213. package/dist/version.js.map +1 -0
  214. package/package.json +100 -0
@@ -0,0 +1,634 @@
1
+ /**
2
+ * Unit Test Generator
3
+ *
4
+ * Generates unit tests from code and specifications
5
+ *
6
+ * @packageDocumentation
7
+ * @module codegen/unit-test-generator
8
+ *
9
+ * @see REQ-TST-001 - Unit Test Generation
10
+ * @see Article VII - Quality Assurance Standards
11
+ */
12
+ /**
13
+ * Default configuration
14
+ */
15
+ export const DEFAULT_TEST_GENERATOR_CONFIG = {
16
+ framework: 'jest',
17
+ assertionStyle: 'expect',
18
+ generateEdgeCases: true,
19
+ generateErrorCases: true,
20
+ generateNullCases: true,
21
+ maxTestCasesPerFunction: 10,
22
+ includeSetupTeardown: true,
23
+ generateMocks: true,
24
+ testFileSuffix: '.test',
25
+ testDirectory: '__tests__',
26
+ verboseDescriptions: true,
27
+ };
28
+ /**
29
+ * Framework templates
30
+ */
31
+ const FRAMEWORK_TEMPLATES = {
32
+ jest: {
33
+ imports: '',
34
+ describe: (name, body) => `describe('${name}', () => {\n${body}\n});`,
35
+ it: (name, body, isAsync) => ` it('${name}', ${isAsync ? 'async ' : ''}() => {\n${body}\n });`,
36
+ beforeAll: (body) => ` beforeAll(() => {\n${body}\n });`,
37
+ afterAll: (body) => ` afterAll(() => {\n${body}\n });`,
38
+ beforeEach: (body) => ` beforeEach(() => {\n${body}\n });`,
39
+ afterEach: (body) => ` afterEach(() => {\n${body}\n });`,
40
+ expect: (value) => `expect(${value})`,
41
+ mockFn: () => 'jest.fn()',
42
+ mockModule: (module) => `jest.mock('${module}')`,
43
+ },
44
+ vitest: {
45
+ imports: "import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest';",
46
+ describe: (name, body) => `describe('${name}', () => {\n${body}\n});`,
47
+ it: (name, body, isAsync) => ` it('${name}', ${isAsync ? 'async ' : ''}() => {\n${body}\n });`,
48
+ beforeAll: (body) => ` beforeAll(() => {\n${body}\n });`,
49
+ afterAll: (body) => ` afterAll(() => {\n${body}\n });`,
50
+ beforeEach: (body) => ` beforeEach(() => {\n${body}\n });`,
51
+ afterEach: (body) => ` afterEach(() => {\n${body}\n });`,
52
+ expect: (value) => `expect(${value})`,
53
+ mockFn: () => 'vi.fn()',
54
+ mockModule: (module) => `vi.mock('${module}')`,
55
+ },
56
+ mocha: {
57
+ imports: "import { expect } from 'chai';",
58
+ describe: (name, body) => `describe('${name}', () => {\n${body}\n});`,
59
+ it: (name, body, isAsync) => ` it('${name}', ${isAsync ? 'async ' : ''}() => {\n${body}\n });`,
60
+ beforeAll: (body) => ` before(() => {\n${body}\n });`,
61
+ afterAll: (body) => ` after(() => {\n${body}\n });`,
62
+ beforeEach: (body) => ` beforeEach(() => {\n${body}\n });`,
63
+ afterEach: (body) => ` afterEach(() => {\n${body}\n });`,
64
+ expect: (value) => `expect(${value})`,
65
+ mockFn: () => "sinon.stub()",
66
+ mockModule: (module) => `// Mock ${module} with sinon or proxyquire`,
67
+ },
68
+ ava: {
69
+ imports: "import test from 'ava';",
70
+ describe: (name, body) => `// ${name}\n${body}`,
71
+ it: (name, body, isAsync) => `test('${name}', ${isAsync ? 'async ' : ''}(t) => {\n${body.replace(/expect\(/g, 't.is(')}\n});`,
72
+ beforeAll: (body) => `test.before(() => {\n${body}\n});`,
73
+ afterAll: (body) => `test.after(() => {\n${body}\n});`,
74
+ beforeEach: (body) => `test.beforeEach(() => {\n${body}\n});`,
75
+ afterEach: (body) => `test.afterEach(() => {\n${body}\n});`,
76
+ expect: (value) => `t.is(${value}`,
77
+ mockFn: () => "sinon.stub()",
78
+ mockModule: (module) => `// Mock ${module} with proxyquire`,
79
+ },
80
+ tap: {
81
+ imports: "import tap from 'tap';",
82
+ describe: (name, body) => `tap.test('${name}', (t) => {\n${body}\n t.end();\n});`,
83
+ it: (name, body, isAsync) => ` t.test('${name}', ${isAsync ? 'async ' : ''}(t) => {\n${body}\n t.end();\n });`,
84
+ beforeAll: (body) => ` t.before(() => {\n${body}\n });`,
85
+ afterAll: (body) => ` t.after(() => {\n${body}\n });`,
86
+ beforeEach: (body) => ` t.beforeEach(() => {\n${body}\n });`,
87
+ afterEach: (body) => ` t.afterEach(() => {\n${body}\n });`,
88
+ expect: (value) => `t.equal(${value}`,
89
+ mockFn: () => "sinon.stub()",
90
+ mockModule: (module) => `// Mock ${module}`,
91
+ },
92
+ pytest: {
93
+ imports: 'import pytest\nfrom unittest.mock import Mock, patch',
94
+ describe: (name, body) => `class Test${name.replace(/\s+/g, '')}:\n${body}`,
95
+ it: (name, body, isAsync) => ` ${isAsync ? 'async ' : ''}def test_${name.replace(/\s+/g, '_').toLowerCase()}(self):\n${body}`,
96
+ beforeAll: (body) => ` @classmethod\n def setup_class(cls):\n${body}`,
97
+ afterAll: (body) => ` @classmethod\n def teardown_class(cls):\n${body}`,
98
+ beforeEach: (body) => ` def setup_method(self):\n${body}`,
99
+ afterEach: (body) => ` def teardown_method(self):\n${body}`,
100
+ expect: (value) => `assert ${value}`,
101
+ mockFn: () => 'Mock()',
102
+ mockModule: (module) => `@patch('${module}')`,
103
+ },
104
+ unittest: {
105
+ imports: 'import unittest\nfrom unittest.mock import Mock, patch',
106
+ describe: (name, body) => `class Test${name.replace(/\s+/g, '')}(unittest.TestCase):\n${body}`,
107
+ it: (name, body, _isAsync) => ` def test_${name.replace(/\s+/g, '_').toLowerCase()}(self):\n${body}`,
108
+ beforeAll: (body) => ` @classmethod\n def setUpClass(cls):\n${body}`,
109
+ afterAll: (body) => ` @classmethod\n def tearDownClass(cls):\n${body}`,
110
+ beforeEach: (body) => ` def setUp(self):\n${body}`,
111
+ afterEach: (body) => ` def tearDown(self):\n${body}`,
112
+ expect: (value) => `self.assertEqual(${value}`,
113
+ mockFn: () => 'Mock()',
114
+ mockModule: (module) => `@patch('${module}')`,
115
+ },
116
+ junit: {
117
+ imports: 'import org.junit.jupiter.api.*;\nimport static org.junit.jupiter.api.Assertions.*;',
118
+ describe: (name, body) => `class ${name.replace(/\s+/g, '')}Test {\n${body}\n}`,
119
+ it: (name, body, _isAsync) => ` @Test\n void test${name.replace(/\s+/g, '')}() {\n${body}\n }`,
120
+ beforeAll: (body) => ` @BeforeAll\n static void setUpAll() {\n${body}\n }`,
121
+ afterAll: (body) => ` @AfterAll\n static void tearDownAll() {\n${body}\n }`,
122
+ beforeEach: (body) => ` @BeforeEach\n void setUp() {\n${body}\n }`,
123
+ afterEach: (body) => ` @AfterEach\n void tearDown() {\n${body}\n }`,
124
+ expect: (value) => `assertEquals(${value}`,
125
+ mockFn: () => 'mock()',
126
+ mockModule: (module) => `// Mock ${module} with Mockito`,
127
+ },
128
+ nunit: {
129
+ imports: 'using NUnit.Framework;\nusing Moq;',
130
+ describe: (name, body) => `[TestFixture]\npublic class ${name.replace(/\s+/g, '')}Tests\n{\n${body}\n}`,
131
+ it: (name, body, _isAsync) => ` [Test]\n public void Test${name.replace(/\s+/g, '')}()\n {\n${body}\n }`,
132
+ beforeAll: (body) => ` [OneTimeSetUp]\n public void SetUpFixture()\n {\n${body}\n }`,
133
+ afterAll: (body) => ` [OneTimeTearDown]\n public void TearDownFixture()\n {\n${body}\n }`,
134
+ beforeEach: (body) => ` [SetUp]\n public void SetUp()\n {\n${body}\n }`,
135
+ afterEach: (body) => ` [TearDown]\n public void TearDown()\n {\n${body}\n }`,
136
+ expect: (value) => `Assert.That(${value}`,
137
+ mockFn: () => 'new Mock<T>()',
138
+ mockModule: (module) => `// Mock ${module} with Moq`,
139
+ },
140
+ xunit: {
141
+ imports: 'using Xunit;\nusing Moq;',
142
+ describe: (name, body) => `public class ${name.replace(/\s+/g, '')}Tests\n{\n${body}\n}`,
143
+ it: (name, body, _isAsync) => ` [Fact]\n public void Test${name.replace(/\s+/g, '')}()\n {\n${body}\n }`,
144
+ beforeAll: (body) => ` public XunitTests()\n {\n${body}\n }`,
145
+ afterAll: (body) => ` public void Dispose()\n {\n${body}\n }`,
146
+ beforeEach: (body) => ` // Setup: ${body}`,
147
+ afterEach: (body) => ` // Teardown: ${body}`,
148
+ expect: (value) => `Assert.Equal(${value}`,
149
+ mockFn: () => 'new Mock<T>()',
150
+ mockModule: (module) => `// Mock ${module} with Moq`,
151
+ },
152
+ 'go-test': {
153
+ imports: 'import (\n "testing"\n)',
154
+ describe: (name, body) => `// ${name}\n${body}`,
155
+ it: (name, body, _isAsync) => `func Test${name.replace(/\s+/g, '')}(t *testing.T) {\n${body}\n}`,
156
+ beforeAll: (body) => `func TestMain(m *testing.M) {\n${body}\n os.Exit(m.Run())\n}`,
157
+ afterAll: (body) => `// Teardown in TestMain: ${body}`,
158
+ beforeEach: (body) => ` // Setup: ${body}`,
159
+ afterEach: (body) => ` // Teardown: ${body}`,
160
+ expect: (value) => `if ${value}`,
161
+ mockFn: () => '// mock function',
162
+ mockModule: (module) => `// Mock ${module}`,
163
+ },
164
+ };
165
+ /**
166
+ * Unit Test Generator
167
+ */
168
+ export class UnitTestGenerator {
169
+ config;
170
+ template;
171
+ constructor(config) {
172
+ this.config = { ...DEFAULT_TEST_GENERATOR_CONFIG, ...config };
173
+ this.template = FRAMEWORK_TEMPLATES[this.config.framework];
174
+ }
175
+ /**
176
+ * Generate tests for a function
177
+ */
178
+ generateForFunction(func, targetFile) {
179
+ const testCases = this.generateTestCases(func);
180
+ const content = this.generateTestFile(func, testCases, targetFile);
181
+ const testFilePath = this.getTestFilePath(targetFile);
182
+ const coverageEstimate = this.estimateCoverage(func, testCases);
183
+ return {
184
+ content,
185
+ filePath: testFilePath,
186
+ testCount: testCases.length,
187
+ coverageEstimate,
188
+ warnings: this.generateWarnings(func, testCases),
189
+ };
190
+ }
191
+ /**
192
+ * Generate tests for multiple functions
193
+ */
194
+ generateForModule(functions, targetFile) {
195
+ const allTestCases = [];
196
+ const warnings = [];
197
+ for (const func of functions) {
198
+ const testCases = this.generateTestCases(func);
199
+ allTestCases.push(...testCases);
200
+ warnings.push(...this.generateWarnings(func, testCases));
201
+ }
202
+ const content = this.generateModuleTestFile(functions, allTestCases, targetFile);
203
+ const testFilePath = this.getTestFilePath(targetFile);
204
+ return {
205
+ content,
206
+ filePath: testFilePath,
207
+ testCount: allTestCases.length,
208
+ coverageEstimate: this.estimateModuleCoverage(functions, allTestCases),
209
+ warnings,
210
+ };
211
+ }
212
+ /**
213
+ * Generate test cases for a function
214
+ */
215
+ generateTestCases(func) {
216
+ const testCases = [];
217
+ // Basic happy path test
218
+ testCases.push(this.generateHappyPathTest(func));
219
+ // Edge cases
220
+ if (this.config.generateEdgeCases) {
221
+ testCases.push(...this.generateEdgeCaseTests(func));
222
+ }
223
+ // Error cases
224
+ if (this.config.generateErrorCases) {
225
+ testCases.push(...this.generateErrorTests(func));
226
+ }
227
+ // Null/undefined cases
228
+ if (this.config.generateNullCases) {
229
+ testCases.push(...this.generateNullTests(func));
230
+ }
231
+ // Limit test cases
232
+ return testCases.slice(0, this.config.maxTestCasesPerFunction);
233
+ }
234
+ /**
235
+ * Generate happy path test
236
+ */
237
+ generateHappyPathTest(func) {
238
+ return {
239
+ name: `should ${this.generateTestName(func)} with valid input`,
240
+ description: `Tests ${func.name} with typical valid input`,
241
+ type: 'unit',
242
+ inputs: func.parameters.map((p) => ({
243
+ name: p.name,
244
+ value: this.generateSampleValue(p.type),
245
+ type: p.type,
246
+ })),
247
+ expectedOutput: {
248
+ type: func.returnType,
249
+ value: this.generateExpectedValue(func.returnType),
250
+ },
251
+ };
252
+ }
253
+ /**
254
+ * Generate edge case tests
255
+ */
256
+ generateEdgeCaseTests(func) {
257
+ const tests = [];
258
+ for (const param of func.parameters) {
259
+ const edgeValues = this.getEdgeValues(param.type);
260
+ for (const edge of edgeValues) {
261
+ tests.push({
262
+ name: `should handle ${param.name} with ${edge.description}`,
263
+ description: `Edge case test for ${param.name}`,
264
+ type: 'edge',
265
+ inputs: func.parameters.map((p) => ({
266
+ name: p.name,
267
+ value: p.name === param.name ? edge.value : this.generateSampleValue(p.type),
268
+ type: p.type,
269
+ })),
270
+ expectedOutput: {
271
+ type: func.returnType,
272
+ },
273
+ tags: ['edge-case'],
274
+ });
275
+ }
276
+ }
277
+ return tests;
278
+ }
279
+ /**
280
+ * Generate error tests
281
+ */
282
+ generateErrorTests(func) {
283
+ const tests = [];
284
+ // Invalid type tests
285
+ for (const param of func.parameters) {
286
+ if (!param.optional) {
287
+ tests.push({
288
+ name: `should throw error when ${param.name} is invalid`,
289
+ description: `Error handling test for invalid ${param.name}`,
290
+ type: 'error',
291
+ inputs: func.parameters.map((p) => ({
292
+ name: p.name,
293
+ value: p.name === param.name ? this.generateInvalidValue(p.type) : this.generateSampleValue(p.type),
294
+ type: p.type,
295
+ })),
296
+ expectedOutput: {
297
+ error: {
298
+ type: 'Error',
299
+ message: `Invalid ${param.name}`,
300
+ },
301
+ },
302
+ tags: ['error-handling'],
303
+ });
304
+ }
305
+ }
306
+ return tests;
307
+ }
308
+ /**
309
+ * Generate null/undefined tests
310
+ */
311
+ generateNullTests(func) {
312
+ const tests = [];
313
+ for (const param of func.parameters) {
314
+ if (!param.optional) {
315
+ // Null test
316
+ tests.push({
317
+ name: `should handle null ${param.name}`,
318
+ description: `Null handling test for ${param.name}`,
319
+ type: 'null',
320
+ inputs: func.parameters.map((p) => ({
321
+ name: p.name,
322
+ value: p.name === param.name ? null : this.generateSampleValue(p.type),
323
+ type: p.type,
324
+ })),
325
+ expectedOutput: {
326
+ error: {
327
+ type: 'TypeError',
328
+ },
329
+ },
330
+ tags: ['null-check'],
331
+ });
332
+ // Undefined test
333
+ tests.push({
334
+ name: `should handle undefined ${param.name}`,
335
+ description: `Undefined handling test for ${param.name}`,
336
+ type: 'null',
337
+ inputs: func.parameters.map((p) => ({
338
+ name: p.name,
339
+ value: p.name === param.name ? undefined : this.generateSampleValue(p.type),
340
+ type: p.type,
341
+ })),
342
+ expectedOutput: {
343
+ error: {
344
+ type: 'TypeError',
345
+ },
346
+ },
347
+ tags: ['undefined-check'],
348
+ });
349
+ }
350
+ }
351
+ return tests;
352
+ }
353
+ /**
354
+ * Generate test file content
355
+ */
356
+ generateTestFile(func, testCases, targetFile) {
357
+ const lines = [];
358
+ // Framework imports
359
+ if (this.template.imports) {
360
+ lines.push(this.template.imports);
361
+ }
362
+ // Import target
363
+ const importPath = this.getImportPath(targetFile);
364
+ lines.push(`import { ${func.name} } from '${importPath}';`);
365
+ lines.push('');
366
+ // Generate describe block
367
+ const testBody = this.generateDescribeBody(func, testCases);
368
+ lines.push(this.template.describe(func.name, testBody));
369
+ return lines.join('\n');
370
+ }
371
+ /**
372
+ * Generate module test file
373
+ */
374
+ generateModuleTestFile(functions, testCases, targetFile) {
375
+ const lines = [];
376
+ // Framework imports
377
+ if (this.template.imports) {
378
+ lines.push(this.template.imports);
379
+ }
380
+ // Import targets
381
+ const importPath = this.getImportPath(targetFile);
382
+ const imports = functions.map((f) => f.name).join(', ');
383
+ lines.push(`import { ${imports} } from '${importPath}';`);
384
+ lines.push('');
385
+ // Generate describe blocks for each function
386
+ for (const func of functions) {
387
+ const funcTests = testCases.filter((t) => t.name.includes(func.name) || t.description.includes(func.name));
388
+ const testBody = this.generateDescribeBody(func, funcTests);
389
+ lines.push(this.template.describe(func.name, testBody));
390
+ lines.push('');
391
+ }
392
+ return lines.join('\n');
393
+ }
394
+ /**
395
+ * Generate describe block body
396
+ */
397
+ generateDescribeBody(func, testCases) {
398
+ const lines = [];
399
+ // Setup/teardown
400
+ if (this.config.includeSetupTeardown) {
401
+ lines.push(this.template.beforeEach(' // Setup'));
402
+ lines.push('');
403
+ }
404
+ // Test cases
405
+ for (const testCase of testCases) {
406
+ const testBody = this.generateTestBody(func, testCase);
407
+ lines.push(this.template.it(testCase.name, testBody, func.isAsync));
408
+ lines.push('');
409
+ }
410
+ return lines.join('\n');
411
+ }
412
+ /**
413
+ * Generate test body
414
+ */
415
+ generateTestBody(func, testCase) {
416
+ const lines = [];
417
+ const indent = ' ';
418
+ // Setup mocks
419
+ if (testCase.mocks) {
420
+ for (const mock of testCase.mocks) {
421
+ lines.push(`${indent}const ${mock.target}Mock = ${this.template.mockFn()};`);
422
+ }
423
+ }
424
+ // Arrange: prepare inputs
425
+ const inputVars = [];
426
+ for (const input of testCase.inputs) {
427
+ const varName = input.name;
428
+ const value = JSON.stringify(input.value);
429
+ lines.push(`${indent}const ${varName} = ${value};`);
430
+ inputVars.push(varName);
431
+ }
432
+ lines.push('');
433
+ // Act: call function
434
+ const funcCall = func.className
435
+ ? `new ${func.className}().${func.name}(${inputVars.join(', ')})`
436
+ : `${func.name}(${inputVars.join(', ')})`;
437
+ if (testCase.expectedOutput.error) {
438
+ // Expect error
439
+ if (func.isAsync) {
440
+ lines.push(`${indent}await ${this.template.expect(`${funcCall}`)}.rejects.toThrow();`);
441
+ }
442
+ else {
443
+ lines.push(`${indent}${this.template.expect(`() => ${funcCall}`)}.toThrow();`);
444
+ }
445
+ }
446
+ else {
447
+ // Expect result
448
+ const resultVar = func.isAsync
449
+ ? `await ${funcCall}`
450
+ : funcCall;
451
+ lines.push(`${indent}const result = ${resultVar};`);
452
+ lines.push('');
453
+ // Assert
454
+ if (testCase.expectedOutput.value !== undefined) {
455
+ const expected = JSON.stringify(testCase.expectedOutput.value);
456
+ lines.push(`${indent}${this.template.expect('result')}.toEqual(${expected});`);
457
+ }
458
+ else {
459
+ lines.push(`${indent}${this.template.expect('result')}.toBeDefined();`);
460
+ }
461
+ }
462
+ return lines.join('\n');
463
+ }
464
+ /**
465
+ * Generate test name from function
466
+ */
467
+ generateTestName(func) {
468
+ // Convert camelCase to words
469
+ const words = func.name
470
+ .replace(/([A-Z])/g, ' $1')
471
+ .toLowerCase()
472
+ .trim();
473
+ return words;
474
+ }
475
+ /**
476
+ * Generate sample value for type
477
+ */
478
+ generateSampleValue(type) {
479
+ const normalized = type.toLowerCase().replace(/\s+/g, '');
480
+ if (normalized.includes('string'))
481
+ return 'test';
482
+ if (normalized.includes('number') || normalized.includes('int') || normalized.includes('float'))
483
+ return 42;
484
+ if (normalized.includes('boolean') || normalized.includes('bool'))
485
+ return true;
486
+ if (normalized.includes('array') || normalized.includes('[]'))
487
+ return [];
488
+ if (normalized.includes('object') || normalized.includes('record'))
489
+ return {};
490
+ if (normalized.includes('date'))
491
+ return new Date().toISOString();
492
+ if (normalized.includes('null'))
493
+ return null;
494
+ if (normalized.includes('undefined'))
495
+ return undefined;
496
+ return {};
497
+ }
498
+ /**
499
+ * Generate expected value for return type
500
+ */
501
+ generateExpectedValue(type) {
502
+ const normalized = type.toLowerCase().replace(/\s+/g, '');
503
+ if (normalized === 'void' || normalized === 'undefined')
504
+ return undefined;
505
+ if (normalized.includes('promise')) {
506
+ const inner = type.match(/<(.+)>/)?.[1] ?? 'unknown';
507
+ return this.generateSampleValue(inner);
508
+ }
509
+ return this.generateSampleValue(type);
510
+ }
511
+ /**
512
+ * Generate invalid value for type
513
+ */
514
+ generateInvalidValue(type) {
515
+ const normalized = type.toLowerCase().replace(/\s+/g, '');
516
+ if (normalized.includes('string'))
517
+ return 123;
518
+ if (normalized.includes('number'))
519
+ return 'not a number';
520
+ if (normalized.includes('boolean'))
521
+ return 'not boolean';
522
+ if (normalized.includes('array'))
523
+ return 'not array';
524
+ if (normalized.includes('object'))
525
+ return 'not object';
526
+ return Symbol('invalid');
527
+ }
528
+ /**
529
+ * Get edge values for type
530
+ */
531
+ getEdgeValues(type) {
532
+ const normalized = type.toLowerCase().replace(/\s+/g, '');
533
+ const edges = [];
534
+ if (normalized.includes('string')) {
535
+ edges.push({ value: '', description: 'empty string' });
536
+ edges.push({ value: ' ', description: 'whitespace' });
537
+ edges.push({ value: 'a'.repeat(1000), description: 'very long string' });
538
+ }
539
+ if (normalized.includes('number')) {
540
+ edges.push({ value: 0, description: 'zero' });
541
+ edges.push({ value: -1, description: 'negative' });
542
+ edges.push({ value: Number.MAX_SAFE_INTEGER, description: 'max safe integer' });
543
+ edges.push({ value: Number.MIN_SAFE_INTEGER, description: 'min safe integer' });
544
+ edges.push({ value: Infinity, description: 'infinity' });
545
+ edges.push({ value: NaN, description: 'NaN' });
546
+ }
547
+ if (normalized.includes('array')) {
548
+ edges.push({ value: [], description: 'empty array' });
549
+ edges.push({ value: new Array(1000).fill(0), description: 'large array' });
550
+ }
551
+ return edges;
552
+ }
553
+ /**
554
+ * Get test file path
555
+ */
556
+ getTestFilePath(targetFile) {
557
+ const parts = targetFile.split('/');
558
+ const filename = parts.pop();
559
+ const ext = filename.split('.').pop();
560
+ const baseName = filename.replace(`.${ext}`, '');
561
+ return [...parts, this.config.testDirectory, `${baseName}${this.config.testFileSuffix}.${ext}`].join('/');
562
+ }
563
+ /**
564
+ * Get import path
565
+ */
566
+ getImportPath(targetFile) {
567
+ // Convert absolute to relative import
568
+ const parts = targetFile.split('/');
569
+ const filename = parts.pop();
570
+ const ext = filename.split('.').pop();
571
+ const baseName = filename.replace(`.${ext}`, '');
572
+ return `../${baseName}`;
573
+ }
574
+ /**
575
+ * Estimate coverage
576
+ */
577
+ estimateCoverage(_func, testCases) {
578
+ const hasHappyPath = testCases.some((t) => t.type === 'unit');
579
+ const hasEdgeCases = testCases.some((t) => t.type === 'edge');
580
+ const hasErrorCases = testCases.some((t) => t.type === 'error');
581
+ const hasNullCases = testCases.some((t) => t.type === 'null');
582
+ let base = hasHappyPath ? 50 : 0;
583
+ if (hasEdgeCases)
584
+ base += 15;
585
+ if (hasErrorCases)
586
+ base += 20;
587
+ if (hasNullCases)
588
+ base += 15;
589
+ return {
590
+ statements: Math.min(base + 10, 100),
591
+ branches: Math.min(base, 100),
592
+ functions: hasHappyPath ? 100 : 0,
593
+ lines: Math.min(base + 10, 100),
594
+ };
595
+ }
596
+ /**
597
+ * Estimate module coverage
598
+ */
599
+ estimateModuleCoverage(functions, testCases) {
600
+ const functionsCovered = functions.filter((f) => testCases.some((t) => t.name.includes(f.name) || t.description.includes(f.name))).length;
601
+ const functionCoverage = functions.length > 0
602
+ ? (functionsCovered / functions.length) * 100
603
+ : 0;
604
+ return {
605
+ statements: Math.round(functionCoverage * 0.9),
606
+ branches: Math.round(functionCoverage * 0.7),
607
+ functions: Math.round(functionCoverage),
608
+ lines: Math.round(functionCoverage * 0.9),
609
+ };
610
+ }
611
+ /**
612
+ * Generate warnings
613
+ */
614
+ generateWarnings(func, testCases) {
615
+ const warnings = [];
616
+ if (testCases.length < 3) {
617
+ warnings.push(`Low test count for ${func.name}: consider adding more test cases`);
618
+ }
619
+ if (func.parameters.length > 5) {
620
+ warnings.push(`${func.name} has many parameters: consider refactoring`);
621
+ }
622
+ if (!testCases.some((t) => t.type === 'error')) {
623
+ warnings.push(`No error handling tests for ${func.name}`);
624
+ }
625
+ return warnings;
626
+ }
627
+ }
628
+ /**
629
+ * Create unit test generator instance
630
+ */
631
+ export function createUnitTestGenerator(config) {
632
+ return new UnitTestGenerator(config);
633
+ }
634
+ //# sourceMappingURL=unit-test-generator.js.map