Coverage for test/test_annosaurus.py: 96%

186 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-23 02:22 +0000

1 

2import pytest 

3import json 

4 

5from unittest.mock import patch 

6 

7from application.vars.annosaurus import Annosaurus, AuthenticationError 

8from test.data.vars_responses import ex_23060001 

9 

10 

11class MockResponse: 

12 def __init__(self, url: str, method: str, status_code: int, headers=None, data=None): 

13 self.url = url 

14 self.status_code = status_code 

15 self.method = method 

16 self.headers = headers or {} 

17 self.data = data 

18 

19 def json(self): 

20 if self.status_code == 404: 

21 return {} 

22 if self.method == 'GET': 

23 match self.url: 

24 case 'http://localhost:test/observations/0059f860-4799-485f-c06c-5830e5ddd31e': 

25 return ex_23060001['annotations'][0] 

26 case 'http://localhost:test/observations/0d9133d7-1d49-47d5-4b6d-6e4fb25dd41e': 

27 return ex_23060001['annotations'][1] 

28 case 'http://localhost:test/observations/080118db-baa2-468a-d06a-144249c1d41e': 

29 return ex_23060001['annotations'][2] 

30 case 'http://localhost:test/observations/35aa2bb9-d067-419b-9a6e-09cdce8ed41e': 

31 return ex_23060001['annotations'][3] 

32 case 'http://localhost:test/observations/invalid': 

33 return {} 

34 elif self.method == 'POST': 

35 match self.url: 

36 case 'http://localhost:test/auth': 

37 if self.headers.get('Authorization') == 'APIKEY valid': 

38 return {'access_token': 'jwt'} 

39 case 'http://localhost:test/associations': 

40 data = self.data 

41 del data['observation_uuid'] 

42 data['uuid'] = 'new_uuid' 

43 return data 

44 elif self.method == 'PUT': 

45 match self.url: 

46 case 'http://localhost:test/annotations/0059f860-4799-485f-c06c-5830e5ddd31e': 

47 annotation = ex_23060001['annotations'][0].copy() 

48 annotation['concept'] = self.data['concept'] 

49 return annotation 

50 case 'http://localhost:test/associations/abc123': 

51 data = self.data 

52 data['uuid'] = 'abc123' 

53 return self.data 

54 case 'http://localhost:test/associations/c4eaa100-comment': 

55 data = self.data 

56 data['uuid'] = 'c4eaa100-comment' 

57 return self.data 

58 case 'http://localhost:test/associations/faf820ac-93fd-4d5a-486a-87775ec1d41e': 

59 data = self.data 

60 data['uuid'] = 'faf820ac-93fd-4d5a-486a-87775ec1d41e' 

61 return self.data 

62 case 'http://localhost:test/associations/297d23d7-5979-46e7-6f66-8f1fcf8ed41e': 

63 data = self.data 

64 data['uuid'] = '297d23d7-5979-46e7-6f66-8f1fcf8ed41e' 

65 return self.data 

66 elif self.method == 'DELETE': 

67 match self.url: 

68 case 'http://localhost:test/associations/abc123': 

69 return {} 

70 raise json.JSONDecodeError('Unable to decode JSON', '', 0) 

71 

72 def text(self): 

73 return '<html>Invalid</html>' 

74 

75 

76def mocked_requests_get(*args, **kwargs): 

77 return MockResponse( 

78 url=kwargs.get('url'), 

79 method='GET', 

80 status_code=200, 

81 headers=kwargs.get('headers'), 

82 ) 

83 

84 

85def mocked_requests_get_404(*args, **kwargs): 

86 return MockResponse( 

87 url=kwargs.get('url'), 

88 method='GET', 

89 status_code=404, 

90 headers=kwargs.get('headers'), 

91 ) 

92 

93 

94def mocked_requests_post(*args, **kwargs): 

95 return MockResponse( 

96 url=kwargs.get('url'), 

97 method='POST', 

98 status_code=201, 

99 headers=kwargs.get('headers'), 

100 data=kwargs.get('data'), 

101 ) 

102 

103 

104def mocked_requests_put(*args, **kwargs): 

105 return MockResponse( 

106 url=kwargs.get('url'), 

107 method='PUT', 

108 status_code=200, 

109 headers=kwargs.get('headers'), 

110 data=kwargs.get('data'), 

111 ) 

112 

113 

114def mocked_requests_delete(*args, **kwargs): 

115 return MockResponse( 

116 url=kwargs.get('url'), 

117 method='DELETE', 

118 status_code=204, 

119 headers=kwargs.get('headers'), 

120 ) 

121 

122 

123class TestAnnosaurus: 

124 def test_init(self): 

