Coverage for tests/test_fits_format_version.py: 95%
63 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-09-30 11:30 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-09-30 11:30 +0000
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.
12from __future__ import annotations
14from pathlib import Path
16import astropy.io.fits
17import numpy as np
18import pytest
20from lsst.images import Image
21from lsst.images.fits import FitsInputArchive
22from lsst.images.serialization import ArchiveReadError, write_archive
25def _write_simple_image_fits(path: Path | str) -> None:
26 """Write a tiny Image to ``path`` via the high-level API."""
27 image = Image(0.0, shape=(4, 4), dtype="float32")
28 write_archive(image, path)
31def test_write_emits_fmtver_and_datamodl(tmp_path: Path) -> None:
32 """Verify a freshly-written FITS carries FMTVER=1 and the root DATAMODL."""
33 path = tmp_path / "x.fits"
34 _write_simple_image_fits(path)
35 with astropy.io.fits.open(path) as hdul:
36 assert hdul[0].header["FMTVER"] == 1
37 assert hdul[0].header["DATAMODL"] == "https://images.lsst.io/schemas/image-1.0.0"
40def test_read_succeeds_when_fmtver_matches(tmp_path: Path) -> None:
41 """Verify that a round-trip read of a freshly-written file succeeds."""
42 path = tmp_path / "x.fits"
43 _write_simple_image_fits(path)
44 with FitsInputArchive.open(path):
45 pass
48def test_read_fails_when_fmtver_too_high(tmp_path: Path) -> None:
49 """Verify that a file whose FMTVER is newer than this release raises."""
50 path = tmp_path / "x.fits"
51 _write_simple_image_fits(path)
52 with astropy.io.fits.open(path, mode="update") as hdul:
53 hdul[0].header["FMTVER"] = 2
54 hdul.flush()
55 with pytest.raises(ArchiveReadError):
56 with FitsInputArchive.open(path):
57 pass
60def test_read_fails_when_fmtver_absent(tmp_path: Path) -> None:
61 """Verify a file lacking FMTVER is rejected rather than assumed to be v1.
63 FitsOutputArchive writes FMTVER before anything else, so every file this
64 reader can open has one; absence means a damaged file, and guessing 1
65 would guess at the layout.
66 """
67 path = tmp_path / "x.fits"
68 _write_simple_image_fits(path)
69 with astropy.io.fits.open(path, mode="update") as hdul:
70 del hdul[0].header["FMTVER"]
71 hdul.flush()
72 with pytest.raises(ArchiveReadError, match="FMTVER"):
73 with FitsInputArchive.open(path):
74 pass
77def test_get_basic_info_fails_when_fmtver_absent(tmp_path: Path) -> None:
78 """Verify the info-only read requires FMTVER as well."""
79 path = tmp_path / "x.fits"
80 _write_simple_image_fits(path)
81 with astropy.io.fits.open(path, mode="update") as hdul:
82 del hdul[0].header["FMTVER"]
83 hdul.flush()
84 with pytest.raises(ArchiveReadError, match="FMTVER"):
85 FitsInputArchive.get_basic_info(path)
88def test_read_succeeds_when_datamodl_absent(tmp_path: Path) -> None:
89 """Verify a file with the layout but no schema card still opens.
91 DATAMODL is informational on read, so only callers that ask for the
92 schema through ``info`` need it; requiring the layout stamp must not
93 start requiring this one too.
94 """
95 path = tmp_path / "x.fits"
96 _write_simple_image_fits(path)
97 with astropy.io.fits.open(path, mode="update") as hdul:
98 del hdul[0].header["DATAMODL"]
99 hdul.flush()
100 with FitsInputArchive.open(path) as archive:
101 with pytest.raises(ArchiveReadError, match="DATAMODL"):
102 archive.info
105def test_foreign_fits_is_rejected_cleanly(tmp_path: Path) -> None:
106 """Verify a FITS file this package did not write raises ArchiveReadError.
108 Such a file has never been readable -- it carries none of the container
109 cards -- but it used to surface as a bare KeyError from the first card
110 popped without a default.
111 """
112 path = tmp_path / "plain.fits"
113 astropy.io.fits.PrimaryHDU(np.zeros((4, 4), dtype="float32")).writeto(path)
114 with pytest.raises(ArchiveReadError):
115 with FitsInputArchive.open(path):
116 pass