Skip to content

Commit

Permalink
Add WdsContractVerificationTest
Browse files Browse the repository at this point in the history
This pairs with
DataBiosphere/terra-workspace-data-service#372
to establish a contract between WDS and WSM, initially focused on
`ReferencedGcpResourceController`.
  • Loading branch information
jladieu committed Oct 30, 2023
1 parent fe726d7 commit 23c1d25
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 47 deletions.
2 changes: 2 additions & 0 deletions scripts/write-config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ landingzone:
- [email protected]
- [email protected]
- [email protected]
pactbroker:
url: localhost:9292
EOF
else
Expand Down
118 changes: 73 additions & 45 deletions service/gradle.lockfile

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion service/gradle/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ dependencies {
testImplementation "org.testcontainers:postgresql:1.17.5"

// pact
testImplementation 'au.com.dius.pact.consumer:junit5:4.1.7'
testImplementation 'au.com.dius.pact.consumer:junit5:4.3.19'
testImplementation 'au.com.dius.pact.provider:junit5:4.3.19'
testImplementation 'au.com.dius.pact.provider:junit5spring:4.3.19'

annotationProcessor group: "com.google.auto.value", name: "auto-value", version: "1.7.4"
annotationProcessor group: "org.springframework.boot", name: "spring-boot-configuration-processor", version: "2.6.6"
Expand Down
17 changes: 17 additions & 0 deletions service/gradle/testing.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,20 @@ task pactTests(type: Test) {
environment.put('pact.provider.version', "$project.version")
}


// verify the provider side of pacts WSM has with other services
task verifyPacts(type: Test) {
useJUnitPlatform {
includeTags "pact-verification"
}

// TODO: do the correct/idiomatic thing here with config values, help needed for WSM specifics, desired behavior:
// default: use the dsp-eng-tools version
// allow optional local-override for local pactbroker
if (System.getenv().containsKey('PACT_BROKER_URL')) {
systemProperty 'pactbroker.url', System.getenv('PACT_BROKER_URL')
}
systemProperty 'pact.provider.version', System.getenv('PACT_PROVIDER_COMMIT')
systemProperty 'pact.provider.branch', System.getenv('PACT_PROVIDER_BRANCH')
systemProperty 'pact.verifier.publishResults', true
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import bio.terra.workspace.service.resource.controlled.ControlledResourceMetadataManager;
import bio.terra.workspace.service.resource.controlled.ControlledResourceService;
import bio.terra.workspace.service.resource.controlled.cloud.azure.AzureStorageAccessService;
import bio.terra.workspace.service.resource.referenced.ReferencedResourceService;
import bio.terra.workspace.service.spendprofile.SpendProfileId;
import bio.terra.workspace.service.workspace.WorkspaceService;
import com.azure.core.management.Region;
Expand All @@ -31,6 +32,7 @@ public class BaseAzureUnitTest extends BaseUnitTestMocks {
@MockBean private WorkspaceService mockWorkspaceService;
@MockBean private ControlledResourceMetadataManager mockControlledResourceMetadataManager;
@MockBean private ControlledResourceService mockControlledResourceService;
@MockBean private ReferencedResourceService mockReferencedResourceService;

public AzureStorageAccessService mockAzureStorageAccessService() {
return mockAzureStorageAccessService;
Expand All @@ -56,6 +58,10 @@ public ControlledResourceService getMockControlledResourceService() {
return mockControlledResourceService;
}

public ReferencedResourceService mockReferencedResourceService() {
return mockReferencedResourceService;
}

public void setupMockLandingZoneRegion(Region region) {
when(mockWorkspaceService().getWorkspace(any()))
.thenReturn(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package bio.terra.workspace.pact;

import static au.com.dius.pact.consumer.dsl.DslPart.UUID_REGEX;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
Expand Down Expand Up @@ -35,6 +34,8 @@
@Tag("pact-test")
@ExtendWith(PactConsumerTestExt.class)
public class TpsApiTest {
private static final String UUID_REGEX =
"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";

// Note: the header must match exactly so pact doesn't add it's own
// if "Content-type" is specified instead,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package bio.terra.workspace.pact;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

import au.com.dius.pact.provider.junit5.PactVerificationContext;
import au.com.dius.pact.provider.junitsupport.Provider;
import au.com.dius.pact.provider.junitsupport.State;
import au.com.dius.pact.provider.junitsupport.loader.PactBroker;
import au.com.dius.pact.provider.spring.junit5.MockMvcTestTarget;
import au.com.dius.pact.provider.spring.junit5.PactVerificationSpringProvider;
import bio.terra.landingzone.db.LandingZoneDao;
import bio.terra.landingzone.job.LandingZoneJobService;
import bio.terra.policy.model.TpsPaoConflict;
import bio.terra.policy.model.TpsPaoDescription;
import bio.terra.policy.model.TpsPaoUpdateResult;
import bio.terra.policy.model.TpsUpdateMode;
import bio.terra.workspace.common.BaseAzureUnitTest;
import bio.terra.workspace.service.iam.AuthenticatedUserRequest;
import bio.terra.workspace.service.job.JobService;
import bio.terra.workspace.service.resource.referenced.model.ReferencedResource;
import bio.terra.workspace.service.workspace.model.Workspace;
import bio.terra.workspace.service.workspace.model.WorkspaceStage;
import java.util.Map;
import java.util.UUID;
import org.broadinstitute.dsde.workbench.client.sam.model.UserStatusInfo;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.TransactionManager;

@Tag("pact-verification")
@Provider("workspacemanager")
@PactBroker()
public class WdsContractVerificationTest extends BaseAzureUnitTest {
@Autowired private MockMvc mockMvc;

// this block of @MockBeans sprouted up as a result of playing whackamole with failed dependency
// injection errors, mostly due to NPEs through by `DataSourceInitializer`
@MockBean
private LandingZoneDao
mockLandingZoneDao; // required for dependency chain up to azureConnectedTestUtils

@MockBean(name = "tlzTransactionManager")
private TransactionManager
mockTlzTransactionManager; // this bean fails on missing datasource otherwise

@MockBean private JobService mockJobService; // for JobService.initialize call on startup

@MockBean
private LandingZoneJobService mockLandingZoneJobService; // for LandingZone.main call on startup

@TestTemplate
@ExtendWith(PactVerificationSpringProvider.class)
void pactVerificationTestTemplate(PactVerificationContext context) {
context.verifyInteraction();
}

@BeforeEach
void setPactVerificationContext(PactVerificationContext context) {
context.setTarget(new MockMvcTestTarget(mockMvc));
}

@State({"authenticated with the given email"})
public void authenticatedAsAccount(Map<?, ?> parameters) throws InterruptedException {
var authenticatedEmail = parameters.get("email").toString();

// for recording the creator of the snapshot reference...
when(mockSamService()
.getUserEmailFromSamAndRethrowOnInterrupt(any(AuthenticatedUserRequest.class)))
.thenReturn(authenticatedEmail);

// for activity logging...
when(mockSamService().getUserStatusInfo(any(AuthenticatedUserRequest.class)))
.thenReturn(new UserStatusInfo().userEmail(authenticatedEmail).userSubjectId("subjectid"));
}

@State({"a workspace with the given id exists"})
public void workspaceIdExists(Map<?, ?> parameters) {
var workspaceUuid = UUID.fromString(parameters.get("id").toString());
when(mockWorkspaceService()
.validateWorkspaceAndAction(
any(AuthenticatedUserRequest.class), eq(workspaceUuid), /* action= */ anyString()))
.thenReturn(
Workspace.builder()
.workspaceId(workspaceUuid)
.workspaceStage(WorkspaceStage.RAWLS_WORKSPACE)
.userFacingId("workspaceid")
.createdByEmail("[email protected]")
.build());
}

@State({"policies allowing snapshot reference creation"})
public void allowSnapshotReferenceCreation() throws InterruptedException {
when(mockFeatureConfiguration().isTpsEnabled()).thenReturn(true);
var policyUpdateSuccessful = new TpsPaoUpdateResult().updateApplied(true);
when(mockWorkspaceService()
.linkPolicies(
/* workspaceUuid= */ any(UUID.class),
any(TpsPaoDescription.class),
any(TpsUpdateMode.class),
any(AuthenticatedUserRequest.class)))
.thenReturn(policyUpdateSuccessful);

when(mockReferencedResourceService()
.createReferenceResource(
any(ReferencedResource.class), any(AuthenticatedUserRequest.class)))
// just echo back the resource that the controller attempted to create to simulate success
.thenAnswer(invocation -> invocation.getArgument(0));
}

@State({"policies preventing snapshot reference creation"})
public void preventSnapshotReferenceCreation() throws InterruptedException {
when(mockFeatureConfiguration().isTpsEnabled()).thenReturn(true);
var policiesConflicting =
new TpsPaoUpdateResult()
.updateApplied(false)
.addConflictsItem(new TpsPaoConflict().name("some conflict"));
when(mockWorkspaceService()
.linkPolicies(
/* workspaceUuid= */ any(UUID.class),
any(TpsPaoDescription.class),
any(TpsUpdateMode.class),
any(AuthenticatedUserRequest.class)))
.thenReturn(policiesConflicting);
}
}
2 changes: 2 additions & 0 deletions service/src/test/resources/application-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,5 @@ landingzone:
landingzone-stairway-database:
poolMaxIdle: 1
uri: jdbc:${env.db.local-db}/landingzone_stairway_db
pactbroker:
url: https://pact-broker.dsp-eng-tools.broadinstitute.org:9292

0 comments on commit 23c1d25

Please sign in to comment.