overseer-testing-protocol 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +19 -0
- data/README.md +75 -0
- data/SOURCE.json +6 -0
- data/bin/overseer-testing-conformance +53 -0
- data/docs/testing-control-implementation-guide.md +116 -0
- data/docs/testing-control-protocol.md +432 -0
- data/lib/overseer/testing_control/conformance/case_file.rb +120 -0
- data/lib/overseer/testing_control/conformance/http_transport.rb +114 -0
- data/lib/overseer/testing_control/conformance/report.rb +104 -0
- data/lib/overseer/testing_control/conformance/runner.rb +783 -0
- data/lib/overseer/testing_control/discovery.rb +79 -0
- data/lib/overseer/testing_control/json_subset_matcher.rb +120 -0
- data/lib/overseer/testing_control/protocol_v3.rb +298 -0
- data/lib/overseer/testing_control/redaction.rb +111 -0
- data/lib/overseer/testing_protocol.rb +9 -0
- data/protocol/testing-control/v3/conformance-case.schema.json +151 -0
- data/protocol/testing-control/v3/conformance-report.schema.json +255 -0
- data/protocol/testing-control/v3/fixtures/capabilities-response.json +147 -0
- data/protocol/testing-control/v3/fixtures/conformance-case.json +23 -0
- data/protocol/testing-control/v3/fixtures/conformance-report.json +88 -0
- data/protocol/testing-control/v3/fixtures/error-response.json +20 -0
- data/protocol/testing-control/v3/fixtures/manifest.json +13 -0
- data/protocol/testing-control/v3/fixtures/probe-request.json +20 -0
- data/protocol/testing-control/v3/fixtures/probe-response.json +22 -0
- data/protocol/testing-control/v3/fixtures/reset-response.json +15 -0
- data/protocol/testing-control/v3/fixtures/sink-query-request.json +26 -0
- data/protocol/testing-control/v3/fixtures/sink-query-response.json +39 -0
- data/protocol/testing-control/v3/fixtures/state-request.json +20 -0
- data/protocol/testing-control/v3/fixtures/state-response.json +23 -0
- data/protocol/testing-control/v3/openapi.yaml +343 -0
- data/protocol/testing-control/v3/schema.json +772 -0
- metadata +85 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1733fd2abba19f5f65ad617c29110553112503f8797b527a9d59b72023a281d5
|
|
4
|
+
data.tar.gz: a737cfad69c9b6fb01523d7e9273f8bdb9589ef7c89f984749768863388b8480
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e5c6d9bdc242edac4bd68815fbb6d7ce8a59f2b99ead35b1515c4a52eddc6e0b72a0e140a7be121b726fd736ec49cfdca1b46c6d8c9e7ae50d9371ddfb4264e7
|
|
7
|
+
data.tar.gz: 8c2eaeaebf9d1d0d17dbd4660d4664f980844da6b8212cc70c8248fda38fd494395b7d9f27e3d22883462e890b31ebfaadfda5bbd465a9f7479ac02b6e91bf47
|
data/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright 2026 Olistik
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Overseer testing protocol
|
|
2
|
+
|
|
3
|
+
The standalone home of **Overseer testing-control v3**. The Overseer names in
|
|
4
|
+
headers and JSON identify the protocol. Frontend tests, backend tests, curl and
|
|
5
|
+
CI can all use it without installing a particular test runner or application.
|
|
6
|
+
|
|
7
|
+
The protocol gives tests a common way to prepare synthetic data, call the
|
|
8
|
+
application's normal API, and inspect the result. The backend defines the
|
|
9
|
+
available operations and their business meaning.
|
|
10
|
+
|
|
11
|
+
The [specification](docs/testing-control-protocol.md),
|
|
12
|
+
[OpenAPI](protocol/testing-control/v3/openapi.yaml),
|
|
13
|
+
[JSON Schema](protocol/testing-control/v3/schema.json) and
|
|
14
|
+
[fixtures](protocol/testing-control/v3/fixtures/manifest.json) define the wire
|
|
15
|
+
contract. The [implementation guide](docs/testing-control-implementation-guide.md)
|
|
16
|
+
explains states, spies, probes and sinks. Product capability names and their
|
|
17
|
+
schemas belong to each backend.
|
|
18
|
+
|
|
19
|
+
| Route relative to the testing base path | Purpose |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `GET /capabilities` | Discover supported setup, observations and limits |
|
|
22
|
+
| `PUT /reset` | Restore the declared baseline or require a restart |
|
|
23
|
+
| `POST /states` | Arrange named synthetic preconditions |
|
|
24
|
+
| `POST /probes` | Read a safe projection, including an internal spy |
|
|
25
|
+
| `POST /sinks/query` | Read captured fake effects without consuming them |
|
|
26
|
+
|
|
27
|
+
## Using the protocol in an application
|
|
28
|
+
|
|
29
|
+
For most backend developers, the main entry point to this gem is the
|
|
30
|
+
`overseer-testing-conformance` command: run it against an implemented testing
|
|
31
|
+
API in your local tests or CI.
|
|
32
|
+
|
|
33
|
+
You can implement that API yourself using the specification. A Rails app can
|
|
34
|
+
instead use the optional
|
|
35
|
+
[`overseer-testing-control-rails` helper](https://source.olisti.co/overseer/helper-rails)
|
|
36
|
+
for routing, JSON validation, correlation and bounded storage. The application
|
|
37
|
+
still owns its setup handlers, read-only observations, fake providers and reset
|
|
38
|
+
behavior. The helper uses the protocol's Ruby methods internally; application
|
|
39
|
+
handlers normally do not need to call `Overseer::TestingControl::ProtocolV3`.
|
|
40
|
+
|
|
41
|
+
Direct use of those methods is useful when writing your own adapter. They
|
|
42
|
+
validate incoming and outgoing JSON and construct protocol envelopes. Request
|
|
43
|
+
validation runs before a handler changes data; the command checks the HTTP
|
|
44
|
+
behavior exercised by its tests. One does not replace the other.
|
|
45
|
+
|
|
46
|
+
Adding the protocol gem alone creates no Rails routes, migrations, model hooks
|
|
47
|
+
or fake providers. A custom backend can follow the specification without
|
|
48
|
+
loading this gem and install the command only in its test tooling.
|
|
49
|
+
|
|
50
|
+
The optional Ruby package `overseer-testing-protocol` requires Ruby 3.2 or newer.
|
|
51
|
+
Its only runtime dependency is `json_schemer`. Other languages can use the data
|
|
52
|
+
artifacts without Ruby or a framework helper.
|
|
53
|
+
Package version `0.1.0` carries wire version `3`; these are separate versions.
|
|
54
|
+
|
|
55
|
+
See the [implementation guide](docs/testing-control-implementation-guide.md)
|
|
56
|
+
for an example test flow, the command invocation, and its current coverage
|
|
57
|
+
limits. Passing the command is a baseline check, not proof that every protocol
|
|
58
|
+
requirement or business operation has been tested.
|
|
59
|
+
|
|
60
|
+
## Working on this package
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
bundle install
|
|
64
|
+
bundle exec rake test
|
|
65
|
+
gem build overseer-testing-protocol.gemspec
|
|
66
|
+
bin/overseer-testing-conformance --help
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The caller owns test-server startup, isolation and cleanup. The command checks
|
|
70
|
+
an already isolated loopback server, requires explicit expected identity plus
|
|
71
|
+
`--allow-reset`, and uses finite timeouts and bounded reads. Release consumers
|
|
72
|
+
should pin the package or a reviewed source commit. A local package build does
|
|
73
|
+
not publish a release.
|
|
74
|
+
|
|
75
|
+
Made with ❤️ by [olistik](https://olisti.co)
|
data/SOURCE.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'optparse'
|
|
5
|
+
require_relative '../lib/overseer/testing_protocol'
|
|
6
|
+
|
|
7
|
+
options = { base_path: '/v1/testing', format: 'text' }
|
|
8
|
+
parser = OptionParser.new do |cli|
|
|
9
|
+
cli.banner = 'Usage: overseer-testing-conformance --origin http://127.0.0.1:PORT --application ID --source VERSION --environment ID --allow-reset [--cases FILE]'
|
|
10
|
+
cli.on('--origin ORIGIN') { |value| options[:origin] = value }
|
|
11
|
+
cli.on('--base-path PATH') { |value| options[:base_path] = value }
|
|
12
|
+
cli.on('--application ID') { |value| options[:application] = value }
|
|
13
|
+
cli.on('--source VERSION') { |value| options[:source] = value }
|
|
14
|
+
cli.on('--environment ID') { |value| options[:environment] = value }
|
|
15
|
+
cli.on('--cases FILE') { |value| options[:cases] = value }
|
|
16
|
+
cli.on('--format FORMAT', %w[text json]) { |value| options[:format] = value }
|
|
17
|
+
cli.on('--allow-reset', 'Allow resets of this caller-owned isolated synthetic runtime') { options[:allow_reset] = true }
|
|
18
|
+
cli.on('-h', '--help') { puts cli; exit 0 }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
parser.parse!
|
|
23
|
+
raise OptionParser::InvalidArgument, 'Unexpected positional arguments' unless ARGV.empty?
|
|
24
|
+
%i[origin application source environment allow_reset].each do |key|
|
|
25
|
+
raise OptionParser::MissingArgument, "--#{key.to_s.tr('_', '-')}" unless options[key]
|
|
26
|
+
end
|
|
27
|
+
protocol = Overseer::TestingControl::ProtocolV3
|
|
28
|
+
protocol.identifier!(options[:application], label: 'Application ID')
|
|
29
|
+
protocol.identifier!(options[:environment], label: 'Environment ID')
|
|
30
|
+
protocol.application_version!(options[:source])
|
|
31
|
+
conformance = Overseer::TestingControl::Conformance
|
|
32
|
+
cases = options[:cases] && conformance::CaseFile.load(options[:cases])
|
|
33
|
+
report = conformance::Runner.new(
|
|
34
|
+
origin: options[:origin], base_path: options[:base_path], target_label: options[:application],
|
|
35
|
+
expected_identity: conformance::Runner::ExpectedIdentity.new(
|
|
36
|
+
application_id: options[:application], application_version: options[:source], environment_id: options[:environment]
|
|
37
|
+
),
|
|
38
|
+
required_capabilities: { 'states' => {}, 'probes' => {}, 'sinks' => {} }, cases:,
|
|
39
|
+
lifecycle: { 'ownership' => 'explicitly-attached', 'started' => true, 'completed' => true, 'cleanup' => 'not-owned' },
|
|
40
|
+
redaction_policy: Overseer::TestingControl::Redaction::Policy.new(
|
|
41
|
+
header_names: %w[authorization cookie], field_patterns: %w[password token secret credential], max_body_bytes: 65_536
|
|
42
|
+
)
|
|
43
|
+
).call
|
|
44
|
+
puts(options[:format] == 'json' ? JSON.pretty_generate(report) : conformance::Report.render(report))
|
|
45
|
+
exit(report.fetch('aggregateStatus') == 'passed' ? 0 : 4)
|
|
46
|
+
rescue OptionParser::ParseError => error
|
|
47
|
+
warn error.message
|
|
48
|
+
warn parser
|
|
49
|
+
exit 3
|
|
50
|
+
rescue StandardError => error
|
|
51
|
+
warn "Conformance could not start (#{error.class.name}); check the supplied configuration."
|
|
52
|
+
exit 2
|
|
53
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Implementing Overseer testing-control v3
|
|
2
|
+
|
|
3
|
+
Use this contract from frontend tests, backend tests, curl or CI. The backend
|
|
4
|
+
and test client agree on the testing API; no particular runner is required.
|
|
5
|
+
The [wire specification](testing-control-protocol.md) defines exact behavior.
|
|
6
|
+
|
|
7
|
+
## Choose how to implement the API
|
|
8
|
+
|
|
9
|
+
| Component | What it does |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| Your application | Defines synthetic setups, read-only observations, fake effects and reset behavior. |
|
|
12
|
+
| An optional framework helper | Handles HTTP routes, protocol validation, correlation and responses. |
|
|
13
|
+
| The protocol command | Calls the running testing API and checks the behavior exercised by its tests. |
|
|
14
|
+
|
|
15
|
+
For Rails, the optional
|
|
16
|
+
[`overseer-testing-control-rails` helper](https://source.olisti.co/overseer/helper-rails)
|
|
17
|
+
also supplies bounded memory stores for fake effects and arrangement
|
|
18
|
+
idempotency. Configure the helper and provide your own handlers and schemas.
|
|
19
|
+
It calls the protocol's Ruby methods internally, so application handlers
|
|
20
|
+
normally do not need a `ProtocolV3` reference.
|
|
21
|
+
|
|
22
|
+
If you build your own adapter, `Overseer::TestingControl::ProtocolV3` offers
|
|
23
|
+
optional methods such as `validate_document!`, `validate_value!` and `envelope`.
|
|
24
|
+
These save you from repeating JSON validation and response construction. For
|
|
25
|
+
example, the adapter can reject an invalid pending-order input before calling
|
|
26
|
+
the application's order setup handler. You still implement dispatch, identity
|
|
27
|
+
checks and the other HTTP requirements.
|
|
28
|
+
|
|
29
|
+
You can also implement the specification without using this gem in the backend
|
|
30
|
+
and install only the command in your test tooling. The backend must still
|
|
31
|
+
validate requests: an external check covers the requests it sends, while the
|
|
32
|
+
adapter validates each request it receives. Installing the protocol gem alone
|
|
33
|
+
does not add Rails endpoints, migrations, spies or fake providers.
|
|
34
|
+
|
|
35
|
+
## Implement the testing API
|
|
36
|
+
|
|
37
|
+
1. Start one isolated synthetic runtime per independent test suite. Block
|
|
38
|
+
external egress, omit real credentials, and keep the control API off deployed
|
|
39
|
+
environments. The library does not create this isolation for you.
|
|
40
|
+
2. Mount the five relative routes under a test-only base path. Verify the test
|
|
41
|
+
environment, activation flag, source identity and runtime identity before
|
|
42
|
+
exposing them. Discover capabilities before changing data.
|
|
43
|
+
3. Register named states that preserve domain invariants. Validate closed,
|
|
44
|
+
bounded input and output schemas. Return identifiers and safe fields;
|
|
45
|
+
passwords and tokens never belong in responses or observations.
|
|
46
|
+
4. Reset all owned state or advertise `runtime-restart`. A failed reset must
|
|
47
|
+
stop the scenario. Reset includes captured observations and idempotency.
|
|
48
|
+
5. Call the ordinary public API with normal authentication and the same run ID.
|
|
49
|
+
Give every public call its own correlation ID; the optional step ID is a
|
|
50
|
+
useful test label. The public API does not acquire a protocol envelope.
|
|
51
|
+
6. Observe database state or captured internal calls through named probes.
|
|
52
|
+
Capture supported external effects through product-owned fake adapters.
|
|
53
|
+
Query a sink using the public call's correlation ID in `data.filter` and a
|
|
54
|
+
new query correlation ID in `meta`. Queries never consume records.
|
|
55
|
+
7. Validate response envelopes and advertised result schemas before assertions.
|
|
56
|
+
Never assume asynchronous work is complete merely because enqueue succeeded.
|
|
57
|
+
|
|
58
|
+
For example, an order test discovers `pending-order`, resets the test server,
|
|
59
|
+
creates a synthetic order through `/states`, and pays through the normal order
|
|
60
|
+
API with normal authentication. It then reads the order status through a probe
|
|
61
|
+
and queries a fake email sink for the confirmation attempt. The application
|
|
62
|
+
defines those names and payloads; the protocol does not know about orders.
|
|
63
|
+
|
|
64
|
+
## Expose spies and fake effects
|
|
65
|
+
|
|
66
|
+
An internal spy should record only the named event and allowlisted synthetic
|
|
67
|
+
identifiers needed for a test. Bound its storage, partition by run, clear it on
|
|
68
|
+
reset, and expose a read-only probe. Do not expose method names, call stacks,
|
|
69
|
+
arbitrary arguments, SQL, model serializers or a general mocking endpoint.
|
|
70
|
+
|
|
71
|
+
A sink should intercept the real effect boundary, preserve domain behavior and
|
|
72
|
+
callbacks, record only an allowlisted projection, and return the response the
|
|
73
|
+
application expects from the fake provider. Distinguish requested, accepted,
|
|
74
|
+
completed and failed outcomes according to what was actually observed. A
|
|
75
|
+
pre-enqueue sink proves the request intent only. Unsupported effects must stay
|
|
76
|
+
contained by the runtime even when there is no corresponding sink.
|
|
77
|
+
|
|
78
|
+
## Check the running API
|
|
79
|
+
|
|
80
|
+
Run HTTP conformance against your already isolated disposable server:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
bundle exec overseer-testing-conformance \
|
|
84
|
+
--origin http://127.0.0.1:3100 --base-path /v1/testing \
|
|
85
|
+
--application my-api --source source-snapshot-001 \
|
|
86
|
+
--environment local-test-001 --allow-reset
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Replace the example application, source and environment IDs with the exact
|
|
90
|
+
values configured for your running backend. The command checks those identities
|
|
91
|
+
before stateful checks. It resets synthetic data and does not start or stop the
|
|
92
|
+
server. It accepts an explicit `http://127.0.0.1:PORT` origin. A backend using
|
|
93
|
+
`runtime-restart` needs a caller that can recreate the runtime; this attached
|
|
94
|
+
command cannot perform that restart itself.
|
|
95
|
+
|
|
96
|
+
For application-specific state/probe checks, append
|
|
97
|
+
`--cases test/conformance.json` and provide that file in your application.
|
|
98
|
+
The case format is demonstrated by
|
|
99
|
+
[`conformance-case.json`](../protocol/testing-control/v3/fixtures/conformance-case.json).
|
|
100
|
+
It allows named setup, read-only probes, identifier bindings and bounded JSON
|
|
101
|
+
assertions. It cannot run scripts, public API calls or sink queries.
|
|
102
|
+
|
|
103
|
+
The current command checks discovery, protocol and identity handling, JSON
|
|
104
|
+
responses, several invalid requests, reset responses, and any supplied
|
|
105
|
+
state/probe cases. It does not exercise `/sinks/query`, prove that reset clears
|
|
106
|
+
captured records, or check repeated requests with the same idempotency key.
|
|
107
|
+
It also cannot prove that a probe is read-only or that network containment is
|
|
108
|
+
in place. The specification's [conformance section](testing-control-protocol.md#15-black-box-conformance)
|
|
109
|
+
records the current coverage and its limits.
|
|
110
|
+
|
|
111
|
+
Keep application request tests for those protocol requirements, plus an
|
|
112
|
+
end-to-end test that exercises the normal API and checks the resulting spies
|
|
113
|
+
and sinks. For a Rails app using the helper, use the helper in the isolated
|
|
114
|
+
test environment, the protocol command in CI, and application tests for the
|
|
115
|
+
business behavior and remaining coverage. A passing command does not replace
|
|
116
|
+
request validation or prove that every advertised operation works.
|