rhizome-node/__tests__/unit/core/json-ast.test.ts

173 lines
4.2 KiB
TypeScript

import { jsonToAst } from '@src/utils/json-ast';
import { JsonNode } from '@src/utils/json-ast/types';
describe('jsonToAst', () => {
it('should handle primitive values', () => {
expect(jsonToAst(42)).toMatchObject({
type: 'number',
value: 42
});
expect(jsonToAst('test')).toMatchObject({
type: 'string',
value: 'test'
});
expect(jsonToAst(true)).toMatchObject({
type: 'boolean',
value: true
});
expect(jsonToAst(null)).toMatchObject({
type: 'null',
value: null
});
});
it('should handle empty objects and arrays', () => {
const emptyObj = jsonToAst({});
expect(emptyObj).toMatchObject({
type: 'object',
children: []
});
const emptyArray = jsonToAst([]);
expect(emptyArray).toMatchObject({
type: 'array',
children: []
});
});
it('should handle nested objects', () => {
const ast = jsonToAst({
name: 'test',
nested: { value: 42 }
});
expect(ast.type).toBe('object');
expect(ast.children).toHaveLength(2);
const nameNode = ast.children?.[0];
const nestedNode = ast.children?.[1];
expect(nameNode).toMatchObject({
type: 'string',
key: 'name',
value: 'test'
});
expect(nestedNode).toMatchObject({
type: 'object',
key: 'nested'
});
expect(nestedNode?.children?.[0]).toMatchObject({
type: 'number',
key: 'value',
value: 42
});
});
it('should handle arrays', () => {
const ast = jsonToAst([1, 'two', true]);
expect(ast.type).toBe('array');
expect(ast.children).toHaveLength(3);
expect(ast.children?.[0]).toMatchObject({
type: 'number',
value: 1
});
expect(ast.children?.[1]).toMatchObject({
type: 'string',
value: 'two'
});
expect(ast.children?.[2]).toMatchObject({
type: 'boolean',
value: true
});
});
it('should include paths when includePath is true', () => {
const ast = jsonToAst({
user: {
name: 'test',
roles: ['admin', 'user']
}
}, { includePath: true });
const findNode = (node: JsonNode, key: string): JsonNode | undefined => {
if (node.key === key) return node;
if (!node.children) return undefined;
for (const child of node.children) {
const found = findNode(child, key);
if (found) return found;
}
return undefined;
};
const nameNode = findNode(ast, 'name');
const rolesNode = findNode(ast, 'roles');
expect(nameNode?.path).toBe('user.name');
expect(rolesNode?.path).toBe('user.roles');
expect(rolesNode?.children?.[0].path).toBe('user.roles[0]');
});
it('should respect maxDepth option', () => {
const deepObject = {
level1: {
level2: {
level3: {
value: 'too deep'
}
}
}
};
const ast = jsonToAst(deepObject, {
maxDepth: 2,
includePath: true
});
const level2 = ast.children?.[0].children?.[0];
expect(level2?.type).toBe('object');
// The implementation sets value to undefined when max depth is exceeded
expect(level2?.value).toBeUndefined();
expect(level2?.path).toBe('level1.level2');
});
it('should apply filter function when provided', () => {
const data = {
name: 'test',
age: 42,
active: true,
address: {
street: '123 Main St',
city: 'Anytown'
}
};
// Only include string and number values
const ast = jsonToAst(data, {
filter: (node: JsonNode) =>
node.type === 'string' ||
node.type === 'number' ||
node.type === 'object' // Keep objects to maintain structure
});
// Should have filtered out the boolean 'active' field
expect(ast.children).toHaveLength(3);
expect(ast.children?.some((c: any) => c.key === 'active')).toBe(false);
// Nested object should only have string properties
const addressNode = ast.children?.find((c: any) => c.key === 'address');
expect(addressNode?.children).toHaveLength(2);
expect(addressNode?.children?.every((c: any) =>
c.type === 'string' || c.key === 'city' || c.key === 'street'
)).toBe(true);
});
});