PureYAML/Tests/ComplexMappingKeyParsingTests.swift
Mihaela Mihaljevic 0588996506 feat(parsing): run the whole library on the ported engine
Routes the value and stream APIs through the ported scanner+parser (the
event API already was), so PureYAML now parses entirely on the libyaml
port. Adds the features needed to match and exceed the previous engine:
JSON UTF-16 surrogate-pair \u escapes, bare documents after an explicit
`...`, reserved-directive handling that still requires a following `---`,
and a 64-level nesting bound enforced on the event stream so deeply nested
input fails with nestingTooDeep instead of overflowing a recursive
consumer. Parser errors are translated to the diagnostic-aware ParseError,
recovering precise codes where identifiable and a readable malformed
fallback otherwise; error-reporting fixtures are rebased onto the port.

Canonical event conformance: 268 -> 271 of 308 (7Z25, HWV9, QT73).
393 tests pass.
2026-06-24 18:02:22 +02:00

154 lines
5.9 KiB
Swift

@testable import PureYAML
import Testing
@Suite("Complex Mapping Key Parsing")
struct ComplexMappingKeyParsingTests {
@Test("Parses block sequence keys as first-class key nodes")
func test_parsesBlockSequenceKeysAsFirstClassKeyNodes() throws {
let value = try PureYAML.parse("""
? [Detroit Tigers, Chicago Cubs]
:
- 2001-07-23
""")
let expectedKey = PureYAML.Model.Key.sequence([
.string("Detroit Tigers"),
.string("Chicago Cubs"),
])
#expect(value == .mapping(.init([
.init(
keyNode: expectedKey,
value: .sequence([.string("2001-07-23")]),
),
])))
#expect(value.rootMapping?.pairs.first?.keyNode == expectedKey)
#expect(value.rootMapping?.pairs.first?.key == "[\"Detroit Tigers\", \"Chicago Cubs\"]")
#expect(value.rootMapping?["Detroit Tigers"] == nil)
#expect(value.rootMapping?[expectedKey] == .sequence([.string("2001-07-23")]))
}
@Test("Parses block mapping keys as first-class key nodes")
func test_parsesBlockMappingKeysAsFirstClassKeyNodes() throws {
let value = try PureYAML.parse("""
? {name: Example, version: 1}
: active
""")
let expectedKey = PureYAML.Model.Key.mapping(.init([
.init(key: "name", value: .string("Example")),
.init(key: "version", value: .int(1)),
]))
#expect(value == .mapping(.init([
.init(keyNode: expectedKey, value: .string("active")),
])))
#expect(value.rootMapping?.pairs.first?.keyNode.description == """
{"name": "Example", "version": 1}
""")
#expect(value.rootMapping?["name"] == nil)
#expect(value.rootMapping?[expectedKey] == .string("active"))
}
@Test("Keeps scalar mapping keys string-keyed even when tags look typed")
func test_keepsScalarMappingKeysStringKeyedEvenWhenTagsLookTyped() throws {
let value = try PureYAML.parse("""
? !!int 1
: integer-looking
? true
: boolean-looking
""")
#expect(value == .mapping(.init([
.init(key: "1", value: .string("integer-looking")),
.init(key: "true", value: .string("boolean-looking")),
])))
#expect(value.rootMapping?["1"] == .string("integer-looking"))
#expect(value.rootMapping?["true"] == .string("boolean-looking"))
#expect(value.rootMapping?[.sequence([.string("1")])] == nil)
}
@Test("Parses alias-backed complex keys without string coercion")
func test_parsesAliasBackedComplexKeysWithoutStringCoercion() throws {
let value = try PureYAML.parse("""
pair: &pair [a, b]
? *pair
: value
""")
let key = PureYAML.Model.Key.sequence([.string("a"), .string("b")])
#expect(value == .mapping(.init([
.init(key: "pair", value: .sequence([.string("a"), .string("b")])),
.init(keyNode: key, value: .string("value")),
])))
#expect(value.rootMapping?["pair"] == .sequence([.string("a"), .string("b")]))
#expect(value.rootMapping?[key] == .string("value"))
#expect(value.rootMapping?["[\"a\", \"b\"]"] == nil)
}
@Test("Parses explicit scalar keys with same-line mapping values")
func test_parsesExplicitScalarKeysWithSameLineMappingValues() throws {
let value = try PureYAML.parse("""
? __stringMin14PatternS3Captions
: minLength: 14
pattern: ^((s3://(.*?)\\.(srt|SRT))|(https?://(.*?)\\.(srt|SRT)))$
type: string
next: done
""")
let key = PureYAML.Model.Key.string("__stringMin14PatternS3Captions")
#expect(value == .mapping(.init([
.init(keyNode: key, value: .mapping(.init([
.init(key: "minLength", value: .int(14)),
.init(
key: "pattern",
value: .string("^((s3://(.*?)\\.(srt|SRT))|(https?://(.*?)\\.(srt|SRT)))$"),
),
.init(key: "type", value: .string("string")),
]))),
.init(key: "next", value: .string("done")),
])))
#expect(value.rootMapping?[key]?.rootMapping?["minLength"] == .int(14))
#expect(value.rootMapping?[key]?.rootMapping?["type"] == .string("string"))
#expect(value.rootMapping?["next"] == .string("done"))
#expect(value.rootMapping?["minLength"] == nil)
}
@Test("Expands merged mappings with complex-key local overrides")
func test_expandsMergedMappingsWithComplexKeyLocalOverrides() throws {
let value = try PureYAML.parse("""
defaults: &defaults
? [a, b]
: inherited
name: inherited-name
service:
<<: *defaults
? [a, b]
: local
""")
let service = value.rootMapping?["service"]
let key = PureYAML.Model.Key.sequence([.string("a"), .string("b")])
#expect(service == .mapping(.init([
.init(key: "name", value: .string("inherited-name")),
.init(keyNode: key, value: .string("local")),
])))
#expect(try PureYAML.validate(value, strict: false).isEmpty)
#expect(service?.rootMapping?[key] == .string("local"))
#expect(service?.rootMapping?["[\"a\", \"b\"]"] == nil)
}
@Test("Rejects a sequence dangling after a complex key mapping")
func test_rejectsSequenceDanglingAfterComplexKeyMapping() {
// `? [a, b]` is a valid explicit key with an implicit empty (null) value,
// so the mapping closes cleanly. The same-column `- invalid` then dangles
// outside any node and is rejected at the document boundary.
expectParseError("""
? [a, b]
- invalid
""", .malformed(
reason: "while parsing a block mapping, did not find expected key",
line: 1,
column: 1,
))
}
}