@skillsmith/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.
Files changed (306) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/src/__tests__/get-skill.test.d.ts +6 -0
  3. package/dist/src/__tests__/get-skill.test.d.ts.map +1 -0
  4. package/dist/src/__tests__/get-skill.test.js +88 -0
  5. package/dist/src/__tests__/get-skill.test.js.map +1 -0
  6. package/dist/src/__tests__/middleware/errorFormatter.test.d.ts +7 -0
  7. package/dist/src/__tests__/middleware/errorFormatter.test.d.ts.map +1 -0
  8. package/dist/src/__tests__/middleware/errorFormatter.test.js +304 -0
  9. package/dist/src/__tests__/middleware/errorFormatter.test.js.map +1 -0
  10. package/dist/src/__tests__/middleware/license.test.d.ts +7 -0
  11. package/dist/src/__tests__/middleware/license.test.d.ts.map +1 -0
  12. package/dist/src/__tests__/middleware/license.test.js +500 -0
  13. package/dist/src/__tests__/middleware/license.test.js.map +1 -0
  14. package/dist/src/__tests__/search.test.d.ts +6 -0
  15. package/dist/src/__tests__/search.test.d.ts.map +1 -0
  16. package/dist/src/__tests__/search.test.js +86 -0
  17. package/dist/src/__tests__/search.test.js.map +1 -0
  18. package/dist/src/__tests__/test-utils.d.ts +19 -0
  19. package/dist/src/__tests__/test-utils.d.ts.map +1 -0
  20. package/dist/src/__tests__/test-utils.js +87 -0
  21. package/dist/src/__tests__/test-utils.js.map +1 -0
  22. package/dist/src/context/index.d.ts +19 -0
  23. package/dist/src/context/index.d.ts.map +1 -0
  24. package/dist/src/context/index.js +25 -0
  25. package/dist/src/context/index.js.map +1 -0
  26. package/dist/src/context/project-detector.d.ts +145 -0
  27. package/dist/src/context/project-detector.d.ts.map +1 -0
  28. package/dist/src/context/project-detector.js +321 -0
  29. package/dist/src/context/project-detector.js.map +1 -0
  30. package/dist/src/context.d.ts +100 -0
  31. package/dist/src/context.d.ts.map +1 -0
  32. package/dist/src/context.js +157 -0
  33. package/dist/src/context.js.map +1 -0
  34. package/dist/src/core-shim.d.ts +7 -0
  35. package/dist/src/core-shim.d.ts.map +1 -0
  36. package/dist/src/core-shim.js +9 -0
  37. package/dist/src/core-shim.js.map +1 -0
  38. package/dist/src/health/healthCheck.d.ts +88 -0
  39. package/dist/src/health/healthCheck.d.ts.map +1 -0
  40. package/dist/src/health/healthCheck.js +117 -0
  41. package/dist/src/health/healthCheck.js.map +1 -0
  42. package/dist/src/health/index.d.ts +21 -0
  43. package/dist/src/health/index.d.ts.map +1 -0
  44. package/dist/src/health/index.js +21 -0
  45. package/dist/src/health/index.js.map +1 -0
  46. package/dist/src/health/readinessCheck.d.ts +139 -0
  47. package/dist/src/health/readinessCheck.d.ts.map +1 -0
  48. package/dist/src/health/readinessCheck.js +266 -0
  49. package/dist/src/health/readinessCheck.js.map +1 -0
  50. package/dist/src/index.d.ts +8 -0
  51. package/dist/src/index.d.ts.map +1 -0
  52. package/dist/src/index.js +178 -0
  53. package/dist/src/index.js.map +1 -0
  54. package/dist/src/index.test.d.ts +2 -0
  55. package/dist/src/index.test.d.ts.map +1 -0
  56. package/dist/src/index.test.js +43 -0
  57. package/dist/src/index.test.js.map +1 -0
  58. package/dist/src/logger.d.ts +26 -0
  59. package/dist/src/logger.d.ts.map +1 -0
  60. package/dist/src/logger.js +179 -0
  61. package/dist/src/logger.js.map +1 -0
  62. package/dist/src/middleware/__tests__/csp.test.d.ts +2 -0
  63. package/dist/src/middleware/__tests__/csp.test.d.ts.map +1 -0
  64. package/dist/src/middleware/__tests__/csp.test.js +389 -0
  65. package/dist/src/middleware/__tests__/csp.test.js.map +1 -0
  66. package/dist/src/middleware/csp.d.ts +87 -0
  67. package/dist/src/middleware/csp.d.ts.map +1 -0
  68. package/dist/src/middleware/csp.js +273 -0
  69. package/dist/src/middleware/csp.js.map +1 -0
  70. package/dist/src/middleware/degradation.d.ts +99 -0
  71. package/dist/src/middleware/degradation.d.ts.map +1 -0
  72. package/dist/src/middleware/degradation.js +315 -0
  73. package/dist/src/middleware/degradation.js.map +1 -0
  74. package/dist/src/middleware/errorFormatter.d.ts +119 -0
  75. package/dist/src/middleware/errorFormatter.d.ts.map +1 -0
  76. package/dist/src/middleware/errorFormatter.js +294 -0
  77. package/dist/src/middleware/errorFormatter.js.map +1 -0
  78. package/dist/src/middleware/index.d.ts +10 -0
  79. package/dist/src/middleware/index.d.ts.map +1 -0
  80. package/dist/src/middleware/index.js +14 -0
  81. package/dist/src/middleware/index.js.map +1 -0
  82. package/dist/src/middleware/license.d.ts +161 -0
  83. package/dist/src/middleware/license.d.ts.map +1 -0
  84. package/dist/src/middleware/license.js +281 -0
  85. package/dist/src/middleware/license.js.map +1 -0
  86. package/dist/src/middleware/toolFeatureMapping.d.ts +36 -0
  87. package/dist/src/middleware/toolFeatureMapping.d.ts.map +1 -0
  88. package/dist/src/middleware/toolFeatureMapping.js +90 -0
  89. package/dist/src/middleware/toolFeatureMapping.js.map +1 -0
  90. package/dist/src/onboarding/first-run.d.ts +64 -0
  91. package/dist/src/onboarding/first-run.d.ts.map +1 -0
  92. package/dist/src/onboarding/first-run.js +77 -0
  93. package/dist/src/onboarding/first-run.js.map +1 -0
  94. package/dist/src/onboarding/index.d.ts +7 -0
  95. package/dist/src/onboarding/index.d.ts.map +1 -0
  96. package/dist/src/onboarding/index.js +7 -0
  97. package/dist/src/onboarding/index.js.map +1 -0
  98. package/dist/src/suggestions/index.d.ts +21 -0
  99. package/dist/src/suggestions/index.d.ts.map +1 -0
  100. package/dist/src/suggestions/index.js +20 -0
  101. package/dist/src/suggestions/index.js.map +1 -0
  102. package/dist/src/suggestions/suggestion-engine.d.ts +185 -0
  103. package/dist/src/suggestions/suggestion-engine.d.ts.map +1 -0
  104. package/dist/src/suggestions/suggestion-engine.js +352 -0
  105. package/dist/src/suggestions/suggestion-engine.js.map +1 -0
  106. package/dist/src/suggestions/types.d.ts +88 -0
  107. package/dist/src/suggestions/types.d.ts.map +1 -0
  108. package/dist/src/suggestions/types.js +21 -0
  109. package/dist/src/suggestions/types.js.map +1 -0
  110. package/dist/src/tools/analyze.d.ts +151 -0
  111. package/dist/src/tools/analyze.d.ts.map +1 -0
  112. package/dist/src/tools/analyze.js +205 -0
  113. package/dist/src/tools/analyze.js.map +1 -0
  114. package/dist/src/tools/compare.d.ts +149 -0
  115. package/dist/src/tools/compare.d.ts.map +1 -0
  116. package/dist/src/tools/compare.js +464 -0
  117. package/dist/src/tools/compare.js.map +1 -0
  118. package/dist/src/tools/get-skill.d.ts +116 -0
  119. package/dist/src/tools/get-skill.d.ts.map +1 -0
  120. package/dist/src/tools/get-skill.js +224 -0
  121. package/dist/src/tools/get-skill.js.map +1 -0
  122. package/dist/src/tools/index.d.ts +20 -0
  123. package/dist/src/tools/index.d.ts.map +1 -0
  124. package/dist/src/tools/index.js +20 -0
  125. package/dist/src/tools/index.js.map +1 -0
  126. package/dist/src/tools/install.d.ts +122 -0
  127. package/dist/src/tools/install.d.ts.map +1 -0
  128. package/dist/src/tools/install.js +314 -0
  129. package/dist/src/tools/install.js.map +1 -0
  130. package/dist/src/tools/recommend.d.ts +171 -0
  131. package/dist/src/tools/recommend.d.ts.map +1 -0
  132. package/dist/src/tools/recommend.js +325 -0
  133. package/dist/src/tools/recommend.js.map +1 -0
  134. package/dist/src/tools/search.d.ts +121 -0
  135. package/dist/src/tools/search.d.ts.map +1 -0
  136. package/dist/src/tools/search.js +249 -0
  137. package/dist/src/tools/search.js.map +1 -0
  138. package/dist/src/tools/suggest.d.ts +181 -0
  139. package/dist/src/tools/suggest.d.ts.map +1 -0
  140. package/dist/src/tools/suggest.js +342 -0
  141. package/dist/src/tools/suggest.js.map +1 -0
  142. package/dist/src/tools/uninstall.d.ts +123 -0
  143. package/dist/src/tools/uninstall.d.ts.map +1 -0
  144. package/dist/src/tools/uninstall.js +250 -0
  145. package/dist/src/tools/uninstall.js.map +1 -0
  146. package/dist/src/tools/validate.d.ts +122 -0
  147. package/dist/src/tools/validate.d.ts.map +1 -0
  148. package/dist/src/tools/validate.js +497 -0
  149. package/dist/src/tools/validate.js.map +1 -0
  150. package/dist/src/utils/installed-skills.d.ts +101 -0
  151. package/dist/src/utils/installed-skills.d.ts.map +1 -0
  152. package/dist/src/utils/installed-skills.js +220 -0
  153. package/dist/src/utils/installed-skills.js.map +1 -0
  154. package/dist/src/utils/validation.d.ts +76 -0
  155. package/dist/src/utils/validation.d.ts.map +1 -0
  156. package/dist/src/utils/validation.js +153 -0
  157. package/dist/src/utils/validation.js.map +1 -0
  158. package/dist/src/webhooks/index.d.ts +8 -0
  159. package/dist/src/webhooks/index.d.ts.map +1 -0
  160. package/dist/src/webhooks/index.js +9 -0
  161. package/dist/src/webhooks/index.js.map +1 -0
  162. package/dist/src/webhooks/webhook-endpoint.d.ts +149 -0
  163. package/dist/src/webhooks/webhook-endpoint.d.ts.map +1 -0
  164. package/dist/src/webhooks/webhook-endpoint.js +339 -0
  165. package/dist/src/webhooks/webhook-endpoint.js.map +1 -0
  166. package/dist/tests/compare.test.d.ts +6 -0
  167. package/dist/tests/compare.test.d.ts.map +1 -0
  168. package/dist/tests/compare.test.js +225 -0
  169. package/dist/tests/compare.test.js.map +1 -0
  170. package/dist/tests/context/project-detector.test.d.ts +6 -0
  171. package/dist/tests/context/project-detector.test.d.ts.map +1 -0
  172. package/dist/tests/context/project-detector.test.js +719 -0
  173. package/dist/tests/context/project-detector.test.js.map +1 -0
  174. package/dist/tests/e2e/compare.e2e.test.d.ts +10 -0
  175. package/dist/tests/e2e/compare.e2e.test.d.ts.map +1 -0
  176. package/dist/tests/e2e/compare.e2e.test.js +286 -0
  177. package/dist/tests/e2e/compare.e2e.test.js.map +1 -0
  178. package/dist/tests/e2e/install-flow.e2e.test.d.ts +10 -0
  179. package/dist/tests/e2e/install-flow.e2e.test.d.ts.map +1 -0
  180. package/dist/tests/e2e/install-flow.e2e.test.js +209 -0
  181. package/dist/tests/e2e/install-flow.e2e.test.js.map +1 -0
  182. package/dist/tests/e2e/recommend.e2e.test.d.ts +12 -0
  183. package/dist/tests/e2e/recommend.e2e.test.d.ts.map +1 -0
  184. package/dist/tests/e2e/recommend.e2e.test.js +347 -0
  185. package/dist/tests/e2e/recommend.e2e.test.js.map +1 -0
  186. package/dist/tests/e2e/skill-flow.e2e.test.d.ts +10 -0
  187. package/dist/tests/e2e/skill-flow.e2e.test.d.ts.map +1 -0
  188. package/dist/tests/e2e/skill-flow.e2e.test.js +280 -0
  189. package/dist/tests/e2e/skill-flow.e2e.test.js.map +1 -0
  190. package/dist/tests/e2e/suggest.e2e.test.d.ts +13 -0
  191. package/dist/tests/e2e/suggest.e2e.test.d.ts.map +1 -0
  192. package/dist/tests/e2e/suggest.e2e.test.js +347 -0
  193. package/dist/tests/e2e/suggest.e2e.test.js.map +1 -0
  194. package/dist/tests/e2e/utils/baseline-collector.d.ts +107 -0
  195. package/dist/tests/e2e/utils/baseline-collector.d.ts.map +1 -0
  196. package/dist/tests/e2e/utils/baseline-collector.js +211 -0
  197. package/dist/tests/e2e/utils/baseline-collector.js.map +1 -0
  198. package/dist/tests/e2e/utils/hardcoded-detector.d.ts +46 -0
  199. package/dist/tests/e2e/utils/hardcoded-detector.d.ts.map +1 -0
  200. package/dist/tests/e2e/utils/hardcoded-detector.js +255 -0
  201. package/dist/tests/e2e/utils/hardcoded-detector.js.map +1 -0
  202. package/dist/tests/e2e/utils/index.d.ts +7 -0
  203. package/dist/tests/e2e/utils/index.d.ts.map +1 -0
  204. package/dist/tests/e2e/utils/index.js +7 -0
  205. package/dist/tests/e2e/utils/index.js.map +1 -0
  206. package/dist/tests/e2e/utils/linear-reporter.d.ts +60 -0
  207. package/dist/tests/e2e/utils/linear-reporter.d.ts.map +1 -0
  208. package/dist/tests/e2e/utils/linear-reporter.js +232 -0
  209. package/dist/tests/e2e/utils/linear-reporter.js.map +1 -0
  210. package/dist/tests/health.test.d.ts +9 -0
  211. package/dist/tests/health.test.d.ts.map +1 -0
  212. package/dist/tests/health.test.js +308 -0
  213. package/dist/tests/health.test.js.map +1 -0
  214. package/dist/tests/integration/analyze.integration.test.d.ts +2 -0
  215. package/dist/tests/integration/analyze.integration.test.d.ts.map +1 -0
  216. package/dist/tests/integration/analyze.integration.test.js +244 -0
  217. package/dist/tests/integration/analyze.integration.test.js.map +1 -0
  218. package/dist/tests/integration/compare.integration.test.d.ts +2 -0
  219. package/dist/tests/integration/compare.integration.test.d.ts.map +1 -0
  220. package/dist/tests/integration/compare.integration.test.js +120 -0
  221. package/dist/tests/integration/compare.integration.test.js.map +1 -0
  222. package/dist/tests/integration/fixtures/test-skills.d.ts +62 -0
  223. package/dist/tests/integration/fixtures/test-skills.d.ts.map +1 -0
  224. package/dist/tests/integration/fixtures/test-skills.js +644 -0
  225. package/dist/tests/integration/fixtures/test-skills.js.map +1 -0
  226. package/dist/tests/integration/get-skill.integration.test.d.ts +6 -0
  227. package/dist/tests/integration/get-skill.integration.test.d.ts.map +1 -0
  228. package/dist/tests/integration/get-skill.integration.test.js +203 -0
  229. package/dist/tests/integration/get-skill.integration.test.js.map +1 -0
  230. package/dist/tests/integration/github-api.integration.test.d.ts +14 -0
  231. package/dist/tests/integration/github-api.integration.test.d.ts.map +1 -0
  232. package/dist/tests/integration/github-api.integration.test.js +190 -0
  233. package/dist/tests/integration/github-api.integration.test.js.map +1 -0
  234. package/dist/tests/integration/install.integration.test.d.ts +6 -0
  235. package/dist/tests/integration/install.integration.test.d.ts.map +1 -0
  236. package/dist/tests/integration/install.integration.test.js +282 -0
  237. package/dist/tests/integration/install.integration.test.js.map +1 -0
  238. package/dist/tests/integration/recommend.integration.test.d.ts +2 -0
  239. package/dist/tests/integration/recommend.integration.test.d.ts.map +1 -0
  240. package/dist/tests/integration/recommend.integration.test.js +215 -0
  241. package/dist/tests/integration/recommend.integration.test.js.map +1 -0
  242. package/dist/tests/integration/search.integration.test.d.ts +6 -0
  243. package/dist/tests/integration/search.integration.test.d.ts.map +1 -0
  244. package/dist/tests/integration/search.integration.test.js +229 -0
  245. package/dist/tests/integration/search.integration.test.js.map +1 -0
  246. package/dist/tests/integration/setup.d.ts +71 -0
  247. package/dist/tests/integration/setup.d.ts.map +1 -0
  248. package/dist/tests/integration/setup.js +124 -0
  249. package/dist/tests/integration/setup.js.map +1 -0
  250. package/dist/tests/integration/uninstall.integration.test.d.ts +6 -0
  251. package/dist/tests/integration/uninstall.integration.test.d.ts.map +1 -0
  252. package/dist/tests/integration/uninstall.integration.test.js +296 -0
  253. package/dist/tests/integration/uninstall.integration.test.js.map +1 -0
  254. package/dist/tests/integration/validate.integration.test.d.ts +2 -0
  255. package/dist/tests/integration/validate.integration.test.d.ts.map +1 -0
  256. package/dist/tests/integration/validate.integration.test.js +181 -0
  257. package/dist/tests/integration/validate.integration.test.js.map +1 -0
  258. package/dist/tests/onboarding/first-run.test.d.ts +7 -0
  259. package/dist/tests/onboarding/first-run.test.d.ts.map +1 -0
  260. package/dist/tests/onboarding/first-run.test.js +258 -0
  261. package/dist/tests/onboarding/first-run.test.js.map +1 -0
  262. package/dist/tests/performance/search-performance.test.d.ts +10 -0
  263. package/dist/tests/performance/search-performance.test.d.ts.map +1 -0
  264. package/dist/tests/performance/search-performance.test.js +218 -0
  265. package/dist/tests/performance/search-performance.test.js.map +1 -0
  266. package/dist/tests/recommend.test.d.ts +6 -0
  267. package/dist/tests/recommend.test.d.ts.map +1 -0
  268. package/dist/tests/recommend.test.js +208 -0
  269. package/dist/tests/recommend.test.js.map +1 -0
  270. package/dist/tests/suggestions/suggestion-engine.test.d.ts +6 -0
  271. package/dist/tests/suggestions/suggestion-engine.test.d.ts.map +1 -0
  272. package/dist/tests/suggestions/suggestion-engine.test.js +448 -0
  273. package/dist/tests/suggestions/suggestion-engine.test.js.map +1 -0
  274. package/dist/tests/test-utils.d.ts +74 -0
  275. package/dist/tests/test-utils.d.ts.map +1 -0
  276. package/dist/tests/test-utils.js +98 -0
  277. package/dist/tests/test-utils.js.map +1 -0
  278. package/dist/tests/tools.test.d.ts +5 -0
  279. package/dist/tests/tools.test.d.ts.map +1 -0
  280. package/dist/tests/tools.test.js +138 -0
  281. package/dist/tests/tools.test.js.map +1 -0
  282. package/dist/tests/unit/installed-skills.test.d.ts +6 -0
  283. package/dist/tests/unit/installed-skills.test.d.ts.map +1 -0
  284. package/dist/tests/unit/installed-skills.test.js +285 -0
  285. package/dist/tests/unit/installed-skills.test.js.map +1 -0
  286. package/dist/tests/unit/logger.test.d.ts +6 -0
  287. package/dist/tests/unit/logger.test.d.ts.map +1 -0
  288. package/dist/tests/unit/logger.test.js +281 -0
  289. package/dist/tests/unit/logger.test.js.map +1 -0
  290. package/dist/tests/validate.test.d.ts +5 -0
  291. package/dist/tests/validate.test.d.ts.map +1 -0
  292. package/dist/tests/validate.test.js +303 -0
  293. package/dist/tests/validate.test.js.map +1 -0
  294. package/dist/tests/webhooks/proxy-trust.security.test.d.ts +8 -0
  295. package/dist/tests/webhooks/proxy-trust.security.test.d.ts.map +1 -0
  296. package/dist/tests/webhooks/proxy-trust.security.test.js +145 -0
  297. package/dist/tests/webhooks/proxy-trust.security.test.js.map +1 -0
  298. package/dist/tests/webhooks/rate-limiter.security.test.d.ts +8 -0
  299. package/dist/tests/webhooks/rate-limiter.security.test.d.ts.map +1 -0
  300. package/dist/tests/webhooks/rate-limiter.security.test.js +122 -0
  301. package/dist/tests/webhooks/rate-limiter.security.test.js.map +1 -0
  302. package/dist/vitest.config.d.ts +6 -0
  303. package/dist/vitest.config.d.ts.map +1 -0
  304. package/dist/vitest.config.js +13 -0
  305. package/dist/vitest.config.js.map +1 -0
  306. package/package.json +63 -0
