Four primitives
test()
Correctness checks
A single request with typed assertions and a structured trace.
contract()
API promises
An executable agreement that fails the build when behavior drifts.
workflow()
Multi-step scenarios
Named steps that pass state forward, each with its own evidence.
loadScenario()
Performance pressure
Turn a transaction into a repeatable load plan — same auth, same client.
Read like intent
import { test } from "@glubean/sdk";
test("list products", async (ctx) => {
const base = ctx.vars.require("BASE_URL");
const data = await ctx.http
.get(`${base}/products?limit=5`)
.json();
ctx.expect(data.products.length).toBe(5);
ctx.log(`Found ${data.total} products`);
});What you get
One request, typed assertions, a structured trace.
Configure once
configure() holds your base URL, headers, and secrets. Every test, contract, and load run reads from it — so tests read like intent, not infrastructure.
import { configure } from "@glubean/sdk";
export const { vars, http } = configure({
vars: { baseUrl: "BASE_URL" },
secrets: { apiKey: "API_KEY" },
http: {
prefixUrl: "BASE_URL",
headers: { Authorization: "Bearer {{API_KEY}}" },
},
});import { test } from "@glubean/sdk";
import { http } from "./configure.ts";
test("list products", async ({ expect }) => {
const data = await http.get("products?limit=5").json();
expect(data.products.length).toBe(5);
});
// configure() lives in one file, shared across every test.
// Tests read like intent, not infrastructure.Plugins, not forks
Add capabilities through the same configure() call, or define your own plugin — all of it keeps the one evidence model.
const { gql, chrome, pay } = configure({
plugins: {
gql: graphql({ endpoint: "{{GQL_URL}}" }),
chrome: browser({ launch: true }),
pay: payments({ baseUrlKey: "PAY_URL" }),
},
});import { definePlugin, configure, test } from "@glubean/sdk";
const payments = (opts: { baseUrlKey: string }) =>
definePlugin((runtime) => {
const client = runtime.http.extend({
prefixUrl: runtime.requireVar(opts.baseUrlKey),
headers: {
Authorization: `Bearer ${runtime.requireSecret("STRIPE_KEY")}`,
},
});
return {
charge: (amount: number) =>
client.post("charges", { json: { amount } }).json(),
refund: (id: string) =>
client.post(`charges/${id}/refund`).json(),
};
});
const { pay } = configure({
plugins: { pay: payments({ baseUrlKey: "PAYMENTS_URL" }) },
});
export const chargeAndRefund = test("charge-refund", async (ctx) => {
const charge = await pay.charge(2500);
ctx.expect(charge.status).toBe("succeeded");
const refund = await pay.refund(charge.id);
ctx.expect(refund.status).toBe("refunded");
});One evidence model
Every run keeps requests, responses, assertions, and traces as structure. Run locally and it stays on your machine; add --upload and the same evidence becomes dashboards, history, and analysis in Cloud.
Local
glubean run
stays on your machine
CI
ci run --upload
redacted, then sent
Cloud
app.glubean.com
dashboards + history
npm i @glubean/sdk