import { describe, expect, it } from 'vitest';
import { detectFormat } from '../apps/runSandbox/utils/detect-format.js';
describe('detectFormat', () => {
// ── plain-text ─────────────────────────────────────────────────────────
describe('plain-text', () => {
it('returns plain-text for empty string', () => {
expect(detectFormat('')).toEqual({ format: 'plain-text' });
});
it('returns plain-text for whitespace-only string', () => {
expect(detectFormat(' \n ')).toEqual({ format: 'plain-text' });
});
it('returns plain-text for non-JSON text', () => {
expect(detectFormat('hello world')).toEqual({ format: 'plain-text' });
});
it('returns plain-text for JSON null', () => {
const result = detectFormat('null');
expect(result.format).toBe('plain-text');
});
it('returns plain-text for JSON number', () => {
const result = detectFormat('42');
expect(result.format).toBe('plain-text');
});
it('returns plain-text for JSON string', () => {
const result = detectFormat('"hello"');
expect(result.format).toBe('plain-text');
});
it('returns plain-text for JSON boolean', () => {
expect(detectFormat('true').format).toBe('plain-text');
expect(detectFormat('false').format).toBe('plain-text');
});
it('returns plain-text for empty array', () => {
const result = detectFormat('[]');
expect(result.format).toBe('plain-text');
});
it('returns plain-text for array of primitives', () => {
expect(detectFormat('[1, 2, 3]').format).toBe('plain-text');
expect(detectFormat('["a", "b"]').format).toBe('plain-text');
});
it('returns plain-text for array of arrays', () => {
expect(detectFormat('[[1,2],[3,4]]').format).toBe('plain-text');
});
it('returns plain-text for array of null', () => {
expect(detectFormat('[null, null]').format).toBe('plain-text');
});
});
// ── json-object ────────────────────────────────────────────────────────
describe('json-object', () => {
it('returns json-object for simple object', () => {
const input = JSON.stringify({ key: 'value' });
const result = detectFormat(input);
expect(result.format).toBe('json-object');
expect(result.parsed).toEqual({ key: 'value' });
});
it('returns json-object for nested object', () => {
const obj = { spec: { containers: [{ name: 'app', image: 'nginx' }] } };
const result = detectFormat(JSON.stringify(obj));
expect(result.format).toBe('json-object');
expect(result.parsed).toEqual(obj);
});
it('returns json-object for empty object', () => {
const result = detectFormat('{}');
expect(result.format).toBe('json-object');
expect(result.parsed).toEqual({});
});
it('returns json-object for object with numeric values', () => {
const result = detectFormat(JSON.stringify({ cpu: 100, memory: 256 }));
expect(result.format).toBe('json-object');
});
it('returns json-object for object with _interactive but invalid structure', () => {
// Has _interactive but type is not 'table' or 'actions'
const result = detectFormat(JSON.stringify({ _interactive: { type: 'other' }, data: 1 }));
expect(result.format).toBe('json-object');
});
it('returns json-object for object with _interactive table but missing rows', () => {
const result = detectFormat(JSON.stringify({ _interactive: { type: 'table' } }));
expect(result.format).toBe('json-object');
});
});
// ── json-table ─────────────────────────────────────────────────────────
describe('json-table', () => {
it('returns json-table for array of objects', () => {
const data = [{ name: 'pod-1', status: 'Running' }, { name: 'pod-2', status: 'Failed' }];
const result = detectFormat(JSON.stringify(data));
expect(result.format).toBe('json-table');
expect(result.parsed).toEqual(data);
});
it('returns json-table for single-element array of objects', () => {
const data = [{ id: 1 }];
const result = detectFormat(JSON.stringify(data));
expect(result.format).toBe('json-table');
});
it('returns json-table for two-element array with timestamp key (needs 3+ for time-series)', () => {
const data = [
{ timestamp: 1000, value: 10 },
{ timestamp: 2000, value: 20 },
];
const result = detectFormat(JSON.stringify(data));
expect(result.format).toBe('json-table');
});
});
// ── time-series ────────────────────────────────────────────────────────
describe('time-series', () => {
it('returns time-series for 3+ items with timestamp and numeric values', () => {
const data = [
{ timestamp: 1000, cpu: 10 },
{ timestamp: 2000, cpu: 20 },
{ timestamp: 3000, cpu: 30 },
];
const result = detectFormat(JSON.stringify(data));
expect(result.format).toBe('time-series');
});
// Note: camelCase keys like 'createdAt' are in the Set but the code
// lowercases input before lookup, so they never match. Only lowercase
// and snake_case keys work. We test actual behavior, not intent.
it.each([
'timestamp', 'time', 'date', '@timestamp', 'ts', 'datetime',
'created_at', 'updated_at',
])('recognizes "%s" as a timestamp key', (key) => {
const data = [
{ [key]: 1000, value: 1 },
{ [key]: 2000, value: 2 },
{ [key]: 3000, value: 3 },
];
expect(detectFormat(JSON.stringify(data)).format).toBe('time-series');
});
it('returns json-table when no numeric values besides timestamp', () => {
const data = [
{ timestamp: 1000, label: 'a' },
{ timestamp: 2000, label: 'b' },
{ timestamp: 3000, label: 'c' },
];
expect(detectFormat(JSON.stringify(data)).format).toBe('json-table');
});
});
// ── interactive-table ──────────────────────────────────────────────────
describe('interactive-table', () => {
it('returns interactive-table for valid _interactive table object', () => {
const obj = {
_interactive: { type: 'table', kind: 'Pod', identifiers: ['name'] },
rows: [{ name: 'pod-1' }],
};
const result = detectFormat(JSON.stringify(obj));
expect(result.format).toBe('interactive-table');
expect(result.parsed).toEqual(obj);
});
});
// ── interactive-actions ────────────────────────────────────────────────
describe('interactive-actions', () => {
it('returns interactive-actions for valid _interactive actions object', () => {
const obj = {
_interactive: { type: 'actions', kind: 'Pod', resource: { name: 'x' } },
actions: [{ id: 'describe', label: 'Describe' }],
};
const result = detectFormat(JSON.stringify(obj));
expect(result.format).toBe('interactive-actions');
expect(result.parsed).toEqual(obj);
});
});
// ── edge cases ─────────────────────────────────────────────────────────
describe('edge cases', () => {
it('handles surrounding whitespace', () => {
const result = detectFormat(' { "a": 1 } \n');
expect(result.format).toBe('json-object');
});
it('returns plain-text for truncated JSON', () => {
expect(detectFormat('{"key": "val').format).toBe('plain-text');
});
it('returns parsed value along with format', () => {
const result = detectFormat(JSON.stringify({ x: 1 }));
expect(result.parsed).toEqual({ x: 1 });
});
});
});