test(compatibility): add downstream document fixtures

This commit is contained in:
Mihaela Mihaljevic 2026-06-05 13:45:23 +02:00
parent 92b0d7c92d
commit d42da7fb67
4 changed files with 257 additions and 20 deletions

View File

@ -74,6 +74,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
fallback behavior, and direct value-tree validation.
- Add fixture-backed literal block emission coverage for newly supported
multiline content and exact quoted fallbacks.
- Add downstream-shaped document fixtures for API-style documents, service
configuration decoding, and unsupported merge-key fallback behavior.
### Changed

View File

@ -63,26 +63,7 @@ flowchart TB
#10 Typed Decoding and Encoding is complete in main.
#11 YAML Compatibility Corpus is active:
```mermaid
flowchart TB
classDef done fill:#ddf9e4,stroke:#34c759,color:#111827
classDef review fill:#fff7d6,stroke:#ffcc00,color:#111827
classDef epic fill:#f2e5ff,stroke:#af52de,color:#111827
classDef todo fill:#f2f4f7,stroke:#8e8e93,color:#111827
Epic11["#11 YAML Compatibility Corpus"]:::epic
Scalar24["#24 Scalar and Tag Corpus"]:::done
Collection25["#25 Collection and Anchor Corpus"]:::done
Gaps27["#27 Unsupported YAML Gaps"]:::done
Literal19["#19 Literal Block Emission"]:::done
Downstream26["#26 Downstream Document Fixtures"]:::todo
Epic11 --> Scalar24
Scalar24 --> Collection25
Collection25 --> Gaps27
Gaps27 --> Literal19
Literal19 --> Downstream26
```
#11 YAML Compatibility Corpus is complete in main.
## Status

View File

@ -0,0 +1,113 @@
@testable import PureYAML
let openAPIStyleDocumentYAML = """
openapi: 3.1.0
info:
title: Article API
version: 1.2.3
servers:
- url: https://api.example.com
paths:
/articles/{id}:
get:
operationId: getArticle
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
"200":
description: Article response
content:
application/json:
schema:
$ref: "#/components/schemas/Article"
components:
schemas:
Article:
type: object
required: [id, title, tags]
properties:
id: {type: string, format: uuid}
title:
type: string
tags:
type: array
items:
type: string
summary:
type: string
nullable: true
"""
let downstreamServiceDocumentYAML = """
service:
name: TileDown
version: "0.4.2"
enabled: true
tags: [static, yaml]
limits:
retries: 3
timeoutSeconds: 30
owners:
- name: Mihaela
email:
- name: Build Bot
email: bot@example.com
metadata:
region: eu
tier: production
"""
let downstreamServiceDocument = DownstreamServiceDocument(
service: .init(
name: "TileDown",
version: "0.4.2",
enabled: true,
tags: ["static", "yaml"],
),
limits: .init(retries: 3, timeoutSeconds: 30),
owners: [
.init(name: "Mihaela", email: nil),
.init(name: "Build Bot", email: "bot@example.com"),
],
metadata: [
"region": "eu",
"tier": "production",
],
)
let downstreamMergeKeyYAML = """
defaults: &serviceDefaults {retries: 3, timeoutSeconds: 30}
services:
api:
<<: *serviceDefaults
name: API
"""
struct DownstreamServiceDocument: Codable, Equatable {
var service: DownstreamService
var limits: DownstreamLimits
var owners: [DownstreamOwner]
var metadata: [String: String]
}
struct DownstreamService: Codable, Equatable {
var name: String
var version: String
var enabled: Bool
var tags: [String]
}
struct DownstreamLimits: Codable, Equatable {
var retries: Int
var timeoutSeconds: Int
}
struct DownstreamOwner: Codable, Equatable {
var name: String
var email: String?
}

View File

@ -0,0 +1,141 @@
@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("Pins unsupported downstream merge keys as unflattened fallback values")
func test_downstreamMergeKeyFallbackFixture() 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")
let merge = expectMapping(api?["<<"], "expected unflattened merge mapping")
#expect(root.pairs.map(\.key) == ["defaults", "services"])
#expect(api?.pairs.map(\.key) == ["<<", "name"])
#expect(defaults?["retries"] == .int(3))
#expect(defaults?["timeoutSeconds"] == .int(30))
#expect(merge == defaults)
#expect(api?["name"] == .string("API"))
#expect(api?["retries"] == nil)
#expect(api?["timeoutSeconds"] == nil)
#expect(PureYAML.Validation.Validator().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
}
}