Coverage for src/mesh/tests/models/test_submission_models.py: 100%

307 statements  

« prev     ^ index     » next       coverage.py v7.9.0, created at 2026-02-04 09:42 +0000

1from unittest.mock import patch 

2 

3from django.utils import timezone 

4 

5from mesh.model.exceptions import SubmissionStateError 

6 

7from ...models.editorial_models import EditorSubmissionRight 

8 

9# from ...models.factories import ReviewFactory 

10from ...models.factories import ( 

11 EditorialDecisionFactory, 

12 SubmissionAuthorFactory, 

13 SubmissionFactory, 

14 SubmissionVersionFactory, 

15 UserFactory, 

16) 

17from ...models.submission_models import ( 

18 Submission, 

19 SubmissionAuthor, 

20 SubmissionLog, 

21 SubmissionMainFile, 

22 SubmissionState, 

23 SubmissionVersion, 

24) 

25from ...models.user_models import User 

26from ..base_test_case import BaseTestCase 

27 

28 

29class SubmissionTestCase(BaseTestCase): 

30 # Emptying the Submission table will delete all other models thanks to their 

31 # cascade relationship. All models are related to a Submission somehow, except 

32 # JournalSection and user models. 

33 used_models = [Submission] 

34 class_used_models = [User] 

35 

36 @classmethod 

37 def setUpClass(cls): 

38 super().setUpClass() 

39 cls.user_author = UserFactory.create() 

40 

41 # def test_unique_submission_name_per_user(self): 

42 # submission = SubmissionFactory.create(created_by=self.user_author) 

43 # # We need to encapsulate the database exception in a transaction, otherwise 

44 # # successive DB operations are not permitted. 

45 # with transaction.atomic(): 

46 # self.assertRaises( 

47 # IntegrityError, 

48 # SubmissionFactory.create, 

49 # name=submission.name, 

50 # created_by=self.user_author, 

51 # ) 

52 

53 def test_date_submission(self): 

54 submission = SubmissionFactory.create(created_by=self.user_author) 

55 self.assertNotEqual(submission.date_created, submission.date_first_version) 

56 self.assertEqual(submission.date_submission, submission.date_created) 

57 

58 submission.date_first_version = timezone.now() 

59 submission.save() 

60 

61 self.assertNotEqual(submission.date_created, submission.date_first_version) 

62 self.assertEqual(submission.date_submission, submission.date_first_version) 

63 

64 def test_is_draft(self): 

65 submission = SubmissionFactory.create(created_by=self.user_author) 

66 self.assertTrue(submission.is_draft) 

67 

68 submission.state = SubmissionState.SUBMITTED.value 

69 self.assertFalse(submission.is_draft) 

70 

71 def test_version_attributes(self): 

72 """ 

73 This tests the submission attributes `all_versions` and `current_version` 

74 along with the `unique_draft_submission_version` constraint 

75 """ 

76 submission = SubmissionFactory.create(created_by=self.user_author) 

77 self.assertEqual(len(submission.all_versions), 0) 

78 self.assertIsNone(submission.current_version) 

79 

80 version_1 = SubmissionVersion(submission=submission, created_by=self.user_author) 

81 version_1.save() 

82 

83 submission = Submission.objects.get(pk=submission.pk) 

84 self.assertEqual(len(submission.all_versions), 1) 

85 self.assertEqual(submission.all_versions[0].pk, version_1.pk) 

86 self.assertEqual(submission.current_version.pk, version_1.pk) # type:ignore 

87 

88 version_2 = SubmissionVersion(submission=submission, created_by=self.user_author) 

89 # The UniqueConstraint has been removed. 

90 # You can have more than 1 version submitted (when you reach Round 3) 

91 # # Can't save because version_1 is not submitted yet. 

92 # with transaction.atomic(): 

93 # self.assertRaises( 

94 # IntegrityError, 

95 # version_2.save, 

96 # ) 

97 

98 version_1.submitted = True 

99 version_1.save() 

100 version_2.save() 

101 submission = Submission.objects.get(pk=submission.pk) 

102 self.assertEqual(len(submission.all_versions), 2) 

103 self.assertEqual(submission.all_versions[0].pk, version_2.pk) 

