Skip to content
Open
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
1 change: 1 addition & 0 deletions tests/load-testing/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/gatling
.idea
.gitlab-ci.yml
/src/test/resources/auth.json
17 changes: 15 additions & 2 deletions tests/load-testing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,19 @@ API load testing using gatling.
Before you start, ensure you have the following prerequisites installed:
- Java Development Kit (JDK) version 17.

## Setup Auth
```bash
cd auth_script
npm install
npm run generate-auth
```

## Manual Run

```bash
export API_URL=https://govtool.cardanoapi.io/api
export API_URL=https://z6b8d2f7a-zca4a4c45-gtw.z937eb260.rustrocks.fr
export METADATA_VALIDATION_API_URL=https://z6b8d2f7a-z2f6a992f-gtw.z937eb260.rustrocks.fr
export PDF_API_URL=https://zdae9891f-zf09d11da-gtw.z937eb260.rustrocks.fr
export PEAK_USERS=100
export RAMP_DURATION=40 # in seconds
export STRESS_DURATION=40 # in seconds
Expand All @@ -32,14 +41,18 @@ docker run \
-e RAMP_DURATION=40 \
-e PEAK_USERS=100 \
-e STRESS_DURATION=40 \
-e API_URL='https://govtool.cardanoapi.io/api'\
-e API_URL='https://z6b8d2f7a-zca4a4c45-gtw.z937eb260.rustrocks.fr' \
-e METADATA_VALIDATION_API_URL='https://z6b8d2f7a-z2f6a992f-gtw.z937eb260.rustrocks.fr' \
-e PDF_API_URL='https://zdae9891f-zf09d11da-gtw.z937eb260.rustrocks.fr' \
govtool/load-testing
```

## Environment Variables
Explain the environment variables used in the project and their purpose.

- API_URL: The URL of the API being tested.
- PDF_API_URL: The URL of the PDF API being tested.
- METADATA_API_URL: The URL of the METADATA_API being tested.
- PEAK_USERS: The number of users to be injected during the test for each scenario.
- RAMP_DURATION:The duration over which the user rate gradually increases.
- STRESS_DURATION: The duration over which the stress peak occurs.
34 changes: 34 additions & 0 deletions tests/load-testing/auth_script/generate_auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { writeFile } from "fs";
import { Cip30ShelleyWallet, ShelleyWallet } from "libcardano-wallet";
import { AuthResponse } from "./lib/types";
import { generateWallets } from "./lib/helpers/generateWallets";
import { authenticate } from "./lib/helpers/auth";
import { loadCrypto } from "libcardano";

async function saveAuths(wallets: ShelleyWallet[]): Promise<void> {

const jsonWallets: AuthResponse[] = [];
for (let i = 0; i < wallets.length; i++) {
const cip30 = new Cip30ShelleyWallet({} as any,{} as any,wallets[i],0)

const stakeAddress = wallets[i].stakeAddrechBech32(0);

let authResponse = await authenticate(cip30);
if (authResponse) {
jsonWallets.push({ ...authResponse, stakeAddress });
}
}
const jsonString = JSON.stringify(jsonWallets, null, 2);
writeFile("../src/test/resources/auth.json", jsonString, "utf-8", (err) => {
if (err) {
throw new Error("Failed to write auth into file");
}
});
}

setTimeout(async () => {
await loadCrypto()
console.log("crypto loaded")
const wallets = await generateWallets();
await saveAuths(wallets);
},3000)
28 changes: 28 additions & 0 deletions tests/load-testing/auth_script/lib/_mock/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { faker } from "@faker-js/faker";

