Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Issue #2473] Add new artillery #2723

Merged
merged 10 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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": []
Comment on lines +7 to +8
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

staging too please! I'll be indexing the load test numbers off staging rather than dev

},
"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,
};