Test Battery
The following is a long set of validation scenarios, each consists of a schema followed by a set of YAML documents that should or should not be valid under it.
## Count Validation
Given a Schema:
---
//foo:
count: 1
Then this YAML document is valid:
---
foo: true
And this YAML document is valid:
---
foo: true
bar: true
But this YAML document is not valid:
---
- foo: true
- foo: true
## Exclusive Constraints
As a reminder, exclusion can either be a boolean expression, in which case it validates that there is no more than one matching node, or the value is taken to be a YPath and validates that there are no matching paths if the main selection is present.
### Boolean Cases
Given a Schema:
---
//foo:
exclusive: true
Then this YAML document is valid:
---
- foo: true
And this YAML document is valid:
---
- foo: true
- bar: true
But this YAML document is not valid:
---
- foo: true
- foo: false
### YPath Cases
Given a Schema:
---
//foo:
exclusive: //bar
Then this YAML document is valid:
---
- foo: true
But this YAML document is not valid:
---
- foo: true
- bar: false
## Inclusive Constraints
As a reminder, inclusion can either be a boolean expression, in which case it validates that there is at least one matching node, or the value is taken to be a ypath and validates that there are matching paths if the main selection is present.
### Boolean Cases
Given a Schema:
---
//foo:
inclusive: true
Then this YAML document is valid:
---
foo: true
And this YAML document is valid:
---
foo: true
bar: true
But this YAML document is not valid:
---
bar: true
### YPath Cases
Given a Schema:
---
//foo:
inclusive: //bar
Then this YAML document is valid:
---
- foo: true
- bar: true
## Tag Validation
Given a Schema:
---
//name:
tag: "!<tag:yes.com,2011:name>"
Then this YAML document is valid:
---
name: !!<tag:yes.com,2011:name> Choo Choo Train
And this YAML document is valid:
---
- name: !!<tag:yes.com,2011:name> Choo Choo Train
But this YAML document is not valid:
---
- name: Choo Choo Train
# Constraints
## Tag Constraint
Given a schema with a ‘type` constraint:
---
//name:
tag: '<tag:yes.com,2011:name>'
And a YAML document that has matching nodes:
---
- name: !!<tag:yes.com,2011:name> Choo Choo Train
Then validation of the YAML document with the schema will be valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks matching nodes:
---
- name: Jar Jar Binks
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
## Type Constraint
Given a schema with a ‘type` constraint:
---
//foo:
type: str
And a YAML document that has matching nodes:
---
- foo: I'm a string!
Then validation of the YAML document with the schema will be valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks matching nodes:
---
- foo: 15907
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
## Requires Constraint
Given a schema with a ‘requires` constraint:
---
/foo:
requires:
- bar
And a YAML document that has the required path:
---
foo:
bar: true
Then validation of the YAML document with the schema will ve valid and retun no validation errors.
yes = YES::Lint.new(@schema)
yes.validate(@yaml).assert.empty?
Given a YAML document that lacks the required path:
---
foo: true
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
TODO: more detailed assertions on returned errors list.
## Range Constraint
### Fixed Range
Given a schema with a ‘range` constraint using a single value, e.g. `1` then the range is equivalent to `1..1`:
---
//foo:
range: 1
And a YAML document that has matching nodes:
---
- foo: 1
Then validation of the YAML document with the schema will ve valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks matching nodes:
---
- foo: 2
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
### Range
Given a schema with a ‘range` constraint using a fixed range:
---
//foo:
range: 1..2
And a YAML document that has matching nodes:
---
- foo: 1
- foo: 2
Then validation of the YAML document with the schema will ve valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks matching nodes:
---
- foo: 3
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
### Range N
Given a schema with a ‘range` constraint using a range from a fixed number to `n`, respesenting infinity:
---
//foo:
range: 2..n
And a YAML document that has matching nodes:
---
- foo: 2
- foo: 3
Then validation of the YAML document with the schema will be valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks matching nodes:
---
- foo: 1
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
TODO: more detailed assertions on returned errors list.
Regular Expression Constraint
Becuase regular expresion engines can vary somewhat across implementations it is wise to stick to the basics.
Given a schema with a ‘regexp` constraint:
---
//email:
regexp: /@/
And a YAML document that has matching nodes:
---
- email: [email protected]
Then validation of the YAML document with the schema will be valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks matching nodes:
---
- email: 15907
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
File Name Constraint
Given a schema with a ‘fnmatch` constraint:
---
//path:
fnmatch: "*.rb"
And a YAML document that has matching nodes:
---
- email: foo.rb
Then validation of the YAML document with the schema will be valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks matching nodes:
---
- path: foo.txt
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
## Length Constraint
### Fixed Length
Given a schema with a ‘length` constraint using a single number:
---
//foo:
length: 1
And a YAML document that has that number of nodes:
---
- foo: "A"
Then validation of the YAML document with the schema will ve valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks the right number of nodes:
---
- foo: "AB"
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
### Range
Given a schema with a ‘length` constraint using a fixed range:
---
//foo:
length: 1..2
And a YAML document that has that range of nodes:
---
- foo: "A"
- foo: "AB"
Then validation of the YAML document with the schema will ve valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks the right number of nodes:
---
- foo: ""
- foo: "ABC"
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
### Range N
Given a schema with a ‘length` constraint using a range from a fixed number to `n`, respesenting infinity:
---
//foo:
length: 2..n
And a YAML document that has such a range of nodes:
---
- foo: "AB"
- foo: "ABC"
Then validation of the YAML document with the schema will be valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks the right number of nodes:
---
- foo: "A"
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
TODO: more detailed assertions on returned errors list.
## Count Constraint
### Fixed Count
Given a schema with a ‘count` constraint using a single number:
---
//foo:
count: 1
And a YAML document that has that number of nodes:
---
- foo: true
Then validation of the YAML document with the schema will ve valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks the right number of nodes:
---
- foo: true
- foo: true
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
### Range
Given a schema with a ‘count` constraint using a fixed range:
---
//foo:
count: 1..2
And a YAML document that has that range of nodes:
---
- foo: true
Then validation of the YAML document with the schema will ve valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks the right number of nodes:
---
- foo: true
- foo: true
- foo: true
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
### Range N
Given a schema with a ‘count` constraint using a range from a fixed number to `n`, respesenting infinity:
---
//foo:
count: 2..n
And a YAML document that has such a range of nodes:
---
- foo: true
- foo: true
- foo: true
Then validation of the YAML document with the schema will be valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks the right number of nodes:
---
- foo: true
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
TODO: more detailed assertions on returned errors list.
## Inclusive Constraint
### Boolean
Given a schema with an ‘inclusive` constraint using a boolean value:
---
//foo:
inclusive: true
And a YAML document that has that includes a matching node:
---
- foo: true
Then validation of the YAML document with the schema will ve valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks a matching node:
---
- bar: true
- baz: true
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
### YPath
Given a schema with an ‘inclusive` constraint using a YPath value:
---
//foo:
inclusive: //bar
And a YAML document that has that includes a matching node:
---
- foo: true
- bar: true
Then validation of the YAML document with the schema will ve valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks a matching node:
---
- foo: true
- baz: true
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
## Exclusive Constraint
### Boolean
Given a schema with an ‘exclusive` constraint using a boolean value:
---
//foo:
exclusive: true
And a YAML document that has that includes a matching node:
---
- foo: true
Then validation of the YAML document with the schema will ve valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks a matching node:
---
- foo: true
- foo: true
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
### YPath
Given a schema with an ‘exclusive` constraint using a YPath value:
---
//foo:
exclusive: //bar
And a YAML document that has that includes a matching node:
---
- foo: true
- gah: true
Then validation of the YAML document with the schema will ve valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks a matching node:
---
- foo: true
- bar: true
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
That covers the basics of exclusive constraints. See here for an exhustive battery of tests.
## Value Constraint
The value constraint differs from other constraints in that it actually applies a separate set of constraints on the value of a sequence’s or mapping’s elements.
Given a schema with an ‘valuee` constraint:
---
//foo:
value:
type: str
And a YAML document that that includes a matching node:
---
foo:
- "okay"
Then validation of the YAML document with the schema will be valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks a matching node:
---
foo:
- 0
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
## Key Constraint
The key constraint differs from other constraints in that it actually applies a separate set of constraints on the keys of a mapping.
Given a schema with a ‘key` constraint:
---
//foo:
key:
type: str
And a YAML document that that includes a matching node:
---
foo:
bar: okay
baz: okay
Then validation of the YAML document with the schema will be valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks a matching node:
---
foo:
0: "not okay"
1: "not okay"
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
## Required Constraint
Given a schema with a ‘required` constraint:
---
//foo:
required: true
And a YAML document that hasthe required path:
---
foo: true
Then validation of the YAML document with the schema will ve valid and retun no validation errors.
yes = YES::Lint.new(@schema)
yes.validate(@yaml).assert.empty?
If given a YAML document that lacks the required path:
---
bar: true
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
TODO: more detailed assertions on returned errors list.
## Lint#select_nodes
The ‘select_nodes` method can take a YPath string or a complex selector.
A Complex selector is a mapping (i.e. a Ruby Hash) that uses node constraints to select nodes from the document tree.
To try this out we need a Lint instance, which we will supply an empty, dummy schema, as w won’t be testing validation.
yes = YES::Lint.new('')
Given a YAML document:
---
- Choo Choo Train
- Choo Choo Toy
Which we parse into a node tree.
tree = YAML.parse(@yaml)
A complex selector can be used to match nodes using the #select_nodes method.
complex_selector = { regexp: /^Choo/ }
selected = yes.select_nodes(complex_selector, tree)
selected.size.assert == 2
## Sub-Selecting Nodes with Map Field
The ‘map` field, while appearing alongside constraint definitions in the schema, is not actually a constraint, but rather a sub-ypath selector that combines the parent ypath with the mapping keys that are given under it. This provides a convenient way to keep sub-paths local to their parent nodes, rather then create separate long-name ypath entries. Obviously the `map` field only effectively applies to mapping nodes.
Lets’ take an example of the more explicit form:
---
foo:
type: map
foo/bar:
type: str
This can be writ instead, Given a schema with a ‘map` field:
---
foo:
type: map
map:
bar:
type: str
And a YAML document that includes a matching node:
---
foo:
bar: okay
Then validation of the YAML document with the schema will be valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks a matching node:
---
foo:
bar: 0
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
## Using a Complex Selector
Given a schema with a hash based selector:
---
? { regexp: /^Choo/ }
: { count: 2 }
And a YAML document that has matching nodes:
---
- Choo Choo Train
- Choo Choo Toy
Then validation of the YAML document with the schema will be valid and retun no validation errors.
yes = YES::Lint.new(@schema)
errors = yes.validate(@yaml)
errors.assert.empty?
If given a YAML document that lacks matching nodes:
---
- Choo Choo Train
Then the validation will return errors.
errors = yes.validate(@yaml)
errors.refute.empty?
## YPath Index
Given a YAML document:
---
foo: "foo1"
bar:
foo: "foo2"
baz: "baz"
Lets let try out some YPaths.
s = @yaml.select('foo')
s.size.assert == 1
s1 = s.first
s1.value.assert == "foo1"
Try another
s = @yaml.select('//foo')
s.size.assert == 2
s1 = s.last
s1.value.assert == "foo2"
## YPath by Tag
Given a YAML document:
---
foo: "foo1"
bar: !example
foo: "foo2"
baz: "baz"
It would be nice if we could select nodes by tag, like:
s = @yaml.select('!example')
s.size.assert == 1
s1 = s.first
s1.type.assert == "map"
But this does not work at this time.