@@ -0,0 +1,448 @@
1
+ /**
2
+ * Unit tests for SMI-913: Contextual skill suggestions after first success
3
+ * Tests the SuggestionEngine class comprehensively
4
+ */
5
+ import { describe, it, expect, beforeEach, afterEach, beforeAll, afterAll } from 'vitest';
6
+ import * as fs from 'node:fs';
7
+ import * as path from 'node:path';
8
+ import * as os from 'node:os';
9
+ import { SuggestionEngine } from '../../src/suggestions/suggestion-engine.js';
10
+ // Create test directory path
11
+ const TEST_BASE_DIR = path.join(os.tmpdir(), 'skillsmith-suggestion-test');
12
+ let testDir;
13
+ /**
14
+ * Helper to create a mock ProjectContext
15
+ */
16
+ function createMockContext(overrides = {}) {
17
+ return {
18
+ hasDocker: false,
19
+ hasLinear: false,
20
+ hasGitHub: false,
21
+ testFramework: null,
22
+ apiFramework: null,
23
+ hasNativeModules: false,
24
+ language: null,
25
+ ...overrides,
26
+ };
27
+ }
28
+ /**
29
+ * Helper to create a mock state file
30
+ */
31
+ function writeMockState(stateDir, state) {
32
+ const fullState = {
33
+ lastSuggestionTime: 0,
34
+ suggestionsToday: 0,
35
+ optedOut: false,
36
+ dismissedSkills: [],
37
+ ...state,
38
+ };
39
+ if (!fs.existsSync(stateDir)) {
40
+ fs.mkdirSync(stateDir, { recursive: true });
41
+ }
42
+ fs.writeFileSync(path.join(stateDir, 'suggestions-state.json'), JSON.stringify(fullState, null, 2));
43
+ }
44
+ /**
45
+ * Clean up test directory
46
+ */
47
+ function cleanupTestDir() {
48
+ if (testDir && fs.existsSync(testDir)) {
49
+ fs.rmSync(testDir, { recursive: true, force: true });
50
+ }
51
+ }
52
+ /**
53
+ * Create a SuggestionEngine with test state directory
54
+ */
55
+ function createTestEngine(config = {}) {
56
+ return new SuggestionEngine({ ...config, stateDir: testDir });
57
+ }
58
+ describe('SuggestionEngine', () => {
59
+ beforeAll(() => {
60
+ // Ensure base test directory exists
61
+ if (!fs.existsSync(TEST_BASE_DIR)) {
62
+ fs.mkdirSync(TEST_BASE_DIR, { recursive: true });
63
+ }
64
+ });
65
+ afterAll(() => {
66
+ // Clean up base test directory
67
+ if (fs.existsSync(TEST_BASE_DIR)) {
68
+ fs.rmSync(TEST_BASE_DIR, { recursive: true, force: true });
69
+ }
70
+ });
71
+ beforeEach(() => {
72
+ // Create unique directory for each test
73
+ testDir = path.join(TEST_BASE_DIR, `test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
74
+ fs.mkdirSync(testDir, { recursive: true });
75
+ });
76
+ afterEach(() => {
77
+ cleanupTestDir();
78
+ });
79
+ describe('constructor', () => {
80
+ it('should initialize with default config', () => {
81
+ const engine = createTestEngine();
82
+ const state = engine.getState();
83
+ expect(state.optedOut).toBe(false);
84
+ expect(state.suggestionsToday).toBe(0);
85
+ expect(state.dismissedSkills).toEqual([]);
86
+ });
87
+ it('should accept custom config', () => {
88
+ const engine = createTestEngine({
89
+ cooldownMs: 10 * 60 * 1000,
90
+ maxSuggestionsPerDay: 5,
91
+ });
92
+ // Engine should still work with custom config
93
+ expect(engine.canSuggest()).toBe(true);
94
+ });
95
+ it('should load existing state from disk', () => {
96
+ // Set lastSuggestionTime to today so the daily count doesn't reset
97
+ writeMockState(testDir, {
98
+ lastSuggestionTime: Date.now(),
99
+ suggestionsToday: 2,
100
+ dismissedSkills: ['community/docker'],
101
+ });
102
+ const engine = createTestEngine();
103
+ const state = engine.getState();
104
+ expect(state.suggestionsToday).toBe(2);
105
+ expect(state.dismissedSkills).toContain('community/docker');
106
+ });
107
+ it('should reset daily count on new day', () => {
108
+ // Write state from yesterday
109
+ const yesterday = Date.now() - 24 * 60 * 60 * 1000;
110
+ writeMockState(testDir, {
111
+ lastSuggestionTime: yesterday,
112
+ suggestionsToday: 3,
113
+ });
114
+ const engine = createTestEngine();
115
+ const state = engine.getState();
116
+ expect(state.suggestionsToday).toBe(0);
117
+ });
118
+ it('should handle corrupted state file gracefully', () => {
119
+ if (!fs.existsSync(testDir)) {
120
+ fs.mkdirSync(testDir, { recursive: true });
121
+ }
122
+ fs.writeFileSync(path.join(testDir, 'suggestions-state.json'), '{ invalid json }');
123
+ const engine = createTestEngine();
124
+ const state = engine.getState();
125
+ expect(state.optedOut).toBe(false);
126
+ expect(state.suggestionsToday).toBe(0);
127
+ });
128
+ });
129
+ describe('canSuggest', () => {
130
+ it('should return true by default', () => {
131
+ const engine = createTestEngine();
132
+ expect(engine.canSuggest()).toBe(true);
133
+ });
134
+ it('should return false when opted out', () => {
135
+ const engine = createTestEngine();
136
+ engine.optOut();
137
+ expect(engine.canSuggest()).toBe(false);
138
+ });
139
+ it('should return false when daily limit reached', () => {
140
+ const engine = createTestEngine({ maxSuggestionsPerDay: 3 });
141
+ // Simulate showing 3 suggestions
142
+ engine.recordSuggestionShown();
143
+ engine.recordSuggestionShown();
144
+ engine.recordSuggestionShown();
145
+ expect(engine.canSuggest()).toBe(false);
146
+ });
147
+ it('should return false during cooldown period', () => {
148
+ const engine = createTestEngine({ cooldownMs: 5 * 60 * 1000 });
149
+ engine.recordSuggestionShown();
150
+ // Should be in cooldown immediately after showing
151
+ expect(engine.canSuggest()).toBe(false);
152
+ });
153
+ it('should return true after cooldown expires', async () => {
154
+ // Use 100ms cooldown for testing
155
+ const engine = createTestEngine({ cooldownMs: 100 });
156
+ engine.recordSuggestionShown();
157
+ // Wait for cooldown
158
+ await new Promise((resolve) => setTimeout(resolve, 150));
159
+ expect(engine.canSuggest()).toBe(true);
160
+ });
161
+ });
162
+ describe('getSuggestions', () => {
163
+ it('should return empty array when opted out', () => {
164
+ const engine = createTestEngine();
165
+ engine.optOut();
166
+ const context = createMockContext({ hasDocker: true, hasNativeModules: true });
167
+ const suggestions = engine.getSuggestions(context);
168
+ expect(suggestions).toEqual([]);
169
+ });
170
+ it('should return empty array when rate limited', () => {
171
+ const engine = createTestEngine({ maxSuggestionsPerDay: 1 });
172
+ engine.recordSuggestionShown();
173
+ const context = createMockContext({ hasDocker: true, hasNativeModules: true });
174
+ const suggestions = engine.getSuggestions(context);
175
+ expect(suggestions).toEqual([]);
176
+ });
177
+ it('should suggest docker for native modules + docker projects', () => {
178
+ const engine = createTestEngine();
179
+ const context = createMockContext({ hasDocker: true, hasNativeModules: true });
180
+ const suggestions = engine.getSuggestions(context);
181
+ expect(suggestions).toHaveLength(1);
182
+ expect(suggestions[0].skillId).toBe('community/docker');
183
+ expect(suggestions[0].skillName).toBe('docker');
184
+ expect(suggestions[0].reason).toContain('native modules');
185
+ });
186
+ it('should suggest linear for Linear-integrated projects', () => {
187
+ const engine = createTestEngine();
188
+ const context = createMockContext({ hasLinear: true });
189
+ const suggestions = engine.getSuggestions(context);
190
+ expect(suggestions).toHaveLength(1);
191
+ expect(suggestions[0].skillId).toBe('user/linear');
192
+ expect(suggestions[0].skillName).toBe('linear');
193
+ });
194
+ it('should suggest review-pr for GitHub projects', () => {
195
+ const engine = createTestEngine();
196
+ const context = createMockContext({ hasGitHub: true });
197
+ const suggestions = engine.getSuggestions(context);
198
+ expect(suggestions).toHaveLength(1);
199
+ expect(suggestions[0].skillId).toBe('anthropic/review-pr');
200
+ expect(suggestions[0].skillName).toBe('review-pr');
201
+ });
202
+ it('should suggest jest-helper for Jest projects', () => {
203
+ const engine = createTestEngine();
204
+ const context = createMockContext({ testFramework: 'jest' });
205
+ const suggestions = engine.getSuggestions(context);
206
+ expect(suggestions).toHaveLength(1);
207
+ expect(suggestions[0].skillId).toBe('community/jest-helper');
208
+ expect(suggestions[0].skillName).toBe('jest-helper');
209
+ });
210
+ it('should suggest vitest-helper for Vitest projects', () => {
211
+ const engine = createTestEngine();
212
+ const context = createMockContext({ testFramework: 'vitest' });
213
+ const suggestions = engine.getSuggestions(context);
214
+ expect(suggestions).toHaveLength(1);
215
+ expect(suggestions[0].skillId).toBe('community/vitest-helper');
216
+ expect(suggestions[0].skillName).toBe('vitest-helper');
217
+ });
218
+ it('should suggest api-docs for Express projects', () => {
219
+ const engine = createTestEngine();
220
+ const context = createMockContext({ apiFramework: 'express' });
221
+ const suggestions = engine.getSuggestions(context);
222
+ expect(suggestions).toHaveLength(1);
223
+ expect(suggestions[0].skillId).toBe('community/api-docs');
224
+ expect(suggestions[0].skillName).toBe('api-docs');
225
+ });
226
+ it('should suggest api-docs for Next.js projects', () => {
227
+ const engine = createTestEngine();
228
+ const context = createMockContext({ apiFramework: 'nextjs' });
229
+ const suggestions = engine.getSuggestions(context);
230
+ expect(suggestions).toHaveLength(1);
231
+ expect(suggestions[0].skillId).toBe('community/api-docs');
232
+ expect(suggestions[0].skillName).toBe('api-docs');
233
+ });
234
+ it('should not suggest FastAPI for api-docs (only express/nextjs)', () => {
235
+ const engine = createTestEngine();
236
+ const context = createMockContext({ apiFramework: 'fastapi' });
237
+ const suggestions = engine.getSuggestions(context);
238
+ expect(suggestions).toHaveLength(0);
239
+ });
240
+ it('should filter out installed skills', () => {
241
+ const engine = createTestEngine();
242
+ const context = createMockContext({ hasLinear: true });
243
+ const suggestions = engine.getSuggestions(context, ['user/linear']);
244
+ expect(suggestions).toHaveLength(0);
245
+ });
246
+ it('should filter out installed skills by partial name match', () => {
247
+ const engine = createTestEngine();
248
+ const context = createMockContext({ hasLinear: true });
249
+ // Match by skill name within the installed skill ID
250
+ const suggestions = engine.getSuggestions(context, ['some-prefix/linear']);
251
+ expect(suggestions).toHaveLength(0);
252
+ });
253
+ it('should filter out dismissed skills', () => {
254
+ const engine = createTestEngine();
255
+ engine.dismissSkill('user/linear');
256
+ const context = createMockContext({ hasLinear: true });
257
+ const suggestions = engine.getSuggestions(context);
258
+ expect(suggestions).toHaveLength(0);
259
+ });
260
+ it('should return highest priority suggestion when multiple match', () => {
261
+ const engine = createTestEngine();
262
+ // Docker + native modules (priority 1) + GitHub (priority 2)
263
+ const context = createMockContext({
264
+ hasDocker: true,
265
+ hasNativeModules: true,
266
+ hasGitHub: true,
267
+ });
268
+ const suggestions = engine.getSuggestions(context);
269
+ expect(suggestions).toHaveLength(1);
270
+ expect(suggestions[0].skillName).toBe('docker');
271
+ expect(suggestions[0].priority).toBe(1);
272
+ });
273
+ it('should include context matches in suggestion', () => {
274
+ const engine = createTestEngine();
275
+ const context = createMockContext({
276
+ hasDocker: true,
277
+ hasNativeModules: true,
278
+ hasGitHub: true,
279
+ testFramework: 'jest',
280
+ });
281
+ const suggestions = engine.getSuggestions(context);
282
+ expect(suggestions[0].contextMatch).toContain('hasDocker');
283
+ expect(suggestions[0].contextMatch).toContain('hasNativeModules');
284
+ expect(suggestions[0].contextMatch).toContain('hasGitHub');
285
+ expect(suggestions[0].contextMatch).toContain('testFramework:jest');
286
+ });
287
+ it('should return empty array for empty context', () => {
288
+ const engine = createTestEngine();
289
+ const context = createMockContext();
290
+ const suggestions = engine.getSuggestions(context);
291
+ expect(suggestions).toEqual([]);
292
+ });
293
+ });
294
+ describe('recordSuggestionShown', () => {
295
+ it('should increment suggestionsToday', () => {
296
+ const engine = createTestEngine();
297
+ engine.recordSuggestionShown();
298
+ expect(engine.getState().suggestionsToday).toBe(1);
299
+ engine.recordSuggestionShown();
300
+ expect(engine.getState().suggestionsToday).toBe(2);
301
+ });
302
+ it('should update lastSuggestionTime', () => {
303
+ const engine = createTestEngine();
304
+ const before = Date.now();
305
+ engine.recordSuggestionShown();
306
+ const state = engine.getState();
307
+ expect(state.lastSuggestionTime).toBeGreaterThanOrEqual(before);
308
+ expect(state.lastSuggestionTime).toBeLessThanOrEqual(Date.now());
309
+ });
310
+ it('should persist state to disk', () => {
311
+ const engine = createTestEngine();
312
+ engine.recordSuggestionShown();
313
+ // Create new engine to load from disk
314
+ const engine2 = createTestEngine();
315
+ expect(engine2.getState().suggestionsToday).toBe(1);
316
+ });
317
+ });
318
+ describe('dismissSkill', () => {
319
+ it('should add skill to dismissed list', () => {
320
+ const engine = createTestEngine();
321
+ engine.dismissSkill('community/docker');
322
+ expect(engine.getState().dismissedSkills).toContain('community/docker');
323
+ });
324
+ it('should not duplicate dismissed skills', () => {
325
+ const engine = createTestEngine();
326
+ engine.dismissSkill('community/docker');
327
+ engine.dismissSkill('community/docker');
328
+ expect(engine.getState().dismissedSkills.filter((s) => s === 'community/docker')).toHaveLength(1);
329
+ });
330
+ it('should persist dismissed skills to disk', () => {
331
+ const engine = createTestEngine();
332
+ engine.dismissSkill('user/linear');
333
+ const engine2 = createTestEngine();
334
+ expect(engine2.getState().dismissedSkills).toContain('user/linear');
335
+ });
336
+ });
337
+ describe('optOut/optIn', () => {
338
+ it('should set optedOut to true', () => {
339
+ const engine = createTestEngine();
340
+ engine.optOut();
341
+ expect(engine.getState().optedOut).toBe(true);
342
+ });
343
+ it('should persist opt-out to disk', () => {
344
+ const engine = createTestEngine();
345
+ engine.optOut();
346
+ const engine2 = createTestEngine();
347
+ expect(engine2.getState().optedOut).toBe(true);
348
+ });
349
+ it('should re-enable suggestions with optIn', () => {
350
+ const engine = createTestEngine();
351
+ engine.optOut();
352
+ expect(engine.canSuggest()).toBe(false);
353
+ engine.optIn();
354
+ expect(engine.getState().optedOut).toBe(false);
355
+ expect(engine.canSuggest()).toBe(true);
356
+ });
357
+ it('should persist opt-in to disk', () => {
358
+ const engine = createTestEngine();
359
+ engine.optOut();
360
+ engine.optIn();
361
+ const engine2 = createTestEngine();
362
+ expect(engine2.getState().optedOut).toBe(false);
363
+ });
364
+ });
365
+ describe('resetState', () => {
366
+ it('should reset all state to defaults', () => {
367
+ const engine = createTestEngine();
368
+ engine.recordSuggestionShown();
369
+ engine.dismissSkill('community/docker');
370
+ engine.optOut();
371
+ engine.resetState();
372
+ const state = engine.getState();
373
+ expect(state.suggestionsToday).toBe(0);
374
+ expect(state.dismissedSkills).toEqual([]);
375
+ expect(state.optedOut).toBe(false);
376
+ expect(state.lastSuggestionTime).toBe(0);
377
+ });
378
+ it('should persist reset state to disk', () => {
379
+ const engine = createTestEngine();
380
+ engine.optOut();
381
+ engine.dismissSkill('user/linear');
382
+ engine.resetState();
383
+ const engine2 = createTestEngine();
384
+ expect(engine2.getState().optedOut).toBe(false);
385
+ expect(engine2.getState().dismissedSkills).toEqual([]);
386
+ });
387
+ });
388
+ describe('getState', () => {
389
+ it('should return a copy of the state', () => {
390
+ const engine = createTestEngine();
391
+ const state = engine.getState();
392
+ // Modifying the returned state should not affect the engine
393
+ state.optedOut = true;
394
+ state.dismissedSkills.push('modified');
395
+ expect(engine.getState().optedOut).toBe(false);
396
+ expect(engine.getState().dismissedSkills).not.toContain('modified');
397
+ });
398
+ });
399
+ describe('daily reset behavior', () => {
400
+ it('should maintain count on same day', () => {
401
+ const engine = createTestEngine();
402
+ engine.recordSuggestionShown();
403
+ engine.recordSuggestionShown();
404
+ // Load fresh engine on same day
405
+ const engine2 = createTestEngine();
406
+ expect(engine2.getState().suggestionsToday).toBe(2);
407
+ });
408
+ it('should not reset count when loading multiple times on same day', () => {
409
+ const engine1 = createTestEngine();
410
+ engine1.recordSuggestionShown();
411
+ const engine2 = createTestEngine();
412
+ engine2.recordSuggestionShown();
413
+ const engine3 = createTestEngine();
414
+ expect(engine3.getState().suggestionsToday).toBe(2);
415
+ });
416
+ });
417
+ describe('edge cases', () => {
418
+ it('should handle missing state directory', () => {
419
+ // Remove the test directory
420
+ fs.rmSync(testDir, { recursive: true, force: true });
421
+ // Directory should be created on first save
422
+ const engine = createTestEngine();
423
+ engine.recordSuggestionShown();
424
+ expect(fs.existsSync(testDir)).toBe(true);
425
+ });
426
+ it('should handle concurrent modifications gracefully', () => {
427
+ const engine1 = createTestEngine();
428
+ const engine2 = createTestEngine();
429
+ engine1.dismissSkill('skill1');
430
+ engine2.dismissSkill('skill2');
431
+ // Last write wins - this is acceptable behavior
432
+ const engine3 = createTestEngine();
433
+ // Should have skill2 (last write)
434
+ expect(engine3.getState().dismissedSkills).toContain('skill2');
435
+ });
436
+ it('should work with zero cooldown', () => {
437
+ const engine = createTestEngine({ cooldownMs: 0 });
438
+ engine.recordSuggestionShown();
439
+ // Should be able to suggest again immediately with zero cooldown
440
+ expect(engine.canSuggest()).toBe(true);
441
+ });
442
+ it('should work with zero max suggestions per day', () => {
443
+ const engine = createTestEngine({ maxSuggestionsPerDay: 0 });
444
+ expect(engine.canSuggest()).toBe(false);
445
+ });
446
+ });
447
+ });
448
+ //# sourceMappingURL=suggestion-engine.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suggestion-engine.test.js","sourceRoot":"","sources":["../../../tests/suggestions/suggestion-engine.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * SMI-992: Shared Test Utilities for Date/Time Mocking
3
+ *
4
+ * Provides consistent date mocking utilities to eliminate flaky tests
5
+ * caused by timing dependencies on Date.now() and new Date() calls.
6
+ */
7
+ /**
8
+ * Fixed timestamp for deterministic testing - January 15, 2024 at 10:00 UTC
9
+ */
10
+ export declare const FIXED_TIMESTAMP = 1705312800000;
11
+ /**
12
+ * Fixed date object for deterministic testing
13
+ */
14
+ export declare const FIXED_DATE: Date;
15
+ /**
16
+ * Fixed ISO string for deterministic testing
17
+ */
18
+ export declare const FIXED_DATE_ISO: string;
19
+ /**
20
+ * One day in milliseconds
21
+ */
22
+ export declare const ONE_DAY_MS: number;
23
+ /**
24
+ * One year in milliseconds (365 days)
25
+ */
26
+ export declare const ONE_YEAR_MS: number;
27
+ /**
28
+ * Creates a date relative to FIXED_DATE
29
+ * @param daysOffset - Number of days to add (negative for past dates)
30
+ * @returns Date object
31
+ */
32
+ export declare function createRelativeDate(daysOffset: number): Date;
33
+ /**
34
+ * Creates an ISO date string relative to FIXED_DATE
35
+ * @param daysOffset - Number of days to add (negative for past dates)
36
+ * @returns ISO date string
37
+ */
38
+ export declare function createRelativeDateISO(daysOffset: number): string;
39
+ /**
40
+ * Sets up fake timers with FIXED_DATE for the current test suite.
41
+ * Call in beforeEach() and pair with cleanupFakeTimers() in afterEach().
42
+ */
43
+ export declare function setupFakeTimers(): void;
44
+ /**
45
+ * Cleans up fake timers after tests.
46
+ * Call in afterEach() when paired with setupFakeTimers().
47
+ */
48
+ export declare function cleanupFakeTimers(): void;
49
+ /**
50
+ * Helper to advance fake timers by a specified amount
51
+ * @param ms - Milliseconds to advance
52
+ */
53
+ export declare function advanceTime(ms: number): void;
54
+ /**
55
+ * Helper to advance fake timers by a number of days
56
+ * @param days - Days to advance
57
+ */
58
+ export declare function advanceTimeByDays(days: number): void;
59
+ /**
60
+ * Creates a unique test directory path with deterministic naming
61
+ * @param prefix - Prefix for the directory name
62
+ * @param counter - A counter to ensure uniqueness within the test
63
+ */
64
+ export declare function createTestDirPath(prefix: string, counter: number): string;
65
+ /**
66
+ * Decorator to set up and tear down fake timers for a describe block.
67
+ * Usage:
68
+ * withFakeTimers(() => {
69
+ * beforeEach(() => { ... })
70
+ * it('test', () => { ... })
71
+ * })
72
+ */
73
+ export declare function withFakeTimers(fn: () => void): void;
74
+ //# sourceMappingURL=test-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../tests/test-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,eAAO,MAAM,eAAe,gBAAgB,CAAA;AAE5C;;GAEG;AACH,eAAO,MAAM,UAAU,MAA4B,CAAA;AAEnD;;GAEG;AACH,eAAO,MAAM,cAAc,QAA2B,CAAA;AAEtD;;GAEG;AACH,eAAO,MAAM,UAAU,QAAsB,CAAA;AAE7C;;GAEG;AACH,eAAO,MAAM,WAAW,QAAmB,CAAA;AAE3C;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAGtC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAUnD"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * SMI-992: Shared Test Utilities for Date/Time Mocking
3
+ *
4
+ * Provides consistent date mocking utilities to eliminate flaky tests
5
+ * caused by timing dependencies on Date.now() and new Date() calls.
6
+ */
7
+ import { vi, beforeEach, afterEach } from 'vitest';
8
+ /**
9
+ * Fixed timestamp for deterministic testing - January 15, 2024 at 10:00 UTC
10
+ */
11
+ export const FIXED_TIMESTAMP = 1705312800000;
12
+ /**
13
+ * Fixed date object for deterministic testing
14
+ */
15
+ export const FIXED_DATE = new Date(FIXED_TIMESTAMP);
16
+ /**
17
+ * Fixed ISO string for deterministic testing
18
+ */
19
+ export const FIXED_DATE_ISO = FIXED_DATE.toISOString();
20
+ /**
21
+ * One day in milliseconds
22
+ */
23
+ export const ONE_DAY_MS = 24 * 60 * 60 * 1000;
24
+ /**
25
+ * One year in milliseconds (365 days)
26
+ */
27
+ export const ONE_YEAR_MS = 365 * ONE_DAY_MS;
28
+ /**
29
+ * Creates a date relative to FIXED_DATE
30
+ * @param daysOffset - Number of days to add (negative for past dates)
31
+ * @returns Date object
32
+ */
33
+ export function createRelativeDate(daysOffset) {
34
+ return new Date(FIXED_TIMESTAMP + daysOffset * ONE_DAY_MS);
35
+ }
36
+ /**
37
+ * Creates an ISO date string relative to FIXED_DATE
38
+ * @param daysOffset - Number of days to add (negative for past dates)
39
+ * @returns ISO date string
40
+ */
41
+ export function createRelativeDateISO(daysOffset) {
42
+ return createRelativeDate(daysOffset).toISOString();
43
+ }
44
+ /**
45
+ * Sets up fake timers with FIXED_DATE for the current test suite.
46
+ * Call in beforeEach() and pair with cleanupFakeTimers() in afterEach().
47
+ */
48
+ export function setupFakeTimers() {
49
+ vi.useFakeTimers();
50
+ vi.setSystemTime(FIXED_DATE);
51
+ }
52
+ /**
53
+ * Cleans up fake timers after tests.
54
+ * Call in afterEach() when paired with setupFakeTimers().
55
+ */
56
+ export function cleanupFakeTimers() {
57
+ vi.useRealTimers();
58
+ }
59
+ /**
60
+ * Helper to advance fake timers by a specified amount
61
+ * @param ms - Milliseconds to advance
62
+ */
63
+ export function advanceTime(ms) {
64
+ vi.advanceTimersByTime(ms);
65
+ }
66
+ /**
67
+ * Helper to advance fake timers by a number of days
68
+ * @param days - Days to advance
69
+ */
70
+ export function advanceTimeByDays(days) {
71
+ vi.advanceTimersByTime(days * ONE_DAY_MS);
72
+ }
73
+ /**
74
+ * Creates a unique test directory path with deterministic naming
75
+ * @param prefix - Prefix for the directory name
76
+ * @param counter - A counter to ensure uniqueness within the test
77
+ */
78
+ export function createTestDirPath(prefix, counter) {
79
+ return `${prefix}-${FIXED_TIMESTAMP}-${counter.toString(36)}`;
80
+ }
81
+ /**
82
+ * Decorator to set up and tear down fake timers for a describe block.
83
+ * Usage:
84
+ * withFakeTimers(() => {
85
+ * beforeEach(() => { ... })
86
+ * it('test', () => { ... })
87
+ * })
88
+ */
89
+ export function withFakeTimers(fn) {
90
+ beforeEach(() => {
91
+ setupFakeTimers();
92
+ });
93
+ afterEach(() => {
94
+ cleanupFakeTimers();
95
+ });
96
+ fn();
97
+ }
98
+ //# sourceMappingURL=test-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../../tests/test-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAElD;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAA;AAE5C;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,IAAI,CAAC,eAAe,CAAC,CAAA;AAEnD;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC,WAAW,EAAE,CAAA;AAEtD;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAE7C;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,GAAG,UAAU,CAAA;AAE3C;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,OAAO,IAAI,IAAI,CAAC,eAAe,GAAG,UAAU,GAAG,UAAU,CAAC,CAAA;AAC5D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,UAAkB;IACtD,OAAO,kBAAkB,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAA;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC7B,EAAE,CAAC,aAAa,EAAE,CAAA;IAClB,EAAE,CAAC,aAAa,CAAC,UAAU,CAAC,CAAA;AAC9B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,EAAE,CAAC,aAAa,EAAE,CAAA;AACpB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,EAAE,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAA;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,EAAE,CAAC,mBAAmB,CAAC,IAAI,GAAG,UAAU,CAAC,CAAA;AAC3C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,OAAe;IAC/D,OAAO,GAAG,MAAM,IAAI,eAAe,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAA;AAC/D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,EAAc;IAC3C,UAAU,CAAC,GAAG,EAAE;QACd,eAAe,EAAE,CAAA;IACnB,CAAC,CAAC,CAAA;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,iBAAiB,EAAE,CAAA;IACrB,CAAC,CAAC,CAAA;IAEF,EAAE,EAAE,CAAA;AACN,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Tests for MCP Tools (SMI-586, SMI-588)
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=tools.test.d.ts.map