@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.
- package/CHANGELOG.md +14 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/activation/ActivationManager.d.ts +7 -0
- package/dist/src/activation/ActivationManager.d.ts.map +1 -1
- package/dist/src/activation/ActivationManager.js +13 -4
- package/dist/src/activation/ActivationManager.js.map +1 -1
- package/dist/src/analysis/adapters/python.d.ts +16 -11
- package/dist/src/analysis/adapters/python.d.ts.map +1 -1
- package/dist/src/analysis/adapters/python.js +46 -61
- package/dist/src/analysis/adapters/python.js.map +1 -1
- package/dist/src/analysis/router.test.d.ts +2 -0
- package/dist/src/analysis/router.test.d.ts.map +1 -0
- package/dist/src/analysis/router.test.js +411 -0
- package/dist/src/analysis/router.test.js.map +1 -0
- package/dist/src/analysis/tree-sitter/manager.d.ts.map +1 -1
- package/dist/src/analysis/tree-sitter/manager.js +12 -5
- package/dist/src/analysis/tree-sitter/manager.js.map +1 -1
- package/dist/src/analysis/tree-sitter/pythonExtractor.d.ts +45 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.js +264 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.js.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.test.d.ts +12 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.test.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.test.js +74 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.test.js.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.d.ts +93 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.d.ts +22 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.js +229 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.js.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.js +287 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.js.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.test.d.ts +17 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.test.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.test.js +142 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.test.js.map +1 -0
- package/dist/src/analysis/tree-sitter/queries/python.d.ts +43 -0
- package/dist/src/analysis/tree-sitter/queries/python.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/queries/python.js +88 -0
- package/dist/src/analysis/tree-sitter/queries/python.js.map +1 -0
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.d.ts +13 -0
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.js +174 -0
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.js.map +1 -0
- package/dist/src/analytics/ROIDashboardService.csv.d.ts +11 -0
- package/dist/src/analytics/ROIDashboardService.csv.d.ts.map +1 -0
- package/dist/src/analytics/ROIDashboardService.csv.js +43 -0
- package/dist/src/analytics/ROIDashboardService.csv.js.map +1 -0
- package/dist/src/analytics/ROIDashboardService.d.ts +64 -3
- package/dist/src/analytics/ROIDashboardService.d.ts.map +1 -1
- package/dist/src/analytics/ROIDashboardService.js +116 -45
- package/dist/src/analytics/ROIDashboardService.js.map +1 -1
- package/dist/src/api/client.d.ts +4 -66
- package/dist/src/api/client.d.ts.map +1 -1
- package/dist/src/api/client.events.d.ts.map +1 -1
- package/dist/src/api/client.events.js +10 -1
- package/dist/src/api/client.events.js.map +1 -1
- package/dist/src/api/client.js +40 -66
- package/dist/src/api/client.js.map +1 -1
- package/dist/src/api/client.token-refresh.d.ts +3 -0
- package/dist/src/api/client.token-refresh.d.ts.map +1 -0
- package/dist/src/api/client.token-refresh.js +19 -0
- package/dist/src/api/client.token-refresh.js.map +1 -0
- package/dist/src/api/client.token-refresh.test.d.ts +2 -0
- package/dist/src/api/client.token-refresh.test.d.ts.map +1 -0
- package/dist/src/api/client.token-refresh.test.js +73 -0
- package/dist/src/api/client.token-refresh.test.js.map +1 -0
- package/dist/src/api/client.types.d.ts +2 -0
- package/dist/src/api/client.types.d.ts.map +1 -1
- package/dist/src/api/index.d.ts +1 -1
- package/dist/src/api/index.d.ts.map +1 -1
- package/dist/src/api/index.js +1 -1
- package/dist/src/api/index.js.map +1 -1
- package/dist/src/api/schemas.d.ts +60 -60
- package/dist/src/api/schemas.d.ts.map +1 -1
- package/dist/src/benchmarks/incrementalParseBenchmark.d.ts +18 -0
- package/dist/src/benchmarks/incrementalParseBenchmark.d.ts.map +1 -0
- package/dist/src/benchmarks/incrementalParseBenchmark.js +121 -0
- package/dist/src/benchmarks/incrementalParseBenchmark.js.map +1 -0
- package/dist/src/billing/GDPRComplianceService.test.d.ts +2 -0
- package/dist/src/billing/GDPRComplianceService.test.d.ts.map +1 -0
- package/dist/src/billing/GDPRComplianceService.test.js +405 -0
- package/dist/src/billing/GDPRComplianceService.test.js.map +1 -0
- package/dist/src/config/index.d.ts +4 -0
- package/dist/src/config/index.d.ts.map +1 -1
- package/dist/src/config/index.js +6 -0
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/config/token-credentials.d.ts +13 -0
- package/dist/src/config/token-credentials.d.ts.map +1 -0
- package/dist/src/config/token-credentials.js +126 -0
- package/dist/src/config/token-credentials.js.map +1 -0
- package/dist/src/config/token-credentials.test.d.ts +10 -0
- package/dist/src/config/token-credentials.test.d.ts.map +1 -0
- package/dist/src/config/token-credentials.test.js +91 -0
- package/dist/src/config/token-credentials.test.js.map +1 -0
- package/dist/src/index.d.ts +7 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/indexer/SkillParser.coverage.test.d.ts +10 -0
- package/dist/src/indexer/SkillParser.coverage.test.d.ts.map +1 -0
- package/dist/src/indexer/SkillParser.coverage.test.js +76 -0
- package/dist/src/indexer/SkillParser.coverage.test.js.map +1 -0
- package/dist/src/indexer/SkillParser.test.d.ts +2 -0
- package/dist/src/indexer/SkillParser.test.d.ts.map +1 -0
- package/dist/src/indexer/SkillParser.test.js +375 -0
- package/dist/src/indexer/SkillParser.test.js.map +1 -0
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js +34 -5
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
- package/dist/src/scripts/github-import/blocklist.d.ts +65 -0
- package/dist/src/scripts/github-import/blocklist.d.ts.map +1 -0
- package/dist/src/scripts/github-import/blocklist.js +124 -0
- package/dist/src/scripts/github-import/blocklist.js.map +1 -0
- package/dist/src/scripts/github-import/index.d.ts +1 -0
- package/dist/src/scripts/github-import/index.d.ts.map +1 -1
- package/dist/src/scripts/github-import/index.js +3 -0
- package/dist/src/scripts/github-import/index.js.map +1 -1
- package/dist/src/scripts/github-import/signal-of-intent.d.ts +87 -0
- package/dist/src/scripts/github-import/signal-of-intent.d.ts.map +1 -0
- package/dist/src/scripts/github-import/signal-of-intent.js +213 -0
- package/dist/src/scripts/github-import/signal-of-intent.js.map +1 -0
- package/dist/src/scripts/github-import/types.d.ts +22 -0
- package/dist/src/scripts/github-import/types.d.ts.map +1 -1
- package/dist/src/scripts/github-import/types.js.map +1 -1
- package/dist/src/scripts/import-github-skills.js +73 -3
- package/dist/src/scripts/import-github-skills.js.map +1 -1
- package/dist/src/scripts/skill-scanner/allowlist.d.ts +38 -0
- package/dist/src/scripts/skill-scanner/allowlist.d.ts.map +1 -0
- package/dist/src/scripts/skill-scanner/allowlist.js +178 -0
- package/dist/src/scripts/skill-scanner/allowlist.js.map +1 -0
- package/dist/src/scripts/skill-scanner/scanner.d.ts +10 -2
- package/dist/src/scripts/skill-scanner/scanner.d.ts.map +1 -1
- package/dist/src/scripts/skill-scanner/scanner.js +15 -3
- package/dist/src/scripts/skill-scanner/scanner.js.map +1 -1
- package/dist/src/scripts/skill-scanner/trust-scorer.d.ts +20 -6
- package/dist/src/scripts/skill-scanner/trust-scorer.d.ts.map +1 -1
- package/dist/src/scripts/skill-scanner/trust-scorer.js +28 -9
- package/dist/src/scripts/skill-scanner/trust-scorer.js.map +1 -1
- package/dist/src/scripts/skill-scanner/types.d.ts +50 -0
- package/dist/src/scripts/skill-scanner/types.d.ts.map +1 -1
- package/dist/src/scripts/validation/types.d.ts +14 -24
- package/dist/src/scripts/validation/types.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.helpers.d.ts +18 -0
- package/dist/src/security/scanner/SecurityScanner.helpers.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.helpers.js +54 -6
- package/dist/src/security/scanner/SecurityScanner.helpers.js.map +1 -1
- package/dist/src/security/scanner/patterns.d.ts.map +1 -1
- package/dist/src/security/scanner/patterns.js +45 -5
- package/dist/src/security/scanner/patterns.js.map +1 -1
- package/dist/src/services/skill-config-schema.d.ts +4 -36
- package/dist/src/services/skill-config-schema.d.ts.map +1 -1
- package/dist/src/sources/LocalFilesystemAdapter.d.ts +104 -10
- package/dist/src/sources/LocalFilesystemAdapter.d.ts.map +1 -1
- package/dist/src/sources/LocalFilesystemAdapter.helpers.d.ts +92 -0
- package/dist/src/sources/LocalFilesystemAdapter.helpers.d.ts.map +1 -0
- package/dist/src/sources/LocalFilesystemAdapter.helpers.js +157 -0
- package/dist/src/sources/LocalFilesystemAdapter.helpers.js.map +1 -0
- package/dist/src/sources/LocalFilesystemAdapter.js +218 -159
- package/dist/src/sources/LocalFilesystemAdapter.js.map +1 -1
- package/dist/src/sources/LocalFilesystemAdapter.scan.d.ts +78 -0
- package/dist/src/sources/LocalFilesystemAdapter.scan.d.ts.map +1 -0
- package/dist/src/sources/LocalFilesystemAdapter.scan.js +118 -0
- package/dist/src/sources/LocalFilesystemAdapter.scan.js.map +1 -0
- package/dist/src/sources/index.d.ts +1 -1
- package/dist/src/sources/index.d.ts.map +1 -1
- package/dist/src/sources/index.js.map +1 -1
- package/dist/src/sources/types.d.ts +28 -0
- package/dist/src/sources/types.d.ts.map +1 -1
- package/dist/src/telemetry/tracer-imports.d.ts +13 -0
- package/dist/src/telemetry/tracer-imports.d.ts.map +1 -0
- package/dist/src/telemetry/tracer-imports.js +26 -0
- package/dist/src/telemetry/tracer-imports.js.map +1 -0
- package/dist/src/telemetry/tracer.d.ts.map +1 -1
- package/dist/src/telemetry/tracer.js +18 -21
- package/dist/src/telemetry/tracer.js.map +1 -1
- package/dist/src/utils/rate-limit.d.ts +39 -0
- package/dist/src/utils/rate-limit.d.ts.map +1 -0
- package/dist/src/utils/rate-limit.js +48 -0
- package/dist/src/utils/rate-limit.js.map +1 -0
- package/dist/src/utils/rate-limit.test.d.ts +11 -0
- package/dist/src/utils/rate-limit.test.d.ts.map +1 -0
- package/dist/src/utils/rate-limit.test.js +86 -0
- package/dist/src/utils/rate-limit.test.js.map +1 -0
- package/dist/src/webhooks/WebhookDeadLetterRepository.d.ts +178 -0
- package/dist/src/webhooks/WebhookDeadLetterRepository.d.ts.map +1 -0
- package/dist/src/webhooks/WebhookDeadLetterRepository.js +196 -0
- package/dist/src/webhooks/WebhookDeadLetterRepository.js.map +1 -0
- package/dist/src/webhooks/WebhookQueue.d.ts +1 -0
- package/dist/src/webhooks/WebhookQueue.d.ts.map +1 -1
- package/dist/src/webhooks/WebhookQueue.js +19 -0
- package/dist/src/webhooks/WebhookQueue.js.map +1 -1
- package/dist/src/webhooks/WebhookQueue.types.d.ts +11 -0
- package/dist/src/webhooks/WebhookQueue.types.d.ts.map +1 -1
- package/dist/src/webhooks/index.d.ts +1 -0
- package/dist/src/webhooks/index.d.ts.map +1 -1
- package/dist/src/webhooks/index.js +2 -0
- package/dist/src/webhooks/index.js.map +1 -1
- package/dist/src/webhooks/webhook-schemas.d.ts +117 -1212
- package/dist/src/webhooks/webhook-schemas.d.ts.map +1 -1
- package/dist/tests/ActivationManager.test.d.ts +13 -0
- package/dist/tests/ActivationManager.test.d.ts.map +1 -0
- package/dist/tests/ActivationManager.test.js +218 -0
- package/dist/tests/ActivationManager.test.js.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.coverage.test.d.ts +13 -0
- package/dist/tests/LocalFilesystemAdapter.coverage.test.d.ts.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.coverage.test.js +314 -0
- package/dist/tests/LocalFilesystemAdapter.coverage.test.js.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.security.test.d.ts +18 -0
- package/dist/tests/LocalFilesystemAdapter.security.test.d.ts.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.security.test.js +344 -0
- package/dist/tests/LocalFilesystemAdapter.security.test.js.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.test.d.ts +12 -0
- package/dist/tests/LocalFilesystemAdapter.test.d.ts.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.test.js +301 -0
- package/dist/tests/LocalFilesystemAdapter.test.js.map +1 -0
- package/dist/tests/ROIDashboardService.coverage.test.d.ts +9 -0
- package/dist/tests/ROIDashboardService.coverage.test.d.ts.map +1 -0
- package/dist/tests/ROIDashboardService.coverage.test.js +118 -0
- package/dist/tests/ROIDashboardService.coverage.test.js.map +1 -0
- package/dist/tests/ROIDashboardService.test.js +87 -0
- package/dist/tests/ROIDashboardService.test.js.map +1 -1
- package/dist/tests/ScraperAdapters.gitlab-coverage.test.d.ts +14 -0
- package/dist/tests/ScraperAdapters.gitlab-coverage.test.d.ts.map +1 -0
- package/dist/tests/ScraperAdapters.gitlab-coverage.test.js +169 -0
- package/dist/tests/ScraperAdapters.gitlab-coverage.test.js.map +1 -0
- package/dist/tests/ScraperAdapters.test.d.ts +5 -1
- package/dist/tests/ScraperAdapters.test.d.ts.map +1 -1
- package/dist/tests/ScraperAdapters.test.js +6 -336
- package/dist/tests/ScraperAdapters.test.js.map +1 -1
- package/dist/tests/WebhookDeadLetterRepository.test.d.ts +2 -0
- package/dist/tests/WebhookDeadLetterRepository.test.d.ts.map +1 -0
- package/dist/tests/WebhookDeadLetterRepository.test.js +333 -0
- package/dist/tests/WebhookDeadLetterRepository.test.js.map +1 -0
- package/dist/tests/WebhookHandler.test.js +93 -1
- package/dist/tests/WebhookHandler.test.js.map +1 -1
- package/dist/tests/WebhookQueue.coverage.test.d.ts +19 -0
- package/dist/tests/WebhookQueue.coverage.test.d.ts.map +1 -0
- package/dist/tests/WebhookQueue.coverage.test.js +190 -0
- package/dist/tests/WebhookQueue.coverage.test.js.map +1 -0
- package/dist/tests/api/client.events.test.d.ts +10 -0
- package/dist/tests/api/client.events.test.d.ts.map +1 -0
- package/dist/tests/api/client.events.test.js +85 -0
- package/dist/tests/api/client.events.test.js.map +1 -0
- package/dist/tests/billing/GDPRCompliance.test.d.ts +2 -2
- package/dist/tests/billing/GDPRCompliance.test.js +221 -36
- package/dist/tests/billing/GDPRCompliance.test.js.map +1 -1
- package/dist/tests/github-import/blocklist.test.d.ts +15 -0
- package/dist/tests/github-import/blocklist.test.d.ts.map +1 -0
- package/dist/tests/github-import/blocklist.test.js +182 -0
- package/dist/tests/github-import/blocklist.test.js.map +1 -0
- package/dist/tests/github-import/signal-of-intent.test.d.ts +15 -0
- package/dist/tests/github-import/signal-of-intent.test.d.ts.map +1 -0
- package/dist/tests/github-import/signal-of-intent.test.js +171 -0
- package/dist/tests/github-import/signal-of-intent.test.js.map +1 -0
- package/dist/tests/security/scanner-regression-guard.test.d.ts +12 -0
- package/dist/tests/security/scanner-regression-guard.test.d.ts.map +1 -1
- package/dist/tests/security/scanner-regression-guard.test.js +15 -3
- package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
- package/dist/tests/security/scanner-wave2-fixtures.test.d.ts +12 -0
- package/dist/tests/security/scanner-wave2-fixtures.test.d.ts.map +1 -0
- package/dist/tests/security/scanner-wave2-fixtures.test.js +173 -0
- package/dist/tests/security/scanner-wave2-fixtures.test.js.map +1 -0
- package/dist/tests/security.test.js +1 -0
- package/dist/tests/security.test.js.map +1 -1
- package/dist/tests/skill-scanner/allowlist.test.d.ts +16 -0
- package/dist/tests/skill-scanner/allowlist.test.d.ts.map +1 -0
- package/dist/tests/skill-scanner/allowlist.test.js +332 -0
- package/dist/tests/skill-scanner/allowlist.test.js.map +1 -0
- package/dist/tests/telemetry.test.js +126 -0
- package/dist/tests/telemetry.test.js.map +1 -1
- package/dist/tests/webhooks/WebhookDeadLetterRepository.test.d.ts +10 -0
- package/dist/tests/webhooks/WebhookDeadLetterRepository.test.d.ts.map +1 -0
- package/dist/tests/webhooks/WebhookDeadLetterRepository.test.js +109 -0
- package/dist/tests/webhooks/WebhookDeadLetterRepository.test.js.map +1 -0
- 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
|
|
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
|
|
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
|
|
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
|
|
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(() => {
|