convex-helpers
Version:
A collection of useful code to complement the official convex package.
103 lines (94 loc) • 3.2 kB
text/typescript
import { ConvexClient } from "convex/browser";
import type {
FunctionArgs,
FunctionReference,
FunctionReturnType,
UserIdentity,
} from "convex/server";
/**
* This is a helper for testing Convex functions against a locally running backend.
*
* An example of calling a function:
* ```
* const t = new ConvexTestingHelper();
* const result = await t.query(api.foo.bar, { arg1: "baz" })
* ```
*
* An example of calling a function with auth:
* ```
* const t = new ConvexTestingHelper();
* const identityA = t.newIdentity({ name: "Person A"})
* const result = await t.withIdentity(identityA).query(api.users.getProfile);
* ```
*/
export class ConvexTestingHelper {
private _nextSubjectId: number = 0;
public client: ConvexClient;
private _adminKey: string;
constructor(options: { adminKey?: string; backendUrl?: string } = {}) {
this.client = new ConvexClient(
options.backendUrl ?? "http://127.0.0.1:3210",
);
this._adminKey =
options.adminKey ??
// default admin key for local backends - from https://github.com/get-convex/convex-backend/blob/main/Justfile
"0135d8598650f8f5cb0f30c34ec2e2bb62793bc28717c8eb6fb577996d50be5f4281b59181095065c5d0f86a2c31ddbe9b597ec62b47ded69782cd";
}
newIdentity(
args: Partial<Omit<UserIdentity, "tokenIdentifier">>,
): Omit<UserIdentity, "tokenIdentifier"> {
const subject = `test subject ${this._nextSubjectId}`;
this._nextSubjectId += 1;
const issuer = "test issuer";
return {
...args,
subject,
issuer,
};
}
withIdentity(
identity: Omit<UserIdentity, "tokenIdentifier">,
): Pick<ConvexClient, "mutation" | "action" | "query"> {
return {
mutation: (functionReference, args) => {
(this.client as any).setAdminAuth(this._adminKey, identity);
return this.client.mutation(functionReference, args).finally(() => {
this.client.client.clearAuth();
});
},
action: (functionReference, args) => {
(this.client as any).setAdminAuth(this._adminKey, identity);
return this.client.action(functionReference, args).finally(() => {
this.client.client.clearAuth();
});
},
query: (functionReference, args) => {
(this.client as any).setAdminAuth(this._adminKey, identity);
return this.client.query(functionReference, args).finally(() => {
this.client.client.clearAuth();
});
},
};
}
async mutation<Mutation extends FunctionReference<"mutation">>(
mutation: Mutation,
args: FunctionArgs<Mutation>,
): Promise<Awaited<FunctionReturnType<Mutation>>> {
return this.client.mutation(mutation, args);
}
async query<Query extends FunctionReference<"query", "public">>(
query: Query,
args: FunctionArgs<Query>,
): Promise<Awaited<FunctionReturnType<Query>>> {
return this.client.query(query, args);
}
async action<Action extends FunctionReference<"action">>(
action: Action,
args: FunctionArgs<Action>,
): Promise<Awaited<FunctionReturnType<Action>>> {
return this.client.action(action, args);
}
async close(): Promise<void> {
return this.client.close();
}
}