Skip to content

Commit

Permalink
[Issue #2473] Add new artillery (#2723)
Browse files Browse the repository at this point in the history
## Summary
Fixes #2473

### Time to review: __5 mins__

## Changes proposed
This adds a `processor.ts` file for running load tests runs get requests
for opp pages, search pages, static pages, and 404 pages.

To test locally, `artillery run -e local artillery-load-test.yml` . This
only runs a few requests locally but you can see the log messages and
check your local frontend server to see the results.
  • Loading branch information
acouch authored Nov 12, 2024
1 parent 306d118 commit c4aa020
Show file tree
Hide file tree
Showing 4 changed files with 337 additions and 16 deletions.
2 changes: 1 addition & 1 deletion frontend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ stop:
##################################################

load-test-local: # Load test the local environment at localhost:3000
artillery run artillery-load-test.yml
artillery run -e local artillery-load-test.yml

load-test-dev: # Load test the dev environment in aws
artillery run -e dev artillery-load-test.yml
Expand Down
54 changes: 39 additions & 15 deletions frontend/artillery-load-test.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
config:
target: http://localhost:3000
target: "http://127.0.0.1:3000"
tls:
rejectUnauthorized: false
http:
Expand All @@ -18,6 +18,20 @@ config:
maxVusers: 1000
name: Spike phase
environments:
local:
target: http://localhost:3000
phases:
- duration: 2
arrivalRate: 5
name: Warm up phase
- duration: 1
arrivalRate: 1
maxVusers: 1
name: Ramp up load
- duration: 1
arrivalRate: 1
maxVusers: 1
name: Spike phase
prod:
target: https://simpler.grants.gov
staging:
Expand All @@ -30,24 +44,34 @@ config:
ensure: {}
apdex: {}
metrics-by-endpoint: {}
apdex:
threshold: 100
processor: "./tests/artillery/processor.ts"

before:
flow:
- function: loadData

scenarios:
- name: root
- name: Opportunity Pages
beforeScenario: getOppId
flow:
- get:
url: "/"
expect:
- statusCode: 200
- name: health
url: "/opportunity/{{ id }}"
- log: "GET /opportunity/{{ id }}"
- name: 404 Pages
beforeScenario: get404
flow:
- get:
url: "/health"
expect:
- statusCode: 200
- name: hello
url: "/{{ route }}"
- log: "GET 404 page /{{ route }}"
- name: Static Pages
beforeScenario: getStatic
flow:
- get:
url: "/api/hello"
expect:
- statusCode: 200
url: "/{{ route }}"
- log: "GET static page /{{ route }}"
- name: Searches
beforeScenario: getSearchQuery
flow:
- get:
url: "/search?{{ query }}"
- log: "GET /search?{{ query }}"
168 changes: 168 additions & 0 deletions frontend/tests/artillery/params.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
{
"ids": {
"local": [
1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
],
"dev": [],
"prod": []
},
"queries": [
"test",
"grants",
"education",
"transportation",
"trauma",
"veterans"
],
"status": ["posted", "forecasted", "closed", "archived", "none"],
"agencies": [
"ARPAH",
"USAID",
"USAID-AFG",
"USAID",
"USAID-ARM",
"USAID-AZE",
"USAID-BAN",
"USAID-BEN",
"AC",
"DC",
"USDA",
"USDA-AMS",
"USDA-FNS1",
"DOC",
"DOC-DOCNOAAERA",
"DOC-EDA",
"DOC-NIST",
"DOD",
"DOD-AMC-ACCAPGN",
"DOD-AMC-ACCAPGD",
"DOD-AFRL-AFRLDET8",
"DOD-AFRL",
"DOD-USAFA",
"DOD-AFOSR",
"DOD-DARPA-BTO",
"ED",
"DOE",
"DOE-ARPAE",
"DOE-GFO",
"DOE-01",
"PAMS",
"PAMS-SC",
"HHS",
"HHS-ACF-FYSB",
"HHS-ACF",
"HHS-ACF-CB",
"DHS",
"DHS-DHS",
"DHS-OPO",
"DHS-USCG",
"HUD",
"USDOJ",
"USDOJ-OJP-BJA",
"USDOJ-OJP-COPS",
"DOL",
"DOL-ETA-ILAB",
"DOL-ETA-CEO",
"DOS",
"DOS-NEA-AC",
"DOS-DRL",
"DOS-ECA",
"DOI",
"DOI-BIA",
"DOI-BLM",
"DOI-BOR",
"USDOT",
"USDOT-ORP",
"USDOT-DO-SIPPRA",
"USDOT-GCR",
"DOT",
"DOT-DOT X-50",
"DOT-RITA",
"DOT-FAA-FAA ARG",
"DOT-FRA",
"DOT-FHWA",
"DOT-FTA",
"DOT-FAA-FAA COE-AJFE",
"DOT-FAA-FAA COE-FAA JAMS",
"DOT-FAA-FAA COE-TTHP",
"DOT-MA",
"DOT-NHTSA",
"VA",
"VA-CSHF",
"VA-HPGPDP",
"VA-LSV",
"VA-NVSP",
"VA-NCAC",
"VA-OMHSP",
"VA-SSVF",
"VA-NCA",
"VA-VLGP",
"EPA",
"IMLS",
"MCC",
"NASA",
"NASA-HQ",
"NASA-JSC",
"NASA-SFC",
"NASA",
"NARA",
"NEA",
"NEH",
"NSF",
"SSA"
],
"eligibility": [
"state_governments",
"county_governments",
"city_or_township_governments",
"special_district_governments",
"independent_school_districts",
"public_and_state_institutions_of_higher_education",
"private_institutions_of_higher_education",
"federally_recognized_native_american_tribal_governments",
"other_native_american_tribal_organizations",
"public_and_indian_housing_authorities",
"nonprofits_non_higher_education_with_501c3",
"nonprofits_non_higher_education_without_501c3",
"for_profit_organizations_other_than_small_businesses",
"small_businesses",
"other",
"unrestricted"
],
"funding": [
"cooperative_agreement",
"grant",
"procurement_contract",
"other"
],
"category": [
"recovery_act",
"agriculture",
"arts",
"business_and_commerce",
"community_development",
"consumer_protection",
"disaster_prevention_and_relief",
"education",
"employment_labor_and_training",
"energy",
"environment",
"food_and_nutrition",
"health",
"housing",
"humanities",
"information_and_statistics",
"infrastructure_investment_and_jobs_act",
"income_security_and_social_services",
"law_justice_and_legal_services",
"natural_resources",
"opportunity_zone_benefits",
"regional_development",
"science_technology_and_other_research_and_development",
"transportation",
"affordable_care_act",
"other"
],
"pages": ["", "process", "research", "subscribe", "subscribe/confirmation"]
}
129 changes: 129 additions & 0 deletions frontend/tests/artillery/processor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { readFile } from "fs/promises";
import { random } from "lodash";

type dataType = {
ids: {
[key: string]: Array<number>;
};
queries: Array<string>;
pages: Array<string>;
status: Array<string>;
agencies: Array<string>;
funding: Array<string>;
eligibility: Array<string>;
category: Array<string>;
};
type globalVars = {
$environment?: string;
};

type returnVars = {
id: number;
query: string;
route: string;
pages: string;
};

// eslint-disable-next-line @typescript-eslint/require-await
async function getOppId(context: { vars: dataType & returnVars & globalVars }) {
const env = context.vars.$environment as string;
context.vars.id =
context.vars.ids[env][random(context.vars.ids[env].length - 1)];
}

// eslint-disable-next-line @typescript-eslint/require-await
async function get404(context: { vars: returnVars }) {
const num = random(10);
// ~50% of 404s are opp pages.
if (num % 2 !== 0) {
context.vars.route = `opportunity/${num}`;
} else {
context.vars.route = randomString(num);
}
}

// eslint-disable-next-line @typescript-eslint/require-await
async function getStatic(context: { vars: returnVars }) {
context.vars.route =
context.vars.pages[random(context.vars.pages.length - 1)];
}

// eslint-disable-next-line @typescript-eslint/require-await
async function getSearchQuery(context: { vars: returnVars & dataType }) {
const { queries, status, agencies, eligibility, category } = context.vars;
const queryParam = `query=${queries[random(queries.length - 1)]}`;
const statusParam = `status=${status[random(status.length - 1)]}`;
const agencyParam = `agency=${agencies[random(agencies.length - 1)]}`;
const categoryParam = `category=${category[random(category.length - 1)]}`;
const eligibilityParam = `eligibility=${eligibility[random(eligibility.length - 1)]}`;
const pagerParam = `page=${random(5)}`;
// Most search params only include the queries, but smaller percent include
// filters. This allows configuring that percent for composing the query
const weights = [
{
percent: 50,
params: [queryParam, statusParam, pagerParam],
},
{
percent: 20,
params: [queryParam, statusParam, agencyParam],
},
{
percent: 20,
params: [queryParam, statusParam, agencyParam, categoryParam],
},
{
percent: 10,
params: [
queryParam,
statusParam,
agencyParam,
categoryParam,
eligibilityParam,
],
},
];
// Weight of percents out of 100
const hundred = random(100);
let total = 0;
const selected = weights.find((item) => {
total += item.percent;
if (hundred <= total) {
return true;
}
return false;
});
context.vars.query = selected?.params.join("&") as string;
}

async function loadData(context: { vars: dataType & globalVars }) {
// Dev and stage have the same data.
const env =
context.vars.$environment === "stage" ? "dev" : context.vars.$environment;
const envs = new Set(["local", "dev", "stage", "prod"]);
if (!env || !envs.has(env)) {
throw new Error(`env ${env ?? ""} does not exist in env list`);
}
const path = "./tests/artillery/params.json";
const file = await readFile(path, "utf8");
context.vars = JSON.parse(file) as dataType;
}

function randomString(length: number) {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = " ";
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(random(charactersLength)));
}
return result;
}

module.exports = {
loadData,
getOppId,
get404,
getStatic,
getSearchQuery,
};

0 comments on commit c4aa020

Please sign in to comment.