@code-rag/core 0.1.0 → 0.1.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/package.json +4 -4
- package/dist/auth/audit-log.js.map +0 -1
- package/dist/auth/audit-log.test.d.ts +0 -1
- package/dist/auth/audit-log.test.js +0 -261
- package/dist/auth/audit-log.test.js.map +0 -1
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/oidc-provider.js.map +0 -1
- package/dist/auth/oidc-provider.test.d.ts +0 -1
- package/dist/auth/oidc-provider.test.js +0 -520
- package/dist/auth/oidc-provider.test.js.map +0 -1
- package/dist/auth/rbac.js.map +0 -1
- package/dist/auth/rbac.test.d.ts +0 -1
- package/dist/auth/rbac.test.js +0 -224
- package/dist/auth/rbac.test.js.map +0 -1
- package/dist/auth/saml-provider.js.map +0 -1
- package/dist/auth/saml-provider.test.d.ts +0 -1
- package/dist/auth/saml-provider.test.js +0 -422
- package/dist/auth/saml-provider.test.js.map +0 -1
- package/dist/auth/types.js.map +0 -1
- package/dist/auth/types.test.d.ts +0 -1
- package/dist/auth/types.test.js +0 -147
- package/dist/auth/types.test.js.map +0 -1
- package/dist/backlog/ab-reference-scanner.js.map +0 -1
- package/dist/backlog/ab-reference-scanner.test.d.ts +0 -1
- package/dist/backlog/ab-reference-scanner.test.js +0 -83
- package/dist/backlog/ab-reference-scanner.test.js.map +0 -1
- package/dist/backlog/azure-devops-provider.js.map +0 -1
- package/dist/backlog/backlog-provider.js.map +0 -1
- package/dist/backlog/backlog-provider.test.d.ts +0 -1
- package/dist/backlog/backlog-provider.test.js +0 -426
- package/dist/backlog/backlog-provider.test.js.map +0 -1
- package/dist/backlog/clickup-provider.js.map +0 -1
- package/dist/backlog/clickup-provider.test.d.ts +0 -1
- package/dist/backlog/clickup-provider.test.js +0 -426
- package/dist/backlog/clickup-provider.test.js.map +0 -1
- package/dist/backlog/clickup-reference-scanner.js.map +0 -1
- package/dist/backlog/clickup-reference-scanner.test.d.ts +0 -1
- package/dist/backlog/clickup-reference-scanner.test.js +0 -92
- package/dist/backlog/clickup-reference-scanner.test.js.map +0 -1
- package/dist/backlog/code-linker.js.map +0 -1
- package/dist/backlog/code-linker.test.d.ts +0 -1
- package/dist/backlog/code-linker.test.js +0 -325
- package/dist/backlog/code-linker.test.js.map +0 -1
- package/dist/backlog/index.js.map +0 -1
- package/dist/backlog/jira-provider.js.map +0 -1
- package/dist/backlog/jira-provider.test.d.ts +0 -1
- package/dist/backlog/jira-provider.test.js +0 -449
- package/dist/backlog/jira-provider.test.js.map +0 -1
- package/dist/backlog/jira-reference-scanner.js.map +0 -1
- package/dist/backlog/jira-reference-scanner.test.d.ts +0 -1
- package/dist/backlog/jira-reference-scanner.test.js +0 -127
- package/dist/backlog/jira-reference-scanner.test.js.map +0 -1
- package/dist/backlog/types.js.map +0 -1
- package/dist/chunker/ast-chunker.js.map +0 -1
- package/dist/chunker/ast-chunker.test.d.ts +0 -1
- package/dist/chunker/ast-chunker.test.js +0 -391
- package/dist/chunker/ast-chunker.test.js.map +0 -1
- package/dist/chunker/chunker.js.map +0 -1
- package/dist/chunker/index.js.map +0 -1
- package/dist/config/config-parser.js.map +0 -1
- package/dist/config/config-parser.test.d.ts +0 -1
- package/dist/config/config-parser.test.js +0 -699
- package/dist/config/config-parser.test.js.map +0 -1
- package/dist/docs/confluence-provider.js.map +0 -1
- package/dist/docs/confluence-provider.test.d.ts +0 -1
- package/dist/docs/confluence-provider.test.js +0 -765
- package/dist/docs/confluence-provider.test.js.map +0 -1
- package/dist/docs/index.js.map +0 -1
- package/dist/docs/sharepoint-provider.js.map +0 -1
- package/dist/docs/sharepoint-provider.test.d.ts +0 -1
- package/dist/docs/sharepoint-provider.test.js +0 -873
- package/dist/docs/sharepoint-provider.test.js.map +0 -1
- package/dist/embedding/bm25-index.js.map +0 -1
- package/dist/embedding/bm25-index.test.d.ts +0 -1
- package/dist/embedding/bm25-index.test.js +0 -289
- package/dist/embedding/bm25-index.test.js.map +0 -1
- package/dist/embedding/hybrid-search.js.map +0 -1
- package/dist/embedding/hybrid-search.test.d.ts +0 -1
- package/dist/embedding/hybrid-search.test.js +0 -266
- package/dist/embedding/hybrid-search.test.js.map +0 -1
- package/dist/embedding/index.js.map +0 -1
- package/dist/embedding/lancedb-store.js.map +0 -1
- package/dist/embedding/lancedb-store.test.d.ts +0 -1
- package/dist/embedding/lancedb-store.test.js +0 -268
- package/dist/embedding/lancedb-store.test.js.map +0 -1
- package/dist/embedding/model-lifecycle-manager.js.map +0 -1
- package/dist/embedding/model-lifecycle-manager.test.d.ts +0 -1
- package/dist/embedding/model-lifecycle-manager.test.js +0 -642
- package/dist/embedding/model-lifecycle-manager.test.js.map +0 -1
- package/dist/embedding/ollama-embedding-provider.js.map +0 -1
- package/dist/embedding/ollama-embedding-provider.test.d.ts +0 -1
- package/dist/embedding/ollama-embedding-provider.test.js +0 -198
- package/dist/embedding/ollama-embedding-provider.test.js.map +0 -1
- package/dist/embedding/openai-compatible-embedding-provider.js.map +0 -1
- package/dist/embedding/openai-compatible-embedding-provider.test.d.ts +0 -1
- package/dist/embedding/openai-compatible-embedding-provider.test.js +0 -456
- package/dist/embedding/openai-compatible-embedding-provider.test.js.map +0 -1
- package/dist/embedding/qdrant-store.js.map +0 -1
- package/dist/embedding/qdrant-store.test.d.ts +0 -1
- package/dist/embedding/qdrant-store.test.js +0 -359
- package/dist/embedding/qdrant-store.test.js.map +0 -1
- package/dist/enrichment/index.js.map +0 -1
- package/dist/enrichment/nl-enricher.js.map +0 -1
- package/dist/enrichment/nl-enricher.test.d.ts +0 -1
- package/dist/enrichment/nl-enricher.test.js +0 -154
- package/dist/enrichment/nl-enricher.test.js.map +0 -1
- package/dist/enrichment/ollama-client.js.map +0 -1
- package/dist/enrichment/ollama-client.test.d.ts +0 -1
- package/dist/enrichment/ollama-client.test.js +0 -129
- package/dist/enrichment/ollama-client.test.js.map +0 -1
- package/dist/git/git-client.js.map +0 -1
- package/dist/git/git-client.test.d.ts +0 -1
- package/dist/git/git-client.test.js +0 -200
- package/dist/git/git-client.test.js.map +0 -1
- package/dist/git/ignore-filter.js.map +0 -1
- package/dist/git/ignore-filter.test.d.ts +0 -1
- package/dist/git/ignore-filter.test.js +0 -87
- package/dist/git/ignore-filter.test.js.map +0 -1
- package/dist/git/index.js.map +0 -1
- package/dist/git/simple-git-client.js.map +0 -1
- package/dist/graph/cross-repo-resolver.js.map +0 -1
- package/dist/graph/cross-repo-resolver.test.d.ts +0 -1
- package/dist/graph/cross-repo-resolver.test.js +0 -548
- package/dist/graph/cross-repo-resolver.test.js.map +0 -1
- package/dist/graph/dependency-graph.js.map +0 -1
- package/dist/graph/dependency-graph.test.d.ts +0 -1
- package/dist/graph/dependency-graph.test.js +0 -276
- package/dist/graph/dependency-graph.test.js.map +0 -1
- package/dist/graph/graph-builder.js.map +0 -1
- package/dist/graph/graph-builder.test.d.ts +0 -1
- package/dist/graph/graph-builder.test.js +0 -178
- package/dist/graph/graph-builder.test.js.map +0 -1
- package/dist/graph/import-resolver.js.map +0 -1
- package/dist/graph/import-resolver.test.d.ts +0 -1
- package/dist/graph/import-resolver.test.js +0 -282
- package/dist/graph/import-resolver.test.js.map +0 -1
- package/dist/graph/index.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/indexer/file-scanner.js.map +0 -1
- package/dist/indexer/file-scanner.test.d.ts +0 -1
- package/dist/indexer/file-scanner.test.js +0 -110
- package/dist/indexer/file-scanner.test.js.map +0 -1
- package/dist/indexer/incremental-indexer.js.map +0 -1
- package/dist/indexer/incremental-indexer.test.d.ts +0 -1
- package/dist/indexer/incremental-indexer.test.js +0 -266
- package/dist/indexer/incremental-indexer.test.js.map +0 -1
- package/dist/indexer/index-check.js.map +0 -1
- package/dist/indexer/index-check.test.d.ts +0 -1
- package/dist/indexer/index-check.test.js +0 -100
- package/dist/indexer/index-check.test.js.map +0 -1
- package/dist/indexer/index-state.js.map +0 -1
- package/dist/indexer/index-state.test.d.ts +0 -1
- package/dist/indexer/index-state.test.js +0 -140
- package/dist/indexer/index-state.test.js.map +0 -1
- package/dist/indexer/index.js.map +0 -1
- package/dist/indexer/multi-repo-indexer.js.map +0 -1
- package/dist/indexer/multi-repo-indexer.test.d.ts +0 -1
- package/dist/indexer/multi-repo-indexer.test.js +0 -238
- package/dist/indexer/multi-repo-indexer.test.js.map +0 -1
- package/dist/parser/index.js.map +0 -1
- package/dist/parser/language-registry.js.map +0 -1
- package/dist/parser/language-registry.test.d.ts +0 -1
- package/dist/parser/language-registry.test.js +0 -225
- package/dist/parser/language-registry.test.js.map +0 -1
- package/dist/parser/markdown-parser.js.map +0 -1
- package/dist/parser/markdown-parser.test.d.ts +0 -1
- package/dist/parser/markdown-parser.test.js +0 -600
- package/dist/parser/markdown-parser.test.js.map +0 -1
- package/dist/parser/tree-sitter-parser.js.map +0 -1
- package/dist/retrieval/context-expander.js.map +0 -1
- package/dist/retrieval/context-expander.test.d.ts +0 -1
- package/dist/retrieval/context-expander.test.js +0 -339
- package/dist/retrieval/context-expander.test.js.map +0 -1
- package/dist/retrieval/cross-encoder-reranker.js.map +0 -1
- package/dist/retrieval/cross-encoder-reranker.test.d.ts +0 -1
- package/dist/retrieval/cross-encoder-reranker.test.js +0 -305
- package/dist/retrieval/cross-encoder-reranker.test.js.map +0 -1
- package/dist/retrieval/index.js.map +0 -1
- package/dist/retrieval/query-analyzer.js.map +0 -1
- package/dist/retrieval/query-analyzer.test.d.ts +0 -1
- package/dist/retrieval/query-analyzer.test.js +0 -236
- package/dist/retrieval/query-analyzer.test.js.map +0 -1
- package/dist/retrieval/token-budget.js.map +0 -1
- package/dist/retrieval/token-budget.test.d.ts +0 -1
- package/dist/retrieval/token-budget.test.js +0 -404
- package/dist/retrieval/token-budget.test.js.map +0 -1
- package/dist/storage/azure-blob-provider.js.map +0 -1
- package/dist/storage/azure-blob-provider.test.d.ts +0 -1
- package/dist/storage/azure-blob-provider.test.js +0 -250
- package/dist/storage/azure-blob-provider.test.js.map +0 -1
- package/dist/storage/gcs-provider.js.map +0 -1
- package/dist/storage/gcs-provider.test.d.ts +0 -1
- package/dist/storage/gcs-provider.test.js +0 -299
- package/dist/storage/gcs-provider.test.js.map +0 -1
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/s3-provider.js.map +0 -1
- package/dist/storage/s3-provider.test.d.ts +0 -1
- package/dist/storage/s3-provider.test.js +0 -329
- package/dist/storage/s3-provider.test.js.map +0 -1
- package/dist/storage/types.js.map +0 -1
- package/dist/types/chunk.js.map +0 -1
- package/dist/types/config.js.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/types/provider.js.map +0 -1
- package/dist/types/search.js.map +0 -1
|
@@ -1,699 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { tmpdir } from 'node:os';
|
|
5
|
-
import { loadConfig, ConfigError, interpolateEnvVars } from './config-parser.js';
|
|
6
|
-
describe('loadConfig', () => {
|
|
7
|
-
let tempDir;
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
tempDir = mkdtempSync(join(tmpdir(), 'coderag-test-'));
|
|
10
|
-
});
|
|
11
|
-
afterEach(() => {
|
|
12
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
13
|
-
});
|
|
14
|
-
it('should load a valid config file', async () => {
|
|
15
|
-
const configContent = `
|
|
16
|
-
version: "1"
|
|
17
|
-
project:
|
|
18
|
-
name: test-project
|
|
19
|
-
languages: auto
|
|
20
|
-
ingestion:
|
|
21
|
-
maxTokensPerChunk: 256
|
|
22
|
-
exclude:
|
|
23
|
-
- node_modules
|
|
24
|
-
- dist
|
|
25
|
-
embedding:
|
|
26
|
-
provider: ollama
|
|
27
|
-
model: nomic-embed-text
|
|
28
|
-
dimensions: 768
|
|
29
|
-
llm:
|
|
30
|
-
provider: ollama
|
|
31
|
-
model: "qwen2.5-coder:7b"
|
|
32
|
-
search:
|
|
33
|
-
topK: 5
|
|
34
|
-
vectorWeight: 0.6
|
|
35
|
-
bm25Weight: 0.4
|
|
36
|
-
storage:
|
|
37
|
-
path: .coderag
|
|
38
|
-
`;
|
|
39
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
40
|
-
const result = await loadConfig(tempDir);
|
|
41
|
-
expect(result.isOk()).toBe(true);
|
|
42
|
-
if (result.isOk()) {
|
|
43
|
-
expect(result.value.project.name).toBe('test-project');
|
|
44
|
-
expect(result.value.ingestion.maxTokensPerChunk).toBe(256);
|
|
45
|
-
expect(result.value.search.topK).toBe(5);
|
|
46
|
-
expect(result.value.search.vectorWeight).toBe(0.6);
|
|
47
|
-
expect(result.value.search.bm25Weight).toBe(0.4);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
it('should accept languages as an array of strings', async () => {
|
|
51
|
-
const configContent = `
|
|
52
|
-
version: "1"
|
|
53
|
-
project:
|
|
54
|
-
name: multi-lang
|
|
55
|
-
languages:
|
|
56
|
-
- typescript
|
|
57
|
-
- python
|
|
58
|
-
`;
|
|
59
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
60
|
-
const result = await loadConfig(tempDir);
|
|
61
|
-
expect(result.isOk()).toBe(true);
|
|
62
|
-
if (result.isOk()) {
|
|
63
|
-
expect(result.value.project.languages).toEqual(['typescript', 'python']);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
it('should apply defaults for missing fields', async () => {
|
|
67
|
-
const configContent = `
|
|
68
|
-
version: "1"
|
|
69
|
-
project:
|
|
70
|
-
name: minimal-project
|
|
71
|
-
`;
|
|
72
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
73
|
-
const result = await loadConfig(tempDir);
|
|
74
|
-
expect(result.isOk()).toBe(true);
|
|
75
|
-
if (result.isOk()) {
|
|
76
|
-
expect(result.value.project.name).toBe('minimal-project');
|
|
77
|
-
expect(result.value.project.languages).toBe('auto');
|
|
78
|
-
expect(result.value.ingestion.maxTokensPerChunk).toBe(512);
|
|
79
|
-
expect(result.value.embedding.provider).toBe('auto');
|
|
80
|
-
expect(result.value.embedding.model).toBe('nomic-embed-text');
|
|
81
|
-
expect(result.value.embedding.dimensions).toBe(768);
|
|
82
|
-
expect(result.value.embedding.autoStart).toBe(true);
|
|
83
|
-
expect(result.value.embedding.autoStop).toBe(false);
|
|
84
|
-
expect(result.value.embedding.docker).toEqual({ image: 'ollama/ollama', gpu: 'auto' });
|
|
85
|
-
expect(result.value.llm.provider).toBe('ollama');
|
|
86
|
-
expect(result.value.search.topK).toBe(10);
|
|
87
|
-
expect(result.value.storage.path).toBe('.coderag');
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
it('should return error on invalid YAML', async () => {
|
|
91
|
-
const invalidYaml = `
|
|
92
|
-
version: "1"
|
|
93
|
-
project:
|
|
94
|
-
name: test
|
|
95
|
-
invalid: [unclosed bracket
|
|
96
|
-
`;
|
|
97
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), invalidYaml);
|
|
98
|
-
const result = await loadConfig(tempDir);
|
|
99
|
-
expect(result.isErr()).toBe(true);
|
|
100
|
-
if (result.isErr()) {
|
|
101
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
102
|
-
expect(result.error.message).toContain('Invalid YAML');
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
it('should return error when file does not exist', async () => {
|
|
106
|
-
const result = await loadConfig(tempDir);
|
|
107
|
-
expect(result.isErr()).toBe(true);
|
|
108
|
-
if (result.isErr()) {
|
|
109
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
110
|
-
expect(result.error.message).toContain('Config file not found');
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
it('should return error when config file contains a scalar value', async () => {
|
|
114
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), 'just a string');
|
|
115
|
-
const result = await loadConfig(tempDir);
|
|
116
|
-
expect(result.isErr()).toBe(true);
|
|
117
|
-
if (result.isErr()) {
|
|
118
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
119
|
-
expect(result.error.message).toContain('empty or not a valid YAML object');
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
it('should return error when config file is empty', async () => {
|
|
123
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), '');
|
|
124
|
-
const result = await loadConfig(tempDir);
|
|
125
|
-
expect(result.isErr()).toBe(true);
|
|
126
|
-
if (result.isErr()) {
|
|
127
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
128
|
-
expect(result.error.message).toContain('empty or not a valid YAML object');
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
// --- Zod validation edge case tests ---
|
|
132
|
-
it('should return validation error for invalid dimensions (negative)', async () => {
|
|
133
|
-
const configContent = `
|
|
134
|
-
version: "1"
|
|
135
|
-
project:
|
|
136
|
-
name: bad-dims
|
|
137
|
-
embedding:
|
|
138
|
-
provider: ollama
|
|
139
|
-
model: nomic-embed-text
|
|
140
|
-
dimensions: -5
|
|
141
|
-
`;
|
|
142
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
143
|
-
const result = await loadConfig(tempDir);
|
|
144
|
-
expect(result.isErr()).toBe(true);
|
|
145
|
-
if (result.isErr()) {
|
|
146
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
147
|
-
expect(result.error.message).toContain('Config validation failed');
|
|
148
|
-
expect(result.error.message).toContain('dimensions');
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
it('should return validation error for non-integer dimensions', async () => {
|
|
152
|
-
const configContent = `
|
|
153
|
-
version: "1"
|
|
154
|
-
project:
|
|
155
|
-
name: bad-dims
|
|
156
|
-
embedding:
|
|
157
|
-
provider: ollama
|
|
158
|
-
model: nomic-embed-text
|
|
159
|
-
dimensions: 768.5
|
|
160
|
-
`;
|
|
161
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
162
|
-
const result = await loadConfig(tempDir);
|
|
163
|
-
expect(result.isErr()).toBe(true);
|
|
164
|
-
if (result.isErr()) {
|
|
165
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
166
|
-
expect(result.error.message).toContain('Config validation failed');
|
|
167
|
-
expect(result.error.message).toContain('dimensions');
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
it('should return validation error for empty embedding provider', async () => {
|
|
171
|
-
const configContent = `
|
|
172
|
-
version: "1"
|
|
173
|
-
project:
|
|
174
|
-
name: no-provider
|
|
175
|
-
embedding:
|
|
176
|
-
provider: ""
|
|
177
|
-
model: nomic-embed-text
|
|
178
|
-
dimensions: 768
|
|
179
|
-
`;
|
|
180
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
181
|
-
const result = await loadConfig(tempDir);
|
|
182
|
-
expect(result.isErr()).toBe(true);
|
|
183
|
-
if (result.isErr()) {
|
|
184
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
185
|
-
expect(result.error.message).toContain('Config validation failed');
|
|
186
|
-
expect(result.error.message).toContain('provider');
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
it('should return validation error for empty LLM provider', async () => {
|
|
190
|
-
const configContent = `
|
|
191
|
-
version: "1"
|
|
192
|
-
project:
|
|
193
|
-
name: no-llm
|
|
194
|
-
llm:
|
|
195
|
-
provider: ""
|
|
196
|
-
model: "qwen2.5-coder:7b"
|
|
197
|
-
`;
|
|
198
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
199
|
-
const result = await loadConfig(tempDir);
|
|
200
|
-
expect(result.isErr()).toBe(true);
|
|
201
|
-
if (result.isErr()) {
|
|
202
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
203
|
-
expect(result.error.message).toContain('Config validation failed');
|
|
204
|
-
expect(result.error.message).toContain('provider');
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
it('should return validation error for negative topK', async () => {
|
|
208
|
-
const configContent = `
|
|
209
|
-
version: "1"
|
|
210
|
-
project:
|
|
211
|
-
name: bad-topk
|
|
212
|
-
search:
|
|
213
|
-
topK: -3
|
|
214
|
-
vectorWeight: 0.7
|
|
215
|
-
bm25Weight: 0.3
|
|
216
|
-
`;
|
|
217
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
218
|
-
const result = await loadConfig(tempDir);
|
|
219
|
-
expect(result.isErr()).toBe(true);
|
|
220
|
-
if (result.isErr()) {
|
|
221
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
222
|
-
expect(result.error.message).toContain('Config validation failed');
|
|
223
|
-
expect(result.error.message).toContain('topK');
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
it('should return validation error for vectorWeight out of range', async () => {
|
|
227
|
-
const configContent = `
|
|
228
|
-
version: "1"
|
|
229
|
-
project:
|
|
230
|
-
name: bad-weight
|
|
231
|
-
search:
|
|
232
|
-
topK: 10
|
|
233
|
-
vectorWeight: 1.5
|
|
234
|
-
bm25Weight: 0.3
|
|
235
|
-
`;
|
|
236
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
237
|
-
const result = await loadConfig(tempDir);
|
|
238
|
-
expect(result.isErr()).toBe(true);
|
|
239
|
-
if (result.isErr()) {
|
|
240
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
241
|
-
expect(result.error.message).toContain('Config validation failed');
|
|
242
|
-
expect(result.error.message).toContain('vectorWeight');
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
it('should return validation error for bm25Weight out of range', async () => {
|
|
246
|
-
const configContent = `
|
|
247
|
-
version: "1"
|
|
248
|
-
project:
|
|
249
|
-
name: bad-bm25
|
|
250
|
-
search:
|
|
251
|
-
topK: 10
|
|
252
|
-
vectorWeight: 0.7
|
|
253
|
-
bm25Weight: -0.1
|
|
254
|
-
`;
|
|
255
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
256
|
-
const result = await loadConfig(tempDir);
|
|
257
|
-
expect(result.isErr()).toBe(true);
|
|
258
|
-
if (result.isErr()) {
|
|
259
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
260
|
-
expect(result.error.message).toContain('Config validation failed');
|
|
261
|
-
expect(result.error.message).toContain('bm25Weight');
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
it('should return validation error for empty version string', async () => {
|
|
265
|
-
const configContent = `
|
|
266
|
-
version: ""
|
|
267
|
-
project:
|
|
268
|
-
name: no-version
|
|
269
|
-
`;
|
|
270
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
271
|
-
const result = await loadConfig(tempDir);
|
|
272
|
-
expect(result.isErr()).toBe(true);
|
|
273
|
-
if (result.isErr()) {
|
|
274
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
275
|
-
expect(result.error.message).toContain('Config validation failed');
|
|
276
|
-
expect(result.error.message).toContain('version');
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
it('should return validation error for empty project name', async () => {
|
|
280
|
-
const configContent = `
|
|
281
|
-
version: "1"
|
|
282
|
-
project:
|
|
283
|
-
name: ""
|
|
284
|
-
`;
|
|
285
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
286
|
-
const result = await loadConfig(tempDir);
|
|
287
|
-
expect(result.isErr()).toBe(true);
|
|
288
|
-
if (result.isErr()) {
|
|
289
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
290
|
-
expect(result.error.message).toContain('Config validation failed');
|
|
291
|
-
expect(result.error.message).toContain('name');
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
it('should return validation error for empty storage path', async () => {
|
|
295
|
-
const configContent = `
|
|
296
|
-
version: "1"
|
|
297
|
-
project:
|
|
298
|
-
name: no-path
|
|
299
|
-
storage:
|
|
300
|
-
path: ""
|
|
301
|
-
`;
|
|
302
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
303
|
-
const result = await loadConfig(tempDir);
|
|
304
|
-
expect(result.isErr()).toBe(true);
|
|
305
|
-
if (result.isErr()) {
|
|
306
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
307
|
-
expect(result.error.message).toContain('Config validation failed');
|
|
308
|
-
expect(result.error.message).toContain('path');
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
|
-
// --- Multi-repo config tests ---
|
|
312
|
-
it('should load a valid multi-repo config with repos array', async () => {
|
|
313
|
-
const configContent = `
|
|
314
|
-
version: "1"
|
|
315
|
-
project:
|
|
316
|
-
name: multi-repo
|
|
317
|
-
languages: auto
|
|
318
|
-
repos:
|
|
319
|
-
- path: /home/dev/repo-a
|
|
320
|
-
name: repo-a
|
|
321
|
-
languages:
|
|
322
|
-
- typescript
|
|
323
|
-
exclude:
|
|
324
|
-
- dist
|
|
325
|
-
- path: /home/dev/repo-b
|
|
326
|
-
name: repo-b
|
|
327
|
-
`;
|
|
328
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
329
|
-
const result = await loadConfig(tempDir);
|
|
330
|
-
expect(result.isOk()).toBe(true);
|
|
331
|
-
if (result.isOk()) {
|
|
332
|
-
expect(result.value.repos).toBeDefined();
|
|
333
|
-
expect(result.value.repos).toHaveLength(2);
|
|
334
|
-
expect(result.value.repos[0].path).toBe('/home/dev/repo-a');
|
|
335
|
-
expect(result.value.repos[0].name).toBe('repo-a');
|
|
336
|
-
expect(result.value.repos[0].languages).toEqual(['typescript']);
|
|
337
|
-
expect(result.value.repos[0].exclude).toEqual(['dist']);
|
|
338
|
-
expect(result.value.repos[1].path).toBe('/home/dev/repo-b');
|
|
339
|
-
expect(result.value.repos[1].name).toBe('repo-b');
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
it('should load config without repos field (single repo, backwards compatible)', async () => {
|
|
343
|
-
const configContent = `
|
|
344
|
-
version: "1"
|
|
345
|
-
project:
|
|
346
|
-
name: single-repo
|
|
347
|
-
languages: auto
|
|
348
|
-
`;
|
|
349
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
350
|
-
const result = await loadConfig(tempDir);
|
|
351
|
-
expect(result.isOk()).toBe(true);
|
|
352
|
-
if (result.isOk()) {
|
|
353
|
-
expect(result.value.repos).toBeUndefined();
|
|
354
|
-
expect(result.value.project.name).toBe('single-repo');
|
|
355
|
-
}
|
|
356
|
-
});
|
|
357
|
-
it('should return validation error for repo entry missing path', async () => {
|
|
358
|
-
const configContent = `
|
|
359
|
-
version: "1"
|
|
360
|
-
project:
|
|
361
|
-
name: bad-repo
|
|
362
|
-
repos:
|
|
363
|
-
- name: no-path-repo
|
|
364
|
-
`;
|
|
365
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
366
|
-
const result = await loadConfig(tempDir);
|
|
367
|
-
expect(result.isErr()).toBe(true);
|
|
368
|
-
if (result.isErr()) {
|
|
369
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
370
|
-
expect(result.error.message).toContain('Config validation failed');
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
it('should accept repo entries with per-repo language and exclude overrides', async () => {
|
|
374
|
-
const configContent = `
|
|
375
|
-
version: "1"
|
|
376
|
-
project:
|
|
377
|
-
name: overrides
|
|
378
|
-
repos:
|
|
379
|
-
- path: /repos/frontend
|
|
380
|
-
languages:
|
|
381
|
-
- typescript
|
|
382
|
-
- javascript
|
|
383
|
-
exclude:
|
|
384
|
-
- node_modules
|
|
385
|
-
- .next
|
|
386
|
-
- path: /repos/backend
|
|
387
|
-
languages:
|
|
388
|
-
- python
|
|
389
|
-
exclude:
|
|
390
|
-
- __pycache__
|
|
391
|
-
- .venv
|
|
392
|
-
`;
|
|
393
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
394
|
-
const result = await loadConfig(tempDir);
|
|
395
|
-
expect(result.isOk()).toBe(true);
|
|
396
|
-
if (result.isOk()) {
|
|
397
|
-
expect(result.value.repos).toHaveLength(2);
|
|
398
|
-
expect(result.value.repos[0].languages).toEqual(['typescript', 'javascript']);
|
|
399
|
-
expect(result.value.repos[0].exclude).toEqual(['node_modules', '.next']);
|
|
400
|
-
expect(result.value.repos[1].languages).toEqual(['python']);
|
|
401
|
-
expect(result.value.repos[1].exclude).toEqual(['__pycache__', '.venv']);
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
it('should return validation error for repo with empty path', async () => {
|
|
405
|
-
const configContent = `
|
|
406
|
-
version: "1"
|
|
407
|
-
project:
|
|
408
|
-
name: empty-path
|
|
409
|
-
repos:
|
|
410
|
-
- path: ""
|
|
411
|
-
`;
|
|
412
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
413
|
-
const result = await loadConfig(tempDir);
|
|
414
|
-
expect(result.isErr()).toBe(true);
|
|
415
|
-
if (result.isErr()) {
|
|
416
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
417
|
-
expect(result.error.message).toContain('Config validation failed');
|
|
418
|
-
expect(result.error.message).toContain('path');
|
|
419
|
-
}
|
|
420
|
-
});
|
|
421
|
-
it('should accept empty repos array', async () => {
|
|
422
|
-
const configContent = `
|
|
423
|
-
version: "1"
|
|
424
|
-
project:
|
|
425
|
-
name: empty-repos
|
|
426
|
-
repos: []
|
|
427
|
-
`;
|
|
428
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
429
|
-
const result = await loadConfig(tempDir);
|
|
430
|
-
expect(result.isOk()).toBe(true);
|
|
431
|
-
if (result.isOk()) {
|
|
432
|
-
expect(result.value.repos).toEqual([]);
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
it('should accept boundary values for weights (0 and 1)', async () => {
|
|
436
|
-
const configContent = `
|
|
437
|
-
version: "1"
|
|
438
|
-
project:
|
|
439
|
-
name: boundary-weights
|
|
440
|
-
search:
|
|
441
|
-
topK: 10
|
|
442
|
-
vectorWeight: 0
|
|
443
|
-
bm25Weight: 1
|
|
444
|
-
`;
|
|
445
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
446
|
-
const result = await loadConfig(tempDir);
|
|
447
|
-
expect(result.isOk()).toBe(true);
|
|
448
|
-
if (result.isOk()) {
|
|
449
|
-
expect(result.value.search.vectorWeight).toBe(0);
|
|
450
|
-
expect(result.value.search.bm25Weight).toBe(1);
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
it('should return validation error for zero topK', async () => {
|
|
454
|
-
const configContent = `
|
|
455
|
-
version: "1"
|
|
456
|
-
project:
|
|
457
|
-
name: zero-topk
|
|
458
|
-
search:
|
|
459
|
-
topK: 0
|
|
460
|
-
vectorWeight: 0.7
|
|
461
|
-
bm25Weight: 0.3
|
|
462
|
-
`;
|
|
463
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
464
|
-
const result = await loadConfig(tempDir);
|
|
465
|
-
expect(result.isErr()).toBe(true);
|
|
466
|
-
if (result.isErr()) {
|
|
467
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
468
|
-
expect(result.error.message).toContain('Config validation failed');
|
|
469
|
-
expect(result.error.message).toContain('topK');
|
|
470
|
-
}
|
|
471
|
-
});
|
|
472
|
-
});
|
|
473
|
-
describe('loadConfig — openaiCompatible embedding config', () => {
|
|
474
|
-
let tempDir;
|
|
475
|
-
beforeEach(() => {
|
|
476
|
-
tempDir = mkdtempSync(join(tmpdir(), 'coderag-openai-'));
|
|
477
|
-
});
|
|
478
|
-
afterEach(() => {
|
|
479
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
480
|
-
});
|
|
481
|
-
it('should load openaiCompatible config with camelCase keys', async () => {
|
|
482
|
-
const configContent = `
|
|
483
|
-
version: "1"
|
|
484
|
-
project:
|
|
485
|
-
name: openai-test
|
|
486
|
-
embedding:
|
|
487
|
-
provider: openai-compatible
|
|
488
|
-
model: text-embedding-3-small
|
|
489
|
-
dimensions: 1536
|
|
490
|
-
openaiCompatible:
|
|
491
|
-
baseUrl: https://api.openai.com/v1
|
|
492
|
-
apiKey: sk-test-key
|
|
493
|
-
maxBatchSize: 50
|
|
494
|
-
`;
|
|
495
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
496
|
-
const result = await loadConfig(tempDir);
|
|
497
|
-
expect(result.isOk()).toBe(true);
|
|
498
|
-
if (result.isOk()) {
|
|
499
|
-
expect(result.value.embedding.provider).toBe('openai-compatible');
|
|
500
|
-
expect(result.value.embedding.openaiCompatible).toBeDefined();
|
|
501
|
-
expect(result.value.embedding.openaiCompatible.baseUrl).toBe('https://api.openai.com/v1');
|
|
502
|
-
expect(result.value.embedding.openaiCompatible.apiKey).toBe('sk-test-key');
|
|
503
|
-
expect(result.value.embedding.openaiCompatible.maxBatchSize).toBe(50);
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
it('should load openaiCompatible config with snake_case YAML keys', async () => {
|
|
507
|
-
const configContent = `
|
|
508
|
-
version: "1"
|
|
509
|
-
project:
|
|
510
|
-
name: openai-snake
|
|
511
|
-
embedding:
|
|
512
|
-
provider: openai-compatible
|
|
513
|
-
model: nomic-embed-text
|
|
514
|
-
dimensions: 768
|
|
515
|
-
openai_compatible:
|
|
516
|
-
base_url: http://localhost:1234/v1
|
|
517
|
-
api_key: lm-studio-key
|
|
518
|
-
max_batch_size: 200
|
|
519
|
-
`;
|
|
520
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
521
|
-
const result = await loadConfig(tempDir);
|
|
522
|
-
expect(result.isOk()).toBe(true);
|
|
523
|
-
if (result.isOk()) {
|
|
524
|
-
expect(result.value.embedding.openaiCompatible).toBeDefined();
|
|
525
|
-
expect(result.value.embedding.openaiCompatible.baseUrl).toBe('http://localhost:1234/v1');
|
|
526
|
-
expect(result.value.embedding.openaiCompatible.apiKey).toBe('lm-studio-key');
|
|
527
|
-
expect(result.value.embedding.openaiCompatible.maxBatchSize).toBe(200);
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
it('should apply defaults for openaiCompatible when minimal config provided', async () => {
|
|
531
|
-
const configContent = `
|
|
532
|
-
version: "1"
|
|
533
|
-
project:
|
|
534
|
-
name: openai-defaults
|
|
535
|
-
embedding:
|
|
536
|
-
provider: openai-compatible
|
|
537
|
-
model: nomic-embed-text
|
|
538
|
-
dimensions: 768
|
|
539
|
-
openaiCompatible:
|
|
540
|
-
baseUrl: http://localhost:11434/v1
|
|
541
|
-
`;
|
|
542
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
543
|
-
const result = await loadConfig(tempDir);
|
|
544
|
-
expect(result.isOk()).toBe(true);
|
|
545
|
-
if (result.isOk()) {
|
|
546
|
-
expect(result.value.embedding.openaiCompatible).toBeDefined();
|
|
547
|
-
expect(result.value.embedding.openaiCompatible.baseUrl).toBe('http://localhost:11434/v1');
|
|
548
|
-
expect(result.value.embedding.openaiCompatible.maxBatchSize).toBe(100);
|
|
549
|
-
}
|
|
550
|
-
});
|
|
551
|
-
it('should load config without openaiCompatible section (backwards compatible)', async () => {
|
|
552
|
-
const configContent = `
|
|
553
|
-
version: "1"
|
|
554
|
-
project:
|
|
555
|
-
name: no-openai
|
|
556
|
-
embedding:
|
|
557
|
-
provider: ollama
|
|
558
|
-
model: nomic-embed-text
|
|
559
|
-
dimensions: 768
|
|
560
|
-
`;
|
|
561
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
562
|
-
const result = await loadConfig(tempDir);
|
|
563
|
-
expect(result.isOk()).toBe(true);
|
|
564
|
-
if (result.isOk()) {
|
|
565
|
-
expect(result.value.embedding.provider).toBe('ollama');
|
|
566
|
-
expect(result.value.embedding.openaiCompatible).toBeUndefined();
|
|
567
|
-
}
|
|
568
|
-
});
|
|
569
|
-
it('should return validation error for empty baseUrl in openaiCompatible', async () => {
|
|
570
|
-
const configContent = `
|
|
571
|
-
version: "1"
|
|
572
|
-
project:
|
|
573
|
-
name: bad-openai
|
|
574
|
-
embedding:
|
|
575
|
-
provider: openai-compatible
|
|
576
|
-
model: nomic-embed-text
|
|
577
|
-
dimensions: 768
|
|
578
|
-
openaiCompatible:
|
|
579
|
-
baseUrl: ""
|
|
580
|
-
maxBatchSize: 100
|
|
581
|
-
`;
|
|
582
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
583
|
-
const result = await loadConfig(tempDir);
|
|
584
|
-
expect(result.isErr()).toBe(true);
|
|
585
|
-
if (result.isErr()) {
|
|
586
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
587
|
-
expect(result.error.message).toContain('Config validation failed');
|
|
588
|
-
expect(result.error.message).toContain('baseUrl');
|
|
589
|
-
}
|
|
590
|
-
});
|
|
591
|
-
});
|
|
592
|
-
describe('interpolateEnvVars', () => {
|
|
593
|
-
const ORIG_ENV = { ...process.env };
|
|
594
|
-
afterEach(() => {
|
|
595
|
-
process.env = { ...ORIG_ENV };
|
|
596
|
-
});
|
|
597
|
-
it('should replace ${VAR} with process.env value', () => {
|
|
598
|
-
process.env['TEST_TOKEN'] = 'secret123';
|
|
599
|
-
const result = interpolateEnvVars({ pat: '${TEST_TOKEN}' });
|
|
600
|
-
expect(result).toEqual({ pat: 'secret123' });
|
|
601
|
-
});
|
|
602
|
-
it('should replace multiple vars in one string', () => {
|
|
603
|
-
process.env['HOST'] = 'localhost';
|
|
604
|
-
process.env['PORT'] = '8080';
|
|
605
|
-
const result = interpolateEnvVars({ url: 'http://${HOST}:${PORT}' });
|
|
606
|
-
expect(result).toEqual({ url: 'http://localhost:8080' });
|
|
607
|
-
});
|
|
608
|
-
it('should return ConfigError for missing env var', () => {
|
|
609
|
-
delete process.env['MISSING_VAR'];
|
|
610
|
-
const result = interpolateEnvVars({ key: '${MISSING_VAR}' });
|
|
611
|
-
expect(result).toBeInstanceOf(ConfigError);
|
|
612
|
-
expect(result.message).toContain('MISSING_VAR');
|
|
613
|
-
});
|
|
614
|
-
it('should NOT interpolate escaped \\${VAR}', () => {
|
|
615
|
-
const result = interpolateEnvVars({ key: '\\${NOT_A_VAR}' });
|
|
616
|
-
expect(result).toEqual({ key: '${NOT_A_VAR}' });
|
|
617
|
-
});
|
|
618
|
-
it('should traverse nested objects recursively', () => {
|
|
619
|
-
process.env['NESTED_VAL'] = 'deep';
|
|
620
|
-
const result = interpolateEnvVars({
|
|
621
|
-
level1: { level2: { value: '${NESTED_VAL}' } },
|
|
622
|
-
});
|
|
623
|
-
expect(result).toEqual({
|
|
624
|
-
level1: { level2: { value: 'deep' } },
|
|
625
|
-
});
|
|
626
|
-
});
|
|
627
|
-
it('should traverse arrays', () => {
|
|
628
|
-
process.env['ARR_VAL'] = 'item';
|
|
629
|
-
const result = interpolateEnvVars({ items: ['${ARR_VAL}', 'literal'] });
|
|
630
|
-
expect(result).toEqual({ items: ['item', 'literal'] });
|
|
631
|
-
});
|
|
632
|
-
it('should leave non-string values untouched', () => {
|
|
633
|
-
const result = interpolateEnvVars({ num: 42, bool: true, nil: null });
|
|
634
|
-
expect(result).toEqual({ num: 42, bool: true, nil: null });
|
|
635
|
-
});
|
|
636
|
-
});
|
|
637
|
-
describe('loadConfig with env var interpolation', () => {
|
|
638
|
-
let tempDir;
|
|
639
|
-
const ORIG_ENV = { ...process.env };
|
|
640
|
-
beforeEach(() => {
|
|
641
|
-
tempDir = mkdtempSync(join(tmpdir(), 'coderag-env-'));
|
|
642
|
-
});
|
|
643
|
-
afterEach(() => {
|
|
644
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
645
|
-
process.env = { ...ORIG_ENV };
|
|
646
|
-
});
|
|
647
|
-
it('should resolve ${ADO_PAT} in backlog config', async () => {
|
|
648
|
-
process.env['ADO_PAT'] = 'my-secret-pat';
|
|
649
|
-
const configContent = `
|
|
650
|
-
version: "1"
|
|
651
|
-
project:
|
|
652
|
-
name: env-test
|
|
653
|
-
backlog:
|
|
654
|
-
provider: ado
|
|
655
|
-
config:
|
|
656
|
-
organization: myorg
|
|
657
|
-
project: MyProject
|
|
658
|
-
pat: \${ADO_PAT}
|
|
659
|
-
`;
|
|
660
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
661
|
-
const result = await loadConfig(tempDir);
|
|
662
|
-
expect(result.isOk()).toBe(true);
|
|
663
|
-
if (result.isOk()) {
|
|
664
|
-
expect(result.value.backlog?.config?.['pat']).toBe('my-secret-pat');
|
|
665
|
-
}
|
|
666
|
-
});
|
|
667
|
-
it('should return error when referenced env var is missing', async () => {
|
|
668
|
-
delete process.env['MISSING_PAT'];
|
|
669
|
-
const configContent = `
|
|
670
|
-
version: "1"
|
|
671
|
-
project:
|
|
672
|
-
name: missing-env
|
|
673
|
-
backlog:
|
|
674
|
-
provider: ado
|
|
675
|
-
config:
|
|
676
|
-
pat: \${MISSING_PAT}
|
|
677
|
-
`;
|
|
678
|
-
writeFileSync(join(tempDir, '.coderag.yaml'), configContent);
|
|
679
|
-
const result = await loadConfig(tempDir);
|
|
680
|
-
expect(result.isErr()).toBe(true);
|
|
681
|
-
if (result.isErr()) {
|
|
682
|
-
expect(result.error).toBeInstanceOf(ConfigError);
|
|
683
|
-
expect(result.error.message).toContain('MISSING_PAT');
|
|
684
|
-
}
|
|
685
|
-
});
|
|
686
|
-
});
|
|
687
|
-
describe('barrel exports', () => {
|
|
688
|
-
it('should re-export loadConfig and ConfigError from the package entry point', async () => {
|
|
689
|
-
const barrel = await import('../index.js');
|
|
690
|
-
expect(barrel.loadConfig).toBeDefined();
|
|
691
|
-
expect(barrel.ConfigError).toBeDefined();
|
|
692
|
-
expect(barrel.EmbedError).toBeDefined();
|
|
693
|
-
expect(barrel.StoreError).toBeDefined();
|
|
694
|
-
expect(barrel.LLMError).toBeDefined();
|
|
695
|
-
expect(barrel.ParseError).toBeDefined();
|
|
696
|
-
expect(barrel.ChunkError).toBeDefined();
|
|
697
|
-
});
|
|
698
|
-
});
|
|
699
|
-
//# sourceMappingURL=config-parser.test.js.map
|