Coverage for test / test_archive_reports.py: 100%

74 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-21 14:31 +0000

1# SPDX-FileCopyrightText: 2025 Arcangelo Massari <arcangelo.massari@unibo.it> 

2# 

3# SPDX-License-Identifier: ISC 

4 

5import json 

6import os 

7import shutil 

8import tempfile 

9import unittest 

10from unittest.mock import MagicMock, patch 

11 

12import yaml 

13from crowdsourcing.archive_manager import ArchiveManager 

14from crowdsourcing.archive_reports import check_and_archive_reports 

15 

16 

17class TestArchiveReports(unittest.TestCase): 

18 def setUp(self): 

19 """Set up test environment before each test.""" 

20 # Create a temporary directory for test files 

21 self.test_dir = tempfile.mkdtemp() 

22 self.reports_dir = os.path.join(self.test_dir, "docs/validation_reports") 

23 os.makedirs(self.reports_dir) 

24 

25 # Create test config file 

26 self.config = { 

27 "validation_reports": { 

28 "max_reports_before_archive": 3, 

29 "reports_dir": self.reports_dir, 

30 "index_file": os.path.join(self.reports_dir, "index.json"), 

31 }, 

32 "zenodo": { 

33 "metadata_template": { 

34 "title": "Test Archive", 

35 "description": "Test Description", 

36 "creators": [{"name": "Test Creator"}], 

37 "access_right": "open", 

38 "upload_type": "dataset", 

39 "license": "CC0-1.0", 

40 } 

41 }, 

42 } 

43 self.config_path = os.path.join(self.test_dir, "test_config.yaml") 

44 with open(self.config_path, "w") as f: 

45 yaml.dump(self.config, f) 

46 

47 # Set environment variables for testing 

48 self.env_patcher = patch.dict( 

49 os.environ, 

50 { 

51 "ENVIRONMENT": "development", 

52 "ZENODO_SANDBOX": "fake-token", 

53 }, 

54 ) 

55 self.env_patcher.start() 

56 

57 # Patch ArchiveManager to use our test config 

58 self.archive_manager_patcher = patch( 

59 "crowdsourcing.archive_reports.ArchiveManager", 

60 return_value=ArchiveManager(config_path=self.config_path), 

61 ) 

62 self.archive_manager_patcher.start() 

63 

64 def tearDown(self): 

65 """Clean up after each test.""" 

66 self.archive_manager_patcher.stop() 

67 self.env_patcher.stop() 

68 shutil.rmtree(self.test_dir) 

69 

70 @patch("crowdsourcing.archive_reports.logger") 

71 def test_check_and_archive_reports_no_reports(self, mock_logger): 

72 """Test check_and_archive_reports when there are no reports to archive.""" 

73 # Initialize archive manager and run check 

74 check_and_archive_reports() 

75 

76 # Verify logging 

77 mock_logger.info.assert_any_call("Starting report archival check") 

78 mock_logger.info.assert_any_call( 

79 "Archival threshold not reached, no action needed" 

80 ) 

81 

82 @patch("crowdsourcing.archive_reports.logger") 

83 def test_check_and_archive_reports_below_threshold(self, mock_logger): 

84 """Test check_and_archive_reports when number of reports is below threshold.""" 

85 # Initialize archive manager 

86 archive_manager = ArchiveManager(config_path=self.config_path) 

87 

88 # Add some reports (less than threshold) 

89 for i in range(2): # threshold is 3 

90 report_name = f"validation_issue_{i}.html" 

91 report_path = os.path.join(self.reports_dir, report_name) 

92 with open(report_path, "w") as f: 

93 f.write(f"Test report {i}") 

94 archive_manager.add_report( 

95 report_name, f"https://example.com/{report_name}" 

96 ) 

97 

98 # Run check 

99 check_and_archive_reports() 

100 

101 # Verify logging 

102 mock_logger.info.assert_any_call("Starting report archival check") 

103 mock_logger.info.assert_any_call( 

104 "Archival threshold not reached, no action needed" 

105 ) 

106 

107 @patch("crowdsourcing.archive_reports.logger") 

108 @patch("crowdsourcing.zenodo_utils.create_deposition_resource") 

109 @patch("crowdsourcing.zenodo_utils.get_zenodo_token") 

110 @patch("requests.put") 

111 @patch("requests.post") 

112 def test_check_and_archive_reports_above_threshold( 

113 self, mock_post, mock_put, mock_get_token, mock_create_deposition, mock_logger 

114 ): 

115 """Test check_and_archive_reports when number of reports is above threshold.""" 

116 # Setup mocks 

117 mock_response = {"id": "123", "links": {"bucket": "http://bucket-url"}} 

118 mock_deposition_response = MagicMock() 

119 mock_deposition_response.json.return_value = mock_response 

120 mock_deposition_response.raise_for_status = MagicMock() 

121 

122 mock_publish_response = MagicMock() 

123 mock_publish_response.json.return_value = {"doi": "10.5281/zenodo.123"} 

124 mock_publish_response.raise_for_status = MagicMock() 

125 mock_post.side_effect = [mock_deposition_response, mock_publish_response] 

126 

127 mock_get_token.return_value = "fake-token" 

128 mock_put.return_value.raise_for_status = MagicMock() 

129 

130 # Mock ArchiveManager to use our test config 

131 with patch( 

132 "crowdsourcing.archive_reports.ArchiveManager" 

133 ) as mock_archive_manager: 

134 mock_instance = mock_archive_manager.return_value 

135 mock_instance.needs_archival.return_value = True 

136 mock_instance.archive_reports.return_value = "10.5281/zenodo.123" 

137 

138 # Run check 

139 check_and_archive_reports() 

140 

141 # Verify logging 

142 mock_logger.info.assert_has_calls( 

143 [ 

144 unittest.mock.call("Starting report archival check"), 

145 unittest.mock.call( 

146 "Archival threshold reached, starting archival process" 

147 ), 

148 unittest.mock.call( 

149 "Successfully archived reports. DOI: 10.5281/zenodo.123" 

150 ), 

151 ], 

152 any_order=False, 

153 ) 

154 

155 @patch("crowdsourcing.archive_reports.logger") 

156 def test_check_and_archive_reports_error(self, mock_logger): 

157 """Test check_and_archive_reports when an error occurs.""" 

158 # Create a mock ArchiveManager that raises an exception 

159 with patch("crowdsourcing.archive_reports.ArchiveManager") as mock_manager: 

160 mock_instance = mock_manager.return_value 

161 mock_instance.needs_archival.side_effect = Exception("Test error") 

162 

163 # Run check 

164 with self.assertRaises(Exception): 

165 check_and_archive_reports() 

166 

167 # Verify logging 

168 mock_logger.info.assert_called_with("Starting report archival check") 

169 mock_logger.error.assert_called_with( 

170 "Error during report archival: Test error" 

171 ) 

172 

173 

174if __name__ == "__main__": # pragma: no cover 

175 unittest.main()