125 anno = Annosaurus('http://localhost:test/') 

126 assert anno.base_url == 'http://localhost:test' 

127 

128 def test_authorize_jwt(self): 

129 anno = Annosaurus('http://localhost:test/') 

130 assert anno.authorize(jwt='jwt') == 'jwt' 

131 

132 @patch('requests.post', side_effect=mocked_requests_post) 

133 def test_authorize_client_secret(self, mock_post): 

134 anno = Annosaurus('http://localhost:test/') 

135 assert anno.authorize(client_secret='valid') == 'jwt' 

136 assert mock_post.call_args[1]['headers'] == {'Authorization': 'APIKEY valid'} 

137 

138 @patch('requests.post', side_effect=mocked_requests_post) 

139 def test_authorize_invalid(self, _): 

140 anno = Annosaurus('http://localhost:test/') 

141 with pytest.raises(AuthenticationError): 

142 anno.authorize(client_secret='invalid') 

143 

144 def test_authorize_none(self): 

145 anno = Annosaurus('http://localhost:test/') 

146 with pytest.raises(AuthenticationError): 

147 anno.authorize() 

148 

149 def test_auth_header(self): 

150 anno = Annosaurus('http://localhost:test/') 

151 assert anno._auth_header('jwt') == {'Authorization': 'Bearer jwt'} 

152 

153 @patch('requests.post', side_effect=mocked_requests_post) 

154 def test_create_association(self, _): 

155 anno = Annosaurus('http://localhost:test') 

156 new_association = {'link_name': 'test', 'to_concept': 'test'} 

157 created = anno.create_association( 

158 observation_uuid='abc123', 

159 association=new_association, 

160 jwt='jwt', 

161 ) 

162 assert created['status'] == 201 

163 assert created['json'] == { 

164 'link_name': 'test', 

165 'link_value': 'nil', 

166 'to_concept': 'test', 

167 'uuid': 'new_uuid', 

168 } 

169 

170 def test_create_association_missing_link_value(self): 

171 anno = Annosaurus('http://localhost:test') 

172 with pytest.raises(ValueError): 

173 anno.create_association( 

174 observation_uuid='abc123', 

175 association={'to_concept': 'test'}, 

176 jwt='jwt', 

177 ) 

178 

179 @patch('requests.put', side_effect=mocked_requests_put) 

180 def test_update_association(self, _): 

181 anno = Annosaurus('http://localhost:test') 

182 updated = anno.update_association( 

183 association_uuid='abc123', 

184 association={'link_name': 'test', 'to_concept': 'test'}, 

185 jwt='jwt', 

186 ) 

187 assert updated['status'] == 200 

188 assert updated['json'] == { 

189 'link_name': 'test', 

190 'to_concept': 'test', 

191 'uuid': 'abc123', 

192 } 

193 

194 @patch('requests.delete', side_effect=mocked_requests_delete) 

195 def test_delete_association(self, _): 

196 anno = Annosaurus('http://localhost:test') 

197 deleted = anno.delete_association( 

198 association_uuid='abc123', 

199 jwt='jwt', 

200 ) 

201 assert deleted['status'] == 204 

202 assert deleted['json'] == {} 

203 

204 @patch('requests.put', side_effect=mocked_requests_put) 

205 def test_update_concept_name(self, _): 

206 anno = Annosaurus('http://localhost:test') 

207 updated = anno.update_concept_name( 

208 observation_uuid='0059f860-4799-485f-c06c-5830e5ddd31e', 

209 concept='Magikarp', 

210 jwt='jwt', 

211 ) 

212 old_anno = ex_23060001['annotations'][0].copy() 

213 old_anno['concept'] = 'Magikarp' 

214 assert updated['status'] == 200 

215 assert updated['json'] == old_anno 

216 

217 @patch('requests.get', side_effect=mocked_requests_get_404) 

218 def test_update_annotation_comment_404(self, _): 

219 anno = Annosaurus('http://localhost:test') 

220 updated = anno.update_annotation_comment( 

221 observation_uuid='invalid', 

222 reviewers=['Test Reviewer'], 

223 jwt='jwt', 

224 ) 

225 assert updated['status'] == 404 

226 assert updated['json'] == {} 

227 

228 @patch('requests.get', side_effect=mocked_requests_get) 

229 @patch('requests.post', side_effect=mocked_requests_post) 

230 def test_update_annotation_comment_new_one_reviewer(self, _, __): 

231 anno = Annosaurus('http://localhost:test') 

232 created = anno.update_annotation_comment( 

233 observation_uuid='0059f860-4799-485f-c06c-5830e5ddd31e', 

234 reviewers=['Test Reviewer'], 

235 jwt='jwt', 

236 ) 

