@skillsmith/core 0.5.3 → 0.5.5

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 (276) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/src/activation/ActivationManager.d.ts +7 -0
  4. package/dist/src/activation/ActivationManager.d.ts.map +1 -1
  5. package/dist/src/activation/ActivationManager.js +13 -4
  6. package/dist/src/activation/ActivationManager.js.map +1 -1
  7. package/dist/src/analysis/adapters/python.d.ts +16 -11
  8. package/dist/src/analysis/adapters/python.d.ts.map +1 -1
  9. package/dist/src/analysis/adapters/python.js +46 -61
  10. package/dist/src/analysis/adapters/python.js.map +1 -1
  11. package/dist/src/analysis/router.test.d.ts +2 -0
  12. package/dist/src/analysis/router.test.d.ts.map +1 -0
  13. package/dist/src/analysis/router.test.js +411 -0
  14. package/dist/src/analysis/router.test.js.map +1 -0
  15. package/dist/src/analysis/tree-sitter/manager.d.ts.map +1 -1
  16. package/dist/src/analysis/tree-sitter/manager.js +12 -5
  17. package/dist/src/analysis/tree-sitter/manager.js.map +1 -1
  18. package/dist/src/analysis/tree-sitter/pythonExtractor.d.ts +45 -0
  19. package/dist/src/analysis/tree-sitter/pythonExtractor.d.ts.map +1 -0
  20. package/dist/src/analysis/tree-sitter/pythonExtractor.js +264 -0
  21. package/dist/src/analysis/tree-sitter/pythonExtractor.js.map +1 -0
  22. package/dist/src/analysis/tree-sitter/pythonExtractor.test.d.ts +12 -0
  23. package/dist/src/analysis/tree-sitter/pythonExtractor.test.d.ts.map +1 -0
  24. package/dist/src/analysis/tree-sitter/pythonExtractor.test.js +74 -0
  25. package/dist/src/analysis/tree-sitter/pythonExtractor.test.js.map +1 -0
  26. package/dist/src/analysis/tree-sitter/pythonIncremental.d.ts +93 -0
  27. package/dist/src/analysis/tree-sitter/pythonIncremental.d.ts.map +1 -0
  28. package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.d.ts +22 -0
  29. package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.d.ts.map +1 -0
  30. package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.js +229 -0
  31. package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.js.map +1 -0
  32. package/dist/src/analysis/tree-sitter/pythonIncremental.js +287 -0
  33. package/dist/src/analysis/tree-sitter/pythonIncremental.js.map +1 -0
  34. package/dist/src/analysis/tree-sitter/pythonIncremental.test.d.ts +17 -0
  35. package/dist/src/analysis/tree-sitter/pythonIncremental.test.d.ts.map +1 -0
  36. package/dist/src/analysis/tree-sitter/pythonIncremental.test.js +142 -0
  37. package/dist/src/analysis/tree-sitter/pythonIncremental.test.js.map +1 -0
  38. package/dist/src/analysis/tree-sitter/queries/python.d.ts +43 -0
  39. package/dist/src/analysis/tree-sitter/queries/python.d.ts.map +1 -0
  40. package/dist/src/analysis/tree-sitter/queries/python.js +88 -0
  41. package/dist/src/analysis/tree-sitter/queries/python.js.map +1 -0
  42. package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.d.ts +13 -0
  43. package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.d.ts.map +1 -0
  44. package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.js +174 -0
  45. package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.js.map +1 -0
  46. package/dist/src/analytics/ROIDashboardService.csv.d.ts +11 -0
  47. package/dist/src/analytics/ROIDashboardService.csv.d.ts.map +1 -0
  48. package/dist/src/analytics/ROIDashboardService.csv.js +43 -0
  49. package/dist/src/analytics/ROIDashboardService.csv.js.map +1 -0
  50. package/dist/src/analytics/ROIDashboardService.d.ts +64 -3
  51. package/dist/src/analytics/ROIDashboardService.d.ts.map +1 -1
  52. package/dist/src/analytics/ROIDashboardService.js +116 -45
  53. package/dist/src/analytics/ROIDashboardService.js.map +1 -1
  54. package/dist/src/api/client.d.ts +4 -66
  55. package/dist/src/api/client.d.ts.map +1 -1
  56. package/dist/src/api/client.events.d.ts.map +1 -1
  57. package/dist/src/api/client.events.js +10 -1
  58. package/dist/src/api/client.events.js.map +1 -1
  59. package/dist/src/api/client.js +40 -66
  60. package/dist/src/api/client.js.map +1 -1
  61. package/dist/src/api/client.token-refresh.d.ts +3 -0
  62. package/dist/src/api/client.token-refresh.d.ts.map +1 -0
  63. package/dist/src/api/client.token-refresh.js +19 -0
  64. package/dist/src/api/client.token-refresh.js.map +1 -0
  65. package/dist/src/api/client.token-refresh.test.d.ts +2 -0
  66. package/dist/src/api/client.token-refresh.test.d.ts.map +1 -0
  67. package/dist/src/api/client.token-refresh.test.js +73 -0
  68. package/dist/src/api/client.token-refresh.test.js.map +1 -0
  69. package/dist/src/api/client.types.d.ts +2 -0
  70. package/dist/src/api/client.types.d.ts.map +1 -1
  71. package/dist/src/api/index.d.ts +1 -1
  72. package/dist/src/api/index.d.ts.map +1 -1
  73. package/dist/src/api/index.js +1 -1
  74. package/dist/src/api/index.js.map +1 -1
  75. package/dist/src/api/schemas.d.ts +60 -60
  76. package/dist/src/api/schemas.d.ts.map +1 -1
  77. package/dist/src/benchmarks/incrementalParseBenchmark.d.ts +18 -0
  78. package/dist/src/benchmarks/incrementalParseBenchmark.d.ts.map +1 -0
  79. package/dist/src/benchmarks/incrementalParseBenchmark.js +121 -0
  80. package/dist/src/benchmarks/incrementalParseBenchmark.js.map +1 -0
  81. package/dist/src/billing/GDPRComplianceService.test.d.ts +2 -0
  82. package/dist/src/billing/GDPRComplianceService.test.d.ts.map +1 -0
  83. package/dist/src/billing/GDPRComplianceService.test.js +405 -0
  84. package/dist/src/billing/GDPRComplianceService.test.js.map +1 -0
  85. package/dist/src/config/index.d.ts +4 -0
  86. package/dist/src/config/index.d.ts.map +1 -1
  87. package/dist/src/config/index.js +6 -0
  88. package/dist/src/config/index.js.map +1 -1
  89. package/dist/src/config/token-credentials.d.ts +13 -0
  90. package/dist/src/config/token-credentials.d.ts.map +1 -0
  91. package/dist/src/config/token-credentials.js +126 -0
  92. package/dist/src/config/token-credentials.js.map +1 -0
  93. package/dist/src/config/token-credentials.test.d.ts +10 -0
  94. package/dist/src/config/token-credentials.test.d.ts.map +1 -0
  95. package/dist/src/config/token-credentials.test.js +91 -0
  96. package/dist/src/config/token-credentials.test.js.map +1 -0
  97. package/dist/src/index.d.ts +7 -4
  98. package/dist/src/index.d.ts.map +1 -1
  99. package/dist/src/index.js +6 -3
  100. package/dist/src/index.js.map +1 -1
  101. package/dist/src/indexer/SkillParser.coverage.test.d.ts +10 -0
  102. package/dist/src/indexer/SkillParser.coverage.test.d.ts.map +1 -0
  103. package/dist/src/indexer/SkillParser.coverage.test.js +76 -0
  104. package/dist/src/indexer/SkillParser.coverage.test.js.map +1 -0
  105. package/dist/src/indexer/SkillParser.test.d.ts +2 -0
  106. package/dist/src/indexer/SkillParser.test.d.ts.map +1 -0
  107. package/dist/src/indexer/SkillParser.test.js +375 -0
  108. package/dist/src/indexer/SkillParser.test.js.map +1 -0
  109. package/dist/src/scripts/__tests__/scan-imported-skills.test.js +34 -5
  110. package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
  111. package/dist/src/scripts/github-import/blocklist.d.ts +65 -0
  112. package/dist/src/scripts/github-import/blocklist.d.ts.map +1 -0
  113. package/dist/src/scripts/github-import/blocklist.js +124 -0
  114. package/dist/src/scripts/github-import/blocklist.js.map +1 -0
  115. package/dist/src/scripts/github-import/index.d.ts +1 -0
  116. package/dist/src/scripts/github-import/index.d.ts.map +1 -1
  117. package/dist/src/scripts/github-import/index.js +3 -0
  118. package/dist/src/scripts/github-import/index.js.map +1 -1
  119. package/dist/src/scripts/github-import/signal-of-intent.d.ts +87 -0
  120. package/dist/src/scripts/github-import/signal-of-intent.d.ts.map +1 -0
  121. package/dist/src/scripts/github-import/signal-of-intent.js +213 -0
  122. package/dist/src/scripts/github-import/signal-of-intent.js.map +1 -0
  123. package/dist/src/scripts/github-import/types.d.ts +22 -0
  124. package/dist/src/scripts/github-import/types.d.ts.map +1 -1
  125. package/dist/src/scripts/github-import/types.js.map +1 -1
  126. package/dist/src/scripts/import-github-skills.js +73 -3
  127. package/dist/src/scripts/import-github-skills.js.map +1 -1
  128. package/dist/src/scripts/skill-scanner/allowlist.d.ts +38 -0
  129. package/dist/src/scripts/skill-scanner/allowlist.d.ts.map +1 -0
  130. package/dist/src/scripts/skill-scanner/allowlist.js +178 -0
  131. package/dist/src/scripts/skill-scanner/allowlist.js.map +1 -0
  132. package/dist/src/scripts/skill-scanner/scanner.d.ts +10 -2
  133. package/dist/src/scripts/skill-scanner/scanner.d.ts.map +1 -1
  134. package/dist/src/scripts/skill-scanner/scanner.js +15 -3
  135. package/dist/src/scripts/skill-scanner/scanner.js.map +1 -1
  136. package/dist/src/scripts/skill-scanner/trust-scorer.d.ts +20 -6
  137. package/dist/src/scripts/skill-scanner/trust-scorer.d.ts.map +1 -1
  138. package/dist/src/scripts/skill-scanner/trust-scorer.js +28 -9
  139. package/dist/src/scripts/skill-scanner/trust-scorer.js.map +1 -1
  140. package/dist/src/scripts/skill-scanner/types.d.ts +50 -0
  141. package/dist/src/scripts/skill-scanner/types.d.ts.map +1 -1
  142. package/dist/src/scripts/validation/types.d.ts +14 -24
  143. package/dist/src/scripts/validation/types.d.ts.map +1 -1
  144. package/dist/src/security/scanner/SecurityScanner.helpers.d.ts +18 -0
  145. package/dist/src/security/scanner/SecurityScanner.helpers.d.ts.map +1 -1
  146. package/dist/src/security/scanner/SecurityScanner.helpers.js +54 -6
  147. package/dist/src/security/scanner/SecurityScanner.helpers.js.map +1 -1
  148. package/dist/src/security/scanner/patterns.d.ts.map +1 -1
  149. package/dist/src/security/scanner/patterns.js +45 -5
  150. package/dist/src/security/scanner/patterns.js.map +1 -1
  151. package/dist/src/services/skill-config-schema.d.ts +4 -36
  152. package/dist/src/services/skill-config-schema.d.ts.map +1 -1
  153. package/dist/src/sources/LocalFilesystemAdapter.d.ts +104 -10
  154. package/dist/src/sources/LocalFilesystemAdapter.d.ts.map +1 -1
  155. package/dist/src/sources/LocalFilesystemAdapter.helpers.d.ts +92 -0
  156. package/dist/src/sources/LocalFilesystemAdapter.helpers.d.ts.map +1 -0
  157. package/dist/src/sources/LocalFilesystemAdapter.helpers.js +157 -0
  158. package/dist/src/sources/LocalFilesystemAdapter.helpers.js.map +1 -0
  159. package/dist/src/sources/LocalFilesystemAdapter.js +218 -159
  160. package/dist/src/sources/LocalFilesystemAdapter.js.map +1 -1
  161. package/dist/src/sources/LocalFilesystemAdapter.scan.d.ts +78 -0
  162. package/dist/src/sources/LocalFilesystemAdapter.scan.d.ts.map +1 -0
  163. package/dist/src/sources/LocalFilesystemAdapter.scan.js +118 -0
  164. package/dist/src/sources/LocalFilesystemAdapter.scan.js.map +1 -0
  165. package/dist/src/sources/index.d.ts +1 -1
  166. package/dist/src/sources/index.d.ts.map +1 -1
  167. package/dist/src/sources/index.js.map +1 -1
  168. package/dist/src/sources/types.d.ts +28 -0
  169. package/dist/src/sources/types.d.ts.map +1 -1
  170. package/dist/src/telemetry/tracer-imports.d.ts +13 -0
  171. package/dist/src/telemetry/tracer-imports.d.ts.map +1 -0
  172. package/dist/src/telemetry/tracer-imports.js +26 -0
  173. package/dist/src/telemetry/tracer-imports.js.map +1 -0
  174. package/dist/src/telemetry/tracer.d.ts.map +1 -1
  175. package/dist/src/telemetry/tracer.js +18 -21
  176. package/dist/src/telemetry/tracer.js.map +1 -1
  177. package/dist/src/utils/rate-limit.d.ts +39 -0
  178. package/dist/src/utils/rate-limit.d.ts.map +1 -0
  179. package/dist/src/utils/rate-limit.js +48 -0
  180. package/dist/src/utils/rate-limit.js.map +1 -0
  181. package/dist/src/utils/rate-limit.test.d.ts +11 -0
  182. package/dist/src/utils/rate-limit.test.d.ts.map +1 -0
  183. package/dist/src/utils/rate-limit.test.js +86 -0
  184. package/dist/src/utils/rate-limit.test.js.map +1 -0
  185. package/dist/src/webhooks/WebhookDeadLetterRepository.d.ts +178 -0
  186. package/dist/src/webhooks/WebhookDeadLetterRepository.d.ts.map +1 -0
  187. package/dist/src/webhooks/WebhookDeadLetterRepository.js +196 -0
  188. package/dist/src/webhooks/WebhookDeadLetterRepository.js.map +1 -0
  189. package/dist/src/webhooks/WebhookQueue.d.ts +1 -0
  190. package/dist/src/webhooks/WebhookQueue.d.ts.map +1 -1
  191. package/dist/src/webhooks/WebhookQueue.js +19 -0
  192. package/dist/src/webhooks/WebhookQueue.js.map +1 -1
  193. package/dist/src/webhooks/WebhookQueue.types.d.ts +11 -0
  194. package/dist/src/webhooks/WebhookQueue.types.d.ts.map +1 -1
  195. package/dist/src/webhooks/index.d.ts +1 -0
  196. package/dist/src/webhooks/index.d.ts.map +1 -1
  197. package/dist/src/webhooks/index.js +2 -0
  198. package/dist/src/webhooks/index.js.map +1 -1
  199. package/dist/src/webhooks/webhook-schemas.d.ts +117 -1212
  200. package/dist/src/webhooks/webhook-schemas.d.ts.map +1 -1
  201. package/dist/tests/ActivationManager.test.d.ts +13 -0
  202. package/dist/tests/ActivationManager.test.d.ts.map +1 -0
  203. package/dist/tests/ActivationManager.test.js +218 -0
  204. package/dist/tests/ActivationManager.test.js.map +1 -0
  205. package/dist/tests/LocalFilesystemAdapter.coverage.test.d.ts +13 -0
  206. package/dist/tests/LocalFilesystemAdapter.coverage.test.d.ts.map +1 -0
  207. package/dist/tests/LocalFilesystemAdapter.coverage.test.js +314 -0
  208. package/dist/tests/LocalFilesystemAdapter.coverage.test.js.map +1 -0
  209. package/dist/tests/LocalFilesystemAdapter.security.test.d.ts +18 -0
  210. package/dist/tests/LocalFilesystemAdapter.security.test.d.ts.map +1 -0
  211. package/dist/tests/LocalFilesystemAdapter.security.test.js +344 -0
  212. package/dist/tests/LocalFilesystemAdapter.security.test.js.map +1 -0
  213. package/dist/tests/LocalFilesystemAdapter.test.d.ts +12 -0
  214. package/dist/tests/LocalFilesystemAdapter.test.d.ts.map +1 -0
  215. package/dist/tests/LocalFilesystemAdapter.test.js +301 -0
  216. package/dist/tests/LocalFilesystemAdapter.test.js.map +1 -0
  217. package/dist/tests/ROIDashboardService.coverage.test.d.ts +9 -0
  218. package/dist/tests/ROIDashboardService.coverage.test.d.ts.map +1 -0
  219. package/dist/tests/ROIDashboardService.coverage.test.js +118 -0
  220. package/dist/tests/ROIDashboardService.coverage.test.js.map +1 -0
  221. package/dist/tests/ROIDashboardService.test.js +87 -0
  222. package/dist/tests/ROIDashboardService.test.js.map +1 -1
  223. package/dist/tests/ScraperAdapters.gitlab-coverage.test.d.ts +14 -0
  224. package/dist/tests/ScraperAdapters.gitlab-coverage.test.d.ts.map +1 -0
  225. package/dist/tests/ScraperAdapters.gitlab-coverage.test.js +169 -0
  226. package/dist/tests/ScraperAdapters.gitlab-coverage.test.js.map +1 -0
  227. package/dist/tests/ScraperAdapters.test.d.ts +5 -1
  228. package/dist/tests/ScraperAdapters.test.d.ts.map +1 -1
  229. package/dist/tests/ScraperAdapters.test.js +6 -336
  230. package/dist/tests/ScraperAdapters.test.js.map +1 -1
  231. package/dist/tests/WebhookDeadLetterRepository.test.d.ts +2 -0
  232. package/dist/tests/WebhookDeadLetterRepository.test.d.ts.map +1 -0
  233. package/dist/tests/WebhookDeadLetterRepository.test.js +333 -0
  234. package/dist/tests/WebhookDeadLetterRepository.test.js.map +1 -0
  235. package/dist/tests/WebhookHandler.test.js +93 -1
  236. package/dist/tests/WebhookHandler.test.js.map +1 -1
  237. package/dist/tests/WebhookQueue.coverage.test.d.ts +19 -0
  238. package/dist/tests/WebhookQueue.coverage.test.d.ts.map +1 -0
  239. package/dist/tests/WebhookQueue.coverage.test.js +190 -0
  240. package/dist/tests/WebhookQueue.coverage.test.js.map +1 -0
  241. package/dist/tests/api/client.events.test.d.ts +10 -0
  242. package/dist/tests/api/client.events.test.d.ts.map +1 -0
  243. package/dist/tests/api/client.events.test.js +85 -0
  244. package/dist/tests/api/client.events.test.js.map +1 -0
  245. package/dist/tests/billing/GDPRCompliance.test.d.ts +2 -2
  246. package/dist/tests/billing/GDPRCompliance.test.js +221 -36
  247. package/dist/tests/billing/GDPRCompliance.test.js.map +1 -1
  248. package/dist/tests/github-import/blocklist.test.d.ts +15 -0
  249. package/dist/tests/github-import/blocklist.test.d.ts.map +1 -0
  250. package/dist/tests/github-import/blocklist.test.js +182 -0
  251. package/dist/tests/github-import/blocklist.test.js.map +1 -0
  252. package/dist/tests/github-import/signal-of-intent.test.d.ts +15 -0
  253. package/dist/tests/github-import/signal-of-intent.test.d.ts.map +1 -0
  254. package/dist/tests/github-import/signal-of-intent.test.js +171 -0
  255. package/dist/tests/github-import/signal-of-intent.test.js.map +1 -0
  256. package/dist/tests/security/scanner-regression-guard.test.d.ts +12 -0
  257. package/dist/tests/security/scanner-regression-guard.test.d.ts.map +1 -1
  258. package/dist/tests/security/scanner-regression-guard.test.js +15 -3
  259. package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
  260. package/dist/tests/security/scanner-wave2-fixtures.test.d.ts +12 -0
  261. package/dist/tests/security/scanner-wave2-fixtures.test.d.ts.map +1 -0
  262. package/dist/tests/security/scanner-wave2-fixtures.test.js +173 -0
  263. package/dist/tests/security/scanner-wave2-fixtures.test.js.map +1 -0
  264. package/dist/tests/security.test.js +1 -0
  265. package/dist/tests/security.test.js.map +1 -1
  266. package/dist/tests/skill-scanner/allowlist.test.d.ts +16 -0
  267. package/dist/tests/skill-scanner/allowlist.test.d.ts.map +1 -0
  268. package/dist/tests/skill-scanner/allowlist.test.js +332 -0
  269. package/dist/tests/skill-scanner/allowlist.test.js.map +1 -0
  270. package/dist/tests/telemetry.test.js +126 -0
  271. package/dist/tests/telemetry.test.js.map +1 -1
  272. package/dist/tests/webhooks/WebhookDeadLetterRepository.test.d.ts +10 -0
  273. package/dist/tests/webhooks/WebhookDeadLetterRepository.test.d.ts.map +1 -0
  274. package/dist/tests/webhooks/WebhookDeadLetterRepository.test.js +109 -0
  275. package/dist/tests/webhooks/WebhookDeadLetterRepository.test.js.map +1 -0
  276. package/package.json +8 -3
