diff --git a/pyproject.toml b/pyproject.toml index 6ef5ca1c..a4065e96 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,8 +86,8 @@ dependencies = [ ] [tool.hatch.envs.dev.scripts] -codegen = "datamodel-codegen --disable-timestamp --input src/reqstool/resources/schemas/v1/ --input-file-type jsonschema --output src/reqstool/models/generated/ --output-model-type pydantic_v2.BaseModel --use-schema-description --use-field-description --use-annotated --field-constraints --target-python-version 3.13" -codegen-check = "datamodel-codegen --check --disable-timestamp --input src/reqstool/resources/schemas/v1/ --input-file-type jsonschema --output src/reqstool/models/generated/ --output-model-type pydantic_v2.BaseModel --use-schema-description --use-field-description --use-annotated --field-constraints --target-python-version 3.13" +codegen = "bash -c 'base=$(mktemp -d) && tmp=$base/v1 && mkdir \"$tmp\" && cp src/reqstool/resources/schemas/v1/*.schema.json \"$tmp/\" && rm -f \"$tmp\"/*_output.schema.json && datamodel-codegen --disable-timestamp --input \"$tmp/\" --input-file-type jsonschema --output src/reqstool/models/generated/ --output-model-type pydantic_v2.BaseModel --use-schema-description --use-field-description --use-annotated --field-constraints --target-python-version 3.13 && rm -rf \"$base\"'" +codegen-check = "bash -c 'base=$(mktemp -d) && tmp=$base/v1 && mkdir \"$tmp\" && cp src/reqstool/resources/schemas/v1/*.schema.json \"$tmp/\" && rm -f \"$tmp\"/*_output.schema.json && datamodel-codegen --check --disable-timestamp --input \"$tmp/\" --input-file-type jsonschema --output src/reqstool/models/generated/ --output-model-type pydantic_v2.BaseModel --use-schema-description --use-field-description --use-annotated --field-constraints --target-python-version 3.13 && rm -rf \"$base\"'" [tool.black] line-length = 120 diff --git a/src/reqstool/resources/schemas/v1/export_output.schema.json b/src/reqstool/resources/schemas/v1/export_output.schema.json new file mode 100644 index 00000000..bd74be61 --- /dev/null +++ b/src/reqstool/resources/schemas/v1/export_output.schema.json @@ -0,0 +1,364 @@ +{ + "$id": "https://raw.githubusercontent.com/reqstool/reqstool-client/main/src/reqstool/resources/schemas/v1/export_output.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Reqstool Export Output", + "description": "Schema for the JSON output of the reqstool export command", + "type": "object", + "additionalProperties": false, + "required": ["metadata", "requirements", "svcs", "mvrs", "annotations", "test_results"], + "properties": { + "metadata": { + "$ref": "#/$defs/metadata" + }, + "requirements": { + "type": "object", + "description": "All requirements, keyed by urn:id", + "additionalProperties": false, + "patternProperties": { + "^.+:.+$": { + "$ref": "#/$defs/requirement" + } + } + }, + "svcs": { + "type": "object", + "description": "All software verification cases, keyed by urn:id", + "additionalProperties": false, + "patternProperties": { + "^.+:.+$": { + "$ref": "#/$defs/svc" + } + } + }, + "mvrs": { + "type": "object", + "description": "All manual verification results, keyed by urn:id", + "additionalProperties": false, + "patternProperties": { + "^.+:.+$": { + "$ref": "#/$defs/mvr" + } + } + }, + "annotations": { + "$ref": "#/$defs/annotations" + }, + "test_results": { + "type": "object", + "description": "Automated test results, keyed by urn:fully_qualified_name", + "additionalProperties": false, + "patternProperties": { + "^.+:.+$": { + "type": "array", + "items": { + "$ref": "#/$defs/test_result" + } + } + } + } + }, + "$defs": { + "metadata": { + "type": "object", + "description": "Metadata about this export output", + "additionalProperties": false, + "required": ["initial_urn", "urn_parsing_order", "import_graph", "filtered"], + "properties": { + "initial_urn": { + "type": "string", + "description": "The URN of the initial dataset that was processed" + }, + "urn_parsing_order": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Order in which URNs were parsed during processing" + }, + "import_graph": { + "type": "object", + "description": "DAG of URN import relationships (parent -> children)", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "filtered": { + "type": "boolean", + "description": "Whether filters were applied during processing" + } + } + }, + "lifecycle": { + "type": "object", + "description": "Lifecycle state of a requirement or SVC", + "additionalProperties": false, + "required": ["state"], + "properties": { + "state": { + "type": "string", + "enum": ["draft", "effective", "deprecated", "obsolete"], + "description": "Current lifecycle state" + }, + "reason": { + "type": ["string", "null"], + "description": "Reason for the current state, required if deprecated or obsolete" + } + } + }, + "revision": { + "type": "object", + "description": "Semantic version of the requirement or SVC", + "additionalProperties": false, + "required": ["major", "minor", "patch"], + "properties": { + "major": { + "type": "integer", + "minimum": 0 + }, + "minor": { + "type": "integer", + "minimum": 0 + }, + "patch": { + "type": "integer", + "minimum": 0 + } + } + }, + "requirement": { + "type": "object", + "description": "A single requirement", + "additionalProperties": false, + "required": ["urn", "id", "title", "significance", "description", "lifecycle", "implementation_type", "categories", "revision"], + "properties": { + "urn": { + "type": "string", + "description": "URN of the system this requirement belongs to" + }, + "id": { + "type": "string", + "description": "Requirement identifier (e.g. REQ_010)" + }, + "title": { + "type": "string", + "description": "Human-readable title" + }, + "significance": { + "type": "string", + "enum": ["shall", "should", "may"], + "description": "Level of significance (RFC 2119)" + }, + "description": { + "type": "string", + "description": "Detailed description of the requirement" + }, + "rationale": { + "type": ["string", "null"], + "description": "Rationale for why this requirement exists" + }, + "lifecycle": { + "$ref": "#/$defs/lifecycle" + }, + "implementation_type": { + "type": "string", + "enum": ["in-code", "N/A"], + "description": "How the requirement is implemented" + }, + "categories": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "functional-suitability", + "performance-efficiency", + "compatibility", + "interaction-capability", + "reliability", + "security", + "maintainability", + "flexibility", + "safety" + ] + }, + "description": "Requirement categories" + }, + "revision": { + "$ref": "#/$defs/revision" + }, + "references": { + "type": "array", + "items": { + "$ref": "#/$defs/reference" + }, + "description": "References to related requirements" + } + } + }, + "reference": { + "type": "object", + "description": "A reference to related requirements", + "additionalProperties": false, + "required": ["requirement_ids"], + "properties": { + "requirement_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of urn:id references to related requirements" + } + } + }, + "svc": { + "type": "object", + "description": "A software verification case", + "additionalProperties": false, + "required": ["urn", "id", "title", "description", "verification", "lifecycle", "revision", "requirement_ids"], + "properties": { + "urn": { + "type": "string", + "description": "URN of the system this SVC belongs to" + }, + "id": { + "type": "string", + "description": "SVC identifier (e.g. SVC_010)" + }, + "title": { + "type": "string", + "description": "Human-readable title" + }, + "description": { + "type": "string", + "description": "Detailed description" + }, + "verification": { + "type": "string", + "enum": ["automated-test", "manual-test", "review", "platform", "other"], + "description": "Type of verification" + }, + "instructions": { + "type": ["string", "null"], + "description": "Instructions for manual verification" + }, + "lifecycle": { + "$ref": "#/$defs/lifecycle" + }, + "revision": { + "$ref": "#/$defs/revision" + }, + "requirement_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of urn:id references to requirements this SVC verifies" + } + } + }, + "mvr": { + "type": "object", + "description": "A manual verification result", + "additionalProperties": false, + "required": ["urn", "id", "passed", "svc_ids"], + "properties": { + "urn": { + "type": "string", + "description": "URN of the system this MVR belongs to" + }, + "id": { + "type": "string", + "description": "MVR identifier (e.g. MVR_201)" + }, + "passed": { + "type": "boolean", + "description": "Whether the manual verification passed" + }, + "comment": { + "type": ["string", "null"], + "description": "Optional comment about the verification result" + }, + "svc_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of urn:id references to SVCs this MVR verifies" + } + } + }, + "annotations": { + "type": "object", + "description": "Code annotations for implementations and tests", + "additionalProperties": false, + "required": ["implementations", "tests"], + "properties": { + "implementations": { + "type": "object", + "description": "Implementation annotations, keyed by urn:requirement_id", + "additionalProperties": false, + "patternProperties": { + "^.+:.+$": { + "type": "array", + "items": { + "$ref": "#/$defs/annotation" + } + } + } + }, + "tests": { + "type": "object", + "description": "Test annotations, keyed by urn:svc_id", + "additionalProperties": false, + "patternProperties": { + "^.+:.+$": { + "type": "array", + "items": { + "$ref": "#/$defs/annotation" + } + } + } + } + } + }, + "annotation": { + "type": "object", + "description": "A single code annotation", + "additionalProperties": false, + "required": ["element_kind", "fully_qualified_name"], + "properties": { + "element_kind": { + "type": "string", + "enum": ["FIELD", "METHOD", "CLASS", "ENUM", "INTERFACE", "RECORD"], + "description": "Kind of code element annotated" + }, + "fully_qualified_name": { + "type": "string", + "description": "Fully qualified name of the annotated element" + } + } + }, + "test_result": { + "type": "object", + "description": "A single automated test result", + "additionalProperties": false, + "required": ["fully_qualified_name", "status"], + "properties": { + "fully_qualified_name": { + "type": "string", + "description": "Fully qualified name of the test" + }, + "status": { + "type": "string", + "enum": ["passed", "failed", "skipped", "missing"], + "description": "Test execution status" + } + } + } + } +} diff --git a/src/reqstool/resources/schemas/v1/status_output.schema.json b/src/reqstool/resources/schemas/v1/status_output.schema.json new file mode 100644 index 00000000..982e1386 --- /dev/null +++ b/src/reqstool/resources/schemas/v1/status_output.schema.json @@ -0,0 +1,248 @@ +{ + "$id": "https://raw.githubusercontent.com/reqstool/reqstool-client/main/src/reqstool/resources/schemas/v1/status_output.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Reqstool Status Output", + "description": "Schema for the JSON output of the reqstool status command", + "type": "object", + "additionalProperties": false, + "required": ["metadata", "requirements", "totals"], + "properties": { + "metadata": { + "$ref": "#/$defs/metadata" + }, + "requirements": { + "type": "object", + "description": "Per-requirement status, keyed by urn:id", + "additionalProperties": false, + "patternProperties": { + "^.+:.+$": { + "$ref": "#/$defs/requirement_status" + } + } + }, + "totals": { + "$ref": "#/$defs/totals" + } + }, + "$defs": { + "metadata": { + "type": "object", + "description": "Metadata about this status output", + "additionalProperties": false, + "required": ["initial_urn", "filtered"], + "properties": { + "initial_urn": { + "type": "string", + "description": "The URN of the initial dataset that was processed" + }, + "filtered": { + "type": "boolean", + "description": "Whether filters were applied during processing" + } + } + }, + "requirement_status": { + "type": "object", + "description": "Status of a single requirement", + "additionalProperties": false, + "required": ["completed", "implementations", "implementation_type", "automated_tests", "manual_tests"], + "properties": { + "completed": { + "type": "boolean", + "description": "Whether the requirement is fully satisfied" + }, + "implementations": { + "type": "integer", + "minimum": 0, + "description": "Number of implementations found for this requirement" + }, + "implementation_type": { + "type": "string", + "enum": ["in-code", "N/A"], + "description": "How the requirement is implemented" + }, + "automated_tests": { + "$ref": "#/$defs/test_stats" + }, + "manual_tests": { + "$ref": "#/$defs/test_stats" + } + } + }, + "test_stats": { + "type": "object", + "description": "Test statistics for a requirement", + "additionalProperties": false, + "required": ["total", "passed", "failed", "skipped", "missing", "not_applicable"], + "properties": { + "total": { + "type": "integer", + "minimum": 0, + "description": "Total number of tests" + }, + "passed": { + "type": "integer", + "minimum": 0, + "description": "Number of tests that passed" + }, + "failed": { + "type": "integer", + "minimum": 0, + "description": "Number of tests that failed" + }, + "skipped": { + "type": "integer", + "minimum": 0, + "description": "Number of tests that were skipped" + }, + "missing": { + "type": "integer", + "minimum": 0, + "description": "Number of expected tests that are missing" + }, + "not_applicable": { + "type": "boolean", + "description": "Whether this test type is not applicable for this requirement" + } + } + }, + "totals": { + "type": "object", + "description": "Aggregate totals across all requirements", + "additionalProperties": false, + "required": ["requirements", "svcs", "tests", "automated_tests", "manual_tests"], + "properties": { + "requirements": { + "$ref": "#/$defs/requirements_totals" + }, + "svcs": { + "$ref": "#/$defs/svcs_totals" + }, + "tests": { + "$ref": "#/$defs/tests_totals" + }, + "automated_tests": { + "$ref": "#/$defs/test_type_totals" + }, + "manual_tests": { + "$ref": "#/$defs/test_type_totals" + } + } + }, + "requirements_totals": { + "type": "object", + "description": "Aggregate requirement totals", + "additionalProperties": false, + "required": ["total", "completed", "with_implementation", "without_implementation"], + "properties": { + "total": { + "type": "integer", + "minimum": 0, + "description": "Total number of requirements" + }, + "completed": { + "type": "integer", + "minimum": 0, + "description": "Number of completed requirements" + }, + "with_implementation": { + "type": "integer", + "minimum": 0, + "description": "Number of requirements with implementation_type in-code" + }, + "without_implementation": { + "type": "object", + "description": "Requirements without in-code implementation (N/A)", + "additionalProperties": false, + "required": ["total", "completed"], + "properties": { + "total": { + "type": "integer", + "minimum": 0, + "description": "Total requirements without implementation" + }, + "completed": { + "type": "integer", + "minimum": 0, + "description": "Completed requirements without implementation" + } + } + } + } + }, + "svcs_totals": { + "type": "object", + "description": "Aggregate SVC totals", + "additionalProperties": false, + "required": ["total"], + "properties": { + "total": { + "type": "integer", + "minimum": 0, + "description": "Total number of software verification cases" + } + } + }, + "tests_totals": { + "type": "object", + "description": "Aggregate test totals across all test types", + "additionalProperties": false, + "required": ["total", "passed", "failed", "skipped", "missing_automated", "missing_manual"], + "properties": { + "total": { + "type": "integer", + "minimum": 0, + "description": "Total number of tests" + }, + "passed": { + "type": "integer", + "minimum": 0, + "description": "Total tests passed" + }, + "failed": { + "type": "integer", + "minimum": 0, + "description": "Total tests failed" + }, + "skipped": { + "type": "integer", + "minimum": 0, + "description": "Total tests skipped" + }, + "missing_automated": { + "type": "integer", + "minimum": 0, + "description": "Number of missing automated tests" + }, + "missing_manual": { + "type": "integer", + "minimum": 0, + "description": "Number of missing manual tests" + } + } + }, + "test_type_totals": { + "type": "object", + "description": "Totals for a specific test type (automated or manual)", + "additionalProperties": false, + "required": ["total", "passed", "failed"], + "properties": { + "total": { + "type": "integer", + "minimum": 0, + "description": "Total tests of this type" + }, + "passed": { + "type": "integer", + "minimum": 0, + "description": "Tests passed" + }, + "failed": { + "type": "integer", + "minimum": 0, + "description": "Tests failed" + } + } + } + } +} diff --git a/tests/unit/reqstool/schemas/__init__.py b/tests/unit/reqstool/schemas/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/reqstool/schemas/test_output_schemas.py b/tests/unit/reqstool/schemas/test_output_schemas.py new file mode 100644 index 00000000..b5c79d1c --- /dev/null +++ b/tests/unit/reqstool/schemas/test_output_schemas.py @@ -0,0 +1,312 @@ +import json +from importlib.resources import files + +import jsonschema +import pytest + +import reqstool.resources.schemas.v1 + + +def _load_schema(name: str) -> dict: + return json.loads(files(reqstool.resources.schemas.v1).joinpath(name).read_text()) + + +@pytest.fixture +def status_schema(): + return _load_schema("status_output.schema.json") + + +@pytest.fixture +def export_schema(): + return _load_schema("export_output.schema.json") + + +# ── Meta-schema validation ──────────────────────────────────────────────────── + + +def test_status_schema_is_valid_json_schema(status_schema): + jsonschema.Draft202012Validator.check_schema(status_schema) + + +def test_export_schema_is_valid_json_schema(export_schema): + jsonschema.Draft202012Validator.check_schema(export_schema) + + +# ── Status schema: positive cases ───────────────────────────────────────────── + +MINIMAL_STATUS = { + "metadata": {"initial_urn": "ms-001", "filtered": False}, + "requirements": {}, + "totals": { + "requirements": { + "total": 0, + "completed": 0, + "with_implementation": 0, + "without_implementation": {"total": 0, "completed": 0}, + }, + "svcs": {"total": 0}, + "tests": {"total": 0, "passed": 0, "failed": 0, "skipped": 0, "missing_automated": 0, "missing_manual": 0}, + "automated_tests": {"total": 0, "passed": 0, "failed": 0}, + "manual_tests": {"total": 0, "passed": 0, "failed": 0}, + }, +} + +FULL_STATUS = { + "metadata": {"initial_urn": "ms-001", "filtered": True}, + "requirements": { + "ms-001:REQ_010": { + "completed": True, + "implementations": 2, + "implementation_type": "in-code", + "automated_tests": { + "total": 3, + "passed": 3, + "failed": 0, + "skipped": 0, + "missing": 0, + "not_applicable": False, + }, + "manual_tests": { + "total": 1, + "passed": 1, + "failed": 0, + "skipped": 0, + "missing": 0, + "not_applicable": False, + }, + }, + "ms-001:REQ_020": { + "completed": False, + "implementations": 0, + "implementation_type": "N/A", + "automated_tests": { + "total": 0, + "passed": 0, + "failed": 0, + "skipped": 0, + "missing": 0, + "not_applicable": True, + }, + "manual_tests": { + "total": 0, + "passed": 0, + "failed": 0, + "skipped": 0, + "missing": 1, + "not_applicable": False, + }, + }, + }, + "totals": { + "requirements": { + "total": 2, + "completed": 1, + "with_implementation": 1, + "without_implementation": {"total": 1, "completed": 0}, + }, + "svcs": {"total": 4}, + "tests": {"total": 4, "passed": 4, "failed": 0, "skipped": 0, "missing_automated": 0, "missing_manual": 1}, + "automated_tests": {"total": 3, "passed": 3, "failed": 0}, + "manual_tests": {"total": 1, "passed": 1, "failed": 0}, + }, +} + + +def test_status_minimal(status_schema): + jsonschema.validate(MINIMAL_STATUS, status_schema) + + +def test_status_full(status_schema): + jsonschema.validate(FULL_STATUS, status_schema) + + +# ── Status schema: negative cases ───────────────────────────────────────────── + + +def test_status_missing_metadata(status_schema): + doc = {k: v for k, v in MINIMAL_STATUS.items() if k != "metadata"} + with pytest.raises(jsonschema.ValidationError): + jsonschema.validate(doc, status_schema) + + +def test_status_missing_totals(status_schema): + doc = {k: v for k, v in MINIMAL_STATUS.items() if k != "totals"} + with pytest.raises(jsonschema.ValidationError): + jsonschema.validate(doc, status_schema) + + +def test_status_extra_top_level_field(status_schema): + doc = {**MINIMAL_STATUS, "extra": True} + with pytest.raises(jsonschema.ValidationError): + jsonschema.validate(doc, status_schema) + + +def test_status_invalid_implementation_type(status_schema): + doc = json.loads(json.dumps(FULL_STATUS)) + doc["requirements"]["ms-001:REQ_010"]["implementation_type"] = "invalid" + with pytest.raises(jsonschema.ValidationError): + jsonschema.validate(doc, status_schema) + + +def test_status_negative_test_count(status_schema): + doc = json.loads(json.dumps(FULL_STATUS)) + doc["requirements"]["ms-001:REQ_010"]["automated_tests"]["total"] = -1 + with pytest.raises(jsonschema.ValidationError): + jsonschema.validate(doc, status_schema) + + +# ── Export schema: positive cases ───────────────────────────────────────────── + +MINIMAL_EXPORT = { + "metadata": { + "initial_urn": "ms-001", + "urn_parsing_order": ["ms-001"], + "import_graph": {}, + "filtered": False, + }, + "requirements": {}, + "svcs": {}, + "mvrs": {}, + "annotations": {"implementations": {}, "tests": {}}, + "test_results": {}, +} + +FULL_EXPORT = { + "metadata": { + "initial_urn": "ms-001", + "urn_parsing_order": ["sys-001", "ext-001", "ms-001"], + "import_graph": {"sys-001": ["ext-001"], "ms-001": ["sys-001"]}, + "filtered": True, + }, + "requirements": { + "ms-001:REQ_010": { + "urn": "ms-001", + "id": "REQ_010", + "title": "User authentication", + "significance": "shall", + "description": "The system shall authenticate users", + "rationale": "Security requirement", + "lifecycle": {"state": "effective", "reason": None}, + "implementation_type": "in-code", + "categories": ["functional-suitability", "security"], + "revision": {"major": 1, "minor": 0, "patch": 0}, + "references": [{"requirement_ids": ["sys-001:REQ_001"]}], + } + }, + "svcs": { + "sys-001:SVC_010": { + "urn": "sys-001", + "id": "SVC_010", + "title": "Verify authentication", + "description": "Verify user auth works", + "verification": "automated-test", + "instructions": None, + "lifecycle": {"state": "effective", "reason": None}, + "revision": {"major": 1, "minor": 0, "patch": 0}, + "requirement_ids": ["ms-001:REQ_010"], + } + }, + "mvrs": { + "ms-001:MVR_201": { + "urn": "ms-001", + "id": "MVR_201", + "passed": True, + "comment": None, + "svc_ids": ["sys-001:SVC_021"], + } + }, + "annotations": { + "implementations": { + "ms-001:REQ_010": [{"element_kind": "METHOD", "fully_qualified_name": "com.example.Foo.bar"}] + }, + "tests": { + "sys-001:SVC_010": [{"element_kind": "METHOD", "fully_qualified_name": "com.example.FooTest.testBar"}] + }, + }, + "test_results": { + "ms-001:com.example.FooTest.testBar": [ + {"fully_qualified_name": "com.example.FooTest.testBar", "status": "passed"} + ] + }, +} + + +def test_export_minimal(export_schema): + jsonschema.validate(MINIMAL_EXPORT, export_schema) + + +def test_export_full(export_schema): + jsonschema.validate(FULL_EXPORT, export_schema) + + +def test_export_requirement_without_optional_fields(export_schema): + doc = json.loads(json.dumps(MINIMAL_EXPORT)) + doc["requirements"]["ms-001:REQ_010"] = { + "urn": "ms-001", + "id": "REQ_010", + "title": "Test", + "significance": "shall", + "description": "A test requirement", + "lifecycle": {"state": "effective"}, + "implementation_type": "in-code", + "categories": ["functional-suitability"], + "revision": {"major": 1, "minor": 0, "patch": 0}, + } + jsonschema.validate(doc, export_schema) + + +# ── Export schema: negative cases ───────────────────────────────────────────── + + +def test_export_missing_metadata(export_schema): + doc = {k: v for k, v in MINIMAL_EXPORT.items() if k != "metadata"} + with pytest.raises(jsonschema.ValidationError): + jsonschema.validate(doc, export_schema) + + +def test_export_extra_top_level_field(export_schema): + doc = {**MINIMAL_EXPORT, "extra": True} + with pytest.raises(jsonschema.ValidationError): + jsonschema.validate(doc, export_schema) + + +def test_export_invalid_significance(export_schema): + doc = json.loads(json.dumps(FULL_EXPORT)) + doc["requirements"]["ms-001:REQ_010"]["significance"] = "must" + with pytest.raises(jsonschema.ValidationError): + jsonschema.validate(doc, export_schema) + + +def test_export_invalid_verification_type(export_schema): + doc = json.loads(json.dumps(FULL_EXPORT)) + doc["svcs"]["sys-001:SVC_010"]["verification"] = "unknown" + with pytest.raises(jsonschema.ValidationError): + jsonschema.validate(doc, export_schema) + + +def test_export_invalid_element_kind(export_schema): + doc = json.loads(json.dumps(FULL_EXPORT)) + doc["annotations"]["implementations"]["ms-001:REQ_010"][0]["element_kind"] = "FUNCTION" + with pytest.raises(jsonschema.ValidationError): + jsonschema.validate(doc, export_schema) + + +def test_export_invalid_test_status(export_schema): + doc = json.loads(json.dumps(FULL_EXPORT)) + doc["test_results"]["ms-001:com.example.FooTest.testBar"][0]["status"] = "error" + with pytest.raises(jsonschema.ValidationError): + jsonschema.validate(doc, export_schema) + + +def test_export_invalid_lifecycle_state(export_schema): + doc = json.loads(json.dumps(FULL_EXPORT)) + doc["requirements"]["ms-001:REQ_010"]["lifecycle"]["state"] = "active" + with pytest.raises(jsonschema.ValidationError): + jsonschema.validate(doc, export_schema) + + +def test_export_invalid_category(export_schema): + doc = json.loads(json.dumps(FULL_EXPORT)) + doc["requirements"]["ms-001:REQ_010"]["categories"] = ["unknown-category"] + with pytest.raises(jsonschema.ValidationError): + jsonschema.validate(doc, export_schema)