From c72ab5f0e133b6cbe5b38ef5765e5772b101adae Mon Sep 17 00:00:00 2001 From: "Keto D. Zhang" Date: Sun, 25 Aug 2024 15:01:41 -0700 Subject: [PATCH] test: add test for example node --- .pre-commit-config.yaml | 1 + tests/examples/test_node.py | 117 ++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 tests/examples/test_node.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2e401e3..76fadfc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,6 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks +default_install_hook_types: [pre-commit, pre-push, commit-msg] repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 diff --git a/tests/examples/test_node.py b/tests/examples/test_node.py new file mode 100644 index 0000000..d863f80 --- /dev/null +++ b/tests/examples/test_node.py @@ -0,0 +1,117 @@ +"""Tests example asdf-pydantic model for nodes of a graph/tree.""" + +from __future__ import annotations + +import textwrap + +import asdf +import asdf.exceptions +import asdf.schema +import pytest +import yaml +from asdf.extension import Extension + +from asdf_pydantic import AsdfPydanticConverter, AsdfPydanticModel + + +class AsdfNode(AsdfPydanticModel): + """Model for a node in a graph/tree. + + Nodes introduce self-referential types. Notice the type of the `child` + attribute it the node type itself. The ASDF schema for this model will require + self-referencing syntax. We assumes this form is valid for ASDF schemas: + + ```yaml + --- + type: object + anyOf: + - $ref: "#/definitions/AsdfNode" + definitions: + AsdfNode: + type: object + properties: + name: + type: string + child: + anyOf: + - $ref: "#/definitions/AsdfNode" + - type: null + ``` + + The self-reference happens in ``definitions[AsdfNode].properties.child.anyOf[0]`` + where the `$ref` is a special JSONSchema syntax that referes to the value, + `#/definitions/AsdfNode`. This value is a json path where `#` denotes "this + schema". + """ + + _tag = "asdf://asdf-pydantic/examples/tags/node-1.0.0" + + name: str + child: AsdfNode | None = None + + +@pytest.fixture() +def asdf_extension(): + """Registers an ASDF extension containing models for this test.""" + AsdfPydanticConverter.add_models(AsdfNode) + + class TestExtension(Extension): + extension_uri = "asdf://asdf-pydantic/examples/extensions/test-1.0.0" + + converters = [AsdfPydanticConverter()] # type: ignore + tags = [AsdfNode.get_tag_definition()] # type: ignore + + with asdf.config_context() as asdf_config: + asdf_config.add_resource_mapping( + { + yaml.safe_load(AsdfNode.model_asdf_schema())[ + "id" + ]: AsdfNode.model_asdf_schema() + } + ) + asdf_config.add_extension(TestExtension()) + yield asdf_config + + +@pytest.mark.usefixtures("asdf_extension") +def test_can_write_valid_asdf_file(tmp_path): + """Tests using the model to write an ASDF file validates its own schema.""" + af = asdf.AsdfFile() + af["root"] = AsdfNode(name="foo", child=None) + af.validate() + af.write_to(tmp_path / "test.asdf") + + with asdf.open(tmp_path / "test.asdf") as af: + assert af.tree + + +@pytest.mark.usefixtures("asdf_extension") +def test_errors_reading_invalid_asdf_file(tmp_path): + """Tests validation fails when ASDF file does not match the schema.""" + content = """\ + #ASDF 1.0.0 + #ASDF_STANDARD 1.5.0 + %YAML 1.1 + %TAG ! tag:stsci.edu:asdf/ + --- !core/asdf-1.1.0 + asdf_library: !core/software-1.0.0 {author: The ASDF Developers, homepage: 'http://github.com/asdf-format/asdf', + name: asdf, version: 3.4.0} + history: + extensions: + - !core/extension_metadata-1.0.0 + extension_class: asdf.extension._manifest.ManifestExtension + extension_uri: asdf://asdf-format.org/core/extensions/core-1.5.0 + manifest_software: !core/software-1.0.0 {name: asdf_standard, version: 1.1.1} + software: !core/software-1.0.0 {name: asdf, version: 3.4.0} + - !core/extension_metadata-1.0.0 {extension_class: tests.examples.test_node.setup_module..TestExtension, + extension_uri: 'asdf://asdf-pydantic/examples/extensions/test-1.0.0'} + root: ! + child: None + ... + """ + with open(tmp_path / "test.asdf", "wb") as f: + f.write(textwrap.dedent(content).encode("utf-8")) + + with pytest.raises(asdf.exceptions.ValidationError): + with asdf.open(tmp_path / "test.asdf") as af: + assert af.tree