@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
package/dist/task/thread.test.js
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { execFileSync } from 'child_process';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
6
|
-
import { appendTaskEvent, readThread, taskOf, threadsDir, threadText } from './thread.js';
|
|
7
|
-
let repo;
|
|
8
|
-
const git = (...args) => execFileSync('git', ['-C', repo, ...args], { encoding: 'utf8' }).trim();
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
repo = fs.mkdtempSync(path.join(os.tmpdir(), 'thread-'));
|
|
11
|
-
git('init', '-q', '-b', 'main');
|
|
12
|
-
git('config', 'user.email', 't@example.com');
|
|
13
|
-
git('config', 'user.name', 't');
|
|
14
|
-
git('config', 'commit.gpgsign', 'false');
|
|
15
|
-
fs.writeFileSync(path.join(repo, 'a.ts'), 'export const a = 1;\n');
|
|
16
|
-
git('add', '-A');
|
|
17
|
-
git('commit', '-qm', 'init');
|
|
18
|
-
});
|
|
19
|
-
afterEach(() => fs.rmSync(repo, { recursive: true, force: true }));
|
|
20
|
-
describe('the engineering task', () => {
|
|
21
|
-
const commit = (subject) => {
|
|
22
|
-
fs.appendFileSync(path.join(repo, 'a.ts'), `// ${subject}\n`);
|
|
23
|
-
git('commit', '-qam', subject);
|
|
24
|
-
};
|
|
25
|
-
it('is the ticket the branch names only when its own commits name it too; a version token is never a ticket', () => {
|
|
26
|
-
for (const branch of ['chore/pin-node-22', 'fix/utf-8-decoding', 'feat/http-2-push', 'release-1.4', 'bump/python-3-12']) {
|
|
27
|
-
git('checkout', '-q', 'main');
|
|
28
|
-
git('checkout', '-qb', branch);
|
|
29
|
-
commit(`${branch.split('/').pop()}: bump it`); // the version token in the subject, as people write it
|
|
30
|
-
expect(taskOf(repo)?.key).toBe(`branch:${branch}`);
|
|
31
|
-
}
|
|
32
|
-
git('checkout', '-q', 'main');
|
|
33
|
-
git('checkout', '-qb', 'feat/proj-123-resume-emails');
|
|
34
|
-
expect(taskOf(repo)?.key).toBe('branch:feat/proj-123-resume-emails'); // named by the branch, not yet by a commit
|
|
35
|
-
commit('proj-123 resume emails');
|
|
36
|
-
expect(taskOf(repo)?.key).toBe('branch:feat/proj-123-resume-emails'); // lower case is how versions are written, not tickets
|
|
37
|
-
commit('PROJ-123: resume emails');
|
|
38
|
-
expect(taskOf(repo)).toMatchObject({ key: 'PROJ-123', branch: 'feat/proj-123-resume-emails' });
|
|
39
|
-
git('checkout', '-qb', 'fix-the-thing');
|
|
40
|
-
expect(taskOf(repo)?.key).toBe('branch:fix-the-thing'); // a branch with no ticket in its name
|
|
41
|
-
git('checkout', '-q', '--detach');
|
|
42
|
-
expect(taskOf(repo)).toBeUndefined();
|
|
43
|
-
expect(appendTaskEvent(repo, { kind: 'push', passed: true })).toBeUndefined();
|
|
44
|
-
});
|
|
45
|
-
it("takes the ticket from the pull request's title once a review recorded it, when the commits use another scope", () => {
|
|
46
|
-
git('checkout', '-qb', 'feat/PROJ-7-retry');
|
|
47
|
-
commit('fix(retry): back off on 429');
|
|
48
|
-
appendTaskEvent(repo, { kind: 'edit-check', files: ['src/retry.ts'], findings: 0 });
|
|
49
|
-
expect(taskOf(repo)?.key).toBe('branch:feat/PROJ-7-retry');
|
|
50
|
-
appendTaskEvent(repo, { kind: 'review', pr: 7, pr_title: 'feat(PROJ-7): retry with backoff', outcome: 'passed', blocking: 0 });
|
|
51
|
-
expect(taskOf(repo)?.key).toBe('PROJ-7'); // the same commit: the title confirmed it, and the kept task was dropped
|
|
52
|
-
appendTaskEvent(repo, { kind: 'push', passed: true });
|
|
53
|
-
expect(readThread(repo, 'PROJ-7')?.events.map(e => [e.kind, e.task])).toEqual([['edit-check', 'branch:feat/PROJ-7-retry'], ['review', 'branch:feat/PROJ-7-retry'], ['push', 'PROJ-7']]);
|
|
54
|
-
});
|
|
55
|
-
it('works the task out once per commit and keeps it, so the edit hook does not read the branch history on every edit', () => {
|
|
56
|
-
git('checkout', '-qb', 'feat/PROJ-9-cache');
|
|
57
|
-
commit('PROJ-9: start');
|
|
58
|
-
expect(taskOf(repo)?.key).toBe('PROJ-9');
|
|
59
|
-
const cache = path.join(repo, '.git', 'rigour', 'task-cache.json');
|
|
60
|
-
expect(JSON.parse(fs.readFileSync(cache, 'utf8'))['feat/PROJ-9-cache']).toEqual({ head: git('rev-parse', 'HEAD'), key: 'PROJ-9' });
|
|
61
|
-
fs.writeFileSync(cache, JSON.stringify({ 'feat/PROJ-9-cache': { head: git('rev-parse', 'HEAD'), key: 'KEPT-1' } }));
|
|
62
|
-
expect(taskOf(repo)?.key).toBe('KEPT-1'); // read from what was kept, not worked out again
|
|
63
|
-
commit('PROJ-9: more');
|
|
64
|
-
expect(taskOf(repo)?.key).toBe('PROJ-9'); // a new commit: worked out again
|
|
65
|
-
});
|
|
66
|
-
it('keeps one file per exact branch: a/b and a_b never share a thread', () => {
|
|
67
|
-
git('checkout', '-qb', 'feat/a/b');
|
|
68
|
-
appendTaskEvent(repo, { kind: 'push', passed: true });
|
|
69
|
-
git('checkout', '-q', 'main');
|
|
70
|
-
git('checkout', '-qb', 'feat/a_b');
|
|
71
|
-
appendTaskEvent(repo, { kind: 'push', passed: false, failed: 2 });
|
|
72
|
-
expect(readThread(repo, 'feat/a/b')?.events.map(e => [e.branch, e.passed])).toEqual([['feat/a/b', true]]);
|
|
73
|
-
expect(readThread(repo, 'feat/a_b')?.events.map(e => [e.branch, e.passed])).toEqual([['feat/a_b', false]]);
|
|
74
|
-
expect(fs.readdirSync(threadsDir(repo))).toHaveLength(2);
|
|
75
|
-
});
|
|
76
|
-
it('gathers a ticket across the branches and worktrees that worked on it, from their first event', () => {
|
|
77
|
-
git('checkout', '-qb', 'feat/PROJ-5-api');
|
|
78
|
-
appendTaskEvent(repo, { kind: 'edit-check', files: ['src/api.ts'], findings: 1 }); // before the first commit names the ticket
|
|
79
|
-
commit('PROJ-5: the api');
|
|
80
|
-
appendTaskEvent(repo, { kind: 'push', passed: true });
|
|
81
|
-
const other = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'thread-wt-')), 'wt');
|
|
82
|
-
git('worktree', 'add', '-q', '-b', 'feat/PROJ-5-ui', other, 'main');
|
|
83
|
-
fs.appendFileSync(path.join(other, 'a.ts'), '// ui\n');
|
|
84
|
-
execFileSync('git', ['-C', other, 'commit', '-qam', 'PROJ-5 the ui'], { encoding: 'utf8' });
|
|
85
|
-
appendTaskEvent(other, { kind: 'push', passed: true }); // written from the other worktree, into the same folder
|
|
86
|
-
const thread = readThread(repo, 'proj-5');
|
|
87
|
-
expect(thread?.task).toBe('PROJ-5');
|
|
88
|
-
expect(thread?.events.map(e => [e.kind, e.branch])).toEqual([['edit-check', 'feat/PROJ-5-api'], ['push', 'feat/PROJ-5-api'], ['push', 'feat/PROJ-5-ui']]);
|
|
89
|
-
expect(readThread(repo, 'feat/PROJ-5-ui')?.events).toHaveLength(1);
|
|
90
|
-
git('worktree', 'remove', '--force', other);
|
|
91
|
-
});
|
|
92
|
-
it('keeps an append-only thread, read by ticket, branch, pull request or the checkout, oldest first, skipping a broken line', () => {
|
|
93
|
-
git('checkout', '-qb', 'feat/PROJ-7-retry');
|
|
94
|
-
commit('PROJ-7 retry');
|
|
95
|
-
appendTaskEvent(repo, { kind: 'edit-check', session: 'sess-aaaa1111', agent: 'claude', files: ['src/job.ts'], findings: 2 });
|
|
96
|
-
appendTaskEvent(repo, { kind: 'edit-check', session: 'sess-aaaa1111', agent: 'claude', files: ['src/job.ts'], findings: 0 });
|
|
97
|
-
appendTaskEvent(repo, { kind: 'stop-review', session: 'sess-aaaa1111', agent: 'claude', blocked: true, blocking: 1 });
|
|
98
|
-
appendTaskEvent(repo, { kind: 'push', passed: false, failed: 1 });
|
|
99
|
-
appendTaskEvent(repo, { kind: 'push', passed: true, failed: 0 });
|
|
100
|
-
appendTaskEvent(repo, { kind: 'review', pr: 42, outcome: 'passed', blocking: 0, should_fix: 1, integrity: 'abcdef0123456789abcdef' });
|
|
101
|
-
const [name] = fs.readdirSync(threadsDir(repo));
|
|
102
|
-
const file = path.join(threadsDir(repo), name);
|
|
103
|
-
expect(name).toMatch(/^feat_PROJ-7-retry-[0-9a-f]{8}\.jsonl$/);
|
|
104
|
-
expect(fs.realpathSync(file)).toBe(fs.realpathSync(path.join(repo, '.git', 'rigour', 'threads', name)));
|
|
105
|
-
expect(git('status', '--porcelain')).toBe(''); // nothing in the working tree
|
|
106
|
-
fs.appendFileSync(file, 'not json\n{"kind":"push"}\n');
|
|
107
|
-
const byTicket = readThread(repo, 'proj-7');
|
|
108
|
-
expect(byTicket?.task).toBe('PROJ-7');
|
|
109
|
-
expect(byTicket?.events.map(e => e.kind)).toEqual(['edit-check', 'edit-check', 'stop-review', 'push', 'push', 'review']);
|
|
110
|
-
expect(readThread(repo, 'feat/PROJ-7-retry')?.events).toHaveLength(6);
|
|
111
|
-
expect(readThread(repo, '#42')?.task).toBe('PROJ-7');
|
|
112
|
-
expect(readThread(repo, '42')?.task).toBe('PROJ-7');
|
|
113
|
-
expect(readThread(repo)?.events).toHaveLength(6);
|
|
114
|
-
expect(readThread(repo, '#43')).toBeUndefined();
|
|
115
|
-
expect(readThread(repo, 'OTHER-1')).toBeUndefined();
|
|
116
|
-
expect(byTicket.events[0]).toMatchObject({ task: 'PROJ-7', branch: 'feat/PROJ-7-retry', head: git('rev-parse', 'HEAD') });
|
|
117
|
-
const text = threadText(byTicket).join('\n');
|
|
118
|
-
expect(text).toContain('PROJ-7 · feat/PROJ-7-retry · PR #42');
|
|
119
|
-
expect(text).toContain('1 agent session(s) (claude)');
|
|
120
|
-
expect(text).toContain('findings caught while writing: 2, files clean again after a finding: 1');
|
|
121
|
-
expect(text).toContain('stop reviews: 1, blocked: 1');
|
|
122
|
-
expect(text).toContain('pushes: 2, blocked: 1');
|
|
123
|
-
expect(text).toContain('reviews: 1, last: passed with 0 blocking (record abcdef0123456789)');
|
|
124
|
-
expect(text).toContain('blocked: 1 check(s) failed');
|
|
125
|
-
});
|
|
126
|
-
it('never fails the hook it runs in: an unwritable thread folder is skipped', () => {
|
|
127
|
-
git('checkout', '-qb', 'feat/PROJ-8');
|
|
128
|
-
fs.writeFileSync(path.join(repo, '.git', 'rigour'), 'a file where the folder should be');
|
|
129
|
-
expect(appendTaskEvent(repo, { kind: 'push', passed: true })).toBeUndefined();
|
|
130
|
-
expect(readThread(repo)?.events ?? []).toEqual([]);
|
|
131
|
-
expect(threadText({ task: 'PROJ-8', events: [] })).toEqual(['PROJ-8: nothing recorded yet']);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,64 +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
|
-
import { countUsage, durationBucket, flushDailyUsage, isTelemetryEnabled, readTelemetryState, setTelemetryEnabled, shouldAskTelemetry, trackUsage } from './telemetry.js';
|
|
6
|
-
let home;
|
|
7
|
-
const env = { RIGOUR_MIXPANEL_TOKEN: 'tok' };
|
|
8
|
-
beforeEach(() => { home = fs.mkdtempSync(path.join(os.tmpdir(), 'telemetry-')); });
|
|
9
|
-
afterEach(() => { fs.rmSync(home, { recursive: true, force: true }); });
|
|
10
|
-
describe('telemetry consent', () => {
|
|
11
|
-
it('sends nothing until the person says yes, and the environment always wins', () => {
|
|
12
|
-
expect(isTelemetryEnabled({ env, home })).toBe(false);
|
|
13
|
-
expect(shouldAskTelemetry(true, { env, home })).toBe(true);
|
|
14
|
-
setTelemetryEnabled(true, home);
|
|
15
|
-
expect(isTelemetryEnabled({ env, home })).toBe(true);
|
|
16
|
-
expect(shouldAskTelemetry(true, { env, home })).toBe(false); // asked once
|
|
17
|
-
expect(isTelemetryEnabled({ env: { ...env, DO_NOT_TRACK: '1' }, home })).toBe(false);
|
|
18
|
-
expect(isTelemetryEnabled({ env: { ...env, RIGOUR_TELEMETRY: '0' }, home })).toBe(false);
|
|
19
|
-
expect(isTelemetryEnabled({ env: { ...env, CI: 'true' }, home })).toBe(false);
|
|
20
|
-
expect(isTelemetryEnabled({ env: {}, home })).toBe(false); // a build without a token never sends
|
|
21
|
-
});
|
|
22
|
-
it('never asks in CI or without a terminal, and keeps one random install id', () => {
|
|
23
|
-
expect(shouldAskTelemetry(false, { env, home })).toBe(false);
|
|
24
|
-
expect(shouldAskTelemetry(true, { env: { ...env, GITHUB_ACTIONS: 'true' }, home })).toBe(false);
|
|
25
|
-
setTelemetryEnabled(true, home);
|
|
26
|
-
for (const ci of ['JENKINS_URL', 'TF_BUILD', 'TEAMCITY_VERSION']) {
|
|
27
|
-
expect(isTelemetryEnabled({ env: { ...env, [ci]: 'x' }, home })).toBe(false); // a build agent's stored yes is not a CI opt-in
|
|
28
|
-
expect(isTelemetryEnabled({ env: { ...env, [ci]: 'x', RIGOUR_TELEMETRY: '1' }, home })).toBe(true);
|
|
29
|
-
}
|
|
30
|
-
const first = setTelemetryEnabled(false, home).installId;
|
|
31
|
-
expect(readTelemetryState(home).installId).toBe(first);
|
|
32
|
-
expect(first).toMatch(/^[0-9a-f-]{36}$/);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
describe('telemetry sending', () => {
|
|
36
|
-
it('sends one event with the install id and no IP, and swallows a failing network', async () => {
|
|
37
|
-
setTelemetryEnabled(true, home);
|
|
38
|
-
const fetch = vi.fn().mockResolvedValue({ ok: true });
|
|
39
|
-
await trackUsage('review_completed', { status: 'PASS', findings_by_gate: { 'semantic-bugs': 1 } }, { env, home, fetch, version: '6.5.0' });
|
|
40
|
-
const [url, init] = fetch.mock.calls[0];
|
|
41
|
-
expect(url).toBe('https://api.mixpanel.com/track?ip=0');
|
|
42
|
-
const [event] = JSON.parse(init.body);
|
|
43
|
-
expect(event.event).toBe('review_completed');
|
|
44
|
-
expect(event.properties).toMatchObject({ token: 'tok', distinct_id: readTelemetryState(home).installId, version: '6.5.0', status: 'PASS' });
|
|
45
|
-
expect(JSON.stringify(event)).not.toContain(home);
|
|
46
|
-
await expect(trackUsage('x', {}, { env, home, fetch: vi.fn().mockRejectedValue(new Error('offline')) })).resolves.toBeUndefined();
|
|
47
|
-
});
|
|
48
|
-
it('counts hot-path activity locally and sends it as one daily event', async () => {
|
|
49
|
-
setTelemetryEnabled(true, home);
|
|
50
|
-
const fetch = vi.fn().mockResolvedValue({ ok: true });
|
|
51
|
-
const day = 24 * 60 * 60 * 1000;
|
|
52
|
-
countUsage('hook_check', 1, { env, home, now: 0 });
|
|
53
|
-
countUsage('hook_check', 1, { env, home, now: 1000 });
|
|
54
|
-
countUsage('mcp:rigour_review', 1, { env, home, now: 2000 });
|
|
55
|
-
expect(await flushDailyUsage({ env, home, fetch, now: 5000 })).toBe(false); // not a day yet
|
|
56
|
-
expect(await flushDailyUsage({ env, home, fetch, now: day + 1 })).toBe(true);
|
|
57
|
-
const [event] = JSON.parse(fetch.mock.calls[0][1].body);
|
|
58
|
-
expect(event).toMatchObject({ event: 'daily_usage', properties: { hook_check: 2, 'mcp:rigour_review': 1 } });
|
|
59
|
-
expect(await flushDailyUsage({ env, home, fetch, now: 2 * day + 2 })).toBe(false); // counters reset
|
|
60
|
-
});
|
|
61
|
-
it('buckets durations so timing never identifies a run', () => {
|
|
62
|
-
expect([500, 3000, 20000, 60000, 600000].map(durationBucket)).toEqual(['<1s', '1-5s', '5-30s', '30s-2m', '>2m']);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/types/index.test.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import yaml from 'yaml';
|
|
3
|
-
import { ConfigSchema } from './index.js';
|
|
4
|
-
describe('rigour.yml', () => {
|
|
5
|
-
it('still loads a file that sets the settings no check ever read, and keeps everything else', () => {
|
|
6
|
-
const old = yaml.parse(`
|
|
7
|
-
version: 1
|
|
8
|
-
planned: [Layer boundaries]
|
|
9
|
-
hooks: { enabled: true, tools: [claude], fast_gates: [file-size], timeout_ms: 5000, block_on_failure: true, dlp: false, require_review_ack: true }
|
|
10
|
-
gates:
|
|
11
|
-
max_file_lines: 300
|
|
12
|
-
forbid_paths: [secret/]
|
|
13
|
-
ast: { complexity: 7, max_nesting: 3, max_function_lines: 40 }
|
|
14
|
-
adaptive: { enabled: true, forced_tier: enterprise }
|
|
15
|
-
input_validation: { enabled: true, min_secret_length: 12 }
|
|
16
|
-
deep: { enabled: true, provider: claude, api_key: sk-test, model_name: x, threads: 8, temperature: 0.2 }
|
|
17
|
-
test_quality: { check_snapshot_abuse: false, ignore_patterns: [x], max_mocks_per_test: 3 }
|
|
18
|
-
`);
|
|
19
|
-
const config = ConfigSchema.parse(old);
|
|
20
|
-
expect(config.gates.max_file_lines).toBe(300);
|
|
21
|
-
expect(config.gates.ast.complexity).toBe(7);
|
|
22
|
-
expect(config.gates.deep.temperature).toBe(0.2);
|
|
23
|
-
expect(config.gates.test_quality.max_mocks_per_test).toBe(3);
|
|
24
|
-
expect(config.hooks.require_review_ack).toBe(true);
|
|
25
|
-
// What nothing read is dropped, so no code can start depending on it by accident.
|
|
26
|
-
expect(config).not.toHaveProperty('version');
|
|
27
|
-
expect(config).not.toHaveProperty('planned');
|
|
28
|
-
expect(config.gates).not.toHaveProperty('adaptive');
|
|
29
|
-
expect(config.gates).not.toHaveProperty('input_validation');
|
|
30
|
-
expect(config.gates.deep).not.toHaveProperty('api_key');
|
|
31
|
-
expect(config.hooks).not.toHaveProperty('block_on_failure');
|
|
32
|
-
});
|
|
33
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { splitCommand } from './command-line.js';
|
|
3
|
-
describe('splitCommand', () => {
|
|
4
|
-
it('splits on spaces and keeps a quoted argument whole, without its quotes', () => {
|
|
5
|
-
expect(splitCommand('npm run lint')).toEqual({ bin: 'npm', args: ['run', 'lint'] });
|
|
6
|
-
expect(splitCommand(`vitest run "src/a b.test.ts" --reporter 'dot'`)).toEqual({ bin: 'vitest', args: ['run', 'src/a b.test.ts', '--reporter', 'dot'] });
|
|
7
|
-
});
|
|
8
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { parseDiff, removedByFile } from './diff.js';
|
|
3
|
-
const DIFF = `diff --git a/src/a.ts b/src/a.ts
|
|
4
|
-
--- a/src/a.ts
|
|
5
|
-
+++ b/src/a.ts
|
|
6
|
-
@@ -1,5 +1,4 @@
|
|
7
|
-
export function load(id: string) {
|
|
8
|
-
- if (!id) return null;
|
|
9
|
-
- audit(id);
|
|
10
|
-
+ audit(id ?? '');
|
|
11
|
-
return fetch(id);
|
|
12
|
-
}
|
|
13
|
-
@@ -20,2 +19,1 @@
|
|
14
|
-
-const legacy = true;
|
|
15
|
-
export const x = 1;
|
|
16
|
-
`;
|
|
17
|
-
describe('removedByFile', () => {
|
|
18
|
-
it('groups removed lines into blocks anchored at the new-side line', () => {
|
|
19
|
-
expect(removedByFile(DIFF)).toEqual({
|
|
20
|
-
'src/a.ts': [
|
|
21
|
-
{ line: 2, text: [' if (!id) return null;', ' audit(id);'] },
|
|
22
|
-
{ line: 19, text: ['const legacy = true;'] },
|
|
23
|
-
],
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
it('leaves added-line parsing unchanged', () => {
|
|
27
|
-
expect([...parseDiff(DIFF)['src/a.ts']]).toEqual([2]);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/utils/diff.test.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { changedLinesByFile, parseDiff } from './diff.js';
|
|
3
|
-
const lines = (m) => changedLinesByFile(m);
|
|
4
|
-
describe('parseDiff', () => {
|
|
5
|
-
it('maps added lines to new-side line numbers', () => {
|
|
6
|
-
const diff = [
|
|
7
|
-
'diff --git a/src/a.ts b/src/a.ts',
|
|
8
|
-
'--- a/src/a.ts',
|
|
9
|
-
'+++ b/src/a.ts',
|
|
10
|
-
'@@ -1,3 +1,4 @@',
|
|
11
|
-
' const a = 1;',
|
|
12
|
-
'-const b = 2;',
|
|
13
|
-
'+const b = 3;',
|
|
14
|
-
'+const c = 4;',
|
|
15
|
-
' export { a };',
|
|
16
|
-
].join('\n');
|
|
17
|
-
expect(lines(parseDiff(diff))).toEqual({ 'src/a.ts': [2, 3] });
|
|
18
|
-
});
|
|
19
|
-
it('handles several files and hunks', () => {
|
|
20
|
-
const diff = [
|
|
21
|
-
'+++ b/a.ts', '@@ -10,2 +10,3 @@', ' x', '+y', ' z',
|
|
22
|
-
'+++ b/b.ts', '@@ -1 +1 @@', '-old', '+new',
|
|
23
|
-
].join('\n');
|
|
24
|
-
expect(lines(parseDiff(diff))).toEqual({ 'a.ts': [11], 'b.ts': [1] });
|
|
25
|
-
});
|
|
26
|
-
it('does not mistake content lines starting with +++ or --- for headers', () => {
|
|
27
|
-
const diff = ['+++ b/a.ts', '@@ -1,2 +1,3 @@', ' let i = 0;', '+++i;', '---j;', ' done();'].join('\n');
|
|
28
|
-
expect(lines(parseDiff(diff))).toEqual({ 'a.ts': [2] });
|
|
29
|
-
});
|
|
30
|
-
it('does not count "No newline at end of file" as a line', () => {
|
|
31
|
-
const diff = ['+++ b/a.ts', '@@ -1,1 +1,2 @@', ' a', '\', '+b'].join('\n');
|
|
32
|
-
expect(lines(parseDiff(diff))).toEqual({ 'a.ts': [2] });
|
|
33
|
-
});
|
|
34
|
-
it('ignores deleted files', () => {
|
|
35
|
-
const diff = ['diff --git a/gone.ts b/gone.ts', '--- a/gone.ts', '+++ /dev/null', '@@ -1,2 +0,0 @@', '-a', '-b'].join('\n');
|
|
36
|
-
expect(parseDiff(diff)).toEqual({});
|
|
37
|
-
});
|
|
38
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,40 +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 { FileScanner } from './scanner.js';
|
|
6
|
-
import { directoriesAsGlobs, withDirectoryForms } from './glob-paths.js';
|
|
7
|
-
let root;
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
root = fs.mkdtempSync(path.join(os.tmpdir(), 'glob-paths-'));
|
|
10
|
-
fs.mkdirSync(path.join(root, 'src'));
|
|
11
|
-
fs.mkdirSync(path.join(root, 'legacy'));
|
|
12
|
-
fs.writeFileSync(path.join(root, 'src', 'a.ts'), 'export const a = 1;\n');
|
|
13
|
-
fs.writeFileSync(path.join(root, 'legacy', 'old.ts'), 'export const b = 2;\n');
|
|
14
|
-
});
|
|
15
|
-
afterEach(() => fs.rmSync(root, { recursive: true, force: true }));
|
|
16
|
-
describe('withDirectoryForms', () => {
|
|
17
|
-
it('adds the directory form to plain paths and leaves globs alone', () => {
|
|
18
|
-
expect(withDirectoryForms(['legacy', 'build/', '.git/**', '**/*.min.js'])).toEqual(['legacy', 'legacy/**', 'build/', 'build/**', '.git/**', '**/*.min.js']);
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
describe('directoriesAsGlobs', () => {
|
|
22
|
-
it('turns a directory into dir/** and leaves files, missing paths and globs alone', () => {
|
|
23
|
-
expect(directoriesAsGlobs(root, ['src', 'src/a.ts', 'missing', '**/*.ts'])).toEqual(['src/**', 'src/a.ts', 'missing', '**/*.ts']);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
describe('FileScanner in a git worktree', () => {
|
|
27
|
-
it('scans when .git is a file and the config ignores .git/**', async () => {
|
|
28
|
-
fs.writeFileSync(path.join(root, '.git'), 'gitdir: /elsewhere/.git/worktrees/x\n');
|
|
29
|
-
const files = await FileScanner.findFiles({ cwd: root, patterns: ['**/*.ts'], ignore: ['.git/**'] });
|
|
30
|
-
expect(files.sort()).toEqual(['legacy/old.ts', 'src/a.ts']);
|
|
31
|
-
});
|
|
32
|
-
it('finds a file or a whole directory named by a plain path', async () => {
|
|
33
|
-
expect(await FileScanner.findFiles({ cwd: root, patterns: ['src/a.ts'] })).toEqual(['src/a.ts']);
|
|
34
|
-
expect(await FileScanner.findFiles({ cwd: root, patterns: ['legacy'] })).toEqual(['legacy/old.ts']);
|
|
35
|
-
});
|
|
36
|
-
it('still ignores a whole directory named by a plain path', async () => {
|
|
37
|
-
const files = await FileScanner.findFiles({ cwd: root, patterns: ['**/*.ts'], ignore: ['legacy'] });
|
|
38
|
-
expect(files).toEqual(['src/a.ts']);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { execFileSync } from 'child_process';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
6
|
-
import { applyProfile, hostPath, profileFor, profileMismatch } from './profile.js';
|
|
7
|
-
let root;
|
|
8
|
-
const saved = { ...process.env };
|
|
9
|
-
const repo = (rel, remote) => {
|
|
10
|
-
const dir = path.join(root, rel);
|
|
11
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
12
|
-
execFileSync('git', ['init', '-q'], { cwd: dir });
|
|
13
|
-
if (remote)
|
|
14
|
-
execFileSync('git', ['remote', 'add', 'origin', remote], { cwd: dir });
|
|
15
|
-
return dir;
|
|
16
|
-
};
|
|
17
|
-
beforeEach(() => { root = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'profiles-'))); });
|
|
18
|
-
afterEach(() => { process.env = { ...saved }; fs.rmSync(root, { recursive: true, force: true }); });
|
|
19
|
-
describe('profiles', () => {
|
|
20
|
-
it('matches a repository by path prefix or by origin remote, first match winning', () => {
|
|
21
|
-
const byPath = repo('work/acme/api');
|
|
22
|
-
const byRemote = repo('elsewhere/web', 'git@github.com:Acme/web.git');
|
|
23
|
-
const other = repo('personal/tool', 'https://github.com/me/tool');
|
|
24
|
-
const profiles = [
|
|
25
|
-
{ name: 'acme-path', match: [path.join(root, 'work/acme')] },
|
|
26
|
-
{ name: 'acme-remote', match: ['github.com/acme/*'] },
|
|
27
|
-
];
|
|
28
|
-
expect(profileFor(path.join(byPath, 'src'), profiles)?.name).toBe('acme-path');
|
|
29
|
-
expect(profileFor(byRemote, profiles)?.name).toBe('acme-remote');
|
|
30
|
-
expect(profileFor(other, profiles)).toBeUndefined();
|
|
31
|
-
expect(profileFor(path.join(root, 'work/acme-evil'), profiles)).toBeUndefined();
|
|
32
|
-
expect(hostPath('https://GitHub.com/Acme/web.git/')).toBe('github.com/acme/web');
|
|
33
|
-
});
|
|
34
|
-
it("applies the profile's home and team, clearing team settings inherited from the shell", () => {
|
|
35
|
-
const dir = repo('work/acme/api');
|
|
36
|
-
delete process.env.RIGOUR_HOME; // no home of your own: the profile chooses it
|
|
37
|
-
process.env.RIGOUR_TEAM_DATABASE_URL = 'postgres://other-org';
|
|
38
|
-
process.env.RIGOUR_ORGANIZATION_ID = 'other-org';
|
|
39
|
-
applyProfile(dir, [{ name: 'acme', match: [path.join(root, 'work/acme')], home: path.join(root, 'acme-home'), githubAccount: 'acme-dev' }]);
|
|
40
|
-
expect(process.env.RIGOUR_HOME).toBe(path.join(root, 'acme-home'));
|
|
41
|
-
expect(process.env.RIGOUR_PROFILE).toBe('acme');
|
|
42
|
-
expect(process.env.RIGOUR_GITHUB_ACCOUNT).toBe('acme-dev');
|
|
43
|
-
expect(process.env.RIGOUR_TEAM_DATABASE_URL).toBeUndefined();
|
|
44
|
-
expect(process.env.RIGOUR_ORGANIZATION_ID).toBeUndefined();
|
|
45
|
-
applyProfile(dir, [{ name: 'acme', match: [dir], team: { organization: 'acme', team: 'web', actor: 'dev', repositories: ['github.com/acme/*'], databaseUrlCommand: 'echo url' } }]);
|
|
46
|
-
expect([process.env.RIGOUR_ORGANIZATION_ID, process.env.RIGOUR_TEAM_REPOSITORIES, process.env.RIGOUR_TEAM_DATABASE_URL_COMMAND]).toEqual(['acme', 'github.com/acme/*', 'echo url']);
|
|
47
|
-
});
|
|
48
|
-
it('leaves a RIGOUR_HOME you set yourself alone: no profile replaces it or brings its team', () => {
|
|
49
|
-
const dir = repo('work/acme/api');
|
|
50
|
-
delete process.env.RIGOUR_PROFILE;
|
|
51
|
-
process.env.RIGOUR_HOME = path.join(root, 'isolated');
|
|
52
|
-
const applied = applyProfile(dir, [{ name: 'acme', match: [path.join(root, 'work/acme')], home: path.join(root, 'acme-home'), team: { organization: 'acme', team: 'web', actor: 'dev' } }]);
|
|
53
|
-
expect(applied).toBeUndefined();
|
|
54
|
-
expect(process.env.RIGOUR_HOME).toBe(path.join(root, 'isolated'));
|
|
55
|
-
expect(process.env.RIGOUR_ORGANIZATION_ID).toBeUndefined();
|
|
56
|
-
});
|
|
57
|
-
it('refuses to act for a repository of another profile than the one the process started with', () => {
|
|
58
|
-
const acme = repo('work/acme/api');
|
|
59
|
-
const mine = repo('personal/tool');
|
|
60
|
-
const profiles = [{ name: 'acme', match: [path.join(root, 'work/acme')] }];
|
|
61
|
-
delete process.env.RIGOUR_PROFILE;
|
|
62
|
-
expect(profileMismatch(mine, profiles)).toBeNull();
|
|
63
|
-
expect(profileMismatch(acme, profiles)).toContain('belongs to the Rigour profile "acme"');
|
|
64
|
-
process.env.RIGOUR_PROFILE = 'acme';
|
|
65
|
-
expect(profileMismatch(acme, profiles)).toBeNull();
|
|
66
|
-
expect(profileMismatch(mine, profiles)).toContain('started as "acme"');
|
|
67
|
-
});
|
|
68
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { FileScanner } from './scanner.js';
|
|
3
|
-
import { globby } from 'globby';
|
|
4
|
-
vi.mock('globby', async (importOriginal) => ({
|
|
5
|
-
isDynamicPattern: (await importOriginal()).isDynamicPattern,
|
|
6
|
-
globby: vi.fn(async () => []),
|
|
7
|
-
}));
|
|
8
|
-
describe('FileScanner', () => {
|
|
9
|
-
it('returns files in a fixed order, whatever order the walk found them in', async () => {
|
|
10
|
-
vi.mocked(globby).mockResolvedValueOnce(['src/z.ts', 'scripts/a.ts', 'src/a.ts']);
|
|
11
|
-
expect(await FileScanner.findFiles({ cwd: '/test' })).toEqual(['scripts/a.ts', 'src/a.ts', 'src/z.ts']);
|
|
12
|
-
});
|
|
13
|
-
it('should merge default ignores with user ignores', async () => {
|
|
14
|
-
const options = {
|
|
15
|
-
cwd: '/test',
|
|
16
|
-
ignore: ['custom-ignore']
|
|
17
|
-
};
|
|
18
|
-
await FileScanner.findFiles(options);
|
|
19
|
-
const call = vi.mocked(globby).mock.calls.at(-1);
|
|
20
|
-
const ignore = call[1].ignore;
|
|
21
|
-
expect(ignore).toContain('**/node_modules/**');
|
|
22
|
-
expect(ignore).toContain('custom-ignore');
|
|
23
|
-
});
|
|
24
|
-
it('should normalize Windows paths to forward slashes', async () => {
|
|
25
|
-
const platform = Object.getOwnPropertyDescriptor(process, 'platform');
|
|
26
|
-
Object.defineProperty(process, 'platform', { value: 'win32' });
|
|
27
|
-
try {
|
|
28
|
-
await FileScanner.findFiles({ cwd: 'C:\\test\\path', patterns: ['**\\*.ts', 'app\\\\[key\\]\\route.ts'] });
|
|
29
|
-
}
|
|
30
|
-
finally {
|
|
31
|
-
Object.defineProperty(process, 'platform', platform);
|
|
32
|
-
}
|
|
33
|
-
const call = vi.mocked(globby).mock.calls.at(-1);
|
|
34
|
-
expect(call[0]).toEqual(['**/*.ts', 'app/\\[key\\]/route.ts']);
|
|
35
|
-
expect(call[1]?.cwd).toBe('C:/test/path');
|
|
36
|
-
});
|
|
37
|
-
it('should keep glob escapes on POSIX', async () => {
|
|
38
|
-
const platform = Object.getOwnPropertyDescriptor(process, 'platform');
|
|
39
|
-
Object.defineProperty(process, 'platform', { value: 'linux' });
|
|
40
|
-
try {
|
|
41
|
-
await FileScanner.findFiles({ cwd: '/repo', patterns: ['src/app/\\[key\\]/route.ts'] });
|
|
42
|
-
}
|
|
43
|
-
finally {
|
|
44
|
-
Object.defineProperty(process, 'platform', platform);
|
|
45
|
-
}
|
|
46
|
-
expect(vi.mocked(globby).mock.calls.at(-1)[0]).toEqual(['src/app/\\[key\\]/route.ts']);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/utils/scope.test.js
DELETED
|
@@ -1,38 +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 { isScoped, normalizeScopePatterns, resolveScopedFiles, touchesScope } from './scope.js';
|
|
6
|
-
describe('scope', () => {
|
|
7
|
-
let cwd;
|
|
8
|
-
beforeEach(async () => {
|
|
9
|
-
cwd = await fs.mkdtemp(path.join(os.tmpdir(), 'rigour-scope-'));
|
|
10
|
-
await fs.outputFile(path.join(cwd, 'src/app/[key]/route.ts'), 'export {}');
|
|
11
|
-
await fs.outputFile(path.join(cwd, 'src/app/k/route.ts'), 'export {}');
|
|
12
|
-
await fs.outputFile(path.join(cwd, 'src/lib/a.ts'), 'export {}');
|
|
13
|
-
});
|
|
14
|
-
afterEach(async () => { await fs.remove(cwd); });
|
|
15
|
-
it('turns absolute, relative and directory inputs into cwd-relative patterns', async () => {
|
|
16
|
-
const patterns = await normalizeScopePatterns(cwd, [path.join(cwd, 'src/lib/a.ts'), './src/lib', 'src/lib/a.ts']);
|
|
17
|
-
expect(patterns).toEqual(['src/lib/a.ts', 'src/lib/**/*', 'src/lib/a.ts']);
|
|
18
|
-
});
|
|
19
|
-
it('escapes glob characters so a Next.js [key] route matches only itself', async () => {
|
|
20
|
-
const patterns = await normalizeScopePatterns(cwd, ['src/app/[key]/route.ts']);
|
|
21
|
-
expect([...await resolveScopedFiles(cwd, patterns)]).toEqual(['src/app/[key]/route.ts']);
|
|
22
|
-
});
|
|
23
|
-
it('keeps a non-existent input as the glob the user wrote', async () => {
|
|
24
|
-
expect(await normalizeScopePatterns(cwd, ['src/**/*.ts'])).toEqual(['src/**/*.ts']);
|
|
25
|
-
});
|
|
26
|
-
it('treats the repo root as everything', async () => {
|
|
27
|
-
expect(await normalizeScopePatterns(cwd, ['.'])).toEqual(['**/*']);
|
|
28
|
-
});
|
|
29
|
-
it('detects scoped runs and scope membership', () => {
|
|
30
|
-
expect(isScoped(undefined)).toBe(false);
|
|
31
|
-
expect(isScoped([])).toBe(false);
|
|
32
|
-
expect(isScoped(['a.ts'])).toBe(true);
|
|
33
|
-
const scoped = new Set(['src/lib/a.ts']);
|
|
34
|
-
expect(touchesScope(['./src/lib/a.ts', 'b.ts'], scoped)).toBe(true);
|
|
35
|
-
expect(touchesScope(['b.ts'], scoped)).toBe(false);
|
|
36
|
-
expect(touchesScope(undefined, scoped)).toBe(false);
|
|
37
|
-
});
|
|
38
|
-
});
|