///|
/// Reusable boundary cases for callers that want a predictable preflight suite.
pub struct BoundaryCase {
name : String
input : String
expected_valid : Bool
focus : String
} derive(Debug, Eq)
///|
pub fn BoundaryCase::new(
name : String,
input : String,
expected_valid : Bool,
focus : String,
) -> BoundaryCase {
{ name, input, expected_valid, focus }
}
///|
pub fn BoundaryCase::is_empty(self : BoundaryCase) -> Bool {
self.input == ""
}
///|
pub fn BoundaryCase::to_markdown(self : BoundaryCase) -> String {
"| " +
self.name +
" | " +
self.focus +
" | " +
self.expected_valid.to_string() +
" |"
}
///|
pub struct BoundaryResult {
case_name : String
expected_valid : Bool
actual_valid : Bool
diagnostics : String
} derive(Debug, Eq)
///|
pub fn BoundaryResult::passed(self : BoundaryResult) -> Bool {
self.expected_valid == self.actual_valid
}
///|
pub fn BoundaryResult::to_markdown(self : BoundaryResult) -> String {
let status = if self.passed() { "pass" } else { "fail" }
"| " +
self.case_name +
" | " +
self.expected_valid.to_string() +
" | " +
self.actual_valid.to_string() +
" | " +
status +
" | " +
self.diagnostics +
" |"
}
///|
pub struct BoundarySuite {
name : String
cases : Array[BoundaryCase]
} derive(Debug, Eq)
///|
pub fn BoundarySuite::new(
name : String,
cases : Array[BoundaryCase],
) -> BoundarySuite {
{ name, cases }
}
///|
pub fn BoundarySuite::parser_edges() -> BoundarySuite {
BoundarySuite::new("parser boundary suite", [
BoundaryCase::new("empty", "", false, "empty input"),
BoundaryCase::new("whitespace", " \n\t", false, "blank input"),
BoundaryCase::new("small-json", "{\"name\":\"x\"}", false, "missing schema"),
BoundaryCase::new(
"trailing-json", "{\"name\":\"x\"} trailing", false, "trailing content",
),
BoundaryCase::new("bad-json", "{\"name\":", false, "unterminated object"),
BoundaryCase::new("bad-yaml", "name:\n -", false, "incomplete sequence"),
BoundaryCase::new(
"duplicate-key", "name: x\nname: y", false, "duplicate mapping key",
),
BoundaryCase::new(
"deep-json", "{\"a\":{\"b\":{\"c\":1}}}", false, "incomplete schema",
),
])
}
///|
pub fn BoundarySuite::document_edges() -> BoundarySuite {
BoundarySuite::new("document boundary suite", [
BoundaryCase::new(
"minimal-name", "{\"name\":\"x\"}", false, "required sections",
),
BoundaryCase::new(
"missing-palette", "{\"name\":\"x\",\"tagline\":\"y\"}", false, "palette",
),
BoundaryCase::new("wrong-type", "{\"name\":12}", false, "scalar type"),
BoundaryCase::new(
"empty-array", "{\"name\":\"x\",\"palette\":{\"colors\":[]}}", false, "empty palette",
),
BoundaryCase::new(
"unknown-fields", "{\"name\":\"x\",\"unexpected\":true}", false, "forward compatibility",
),
])
}
///|
pub fn BoundarySuite::len(self : BoundarySuite) -> Int {
self.cases.length()
}
///|
pub fn BoundarySuite::case_names(self : BoundarySuite) -> Array[String] {
self.cases.map(fn(item) { item.name })
}
///|
pub fn BoundarySuite::run(self : BoundarySuite) -> Array[BoundaryResult] {
self.cases.map(fn(item) {
let parsed = parse_brand_document(item.input)
BoundaryResult::{
case_name: item.name,
expected_valid: item.expected_valid,
actual_valid: parsed.value is Some(_) && !parsed.diagnostics.has_errors(),
diagnostics: parsed.diagnostics.summary(),
}
})
}
///|
pub fn BoundarySuite::passed(self : BoundarySuite) -> Bool {
self.run().all(fn(result) { result.passed() })
}
///|
pub fn BoundarySuite::failures(self : BoundarySuite) -> Array[BoundaryResult] {
self.run().filter(fn(result) { !result.passed() })
}
///|
pub fn BoundarySuite::to_markdown(self : BoundarySuite) -> String {
let lines : Array[String] = [
"## " + self.name,
"",
"| Case | Expected | Actual | Status | Diagnostics |",
"| --- | --- | --- | --- | --- |",
]
for result in self.run() {
lines.push(result.to_markdown())
}
lines.join("\n")
}
///|
pub fn BoundarySuite::to_json(self : BoundarySuite) -> String {
"{\"name\":\"" +
json_escape(self.name) +
"\",\"passed\":" +
self.passed().to_string() +
",\"results\":[" +
self
.run()
.map(fn(result) {
"{\"case\":\"" +
json_escape(result.case_name) +
"\",\"passed\":" +
result.passed().to_string() +
",\"diagnostics\":\"" +
json_escape(result.diagnostics) +
"\"}"
})
.join(",") +
"]}"
}
///|
pub fn standard_boundary_suites() -> Array[BoundarySuite] {
[BoundarySuite::parser_edges(), BoundarySuite::document_edges()]
}
///|
pub fn run_boundary_suites() -> Bool {
standard_boundary_suites().all(fn(suite) { suite.passed() })
}
///|
pub fn boundary_report_markdown() -> String {
let sections : Array[String] = ["# Boundary report", ""]
for suite in standard_boundary_suites() {
sections.push(suite.to_markdown())
sections.push("")
}
sections.join("\n")
}
///|
pub fn boundary_report_json() -> String {
"[" +
standard_boundary_suites().map(fn(suite) { suite.to_json() }).join(",") +
"]"
}