mirror of
https://github.com/bitwarden/server.git
synced 2024-11-21 12:05:42 +01:00
Load tests (#2946)
This commit is contained in:
parent
662ac7f059
commit
a1fb847233
58
perf/load/config.js
Normal file
58
perf/load/config.js
Normal file
@ -0,0 +1,58 @@
|
||||
import http from "k6/http";
|
||||
import { check, fail } from "k6";
|
||||
import { authenticate } from "./helpers/auth.js";
|
||||
|
||||
const IDENTITY_URL = __ENV.IDENTITY_URL;
|
||||
const API_URL = __ENV.API_URL;
|
||||
const CLIENT_ID = __ENV.CLIENT_ID;
|
||||
const AUTH_USERNAME = __ENV.AUTH_USER_EMAIL;
|
||||
const AUTH_PASSWORD = __ENV.AUTH_USER_PASSWORD_HASH;
|
||||
|
||||
export const options = {
|
||||
ext: {
|
||||
loadimpact: {
|
||||
projectID: 3639465,
|
||||
name: "Config",
|
||||
},
|
||||
},
|
||||
stages: [
|
||||
{ duration: "30s", target: 10 },
|
||||
{ duration: "1m", target: 20 },
|
||||
{ duration: "2m", target: 25 },
|
||||
{ duration: "30s", target: 0 },
|
||||
],
|
||||
thresholds: {
|
||||
http_req_failed: ["rate<0.01"],
|
||||
http_req_duration: ["p(95)<1000"],
|
||||
},
|
||||
};
|
||||
|
||||
export function setup() {
|
||||
return authenticate(IDENTITY_URL, CLIENT_ID, AUTH_USERNAME, AUTH_PASSWORD);
|
||||
}
|
||||
|
||||
export default function (data) {
|
||||
const params = {
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
Authorization: `Bearer ${data.access_token}`,
|
||||
"X-ClientId": CLIENT_ID,
|
||||
},
|
||||
tags: { name: "Config" },
|
||||
};
|
||||
|
||||
const res = http.get(`${API_URL}/config`, params);
|
||||
if (
|
||||
!check(res, {
|
||||
"config status is 200": (r) => r.status === 200,
|
||||
})
|
||||
) {
|
||||
fail("config status code was *not* 200");
|
||||
}
|
||||
|
||||
const json = res.json();
|
||||
|
||||
check(json, {
|
||||
"config version is available": (j) => j.version !== "",
|
||||
});
|
||||
}
|
131
perf/load/groups.js
Normal file
131
perf/load/groups.js
Normal file
@ -0,0 +1,131 @@
|
||||
import http from "k6/http";
|
||||
import { check, fail } from "k6";
|
||||
import { authenticate } from "./helpers/auth.js";
|
||||
import { uuidv4 } from "https://jslib.k6.io/k6-utils/1.4.0/index.js";
|
||||
|
||||
const IDENTITY_URL = __ENV.IDENTITY_URL;
|
||||
const API_URL = __ENV.API_URL;
|
||||
const CLIENT_ID = __ENV.CLIENT_ID;
|
||||
const AUTH_CLIENT_ID = __ENV.AUTH_CLIENT_ID;
|
||||
const AUTH_CLIENT_SECRET = __ENV.AUTH_CLIENT_SECRET;
|
||||
|
||||
export const options = {
|
||||
ext: {
|
||||
loadimpact: {
|
||||
projectID: 3639465,
|
||||
name: "Groups",
|
||||
},
|
||||
},
|
||||
stages: [
|
||||
{ duration: "30s", target: 10 },
|
||||
{ duration: "1m", target: 20 },
|
||||
{ duration: "2m", target: 25 },
|
||||
{ duration: "30s", target: 0 },
|
||||
],
|
||||
thresholds: {
|
||||
http_req_failed: ["rate<0.01"],
|
||||
http_req_duration: ["p(95)<1500"],
|
||||
},
|
||||
};
|
||||
|
||||
export function setup() {
|
||||
return authenticate(
|
||||
IDENTITY_URL,
|
||||
CLIENT_ID,
|
||||
null,
|
||||
null,
|
||||
AUTH_CLIENT_ID,
|
||||
AUTH_CLIENT_SECRET
|
||||
);
|
||||
}
|
||||
|
||||
export default function (data) {
|
||||
const params = {
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${data.access_token}`,
|
||||
"X-ClientId": CLIENT_ID,
|
||||
},
|
||||
tags: { name: "Groups" },
|
||||
};
|
||||
|
||||
let name = `Name ${uuidv4()}`;
|
||||
const createPayload = {
|
||||
name: name,
|
||||
accessAll: true,
|
||||
externalId: `External ${uuidv4()}`,
|
||||
};
|
||||
|
||||
const createRes = http.post(
|
||||
`${API_URL}/public/groups`,
|
||||
JSON.stringify(createPayload),
|
||||
params
|
||||
);
|
||||
if (
|
||||
!check(createRes, {
|
||||
"group create status is 200": (r) => r.status === 200,
|
||||
})
|
||||
) {
|
||||
fail("group create status code was *not* 200");
|
||||
}
|
||||
|
||||
const createJson = createRes.json();
|
||||
|
||||
if (
|
||||
!check(createJson, {
|
||||
"group create id is available": (j) => j.id !== "",
|
||||
})
|
||||
) {
|
||||
fail("group create id was *not* available");
|
||||
}
|
||||
|
||||
const id = createJson.id;
|
||||
const getRes = http.get(`${API_URL}/public/groups/${id}`, params);
|
||||
if (
|
||||
!check(getRes, {
|
||||
"group get status is 200": (r) => r.status === 200,
|
||||
})
|
||||
) {
|
||||
fail("group get status code was *not* 200");
|
||||
}
|
||||
|
||||
const getJson = getRes.json();
|
||||
|
||||
if (
|
||||
!check(getJson, {
|
||||
"group get name matches": (j) => j.name === name,
|
||||
})
|
||||
) {
|
||||
fail("group get name did *not* match");
|
||||
}
|
||||
|
||||
name = `Name ${uuidv4()}`;
|
||||
const updatePayload = {
|
||||
name: name,
|
||||
accessAll: createPayload.accessAll,
|
||||
externalId: createPayload.externalId,
|
||||
};
|
||||
|
||||
const updateRes = http.put(
|
||||
`${API_URL}/public/groups/${id}`,
|
||||
JSON.stringify(updatePayload),
|
||||
params
|
||||
);
|
||||
if (
|
||||
!check(updateRes, {
|
||||
"group update status is 200": (r) => r.status === 200,
|
||||
})
|
||||
) {
|
||||
fail("group update status code was *not* 200");
|
||||
}
|
||||
|
||||
const deleteRes = http.del(`${API_URL}/public/groups/${id}`, null, params);
|
||||
if (
|
||||
!check(deleteRes, {
|
||||
"group delete status is 200": (r) => r.status === 200,
|
||||
})
|
||||
) {
|
||||
fail("group delete status code was *not* 200");
|
||||
}
|
||||
}
|
73
perf/load/helpers/auth.js
Normal file
73
perf/load/helpers/auth.js
Normal file
@ -0,0 +1,73 @@
|
||||
import http from "k6/http";
|
||||
import { check, fail } from "k6";
|
||||
import encoding from "k6/encoding";
|
||||
|
||||
/**
|
||||
* Authenticate using OAuth against Bitwarden
|
||||
* @function
|
||||
* @param {string} identityUrl - Identity Server URL
|
||||
* @param {string} clientHeader - X-ClientId header value
|
||||
* @param {string} username - User email (password grant)
|
||||
* @param {string} password - User password (password grant)
|
||||
* @param {string} clientId - Client ID (client credentials grant)
|
||||
* @param {string} clientSecret - Client secret (client credentials grant)
|
||||
*/
|
||||
export function authenticate(
|
||||
identityUrl,
|
||||
clientHeader,
|
||||
username,
|
||||
password,
|
||||
clientId,
|
||||
clientSecret
|
||||
) {
|
||||
const url = `${identityUrl}/connect/token`;
|
||||
const params = {
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"X-ClientId": clientHeader,
|
||||
},
|
||||
tags: { name: "Login" },
|
||||
};
|
||||
const payload = {
|
||||
deviceIdentifier: "a455f262-3d24-4bcd-b178-39dcd67d5c3f",
|
||||
};
|
||||
|
||||
if (username !== null) {
|
||||
payload["scope"] = "api offline_access";
|
||||
payload["grant_type"] = "password";
|
||||
payload["client_id"] = "web";
|
||||
payload["deviceType"] = "9";
|
||||
payload["deviceName"] = "chrome";
|
||||
payload["username"] = username;
|
||||
payload["password"] = password;
|
||||
|
||||
params.headers["Auth-Email"] = encoding.b64encode(username);
|
||||
} else {
|
||||
payload["scope"] = "api.organization";
|
||||
payload["grant_type"] = "client_credentials";
|
||||
payload["client_id"] = clientId;
|
||||
payload["client_secret"] = clientSecret;
|
||||
}
|
||||
|
||||
const res = http.post(url, payload, params);
|
||||
|
||||
if (
|
||||
!check(res, {
|
||||
"login status is 200": (r) => r.status === 200,
|
||||
})
|
||||
) {
|
||||
fail("login status code was *not* 200");
|
||||
}
|
||||
|
||||
const json = res.json();
|
||||
|
||||
if (
|
||||
!check(json, {
|
||||
"login access token is available": (j) => j.access_token !== "",
|
||||
})
|
||||
) {
|
||||
fail("login access token was *not* available");
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
29
perf/load/login.js
Normal file
29
perf/load/login.js
Normal file
@ -0,0 +1,29 @@
|
||||
import { authenticate } from "./helpers/auth.js";
|
||||
|
||||
const IDENTITY_URL = __ENV.IDENTITY_URL;
|
||||
const CLIENT_ID = __ENV.CLIENT_ID;
|
||||
const AUTH_USERNAME = __ENV.AUTH_USER_EMAIL;
|
||||
const AUTH_PASSWORD = __ENV.AUTH_USER_PASSWORD_HASH;
|
||||
|
||||
export const options = {
|
||||
ext: {
|
||||
loadimpact: {
|
||||
projectID: 3639465,
|
||||
name: "Login",
|
||||
},
|
||||
},
|
||||
stages: [
|
||||
{ duration: "30s", target: 10 },
|
||||
{ duration: "1m", target: 20 },
|
||||
{ duration: "2m", target: 25 },
|
||||
{ duration: "30s", target: 0 },
|
||||
],
|
||||
thresholds: {
|
||||
http_req_failed: ["rate<0.01"],
|
||||
http_req_duration: ["p(95)<3000"],
|
||||
},
|
||||
};
|
||||
|
||||
export default function (data) {
|
||||
authenticate(IDENTITY_URL, CLIENT_ID, AUTH_USERNAME, AUTH_PASSWORD);
|
||||
}
|
Loading…
Reference in New Issue
Block a user