feat(validation): configure the validator by rule identity
Move the validator toward the validation-rules idiom's identity-based configuration. withoutValidating(_:) removes a rule by its description, the removal identity the idiom uses, so descriptions must be unique and stable. validationDescriptions lists the active rules in application order. A configuration-pin test asserts the exact descriptions of the blank and default validators and that the default descriptions are unique, so adding, removing, or rewording a rule is a deliberate change that a test catches. Both additions are non-breaking; the type-directed Validation<Subject> dispatch and the error-rendering change remain the larger migration steps.
This commit is contained in:
parent
85663682a2
commit
d3b4fab0d9
@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Added
|
||||
|
||||
- The validator is now configurable by rule identity, per the validation-rules idiom:
|
||||
`withoutValidating(_ description:)` removes a rule by its description (the removal identity, so
|
||||
descriptions must be unique and stable), and `validationDescriptions` lists the active rules in
|
||||
application order. A configuration-pin test asserts the exact descriptions of the blank and
|
||||
default validators, so adding, removing, or rewording a rule is a deliberate change.
|
||||
- A validation-coverage gate (`scripts/check-validation-coverage.sh`, wired into
|
||||
`scripts/check-all.sh` and CI) enforcing the validation-rules discipline that every public type is
|
||||
either validated or excluded. It enumerates the public surface from the compiler symbol graph (the
|
||||
|
||||
@ -63,6 +63,21 @@ public extension PureYAML.Validation {
|
||||
copy.rules.append(rule)
|
||||
return copy
|
||||
}
|
||||
|
||||
/// Removes every rule whose description matches, the identity the validation idiom uses for
|
||||
/// removal. Descriptions are therefore required to be unique and stable across the rule set.
|
||||
public func withoutValidating(_ description: String) -> Self {
|
||||
var copy = self
|
||||
copy.rules.removeAll { $0.description == description }
|
||||
return copy
|
||||
}
|
||||
|
||||
/// The descriptions of the active rules, in application order: the fixed point a
|
||||
/// configuration-pin test asserts against, so adding, removing, or rewording a rule changes
|
||||
/// it deliberately.
|
||||
public var validationDescriptions: [String] {
|
||||
rules.map(\.description)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
39
Tests/ValidationConfigurationTests.swift
Normal file
39
Tests/ValidationConfigurationTests.swift
Normal file
@ -0,0 +1,39 @@
|
||||
@testable import PureYAML
|
||||
import Testing
|
||||
|
||||
@Suite("Validator configuration")
|
||||
struct ValidationConfigurationTests {
|
||||
private var duplicateKeys: PureYAML.Model.Value {
|
||||
.mapping(.init([
|
||||
.init(key: "a", value: .int(1)),
|
||||
.init(key: "a", value: .int(2)),
|
||||
]))
|
||||
}
|
||||
|
||||
@Test("The blank validator has no rules; the default set is pinned by description")
|
||||
func test_configurationDescriptionsArePinned() {
|
||||
#expect(PureYAML.Validation.Validator.blank.validationDescriptions == [])
|
||||
#expect(PureYAML.Validation.Validator().validationDescriptions == ["Mapping keys are unique"])
|
||||
}
|
||||
|
||||
@Test("Default rule descriptions are unique, the identity withoutValidating removes by")
|
||||
func test_defaultDescriptionsAreUnique() {
|
||||
let descriptions = PureYAML.Validation.Validator().validationDescriptions
|
||||
#expect(Set(descriptions).count == descriptions.count)
|
||||
}
|
||||
|
||||
@Test("withoutValidating removes a rule by its description")
|
||||
func test_withoutValidatingRemovesByDescription() throws {
|
||||
let validator = PureYAML.Validation.Validator().withoutValidating("Mapping keys are unique")
|
||||
#expect(validator.validationDescriptions == [])
|
||||
// With the rule removed, a document with duplicate keys validates clean.
|
||||
_ = try validator.validate(duplicateKeys)
|
||||
}
|
||||
|
||||
@Test("The default validator still catches what its removable rule guards")
|
||||
func test_defaultStillValidates() {
|
||||
#expect(throws: PureYAML.Validation.Issue.Collection.self) {
|
||||
try PureYAML.Validation.Validator().validate(duplicateKeys)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user