@rigour-labs/core 6.7.10 → 6.8.1-rc.1
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/dist/brief/briefing.d.ts +59 -0
- package/dist/brief/briefing.js +126 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/inference/cloud-provider.js +12 -2
- package/dist/review/backtest-init.d.ts +8 -5
- package/dist/review/backtest-init.js +27 -9
- package/dist/review/backtest-last.js +3 -1
- package/dist/review/reviewer/adapters.d.ts +4 -0
- package/dist/review/reviewer/adapters.js +23 -0
- package/dist/review/reviewer/inputs.d.ts +4 -1
- package/dist/review/reviewer/inputs.js +36 -2
- package/dist/review/reviewer/record.d.ts +5 -0
- package/dist/review/reviewer/record.js +3 -3
- package/dist/review/reviewer/verdict.d.ts +14 -0
- package/dist/review/reviewer/verdict.js +46 -6
- package/dist/review/reviewer.d.ts +2 -0
- package/dist/review/reviewer.js +24 -5
- package/dist/review-learning/repo-rules.d.ts +14 -2
- package/dist/review-learning/repo-rules.js +59 -9
- package/dist/task/thread.d.ts +47 -0
- package/dist/task/thread.js +234 -0
- package/dist/templates/universal-config.js +4 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/index.js +7 -0
- package/package.json +11 -8
- package/dist/context/automatic-index-cache.test.d.ts +0 -1
- package/dist/context/automatic-index-cache.test.js +0 -30
- package/dist/context/automatic-index.test.d.ts +0 -1
- package/dist/context/automatic-index.test.js +0 -45
- package/dist/context/cache-engine.test.d.ts +0 -1
- package/dist/context/cache-engine.test.js +0 -99
- package/dist/context/dependency-graph.test.d.ts +0 -1
- package/dist/context/dependency-graph.test.js +0 -13
- package/dist/context/index-status.test.d.ts +0 -1
- package/dist/context/index-status.test.js +0 -26
- package/dist/context.test.d.ts +0 -1
- package/dist/context.test.js +0 -228
- package/dist/deep/agent-review.test.d.ts +0 -1
- package/dist/deep/agent-review.test.js +0 -53
- package/dist/deep/code-context.test.d.ts +0 -1
- package/dist/deep/code-context.test.js +0 -48
- package/dist/deep/code-pass.test.d.ts +0 -1
- package/dist/deep/code-pass.test.js +0 -112
- package/dist/deep/code-review-prompt.test.d.ts +0 -1
- package/dist/deep/code-review-prompt.test.js +0 -20
- package/dist/deep/code-verifier.test.d.ts +0 -1
- package/dist/deep/code-verifier.test.js +0 -54
- package/dist/deep/diff-tests/calls.test.d.ts +0 -1
- package/dist/deep/diff-tests/calls.test.js +0 -33
- package/dist/deep/diff-tests/run.test.d.ts +0 -1
- package/dist/deep/diff-tests/run.test.js +0 -58
- package/dist/deep/fact-extractor.test.d.ts +0 -1
- package/dist/deep/fact-extractor.test.js +0 -581
- package/dist/deep/parse-findings.test.d.ts +0 -1
- package/dist/deep/parse-findings.test.js +0 -25
- package/dist/deep/pr-review.test.d.ts +0 -1
- package/dist/deep/pr-review.test.js +0 -80
- package/dist/deep/prompts.test.d.ts +0 -1
- package/dist/deep/prompts.test.js +0 -235
- package/dist/deep/reference-pack.test.d.ts +0 -1
- package/dist/deep/reference-pack.test.js +0 -46
- package/dist/deep/related-changes.test.d.ts +0 -1
- package/dist/deep/related-changes.test.js +0 -33
- package/dist/deep/review-context-export.test.d.ts +0 -1
- package/dist/deep/review-context-export.test.js +0 -32
- package/dist/deep/review-tools.test.d.ts +0 -1
- package/dist/deep/review-tools.test.js +0 -39
- package/dist/deep/risk.test.d.ts +0 -1
- package/dist/deep/risk.test.js +0 -101
- package/dist/deep/verifier.test.d.ts +0 -1
- package/dist/deep/verifier.test.js +0 -635
- package/dist/discovery.test.d.ts +0 -1
- package/dist/discovery.test.js +0 -93
- package/dist/environment.test.d.ts +0 -1
- package/dist/environment.test.js +0 -94
- package/dist/firewall/firewall.test.d.ts +0 -1
- package/dist/firewall/firewall.test.js +0 -117
- package/dist/firewall/trust-boundaries.test.d.ts +0 -1
- package/dist/firewall/trust-boundaries.test.js +0 -64
- package/dist/firewall/trusted-control.test.d.ts +0 -1
- package/dist/firewall/trusted-control.test.js +0 -188
- package/dist/gates/agent-team.test.d.ts +0 -1
- package/dist/gates/agent-team.test.js +0 -113
- package/dist/gates/ast.test.d.ts +0 -1
- package/dist/gates/ast.test.js +0 -112
- package/dist/gates/checkpoint.test.d.ts +0 -1
- package/dist/gates/checkpoint.test.js +0 -105
- package/dist/gates/content.test.d.ts +0 -1
- package/dist/gates/content.test.js +0 -73
- package/dist/gates/coverage.test.d.ts +0 -1
- package/dist/gates/coverage.test.js +0 -53
- package/dist/gates/dedupe-failures.test.d.ts +0 -1
- package/dist/gates/dedupe-failures.test.js +0 -12
- package/dist/gates/deep-analysis.test.d.ts +0 -1
- package/dist/gates/deep-analysis.test.js +0 -86
- package/dist/gates/deep-intent.test.d.ts +0 -1
- package/dist/gates/deep-intent.test.js +0 -51
- package/dist/gates/deep-timeout.test.d.ts +0 -1
- package/dist/gates/deep-timeout.test.js +0 -10
- package/dist/gates/deprecated-apis.test.d.ts +0 -1
- package/dist/gates/deprecated-apis.test.js +0 -318
- package/dist/gates/deprecated-dependencies.test.d.ts +0 -1
- package/dist/gates/deprecated-dependencies.test.js +0 -55
- package/dist/gates/frontend-secret-exposure.test.d.ts +0 -1
- package/dist/gates/frontend-secret-exposure.test.js +0 -148
- package/dist/gates/hallucinated-imports/framework-modules-nuxt.test.d.ts +0 -1
- package/dist/gates/hallucinated-imports/framework-modules-nuxt.test.js +0 -27
- package/dist/gates/hallucinated-imports/js-resolver-types.test.d.ts +0 -1
- package/dist/gates/hallucinated-imports/js-resolver-types.test.js +0 -14
- package/dist/gates/hallucinated-imports-sveltekit.test.d.ts +0 -1
- package/dist/gates/hallucinated-imports-sveltekit.test.js +0 -132
- package/dist/gates/hallucinated-imports.test.d.ts +0 -1
- package/dist/gates/hallucinated-imports.test.js +0 -1206
- package/dist/gates/js-style-context.test.d.ts +0 -1
- package/dist/gates/js-style-context.test.js +0 -35
- package/dist/gates/logic-drift.test.d.ts +0 -1
- package/dist/gates/logic-drift.test.js +0 -52
- package/dist/gates/phantom-apis.test.d.ts +0 -1
- package/dist/gates/phantom-apis.test.js +0 -396
- package/dist/gates/promise-safety.test.d.ts +0 -1
- package/dist/gates/promise-safety.test.js +0 -34
- package/dist/gates/runner.test.d.ts +0 -1
- package/dist/gates/runner.test.js +0 -77
- package/dist/gates/scoped-gates.test.d.ts +0 -1
- package/dist/gates/scoped-gates.test.js +0 -52
- package/dist/gates/security-patterns-owasp.test.d.ts +0 -1
- package/dist/gates/security-patterns-owasp.test.js +0 -186
- package/dist/gates/security-patterns.test.d.ts +0 -1
- package/dist/gates/security-patterns.test.js +0 -194
- package/dist/gates/semantic-bugs.test.d.ts +0 -1
- package/dist/gates/semantic-bugs.test.js +0 -76
- package/dist/gates/side-effect-analysis.test.d.ts +0 -1
- package/dist/gates/side-effect-analysis.test.js +0 -162
- package/dist/gates/style-drift.test.d.ts +0 -1
- package/dist/gates/style-drift.test.js +0 -26
- package/dist/gates/test-quality.test.d.ts +0 -1
- package/dist/gates/test-quality.test.js +0 -325
- package/dist/gates/trusted-reviews.test.d.ts +0 -1
- package/dist/gates/trusted-reviews.test.js +0 -16
- package/dist/gates/unindexed-reads/queries.test.d.ts +0 -1
- package/dist/gates/unindexed-reads/queries.test.js +0 -54
- package/dist/gates/unindexed-reads/schema.test.d.ts +0 -1
- package/dist/gates/unindexed-reads/schema.test.js +0 -81
- package/dist/gates/unindexed-reads/unindexed-reads.test.d.ts +0 -1
- package/dist/gates/unindexed-reads/unindexed-reads.test.js +0 -90
- package/dist/hooks/checker.test.d.ts +0 -1
- package/dist/hooks/checker.test.js +0 -159
- package/dist/hooks/dlp-confidence.test.d.ts +0 -1
- package/dist/hooks/dlp-confidence.test.js +0 -51
- package/dist/hooks/dlp-feedback.test.d.ts +0 -1
- package/dist/hooks/dlp-feedback.test.js +0 -131
- package/dist/hooks/input-validator.test.d.ts +0 -1
- package/dist/hooks/input-validator.test.js +0 -329
- package/dist/hooks/templates.test.d.ts +0 -1
- package/dist/hooks/templates.test.js +0 -27
- package/dist/inference/brain-placeholder.test.d.ts +0 -1
- package/dist/inference/brain-placeholder.test.js +0 -28
- package/dist/inference/cloud-provider.test.d.ts +0 -1
- package/dist/inference/cloud-provider.test.js +0 -139
- package/dist/inference/executable.test.d.ts +0 -1
- package/dist/inference/executable.test.js +0 -41
- package/dist/inference/http-download.test.d.ts +0 -1
- package/dist/inference/http-download.test.js +0 -109
- package/dist/inference/llama-engine-checksum.test.d.ts +0 -1
- package/dist/inference/llama-engine-checksum.test.js +0 -27
- package/dist/inference/llama-engine.test.d.ts +0 -1
- package/dist/inference/llama-engine.test.js +0 -51
- package/dist/inference/llama-process.test.d.ts +0 -1
- package/dist/inference/llama-process.test.js +0 -61
- package/dist/inference/local-model.test.d.ts +0 -1
- package/dist/inference/local-model.test.js +0 -23
- package/dist/inference/model-download.test.d.ts +0 -1
- package/dist/inference/model-download.test.js +0 -125
- package/dist/inference/model-manager.test.d.ts +0 -1
- package/dist/inference/model-manager.test.js +0 -24
- package/dist/inference/types.test.d.ts +0 -1
- package/dist/inference/types.test.js +0 -19
- package/dist/memory/recall.test.d.ts +0 -1
- package/dist/memory/recall.test.js +0 -36
- package/dist/pattern-index/indexer.test.d.ts +0 -6
- package/dist/pattern-index/indexer.test.js +0 -197
- package/dist/pattern-index/matcher.test.d.ts +0 -6
- package/dist/pattern-index/matcher.test.js +0 -238
- package/dist/pattern-index/pattern-reuse.test.d.ts +0 -1
- package/dist/pattern-index/pattern-reuse.test.js +0 -76
- package/dist/pattern-index/semantic-runtime.test.d.ts +0 -1
- package/dist/pattern-index/semantic-runtime.test.js +0 -31
- package/dist/pattern-index/staleness.test.d.ts +0 -6
- package/dist/pattern-index/staleness.test.js +0 -211
- package/dist/review/agent-fixes.test.d.ts +0 -1
- package/dist/review/agent-fixes.test.js +0 -32
- package/dist/review/backtest-init.test.d.ts +0 -1
- package/dist/review/backtest-init.test.js +0 -98
- package/dist/review/backtest-judges.test.d.ts +0 -1
- package/dist/review/backtest-judges.test.js +0 -32
- package/dist/review/backtest-last.test.d.ts +0 -1
- package/dist/review/backtest-last.test.js +0 -109
- package/dist/review/backtest.test.d.ts +0 -1
- package/dist/review/backtest.test.js +0 -183
- package/dist/review/baseline.test.d.ts +0 -1
- package/dist/review/baseline.test.js +0 -22
- package/dist/review/branch-checks.test.d.ts +0 -1
- package/dist/review/branch-checks.test.js +0 -48
- package/dist/review/check-outcomes.test.d.ts +0 -1
- package/dist/review/check-outcomes.test.js +0 -56
- package/dist/review/code-patterns.test.d.ts +0 -1
- package/dist/review/code-patterns.test.js +0 -142
- package/dist/review/dead-code.test.d.ts +0 -1
- package/dist/review/dead-code.test.js +0 -154
- package/dist/review/deep-runs.test.d.ts +0 -1
- package/dist/review/deep-runs.test.js +0 -23
- package/dist/review/effectiveness.test.d.ts +0 -1
- package/dist/review/effectiveness.test.js +0 -42
- package/dist/review/fix-scope.test.d.ts +0 -1
- package/dist/review/fix-scope.test.js +0 -104
- package/dist/review/generated-files.test.d.ts +0 -1
- package/dist/review/generated-files.test.js +0 -25
- package/dist/review/migration-order.test.d.ts +0 -1
- package/dist/review/migration-order.test.js +0 -62
- package/dist/review/quiet.test.d.ts +0 -1
- package/dist/review/quiet.test.js +0 -38
- package/dist/review/receipt.test.d.ts +0 -1
- package/dist/review/receipt.test.js +0 -60
- package/dist/review/review-task.test.d.ts +0 -1
- package/dist/review/review-task.test.js +0 -89
- package/dist/review/review.test.d.ts +0 -1
- package/dist/review/review.test.js +0 -203
- package/dist/review/reviewer/adapters.test.d.ts +0 -1
- package/dist/review/reviewer/adapters.test.js +0 -60
- package/dist/review/reviewer/api-judge.test.d.ts +0 -1
- package/dist/review/reviewer/api-judge.test.js +0 -108
- package/dist/review/reviewer/background.test.d.ts +0 -1
- package/dist/review/reviewer/background.test.js +0 -118
- package/dist/review/reviewer/context.test.d.ts +0 -1
- package/dist/review/reviewer/context.test.js +0 -45
- package/dist/review/reviewer/exec.test.d.ts +0 -1
- package/dist/review/reviewer/exec.test.js +0 -59
- package/dist/review/reviewer/inputs.test.d.ts +0 -1
- package/dist/review/reviewer/inputs.test.js +0 -26
- package/dist/review/reviewer/panel.test.d.ts +0 -1
- package/dist/review/reviewer/panel.test.js +0 -110
- package/dist/review/reviewer/record.test.d.ts +0 -1
- package/dist/review/reviewer/record.test.js +0 -32
- package/dist/review/reviewer/rule-writer.test.d.ts +0 -1
- package/dist/review/reviewer/rule-writer.test.js +0 -52
- package/dist/review/reviewer/settings.test.d.ts +0 -1
- package/dist/review/reviewer/settings.test.js +0 -57
- package/dist/review/reviewer/usage.test.d.ts +0 -1
- package/dist/review/reviewer/usage.test.js +0 -14
- package/dist/review/reviewer.test.d.ts +0 -1
- package/dist/review/reviewer.test.js +0 -705
- package/dist/review/stories.test.d.ts +0 -1
- package/dist/review/stories.test.js +0 -58
- package/dist/review/toolchain.test.d.ts +0 -1
- package/dist/review/toolchain.test.js +0 -108
- package/dist/review/typed/redundancy.test.d.ts +0 -1
- package/dist/review/typed/redundancy.test.js +0 -315
- package/dist/review/typed/schema-nullability.test.d.ts +0 -1
- package/dist/review/typed/schema-nullability.test.js +0 -63
- package/dist/review-learning/human-edits.test.d.ts +0 -1
- package/dist/review-learning/human-edits.test.js +0 -47
- package/dist/review-learning/repo-rules.test.d.ts +0 -1
- package/dist/review-learning/repo-rules.test.js +0 -55
- package/dist/review-learning/review-learning.test.d.ts +0 -1
- package/dist/review-learning/review-learning.test.js +0 -249
- package/dist/safety.test.d.ts +0 -1
- package/dist/safety.test.js +0 -42
- package/dist/semantic/benchmark.test.d.ts +0 -1
- package/dist/semantic/benchmark.test.js +0 -22
- package/dist/semantic/intent/intent.test.d.ts +0 -1
- package/dist/semantic/intent/intent.test.js +0 -46
- package/dist/semantic/learn/learn.test.d.ts +0 -1
- package/dist/semantic/learn/learn.test.js +0 -67
- package/dist/semantic/origins.test.d.ts +0 -1
- package/dist/semantic/origins.test.js +0 -77
- package/dist/semantic/project-facts.test.d.ts +0 -1
- package/dist/semantic/project-facts.test.js +0 -45
- package/dist/semantic/sites/call-sites.test.d.ts +0 -1
- package/dist/semantic/sites/call-sites.test.js +0 -46
- package/dist/services/adaptive-thresholds.test.d.ts +0 -1
- package/dist/services/adaptive-thresholds.test.js +0 -53
- package/dist/services/agent-history.test.d.ts +0 -1
- package/dist/services/agent-history.test.js +0 -69
- package/dist/services/context-scope-summary.test.d.ts +0 -1
- package/dist/services/context-scope-summary.test.js +0 -17
- package/dist/services/context-telemetry-service.test.d.ts +0 -1
- package/dist/services/context-telemetry-service.test.js +0 -182
- package/dist/services/cursor-usage-sync.test.d.ts +0 -1
- package/dist/services/cursor-usage-sync.test.js +0 -173
- package/dist/services/engineering-knowledge-graph.test.d.ts +0 -1
- package/dist/services/engineering-knowledge-graph.test.js +0 -76
- package/dist/services/model-pricing.test.d.ts +0 -1
- package/dist/services/model-pricing.test.js +0 -44
- package/dist/services/observed-savings.test.d.ts +0 -1
- package/dist/services/observed-savings.test.js +0 -37
- package/dist/services/score-history.test.d.ts +0 -1
- package/dist/services/score-history.test.js +0 -61
- package/dist/smoke.test.d.ts +0 -1
- package/dist/smoke.test.js +0 -17
- package/dist/storage/cache-cleanup.test.d.ts +0 -1
- package/dist/storage/cache-cleanup.test.js +0 -54
- package/dist/storage/context-telemetry.test.d.ts +0 -1
- package/dist/storage/context-telemetry.test.js +0 -80
- package/dist/storage/db.test.d.ts +0 -1
- package/dist/storage/db.test.js +0 -46
- package/dist/storage/fix-lessons.test.d.ts +0 -1
- package/dist/storage/fix-lessons.test.js +0 -61
- package/dist/storage/lessons.test.d.ts +0 -1
- package/dist/storage/lessons.test.js +0 -81
- package/dist/storage/local-encryption.test.d.ts +0 -1
- package/dist/storage/local-encryption.test.js +0 -34
- package/dist/storage/local-memory.test.d.ts +0 -1
- package/dist/storage/local-memory.test.js +0 -55
- package/dist/storage/share-memory.test.d.ts +0 -1
- package/dist/storage/share-memory.test.js +0 -34
- package/dist/storage/team-diagnostics.test.d.ts +0 -1
- package/dist/storage/team-diagnostics.test.js +0 -51
- package/dist/storage/team-scope.test.d.ts +0 -1
- package/dist/storage/team-scope.test.js +0 -22
- package/dist/storage/team-store.test.d.ts +0 -1
- package/dist/storage/team-store.test.js +0 -11
- package/dist/storage/team-sync-scope.test.d.ts +0 -1
- package/dist/storage/team-sync-scope.test.js +0 -100
- package/dist/storage/team-vector-store.test.d.ts +0 -1
- package/dist/storage/team-vector-store.test.js +0 -56
- package/dist/storage/telemetry-scope.test.d.ts +0 -1
- package/dist/storage/telemetry-scope.test.js +0 -38
- package/dist/telemetry/telemetry.test.d.ts +0 -1
- package/dist/telemetry/telemetry.test.js +0 -64
- package/dist/types/index.test.d.ts +0 -1
- package/dist/types/index.test.js +0 -33
- package/dist/utils/command-line.test.d.ts +0 -1
- package/dist/utils/command-line.test.js +0 -8
- package/dist/utils/diff-removed.test.d.ts +0 -1
- package/dist/utils/diff-removed.test.js +0 -29
- package/dist/utils/diff.test.d.ts +0 -1
- package/dist/utils/diff.test.js +0 -38
- package/dist/utils/glob-paths.test.d.ts +0 -1
- package/dist/utils/glob-paths.test.js +0 -40
- package/dist/utils/profile.test.d.ts +0 -1
- package/dist/utils/profile.test.js +0 -68
- package/dist/utils/scanner.test.d.ts +0 -1
- package/dist/utils/scanner.test.js +0 -48
- package/dist/utils/scope.test.d.ts +0 -1
- package/dist/utils/scope.test.js +0 -38
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pattern Indexer Tests
|
|
3
|
-
*
|
|
4
|
-
* Comprehensive tests for the pattern indexer.
|
|
5
|
-
*/
|
|
6
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
7
|
-
import * as fs from 'fs/promises';
|
|
8
|
-
import * as path from 'path';
|
|
9
|
-
import * as os from 'os';
|
|
10
|
-
import { PatternIndexer, savePatternIndex, loadPatternIndex } from './indexer.js';
|
|
11
|
-
describe('PatternIndexer', () => {
|
|
12
|
-
let testDir;
|
|
13
|
-
beforeEach(async () => {
|
|
14
|
-
// Create a temporary test directory
|
|
15
|
-
testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'rigour-test-'));
|
|
16
|
-
await fs.mkdir(path.join(testDir, 'src'), { recursive: true });
|
|
17
|
-
});
|
|
18
|
-
afterEach(async () => {
|
|
19
|
-
// Clean up
|
|
20
|
-
await fs.rm(testDir, { recursive: true, force: true });
|
|
21
|
-
});
|
|
22
|
-
describe('buildIndex', () => {
|
|
23
|
-
it('should index function declarations', async () => {
|
|
24
|
-
await fs.writeFile(path.join(testDir, 'src', 'utils.ts'), `
|
|
25
|
-
/**
|
|
26
|
-
* Format a date to a readable string.
|
|
27
|
-
*/
|
|
28
|
-
export function formatDate(date: Date): string {
|
|
29
|
-
return date.toISOString();
|
|
30
|
-
}
|
|
31
|
-
`);
|
|
32
|
-
const indexer = new PatternIndexer(testDir);
|
|
33
|
-
const index = await indexer.buildIndex();
|
|
34
|
-
expect(index.patterns).toHaveLength(1);
|
|
35
|
-
expect(index.patterns[0].name).toBe('formatDate');
|
|
36
|
-
expect(index.patterns[0].type).toBe('function');
|
|
37
|
-
expect(index.patterns[0].exported).toBe(true);
|
|
38
|
-
expect(index.patterns[0].signature).toContain('date: Date');
|
|
39
|
-
expect(index.patterns[0].description).toContain('Format a date');
|
|
40
|
-
});
|
|
41
|
-
it('should index source files nested in monorepo packages', async () => {
|
|
42
|
-
const packageSrc = path.join(testDir, 'packages', 'api', 'src');
|
|
43
|
-
await fs.mkdir(packageSrc, { recursive: true });
|
|
44
|
-
await fs.writeFile(path.join(packageSrc, 'handler.ts'), 'export function handleRequest() {}');
|
|
45
|
-
const indexer = new PatternIndexer(testDir);
|
|
46
|
-
const index = await indexer.buildIndex();
|
|
47
|
-
expect(index.files.map(file => file.path)).toContain(path.join('packages', 'api', 'src', 'handler.ts'));
|
|
48
|
-
expect(index.patterns.some(pattern => pattern.name === 'handleRequest')).toBe(true);
|
|
49
|
-
});
|
|
50
|
-
it('should index arrow functions', async () => {
|
|
51
|
-
await fs.writeFile(path.join(testDir, 'src', 'helpers.ts'), `
|
|
52
|
-
export const slugify = (text: string): string => {
|
|
53
|
-
return text.toLowerCase().replace(/\\s+/g, '-');
|
|
54
|
-
};
|
|
55
|
-
`);
|
|
56
|
-
const indexer = new PatternIndexer(testDir);
|
|
57
|
-
const index = await indexer.buildIndex();
|
|
58
|
-
expect(index.patterns).toHaveLength(1);
|
|
59
|
-
expect(index.patterns[0].name).toBe('slugify');
|
|
60
|
-
expect(index.patterns[0].type).toBe('function');
|
|
61
|
-
});
|
|
62
|
-
it('should detect React hooks', async () => {
|
|
63
|
-
await fs.writeFile(path.join(testDir, 'src', 'hooks.ts'), `
|
|
64
|
-
export const useAuth = () => {
|
|
65
|
-
return { user: null, login: () => {} };
|
|
66
|
-
};
|
|
67
|
-
`);
|
|
68
|
-
const indexer = new PatternIndexer(testDir);
|
|
69
|
-
const index = await indexer.buildIndex();
|
|
70
|
-
expect(index.patterns).toHaveLength(1);
|
|
71
|
-
expect(index.patterns[0].name).toBe('useAuth');
|
|
72
|
-
expect(index.patterns[0].type).toBe('hook');
|
|
73
|
-
});
|
|
74
|
-
it('should index classes', async () => {
|
|
75
|
-
await fs.writeFile(path.join(testDir, 'src', 'services.ts'), `
|
|
76
|
-
export class UserService {
|
|
77
|
-
getUser(id: string) {
|
|
78
|
-
return { id };
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
`);
|
|
82
|
-
const indexer = new PatternIndexer(testDir);
|
|
83
|
-
const index = await indexer.buildIndex();
|
|
84
|
-
expect(index.patterns.some(p => p.name === 'UserService')).toBe(true);
|
|
85
|
-
expect(index.patterns.find(p => p.name === 'UserService')?.type).toBe('class');
|
|
86
|
-
});
|
|
87
|
-
it('should detect error classes', async () => {
|
|
88
|
-
await fs.writeFile(path.join(testDir, 'src', 'errors.ts'), `
|
|
89
|
-
export class ValidationError extends Error {
|
|
90
|
-
constructor(message: string) {
|
|
91
|
-
super(message);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
`);
|
|
95
|
-
const indexer = new PatternIndexer(testDir);
|
|
96
|
-
const index = await indexer.buildIndex();
|
|
97
|
-
expect(index.patterns[0].type).toBe('error');
|
|
98
|
-
});
|
|
99
|
-
it('should index interfaces', async () => {
|
|
100
|
-
await fs.writeFile(path.join(testDir, 'src', 'types.ts'), `
|
|
101
|
-
export interface User {
|
|
102
|
-
id: string;
|
|
103
|
-
name: string;
|
|
104
|
-
email: string;
|
|
105
|
-
}
|
|
106
|
-
`);
|
|
107
|
-
const indexer = new PatternIndexer(testDir);
|
|
108
|
-
const index = await indexer.buildIndex();
|
|
109
|
-
expect(index.patterns).toHaveLength(1);
|
|
110
|
-
expect(index.patterns[0].name).toBe('User');
|
|
111
|
-
expect(index.patterns[0].type).toBe('interface');
|
|
112
|
-
});
|
|
113
|
-
it('should index type aliases', async () => {
|
|
114
|
-
await fs.writeFile(path.join(testDir, 'src', 'types.ts'), `
|
|
115
|
-
export type UserId = string;
|
|
116
|
-
`);
|
|
117
|
-
const indexer = new PatternIndexer(testDir);
|
|
118
|
-
const index = await indexer.buildIndex();
|
|
119
|
-
expect(index.patterns).toHaveLength(1);
|
|
120
|
-
expect(index.patterns[0].type).toBe('type');
|
|
121
|
-
});
|
|
122
|
-
it('should index enums', async () => {
|
|
123
|
-
await fs.writeFile(path.join(testDir, 'src', 'constants.ts'), `
|
|
124
|
-
export enum Status {
|
|
125
|
-
Active = 'active',
|
|
126
|
-
Inactive = 'inactive'
|
|
127
|
-
}
|
|
128
|
-
`);
|
|
129
|
-
const indexer = new PatternIndexer(testDir);
|
|
130
|
-
const index = await indexer.buildIndex();
|
|
131
|
-
expect(index.patterns).toHaveLength(1);
|
|
132
|
-
expect(index.patterns[0].name).toBe('Status');
|
|
133
|
-
expect(index.patterns[0].type).toBe('enum');
|
|
134
|
-
});
|
|
135
|
-
it('should index constants (all caps)', async () => {
|
|
136
|
-
await fs.writeFile(path.join(testDir, 'src', 'config.ts'), `
|
|
137
|
-
export const API_URL = 'https://api.example.com';
|
|
138
|
-
export const MAX_RETRIES = 3;
|
|
139
|
-
`);
|
|
140
|
-
const indexer = new PatternIndexer(testDir);
|
|
141
|
-
const index = await indexer.buildIndex();
|
|
142
|
-
const constants = index.patterns.filter(p => p.type === 'constant');
|
|
143
|
-
expect(constants.length).toBeGreaterThanOrEqual(1);
|
|
144
|
-
expect(constants.some(c => c.name === 'API_URL')).toBe(true);
|
|
145
|
-
});
|
|
146
|
-
it('should track index statistics', async () => {
|
|
147
|
-
await fs.writeFile(path.join(testDir, 'src', 'utils.ts'), `
|
|
148
|
-
export function foo() {}
|
|
149
|
-
export function bar() {}
|
|
150
|
-
export interface Baz {}
|
|
151
|
-
`);
|
|
152
|
-
const indexer = new PatternIndexer(testDir);
|
|
153
|
-
const index = await indexer.buildIndex();
|
|
154
|
-
expect(index.stats.totalPatterns).toBe(3);
|
|
155
|
-
expect(index.stats.totalFiles).toBe(1);
|
|
156
|
-
expect(index.stats.indexDurationMs).toBeGreaterThan(0);
|
|
157
|
-
});
|
|
158
|
-
it('should exclude test files by default', async () => {
|
|
159
|
-
await fs.writeFile(path.join(testDir, 'src', 'utils.ts'), 'export function main() {}');
|
|
160
|
-
await fs.writeFile(path.join(testDir, 'src', 'utils.test.ts'), 'export function testMain() {}');
|
|
161
|
-
const indexer = new PatternIndexer(testDir);
|
|
162
|
-
const index = await indexer.buildIndex();
|
|
163
|
-
expect(index.patterns.every(p => !p.file.includes('.test.'))).toBe(true);
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
describe('updateIndex (incremental)', () => {
|
|
167
|
-
it('should only reindex changed files', async () => {
|
|
168
|
-
// Initial index
|
|
169
|
-
await fs.writeFile(path.join(testDir, 'src', 'a.ts'), 'export function funcA() {}');
|
|
170
|
-
await fs.writeFile(path.join(testDir, 'src', 'b.ts'), 'export function funcB() {}');
|
|
171
|
-
const indexer = new PatternIndexer(testDir);
|
|
172
|
-
const initialIndex = await indexer.buildIndex();
|
|
173
|
-
expect(initialIndex.patterns).toHaveLength(2);
|
|
174
|
-
// Modify only one file
|
|
175
|
-
await fs.writeFile(path.join(testDir, 'src', 'a.ts'), 'export function funcA() {} export function funcA2() {}');
|
|
176
|
-
const updatedIndex = await indexer.updateIndex(initialIndex);
|
|
177
|
-
expect(updatedIndex.patterns).toHaveLength(3);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
describe('savePatternIndex / loadPatternIndex', () => {
|
|
181
|
-
it('should save and load index correctly', async () => {
|
|
182
|
-
await fs.writeFile(path.join(testDir, 'src', 'utils.ts'), 'export function myFunc() {}');
|
|
183
|
-
const indexer = new PatternIndexer(testDir);
|
|
184
|
-
const index = await indexer.buildIndex();
|
|
185
|
-
const indexPath = path.join(testDir, '.rigour', 'patterns.json');
|
|
186
|
-
await savePatternIndex(index, indexPath);
|
|
187
|
-
const loaded = await loadPatternIndex(indexPath);
|
|
188
|
-
expect(loaded).not.toBeNull();
|
|
189
|
-
expect(loaded.patterns).toHaveLength(index.patterns.length);
|
|
190
|
-
expect(loaded.version).toBe(index.version);
|
|
191
|
-
});
|
|
192
|
-
it('should return null for non-existent index', async () => {
|
|
193
|
-
const loaded = await loadPatternIndex('/non/existent/path.json');
|
|
194
|
-
expect(loaded).toBeNull();
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
});
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pattern Matcher Tests
|
|
3
|
-
*
|
|
4
|
-
* Comprehensive tests for the pattern matcher.
|
|
5
|
-
*/
|
|
6
|
-
import { describe, it, expect } from 'vitest';
|
|
7
|
-
import { PatternMatcher, checkPatternDuplicate } from './matcher.js';
|
|
8
|
-
// Helper to create a mock pattern
|
|
9
|
-
function createPattern(overrides = {}) {
|
|
10
|
-
return {
|
|
11
|
-
id: 'test-id',
|
|
12
|
-
type: 'function',
|
|
13
|
-
name: 'testFunction',
|
|
14
|
-
file: 'src/utils.ts',
|
|
15
|
-
line: 1,
|
|
16
|
-
endLine: 5,
|
|
17
|
-
signature: '(input: string): string',
|
|
18
|
-
description: 'A test function',
|
|
19
|
-
keywords: ['test', 'function'],
|
|
20
|
-
hash: 'abc123',
|
|
21
|
-
exported: true,
|
|
22
|
-
usageCount: 0,
|
|
23
|
-
indexedAt: new Date().toISOString(),
|
|
24
|
-
...overrides
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
// Helper to create a mock index
|
|
28
|
-
function createIndex(patterns) {
|
|
29
|
-
return {
|
|
30
|
-
version: '1.0.0',
|
|
31
|
-
lastUpdated: new Date().toISOString(),
|
|
32
|
-
rootDir: '/test',
|
|
33
|
-
patterns,
|
|
34
|
-
stats: {
|
|
35
|
-
totalPatterns: patterns.length,
|
|
36
|
-
totalFiles: 1,
|
|
37
|
-
byType: { function: patterns.length },
|
|
38
|
-
indexDurationMs: 100
|
|
39
|
-
},
|
|
40
|
-
files: []
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
describe('PatternMatcher', () => {
|
|
44
|
-
describe('exact name match', () => {
|
|
45
|
-
it('should find exact name matches with 100% confidence', async () => {
|
|
46
|
-
const pattern = createPattern({ name: 'formatDate' });
|
|
47
|
-
const index = createIndex([pattern]);
|
|
48
|
-
const matcher = new PatternMatcher(index, { useEmbeddings: false });
|
|
49
|
-
const result = await matcher.match({ name: 'formatDate' });
|
|
50
|
-
expect(result.status).toBe('FOUND_SIMILAR');
|
|
51
|
-
expect(result.matches).toHaveLength(1);
|
|
52
|
-
expect(result.matches[0].matchType).toBe('exact');
|
|
53
|
-
expect(result.matches[0].confidence).toBe(100);
|
|
54
|
-
});
|
|
55
|
-
it('should be case-insensitive for exact matches', async () => {
|
|
56
|
-
const pattern = createPattern({ name: 'formatDate' });
|
|
57
|
-
const index = createIndex([pattern]);
|
|
58
|
-
const matcher = new PatternMatcher(index, { useEmbeddings: false });
|
|
59
|
-
const result = await matcher.match({ name: 'FormatDate' });
|
|
60
|
-
expect(result.matches).toHaveLength(1);
|
|
61
|
-
expect(result.matches[0].matchType).toBe('exact');
|
|
62
|
-
});
|
|
63
|
-
it('should set action to BLOCK for exact matches', async () => {
|
|
64
|
-
const pattern = createPattern({ name: 'formatDate' });
|
|
65
|
-
const index = createIndex([pattern]);
|
|
66
|
-
const matcher = new PatternMatcher(index, { useEmbeddings: false });
|
|
67
|
-
const result = await matcher.match({ name: 'formatDate' });
|
|
68
|
-
expect(result.action).toBe('BLOCK');
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
describe('fuzzy name match', () => {
|
|
72
|
-
it('should find fuzzy matches for similar names', async () => {
|
|
73
|
-
const pattern = createPattern({ name: 'formatDate' });
|
|
74
|
-
const index = createIndex([pattern]);
|
|
75
|
-
const matcher = new PatternMatcher(index, { useEmbeddings: false });
|
|
76
|
-
// 'formatDate' vs 'formatDates' have extremely high similarity
|
|
77
|
-
const result = await matcher.match({ name: 'formatDates' });
|
|
78
|
-
expect(result.status).toBe('FOUND_SIMILAR');
|
|
79
|
-
expect(result.matches.some(m => m.matchType === 'fuzzy')).toBe(true);
|
|
80
|
-
});
|
|
81
|
-
it('should find matches for renamed patterns', async () => {
|
|
82
|
-
const pattern = createPattern({
|
|
83
|
-
name: 'formatDate',
|
|
84
|
-
keywords: ['format', 'date']
|
|
85
|
-
});
|
|
86
|
-
const index = createIndex([pattern]);
|
|
87
|
-
const matcher = new PatternMatcher(index, { useEmbeddings: false });
|
|
88
|
-
const result = await matcher.match({ name: 'formatDateString' });
|
|
89
|
-
expect(result.matches.length).toBeGreaterThan(0);
|
|
90
|
-
});
|
|
91
|
-
it('should not match completely unrelated names', async () => {
|
|
92
|
-
const pattern = createPattern({ name: 'formatDate' });
|
|
93
|
-
const index = createIndex([pattern]);
|
|
94
|
-
const matcher = new PatternMatcher(index, { useEmbeddings: false });
|
|
95
|
-
const result = await matcher.match({ name: 'sendEmail' });
|
|
96
|
-
expect(result.status).toBe('NO_MATCH');
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
describe('signature match', () => {
|
|
100
|
-
it('should find matches with identical signatures', async () => {
|
|
101
|
-
const pattern = createPattern({
|
|
102
|
-
name: 'formatDate',
|
|
103
|
-
signature: '(date: Date): string'
|
|
104
|
-
});
|
|
105
|
-
const index = createIndex([pattern]);
|
|
106
|
-
const matcher = new PatternMatcher(index, { useEmbeddings: false });
|
|
107
|
-
const result = await matcher.match({
|
|
108
|
-
name: 'myDateFormatter',
|
|
109
|
-
signature: '(date: Date): string'
|
|
110
|
-
});
|
|
111
|
-
expect(result.matches.some(m => m.matchType === 'signature')).toBe(true);
|
|
112
|
-
});
|
|
113
|
-
it('should match similar parameter patterns', async () => {
|
|
114
|
-
const pattern = createPattern({
|
|
115
|
-
name: 'formatDate',
|
|
116
|
-
signature: '(date: Date, format: string): string'
|
|
117
|
-
});
|
|
118
|
-
const index = createIndex([pattern]);
|
|
119
|
-
const matcher = new PatternMatcher(index, { useEmbeddings: false });
|
|
120
|
-
const result = await matcher.match({
|
|
121
|
-
signature: '(input: Date, pattern: string): string'
|
|
122
|
-
});
|
|
123
|
-
expect(result.matches.length).toBeGreaterThan(0);
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
describe('keyword match', () => {
|
|
127
|
-
it('should find matches based on keywords', async () => {
|
|
128
|
-
const pattern = createPattern({
|
|
129
|
-
name: 'formatDate',
|
|
130
|
-
keywords: ['format', 'date', 'time']
|
|
131
|
-
});
|
|
132
|
-
const index = createIndex([pattern]);
|
|
133
|
-
const matcher = new PatternMatcher(index, { useEmbeddings: false });
|
|
134
|
-
const result = await matcher.match({
|
|
135
|
-
keywords: ['date', 'format']
|
|
136
|
-
});
|
|
137
|
-
expect(result.matches.some(m => m.matchType === 'semantic')).toBe(true);
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
describe('overrides', () => {
|
|
141
|
-
it('should allow overridden patterns', async () => {
|
|
142
|
-
const pattern = createPattern({ name: 'formatDate' });
|
|
143
|
-
const index = createIndex([pattern]);
|
|
144
|
-
const matcher = new PatternMatcher(index, { useEmbeddings: false }, [
|
|
145
|
-
{
|
|
146
|
-
pattern: 'formatDate',
|
|
147
|
-
reason: 'Refactoring in progress',
|
|
148
|
-
createdAt: new Date().toISOString()
|
|
149
|
-
}
|
|
150
|
-
]);
|
|
151
|
-
const result = await matcher.match({ name: 'formatDate' });
|
|
152
|
-
expect(result.status).toBe('OVERRIDE_ALLOWED');
|
|
153
|
-
expect(result.action).toBe('ALLOW');
|
|
154
|
-
});
|
|
155
|
-
it('should support glob overrides', async () => {
|
|
156
|
-
const pattern = createPattern({ name: 'formatDate' });
|
|
157
|
-
const index = createIndex([pattern]);
|
|
158
|
-
const matcher = new PatternMatcher(index, { useEmbeddings: false }, [
|
|
159
|
-
{
|
|
160
|
-
pattern: 'format*',
|
|
161
|
-
reason: 'All format functions are exempt',
|
|
162
|
-
createdAt: new Date().toISOString()
|
|
163
|
-
}
|
|
164
|
-
]);
|
|
165
|
-
const result = await matcher.match({ name: 'formatDate' });
|
|
166
|
-
expect(result.status).toBe('OVERRIDE_ALLOWED');
|
|
167
|
-
});
|
|
168
|
-
it('should ignore expired overrides', async () => {
|
|
169
|
-
const pattern = createPattern({ name: 'formatDate' });
|
|
170
|
-
const index = createIndex([pattern]);
|
|
171
|
-
const matcher = new PatternMatcher(index, { useEmbeddings: false }, [
|
|
172
|
-
{
|
|
173
|
-
pattern: 'formatDate',
|
|
174
|
-
reason: 'Expired override',
|
|
175
|
-
createdAt: '2020-01-01T00:00:00Z',
|
|
176
|
-
expiresAt: '2020-01-02T00:00:00Z' // Expired
|
|
177
|
-
}
|
|
178
|
-
]);
|
|
179
|
-
const result = await matcher.match({ name: 'formatDate' });
|
|
180
|
-
expect(result.status).toBe('FOUND_SIMILAR');
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
describe('configuration', () => {
|
|
184
|
-
it('should respect minConfidence setting', async () => {
|
|
185
|
-
const pattern = createPattern({ name: 'formatDate' });
|
|
186
|
-
const index = createIndex([pattern]);
|
|
187
|
-
const matcher = new PatternMatcher(index, { minConfidence: 95, useEmbeddings: false });
|
|
188
|
-
// Fuzzy match won't meet 95% threshold
|
|
189
|
-
const result = await matcher.match({ name: 'dateFormat' });
|
|
190
|
-
// Should only include exact matches at 95%+ threshold
|
|
191
|
-
expect(result.matches.every(m => m.confidence >= 95)).toBe(true);
|
|
192
|
-
});
|
|
193
|
-
it('should respect maxMatches setting', async () => {
|
|
194
|
-
const patterns = [
|
|
195
|
-
createPattern({ id: '1', name: 'formatDate1' }),
|
|
196
|
-
createPattern({ id: '2', name: 'formatDate2' }),
|
|
197
|
-
createPattern({ id: '3', name: 'formatDate3' }),
|
|
198
|
-
createPattern({ id: '4', name: 'formatDate4' }),
|
|
199
|
-
];
|
|
200
|
-
const index = createIndex(patterns);
|
|
201
|
-
const matcher = new PatternMatcher(index, { maxMatches: 2, useEmbeddings: false });
|
|
202
|
-
const result = await matcher.match({ name: 'formatDate' });
|
|
203
|
-
expect(result.matches.length).toBeLessThanOrEqual(2);
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
describe('suggestions', () => {
|
|
207
|
-
it('should suggest importing exported patterns', async () => {
|
|
208
|
-
const pattern = createPattern({
|
|
209
|
-
name: 'formatDate',
|
|
210
|
-
file: 'src/utils/date.ts',
|
|
211
|
-
exported: true
|
|
212
|
-
});
|
|
213
|
-
const index = createIndex([pattern]);
|
|
214
|
-
const matcher = new PatternMatcher(index, { useEmbeddings: false });
|
|
215
|
-
const result = await matcher.match({ name: 'formatDate' });
|
|
216
|
-
expect(result.suggestion).toContain('Import');
|
|
217
|
-
expect(result.suggestion).toContain('src/utils/date.ts');
|
|
218
|
-
});
|
|
219
|
-
it('should suggest extracting non-exported patterns', async () => {
|
|
220
|
-
const pattern = createPattern({
|
|
221
|
-
name: 'formatDate',
|
|
222
|
-
exported: false
|
|
223
|
-
});
|
|
224
|
-
const index = createIndex([pattern]);
|
|
225
|
-
const matcher = new PatternMatcher(index, { useEmbeddings: false });
|
|
226
|
-
const result = await matcher.match({ name: 'formatDate' });
|
|
227
|
-
expect(result.suggestion).toContain('similar pattern');
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
describe('checkPatternDuplicate', () => {
|
|
232
|
-
it('should be a quick helper for duplicate checking', async () => {
|
|
233
|
-
const pattern = createPattern({ name: 'myUtil' });
|
|
234
|
-
const index = createIndex([pattern]);
|
|
235
|
-
const result = await checkPatternDuplicate(index, 'myUtil');
|
|
236
|
-
expect(result.status).toBe('FOUND_SIMILAR');
|
|
237
|
-
});
|
|
238
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
-
/** Topic vectors: one dimension per word, so similarity between texts is predictable. */
|
|
6
|
-
const TOPICS = ['retry', 'backoff', 'request', 'date', 'format', 'chart'];
|
|
7
|
-
const topicVector = (text) => TOPICS.map(t => (text.toLowerCase().includes(t) ? 1 : 0));
|
|
8
|
-
vi.mock('./embeddings.js', async (importOriginal) => {
|
|
9
|
-
const real = await importOriginal();
|
|
10
|
-
return {
|
|
11
|
-
...real,
|
|
12
|
-
generateEmbedding: async (text) => topicVector(text),
|
|
13
|
-
embedPattern: async (p) => topicVector(real.patternEmbeddingText(p)),
|
|
14
|
-
};
|
|
15
|
-
});
|
|
16
|
-
const { patternEmbeddingText, EMBEDDING_TEXT_VERSION } = await import('./embeddings.js');
|
|
17
|
-
const { PatternMatcher, SEMANTIC_MATCH_FLOOR } = await import('./matcher.js');
|
|
18
|
-
const { PatternIndexer } = await import('./indexer.js');
|
|
19
|
-
const { assessPattern } = await import('./assess.js');
|
|
20
|
-
const pattern = (name, extra = {}) => ({
|
|
21
|
-
id: name, type: 'function', name, file: 'src/util.ts', line: 1, endLine: 3, signature: '', description: '',
|
|
22
|
-
keywords: [], hash: name, exported: true, usageCount: 0, indexedAt: '2026-10-02T00:00:00Z', ...extra,
|
|
23
|
-
});
|
|
24
|
-
const index = (patterns, version) => ({
|
|
25
|
-
version: '1.0.0', lastUpdated: '', rootDir: '/x', patterns, files: [],
|
|
26
|
-
stats: { totalPatterns: patterns.length, totalFiles: 1, byType: {}, indexDurationMs: 0 },
|
|
27
|
-
...(version ? { embeddingTextVersion: version } : {}),
|
|
28
|
-
});
|
|
29
|
-
describe('pattern embedding text', () => {
|
|
30
|
-
it('reads a name as words, which is what lets an intent find it', () => {
|
|
31
|
-
expect(patternEmbeddingText({ name: 'retryWithBackoff', type: 'function' })).toBe('retry with backoff function');
|
|
32
|
-
expect(patternEmbeddingText({ name: 'parse_csv-rows', type: 'function', description: 'Rows from a file' })).toBe('parse csv rows function Rows from a file');
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
describe('matching by meaning', () => {
|
|
36
|
-
it(`accepts a match by meaning at ${SEMANTIC_MATCH_FLOOR}, below the bar name matching keeps`, async () => {
|
|
37
|
-
// The query shares 1 of 3 topics with the pattern: cosine 0.58, under the old 0.60.
|
|
38
|
-
const retry = pattern('retryWithBackoff', { embedding: topicVector('retry backoff request') });
|
|
39
|
-
const result = await new PatternMatcher(index([retry]), { useFuzzy: false }).match({ name: 'callAgain', intent: 'make the request work on a flaky network' });
|
|
40
|
-
expect(result.matches.map(m => [m.pattern.name, m.matchType])).toEqual([['retryWithBackoff', 'semantic']]);
|
|
41
|
-
expect(result.action).toBe('WARN');
|
|
42
|
-
});
|
|
43
|
-
it('returns nothing for an unrelated intent', async () => {
|
|
44
|
-
const retry = pattern('retryWithBackoff', { embedding: topicVector('retry backoff request') });
|
|
45
|
-
const result = await new PatternMatcher(index([retry]), { useFuzzy: false }).match({ name: 'drawPie', intent: 'render a pie chart' });
|
|
46
|
-
expect(result.status).toBe('NO_MATCH');
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
describe('enrichIndex', () => {
|
|
50
|
-
it('re-embeds an index built from an older embedding text, and marks the version', async () => {
|
|
51
|
-
const stale = index([pattern('formatDate', { embedding: [9, 9, 9, 9, 9, 9] })], 1);
|
|
52
|
-
const enriched = await new PatternIndexer('/x', { useEmbeddings: true }).enrichIndex(stale);
|
|
53
|
-
expect(enriched.embeddingTextVersion).toBe(EMBEDDING_TEXT_VERSION);
|
|
54
|
-
expect(enriched.patterns[0].embedding).toEqual(topicVector('format date function'));
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
describe('assessPattern', () => {
|
|
58
|
-
let cwd;
|
|
59
|
-
beforeEach(() => {
|
|
60
|
-
cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'assess-'));
|
|
61
|
-
fs.mkdirSync(path.join(cwd, 'src'));
|
|
62
|
-
fs.writeFileSync(path.join(cwd, 'package.json'), JSON.stringify({ dependencies: { 'react-dom': '18.2.0' } }));
|
|
63
|
-
fs.writeFileSync(path.join(cwd, 'src/mount.ts'), 'export function mountApp(el) {\n ReactDOM.render(<App />, el);\n}\n');
|
|
64
|
-
});
|
|
65
|
-
afterEach(() => fs.rmSync(cwd, { recursive: true, force: true }));
|
|
66
|
-
it('blocks an exact duplicate and reports what is deprecated inside the function it would reuse', async () => {
|
|
67
|
-
const mount = pattern('mountApp', { file: 'src/mount.ts', line: 1, endLine: 3 });
|
|
68
|
-
const result = await assessPattern(cwd, index([mount]), { name: 'mountApp' });
|
|
69
|
-
expect(result.action).toBe('BLOCK');
|
|
70
|
-
expect(result.deprecations.map(d => d.replacement)).toEqual(['createRoot(container).render(<App />)']);
|
|
71
|
-
});
|
|
72
|
-
it('allows a name nothing resembles', async () => {
|
|
73
|
-
const result = await assessPattern(cwd, index([pattern('mountApp', { file: 'src/mount.ts' })]), { name: 'exportPdfReport' });
|
|
74
|
-
expect(result).toEqual({ action: 'ALLOW', suggestion: '', deprecations: [] });
|
|
75
|
-
});
|
|
76
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
-
import { locateTransformers, semanticRuntimeDir, semanticRuntimeInstalled } from './semantic-runtime.js';
|
|
6
|
-
let home;
|
|
7
|
-
const original = process.env.RIGOUR_HOME;
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
home = fs.mkdtempSync(path.join(os.tmpdir(), 'semantic-home-'));
|
|
10
|
-
process.env.RIGOUR_HOME = home;
|
|
11
|
-
});
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
process.env.RIGOUR_HOME = original;
|
|
14
|
-
fs.rmSync(home, { recursive: true, force: true });
|
|
15
|
-
});
|
|
16
|
-
describe('the semantic runtime', () => {
|
|
17
|
-
it('lives in Rigour\'s home, shared by every Rigour version, and is seen there once installed', () => {
|
|
18
|
-
expect(semanticRuntimeDir()).toBe(path.join(home, '.rigour', 'runtime', 'semantic'));
|
|
19
|
-
expect(semanticRuntimeInstalled()).toBe(false);
|
|
20
|
-
const pkg = path.join(semanticRuntimeDir(), 'node_modules', '@xenova', 'transformers');
|
|
21
|
-
fs.mkdirSync(pkg, { recursive: true });
|
|
22
|
-
fs.writeFileSync(path.join(pkg, 'package.json'), JSON.stringify({ name: '@xenova/transformers', main: 'index.js' }));
|
|
23
|
-
fs.writeFileSync(path.join(pkg, 'index.js'), 'module.exports = {};');
|
|
24
|
-
expect(semanticRuntimeInstalled()).toBe(true);
|
|
25
|
-
expect(locateTransformers(home)).toBeDefined();
|
|
26
|
-
});
|
|
27
|
-
it('is not a dependency of the published package: the 240 MB is installed once, never per version', () => {
|
|
28
|
-
const manifest = JSON.parse(fs.readFileSync(path.join(__dirname, '../../package.json'), 'utf8'));
|
|
29
|
-
expect({ ...manifest.dependencies, ...manifest.optionalDependencies }).not.toHaveProperty('@xenova/transformers');
|
|
30
|
-
});
|
|
31
|
-
});
|