237 assert created['status'] == 201 

238 assert created['json'] == { 

239 'link_name': 'comment', 

240 'link_value': 'Added for review: Test Reviewer', 

241 'to_concept': 'self', 

242 'uuid': 'new_uuid' 

243 } 

244 

245 @patch('requests.get', side_effect=mocked_requests_get) 

246 @patch('requests.post', side_effect=mocked_requests_post) 

247 def test_update_annotation_comment_new_multiple_reviewers(self, _, __): 

248 anno = Annosaurus('http://localhost:test') 

249 created = anno.update_annotation_comment( 

250 observation_uuid='0059f860-4799-485f-c06c-5830e5ddd31e', 

251 reviewers=['Test Reviewer', 'Ronald McDonald'], 

252 jwt='jwt', 

253 ) 

254 assert created['status'] == 201 

255 assert created['json'] == { 

256 'link_name': 'comment', 

257 'link_value': 'Added for review: Test Reviewer, Ronald McDonald', 

258 'to_concept': 'self', 

259 'uuid': 'new_uuid' 

260 } 

261 

262 @patch('requests.get', side_effect=mocked_requests_get) 

263 @patch('requests.put', side_effect=mocked_requests_put) 

264 def test_update_annotation_comment_update_no_prev_reviewers(self, _, __): 

265 anno = Annosaurus('http://localhost:test') 

266 updated = anno.update_annotation_comment( 

267 observation_uuid='0d9133d7-1d49-47d5-4b6d-6e4fb25dd41e', 

268 reviewers=['J. Dolan'], 

269 jwt='jwt', 

270 ) 

271 assert updated['status'] == 200 

272 assert updated['json'] == { 

273 'link_value': 'this is a comment; Added for review: J. Dolan', 

274 'uuid': 'c4eaa100-comment' 

275 } 

276 

277 @patch('requests.get', side_effect=mocked_requests_get) 

278 @patch('requests.put', side_effect=mocked_requests_put) 

279 def test_update_annotation_comment_update_prev_reviewers_add(self, _, __): 

280 anno = Annosaurus('http://localhost:test') 

281 updated = anno.update_annotation_comment( 

282 observation_uuid='080118db-baa2-468a-d06a-144249c1d41e', 

283 reviewers=['J. Dolan', 'Don Draper'], 

284 jwt='jwt', 

285 ) 

286 assert updated['status'] == 200 

287 assert updated['json'] == { 

288 'link_value': 'Added for review: J. Dolan, Don Draper', 

289 'uuid': 'faf820ac-93fd-4d5a-486a-87775ec1d41e' 

290 } 

291 

292 @patch('requests.get', side_effect=mocked_requests_get) 

293 @patch('requests.put', side_effect=mocked_requests_put) 

294 def test_update_annotation_comment_update_prev_reviewers_replace(self, _, __): 

295 anno = Annosaurus('http://localhost:test') 

296 updated = anno.update_annotation_comment( 

297 observation_uuid='080118db-baa2-468a-d06a-144249c1d41e', 

298 reviewers=['J. Dolan'], 

299 jwt='jwt', 

300 ) 

301 assert updated['status'] == 200 

302 assert updated['json'] == { 

303 'link_value': 'Added for review: J. Dolan', 

304 'uuid': 'faf820ac-93fd-4d5a-486a-87775ec1d41e' 

305 } 

306 

307 @patch('requests.get', side_effect=mocked_requests_get) 

308 @patch('requests.delete', side_effect=mocked_requests_delete) 

309 def test_update_annotation_comment_delete_empty(self, _, __): 

310 anno = Annosaurus('http://localhost:test') 

311 deleted = anno.update_annotation_comment( 

312 observation_uuid='080118db-baa2-468a-d06a-144249c1d41e', 

313 reviewers=[], 

314 jwt='jwt', 

315 ) 

316 assert deleted['status'] == 204 

317 assert deleted['json'] == {} 

318 

319 @patch('requests.get', side_effect=mocked_requests_get) 

320 @patch('requests.put', side_effect=mocked_requests_put) 

321 def test_update_annotation_comment_delete_not_empty(self, _, __): 

322 anno = Annosaurus('http://localhost:test') 

323 deleted = anno.update_annotation_comment( 

324 observation_uuid='35aa2bb9-d067-419b-9a6e-09cdce8ed41e', 

325 reviewers=[], 

326 jwt='jwt', 

327 ) 

328 assert deleted['status'] == 200 

329 assert deleted['json'] == { 

330 'link_value': 'This is a weird lookin sponge thing!', 

331 'uuid': '297d23d7-5979-46e7-6f66-8f1fcf8ed41e' 

332 }