The ported engine wins every conflict: main's post-fork patches to the hand-rolled Scanner and TokenEventParser land on deleted files and are dropped; their behaviors are carried by the port and guarded by the whole-suite conformance gate (402/402 passing). Merge adaptations, all gates green (style, namespacing, forbidden patterns, changelog, roadmap, swiftformat, swiftlint strict, build, full test suite, whole-suite conformance sweep): - PureYAMLJSON representability rules move onto the Check algebra, replacing the retired legacy Rule type. - The permissive tag-handle pre-pass is removed; the port rejects an undefined handle at parse time (suite case QLJ7) and the validation report carries it as a parse diagnostic. - EventStreamComposer's nested types split one-per-file per the namespacing gate. - Tests pinned to old-engine behavior were re-verified against the reference and updated: libyaml rejects an explicit-key continuation ending at a value colon, and suite case QB6E requires rejecting a quoted continuation at column 0. - CHANGELOG unreleased entries from both lines are preserved in two groups with a provenance note. Closes #1, closes #3
60 lines
2.6 KiB
Swift
60 lines
2.6 KiB
Swift
@testable import PureYAML
|
|
import Testing
|
|
|
|
/// A tag shorthand using a named handle (`!handle!suffix`) must be declared by a `%TAG` directive
|
|
/// in the document that uses it; the handle does not carry across documents. The ported engine is
|
|
/// strict (official suite case QLJ7): an undeclared handle fails the parse, and the validation
|
|
/// report carries it as a parse diagnostic. These prove the error is exact: it fires for an
|
|
/// undeclared handle and stays silent for a declared one, a local tag, the secondary handle, and a
|
|
/// `!x!` that is only scalar content.
|
|
@Suite("Undefined tag handle")
|
|
struct UndefinedTagHandleTests {
|
|
private func codes(_ yaml: String) -> [String] {
|
|
PureYAML.validationReport(yaml).diagnostics.compactMap(\.code)
|
|
}
|
|
|
|
@Test("A handle not redeclared in a later document is flagged there, not in the declaring one")
|
|
func test_handleScopeIsPerDocument() {
|
|
let yaml = """
|
|
%TAG !prefix! tag:example.com,2011:
|
|
--- !prefix!A
|
|
a: b
|
|
--- !prefix!B
|
|
c: d
|
|
"""
|
|
// The ported engine is strict (official suite case QLJ7): the second document, which
|
|
// did not redeclare the handle, fails the parse, and the report carries that failure
|
|
// as a parse diagnostic instead of the old permissive-parse pre-pass diagnostic.
|
|
let report = PureYAML.validationReport(yaml)
|
|
let parseErrors = report.diagnostics.filter { $0.kind == .parse && $0.severity == .error }
|
|
#expect(parseErrors.count == 1)
|
|
#expect(parseErrors.first?.reason.contains("undefined tag handle") == true)
|
|
#expect(parseErrors.first?.line == 4)
|
|
}
|
|
|
|
@Test("A handle redeclared for each document is accepted with no diagnostic")
|
|
func test_redeclaredHandleIsClean() {
|
|
let yaml = """
|
|
%TAG !m! !my-
|
|
--- !m!light fluorescent
|
|
...
|
|
%TAG !m! !my-
|
|
--- !m!light green
|
|
"""
|
|
#expect(!codes(yaml).contains("undefinedTagHandle"))
|
|
}
|
|
|
|
@Test("A local tag, the secondary handle, and a verbatim tag are not handle references")
|
|
func test_nonHandleTagsAreClean() {
|
|
#expect(!codes("a: !foo bar").contains("undefinedTagHandle"))
|
|
#expect(!codes("a: !!str x").contains("undefinedTagHandle"))
|
|
#expect(!codes("a: !<tag:example.com,2011:x> y").contains("undefinedTagHandle"))
|
|
}
|
|
|
|
@Test("A bang pair inside scalar content is not a tag and is never flagged")
|
|
func test_noFalsePositiveInScalarContent() {
|
|
#expect(!codes("msg: hello !world! bye").contains("undefinedTagHandle"))
|
|
#expect(!codes("quoted: \"a !x! b\"").contains("undefinedTagHandle"))
|
|
}
|
|
}
|