Skip to content

Commit b20dbef

Browse files
committed
test: add edge case tests for parseFrontmatter with malformed YAML
Signed-off-by: leocavalcante <leo@cavalcante.dev>
1 parent aa7b5e8 commit b20dbef

1 file changed

Lines changed: 186 additions & 0 deletions

File tree

tests/paths.test.ts

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,192 @@ url: https://example.com
13981398
"parseFrontmatter: content must be a string, got boolean",
13991399
)
14001400
})
1401+
1402+
describe("edge cases with malformed YAML", () => {
1403+
it("should return empty fields for frontmatter with only whitespace between delimiters", () => {
1404+
const content = `---
1405+
1406+
1407+
---
1408+
# Content`
1409+
const result = parseFrontmatter(content)
1410+
expect(result.found).toBe(true)
1411+
expect(result.fields).toEqual({})
1412+
expect(result.endIndex).toBeGreaterThan(0)
1413+
})
1414+
1415+
it("should handle values containing unbalanced double quotes", () => {
1416+
const content = `---
1417+
name: "Unbalanced quote value
1418+
version: 1.0
1419+
---
1420+
# Content`
1421+
const result = parseFrontmatter(content)
1422+
expect(result.found).toBe(true)
1423+
// The unbalanced quote is kept as-is since it doesn't match the balanced quote pattern
1424+
expect(result.fields.name).toBe('"Unbalanced quote value')
1425+
expect(result.fields.version).toBe("1.0")
1426+
})
1427+
1428+
it("should handle values containing unbalanced single quotes", () => {
1429+
const content = `---
1430+
name: 'Unbalanced single quote
1431+
version: 1.0
1432+
---
1433+
# Content`
1434+
const result = parseFrontmatter(content)
1435+
expect(result.found).toBe(true)
1436+
// The unbalanced quote is kept as-is
1437+
expect(result.fields.name).toBe("'Unbalanced single quote")
1438+
expect(result.fields.version).toBe("1.0")
1439+
})
1440+
1441+
it("should handle values with mismatched quote types", () => {
1442+
const content = `---
1443+
name: "Mismatched quotes'
1444+
version: 1.0
1445+
---
1446+
# Content`
1447+
const result = parseFrontmatter(content)
1448+
expect(result.found).toBe(true)
1449+
// Mismatched quotes are not stripped
1450+
expect(result.fields.name).toBe("\"Mismatched quotes'")
1451+
expect(result.fields.version).toBe("1.0")
1452+
})
1453+
1454+
it("should return unclosed when closing --- is on same line as content", () => {
1455+
const content = `---
1456+
version: 1.0
1457+
requires: opencode---
1458+
# Content`
1459+
const result = parseFrontmatter(content)
1460+
// The closing --- must be on its own line (preceded by \n)
1461+
// "opencode---" does not match "\n---" pattern
1462+
expect(result.found).toBe(false)
1463+
expect(result.reason).toBe("unclosed")
1464+
})
1465+
1466+
it("should handle closing --- immediately after newline with no space", () => {
1467+
const content = `---
1468+
version: 1.0
1469+
---`
1470+
const result = parseFrontmatter(content)
1471+
expect(result.found).toBe(true)
1472+
expect(result.fields.version).toBe("1.0")
1473+
})
1474+
1475+
it("should use the last value when duplicate keys exist", () => {
1476+
const content = `---
1477+
version: 1.0
1478+
requires: opencode
1479+
version: 2.0
1480+
---
1481+
# Content`
1482+
const result = parseFrontmatter(content)
1483+
expect(result.found).toBe(true)
1484+
// The parser iterates through lines and overwrites, so last value wins
1485+
expect(result.fields.version).toBe("2.0")
1486+
expect(result.fields.requires).toBe("opencode")
1487+
})
1488+
1489+
it("should handle multiple duplicate keys with last value winning", () => {
1490+
const content = `---
1491+
key: first
1492+
key: second
1493+
key: third
1494+
---
1495+
# Content`
1496+
const result = parseFrontmatter(content)
1497+
expect(result.found).toBe(true)
1498+
expect(result.fields.key).toBe("third")
1499+
})
1500+
1501+
it("should preserve leading whitespace in values", () => {
1502+
const content = `---
1503+
name: leading spaces
1504+
version: 1.0
1505+
---
1506+
# Content`
1507+
const result = parseFrontmatter(content)
1508+
expect(result.found).toBe(true)
1509+
// trim() is called on the value, so leading spaces are removed
1510+
expect(result.fields.name).toBe("leading spaces")
1511+
})
1512+
1513+
it("should preserve trailing whitespace in values (trimmed)", () => {
1514+
const content = `---
1515+
name: trailing spaces
1516+
version: 1.0
1517+
---
1518+
# Content`
1519+
const result = parseFrontmatter(content)
1520+
expect(result.found).toBe(true)
1521+
// trim() is called on the value, so trailing spaces are removed
1522+
expect(result.fields.name).toBe("trailing spaces")
1523+
})
1524+
1525+
it("should handle values with both leading and trailing whitespace", () => {
1526+
const content = `---
1527+
name: surrounded by spaces
1528+
version: 1.0
1529+
---
1530+
# Content`
1531+
const result = parseFrontmatter(content)
1532+
expect(result.found).toBe(true)
1533+
// Both leading and trailing spaces are trimmed
1534+
expect(result.fields.name).toBe("surrounded by spaces")
1535+
})
1536+
1537+
it("should handle whitespace-only values as empty strings", () => {
1538+
const content = `---
1539+
name:
1540+
version: 1.0
1541+
---
1542+
# Content`
1543+
const result = parseFrontmatter(content)
1544+
expect(result.found).toBe(true)
1545+
// Whitespace-only value becomes empty string after trim
1546+
expect(result.fields.name).toBe("")
1547+
expect(result.fields.version).toBe("1.0")
1548+
})
1549+
1550+
it("should handle quoted values with internal whitespace preserved", () => {
1551+
const content = `---
1552+
name: " internal spaces "
1553+
version: 1.0
1554+
---
1555+
# Content`
1556+
const result = parseFrontmatter(content)
1557+
expect(result.found).toBe(true)
1558+
// Quotes are stripped, but internal whitespace is preserved
1559+
expect(result.fields.name).toBe(" internal spaces ")
1560+
})
1561+
1562+
it("should handle empty value after colon", () => {
1563+
const content = `---
1564+
name:
1565+
version: 1.0
1566+
---
1567+
# Content`
1568+
const result = parseFrontmatter(content)
1569+
expect(result.found).toBe(true)
1570+
expect(result.fields.name).toBe("")
1571+
expect(result.fields.version).toBe("1.0")
1572+
})
1573+
1574+
it("should handle keys with leading/trailing whitespace", () => {
1575+
const content = `---
1576+
name : value
1577+
version: 1.0
1578+
---
1579+
# Content`
1580+
const result = parseFrontmatter(content)
1581+
expect(result.found).toBe(true)
1582+
// Key whitespace is trimmed
1583+
expect(result.fields.name).toBe("value")
1584+
expect(result.fields.version).toBe("1.0")
1585+
})
1586+
})
14011587
})
14021588

14031589
describe("validateAgentContent", () => {

0 commit comments

Comments
 (0)