Class: SpecGuard::RSpec::ViolationRenderer

Inherits:
Object
  • Object
show all
Defined in:
lib/specguard/rspec/violation_renderer.rb

Overview

Renders json_schemer's structured validation errors in the grammar and the order used by open-test-intent's bin/validate-intent.

Why not just print json_schemer's own message?

Because the protocol repo ships a validator, and "client and platform agree on what's valid" is worth very little if the two tools say different things about the same annotation. The verdicts already agree: the v1 schema uses only type / enum / required / additionalProperties / properties / items / minLength, all of which both implementations support. The messages do not agree at all:

reference   layer: value 'e2e' is not one of ['unit', 'integration', ...]
json_schemer  value at `/layer` is not one of: ["unit", "integration", ...]

So the text is rebuilt here. It is rebuilt from the structured fields of each error (type, data_pointer, schema, details.missing_keys) and never by string-munging error["error"]: that sentence is gem-version-dependent, so a json_schemer bump would silently drift the output away from the reference. Structured fields are the gem's API; the sentence is not.

Three divergences that are not wording

  1. Order. json_schemer emits in its own traversal order — additionalProperties before required, minLength before required. The reference walks the document deliberately (see validate at bin/validate-intent:150-210): per node, type, then enum, then every missing required key in schema order, then each instance property in insertion order (recursing, or reporting the disallowed additional property), then array, string and number keywords. #render reproduces that traversal as a sort key rather than re-walking the document, so the two tools list the same violations in the same sequence.

  2. Cardinality. Two missing required keys are one json_schemer error carrying details.missing_keys = ["action", "layer"]; the reference prints one line per key (its loop at bin/validate-intent:163-165). The batch is fanned back out here.

  3. additionalProperties does not have the type you would guess. It arrives as type: "schema" (schema_pointer /additionalProperties, schema literally false) with data_pointer on the offending property/entiity, not the object that disallowed it. A renderer dispatching on a guessed "additionalProperties" would render nothing for this case, silently. The error is re-attributed to the parent object here, which is what makes the reference's <root>: additional property 'entiity' is not allowed come out.

A fourth, smaller one: the reference returns as soon as a node's type is wrong ("a type mismatch makes the remaining keywords moot"), while json_schemer reports the type failure and every other keyword at that node — a non-string layer yields both a type and an enum error. See #drop_shadowed_by_type_mismatch.

Defined Under Namespace

Classes: Entry

Constant Summary collapse

ROOT =

What the reference calls the document root in a message.

"<root>"
RANK_TYPE =

Keyword ranks within one node, in the reference's evaluation order. Children start at CHILD_BASE so every keyword of a node sorts before any of its descendants — which is exactly what required-before- property-iteration means.

0
RANK_ENUM =
1
RANK_REQUIRED =
2
RANK_MIN =

minItems / minLength / minimum — instance types are disjoint

3
RANK_MAX =

maxItems / maxLength / maximum

4
RANK_PATTERN =
5
RANK_UNKNOWN =
9
CHILD_BASE =
10
JSON_TYPE_NAMES =

type values json_schemer uses for a failed type keyword. A union ("type": ["string", "null"]) reports the literal "type" instead.

%w[object array string integer number boolean null].freeze
TYPE_KEYWORD =
"type"
REQUIRED_KEYWORD =
"required"
SUBSCHEMA_KEYWORD =

json_schemer's error type for a subschema that rejected the instance without a more specific keyword to blame. additionalProperties: false is one such subschema — and the only one the v1 schema contains — but a literal false schema or an unmatched not reports identically. The name is deliberately the general one; #additional_properties? is what narrows it, so a schema revision that grows a not renders json_schemer's sentence instead of a nonsense "additional property" line about a property that was declared.

"schema"
ADDITIONAL_PROPERTIES_POINTER =
"/additionalProperties"

Instance Method Summary collapse

Instance Method Details

#render(errors, instance) ⇒ Array<String>

Returns reason lines, reference grammar, reference order.

Parameters:

  • errors (Enumerable<Hash>)

    raw json_schemer error hashes

  • instance (Object)

    the document that was validated. Needed for ordering: the reference iterates an object's properties in insertion order, which only the instance knows.

Returns:

  • (Array<String>)

    reason lines, reference grammar, reference order



105
106
107
108
109
# File 'lib/specguard/rspec/violation_renderer.rb', line 105

def render(errors, instance)
  entries = errors.each_with_index.flat_map { |error, seq| entries_for(error, instance, seq) }

  drop_shadowed_by_type_mismatch(entries).sort_by(&:sort_key).map(&:message)
end