ktx/packages/cli/test/commands/mongo-query-commands.test.ts
Kevin Messiaen 0e2e6c7d90
refactor(mongo-query): consolidate query types and drop unsafe cast
Route the Mongo read path through one canonical request/result shape and
remove the `as unknown as` narrowing flagged in review.

- Add optional executeQuery to KtxScanConnector and narrow by capability
  in the runner, replacing the double cast with a driver + method guard.
- Move KtxMongoQueryInput/KtxMongoQueryResult into context/scan/types.ts;
  delete the duplicate runner request type, the inline MCP-port input, and
  the KtxMongoQueryResponse alias. KtxMongoQueryArgs now extends the base.
- Inline the pass-through executeMongoQuery wrapper.
- Correct the --output default in the mongo-query and sql CLI docs: it is
  TTY/CI/KTX_OUTPUT-derived, not unconditionally pretty.
- Add tests: database override, empty result, parseLimitOption bounds,
  bigint/plural-row/empty-header rendering, and mongo_query_completed
  telemetry fields on both outcomes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ga2tvQ2YCEphmY8HM5Guzq
2026-07-11 21:19:55 +07:00

59 lines
2.1 KiB
TypeScript

import { InvalidArgumentError } from '@commander-js/extra-typings';
import { describe, expect, it } from 'vitest';
import { parseLimitOption, parsePipelineArgument } from '../../src/commands/mongo-query-commands.js';
describe('parsePipelineArgument', () => {
it('parses a valid JSON array of pipeline-stage objects', () => {
expect(parsePipelineArgument('[{"$match":{"city":"NY"}},{"$limit":10}]')).toEqual([
{ $match: { city: 'NY' } },
{ $limit: 10 },
]);
});
it('parses an empty pipeline array', () => {
expect(parsePipelineArgument('[]')).toEqual([]);
});
it('throws InvalidArgumentError on invalid JSON', () => {
expect(() => parsePipelineArgument('{not json')).toThrow(InvalidArgumentError);
});
it('throws InvalidArgumentError when the value is not an array', () => {
expect(() => parsePipelineArgument('{}')).toThrow(InvalidArgumentError);
});
it('throws InvalidArgumentError when the array contains a non-object stage', () => {
expect(() => parsePipelineArgument('[{"$match":{}}, "not-a-stage"]')).toThrow(InvalidArgumentError);
});
it('throws InvalidArgumentError when the array contains a nested array stage', () => {
expect(() => parsePipelineArgument('[[1,2,3]]')).toThrow(InvalidArgumentError);
});
it('throws InvalidArgumentError when the array contains null', () => {
expect(() => parsePipelineArgument('[null]')).toThrow(InvalidArgumentError);
});
});
describe('parseLimitOption', () => {
it('accepts the boundary values 1 and 10000', () => {
expect(parseLimitOption('1')).toBe(1);
expect(parseLimitOption('10000')).toBe(10000);
});
it('rejects a non-numeric value', () => {
expect(() => parseLimitOption('abc')).toThrow(InvalidArgumentError);
});
it('rejects a non-integer value', () => {
expect(() => parseLimitOption('1.5')).toThrow(InvalidArgumentError);
});
it('rejects a value below 1', () => {
expect(() => parseLimitOption('0')).toThrow(InvalidArgumentError);
});
it('rejects a value above the cap', () => {
expect(() => parseLimitOption('10001')).toThrow(InvalidArgumentError);
});
});