104 self.assertEqual(submission.all_versions[1].pk, version_1.pk) 

105 self.assertEqual(submission.current_version.pk, version_2.pk) # type:ignore 

106 

107 def test_all_authors(self): 

108 submission = SubmissionFactory.create() 

109 self.assertEqual(len(submission.all_authors), 0) 

110 

111 author_1 = SubmissionAuthorFactory.create(submission=submission, first_name="B") 

112 author_2 = SubmissionAuthorFactory.create(submission=submission, first_name="A") 

113 submission = Submission.objects.get(pk=submission.pk) 

114 self.assertEqual(len(submission.all_authors), 2) 

115 self.assertEqual(submission.all_authors[0].pk, author_2.pk) 

116 self.assertEqual(submission.all_authors[1].pk, author_1.pk) 

117 

118 # # Test unique constraint 

119 # with transaction.atomic(): 

120 # self.assertRaises( 

121 # IntegrityError, 

122 # SubmissionAuthorFactory.create, 

123 # submission=submission, 

124 # email=author_1.email, 

125 # ) 

126 

127 def test_all_assigned_editors(self): 

128 submission = SubmissionFactory.create() 

129 editor_1 = UserFactory.create(first_name="Jean") 

130 editor_2 = UserFactory.create(first_name="Amélie") 

131 _ = EditorSubmissionRight.objects.create(user=editor_1, submission=submission) 

132 _ = EditorSubmissionRight.objects.create(user=editor_2, submission=submission) 

133 

134 assigned_editors = submission.all_assigned_editors 

135 self.assertEqual(len(assigned_editors), 2) 

136 self.assertEqual(assigned_editors[0].pk, editor_2.pk) 

137 self.assertEqual(assigned_editors[1].pk, editor_1.pk) 

138 

139 

140class SubmissionWorkflowTestCase(BaseTestCase): 

141 used_models = [Submission] 

142 

143 @classmethod 

144 def setUpClass(cls): 

145 super().setUpClass() 

146 cls.user_author = UserFactory.create() 

147 cls.user_reviewer = UserFactory.create() 

148 cls.user_editor = UserFactory.create() 

149 cls.user_journal_manager = UserFactory.create(journal_manager=True) 

150 cls.user_admin = UserFactory.create(is_superuser=True) 

151 

152 @classmethod 

153 def tearDownClass(cls): 

154 super().tearDownClass() 

155 User.objects.all().delete() 

156 

157 def setUp(self): 

158 super().setUp() 

159 

160 def test_is_submittable(self): 

161 submission = SubmissionFactory.create(created_by=self.user_author) 

162 self.assertFalse(submission.is_submittable) 

163 

164 submission.state = SubmissionState.OPENED.value 

165 submission.author_agreement = True 

166 submission.save() 

167 submission_version = SubmissionVersionFactory.create(submission=submission) 

168 SubmissionMainFile.objects.create(attached_to=submission_version, file=self.dummy_file) 

169 SubmissionAuthorFactory.create(submission=submission) 

170 submission = Submission.objects.get(pk=submission.pk) 

171 self.assertTrue(submission.is_submittable) 

172 

173 # Test each condition separately 

174 submission.state = SubmissionState.SUBMITTED.value 

175 self.assertFalse(submission.is_submittable) 

176 

177 submission.state = SubmissionState.OPENED.value 

178 self.assertTrue(submission.is_submittable) 

179 SubmissionAuthor.objects.all().delete() 

180 submission = Submission.objects.get(pk=submission.pk) 

181 self.assertFalse(submission.is_submittable) 

182 

183 SubmissionAuthorFactory.create(submission=submission) 

184 submission = Submission.objects.get(pk=submission.pk) 

185 self.assertTrue(submission.is_submittable) 

186 submission_version.submitted = True 

187 submission_version.save() 

188 submission = Submission.objects.get(pk=submission.pk) 

189 self.assertFalse(submission.is_submittable) 

190 

191 submission_version.submitted = False 

192 submission_version.save() 

193 submission = Submission.objects.get(pk=submission.pk) 

194 self.assertTrue(submission.is_submittable) 

195 SubmissionMainFile.objects.all().delete() 

196 submission = Submission.objects.get(pk=submission.pk) 

197 self.assertFalse(submission.is_submittable) 

