@rigour-labs/core 6.8.0 → 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/inference/cloud-provider.js +12 -2
- package/package.json +11 -8
- package/dist/brief/briefing.test.d.ts +0 -1
- package/dist/brief/briefing.test.js +0 -107
- 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 -136
- 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 -100
- 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 -52
- 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 -40
- 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 -792
- 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 -93
- 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/task/thread.test.d.ts +0 -1
- package/dist/task/thread.test.js +0 -133
- 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 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { extractComparableJsNames } from './js-style-context.js';
|
|
3
|
-
describe('context-aware JS naming', () => {
|
|
4
|
-
it('excludes exported Next route methods only inside app route files', () => {
|
|
5
|
-
const code = `
|
|
6
|
-
export async function GET() { return Response.json({}); }
|
|
7
|
-
export const POST = async () => Response.json({});
|
|
8
|
-
function GET_HELPER() { return 1; }
|
|
9
|
-
`;
|
|
10
|
-
expect(extractComparableJsNames(code, 'src/app/api/items/route.ts').map(item => item.name))
|
|
11
|
-
.toEqual(['GET_HELPER']);
|
|
12
|
-
expect(extractComparableJsNames(code, 'src/lib/route.ts').map(item => item.name))
|
|
13
|
-
.toEqual(['GET', 'POST', 'GET_HELPER']);
|
|
14
|
-
});
|
|
15
|
-
it('excludes JSX-returning components but keeps ordinary PascalCase functions', () => {
|
|
16
|
-
const code = `
|
|
17
|
-
export function StepReview() { return <section />; }
|
|
18
|
-
const WizardField = () => <input />;
|
|
19
|
-
function ParseValue() { return 1; }
|
|
20
|
-
`;
|
|
21
|
-
expect(extractComparableJsNames(code, 'src/components/WizardField.tsx').map(item => item.name))
|
|
22
|
-
.toEqual(['ParseValue']);
|
|
23
|
-
});
|
|
24
|
-
it('excludes module constants but keeps unconventional local variables', () => {
|
|
25
|
-
const code = `
|
|
26
|
-
const EMAIL_TEMPLATE = 'hello';
|
|
27
|
-
function sendMessage() {
|
|
28
|
-
const LOCAL_VALUE = 1;
|
|
29
|
-
return LOCAL_VALUE;
|
|
30
|
-
}
|
|
31
|
-
`;
|
|
32
|
-
expect(extractComparableJsNames(code, 'src/lib/template.ts').map(item => item.name))
|
|
33
|
-
.toEqual(['sendMessage', 'LOCAL_VALUE']);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import * as fs from 'fs';
|
|
4
|
-
import * as os from 'os';
|
|
5
|
-
import * as path from 'path';
|
|
6
|
-
import { LogicDriftGate } from './logic-drift.js';
|
|
7
|
-
describe('LogicDriftGate Git baseline', () => {
|
|
8
|
-
let cwd;
|
|
9
|
-
const git = (...args) => execFileSync('git', args, { cwd, stdio: 'pipe' });
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'logic-drift-'));
|
|
12
|
-
git('init', '-q', '-b', 'main');
|
|
13
|
-
git('config', 'user.name', 'Rigour Test');
|
|
14
|
-
git('config', 'user.email', 'test@example.invalid');
|
|
15
|
-
fs.mkdirSync(path.join(cwd, 'src'));
|
|
16
|
-
fs.writeFileSync(path.join(cwd, 'src/rules.ts'), 'export function eligible(score: number) { return score >= 10; }\n');
|
|
17
|
-
git('add', '.');
|
|
18
|
-
git('commit', '-qm', 'base');
|
|
19
|
-
});
|
|
20
|
-
afterEach(() => { fs.rmSync(cwd, { recursive: true, force: true }); });
|
|
21
|
-
it('reports a branch mutation consistently and clears it after merge', async () => {
|
|
22
|
-
const gate = new LogicDriftGate();
|
|
23
|
-
expect(await gate.run({ cwd })).toEqual([]);
|
|
24
|
-
git('switch', '-q', '-c', 'feature');
|
|
25
|
-
fs.writeFileSync(path.join(cwd, 'src/rules.ts'), 'export function eligible(score: number) { return score > 10; }\n');
|
|
26
|
-
const first = await gate.run({ cwd });
|
|
27
|
-
const second = await gate.run({ cwd });
|
|
28
|
-
expect(first).toEqual(second);
|
|
29
|
-
expect(first).toHaveLength(1);
|
|
30
|
-
expect(first.some(finding => finding.details.includes("'>=' to '>'"))).toBe(true);
|
|
31
|
-
expect(fs.existsSync(path.join(cwd, '.rigour/logic-baseline.json'))).toBe(false);
|
|
32
|
-
git('add', '.');
|
|
33
|
-
git('commit', '-qm', 'change boundary');
|
|
34
|
-
git('switch', '-q', 'main');
|
|
35
|
-
git('merge', '-q', '--ff-only', 'feature');
|
|
36
|
-
expect(await gate.run({ cwd })).toEqual([]);
|
|
37
|
-
}, 30_000);
|
|
38
|
-
it('reports return and branch count changes only when opted in', async () => {
|
|
39
|
-
git('switch', '-q', '-c', 'feature');
|
|
40
|
-
fs.writeFileSync(path.join(cwd, 'src/rules.ts'), 'export function eligible(score: number) {\n if (score < 0) return false;\n if (score > 100) return false;\n return score >= 10;\n}\n');
|
|
41
|
-
expect(await new LogicDriftGate().run({ cwd })).toEqual([]);
|
|
42
|
-
const opted = await new LogicDriftGate({ track_returns: true, track_branches: true }).run({ cwd });
|
|
43
|
-
expect(opted.map(f => f.title).sort()).toEqual(['Logic Drift: Branch Change', 'Logic Drift: Return Change']);
|
|
44
|
-
}, 30_000);
|
|
45
|
-
it('does not use a moving local snapshot when Git has no main reference', async () => {
|
|
46
|
-
git('switch', '-q', '-c', 'feature');
|
|
47
|
-
git('branch', '-D', 'main');
|
|
48
|
-
const gate = new LogicDriftGate();
|
|
49
|
-
expect(await gate.run({ cwd })).toEqual([]);
|
|
50
|
-
expect(fs.existsSync(path.join(cwd, '.rigour/logic-baseline.json'))).toBe(false);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,396 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
const mockFindFiles = vi.hoisted(() => vi.fn());
|
|
3
|
-
const mockReadFile = vi.hoisted(() => vi.fn());
|
|
4
|
-
const mockPathExists = vi.hoisted(() => vi.fn());
|
|
5
|
-
vi.mock('../utils/scanner.js', () => ({
|
|
6
|
-
FileScanner: { findFiles: mockFindFiles },
|
|
7
|
-
}));
|
|
8
|
-
vi.mock('fs-extra', () => ({
|
|
9
|
-
default: {
|
|
10
|
-
readFile: mockReadFile,
|
|
11
|
-
pathExists: mockPathExists,
|
|
12
|
-
pathExistsSync: vi.fn().mockReturnValue(false),
|
|
13
|
-
readFileSync: vi.fn().mockReturnValue(''),
|
|
14
|
-
readJson: vi.fn().mockResolvedValue(null),
|
|
15
|
-
readdirSync: vi.fn().mockReturnValue([]),
|
|
16
|
-
},
|
|
17
|
-
}));
|
|
18
|
-
import { PhantomApisGate } from './phantom-apis.js';
|
|
19
|
-
describe('PhantomApisGate — Node.js', () => {
|
|
20
|
-
let gate;
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
gate = new PhantomApisGate();
|
|
23
|
-
vi.clearAllMocks();
|
|
24
|
-
});
|
|
25
|
-
it('should flag non-existent fs methods', async () => {
|
|
26
|
-
mockFindFiles.mockResolvedValue(['src/utils.ts']);
|
|
27
|
-
mockReadFile.mockResolvedValue(`
|
|
28
|
-
import fs from 'fs';
|
|
29
|
-
const data = fs.readFileAsync('test.txt');
|
|
30
|
-
const result = fs.writeFilePromise('out.txt', data);
|
|
31
|
-
`);
|
|
32
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
33
|
-
expect(failures).toHaveLength(1);
|
|
34
|
-
expect(failures[0].details).toContain('readFileAsync');
|
|
35
|
-
expect(failures[0].details).toContain('writeFilePromise');
|
|
36
|
-
});
|
|
37
|
-
it('should NOT flag real fs methods', async () => {
|
|
38
|
-
mockFindFiles.mockResolvedValue(['src/utils.ts']);
|
|
39
|
-
mockReadFile.mockResolvedValue(`
|
|
40
|
-
import fs from 'fs';
|
|
41
|
-
const data = fs.readFileSync('test.txt', 'utf-8');
|
|
42
|
-
fs.writeFileSync('out.txt', data);
|
|
43
|
-
fs.existsSync('/tmp');
|
|
44
|
-
const stream = fs.createReadStream('big.txt');
|
|
45
|
-
`);
|
|
46
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
47
|
-
expect(failures).toHaveLength(0);
|
|
48
|
-
});
|
|
49
|
-
it('should flag non-existent path methods', async () => {
|
|
50
|
-
mockFindFiles.mockResolvedValue(['src/paths.ts']);
|
|
51
|
-
mockReadFile.mockResolvedValue(`
|
|
52
|
-
import path from 'path';
|
|
53
|
-
const combined = path.combine('a', 'b');
|
|
54
|
-
const exists = path.exists('/tmp');
|
|
55
|
-
`);
|
|
56
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
57
|
-
expect(failures).toHaveLength(1);
|
|
58
|
-
expect(failures[0].details).toContain('combine');
|
|
59
|
-
expect(failures[0].details).toContain('exists');
|
|
60
|
-
});
|
|
61
|
-
it('should NOT flag a property that shares the module name', async () => {
|
|
62
|
-
mockFindFiles.mockResolvedValue(['src/generate.ts']);
|
|
63
|
-
mockReadFile.mockResolvedValue(`
|
|
64
|
-
import * as path from 'node:path';
|
|
65
|
-
const tag = proc.path.split('.')[0];
|
|
66
|
-
const dir = path.dirname(file);
|
|
67
|
-
`);
|
|
68
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
69
|
-
expect(failures).toHaveLength(0);
|
|
70
|
-
});
|
|
71
|
-
it('should NOT flag real path methods', async () => {
|
|
72
|
-
mockFindFiles.mockResolvedValue(['src/paths.ts']);
|
|
73
|
-
mockReadFile.mockResolvedValue(`
|
|
74
|
-
import path from 'path';
|
|
75
|
-
const p = path.join('a', 'b');
|
|
76
|
-
const ext = path.extname('file.txt');
|
|
77
|
-
const abs = path.resolve('.');
|
|
78
|
-
`);
|
|
79
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
80
|
-
expect(failures).toHaveLength(0);
|
|
81
|
-
});
|
|
82
|
-
it('should flag non-existent crypto methods', async () => {
|
|
83
|
-
mockFindFiles.mockResolvedValue(['src/crypto.ts']);
|
|
84
|
-
mockReadFile.mockResolvedValue(`
|
|
85
|
-
import crypto from 'crypto';
|
|
86
|
-
const hash = crypto.generateHash('sha256', 'data');
|
|
87
|
-
const key = crypto.createKey('aes-256');
|
|
88
|
-
`);
|
|
89
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
90
|
-
expect(failures).toHaveLength(1);
|
|
91
|
-
expect(failures[0].details).toContain('generateHash');
|
|
92
|
-
expect(failures[0].details).toContain('createKey');
|
|
93
|
-
});
|
|
94
|
-
it('should NOT flag real crypto methods', async () => {
|
|
95
|
-
mockFindFiles.mockResolvedValue(['src/crypto.ts']);
|
|
96
|
-
mockReadFile.mockResolvedValue(`
|
|
97
|
-
import crypto from 'crypto';
|
|
98
|
-
const hash = crypto.createHash('sha256');
|
|
99
|
-
const uuid = crypto.randomUUID();
|
|
100
|
-
`);
|
|
101
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
102
|
-
expect(failures).toHaveLength(0);
|
|
103
|
-
});
|
|
104
|
-
it('should handle require() imports', async () => {
|
|
105
|
-
mockFindFiles.mockResolvedValue(['src/legacy.js']);
|
|
106
|
-
mockReadFile.mockResolvedValue(`
|
|
107
|
-
const fs = require('fs');
|
|
108
|
-
fs.readFileAsync('data.txt');
|
|
109
|
-
`);
|
|
110
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
111
|
-
expect(failures).toHaveLength(1);
|
|
112
|
-
expect(failures[0].details).toContain('readFileAsync');
|
|
113
|
-
});
|
|
114
|
-
it('should handle node: protocol imports', async () => {
|
|
115
|
-
mockFindFiles.mockResolvedValue(['src/modern.ts']);
|
|
116
|
-
mockReadFile.mockResolvedValue(`
|
|
117
|
-
import os from 'node:os';
|
|
118
|
-
const info = os.cpuInfo();
|
|
119
|
-
`);
|
|
120
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
121
|
-
expect(failures).toHaveLength(1);
|
|
122
|
-
expect(failures[0].details).toContain('cpuInfo');
|
|
123
|
-
});
|
|
124
|
-
it('should suggest closest real method', async () => {
|
|
125
|
-
mockFindFiles.mockResolvedValue(['src/typo.ts']);
|
|
126
|
-
mockReadFile.mockResolvedValue(`
|
|
127
|
-
import fs from 'fs';
|
|
128
|
-
fs.readFileSyn('data.txt');
|
|
129
|
-
`);
|
|
130
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
131
|
-
expect(failures).toHaveLength(1);
|
|
132
|
-
expect(failures[0].details).toContain('readFileSync');
|
|
133
|
-
});
|
|
134
|
-
it('should ignore phantom method mentions inside comments', async () => {
|
|
135
|
-
mockFindFiles.mockResolvedValue(['src/docs.ts']);
|
|
136
|
-
mockReadFile.mockResolvedValue(`
|
|
137
|
-
import fs from 'fs';
|
|
138
|
-
// fs.readFileAsync('data.txt')
|
|
139
|
-
/* path.combine('a', 'b') */
|
|
140
|
-
/**
|
|
141
|
-
* fs.writeFilePromise('out.txt')
|
|
142
|
-
*/
|
|
143
|
-
const data = fs.readFileSync('real.txt', 'utf-8');
|
|
144
|
-
`);
|
|
145
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
146
|
-
expect(failures).toHaveLength(0);
|
|
147
|
-
});
|
|
148
|
-
it('should not scan when disabled', async () => {
|
|
149
|
-
const disabled = new PhantomApisGate({ enabled: false });
|
|
150
|
-
const failures = await disabled.run({ cwd: '/project' });
|
|
151
|
-
expect(failures).toHaveLength(0);
|
|
152
|
-
expect(mockFindFiles).not.toHaveBeenCalled();
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
describe('PhantomApisGate — Python', () => {
|
|
156
|
-
let gate;
|
|
157
|
-
beforeEach(() => {
|
|
158
|
-
gate = new PhantomApisGate();
|
|
159
|
-
vi.clearAllMocks();
|
|
160
|
-
});
|
|
161
|
-
it('should flag non-existent os methods', async () => {
|
|
162
|
-
mockFindFiles.mockResolvedValue(['utils.py']);
|
|
163
|
-
mockReadFile.mockResolvedValue(`
|
|
164
|
-
import os
|
|
165
|
-
current = os.getCurrentDirectory()
|
|
166
|
-
files = os.listFiles('.')
|
|
167
|
-
`);
|
|
168
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
169
|
-
expect(failures).toHaveLength(1);
|
|
170
|
-
expect(failures[0].details).toContain('getCurrentDirectory');
|
|
171
|
-
expect(failures[0].details).toContain('listFiles');
|
|
172
|
-
});
|
|
173
|
-
it('should NOT flag real os methods', async () => {
|
|
174
|
-
mockFindFiles.mockResolvedValue(['utils.py']);
|
|
175
|
-
mockReadFile.mockResolvedValue(`
|
|
176
|
-
import os
|
|
177
|
-
current = os.getcwd()
|
|
178
|
-
files = os.listdir('.')
|
|
179
|
-
exists = os.path
|
|
180
|
-
`);
|
|
181
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
182
|
-
expect(failures).toHaveLength(0);
|
|
183
|
-
});
|
|
184
|
-
it('should flag non-existent json methods', async () => {
|
|
185
|
-
mockFindFiles.mockResolvedValue(['parser.py']);
|
|
186
|
-
mockReadFile.mockResolvedValue(`
|
|
187
|
-
import json
|
|
188
|
-
data = json.parse('{"key": "value"}')
|
|
189
|
-
result = json.stringify(data)
|
|
190
|
-
`);
|
|
191
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
192
|
-
expect(failures).toHaveLength(1);
|
|
193
|
-
expect(failures[0].details).toContain('parse');
|
|
194
|
-
expect(failures[0].details).toContain('stringify');
|
|
195
|
-
});
|
|
196
|
-
it('should NOT flag real json methods', async () => {
|
|
197
|
-
mockFindFiles.mockResolvedValue(['parser.py']);
|
|
198
|
-
mockReadFile.mockResolvedValue(`
|
|
199
|
-
import json
|
|
200
|
-
data = json.loads('{"key": "value"}')
|
|
201
|
-
output = json.dumps(data)
|
|
202
|
-
`);
|
|
203
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
204
|
-
expect(failures).toHaveLength(0);
|
|
205
|
-
});
|
|
206
|
-
it('should handle aliased imports', async () => {
|
|
207
|
-
mockFindFiles.mockResolvedValue(['utils.py']);
|
|
208
|
-
mockReadFile.mockResolvedValue(`
|
|
209
|
-
import os as operating_system
|
|
210
|
-
result = operating_system.getCurrentDirectory()
|
|
211
|
-
`);
|
|
212
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
213
|
-
expect(failures).toHaveLength(1);
|
|
214
|
-
expect(failures[0].details).toContain('getCurrentDirectory');
|
|
215
|
-
});
|
|
216
|
-
it('should flag non-existent subprocess methods', async () => {
|
|
217
|
-
mockFindFiles.mockResolvedValue(['runner.py']);
|
|
218
|
-
mockReadFile.mockResolvedValue(`
|
|
219
|
-
import subprocess
|
|
220
|
-
result = subprocess.execute(['ls', '-la'])
|
|
221
|
-
`);
|
|
222
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
223
|
-
expect(failures).toHaveLength(1);
|
|
224
|
-
expect(failures[0].details).toContain('execute');
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
describe('PhantomApisGate — Go', () => {
|
|
228
|
-
let gate;
|
|
229
|
-
beforeEach(() => {
|
|
230
|
-
gate = new PhantomApisGate();
|
|
231
|
-
vi.clearAllMocks();
|
|
232
|
-
});
|
|
233
|
-
it('should flag Python-style Go method names', async () => {
|
|
234
|
-
mockFindFiles.mockResolvedValue(['main.go']);
|
|
235
|
-
mockReadFile.mockResolvedValue(`
|
|
236
|
-
package main
|
|
237
|
-
import "strings"
|
|
238
|
-
func main() {
|
|
239
|
-
result := strings.includes("hello", "ell")
|
|
240
|
-
}
|
|
241
|
-
`);
|
|
242
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
243
|
-
expect(failures).toHaveLength(1);
|
|
244
|
-
expect(failures[0].details).toContain('includes');
|
|
245
|
-
expect(failures[0].details).toContain('strings.Contains');
|
|
246
|
-
});
|
|
247
|
-
it('should flag JS-style JSON methods in Go', async () => {
|
|
248
|
-
mockFindFiles.mockResolvedValue(['parser.go']);
|
|
249
|
-
mockReadFile.mockResolvedValue(`
|
|
250
|
-
package main
|
|
251
|
-
import "encoding/json"
|
|
252
|
-
func parse() {
|
|
253
|
-
data := json.parse(raw)
|
|
254
|
-
out := json.stringify(data)
|
|
255
|
-
}
|
|
256
|
-
`);
|
|
257
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
258
|
-
expect(failures).toHaveLength(1);
|
|
259
|
-
expect(failures[0].details).toContain('parse');
|
|
260
|
-
});
|
|
261
|
-
it('should flag os.Exists in Go', async () => {
|
|
262
|
-
mockFindFiles.mockResolvedValue(['files.go']);
|
|
263
|
-
mockReadFile.mockResolvedValue(`
|
|
264
|
-
package main
|
|
265
|
-
import "os"
|
|
266
|
-
func check() {
|
|
267
|
-
exists := os.Exists("/tmp/file")
|
|
268
|
-
}
|
|
269
|
-
`);
|
|
270
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
271
|
-
expect(failures).toHaveLength(1);
|
|
272
|
-
expect(failures[0].details).toContain('Exists');
|
|
273
|
-
});
|
|
274
|
-
it('should NOT flag real Go stdlib functions (os.ReadFile, fmt.Print, math.Max)', async () => {
|
|
275
|
-
mockFindFiles.mockResolvedValue(['main.go']);
|
|
276
|
-
mockReadFile.mockResolvedValue(`
|
|
277
|
-
package main
|
|
278
|
-
|
|
279
|
-
import (
|
|
280
|
-
"fmt"
|
|
281
|
-
"math"
|
|
282
|
-
"os"
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
func main() {
|
|
286
|
-
data, err := os.ReadFile("config.json")
|
|
287
|
-
if err != nil {
|
|
288
|
-
fmt.Printf("error: %v\n", err)
|
|
289
|
-
return
|
|
290
|
-
}
|
|
291
|
-
fmt.Println(string(data))
|
|
292
|
-
fmt.Print("done\n")
|
|
293
|
-
x := math.Max(1.5, 2.5)
|
|
294
|
-
fmt.Sprintf("max: %f", x)
|
|
295
|
-
fmt.Fprintf(os.Stderr, "log\n")
|
|
296
|
-
}
|
|
297
|
-
`);
|
|
298
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
299
|
-
expect(failures).toHaveLength(0);
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
describe('PhantomApisGate — C#', () => {
|
|
303
|
-
let gate;
|
|
304
|
-
beforeEach(() => {
|
|
305
|
-
gate = new PhantomApisGate();
|
|
306
|
-
vi.clearAllMocks();
|
|
307
|
-
});
|
|
308
|
-
it('should flag Java-style method casing in C#', async () => {
|
|
309
|
-
mockFindFiles.mockResolvedValue(['Program.cs']);
|
|
310
|
-
mockReadFile.mockResolvedValue(`
|
|
311
|
-
using System;
|
|
312
|
-
class Program {
|
|
313
|
-
void Main() {
|
|
314
|
-
string s = "hello";
|
|
315
|
-
int len = s.length;
|
|
316
|
-
bool eq = s.equals("world");
|
|
317
|
-
string str = s.toString();
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
`);
|
|
321
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
322
|
-
expect(failures.length).toBeGreaterThanOrEqual(1);
|
|
323
|
-
expect(failures[0].details).toMatch(/length|equals|toString/);
|
|
324
|
-
});
|
|
325
|
-
it('should flag Java collections in C#', async () => {
|
|
326
|
-
mockFindFiles.mockResolvedValue(['Service.cs']);
|
|
327
|
-
mockReadFile.mockResolvedValue(`
|
|
328
|
-
using System.Collections.Generic;
|
|
329
|
-
List<string> items = new ArrayList<string>();
|
|
330
|
-
HashMap<string, int> map = new HashMap<string, int>();
|
|
331
|
-
System.out.println("hello");
|
|
332
|
-
`);
|
|
333
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
334
|
-
expect(failures.length).toBeGreaterThanOrEqual(1);
|
|
335
|
-
});
|
|
336
|
-
});
|
|
337
|
-
describe('PhantomApisGate — Java', () => {
|
|
338
|
-
let gate;
|
|
339
|
-
beforeEach(() => {
|
|
340
|
-
gate = new PhantomApisGate();
|
|
341
|
-
vi.clearAllMocks();
|
|
342
|
-
});
|
|
343
|
-
it('should flag JS/Python-style method names in Java', async () => {
|
|
344
|
-
mockFindFiles.mockResolvedValue(['Main.java']);
|
|
345
|
-
mockReadFile.mockResolvedValue(`
|
|
346
|
-
import java.util.List;
|
|
347
|
-
class Main {
|
|
348
|
-
void test() {
|
|
349
|
-
List<String> items = new ArrayList<>();
|
|
350
|
-
items.push("hello");
|
|
351
|
-
items.includes("test");
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
`);
|
|
355
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
356
|
-
expect(failures.length).toBeGreaterThanOrEqual(1);
|
|
357
|
-
expect(failures[0].details).toMatch(/push|includes/);
|
|
358
|
-
});
|
|
359
|
-
it('should flag new List() in Java', async () => {
|
|
360
|
-
mockFindFiles.mockResolvedValue(['Service.java']);
|
|
361
|
-
mockReadFile.mockResolvedValue(`
|
|
362
|
-
import java.util.*;
|
|
363
|
-
class Service {
|
|
364
|
-
List<String> items = new List<String>();
|
|
365
|
-
Map<String, Integer> map = new Map<String, Integer>();
|
|
366
|
-
}
|
|
367
|
-
`);
|
|
368
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
369
|
-
expect(failures.length).toBeGreaterThanOrEqual(1);
|
|
370
|
-
});
|
|
371
|
-
it('should NOT flag real Java methods (Deque.push, StringBuilder.append, stream().sorted(), List.reversed())', async () => {
|
|
372
|
-
mockFindFiles.mockResolvedValue(['RealJava.java']);
|
|
373
|
-
mockReadFile.mockResolvedValue(`
|
|
374
|
-
import java.util.*;
|
|
375
|
-
import java.util.stream.*;
|
|
376
|
-
class RealJava {
|
|
377
|
-
void test() {
|
|
378
|
-
Deque<String> deque = new ArrayDeque<>();
|
|
379
|
-
deque.push("hello");
|
|
380
|
-
|
|
381
|
-
Stack<Integer> stack = new Stack<>();
|
|
382
|
-
stack.push(42);
|
|
383
|
-
|
|
384
|
-
StringBuilder sb = new StringBuilder();
|
|
385
|
-
sb.append("world");
|
|
386
|
-
|
|
387
|
-
List<Integer> nums = List.of(3, 1, 2);
|
|
388
|
-
List<Integer> sorted = nums.stream().sorted().collect(Collectors.toList());
|
|
389
|
-
List<Integer> rev = nums.reversed();
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
`);
|
|
393
|
-
const failures = await gate.run({ cwd: '/project' });
|
|
394
|
-
expect(failures).toHaveLength(0);
|
|
395
|
-
});
|
|
396
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,34 +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 { PromiseSafetyGate } from './promise-safety.js';
|
|
6
|
-
describe('PromiseSafetyGate unhandled .then', () => {
|
|
7
|
-
let cwd;
|
|
8
|
-
beforeEach(() => { cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'promise-safety-')); });
|
|
9
|
-
afterEach(() => { fs.rmSync(cwd, { recursive: true, force: true }); });
|
|
10
|
-
async function thenFindings(code) {
|
|
11
|
-
fs.writeFileSync(path.join(cwd, 'session.ts'), code);
|
|
12
|
-
const failures = await new PromiseSafetyGate().run({ cwd });
|
|
13
|
-
return failures.filter(f => f.details.includes('unhandled-then')).length;
|
|
14
|
-
}
|
|
15
|
-
it('flags a .then chain with no .catch', async () => {
|
|
16
|
-
expect(await thenFindings('export function wake(p: Promise<number>) {\n p.then(n => {\n if (n > 1) log(n);\n });\n}\ndeclare function log(n: number): void;\n')).toBe(1);
|
|
17
|
-
});
|
|
18
|
-
it('finds the .catch that closes a chain whose callback body has if and return statements', async () => {
|
|
19
|
-
const code = [
|
|
20
|
-
'export function wake(accepted: Promise<{ action: string }>) {',
|
|
21
|
-
' void accepted',
|
|
22
|
-
' .then(result => {',
|
|
23
|
-
" if (result.action === 'wake') {",
|
|
24
|
-
' return run();',
|
|
25
|
-
' }',
|
|
26
|
-
' return undefined;',
|
|
27
|
-
' })',
|
|
28
|
-
' .catch(() => {});',
|
|
29
|
-
'}',
|
|
30
|
-
'declare function run(): Promise<void>;',
|
|
31
|
-
].join('\n');
|
|
32
|
-
expect(await thenFindings(code)).toBe(0);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
-
import { GateRunner } from './runner.js';
|
|
6
|
-
import { DeepAnalysisGate } from './deep-analysis.js';
|
|
7
|
-
function mockDeep(outcome, failures = []) {
|
|
8
|
-
vi.spyOn(DeepAnalysisGate.prototype, 'run').mockResolvedValue(failures);
|
|
9
|
-
vi.spyOn(DeepAnalysisGate.prototype, 'getOutcome').mockReturnValue({
|
|
10
|
-
status: 'ok', mode: 'facts', filesAnalyzed: 1, chunksTotal: 1, chunksFailed: 0, ...outcome,
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
describe('GateRunner deep stats execution mode', () => {
|
|
14
|
-
let testDir;
|
|
15
|
-
beforeEach(async () => {
|
|
16
|
-
testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'rigour-runner-deep-'));
|
|
17
|
-
await fs.writeFile(path.join(testDir, 'index.ts'), 'export const ok = true;\n');
|
|
18
|
-
});
|
|
19
|
-
afterEach(async () => {
|
|
20
|
-
vi.restoreAllMocks();
|
|
21
|
-
await fs.remove(testDir);
|
|
22
|
-
});
|
|
23
|
-
function createRunner() {
|
|
24
|
-
return new GateRunner({
|
|
25
|
-
version: 1,
|
|
26
|
-
commands: {},
|
|
27
|
-
gates: {
|
|
28
|
-
max_file_lines: 500,
|
|
29
|
-
forbid_todos: true,
|
|
30
|
-
forbid_fixme: true,
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
it('reports local lite tier and the model the provider actually loaded', async () => {
|
|
35
|
-
mockDeep({ model: 'Qwen2.5-Coder-0.5B-Instruct (stock)', modelFallback: true });
|
|
36
|
-
const report = await createRunner().run(testDir, undefined, {
|
|
37
|
-
enabled: true, apiKey: 'sk-test', provider: 'local', pro: false,
|
|
38
|
-
});
|
|
39
|
-
expect(report.stats.deep?.tier).toBe('lite');
|
|
40
|
-
expect(report.stats.deep?.model).toBe('Qwen2.5-Coder-0.5B-Instruct (stock)');
|
|
41
|
-
expect(report.stats.deep?.model_fallback).toBe(true);
|
|
42
|
-
expect(report.stats.deep?.status).toBe('ok');
|
|
43
|
-
expect(report.summary['deep-analysis']).toBe('PASS');
|
|
44
|
-
});
|
|
45
|
-
it('reports local deep tier when provider=local and pro=true', async () => {
|
|
46
|
-
mockDeep({ model: 'Rigour-Deep-v5.0.0 (Qwen2.5-Coder-1.5B fine-tuned)', modelFallback: false });
|
|
47
|
-
const report = await createRunner().run(testDir, undefined, {
|
|
48
|
-
enabled: true, apiKey: 'sk-test', provider: 'local', pro: true,
|
|
49
|
-
});
|
|
50
|
-
expect(report.stats.deep?.tier).toBe('deep');
|
|
51
|
-
expect(report.stats.deep?.model).toBe('Rigour-Deep-v5.0.0 (Qwen2.5-Coder-1.5B fine-tuned)');
|
|
52
|
-
});
|
|
53
|
-
it('reports cloud tier/model for cloud providers', async () => {
|
|
54
|
-
mockDeep({ model: 'gpt-4.1-mini' });
|
|
55
|
-
const report = await createRunner().run(testDir, undefined, {
|
|
56
|
-
enabled: true, apiKey: 'sk-test', provider: 'openai', modelName: 'gpt-4.1-mini', pro: false,
|
|
57
|
-
});
|
|
58
|
-
expect(report.stats.deep?.tier).toBe('cloud');
|
|
59
|
-
expect(report.stats.deep?.model).toBe('gpt-4.1-mini');
|
|
60
|
-
});
|
|
61
|
-
it('reports a deep run that could not analyze anything as ERROR with a visible failure', async () => {
|
|
62
|
-
mockDeep({ status: 'error', chunksTotal: 3, chunksFailed: 3, error: 'All 3 inference call(s) failed.' });
|
|
63
|
-
const report = await createRunner().run(testDir, undefined, { enabled: true, pro: false });
|
|
64
|
-
expect(report.summary['deep-analysis']).toBe('ERROR');
|
|
65
|
-
expect(report.status).toBe('FAIL');
|
|
66
|
-
expect(report.stats.deep).toMatchObject({ status: 'error', chunks_total: 3, chunks_failed: 3, error: 'All 3 inference call(s) failed.' });
|
|
67
|
-
const failure = report.failures.find(f => f.id === 'deep-analysis');
|
|
68
|
-
expect(failure?.title).toBe('Deep analysis did not run');
|
|
69
|
-
expect(failure?.details).toContain('All 3 inference call(s) failed.');
|
|
70
|
-
});
|
|
71
|
-
it('keeps a partial run as PASS/FAIL by findings and records failed chunks', async () => {
|
|
72
|
-
mockDeep({ status: 'partial', chunksTotal: 4, chunksFailed: 1 });
|
|
73
|
-
const report = await createRunner().run(testDir, undefined, { enabled: true, pro: false });
|
|
74
|
-
expect(report.summary['deep-analysis']).toBe('PASS');
|
|
75
|
-
expect(report.stats.deep).toMatchObject({ status: 'partial', chunks_total: 4, chunks_failed: 1 });
|
|
76
|
-
});
|
|
77
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
-
import { ContextGate } from './context.js';
|
|
6
|
-
import { DependencyGate } from './dependency.js';
|
|
7
|
-
import { LogicDriftGate } from './logic-drift.js';
|
|
8
|
-
import { StyleDriftGate } from './style-drift.js';
|
|
9
|
-
describe('gates on a scoped run', () => {
|
|
10
|
-
let cwd;
|
|
11
|
-
beforeEach(async () => { cwd = await fs.mkdtemp(path.join(os.tmpdir(), 'rigour-scoped-')); });
|
|
12
|
-
afterEach(async () => { await fs.remove(cwd); });
|
|
13
|
-
it('context-drift reports only findings that cite a file in scope', async () => {
|
|
14
|
-
await fs.outputFile(path.join(cwd, 'src/a.ts'), 'export const a = process.env.API_URL_PROD;\n');
|
|
15
|
-
await fs.outputFile(path.join(cwd, 'src/b.ts'), 'export const b = process.env.API_URL_STAGING;\n');
|
|
16
|
-
const gate = new ContextGate({ context: { enabled: true } });
|
|
17
|
-
const record = { anchors: [{ type: 'env', id: 'API_URL', confidence: 1 }] };
|
|
18
|
-
const unscoped = await gate.run({ cwd, record });
|
|
19
|
-
const scoped = await gate.run({ cwd, record, patterns: ['src/a.ts'] });
|
|
20
|
-
expect(unscoped.flatMap(f => f.files)).toEqual(expect.arrayContaining(['src/a.ts', 'src/b.ts']));
|
|
21
|
-
expect(scoped.length).toBeGreaterThan(0);
|
|
22
|
-
expect(scoped.every(f => f.files?.includes('src/a.ts'))).toBe(true);
|
|
23
|
-
});
|
|
24
|
-
it('dependency-guardian checks the manifest only when package.json is in scope', async () => {
|
|
25
|
-
await fs.outputJson(path.join(cwd, 'package.json'), { dependencies: { 'left-pad': '1.0.0' } });
|
|
26
|
-
await fs.outputFile(path.join(cwd, 'src/a.ts'), "import pad from 'left-pad';\n");
|
|
27
|
-
const gate = new DependencyGate({ gates: { dependencies: { forbid: ['left-pad'] } } });
|
|
28
|
-
expect(await gate.run({ cwd, patterns: ['src/a.ts'] })).toEqual([]);
|
|
29
|
-
const withManifest = await gate.run({ cwd, patterns: ['package.json'] });
|
|
30
|
-
expect(withManifest.some(f => f.title === 'Forbidden Dependency')).toBe(true);
|
|
31
|
-
});
|
|
32
|
-
it('logic-drift never creates or rewrites its baseline from a scoped run', async () => {
|
|
33
|
-
const baseline = path.join(cwd, '.rigour/logic-baseline.json');
|
|
34
|
-
await fs.outputFile(path.join(cwd, 'src/rules.ts'), 'export function eligible(score: number) { return score >= 10; }\n');
|
|
35
|
-
await fs.outputFile(path.join(cwd, 'src/other.ts'), 'export function other(n: number) { return n > 1; }\n');
|
|
36
|
-
const gate = new LogicDriftGate();
|
|
37
|
-
expect(await gate.run({ cwd, patterns: ['src/rules.ts'] })).toEqual([]);
|
|
38
|
-
expect(await fs.pathExists(baseline)).toBe(false);
|
|
39
|
-
await gate.run({ cwd });
|
|
40
|
-
const before = await fs.readFile(baseline, 'utf-8');
|
|
41
|
-
await fs.outputFile(path.join(cwd, 'src/rules.ts'), 'export function eligible(score: number) { return score > 10; }\n');
|
|
42
|
-
const scoped = await gate.run({ cwd, patterns: ['src/rules.ts'] });
|
|
43
|
-
expect(scoped.some(f => f.details.includes("'>=' to '>'"))).toBe(true);
|
|
44
|
-
expect(await fs.readFile(baseline, 'utf-8')).toBe(before);
|
|
45
|
-
});
|
|
46
|
-
it('style-drift does not create its baseline from a scoped run', async () => {
|
|
47
|
-
await fs.outputFile(path.join(cwd, 'src/a.ts'), 'export function doThing() { return 1; }\n');
|
|
48
|
-
const gate = new StyleDriftGate();
|
|
49
|
-
expect(await gate.run({ cwd, patterns: ['src/a.ts'] })).toEqual([]);
|
|
50
|
-
expect(await fs.pathExists(path.join(cwd, '.rigour/style-baseline.json'))).toBe(false);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|