Coverage for tests/test_ndf_common.py: 97%

77 statements  

« 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. 

11 

12from __future__ import annotations 

13 

14import pytest 

15 

16try: 

17 from lsst.images.ndf import ( 

18 HdsNameShrinker, 

19 NdfPointerModel, 

20 archive_path_to_hdf5_path, 

21 ) 

22 from lsst.images.ndf._hds import DAT__SZNAM 

23 

24 HAVE_H5PY = True 

25except ImportError: 

26 HAVE_H5PY = False 

27 

28skip_no_h5py = pytest.mark.skipif(not HAVE_H5PY, reason="h5py is not installed") 

29 

30 

31@skip_no_h5py 

32def test_round_trips_through_json() -> None: 

33 """Verify NdfPointerModel round-trips through JSON serialization.""" 

34 original = NdfPointerModel(path="/MORE/LSST/PSF") 

35 json_bytes = original.model_dump_json().encode() 

36 recovered = NdfPointerModel.model_validate_json(json_bytes) 

37 assert recovered == original 

38 

39 

40@skip_no_h5py 

41def test_archive_path_to_hdf5_path() -> None: 

42 """Verify archive_path_to_hdf5_path maps archive paths to HDF5 paths.""" 

43 shrinker = HdsNameShrinker() 

44 assert archive_path_to_hdf5_path("", shrinker) == "/MORE/LSST/JSON" 

45 assert archive_path_to_hdf5_path("/psf", shrinker) == "/MORE/LSST/PSF" 

46 assert archive_path_to_hdf5_path("/psf/coefficients", shrinker) == "/MORE/LSST/PSF/COEFFICIENTS" 

47 

48 

49@skip_no_h5py 

50def test_archive_path_shrinks_long_components() -> None: 

51 """Verify long path components are shrunk to the HDS name limit.""" 

52 shrinker = HdsNameShrinker() 

53 result = archive_path_to_hdf5_path("/psf/this_component_is_too_long", shrinker) 

54 assert result.startswith("/MORE/LSST/PSF/") 

55 leaf = result.rsplit("/", 1)[-1] 

56 assert len(leaf) <= DAT__SZNAM 

57 # The short parent component is untouched; only the long leaf shrinks. 

58 assert result.split("/")[3] == "PSF" 

59 

60 

61@skip_no_h5py 

62def test_archive_path_shrink_round_trips_to_same_value() -> None: 

63 """Verify shrinking the same path twice returns the same HDF5 path.""" 

64 shrinker = HdsNameShrinker() 

65 assert archive_path_to_hdf5_path("/noise_realizations/0", shrinker) == archive_path_to_hdf5_path( 

66 "/noise_realizations/0", shrinker 

67 ) 

68 

69 

70@skip_no_h5py 

71def test_short_names_pass_through_uppercased() -> None: 

72 """Verify short names are uppercased and passed through unchanged.""" 

73 shrinker = HdsNameShrinker() 

74 assert shrinker.shrink("psf") == "PSF" 

75 # A name exactly at the limit passes through unchanged (uppercased). 

76 assert shrinker.shrink("a" * DAT__SZNAM) == "A" * DAT__SZNAM 

77 # One character over the limit is shrunk to the limit. 

78 assert len(shrinker.shrink("a" * (DAT__SZNAM + 1))) == DAT__SZNAM 

79 

80 

81@skip_no_h5py 

82def test_long_names_keep_prefix_and_get_counter_token() -> None: 

83 """Verify long names are truncated to the HDS limit with a counter.""" 

84 shrinker = HdsNameShrinker() 

85 shrunk = shrinker.shrink("noise_realizations") 

86 assert len(shrunk) == DAT__SZNAM 

87 assert shrunk == "NOISE_REALI_001" 

88 

89 

90@skip_no_h5py 

91def test_shrink_is_deterministic_per_instance() -> None: 

92 """Verify shrinking the same name twice returns the same result.""" 

93 shrinker = HdsNameShrinker() 

94 assert shrinker.shrink("noise_realizations") == shrinker.shrink("noise_realizations") 

95 

96 

97@skip_no_h5py 

98def test_distinct_long_names_get_distinct_tokens() -> None: 

99 """Verify distinct long names with the same prefix get distinct tokens.""" 

100 shrinker = HdsNameShrinker() 

101 # Identical truncated prefixes cannot collide because the counter 

102 # increments for each newly assigned name. 

103 assert shrinker.shrink("noise_realization_field") == "NOISE_REALI_001" 

104 assert shrinker.shrink("noise_realization_other") == "NOISE_REALI_002" 

105 

106 

107@skip_no_h5py 

108def test_reserve_shortens_the_budget() -> None: 

109 """Verify the reserve parameter reduces the available name length.""" 

110 shrinker = HdsNameShrinker() 

111 shrunk = shrinker.shrink("noise_realizations", reserve=2) 

112 assert len(shrunk) == DAT__SZNAM - 2 

113 

114 

115@skip_no_h5py 

116def test_version_one_matches_plain_shrink() -> None: 

117 """Verify shrink_versioned(name, 1) equals plain shrink(name).""" 

118 shrinker = HdsNameShrinker() 

119 assert shrinker.shrink_versioned("noise_realizations", 1) == shrinker.shrink("noise_realizations") 

120 

121 

122@skip_no_h5py 

123def test_short_versioned_name_keeps_visible_suffix() -> None: 

124 """Verify short names with a version suffix remain human-readable.""" 

125 shrinker = HdsNameShrinker() 

126 assert shrinker.shrink_versioned("data", 2) == "DATA_2" 

127 

128 

129@skip_no_h5py 

130def test_long_versioned_name_preserves_suffix_within_limit() -> None: 

131 """Verify long versioned names stay within the HDS limit.""" 

132 shrinker = HdsNameShrinker() 

133 shrunk = shrinker.shrink_versioned("noise_realizations", 99) 

134 assert len(shrunk) == DAT__SZNAM 

135 assert shrunk.endswith("_99") 

136 

137 

138@skip_no_h5py 

139def test_same_base_different_versions_are_distinct() -> None: 

140 """Verify the same base name with differing versions gives distinct 

141 results. 

142 """ 

143 shrinker = HdsNameShrinker() 

144 assert shrinker.shrink_versioned("noise_realizations", 2) != shrinker.shrink_versioned( 

145 "noise_realizations", 3 

146 )