198 

199 def test_submit(self): 

200 submission = SubmissionFactory.create(created_by=self.user_author, author_agreement=True) 

201 submission_version = SubmissionVersionFactory.create(submission=submission) 

202 submission = Submission.objects.get(pk=submission.pk) 

203 self.assertFalse(submission_version.submitted) 

204 self.assertIsNone(submission_version.date_submitted) 

205 self.assertEqual(submission.state, SubmissionState.OPENED.value) 

206 self.assertFalse(SubmissionLog.objects.filter(attached_to=submission).exists()) 

207 request = self.dummy_request() 

208 request.user = self.user_author 

209 date_last_activity = submission.date_last_activity # type:ignore 

210 self.assertIsNone(date_last_activity) 

211 

212 # Test submitting non-submittable submission 

213 with patch.object(Submission, "is_submittable", False): 

214 self.assertRaises(SubmissionStateError, submission.submit, self.user_author) 

215 

216 submission = Submission.objects.get(pk=submission.pk) 

217 self.assertEqual(submission.state, SubmissionState.OPENED.value) 

218 submission_version: SubmissionVersion = submission.current_version # type:ignore 

219 self.assertFalse(submission_version.submitted) 

220 self.assertFalse(SubmissionLog.objects.filter(attached_to=submission).exists()) 

221 date_last_activity = submission.date_last_activity # type:ignore 

222 self.assertIsNone(date_last_activity) 

223 

224 # Test submiting a submittable OPENED submission 

225 with patch.object(Submission, "is_submittable", True): 

226 submission.submit(self.user_author) 

227 

228 submission = Submission.objects.get(pk=submission.pk) 

229 self.assertEqual(submission.state, SubmissionState.ON_REVIEW.value) 

230 submission_version: SubmissionVersion = submission.current_version # type:ignore 

231 self.assertTrue(submission_version.submitted) 

232 self.assertIsNotNone(submission_version.date_submitted) 

233 self.assertEqual(len(SubmissionLog.objects.filter(attached_to=submission).all()), 1) 

234 date_last_activity = submission.date_last_activity # type:ignore 

235 self.assertIsNotNone(date_last_activity) 

236 

237 # Test submiting a 2nd version of the REVISION_REQUESTED submission 

238 submission_version.review_open = False 

239 submission_version.save() 

240 submission_version_2 = SubmissionVersionFactory.create(submission=submission) 

241 self.assertFalse(submission_version_2.submitted) 

242 submission.state = SubmissionState.REVISION_REQUESTED.value 

243 submission = Submission.objects.get(pk=submission.pk) 

244 

245 with patch.object(Submission, "is_submittable", True): 

246 submission.submit(self.user_author) 

247 

248 submission = Submission.objects.get(pk=submission.pk) 

249 self.assertEqual(submission.state, SubmissionState.ON_REVIEW.value) 

250 current_version: SubmissionVersion = submission.current_version # type:ignore 

251 self.assertEqual(current_version.pk, submission_version_2.pk) 

252 self.assertTrue(current_version.submitted) 

253 self.assertEqual(len(SubmissionLog.objects.filter(attached_to=submission).all()), 2) 

254 date_last_activity_2 = submission.date_last_activity # type:ignore 

255 self.assertIsNotNone(date_last_activity_2) 

256 self.assertGreater(date_last_activity_2, date_last_activity) 

257 

258 def test_is_reviewable(self): 

259 submission = SubmissionFactory.create(state=SubmissionState.SUBMITTED.value) 

260 version = SubmissionVersionFactory.create( 

261 submission=submission, submitted=True, review_open=False 

262 ) 

263 # Not reviewable if the version has no main file 

264 submission = Submission.objects.get(pk=submission.pk) 

265 self.assertFalse(submission.is_reviewable) 

266 

267 SubmissionMainFile.objects.create(file=self.dummy_file, attached_to=version) 

268 submission = Submission.objects.get(pk=submission.pk) 

269 self.assertTrue(submission.is_reviewable) 

270 

271 # Test all submission state 

272 submission.state = SubmissionState.REVISION_SUBMITTED.value 

273 submission.save() 

274 self.assertTrue(submission.is_reviewable) 

275 

276 submission.state = SubmissionState.OPENED.value 

