test: cover manifest validation errors
This commit is contained in:
@@ -1,10 +1,25 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from wf_contract_manifest import manifest_from_openrpc
|
from typing import Any
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from wf_contract_manifest import ManifestError, manifest_from_openrpc
|
||||||
|
|
||||||
from .fixtures import synthetic_openrpc_document
|
from .fixtures import synthetic_openrpc_document
|
||||||
|
|
||||||
|
|
||||||
|
def assert_manifest_error(
|
||||||
|
document: dict[str, Any], path: str, message: str
|
||||||
|
) -> None:
|
||||||
|
with pytest.raises(ManifestError) as error_info:
|
||||||
|
manifest_from_openrpc(document)
|
||||||
|
|
||||||
|
assert error_info.value.path == path
|
||||||
|
assert error_info.value.message == message
|
||||||
|
assert str(error_info.value) == f"{path}: {message}"
|
||||||
|
|
||||||
|
|
||||||
def test_normalizes_operations_and_components_deterministically() -> None:
|
def test_normalizes_operations_and_components_deterministically() -> None:
|
||||||
manifest = manifest_from_openrpc(synthetic_openrpc_document())
|
manifest = manifest_from_openrpc(synthetic_openrpc_document())
|
||||||
|
|
||||||
@@ -76,3 +91,134 @@ def test_preserves_conditional_schema_keywords() -> None:
|
|||||||
assert alpha["if"] == {"properties": {"mode": {"const": "alpha"}}}
|
assert alpha["if"] == {"properties": {"mode": {"const": "alpha"}}}
|
||||||
assert alpha["then"] == {"required": ["payload"]}
|
assert alpha["then"] == {"required": ["payload"]}
|
||||||
assert alpha["not"] == {"required": ["forbidden"]}
|
assert alpha["not"] == {"required": ["forbidden"]}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("field", "value", "missing", "path", "message"),
|
||||||
|
[
|
||||||
|
("openrpc", None, True, "$.openrpc", "expected a non-empty string"),
|
||||||
|
("openrpc", 1.2, False, "$.openrpc", "expected a non-empty string"),
|
||||||
|
("methods", None, True, "$.methods", "expected an array"),
|
||||||
|
("methods", {}, False, "$.methods", "expected an array"),
|
||||||
|
("components", None, True, "$.components", "expected an object"),
|
||||||
|
("components", [], False, "$.components", "expected an object"),
|
||||||
|
(
|
||||||
|
"schemas",
|
||||||
|
None,
|
||||||
|
True,
|
||||||
|
"$.components.schemas",
|
||||||
|
"expected an object",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"schemas",
|
||||||
|
[],
|
||||||
|
False,
|
||||||
|
"$.components.schemas",
|
||||||
|
"expected an object",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"errors",
|
||||||
|
None,
|
||||||
|
True,
|
||||||
|
"$.components.errors",
|
||||||
|
"expected an object",
|
||||||
|
),
|
||||||
|
("errors", [], False, "$.components.errors", "expected an object"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_rejects_invalid_or_missing_top_level_envelope_values(
|
||||||
|
field: str,
|
||||||
|
value: object,
|
||||||
|
missing: bool,
|
||||||
|
path: str,
|
||||||
|
message: str,
|
||||||
|
) -> None:
|
||||||
|
document = synthetic_openrpc_document()
|
||||||
|
if field in {"schemas", "errors"}:
|
||||||
|
components = document["components"]
|
||||||
|
if missing:
|
||||||
|
components.pop(field)
|
||||||
|
else:
|
||||||
|
components[field] = value
|
||||||
|
elif missing:
|
||||||
|
document.pop(field)
|
||||||
|
else:
|
||||||
|
document[field] = value
|
||||||
|
|
||||||
|
assert_manifest_error(document, path, message)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("name", "message"),
|
||||||
|
[
|
||||||
|
("", "expected a non-empty string"),
|
||||||
|
("workflow", "expected a method name with non-empty dot-separated segments"),
|
||||||
|
(
|
||||||
|
"workflow..run",
|
||||||
|
"expected a method name with non-empty dot-separated segments",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
".workflow.run",
|
||||||
|
"expected a method name with non-empty dot-separated segments",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"workflow.run.",
|
||||||
|
"expected a method name with non-empty dot-separated segments",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_rejects_empty_or_malformed_dotted_method_names(
|
||||||
|
name: str, message: str
|
||||||
|
) -> None:
|
||||||
|
document = synthetic_openrpc_document()
|
||||||
|
document["methods"][0]["name"] = name
|
||||||
|
|
||||||
|
assert_manifest_error(document, "$.methods[0].name", message)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("field", "value", "missing", "message"),
|
||||||
|
[
|
||||||
|
("required", "yes", False, "expected a boolean"),
|
||||||
|
("required", None, True, "expected a boolean"),
|
||||||
|
("schema", [], False, "expected a schema object"),
|
||||||
|
("schema", None, True, "expected a schema object"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_rejects_invalid_or_missing_parameter_fields(
|
||||||
|
field: str, value: object, missing: bool, message: str
|
||||||
|
) -> None:
|
||||||
|
document = synthetic_openrpc_document()
|
||||||
|
parameter = document["methods"][1]["params"][0]
|
||||||
|
if missing:
|
||||||
|
parameter.pop(field)
|
||||||
|
else:
|
||||||
|
parameter[field] = value
|
||||||
|
|
||||||
|
assert_manifest_error(
|
||||||
|
document,
|
||||||
|
f"$.methods[1].params[0].{field}",
|
||||||
|
message,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_rejects_invalid_result_schema_shape() -> None:
|
||||||
|
document = synthetic_openrpc_document()
|
||||||
|
document["methods"][1]["result"]["schema"] = []
|
||||||
|
|
||||||
|
assert_manifest_error(
|
||||||
|
document,
|
||||||
|
"$.methods[1].result.schema",
|
||||||
|
"expected a schema object",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_rejects_invalid_method_error_schema_shape() -> None:
|
||||||
|
document = synthetic_openrpc_document()
|
||||||
|
document["methods"][1]["errors"][0] = []
|
||||||
|
|
||||||
|
assert_manifest_error(
|
||||||
|
document,
|
||||||
|
"$.methods[1].errors[0]",
|
||||||
|
"expected a schema object",
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user