Skip to content

Commit 7e15348

Browse files
committed
Tests and Types (and improvements to context passing on tests helpers)
1 parent e0959ad commit 7e15348

5 files changed

Lines changed: 118 additions & 6 deletions

File tree

src/schema/user/mutations.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,9 @@ builder.mutationField("retoolToken", (t) =>
186186
const user = await ctx.DB.query.usersSchema.findFirst({
187187
where: (u, { eq, and }) =>
188188
and(
189-
eq(u.email, userEmail.trim().toLocaleLowerCase()),
189+
eq(u.email, userEmail),
190190
eq(u.isRetoolEnabled, true),
191+
eq(u.isEmailVerified, true),
191192
eq(u.isSuperAdmin, true),
192193
),
193194
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* eslint-disable */
2+
/* @ts-nocheck */
3+
/* prettier-ignore */
4+
/* This file is automatically generated using `npm run graphql:types` */
5+
import type * as Types from '../../../generated/types';
6+
7+
import type { JsonObject } from "type-fest";
8+
import gql from 'graphql-tag';
9+
export type TokensMutationVariables = Types.Exact<{
10+
input: Types.RetoolToken;
11+
}>;
12+
13+
14+
export type TokensMutation = { __typename?: 'Mutation', retoolToken: { __typename?: 'TokenRef', token: string } };
15+
16+
17+
export const Tokens = gql`
18+
mutation Tokens($input: retoolToken!) {
19+
retoolToken(input: $input) {
20+
token
21+
}
22+
}
23+
`;

src/schema/user/tests/token.gql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mutation Tokens($input: retoolToken!) {
2+
retoolToken(input: $input) {
3+
token
4+
}
5+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { faker } from "@faker-js/faker";
2+
import { decode, verify } from "@tsndr/cloudflare-worker-jwt";
3+
import { it, describe, assert } from "vitest";
4+
5+
import { USER } from "~/datasources/db/users";
6+
import { executeGraphqlOperation, insertUser } from "~/tests/fixtures";
7+
8+
import {
9+
Tokens,
10+
TokensMutation,
11+
TokensMutationVariables,
12+
} from "./token.generated";
13+
14+
describe("User", () => {
15+
it("Should create a token", async () => {
16+
const retoolToken = faker.string.alphanumeric(10);
17+
const encoder = faker.string.alphanumeric(10);
18+
const user1 = await insertUser({
19+
isRetoolEnabled: true,
20+
isSuperAdmin: true,
21+
isEmailVerified: true,
22+
});
23+
const response = await executeGraphqlOperation<
24+
TokensMutation,
25+
TokensMutationVariables
26+
>(
27+
{
28+
document: Tokens,
29+
variables: {
30+
input: {
31+
authToken: retoolToken,
32+
userEmail: user1.email,
33+
},
34+
},
35+
},
36+
{
37+
RETOOL_AUTHENTICATION_TOKEN: retoolToken,
38+
SUPABASE_JWT_ENCODER: encoder,
39+
},
40+
);
41+
42+
assert.equal(response.errors, undefined);
43+
44+
const token = response.data?.retoolToken?.token;
45+
46+
assert.exists(token);
47+
48+
if (!token) {
49+
throw new Error("Token not found");
50+
}
51+
52+
const verified = await verify(token, encoder);
53+
54+
assert.exists(verified);
55+
56+
assert.equal(verified, true);
57+
58+
const decodedToken = decode<{
59+
user_metadata: USER;
60+
}>(token);
61+
62+
const userTokenData = decodedToken?.payload?.user_metadata;
63+
64+
assert.equal(userTokenData?.email, user1.email);
65+
66+
assert.equal(userTokenData?.isSuperAdmin, true);
67+
68+
assert.equal(userTokenData?.isRetoolEnabled, true);
69+
70+
assert.equal(userTokenData?.isEmailVerified, true);
71+
72+
assert.equal(userTokenData?.id, user1.id);
73+
});
74+
});

src/tests/fixtures/index.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ import {
100100
import { defaultLogger } from "~/logging";
101101
import { schema } from "~/schema";
102102
import { getTestDB } from "~/tests/fixtures/databaseHelper";
103+
import { Context } from "~/types";
103104

104105
const insertUserRequest = insertUsersSchema.deepPartial();
105106

@@ -121,7 +122,10 @@ const CRUDDates = ({
121122
deletedAt: typeof deletedAt !== "undefined" ? deletedAt : faker.date.recent(),
122123
});
123124

124-
const createExecutor = (user?: Awaited<ReturnType<typeof insertUser>>) =>
125+
const createExecutor = (
126+
user?: Awaited<ReturnType<typeof insertUser>> | undefined,
127+
context?: Partial<Context>,
128+
) =>
125129
buildHTTPExecutor({
126130
fetch: createYoga<Env>({
127131
schema,
@@ -134,6 +138,7 @@ const createExecutor = (user?: Awaited<ReturnType<typeof insertUser>>) =>
134138
logger: defaultLogger,
135139
USER: user ? user : undefined,
136140
GET_STRIPE_CLIENT: () => null,
141+
...(context ?? {}),
137142
};
138143
},
139144
plugins: [authZEnvelopPlugin({ rules })],
@@ -145,8 +150,9 @@ export const executeGraphqlOperation = <
145150
TVariables extends Record<string, any> = Record<string, any>,
146151
>(
147152
params: ExecutionRequest<TVariables, unknown, unknown, undefined, unknown>,
153+
context?: Partial<Context>,
148154
): Promise<ExecutionResult<TResult>> => {
149-
const executor = createExecutor();
155+
const executor = createExecutor(undefined, context);
150156

151157
// @ts-expect-error This is ok. Executor returns a promise with they types passed
152158
return executor(params);
@@ -158,8 +164,9 @@ export const executeGraphqlOperationAsUser = <
158164
>(
159165
params: ExecutionRequest<TVariables, unknown, unknown, undefined, unknown>,
160166
user: Awaited<ReturnType<typeof insertUser>>,
167+
context?: Partial<Context>,
161168
): Promise<ExecutionResult<TResult>> => {
162-
const executor = createExecutor(user);
169+
const executor = createExecutor(user, context);
163170

164171
// @ts-expect-error This error is ok. Executor returns a promise with they types passed
165172
return executor(params);
@@ -171,13 +178,14 @@ export const executeGraphqlOperationAsSuperAdmin = async <
171178
>(
172179
params: ExecutionRequest<TVariables, unknown, unknown, undefined, unknown>,
173180
user?: Awaited<ReturnType<typeof insertUser>>,
181+
context?: Partial<Context>,
174182
): Promise<ExecutionResult<TResult>> => {
175183
if (user && !user.isSuperAdmin) {
176184
throw new Error("User passed is not a super admin");
177185
}
178186

179187
const superAdmin = user ?? (await insertUser({ isSuperAdmin: true }));
180-
const executor = createExecutor(superAdmin);
188+
const executor = createExecutor(superAdmin, context);
181189

182190
// @ts-expect-error This error is ok. Executor returns a promise with they types passed
183191
return executor(params);
@@ -234,10 +242,11 @@ export const insertUser = async (
234242
externalId: partialInput?.externalId ?? faker.string.uuid(),
235243
username: partialInput?.username ?? faker.internet.userName(),
236244
bio: partialInput?.bio ?? faker.lorem.paragraph(),
237-
email: partialInput?.email ?? faker.internet.email(),
245+
email: (partialInput?.email ?? faker.internet.email()).toLowerCase(),
238246
name: partialInput?.name,
239247
isSuperAdmin: partialInput?.isSuperAdmin,
240248
isEmailVerified: partialInput?.isEmailVerified,
249+
isRetoolEnabled: partialInput?.isRetoolEnabled,
241250
pronouns:
242251
partialInput?.pronouns ??
243252
faker.helpers.arrayElement(Object.values(PronounsEnum)),

0 commit comments

Comments
 (0)