277 submission.save() 

278 self.assertFalse(submission.is_reviewable) 

279 

280 submission.state = SubmissionState.ON_REVIEW.value 

281 submission.save() 

282 self.assertFalse(submission.is_reviewable) 

283 

284 submission.state = SubmissionState.REVISION_REQUESTED.value 

285 submission.save() 

286 self.assertFalse(submission.is_reviewable) 

287 

288 submission.state = SubmissionState.ACCEPTED.value 

289 submission.save() 

290 self.assertFalse(submission.is_reviewable) 

291 

292 submission.state = SubmissionState.REJECTED.value 

293 submission.save() 

294 self.assertFalse(submission.is_reviewable) 

295 

296 # Test version parameters 

297 # Already on review 

298 submission.state = SubmissionState.SUBMITTED.value 

299 submission.save() 

300 self.assertTrue(submission.is_reviewable) 

301 version.review_open = True 

302 version.save() 

303 submission = Submission.objects.get(pk=submission.pk) 

304 self.assertFalse(submission.is_reviewable) 

305 

306 version.review_open = False 

307 version.save() 

308 submission = Submission.objects.get(pk=submission.pk) 

309 self.assertTrue(submission.is_reviewable) 

310 version.submitted = False 

311 version.save() 

312 submission = Submission.objects.get(pk=submission.pk) 

313 self.assertFalse(submission.is_reviewable) 

314 

315 def test_start_review_process(self): 

316 submission = SubmissionFactory.create( 

317 created_by=self.user_author, state=SubmissionState.SUBMITTED.value 

318 ) 

319 version = SubmissionVersionFactory.create( 

320 submission=submission, submitted=True, review_open=False 

321 ) 

322 SubmissionMainFile.objects.create(file=self.dummy_file, attached_to=version) 

323 

324 submission = Submission.objects.get(pk=submission.pk) 

325 

326 self.assertFalse(SubmissionLog.objects.filter(attached_to=submission).exists()) 

327 self.assertEqual(submission.state, SubmissionState.SUBMITTED.value) 

328 

329 request = self.dummy_request() 

330 request.user = self.user_editor 

331 with patch.object(Submission, "is_reviewable", False): 

332 self.assertRaises( 

333 SubmissionStateError, submission.start_review_process, self.user_editor 

334 ) 

335 

336 submission = Submission.objects.get(pk=submission.pk) 

337 version.refresh_from_db() 

338 self.assertFalse(SubmissionLog.objects.filter(attached_to=submission).exists()) 

339 self.assertEqual(submission.state, SubmissionState.SUBMITTED.value) 

340 self.assertFalse(version.review_open) 

341 

342 with patch.object(Submission, "is_reviewable", True): 

343 submission.start_review_process(self.user_editor) 

344 

345 submission = Submission.objects.get(pk=submission.pk) 

346 version.refresh_from_db() 

347 self.assertEqual(len(SubmissionLog.objects.filter(attached_to=submission)), 1) 

348 self.assertEqual(submission.state, SubmissionState.ON_REVIEW.value) 

349 self.assertTrue(version.review_open) 

350 

351 def test_apply_editorial_decision(self): 

352 submission = SubmissionFactory.create( 

353 created_by=self.user_author, state=SubmissionState.ON_REVIEW.value 

354 ) 

355 version = SubmissionVersionFactory.create( 

356 submission=submission, submitted=True, review_open=True 

357 ) 

358 SubmissionMainFile.objects.create(file=self.dummy_file, attached_to=version) 

359 

360 submission = Submission.objects.get(pk=submission.pk) 

361 log_count = 0 

362 self.assertEqual(len(SubmissionLog.objects.filter(attached_to=submission)), log_count) 

363 self.assertEqual(submission.state, SubmissionState.ON_REVIEW.value) 

364 self.assertTrue(version.review_open) 

365 

366 decision = EditorialDecisionFactory.create( 

367 version=version, value=SubmissionState.REVISION_REQUESTED.value 

368 ) 

369 request = self.dummy_request() 

370 request.user = self.user_editor 

371 with patch.object(Submission, "is_draft", True): 

372 self.assertRaises( 

373 SubmissionStateError, 

374 submission.apply_editorial_decision, 

375 decision, 

376 self.user_editor, 

377 ) 

