Coverage for tests/test_serialization_basic_info.py: 98%
81 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-16 15:24 -0700
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-16 15:24 -0700
1# This file is part of lsst-images.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# Use of this source code is governed by a 3-clause BSD-style
10# license that can be found in the LICENSE file.
11from __future__ import annotations
13import json
14from pathlib import Path
16import numpy as np
17import pydantic
18import pytest
20from lsst.images import Box, Image
21from lsst.images import fits as images_fits
22from lsst.images import json as images_json
23from lsst.images.fits import FitsInputArchive
24from lsst.images.json import JsonInputArchive
25from lsst.images.serialization import ArchiveInfo, InputArchive
27try:
28 import h5py
30 from lsst.images import ndf as images_ndf
31 from lsst.images.ndf import NdfInputArchive
33 HAVE_H5PY = True
34except ImportError:
35 HAVE_H5PY = False
37skip_no_h5py = pytest.mark.skipif(not HAVE_H5PY, reason="h5py is not installed")
40def _make_image() -> Image:
41 """Return a small float32 Image for serialization tests."""
42 return Image(np.zeros((4, 4), dtype=np.float32), bbox=Box.factory[0:4, 0:4])
45def test_archive_info_from_schema_url() -> None:
46 """Verify ArchiveInfo.from_schema_url parses schema name, version, and
47 format correctly.
48 """
49 info = ArchiveInfo.from_schema_url("https://images.lsst.io/schemas/visit_image-1.2.3", format_version=1)
50 assert info.schema_name == "visit_image"
51 assert info.schema_version == "1.2.3"
52 assert info.schema_url == "https://images.lsst.io/schemas/visit_image-1.2.3"
53 assert info.format_version == 1
56def test_archive_info_from_schema_url_none_format() -> None:
57 """Verify ArchiveInfo.from_schema_url accepts None for format_version."""
58 info = ArchiveInfo.from_schema_url("https://images.lsst.io/schemas/image-1.0.0", format_version=None)
59 assert info.schema_name == "image"
60 assert info.format_version is None
63def test_archive_info_frozen() -> None:
64 """Verify ArchiveInfo is frozen (schema_name cannot be reassigned)."""
65 info = ArchiveInfo.from_schema_url("https://images.lsst.io/schemas/image-1.0.0", format_version=None)
66 with pytest.raises(pydantic.ValidationError):
67 info.schema_name = "other" # type: ignore[misc]
70def test_archive_info_from_schema_url_invalid() -> None:
71 """Verify ArchiveInfo.from_schema_url raises ValueError for URLs without
72 a version.
73 """
74 with pytest.raises(ValueError):
75 ArchiveInfo.from_schema_url("https://images.lsst.io/schemas/noversion", format_version=None)
78def test_archive_info_from_schema_url_foreign_host() -> None:
79 """Verify ArchiveInfo.from_schema_url rejects URLs from non-lsst hosts."""
80 with pytest.raises(ValueError):
81 ArchiveInfo.from_schema_url("https://evil.example.com/schemas/image-1.0.0", format_version=None)
84def test_input_archive_get_basic_info_base_raises() -> None:
85 """Verify InputArchive.get_basic_info raises NotImplementedError on the
86 base class.
87 """
88 with pytest.raises(NotImplementedError):
89 InputArchive.get_basic_info("x.fits")
92def test_json_basic_info(tmp_path: Path) -> None:
93 """Verify JsonInputArchive.get_basic_info returns correct schema
94 metadata.
95 """
96 path = tmp_path / "x.json"
97 images_json.write(_make_image(), path)
98 info = JsonInputArchive.get_basic_info(path)
99 assert info.schema_name == "image"
100 assert info.schema_version == "1.0.0"
101 assert info.schema_url == "https://images.lsst.io/schemas/image-1.0.0"
102 assert info.format_version is None
105def test_fits_basic_info(tmp_path: Path) -> None:
106 """Verify FitsInputArchive.get_basic_info returns correct schema
107 metadata.
108 """
109 path = tmp_path / "x.fits"
110 images_fits.write(_make_image(), path)
111 info = FitsInputArchive.get_basic_info(path)
112 assert info.schema_name == "image"
113 assert info.schema_version == "1.0.0"
114 assert info.schema_url == "https://images.lsst.io/schemas/image-1.0.0"
115 assert info.format_version == 1
118@skip_no_h5py
119def test_ndf_basic_info(tmp_path: Path) -> None:
120 """Verify NdfInputArchive.get_basic_info returns correct schema
121 metadata.
122 """
123 path = tmp_path / "x.sdf"
124 images_ndf.write(_make_image(), path)
125 info = NdfInputArchive.get_basic_info(path)
126 assert info.schema_name == "image"
127 assert info.schema_version == "1.0.0"
128 assert info.schema_url == "https://images.lsst.io/schemas/image-1.0.0"
129 assert info.format_version == 1
132@skip_no_h5py
133def test_ndf_basic_info_ignores_nested_json(tmp_path: Path) -> None:
134 """Verify NdfInputArchive.get_basic_info reads the top-level schema, not
135 nested pointer trees.
136 """
137 path = tmp_path / "nested.sdf"
138 images_ndf.write(_make_image(), path)
139 # Inject a nested pointer-tree JSON alongside the top-level one; it
140 # sorts before "JSON" so a depth-first scan would hit it first.
141 payload = json.dumps({"schema_url": "https://images.lsst.io/schemas/aaa_child-9.9.9"})
142 with h5py.File(path, "a") as handle:
143 child = handle.require_group("MORE/LSST/AAA")
144 child.create_dataset("JSON", data=np.frombuffer(payload.encode("utf-8"), dtype=np.uint8))
145 info = NdfInputArchive.get_basic_info(path)
146 assert info.schema_name == "image"
147 assert info.schema_url == "https://images.lsst.io/schemas/image-1.0.0"