export const valid = {
username: () => {
let timeStamp = Date.now();
let username = `${faker.internet.displayName().toLowerCase()}_${timeStamp}`;

// Remove any invalid characters
username = username.replace(/[^a-z0-9._]/g, "");

// Ensure the username is between 1 and 30 characters
if (username.length > 30) {
username = username.substring(0, 30);
}

// Ensure the first character is not a period or underscore
if (username.startsWith(".") || username.startsWith("_")) {
username = "a" + username.substring(1);
}

// Ensure the username is not empty after the transformations
if (username.length === 0) {
username = "user" + faker.number.int({ min: 1, max: 9999 });
}

return username;
}
};
74 changes: 74 additions & 0 deletions tests/load-testing/auth_script/lib/helpers/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { bech32 } from "libcardano";
import { valid } from "../_mock/index";
import { AuthResponse } from "lib/types";
import { Cip30, Cip30Provider, ShelleyWallet } from "libcardano-wallet";
import { Cip8SignResult } from "libcardano/cardano/cip8";

async function addUsername(jsonResponse: AuthResponse) {
const username = valid.username();
await fetch(process.env.PDF_API_URL + "/users/edit", {
method: "PUT",
body: JSON.stringify({
govtoolUsername: username,
}),
headers: {
"content-type": "application/json",
Authorization: "Bearer " + jsonResponse.jwt,
},
});
}

export async function authenticate(
wallet: Cip30Provider,
max_retries: number=2
) {
const stakeAddress=(await wallet.getRewardAddresses())[0]
const stakeAddressHex = stakeAddress.toBytes().toString('hex')
const challengeResponse=await fetch( process.env.PDF_API_URL +"/api/auth/challenge?identifier="+stakeAddressHex, {
"method": "GET",
"mode": "cors",
});
const challengeJson: {message: string} = await challengeResponse.json()
console.log("Challenge",stakeAddressHex,challengeJson)
const signature = await wallet.signData(stakeAddressHex,Buffer.from(challengeJson.message).toString('hex'))
const signatureJson: Cip8SignResult & {expectedSignedMessage: string} = {...signature.toCip8Json(),expectedSignedMessage:challengeJson.message}

const signDataResponse = await fetch(
process.env.PDF_API_URL + "/api/auth/local",
{
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
identifier: stakeAddressHex,
signedMessage: signatureJson,
}),
}
);

if (signDataResponse.status == 200) {
const jsonResponse = await signDataResponse.json();
await addUsername(jsonResponse);
return jsonResponse;
}
if (signDataResponse.status === 429) {
let remainingRateLimitTime =
parseInt(signDataResponse.headers.get("x-ratelimit-reset")) -
Math.floor(new Date().getTime() / 1000);

while (remainingRateLimitTime > 0) {
await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second
remainingRateLimitTime =
parseInt(signDataResponse.headers.get("x-ratelimit-reset")) -
Math.floor(new Date().getTime() / 1000);
}

console.log("Retrying now...");
return await authenticate(wallet,max_retries-1);
}else{
console.log("Authentication failed. Status:", signDataResponse.status);
console.log(signDataResponse.url,signDataResponse.url)
console.log("Response body:", await signDataResponse.text());
}
}
19 changes: 19 additions & 0 deletions tests/load-testing/auth_script/lib/helpers/generateWallets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { HdWallet } from "libcardano-wallet";

function createRange(start: number, end: number) {
return Array.from({ length: end - start }, (_, i) => start + i);
}

const getAccount = async (index: number, wallet: HdWallet) => {
const nthWallet = await wallet.getAccount(index);
const singleAddrWallet = await nthWallet.singleAddressWallet();
return singleAddrWallet;
};

export async function generateWallets() {
const PEAK_USERS = parseInt(process.env.PEAK_USERS || "100");
const wallet = await HdWallet.fromMnemonicString(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"
);
return await Promise.all(createRange(0, PEAK_USERS).map(index => getAccount(index, wallet)));
}
17 changes: 17 additions & 0 deletions tests/load-testing/auth_script/lib/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export interface AuthResponse {
jwt: string;
user: User;
stakeAddress?: string;
}

interface User {
id: number;
username: string;
email: string;
provider: string;
confirmed: boolean;
blocked: boolean;
govtool_username: string | null;
createdAt: string;
updatedAt: string;
}
Loading