|
18 | 18 |
|
19 | 19 | import org.apache.commons.lang3.StringUtils; |
20 | 20 | import org.apache.hc.core5.http.HttpStatus; |
| 21 | +import org.json.JSONObject; |
| 22 | +import org.junit.Assert; |
21 | 23 | import org.junit.experimental.categories.Category; |
| 24 | +import org.labkey.remoteapi.CommandException; |
| 25 | +import org.labkey.remoteapi.CommandResponse; |
| 26 | +import org.labkey.remoteapi.Connection; |
| 27 | +import org.labkey.remoteapi.SimplePostCommand; |
22 | 28 | import org.labkey.test.BaseWebDriverTest; |
23 | 29 | import org.labkey.test.Locator; |
24 | 30 | import org.labkey.test.Locators; |
|
36 | 42 | import org.labkey.test.util.LogMethod; |
37 | 43 | import org.labkey.test.util.LoggedParam; |
38 | 44 | import org.labkey.test.util.PasswordUtil; |
| 45 | +import org.labkey.test.util.PermissionsHelper; |
39 | 46 | import org.labkey.test.util.PortalHelper; |
40 | 47 | import org.labkey.test.util.StudyHelper; |
41 | 48 | import org.labkey.test.util.TextSearcher; |
|
51 | 58 | import java.util.HashSet; |
52 | 59 | import java.util.Hashtable; |
53 | 60 | import java.util.List; |
| 61 | +import java.util.Map; |
54 | 62 | import java.util.Set; |
55 | 63 |
|
56 | 64 | import static org.junit.Assert.assertEquals; |
|
59 | 67 | import static org.junit.Assert.assertTrue; |
60 | 68 | import static org.labkey.test.pages.study.specimen.ManageNotificationsPage.SpecimensAttachment; |
61 | 69 | import static org.labkey.test.util.DataRegionTable.DataRegion; |
| 70 | +import static org.labkey.test.util.PermissionsHelper.READER_ROLE; |
62 | 71 |
|
63 | 72 | @Category({Daily.class, Specimen.class}) |
64 | 73 | @BaseWebDriverTest.ClassTimeout(minutes = 20) |
@@ -124,11 +133,12 @@ protected void setupRequestabilityRules() |
124 | 133 |
|
125 | 134 | @Override |
126 | 135 | @LogMethod |
127 | | - protected void doVerifySteps() throws IOException |
| 136 | + protected void doVerifySteps() throws IOException, CommandException |
128 | 137 | { |
129 | 138 | verifyActorDetails(); |
130 | 139 | verifySpecimenEventsRedirect(); |
131 | 140 | createRequest(); |
| 141 | + createRequestWithApi(); |
132 | 142 | verifyViews(); |
133 | 143 | verifyAdditionalRequestFields(); |
134 | 144 | verifyNotificationEmails(); |
@@ -435,6 +445,110 @@ private void createRequest() |
435 | 445 | waitForElement(Locator.css(".labkey-message").withText("Complete")); |
436 | 446 | } |
437 | 447 |
|
| 448 | + @LogMethod |
| 449 | + private void createRequestWithApi() throws IOException, CommandException |
| 450 | + { |
| 451 | + // Create an empty specimen request as admin user |
| 452 | + goToSpecimenData(); |
| 453 | + click(Locator.tag("img").withAttributeContaining("alt", "[New Request Icon]")); |
| 454 | + selectOptionByText(Locator.name("destinationLocation"), DESTINATION_SITE); |
| 455 | + setFormElement(Locator.id("input0"), "Assay Plan"); |
| 456 | + setFormElement(Locator.id("input1"), "Shipping"); |
| 457 | + setFormElement(Locator.id("input3"), "Comments"); |
| 458 | + clickButton("Create and View Details"); |
| 459 | + int requestId = Integer.parseInt(getUrlParam("id")); |
| 460 | + |
| 461 | + // Create commands to add vials, remove vials, add specimens, and cancel the request |
| 462 | + JSONObject vialParameters = new JSONObject( |
| 463 | + Map.of( |
| 464 | + "requestId", requestId, |
| 465 | + "idType", "GlobalUniqueId", |
| 466 | + "vialIds", new String[]{"ABH00LT8-01"} |
| 467 | + ) |
| 468 | + ); |
| 469 | + SimplePostCommand addVialsCommand = new SimplePostCommand("specimen-api", "addVialsToRequest"); |
| 470 | + addVialsCommand.setJsonObject(vialParameters); |
| 471 | + SimplePostCommand removeVialsCommand = new SimplePostCommand("specimen-api", "removeVialsFromRequest"); |
| 472 | + removeVialsCommand.setJsonObject(vialParameters); |
| 473 | + SimplePostCommand addSpecimenCommand = new SimplePostCommand("specimen-api", "addSpecimensToRequest"); |
| 474 | + addSpecimenCommand.setJsonObject(new JSONObject( |
| 475 | + Map.of( |
| 476 | + "requestId", requestId, |
| 477 | + "specimenHashes", new String[]{"FakeSpecimenHashThatDoesntMatter"} |
| 478 | + ) |
| 479 | + )); |
| 480 | + SimplePostCommand cancelRequestCommand = new SimplePostCommand("specimen-api", "cancelRequest"); |
| 481 | + cancelRequestCommand.setJsonObject(new JSONObject( |
| 482 | + Map.of( |
| 483 | + "requestId", requestId |
| 484 | + ) |
| 485 | + )); |
| 486 | + |
| 487 | + // Assign "Specimen Requester" role to USER1 |
| 488 | + String containerPath = getCurrentContainerPath(); |
| 489 | + log(containerPath); |
| 490 | + ApiPermissionsHelper helper = new ApiPermissionsHelper(containerPath); |
| 491 | + helper.uncheckInheritedPermissions(); // Uses UI to un-inherit and save... |
| 492 | + clickButton("Cancel"); // ...so click button to return to specimen page |
| 493 | + helper.addMemberToRole(USER1, READER_ROLE, PermissionsHelper.MemberType.user, containerPath); |
| 494 | + helper.addMemberToRole(USER1, "Specimen Requester", PermissionsHelper.MemberType.user, containerPath); |
| 495 | + Connection conn = createDefaultConnection(); |
| 496 | + |
| 497 | + // Impersonate USER1. Specimen Requester role who doesn't own the request shouldn't be able to modify it. |
| 498 | + impersonate(USER1); |
| 499 | + Assert.assertThrows("Request " + requestId + " was not found or the current user does not have permissions to access it.", |
| 500 | + CommandException.class, |
| 501 | + () -> addVialsCommand.execute(conn, containerPath) |
| 502 | + ); |
| 503 | + Assert.assertThrows("Request " + requestId + " was not found or the current user does not have permissions to access it.", |
| 504 | + CommandException.class, |
| 505 | + () -> addSpecimenCommand.execute(conn, containerPath) |
| 506 | + ); |
| 507 | + Assert.assertThrows("Request " + requestId + " was not found or the current user does not have permissions to access it.", |
| 508 | + CommandException.class, |
| 509 | + () -> cancelRequestCommand.execute(conn, containerPath) |
| 510 | + ); |
| 511 | + stopImpersonating(false); |
| 512 | + |
| 513 | + // Verify that the owner can add a vial |
| 514 | + impersonateRoles(READER_ROLE, "Specimen Requester"); |
| 515 | + CommandResponse response = addVialsCommand.execute(conn, containerPath); |
| 516 | + assertEquals(HttpStatus.SC_OK, response.getStatusCode()); |
| 517 | + //noinspection unchecked |
| 518 | + assertEquals(1, ((List<String>)response.getParsedData().get("vials")).size()); |
| 519 | + stopImpersonating(false); |
| 520 | + |
| 521 | + // Attempt to remove the just-added vial as non-owner |
| 522 | + impersonate(USER1); |
| 523 | + Assert.assertThrows("Request " + requestId + " was not found or the current user does not have permissions to access it.", |
| 524 | + CommandException.class, |
| 525 | + () -> removeVialsCommand.execute(conn, containerPath) |
| 526 | + ); |
| 527 | + stopImpersonating(false); |
| 528 | + |
| 529 | + // Owner can remove vials and add via specimen hash |
| 530 | + impersonateRoles(READER_ROLE, "Specimen Requester"); |
| 531 | + response = removeVialsCommand.execute(conn, containerPath); |
| 532 | + assertEquals(HttpStatus.SC_OK, response.getStatusCode()); |
| 533 | + //noinspection unchecked |
| 534 | + assertEquals(0, ((List<String>)response.getParsedData().get("vials")).size()); |
| 535 | + response = addSpecimenCommand.execute(conn, containerPath); |
| 536 | + assertEquals(HttpStatus.SC_OK, response.getStatusCode()); |
| 537 | + stopImpersonating(false); |
| 538 | + |
| 539 | + // Now give USER1 the "Specimen Coordinator" role and verify they can modify the request, including cancel |
| 540 | + helper.addMemberToRole(USER1, "Specimen Coordinator", PermissionsHelper.MemberType.user, containerPath); |
| 541 | + impersonate(USER1); |
| 542 | + assertEquals(HttpStatus.SC_OK, addVialsCommand.execute(conn, containerPath).getStatusCode()); |
| 543 | + assertEquals(HttpStatus.SC_OK, removeVialsCommand.execute(conn, containerPath).getStatusCode()); |
| 544 | + assertEquals(HttpStatus.SC_OK, addSpecimenCommand.execute(conn, containerPath).getStatusCode()); |
| 545 | + assertEquals(HttpStatus.SC_OK, cancelRequestCommand.execute(conn, containerPath).getStatusCode()); |
| 546 | + stopImpersonating(false); |
| 547 | + |
| 548 | + // Restore permissions back to inherit |
| 549 | + helper.checkInheritedPermissions(); |
| 550 | + } |
| 551 | + |
438 | 552 | @LogMethod |
439 | 553 | private void verifyViews() |
440 | 554 | { |
@@ -584,8 +698,8 @@ private void verifyNotificationEmails() |
584 | 698 | assertTrue(emailMessages1.getFirst().getBody().contains(_specimen_McMichael)); |
585 | 699 | assertNotNull("No message found", emailMessages1); |
586 | 700 | String messageBody = emailMessages2.getFirst().getBody().replaceFirst("-*=_Part_\\d{3}_\\d*.\\d*\\n",""); |
587 | | - messageBody = messageBody.replaceAll("Content-Type: text\\/html; charset=UTF-8\n",""); |
588 | | - messageBody = messageBody.replaceAll("Content-Transfer-Encoding: 7bit\n", ""); |
| 701 | + messageBody = messageBody.replace("Content-Type: text/html; charset=UTF-8\n",""); |
| 702 | + messageBody = messageBody.replace("Content-Transfer-Encoding: 7bit\n", ""); |
589 | 703 | assertTrue("Notification was not as expected.\nExpected:\n" + notification + "\n\nActual:\n" + messageBody, messageBody.contains(notification)); |
590 | 704 |
|
591 | 705 | String attachment1 = getAttribute(Locator.linkWithText(ATTACHMENT1), "href"); |
|
0 commit comments