@@ -0,0 +1,14 @@
1
+ /**
2
+ * GitLabSourceAdapter additional coverage (SMI-4290 / closes #595)
3
+ *
4
+ * Sidecar to ScraperAdapters.test.ts — fills genuine coverage gaps for
5
+ * `skillExists`, `getSkillSha`, and the `createGitLabAdapter` factory.
6
+ *
7
+ * The primary suite (`ScraperAdapters.test.ts:631`) already has 14 tests
8
+ * covering init, health, search, pagination, `getRepository`, and
9
+ * `fetchSkillContent`. This file stays under 500 lines and is kept
10
+ * separate so pre-commit's file-length check does not block edits to
11
+ * the main (>500 line) test file.
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=ScraperAdapters.gitlab-coverage.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScraperAdapters.gitlab-coverage.test.d.ts","sourceRoot":"","sources":["../../tests/ScraperAdapters.gitlab-coverage.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * GitLabSourceAdapter additional coverage (SMI-4290 / closes #595)
3
+ *
4
+ * Sidecar to ScraperAdapters.test.ts — fills genuine coverage gaps for
5
+ * `skillExists`, `getSkillSha`, and the `createGitLabAdapter` factory.
6
+ *
7
+ * The primary suite (`ScraperAdapters.test.ts:631`) already has 14 tests
8
+ * covering init, health, search, pagination, `getRepository`, and
9
+ * `fetchSkillContent`. This file stays under 500 lines and is kept
10
+ * separate so pre-commit's file-length check does not block edits to
11
+ * the main (>500 line) test file.
12
+ */
13
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
14
+ import { GitLabSourceAdapter, createGitLabAdapter } from '../src/sources/GitLabSourceAdapter.js';
15
+ const mockFetch = vi.fn();
16
+ vi.stubGlobal('fetch', mockFetch);
17
+ function okResponse(body) {
18
+ return {
19
+ ok: true,
20
+ status: 200,
21
+ headers: new Headers(),
22
+ json: async () => body,
23
+ };
24
+ }
25
+ function notFoundResponse() {
26
+ return {
27
+ ok: false,
28
+ status: 404,
29
+ headers: new Headers(),
30
+ };
31
+ }
32
+ function rateLimitedResponse() {
33
+ return {
34
+ ok: false,
35
+ status: 429,
36
+ headers: new Headers(),
37
+ };
38
+ }
39
+ describe('GitLabSourceAdapter — additional coverage (SMI-4290)', () => {
40
+ let adapter;
41
+ beforeEach(() => {
42
+ mockFetch.mockReset();
43
+ adapter = new GitLabSourceAdapter({
44
+ id: 'test-gitlab-coverage',
45
+ name: 'Test GitLab Coverage',
46
+ type: 'gitlab',
47
+ baseUrl: 'https://gitlab.com/api/v4',
48
+ enabled: true,
49
+ rateLimit: { maxRequests: 100, windowMs: 60000, minDelayMs: 0 },
50
+ });
51
+ });
52
+ describe('skillExists', () => {
53
+ it('returns true when the default SKILL.md path resolves 200', async () => {
54
+ mockFetch.mockResolvedValueOnce(okResponse({}));
55
+ const exists = await adapter.skillExists({ owner: 'user', repo: 'skill-repo' });
56
+ expect(exists).toBe(true);
57
+ expect(mockFetch).toHaveBeenCalledTimes(1);
58
+ const [url, init] = mockFetch.mock.calls[0];
59
+ expect(String(url)).toContain('/projects/user%2Fskill-repo/repository/files/');
60
+ expect(init.method).toBe('HEAD');
61
+ });
62
+ it('returns false when every candidate path returns 404', async () => {
63
+ // GitLabSourceAdapter falls back through SKILL_FILE_PATHS; each lookup 404s.
64
+ mockFetch.mockResolvedValue(notFoundResponse());
65
+ const exists = await adapter.skillExists({ owner: 'user', repo: 'missing-repo' });
66
+ expect(exists).toBe(false);
67
+ expect(mockFetch.mock.calls.length).toBeGreaterThan(0);
68
+ });
69
+ it('returns true when an explicit path is provided and resolves', async () => {
70
+ mockFetch.mockResolvedValueOnce(okResponse({}));
71
+ const exists = await adapter.skillExists({
72
+ owner: 'user',
73
+ repo: 'skill-repo',
74
+ path: 'docs/custom-skill.md',
75
+ });
76
+ expect(exists).toBe(true);
77
+ // With an explicit path only one request should be attempted.
78
+ expect(mockFetch).toHaveBeenCalledTimes(1);
79
+ expect(String(mockFetch.mock.calls[0][0])).toContain(encodeURIComponent('docs/custom-skill.md'));
80
+ });
81
+ it('returns false when the request throws (network error surfaced as continue)', async () => {
82
+ mockFetch.mockRejectedValue(new Error('ECONNRESET'));
83
+ const exists = await adapter.skillExists({ owner: 'user', repo: 'broken-repo' });
84
+ expect(exists).toBe(false);
85
+ });
86
+ it('returns false when the rate-limited status is surfaced (not a 200)', async () => {
87
+ mockFetch.mockResolvedValue(rateLimitedResponse());
88
+ const exists = await adapter.skillExists({ owner: 'user', repo: 'limited-repo' });
89
+ expect(exists).toBe(false);
90
+ });
91
+ });
92
+ describe('getSkillSha', () => {
93
+ it('returns the content SHA-256 when the file resolves', async () => {
94
+ mockFetch.mockResolvedValueOnce(okResponse({
95
+ file_name: 'SKILL.md',
96
+ file_path: 'SKILL.md',
97
+ content: '',
98
+ encoding: 'base64',
99
+ content_sha256: 'sha-abc-123',
100
+ }));
101
+ const sha = await adapter.getSkillSha({ owner: 'user', repo: 'skill-repo' });
102
+ expect(sha).toBe('sha-abc-123');
103
+ });
104
+ it('returns null when every candidate path 404s', async () => {
105
+ mockFetch.mockResolvedValue(notFoundResponse());
106
+ const sha = await adapter.getSkillSha({ owner: 'user', repo: 'missing-repo' });
107
+ expect(sha).toBeNull();
108
+ });
109
+ it('falls back to the second path when the first 404s', async () => {
110
+ mockFetch.mockResolvedValueOnce(notFoundResponse()).mockResolvedValueOnce(okResponse({
111
+ file_name: 'skill.md',
112
+ file_path: 'skill.md',
113
+ content: '',
114
+ encoding: 'base64',
115
+ content_sha256: 'sha-fallback-789',
116
+ }));
117
+ const sha = await adapter.getSkillSha({ owner: 'user', repo: 'skill-repo' });
118
+ expect(sha).toBe('sha-fallback-789');
119
+ expect(mockFetch).toHaveBeenCalledTimes(2);
120
+ });
121
+ it('returns null when a network error is thrown and no path succeeds', async () => {
122
+ mockFetch.mockRejectedValue(new Error('ETIMEDOUT'));
123
+ const sha = await adapter.getSkillSha({ owner: 'user', repo: 'broken-repo' });
124
+ expect(sha).toBeNull();
125
+ });
126
+ });
127
+ describe('createGitLabAdapter factory', () => {
128
+ it('returns a GitLabSourceAdapter with type set to gitlab', () => {
129
+ const built = createGitLabAdapter({
130
+ id: 'factory-gitlab',
131
+ name: 'Factory GitLab',
132
+ enabled: true,
133
+ });
134
+ expect(built).toBeInstanceOf(GitLabSourceAdapter);
135
+ expect(built.type).toBe('gitlab');
136
+ });
137
+ it('defaults baseUrl to the public gitlab.com endpoint when not provided', async () => {
138
+ const built = createGitLabAdapter({
139
+ id: 'factory-default-url',
140
+ name: 'Factory Default URL',
141
+ enabled: true,
142
+ });
143
+ mockFetch.mockResolvedValueOnce(okResponse({}));
144
+ await built.skillExists({ owner: 'user', repo: 'skill-repo' });
145
+ expect(String(mockFetch.mock.calls[0][0])).toContain('https://gitlab.com/api/v4');
146
+ });
147
+ it('respects a custom baseUrl when provided', async () => {
148
+ const built = createGitLabAdapter({
149
+ id: 'factory-custom-url',
150
+ name: 'Factory Custom URL',
151
+ enabled: true,
152
+ baseUrl: 'https://gitlab.example.com/api/v4',
153
+ });
154
+ mockFetch.mockResolvedValueOnce(okResponse({}));
155
+ await built.skillExists({ owner: 'user', repo: 'skill-repo' });
156
+ expect(String(mockFetch.mock.calls[0][0])).toContain('https://gitlab.example.com/api/v4');
157
+ });
158
+ it('forwards auth configuration to the constructed adapter', () => {
159
+ const built = createGitLabAdapter({
160
+ id: 'factory-auth',
161
+ name: 'Factory Auth',
162
+ enabled: true,
163
+ auth: { type: 'token', credentials: 'glpat-stub' },
164
+ });
165
+ expect(built).toBeInstanceOf(GitLabSourceAdapter);
166
+ });
167
+ });
168
+ });
169
+ //# sourceMappingURL=ScraperAdapters.gitlab-coverage.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScraperAdapters.gitlab-coverage.test.js","sourceRoot":"","sources":["../../tests/ScraperAdapters.gitlab-coverage.test.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * Scraper Adapters Tests (SMI-591)
3
3
  *
