mirror of
https://github.com/Kaelio/ktx.git
synced 2026-07-22 11:51:01 +02:00
test: improve PR coverage
This commit is contained in:
parent
e6f0d9cd51
commit
b43b12729e
6 changed files with 308 additions and 0 deletions
|
|
@ -111,6 +111,26 @@ describe('createLocalBundleIngestRuntime', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('warns when embeddings are configured but no embedding provider is supplied', () => {
|
||||
const logger = { log: vi.fn(), warn: vi.fn(), error: vi.fn() };
|
||||
project.config.ingest.embeddings = {
|
||||
backend: 'openai',
|
||||
model: 'text-embedding-3-small',
|
||||
dimensions: 1536,
|
||||
};
|
||||
|
||||
createLocalBundleIngestRuntime({
|
||||
project,
|
||||
adapters: [new FakeSourceAdapter()],
|
||||
agentRunner: testAgentRunner(),
|
||||
logger: logger as never,
|
||||
});
|
||||
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
'[local-bundle-runtime] embeddings backend "openai" is configured but no embedding provider was passed; embedding-dependent stages will run against a no-op embedding port.',
|
||||
);
|
||||
});
|
||||
|
||||
it('builds runner deps with local SQLite stores and context tools enabled', async () => {
|
||||
const agentRunner = testAgentRunner();
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,25 @@ describe('createLocalProjectMemoryIngest', () => {
|
|||
await rm(tempDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('warns when embeddings are configured but memory ingest is created without an embedding provider', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
project.config.ingest.embeddings = {
|
||||
backend: 'openai',
|
||||
model: 'text-embedding-3-small',
|
||||
dimensions: 1536,
|
||||
};
|
||||
const logger = { log: vi.fn(), warn: vi.fn(), error: vi.fn() };
|
||||
|
||||
createLocalProjectMemoryIngest(project, {
|
||||
agentRunner: { runLoop: vi.fn() } as never,
|
||||
logger: logger as never,
|
||||
});
|
||||
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
'[memory-ingest] embeddings backend "openai" is configured but no embedding provider was passed; semantic search will fall back to a no-op embedding port.',
|
||||
);
|
||||
});
|
||||
|
||||
it('captures a wiki page through the local memory agent and persists pollable status', async () => {
|
||||
const project = await initKtxProject({ projectDir: tempDir });
|
||||
const agentRunner = {
|
||||
|
|
|
|||
|
|
@ -176,6 +176,28 @@ llm:
|
|||
});
|
||||
});
|
||||
|
||||
it('requires a non-empty Vertex location when the Vertex provider block is present', () => {
|
||||
const yaml = `
|
||||
llm:
|
||||
provider:
|
||||
backend: vertex
|
||||
vertex:
|
||||
project: local-gcp-project
|
||||
`;
|
||||
|
||||
expect(() => parseKtxProjectConfig(yaml)).toThrow(/llm\.provider\.vertex\.location/);
|
||||
|
||||
const validation = validateKtxProjectConfig(yaml);
|
||||
expect(validation.ok).toBe(false);
|
||||
expect(validation.issues).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
path: 'llm.provider.vertex.location',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('parses Claude Code as a first-class LLM backend', () => {
|
||||
const config = parseKtxProjectConfig(`
|
||||
llm:
|
||||
|
|
|
|||
|
|
@ -67,6 +67,23 @@ describe('listConnectionIdsWithNames', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('loadSource', () => {
|
||||
it('warns and returns null when an existing source file has invalid YAML', async () => {
|
||||
const logger = { log: vi.fn(), warn: vi.fn(), error: vi.fn() };
|
||||
const configService = {
|
||||
readFile: vi.fn().mockResolvedValue({ content: 'name: [' }),
|
||||
};
|
||||
const service = new SemanticLayerService(configService as never, connectionCatalog(), pythonPort, logger as never);
|
||||
|
||||
await expect(service.loadSource('warehouse', 'orders')).resolves.toBeNull();
|
||||
|
||||
expect(configService.readFile).toHaveBeenCalledWith('semantic-layer/warehouse/orders.yaml');
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
expect.stringContaining('[loadSource] warehouse/orders.yaml: YAML parse failed:'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('composeOverlay', () => {
|
||||
it('carries top-level segments from overlay into the composed source', () => {
|
||||
const overlay = {
|
||||
|
|
@ -856,6 +873,22 @@ describe('loadAllSources — standalone enrichment via inherits_columns_from', (
|
|||
expect(loadErrors.join('\n')).toContain(overlayPath);
|
||||
expect(loadErrors.join('\n')).toContain("move it to 'column_overrides:'");
|
||||
});
|
||||
|
||||
it('reports and logs directory listing failures instead of treating them as empty sources', async () => {
|
||||
const logger = { log: vi.fn(), warn: vi.fn(), error: vi.fn() };
|
||||
configService.listFiles.mockRejectedValue(new Error('permission denied'));
|
||||
service = new SemanticLayerService(configService as never, connectionCatalog(), pythonPort, logger as never);
|
||||
|
||||
const { sources, loadErrors } = await service.loadAllSources('conn-1');
|
||||
|
||||
expect(sources).toEqual([]);
|
||||
expect(loadErrors).toEqual([
|
||||
'Failed to list semantic-layer files under semantic-layer/conn-1: permission denied',
|
||||
]);
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
'Failed to list semantic-layer files under semantic-layer/conn-1: permission denied',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateWithProposedSource', () => {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,27 @@ function makeService() {
|
|||
|
||||
const fm: WikiFrontmatter = { summary: 'sum', usage_mode: 'auto' };
|
||||
|
||||
describe('KnowledgeWikiService file reads', () => {
|
||||
it('warns and returns null when an existing page cannot be parsed', async () => {
|
||||
const { service, configService, logger } = makeService();
|
||||
configService.readFile.mockResolvedValue({ content: '---\nsummary: [\n---\nBody' });
|
||||
|
||||
await expect(service.readPage('GLOBAL', null, 'revenue')).resolves.toBeNull();
|
||||
|
||||
expect(configService.readFile).toHaveBeenCalledWith('wiki/global/revenue.md');
|
||||
expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('[readPage] wiki/global/revenue.md: parse failed:'));
|
||||
});
|
||||
|
||||
it('warns and returns an empty page list when directory listing fails', async () => {
|
||||
const { service, configService, logger } = makeService();
|
||||
configService.listFiles.mockRejectedValue(new Error('filesystem unavailable'));
|
||||
|
||||
await expect(service.listPageKeys('GLOBAL', null)).resolves.toEqual([]);
|
||||
|
||||
expect(logger.warn).toHaveBeenCalledWith('[listPageKeys] wiki/global: filesystem unavailable');
|
||||
});
|
||||
});
|
||||
|
||||
describe('KnowledgeWikiService.syncIndex result stats', () => {
|
||||
it('reports scanned, updated, deleted, and embedding counts', async () => {
|
||||
const { service, pagesRepository, embeddingService, configService } = makeService();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue