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.