4
- * Tests for RawUrlSourceAdapter, LocalFilesystemAdapter, and GitLabSourceAdapter
4
+ * Tests for RawUrlSourceAdapter and GitLabSourceAdapter.
5
+ *
6
+ * LocalFilesystemAdapter tests live in `LocalFilesystemAdapter.test.ts`
7
+ * (extracted by SMI-4287 to keep this file under the 500-line governance
8
+ * ceiling and avoid a duplicate `describe` block — see SMI-4286 retro).
5
9
  */
6
10
  export {};
7
11
  //# sourceMappingURL=ScraperAdapters.test.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ScraperAdapters.test.d.ts","sourceRoot":"","sources":["../../tests/ScraperAdapters.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
1
+ {"version":3,"file":"ScraperAdapters.test.d.ts","sourceRoot":"","sources":["../../tests/ScraperAdapters.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * Scraper Adapters Tests (SMI-591)
3
3
  *
4
- * Tests for RawUrlSourceAdapter, LocalFilesystemAdapter, and GitLabSourceAdapter
4
+ * Tests for RawUrlSourceAdapter and GitLabSourceAdapter.
5
+ *
6
+ * LocalFilesystemAdapter tests live in `LocalFilesystemAdapter.test.ts`
7
+ * (extracted by SMI-4287 to keep this file under the 500-line governance
8
+ * ceiling and avoid a duplicate `describe` block — see SMI-4286 retro).
5
9
  */
6
- import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
10
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
7
11
  import { RawUrlSourceAdapter } from '../src/sources/RawUrlSourceAdapter.js';
8
- import { LocalFilesystemAdapter } from '../src/sources/LocalFilesystemAdapter.js';
9
12
  import { GitLabSourceAdapter } from '../src/sources/GitLabSourceAdapter.js';
10
- import { promises as fs } from 'fs';
11
- import { join } from 'path';
12
- import { tmpdir } from 'os';
13
13
  // Mock fetch for network adapters
14
14
  const mockFetch = vi.fn();
15
15
  vi.stubGlobal('fetch', mockFetch);
@@ -191,336 +191,6 @@ describe('RawUrlSourceAdapter (SMI-591)', () => {
191
191
  });
192
192
  });
