mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 16:57:46 +00:00
194 lines
6.2 KiB
TypeScript
194 lines
6.2 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Qwen
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
import { parse, stringify } from './yaml-parser.js';
|
|
|
|
describe('yaml-parser', () => {
|
|
describe('parse', () => {
|
|
it('should parse simple key-value pairs', () => {
|
|
const yaml = 'name: test\ndescription: A test config';
|
|
const result = parse(yaml);
|
|
expect(result).toEqual({
|
|
name: 'test',
|
|
description: 'A test config',
|
|
});
|
|
});
|
|
|
|
it('should parse arrays', () => {
|
|
const yaml = 'tools:\n - file\n - shell';
|
|
const result = parse(yaml);
|
|
expect(result).toEqual({
|
|
tools: ['file', 'shell'],
|
|
});
|
|
});
|
|
|
|
it('should parse nested objects', () => {
|
|
const yaml = 'modelConfig:\n temperature: 0.7\n maxTokens: 1000';
|
|
const result = parse(yaml);
|
|
expect(result).toEqual({
|
|
modelConfig: {
|
|
temperature: 0.7,
|
|
maxTokens: 1000,
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('stringify', () => {
|
|
it('should stringify simple objects', () => {
|
|
const obj = { name: 'test', description: 'A test config' };
|
|
const result = stringify(obj);
|
|
expect(result).toBe('name: test\ndescription: A test config');
|
|
});
|
|
|
|
it('should stringify arrays', () => {
|
|
const obj = { tools: ['file', 'shell'] };
|
|
const result = stringify(obj);
|
|
expect(result).toBe('tools:\n - file\n - shell');
|
|
});
|
|
|
|
it('should stringify nested objects', () => {
|
|
const obj = {
|
|
modelConfig: {
|
|
temperature: 0.7,
|
|
maxTokens: 1000,
|
|
},
|
|
};
|
|
const result = stringify(obj);
|
|
expect(result).toBe(
|
|
'modelConfig:\n temperature: 0.7\n maxTokens: 1000',
|
|
);
|
|
});
|
|
|
|
describe('string escaping security', () => {
|
|
it('should properly escape strings with quotes', () => {
|
|
const obj = { key: 'value with "quotes"' };
|
|
const result = stringify(obj);
|
|
expect(result).toBe('key: "value with \\"quotes\\""');
|
|
});
|
|
|
|
it('should properly escape strings with backslashes', () => {
|
|
const obj = { key: 'value with \\ backslash' };
|
|
const result = stringify(obj);
|
|
expect(result).toBe('key: "value with \\\\ backslash"');
|
|
});
|
|
|
|
it('should properly escape strings with backslash-quote sequences', () => {
|
|
// This is the critical security test case
|
|
const obj = { key: 'value with \\" sequence' };
|
|
const result = stringify(obj);
|
|
// Should escape backslashes first, then quotes
|
|
expect(result).toBe('key: "value with \\\\\\" sequence"');
|
|
});
|
|
|
|
it('should handle complex escaping scenarios', () => {
|
|
const testCases = [
|
|
{
|
|
input: { path: 'C:\\Program Files\\"App"\\file.txt' },
|
|
expected: 'path: "C:\\\\Program Files\\\\\\"App\\"\\\\file.txt"',
|
|
},
|
|
{
|
|
input: { message: 'He said: \\"Hello\\"' },
|
|
expected: 'message: "He said: \\\\\\"Hello\\\\\\""',
|
|
},
|
|
{
|
|
input: { complex: 'Multiple \\\\ backslashes \\" and " quotes' },
|
|
expected:
|
|
'complex: "Multiple \\\\\\\\ backslashes \\\\\\" and \\" quotes"',
|
|
},
|
|
];
|
|
|
|
testCases.forEach(({ input, expected }) => {
|
|
const result = stringify(input);
|
|
expect(result).toBe(expected);
|
|
});
|
|
});
|
|
|
|
it('should maintain round-trip integrity for escaped strings', () => {
|
|
const testStrings = [
|
|
'simple string',
|
|
'string with "quotes"',
|
|
'string with \\ backslash',
|
|
'string with \\" sequence',
|
|
'path\\to\\"file".txt',
|
|
'He said: \\"Hello\\"',
|
|
'Multiple \\\\ backslashes \\" and " quotes',
|
|
];
|
|
|
|
testStrings.forEach((testString) => {
|
|
// Force quoting by adding a colon
|
|
const originalObj = { key: testString + ':' };
|
|
const yamlString = stringify(originalObj);
|
|
const parsedObj = parse(yamlString);
|
|
expect(parsedObj).toEqual(originalObj);
|
|
});
|
|
});
|
|
|
|
it('should not quote strings that do not need quoting', () => {
|
|
const obj = { key: 'simplevalue' };
|
|
const result = stringify(obj);
|
|
expect(result).toBe('key: simplevalue');
|
|
});
|
|
|
|
it('should quote strings with colons', () => {
|
|
const obj = { key: 'value:with:colons' };
|
|
const result = stringify(obj);
|
|
expect(result).toBe('key: "value:with:colons"');
|
|
});
|
|
|
|
it('should quote strings with hash symbols', () => {
|
|
const obj = { key: 'value#with#hash' };
|
|
const result = stringify(obj);
|
|
expect(result).toBe('key: "value#with#hash"');
|
|
});
|
|
|
|
it('should quote strings with leading/trailing whitespace', () => {
|
|
const obj = { key: ' value with spaces ' };
|
|
const result = stringify(obj);
|
|
expect(result).toBe('key: " value with spaces "');
|
|
});
|
|
});
|
|
|
|
describe('numeric string handling', () => {
|
|
it('should parse unquoted numeric values as numbers', () => {
|
|
const yaml = 'name: 11\ndescription: 333';
|
|
const result = parse(yaml);
|
|
expect(result).toEqual({
|
|
name: 11,
|
|
description: 333,
|
|
});
|
|
expect(typeof result['name']).toBe('number');
|
|
expect(typeof result['description']).toBe('number');
|
|
});
|
|
|
|
it('should parse quoted numeric values as strings', () => {
|
|
const yaml = 'name: "11"\ndescription: "333"';
|
|
const result = parse(yaml);
|
|
expect(result).toEqual({
|
|
name: '11',
|
|
description: '333',
|
|
});
|
|
expect(typeof result['name']).toBe('string');
|
|
expect(typeof result['description']).toBe('string');
|
|
});
|
|
|
|
it('should handle mixed numeric and string values', () => {
|
|
const yaml = 'name: "11"\nage: 25\ndescription: "333"';
|
|
const result = parse(yaml);
|
|
expect(result).toEqual({
|
|
name: '11',
|
|
age: 25,
|
|
description: '333',
|
|
});
|
|
expect(typeof result['name']).toBe('string');
|
|
expect(typeof result['age']).toBe('number');
|
|
expect(typeof result['description']).toBe('string');
|
|
});
|
|
});
|
|
});
|
|
});
|