PureYAML/Tests/DownstreamDocumentTests.swift
Mihaela Mihaljevic bf2dc4ece7 refactor(validation): rename framework to match the validation idiom
The validation module already implemented the composable-rule idiom under
its own names (Check/Issue/SubjectContext) and a value-type Validator with
duplicated traversal machinery per layer. Renamed the core types to the
canonical names (Validation/ValidationError/ValidationErrorCollection/
ValidationContext/BuiltinValidation/AnyValidation), converted Validator to
a reference-type class with the two-tier default-rule split, and genericized
it over both Subject and Document via a new Traversable protocol so the
model and tagged layers share one walk engine instead of two copies. Wired
the JSON-representability rule set into the same shape and closed the two
missing pieces (unwrap combinator, JSON coverage/config-pin tests).
2026-07-12 18:10:00 +02:00

141 lines
7.1 KiB
Swift

@testable import PureYAML
import Testing
@Suite("Downstream Documents")
struct DownstreamDocumentTests {
@Test("Parses OpenAPI-style document fixture with exact representative paths")
func test_openAPIStyleDocumentFixture() throws {
let root = try requireRootMapping(openAPIStyleDocumentYAML)
#expect(root.pairs.map(\.key) == ["openapi", "info", "servers", "paths", "components"])
#expect(root["openapi"] == .string("3.1.0"))
#expect(root["swagger"] == nil)
let info = expectMapping(root["info"], "expected info mapping")
#expect(info?["title"] == .string("Article API"))
#expect(info?["version"] == .string("1.2.3"))
let servers = requireSequence(root["servers"], "expected servers sequence")
#expect(servers?.count == 1)
let server = expectMapping(servers?.first, "expected first server mapping")
#expect(server?["url"] == .string("https://api.example.com"))
let paths = expectMapping(root["paths"], "expected paths mapping")
#expect(paths?["/missing"] == nil)
let articlePath = expectMapping(paths?["/articles/{id}"], "expected article path mapping")
let get = expectMapping(articlePath?["get"], "expected get operation mapping")
#expect(get?.pairs.map(\.key) == ["operationId", "parameters", "responses"])
#expect(get?["operationId"] == .string("getArticle"))
#expect(get?["post"] == nil)
let parameters = requireSequence(get?["parameters"], "expected parameters sequence")
#expect(parameters?.count == 1)
let parameter = expectMapping(parameters?.first, "expected parameter mapping")
#expect(parameter?["name"] == .string("id"))
#expect(parameter?["in"] == .string("path"))
#expect(parameter?["required"] == .bool(true))
let parameterSchema = expectMapping(parameter?["schema"], "expected parameter schema mapping")
#expect(parameterSchema?["type"] == .string("string"))
#expect(parameterSchema?["format"] == .string("uuid"))
let responses = expectMapping(get?["responses"], "expected responses mapping")
#expect(responses?["404"] == nil)
let success = expectMapping(responses?["200"], "expected success response mapping")
#expect(success?["description"] == .string("Article response"))
let content = expectMapping(success?["content"], "expected content mapping")
let mediaType = expectMapping(content?["application/json"], "expected json media type mapping")
let responseSchema = expectMapping(mediaType?["schema"], "expected response schema mapping")
#expect(responseSchema?["$ref"] == .string("#/components/schemas/Article"))
let components = expectMapping(root["components"], "expected components mapping")
let schemas = expectMapping(components?["schemas"], "expected schemas mapping")
let article = expectMapping(schemas?["Article"], "expected Article schema mapping")
#expect(article?["type"] == .string("object"))
let required = requireSequence(article?["required"], "expected required sequence")
#expect(required == [.string("id"), .string("title"), .string("tags")])
let properties = expectMapping(article?["properties"], "expected properties mapping")
let tags = expectMapping(properties?["tags"], "expected tags property mapping")
#expect(tags?["type"] == .string("array"))
let tagItems = expectMapping(tags?["items"], "expected tag item mapping")
#expect(tagItems?["type"] == .string("string"))
let summary = expectMapping(properties?["summary"], "expected summary property mapping")
#expect(summary?["nullable"] == .bool(true))
}
@Test("Decodes downstream service configuration fixture exactly")
func test_downstreamServiceConfigurationFixture() throws {
let parsed = try PureYAML.parse(downstreamServiceDocumentYAML)
let root = expectMapping(parsed, "expected service configuration mapping")
#expect(root?.pairs.map(\.key) == ["service", "limits", "owners", "metadata"])
let service = expectMapping(root?["service"], "expected service mapping")
#expect(service?["name"] == .string("TileDown"))
#expect(service?["version"] == .string("0.4.2"))
#expect(service?["enabled"] == .bool(true))
#expect(requireSequence(service?["tags"]) == [.string("static"), .string("yaml")])
let limits = expectMapping(root?["limits"], "expected limits mapping")
#expect(limits?["retries"] == .int(3))
#expect(limits?["timeoutSeconds"] == .int(30))
let owners = requireSequence(root?["owners"], "expected owners sequence")
#expect(owners?.count == 2)
let firstOwner = expectMapping(owners?.first, "expected first owner mapping")
#expect(firstOwner?["name"] == .string("Mihaela"))
#expect(firstOwner?["email"] == .null)
let secondOwner = expectMapping(owners?.last, "expected second owner mapping")
#expect(secondOwner?["email"] == .string("bot@example.com"))
let metadata = expectMapping(root?["metadata"], "expected metadata mapping")
#expect(metadata?["region"] == .string("eu"))
#expect(metadata?["tier"] == .string("production"))
#expect(metadata?["missing"] == nil)
#expect(try PureYAML.decode(DownstreamServiceDocument.self, from: parsed) == downstreamServiceDocument)
#expect(
try PureYAML.decode(
DownstreamServiceDocument.self,
from: downstreamServiceDocumentYAML,
) == downstreamServiceDocument,
)
}
@Test("Expands downstream merge keys into effective service mappings")
func test_downstreamMergeKeyFixture() throws {
let root = try requireRootMapping(downstreamMergeKeyYAML)
let defaults = expectMapping(root["defaults"], "expected defaults mapping")
let services = expectMapping(root["services"], "expected services mapping")
let api = expectMapping(services?["api"], "expected api service mapping")
#expect(root.pairs.map(\.key) == ["defaults", "services"])
#expect(api?.pairs.map(\.key) == ["retries", "timeoutSeconds", "name"])
#expect(defaults?["retries"] == .int(3))
#expect(defaults?["timeoutSeconds"] == .int(30))
#expect(api?["<<"] == nil)
#expect(api?["name"] == .string("API"))
#expect(api?["retries"] == .int(3))
#expect(api?["timeoutSeconds"] == .int(30))
#expect(PureYAML.Validation.Validator<PureYAML.Model.Value, PureYAML.Model.Value>().collect(.mapping(root)) == .init())
}
func requireRootMapping(_ yaml: String) throws -> PureYAML.Model.Mapping {
guard case let .mapping(mapping) = try PureYAML.parse(yaml) else {
recordIssue("expected root mapping")
return .init()
}
return mapping
}
func expectMapping(
_ value: PureYAML.Model.Value?,
_ message: String,
) -> PureYAML.Model.Mapping? {
guard case let .mapping(mapping)? = value else {
recordIssue(message)
return nil
}
return mapping
}
}