-
Notifications
You must be signed in to change notification settings - Fork 53
WireMock
WireMock is a service that allows us to record requests and responses to an external service and use these requests/responses (called "mappings" in WireMock lingo) to run tests. We currently use WireMock to tests some areas of the application that require Okta, such as the account creation flow or organization signup flow.
When running in record mode, WireMock acts as a proxy for all your requests to the third-party service and generates mappings based off the actual responses from the service. In later runnings of the test, WireMock will serve up the mappings instead of calling the third party service again. (This is particularly important with Okta because we're rate-limited and they don't have a test instance, so we can't just hit a real Okta endpoint every time we want to run a test.)
If you are writing a new Cypress test for a flow that uses an external service, you can generate new mappings by using Wiremock's recording feature. To use it, start Wiremock in standalone mode:
- Download wiremock. There should be a link at the very bottom of that page to download the wiremock jar file.
- Move the downloaded jar file to the root of the
prime-simplereport
directory - Start wiremock
- the jar filename may be different based on the version you are using
java -jar ./wiremock-standalone-3.2.0.jar --local-response-templating --port 8088 --root-dir ./wiremock/stubs/$1
- Then navigate to http://localhost:8088/__admin/recorder. That will open a page where you can point Wiremock at the external service you wish to record for. You'll then need to configure the app to point at Wiremock for the service, which will then proxy the requests to the real service through the recorder. For example, if you are generating mappings for Okta, you would set the following in
application-local.yml
:
okta:
client:
org-url: http://localhost:8088
-
Point the Wiremock recorder at https://hhs-prime.oktapreview.com. NOTE: since Okta requires https, you'll need to run the backend with the following environment variable
OKTA_TESTING_DISABLEHTTPSCHECK=true
(or serve Wiremock via https if you wanna get real fancy) -
Start the backend and frontend with
Okta local
configurations -
Then you can open up the app in your browser and take whatever actions you plan on doing in the test (or write the test first and actually run it). When you're done, click "Stop" on the Wiremock recording page. Your new mappings should then appear in
frontend/cypress/stubs
! You can then organize them into their own subfolder (see existing mappings for example).
You may need to make adjustments to make the mappings a little more broad so that they can cover more test cases, for example by using regex matching. See the doc pages on stubbing and request matching for more info on this.
LiveOktaAuthentication is tested with WireMock. If you make changes to that file, you'll likely need to update the WireMock recordings.
Here are the steps to do so:
- delete all the existing mappings.
- create a new user in Okta (following the test fields, so name is TEST INTEGRATION and email is [email protected]) curl request:
curl -v -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: SSWS {$actualOktaApiToken}" \
-d '{
"profile": {
"firstName": "TEST",
"lastName": "INTEGRATION",
"email": "[email protected]",
"login": "[email protected]",
"mobilePhone": "999-999-9999"
}
}' "https://hhs-prime.okta.com/api/v1/users?activate=false"
This should return a user id, you'll want to keep that.
- Next, you need to activate the user in Okta. (This returns the activation token; it's not the same as the programmatic activation we do in the test.) curl request:
curl -v -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: SSWS ${actualOktaAiToken}" \
"https://hhs-prime.okta.com/api/v1/users/${userIdFromStep3}/lifecycle/activate?sendEmail=false"
Get the activation token out of the response.
-
Replace the
userId
andactivationToken
inLiveOktaAuthenticationTest
with the new user id and activation token. -
Add a call to
Thread.sleep(45000)
in theresendActivationPasscode
success case test. (If there's not a 30 second pause between requests, Okta will throw an error) -
Replace the static "passcode" in
verifyActivationPasscodeSuccessful
with the following code:
JSONObject embeddedJson = new JSONObject(factor.getEmbedded());
String sharedSecret = embeddedJson.getJSONObject("activation").getString("sharedSecret");
long timeStep = embeddedJson.getJSONObject("activation").getLong("timeStep");
CodeGenerator codeGenerator = new DefaultCodeGenerator();
long millisSinceEpoch = Instant.now().toEpochMilli();
long counter = millisSinceEpoch / 1000 / timeStep;
String passcode = codeGenerator.generate(sharedSecret, counter);
You will likely also need to add the library to build.gradle
and add the imports back in. The gradle
build is:
testCompile 'dev.samstevens.totp:totp:1.7.1'
The imports are:
import dev.samstevens.totp.code.CodeGenerator;
import dev.samstevens.totp.code.DefaultCodeGenerator;
import java.time.Instant;
- Add the Wiremock recording lines back in.
@BeforeAll
public void startMockServer() throws IOException {
WireMock.startRecording("https://hhs-prime.okta.com");
}
@AfterAll
public void stopMockServer() throws IOException {
List<StubMapping> recordedMappings = WireMock.stopRecording().getStubMappings();
}
The Wiremock imports are as follows:
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
import com.github.tomakehurst.wiremock.client.WireMock;
- Add the actual okta token to
application-default.yaml
- Run the test! This should pass and generate all the recordings. (There might be a failure around removing/deactivating the user in Okta, you can ignore this. It'll present as some kind of
executionException
, and it happens because there's no response body for the final user deletion call and WireMock doesn't know how to handle it.) - Search in the
/mappings
folder for the request/response for verifying an activation passcode. Find the success case, and copy/paste that passcode toverifyActivationPasscodeSuccessful
(replacing the CodeGenerator from step 7.) - Remove the Wiremock recording code, remove the totp import from
build.gradle
, and remove the call toThread.sleep
. - If user deactivation/deletion did fail, delete the user. This can either be done directly in the Okta admin console, or with the following curl requests: deactivate:
curl -v -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: SSWS ${actualOktaApiToken} \
"https://hhs-prime.okta.com/api/v1/users/${userId}/lifecycle/deactivate?sendEmail=false"
delete (you probably have to run this twice, once to soft-delete and once to hard-delete):
curl -v -X DELETE \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: SSWS ${actualOktaApiToken}" \
"https://hhs-prime.okta.com/api/v1/users/${userId}?sendEmail=false"
- Re-run the test and it should pass using the recordings!
- If you're feeling ambitious and like you have a couple hours to kill, you can categorize and rename the mappings (also possibly strip some of the unnecessary info, like headers in the response.) If you're not feeling ambitious and/or have better things to do, you can apologize to your reviewers for the massive file dump ¯_(ツ)_/¯
The reason for the nonsense with creating/activating/deleting a user on every run of the recording is that there's a test which verifies that a user's password has never been set before. If you just de-activate the user, their previous setPassword information will be kept, and the test will fail.
When using wiremock, the response will usually be given with what wiremock is attempting to match When the Cypress tests are running, you may not be able to see the wiremock logs because Cypress will restart wiremock each time. In order to see those logs, you must capture the output in the startWireMock method in frontend/cypress/plugins/index.js
let scriptOutput = "";
wm.stdout.setEncoding("utf8");
wm.stdout.on("data", function (data) {
scriptOutput += data.toString();
});
wm.stderr.setEncoding("utf8");
wm.stderr.on("data", function (data) {
scriptOutput += data.toString();
});
wm.on("close", function (code) {
console.log(scriptOutput);
});
- Getting Started
- [Setup] Docker and docker compose development
- [Setup] IntelliJ run configurations
- [Setup] Running DB outside of Docker (optional)
- [Setup] Running nginx locally (optional)
- [Setup] Running outside of docker
- Accessing and testing weird parts of the app on local dev
- Accessing patient experience in local dev
- API Testing with Insomnia
- Cypress
- How to run e2e locally for development
- E2E tests
- Database maintenance
- MailHog
- Running tests
- SendGrid
- Setting up okta
- Sonar
- Storybook and Chromatic
- Twilio
- User roles
- Wiremock
- CSV Uploader
- Log local DB queries
- Code review and PR conventions
- SimpleReport Style Guide
- How to Review and Test Pull Requests for Dependabot
- How to Review and Test Pull Requests with Terraform Changes
- SimpleReport Deployment Process
- Adding a Developer
- Removing a developer
- Non-deterministic test tracker
- Alert Response - When You Know What is Wrong
- What to Do When You Have No Idea What is Wrong
- Main Branch Status
- Maintenance Mode
- Swapping Slots
- Monitoring
- Container Debugging
- Debugging the ReportStream Uploader
- Renew Azure Service Principal Credentials
- Releasing Changelog Locks
- Muting Alerts
- Architectural Decision Records
- Backend Stack Overview
- Frontend Overview
- Cloud Architecture
- Cloud Environments
- Database ERD
- External IDs
- GraphQL Flow
- Hibernate Lazy fetching and nested models
- Identity Verification (Experian)
- Spring Profile Management
- SR Result bulk uploader device validation logic
- Test Metadata and how we store it
- TestOrder vs TestEvent
- ReportStream Integration
- Feature Flag Setup
- FHIR Resources
- FHIR Conversions
- Okta E2E Integration
- Deploy Application Action
- Slack notifications for support escalations
- Creating a New Environment Within a Resource Group
- How to Add and Use Environment Variables in Azure
- Web Application Firewall (WAF) Troubleshooting and Maintenance
- How to Review and Test Pull Requests with Terraform Changes