378 

379 submission = Submission.objects.get(pk=submission.pk) 

380 version.refresh_from_db() 

381 self.assertEqual(len(SubmissionLog.objects.filter(attached_to=submission)), log_count) 

382 self.assertEqual(submission.state, SubmissionState.ON_REVIEW.value) 

383 self.assertTrue(version.review_open) 

384 

385 with patch.object(Submission, "is_draft", False): 

386 submission.apply_editorial_decision(decision, self.user_editor) 

387 

388 submission = Submission.objects.get(pk=submission.pk) 

389 version.refresh_from_db() 

390 log_count += 1 

391 self.assertEqual(len(SubmissionLog.objects.filter(attached_to=submission)), log_count) 

392 self.assertEqual(submission.state, SubmissionState.REVISION_REQUESTED.value) 

393 self.assertFalse(version.review_open) 

394 

395 # Test that an editorial decision is possible from all submisison state 

396 # SUBMITTED 

397 submission.state = SubmissionState.SUBMITTED.value 

398 submission.save() 

399 with patch.object(Submission, "is_draft", False): 

400 submission.apply_editorial_decision(decision, self.user_editor) 

401 

402 submission = Submission.objects.get(pk=submission.pk) 

403 version.refresh_from_db() 

404 log_count += 1 

405 self.assertEqual(len(SubmissionLog.objects.filter(attached_to=submission)), log_count) 

406 self.assertEqual(submission.state, SubmissionState.REVISION_REQUESTED.value) 

407 self.assertFalse(version.review_open) 

408 

409 # REVISION_REQUESTED 

410 submission.state = SubmissionState.REVISION_REQUESTED.value 

411 submission.save() 

412 decision.value = SubmissionState.ACCEPTED.value 

413 decision.save() 

414 with patch.object(Submission, "is_draft", False): 

415 submission.apply_editorial_decision(decision, self.user_editor) 

416 

417 submission = Submission.objects.get(pk=submission.pk) 

418 version.refresh_from_db() 

419 log_count += 1 

420 self.assertEqual(len(SubmissionLog.objects.filter(attached_to=submission)), log_count) 

421 self.assertEqual(submission.state, SubmissionState.ACCEPTED.value) 

422 self.assertFalse(version.review_open) 

423 

424 # REVISION_SUBMITTED 

425 submission.state = SubmissionState.REVISION_SUBMITTED.value 

426 submission.save() 

427 with patch.object(Submission, "is_draft", False): 

428 submission.apply_editorial_decision(decision, self.user_editor) 

429 

430 submission = Submission.objects.get(pk=submission.pk) 

431 version.refresh_from_db() 

432 log_count += 1 

433 self.assertEqual(len(SubmissionLog.objects.filter(attached_to=submission)), log_count) 

434 self.assertEqual(submission.state, SubmissionState.ACCEPTED.value) 

435 self.assertFalse(version.review_open) 

436 

437 # REJECTED 

438 submission.state = SubmissionState.REJECTED.value 

439 submission.save() 

440 with patch.object(Submission, "is_draft", False): 

441 submission.apply_editorial_decision(decision, self.user_editor) 

442 

443 submission = Submission.objects.get(pk=submission.pk) 

444 version.refresh_from_db() 

445 log_count += 1 

446 self.assertEqual(len(SubmissionLog.objects.filter(attached_to=submission)), log_count) 

447 self.assertEqual(submission.state, SubmissionState.ACCEPTED.value) 

448 self.assertFalse(version.review_open) 

449 

450 # ACCEPTED 

451 submission.state = SubmissionState.ACCEPTED.value 

452 submission.save() 

453 decision.value = SubmissionState.REJECTED.value 

454 decision.save() 

455 with patch.object(Submission, "is_draft", False): 

456 submission.apply_editorial_decision(decision, self.user_editor) 

457 

458 submission = Submission.objects.get(pk=submission.pk) 

459 version.refresh_from_db() 

460 log_count += 1 

461 self.assertEqual(len(SubmissionLog.objects.filter(attached_to=submission)), log_count) 

462 self.assertEqual(submission.state, SubmissionState.REJECTED.value) 

463 self.assertFalse(version.review_open)