193
193
  });
194
- describe('LocalFilesystemAdapter (SMI-591)', () => {
195
- let adapter;
196
- let testDir;
197
- beforeEach(async () => {
198
- // Create test directory structure
199
- testDir = join(tmpdir(), `skillsmith-test-${Date.now()}`);
200
- await fs.mkdir(testDir, { recursive: true });
201
- // Create test skill files
202
- await fs.mkdir(join(testDir, 'skill-one'), { recursive: true });
203
- await fs.writeFile(join(testDir, 'skill-one', 'SKILL.md'), '---\nname: Skill One\ndescription: First skill\n---\n# Skill One');
204
- await fs.mkdir(join(testDir, 'skill-two'), { recursive: true });
205
- await fs.writeFile(join(testDir, 'skill-two', 'SKILL.md'), '---\nname: Skill Two\n---\n# Skill Two');
206
- // Create node_modules (should be excluded)
207
- await fs.mkdir(join(testDir, 'node_modules', 'some-module'), { recursive: true });
208
- await fs.writeFile(join(testDir, 'node_modules', 'some-module', 'SKILL.md'), '# Should be excluded');
209
- adapter = new LocalFilesystemAdapter({
210
- id: 'test-local',
211
- name: 'Test Local',
212
- type: 'local',
213
- baseUrl: 'file://',
214
- enabled: true,
215
- rootDir: testDir,
216
- rateLimit: { maxRequests: 100, windowMs: 60000, minDelayMs: 0 },
217
- });
218
- await adapter.initialize();
219
- });
220
- afterEach(async () => {
221
- // Clean up test directory
222
- try {
223
- await fs.rm(testDir, { recursive: true, force: true });
224
- }
225
- catch {
226
- // Ignore cleanup errors
227
- }
228
- });
229
- describe('Initialization', () => {
230
- it('should discover skill files in root directory', () => {
231
- expect(adapter.skillCount).toBe(2);
232
- });
233
- it('should exclude node_modules', () => {
234
- expect(adapter.skillCount).toBe(2); // Only skill-one and skill-two
235
- });
236
- });
237
- describe('Health Check', () => {
238
- it('should return healthy when root directory exists', async () => {
239
- const health = await adapter.checkHealth();
240
- expect(health.healthy).toBe(true);
241
- });
242
- it('should return unhealthy when root directory does not exist', async () => {
243
- const badAdapter = new LocalFilesystemAdapter({
244
- id: 'bad',
245
- name: 'Bad',
246
- type: 'local',
247
- baseUrl: 'file://',
248
- enabled: true,
249
- rootDir: '/nonexistent/path',
250
- rateLimit: { maxRequests: 100, windowMs: 60000, minDelayMs: 0 },
251
- });
252
- const health = await badAdapter.checkHealth();
253
- expect(health.healthy).toBe(false);
254
- });
255
- });
256
- describe('Search', () => {
257
- it('should return all discovered skills', async () => {
258
- const result = await adapter.search({});
259
- expect(result.repositories).toHaveLength(2);
260
- });
261
- it('should filter by query', async () => {
262
- const result = await adapter.search({ query: 'skill-one' });
263
- expect(result.repositories).toHaveLength(1);
264
- expect(result.repositories[0].name).toBe('Skill One');
265
- });
266
- it('should extract name from frontmatter', async () => {
267
- const result = await adapter.search({});
268
- const names = result.repositories.map((r) => r.name);
269
- expect(names).toContain('Skill One');
270
- expect(names).toContain('Skill Two');
271
- });
272
- it('should extract description from frontmatter', async () => {
273
- const result = await adapter.search({});
274
- const skillOne = result.repositories.find((r) => r.name === 'Skill One');
275
- expect(skillOne?.description).toBe('First skill');
276
- });
277
- });
278
- describe('Fetch Skill Content', () => {
279
- it('should fetch skill content by path', async () => {
280
- const content = await adapter.fetchSkillContent({
281
- path: join(testDir, 'skill-one', 'SKILL.md'),
282
- });
283
- expect(content.rawContent).toContain('# Skill One');
284
- expect(content.sha).toBeDefined();
285
- });
286
- it('should throw error for non-existent skill', async () => {
287
- await expect(adapter.fetchSkillContent({
288
- path: join(testDir, 'nonexistent', 'SKILL.md'),
289
- })).rejects.toThrow('Failed to read skill file');
290
- });
291
- });
292
- describe('Skill Exists', () => {
293
- it('should return true for existing skill', async () => {
294
- const exists = await adapter.skillExists({
295
- path: join(testDir, 'skill-one', 'SKILL.md'),
296
- });
297
- expect(exists).toBe(true);
298
- });
299
- it('should return false for non-existent skill', async () => {
300
- const exists = await adapter.skillExists({
301
- path: join(testDir, 'nonexistent', 'SKILL.md'),
302
- });
303
- expect(exists).toBe(false);
304
- });
305
- });
306
- describe('Rescan', () => {
307
- it('should discover newly added skills', async () => {
308
- // Add a new skill
309
- await fs.mkdir(join(testDir, 'skill-three'), { recursive: true });
310
- await fs.writeFile(join(testDir, 'skill-three', 'SKILL.md'), '# Skill Three');
311
- // Rescan
312
- const count = await adapter.rescan();
313
- expect(count).toBe(3);
314
- });
315
- });
316
- describe('Path Traversal Prevention (SMI-720)', () => {
317
- it('should reject relative path traversal with ../', async () => {
318
- await expect(adapter.fetchSkillContent({
319
- path: '../../../etc/passwd',
320
- })).rejects.toThrow('Path traversal detected');
321
- });
322
- it('should reject deeply nested path traversal', async () => {
323
- await expect(adapter.fetchSkillContent({
324
- path: 'skill-one/../../../../../../etc/shadow',
325
- })).rejects.toThrow('Path traversal detected');
326
- });
327
- it('should reject absolute paths outside rootDir', async () => {
328
- await expect(adapter.fetchSkillContent({
329
- path: '/etc/passwd',
330
- })).rejects.toThrow('Path traversal detected');
331
- });
332
- it('should reject path traversal via owner/repo', async () => {
333
- await expect(adapter.getRepository({
334
- owner: '..',
335
- repo: '../../../etc',
336
- })).rejects.toThrow('Path traversal detected');
337
- });
338
- it('should reject path traversal via repo only', async () => {
339
- await expect(adapter.skillExists({
340
- repo: '../../../etc/passwd',
341
- })).rejects.toThrow('Path traversal detected');
342
- });
343
- it('should allow valid paths within rootDir', async () => {
344
- const exists = await adapter.skillExists({
345
- path: join(testDir, 'skill-one', 'SKILL.md'),
346
- });
347
- expect(exists).toBe(true);
348
- });
349
- it('should allow valid relative paths that stay within rootDir', async () => {
350
- const content = await adapter.fetchSkillContent({
351
- path: 'skill-one/SKILL.md',
352
- });
353
- expect(content.rawContent).toContain('# Skill One');
354
- });
355
- });
356
- describe('Symlink Handling (SMI-724)', () => {
357
- it('should skip symlinks by default', async () => {
358
- // Create a symlink to external directory
359
- const externalDir = join(tmpdir(), `external-${Date.now()}`);
360
- await fs.mkdir(externalDir, { recursive: true });
361
- await fs.writeFile(join(externalDir, 'SKILL.md'), '# External Skill');
362
- try {
363
- await fs.symlink(externalDir, join(testDir, 'symlink-skill'));
364
- }
365
- catch {
366
- // Skip test on platforms that don't support symlinks
367
- return;
368
- }
369
- // Create new adapter (symlinks disabled by default)
370
- const newAdapter = new LocalFilesystemAdapter({
371
- id: 'test-symlink',
372
- name: 'Test Symlink',
373
- type: 'local',
374
- baseUrl: 'file://',
375
- enabled: true,
376
- rootDir: testDir,
377
- followSymlinks: false,
378
- rateLimit: { maxRequests: 100, windowMs: 60000, minDelayMs: 0 },
379
- });
380
- await newAdapter.initialize();
381
- // Should not include symlinked skill
382
- expect(newAdapter.skillCount).toBe(2);
383
- // Cleanup
384
- await fs.rm(externalDir, { recursive: true, force: true });
385
- });
386
- it('should follow symlinks when enabled', async () => {
387
- // Create a symlink to skill directory
388
- const externalDir = join(tmpdir(), `external-follow-${Date.now()}`);
389
- await fs.mkdir(externalDir, { recursive: true });
390
- await fs.writeFile(join(externalDir, 'SKILL.md'), '# Symlinked Skill');
391
- try {
392
- await fs.symlink(externalDir, join(testDir, 'symlink-follow'));
393
- }
394
- catch {
395
- // Skip test on platforms that don't support symlinks
396
- return;
397
- }
398
- const followAdapter = new LocalFilesystemAdapter({
399
- id: 'test-follow-symlink',
400
- name: 'Test Follow Symlink',
401
- type: 'local',
402
- baseUrl: 'file://',
403
- enabled: true,
404
- rootDir: testDir,
405
- followSymlinks: true,
406
- rateLimit: { maxRequests: 100, windowMs: 60000, minDelayMs: 0 },
407
- });
408
- await followAdapter.initialize();
409
- // Should include symlinked skill
410
- expect(followAdapter.skillCount).toBe(3);
411
- // Cleanup
412
- await fs.rm(externalDir, { recursive: true, force: true });
413
- });
414
- });
415
- describe('Deep Directory Structures (SMI-724)', () => {
416
- it('should respect maxDepth limit', async () => {
417
- // Create deeply nested skill
418
- const deepPath = join(testDir, 'level1', 'level2', 'level3', 'level4', 'level5', 'level6');
419
- await fs.mkdir(deepPath, { recursive: true });
420
- await fs.writeFile(join(deepPath, 'SKILL.md'), '# Deep Skill');
421
- const shallowAdapter = new LocalFilesystemAdapter({
422
- id: 'test-shallow',
423
- name: 'Test Shallow',
424
- type: 'local',
425
- baseUrl: 'file://',
426
- enabled: true,
427
- rootDir: testDir,
428
- maxDepth: 3, // Only go 3 levels deep
429
- rateLimit: { maxRequests: 100, windowMs: 60000, minDelayMs: 0 },
430
- });
431
- await shallowAdapter.initialize();
432
- // Should not find the deep skill (at level 6)
433
- expect(shallowAdapter.skillCount).toBe(2);
434
- });
435
- it('should find skills within maxDepth', async () => {
436
- // Create skill at level 3
437
- const level3Path = join(testDir, 'a', 'b', 'c');
438
- await fs.mkdir(level3Path, { recursive: true });
439
- await fs.writeFile(join(level3Path, 'SKILL.md'), '# Level 3 Skill');
440
- const deepAdapter = new LocalFilesystemAdapter({
441
- id: 'test-deep',
442
- name: 'Test Deep',
443
- type: 'local',
444
- baseUrl: 'file://',
445
- enabled: true,
446
- rootDir: testDir,
447
- maxDepth: 5,
448
- rateLimit: { maxRequests: 100, windowMs: 60000, minDelayMs: 0 },
449
- });
450
- await deepAdapter.initialize();
451
- // Should find the level 3 skill
452
- expect(deepAdapter.skillCount).toBe(3);
453
- });
454
- });
455
- describe('Invalid Regex Patterns (SMI-722)', () => {
456
- it('should not crash with invalid regex patterns like unclosed parenthesis', async () => {
457
- // Create adapter with invalid regex pattern that would crash without fix
458
- const adapterWithInvalidPattern = new LocalFilesystemAdapter({
459
- id: 'test-invalid-regex',
460
- name: 'Test Invalid Regex',
461
- type: 'local',
462
- baseUrl: 'file://',
463
- enabled: true,
464
- rootDir: testDir,
465
- excludePatterns: ['(', 'node_modules'], // '(' is invalid regex
466
- rateLimit: { maxRequests: 100, windowMs: 60000, minDelayMs: 0 },
467
- });
468
- // Should not throw during initialization
469
- await expect(adapterWithInvalidPattern.initialize()).resolves.not.toThrow();
470
- expect(adapterWithInvalidPattern.skillCount).toBe(2);
471
- });
472
- it('should fall back to includes check for invalid regex patterns', async () => {
473
- // Create a directory that contains the invalid pattern as substring
474
- await fs.mkdir(join(testDir, 'test(dir'), { recursive: true });
475
- await fs.writeFile(join(testDir, 'test(dir', 'SKILL.md'), '# Test Paren Dir');
476
- const adapterWithInvalidPattern = new LocalFilesystemAdapter({
477
- id: 'test-includes-fallback',
478
- name: 'Test Includes Fallback',
479
- type: 'local',
480
- baseUrl: 'file://',
481
- enabled: true,
482
- rootDir: testDir,
483
- excludePatterns: ['(', 'node_modules'], // '(' should match via includes
484
- rateLimit: { maxRequests: 100, windowMs: 60000, minDelayMs: 0 },
485
- });
486
- await adapterWithInvalidPattern.initialize();
487
- // test(dir should be excluded because it includes '('
488
- expect(adapterWithInvalidPattern.skillCount).toBe(2);
489
- });
490
- it('should handle multiple invalid regex patterns', async () => {
491
- const adapterWithMultipleInvalid = new LocalFilesystemAdapter({
492
- id: 'test-multiple-invalid',
493
- name: 'Test Multiple Invalid',
494
- type: 'local',
495
- baseUrl: 'file://',
496
- enabled: true,
497
- rootDir: testDir,
498
- excludePatterns: ['[invalid', '(unclosed', '*bad', 'node_modules'],
499
- rateLimit: { maxRequests: 100, windowMs: 60000, minDelayMs: 0 },
500
- });
501
- await expect(adapterWithMultipleInvalid.initialize()).resolves.not.toThrow();
502
- expect(adapterWithMultipleInvalid.skillCount).toBe(2);
503
- });
504
- it('should still work with valid regex patterns', async () => {
505
- // Create a directory matching a valid regex pattern
506
- await fs.mkdir(join(testDir, 'test-temp-123'), { recursive: true });
507
- await fs.writeFile(join(testDir, 'test-temp-123', 'SKILL.md'), '# Temp Skill');
508
- const adapterWithValidRegex = new LocalFilesystemAdapter({
509
- id: 'test-valid-regex',
510
- name: 'Test Valid Regex',
511
- type: 'local',
512
- baseUrl: 'file://',
513
- enabled: true,
514
- rootDir: testDir,
515
- excludePatterns: ['test-temp-\\d+', 'node_modules'], // Valid regex
516
- rateLimit: { maxRequests: 100, windowMs: 60000, minDelayMs: 0 },
517
- });
518
- await adapterWithValidRegex.initialize();
519
- // test-temp-123 should be excluded by valid regex
520
- expect(adapterWithValidRegex.skillCount).toBe(2);
521
- });
522
- });
523
- });
524
194
  describe('GitLabSourceAdapter (SMI-591)', () => {
525
195
  let adapter;
526
196
  beforeEach(() => {