# Qwiratry::Test

Reusable conformance ("contract") test kit for [Qwiratry](https://github.com/wayland/raku-Qwiratry)
**Format** and **Location** plugins.

Instead of every plugin re-writing the same discovery / factory / round-trip /
error-handling tests, a plugin depends on `Qwiratry::Test` at test time and calls
one routine that runs the shared battery against its implementation. The plugin
then only needs to add tests for behaviour that is genuinely specific to it.

## Requirements

- Rakudo with RakuAST (`RAKUDO_RAKUAST=1`), as required by Qwiratry itself.
- [`Qwiratry`](https://github.com/wayland/raku-Qwiratry) (a runtime dependency of this kit).

## Installation

```bash
zef install Qwiratry::Test
```

## Usage

Add the kit to your plugin's `META6.json` as a test-phase dependency:

```json
"depends": {
  "runtime": { "requires": [ "Qwiratry" ] },
  "test":    { "requires": [ "Qwiratry::Test" ] }
}
```

(The classic flat `"test-depends": [ "Qwiratry::Test" ]` array works too.)

### Testing a format plugin

```raku
use Test;
use Qwiratry::Test::Format;

conforms-to-format-contract(
    format        => 'JSON',
    parse-samples => [ '{"b":2,"a":1}', '[1,2,3]' ],
    render-values => [ { :a(1), :b(2) }, [1, 2, 3] ],
    :lossless,
);

done-testing;
```

`conforms-to-format-contract` verifies:

- the format is discoverable through `Qwiratry::Format.formats`;
- the factory autoloads `::Parse` / `::Render` classes that inherit
  `Qwiratry::Format::Base::Parse` / `::Render`;
- `parse` yields defined data and `render` yields `Str`;
- the lossless round-trip law `parse ∘ render ∘ parse == parse` (when `:lossless`);
- the `↱` (parse) and `↴` (render) pipeline operators agree with direct calls;
- invalid input is rejected (when `:invalid-samples` are supplied).

Everything except `:format` is optional and capability-driven — pass only the
samples and flags that apply to your format. `:parse`/`:render` (default both)
and `:pipeline` (default on) let you narrow the surface.

### Testing a location plugin

```raku
use Test;
use Qwiratry::Test::Location;

conforms-to-location-contract(
    backend            => 'HTTP',
    scheme             => 'https',
    sample-location    => 'https://example.test/data',
    :!writable,          # a GET-only backend skips write/round-trip checks
    :needs-network,      # gate live checks behind QWIRATRY_TEST_NETWORK
);

done-testing;
```

`conforms-to-location-contract` verifies:

- the backend is discoverable through `Qwiratry::Location.backends`;
- the factory autoloads `::Source` / `::Destination` classes that inherit
  `Qwiratry::Location::Base::Source` / `::Destination`;
- the URI scheme resolves to the backend (when `:scheme` is supplied);
- write-then-read round-trips, directly and through the `⮷` / `⮳` operators
  (when both `:writable` and `:readable` and a `:roundtrip-location` are given);
- a missing location raises `X::Qwiratry::IO::LocationError`
  (when `:missing-location` is supplied).

Use `:needs-network` for backends that reach the outside world; those subtests
are skipped unless `QWIRATRY_TEST_NETWORK` is set, so plugin CI stays hermetic by
default.

## How the kit tests itself

To avoid a circular dependency, the base `Qwiratry` distribution does **not**
depend on this kit. Instead, `Qwiratry::Test` depends on `Qwiratry` and its own
test suite runs the kit against Qwiratry's bundled reference implementations —
the `File` location backend and the `JSONdemo` format — so the contract battery
is validated end to end. See `t/`.

## Versioning

The kit's assertions are effectively part of Qwiratry's plugin API. Tightening a
check is a semver-relevant change, so plugins should pin a minimum version, e.g.
`"Qwiratry::Test:ver<0.0.1+>"`.

## License

Licensed under the same terms as Raku itself.

## Author

Tim Nelson (wayland at wayland dot id dot au)
