Skip to content

Fix a trio of schema validation bugs - #421

Open
dmarcotte wants to merge 10 commits into
kson-org:mainfrom
dmarcotte:schema-improvements
Open

Fix a trio of schema validation bugs#421
dmarcotte wants to merge 10 commits into
kson-org:mainfrom
dmarcotte:schema-improvements

Conversation

@dmarcotte

@dmarcotte dmarcotte commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Fix up a trio of gaps in how we serve parse requests with schema validation, plus some clean-ups adjacent to this work that either supported the fixes or came up naturally during this work. The bugs were all of the same flavor (incorrect or incomplete parse messages when a schema is provided), so I tackled them together here even though it makes this PR quite large. The individual commits should be read rather than reading this PR as a whole.

NOTE: bb3cbfb is a breaking change to the public API, adding a parameter to the analyze endpoint.

Fixes

  • 3752f63 Stop reporting OK against broken schemas: a schema that could not be used reported nothing at all, so a document looked fine
  • 0f2de3f Do not silently swallow attached schemas in LSP: the language server dropped a workspace schema it could not use rather than saying so
  • cea3326 Make schema validation part of "analyze": stop silently dropping warnings when given a schema to validate against. Breaking change: adds an optional SchemaValidator parameter to the public Kson.analyze with it

Clean-ups

  • 36e9977: Clarify ignoreErrors parse contract
  • e6a520b: Fix assertEquals argument order in CLI tests
  • 0806f34: Stop applying LargeClass lint to tests
  • b8942b7: Cover the CLI's --schema switch
  • 3b3acbf: Use null for "no schema", not the trivial schema
  • c25753f: Don't pollute cli output with "schema success!"
  • 826a825: Fix inaccuracy in cli's validate help

Validating a document against a schema with problems was resulting in
the schema being ignored entirely and the validation reporting an
inaccurate "OK!"

We now add a diagnostic to the validation's results noting the invalid
schema so the reader knows the validation could not succeed (it is
impossible to be valid against an invalid schema) with a pointer to
what the real problem is.

Note that "schema with problems" here is broader than "schema won't
parse": a schema can parse cleanly and still be invalid (`type: 5`, for
instance).
A caller who asked the parser to `ignoreErrors` should not really have
any business then asking the resulting (possibly error-stitched) AST
for a KsonValue (KsonValue is explicitly designed to represent fully
valid KSON values), but nothing in the type design prevented it,
so we've got a couple of callers who are taking advantage of being able
to get a KsonValue when possible (and null otherwise).

This makes for a somewhat inconsistent abstraction: we _want_ for
`messages` to have errors logged any time an AST is invalid, and hence
cannot be turned into a KsonValue, but `ignoreErrors` currently creates
a hole where it's possible for an `AstParseResult` to have a null
`ksonValue` with no messages to explain why.

This is currently not a problem in the code, so we've queued up the
intention to refactor this and in the meantime this commit simply
clarifies the AstParseResult contract for `ignoreErrors` and improves
a bit of the code around it:

`parseSchema` can be less defensive around null, and
`AstParseResult.ksonValue` can be improved to _only_ swallow errors when
`ignoreErrors` is used, rather than risk masking real bugs (neither the
test suite nor manual testing revealed any already-masked bugs, so as
far as we know, the time we were inappropriately swallowing these has
not allowed any bugs to creep in)
Our schema loader was inappropriately "helpful", swallowing schemas with
any parse message (even warnings!) and making it appear to callers in
that case that NO schema was loaded.

In the face of errors in an attached schema, the LSP now tells users
that the schema they asked to validate against is _impossible_ to
validate against. For schemas whose only problems are warnings in their
KSON source, the LSP now correctly uses the schema to validate the
document it is attached to, rather than possibly invalidly declaring the
document problem-free.

Testing this wanted a `SchemaProvider` stub identical to what was
implemented for `CompositeSchemaProvider`, so that stub graduates to
`TestHelpers` and is shared from there. We also introduce
`KsonDocumentsManager` tests covering the link that carries a loaded
schema to the document that asked for it.
`assertCommand` had the classic argument order mistake in its
`assertEquals`. Fix the order so we don't trip again on the incorrect
expected/actual output when a test fails.
The LargeClass lint does not apply to tests since tests are a collection
of individual little "apps" that verify the class the test is linked to
by name, so a large test class never implies an incoherent or sprawling
class interface, rather it means good coverage for some other hopefully
coherent class.

We had already ad-hoc been declining to apply the LargeClass lint to
test classes (as evidenced by the baseline entries we were able to
remove as part of this commit), so this change just makes more ergonomic
a policy we are already applying.
Seems the CLI's `--schema` switch somehow dodged being tested. Fill that
gap now that we've spotted it.
Using the "trivial" JSON Schema to represent "no schema" worked, but
erased the downstream code's ability to easily/robustly differentiate
between calls that asked for schema validation and those which did not.
Using `null` here is semantically more accurate and robust.

As it stands today, this change is a pure refactor with no behavior
change and hence no test changes. This change was motivated by upcoming
work in schema handling that likely needs/wants this improved ability
to distinguish when no schema was passed.
NOTE: this is a breaking change to the public API, adding a parameter
to the `analyze` endpoint.

Our root analysis entry point did not previously have the ability to
incorporate schema validation (SchemaValidator lives as a standalone
object so a schema can be parsed once and used to validate many KSON
documents), but of course in the case of a schema'ed document, that
validation should definitely be part of "analyzing" a KSON document.

So: `Kson.analyze` now takes optionally a SchemaValidator object to
use as part of its analysis. This lets callers choose how they'd like
to use the SchemaValidator:
- as a pure schema validator that ONLY does schema validation (in
particular, it does not concern itself with KSON language-level warnings
when asked to validate a chunk of KSON source)
- or does the caller want a _full_ analysis of the KSON source, with
all messages/errors/warnings returned at once?

This fixes a bug in `validate` code paths (including the KSON Cli entry
point) where KSON warnings were always swallowed if a schema was also
given to validate against.
`validateWithSchema` was echo'ing "Document is valid according to the
schema" to stdout, where `json`, `yaml` and `format` write their results
by default, corrupting output that likely often wants to be piped
elsewhere as pure data.

Also update the cli test suite to catch this type of stdout pollution
in the future by adding an assertion in `assertCommand` that ensures
nothing extraneous gets written to stdout on success of any of our
commands, letting the commands' results and their exit code speak
for themselves.
The `validate` command exits 1 on errors _and_ warnings, intentionally,
so fix up the help text to properly reflect that.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant