TripleSpeeder
TripleSpeederβ€’2w ago

After moving from Clerk to Convex-Auth my convex tests fail

I'm pretty sure that everything in convex folder is fine, convex dev does not report any problems. But all my tests are now failing with this import error:
Error: Cannot find module '/home/michael/WebstormProjects/bikeproto/node_modules/@convex-dev/auth/dist/server/oauth/checks' imported from /home/michael/WebstormProjects/bikeproto/node_modules/@convex-dev/auth/dist/server/oauth/callback.js
Error: Cannot find module '/home/michael/WebstormProjects/bikeproto/node_modules/@convex-dev/auth/dist/server/oauth/checks' imported from /home/michael/WebstormProjects/bikeproto/node_modules/@convex-dev/auth/dist/server/oauth/callback.js
I'm probably missing something obvious? Already wiped my node_modules and reinstalled all dependencies, but no bueno.
28 Replies
Convex Bot
Convex Botβ€’2w ago
Thanks for posting in <#1088161997662724167>. Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets. - Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.) - Use search.convex.dev to search Docs, Stack, and Discord all at once. - Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI. - Avoid tagging staff unless specifically instructed. Thank you!
erquhart
erquhartβ€’2w ago
Is it possible you have two versions of @convex-dev/auth between your tests and your main project? Are you on a monorepo or do you have a single package.json for your whole repo
edproton
edprotonβ€’2w ago
I'm having the same problem haha WHAT in the same second.
TripleSpeeder
TripleSpeederOPβ€’2w ago
Its all in one normal repo with one package.json. I do have a dedicated Typescript config in the convex dir though... Could this be a problem? I think I don't really need it.
edproton
edprotonβ€’2w ago
πŸ˜‚
TripleSpeeder
TripleSpeederOPβ€’2w ago
Ha πŸ˜‚
edproton
edprotonβ€’2w ago
What, we're in the same repo πŸ˜‚ just kidding.
erquhart
erquhartβ€’2w ago
Convex requires that config, it's needed
edproton
edprotonβ€’2w ago
I will provide an example
// subjects.test.ts
import { convexTest } from "convex-test";
import { expect, test } from "vitest";
import { api } from "./_generated/api";
import schema from "./schema";

test("creating and retrieving subjects", async () => {
const t = convexTest(schema);

// Setup admin user
const asAdmin = t.withIdentity({
name: "Admin User",
role: "admin",
});

// Test creating a subject
const mathId = await asAdmin.mutation(api.subjects.createSubject, {
name: "Mathematics",
});

// Test getting all subjects
const allSubjects = await t.query(api.subjects.getAllSubjects);
expect(allSubjects).toHaveLength(1);
expect(allSubjects[0]).toMatchObject({
name: "Mathematics",
});

// Test getting subject by ID
const mathSubject = await t.query(api.subjects.getSubjectById, {
id: mathId,
});
expect(mathSubject).toMatchObject({
name: "Mathematics",
});
});

// subjects.ts
...
// Mutation to create a new subject
export const createSubject = mutation({
args: {
name: v.string(),
},
handler: async (ctx, args) => {
await requireRole(ctx, ROLES.ADMIN);

const subjectId = await ctx.db.insert("subjects", {
name: args.name,
});

return subjectId;
},
});

// schemas.ts
import { defineSchema, defineTable } from "convex/server";
import { authTables } from "@convex-dev/auth/server";
import { v } from "convex/values";
import { ROLES } from "./lib/permissions";

const schema = defineSchema({
...authTables,

/**
* Users table
*/
users: defineTable({
name: v.optional(v.string()),
...
role: v.optional(
v.union(
v.literal(ROLES.ADMIN),
v.literal(ROLES.TUTOR),
v.literal(ROLES.STUDENT)
)
),
})
.index("email", ["email"])
.index("phone", ["phone"])
.index("role", ["role"]),

/**
* Subjects table
*/
subjects: defineTable({
name: v.string(),
}),

/**
* Levels table
*/
levels: defineTable({
name: v.string(),
subjectId: v.id("subjects"),
}).index("by_subjectId", ["subjectId"]),
});

export default schema;

// permissions.ts
export const ROLES = {
STUDENT: "student",
TUTOR: "tutor",
ADMIN: "admin",
} as const;

export async function checkRole(
ctx: QueryCtx | MutationCtx,
requiredRole: Role
): Promise<boolean> {
const user = await ctx.auth.getUserIdentity();

// If the user doesn't exist or doesn't have a role, return false
if (!user || !user.roles) return false;

// Check if the user has the required role
return user.roles === requiredRole;
}

export async function requireRole(
ctx: QueryCtx | MutationCtx,
requiredRole: Role
): Promise<void> {
const hasRole = await checkRole(ctx, requiredRole);
if (!hasRole) {
throw new Error(`Access denied. Required role: ${requiredRole}`);
}
}
// subjects.test.ts
import { convexTest } from "convex-test";
import { expect, test } from "vitest";
import { api } from "./_generated/api";
import schema from "./schema";

test("creating and retrieving subjects", async () => {
const t = convexTest(schema);

// Setup admin user
const asAdmin = t.withIdentity({
name: "Admin User",
role: "admin",
});

// Test creating a subject
const mathId = await asAdmin.mutation(api.subjects.createSubject, {
name: "Mathematics",
});

// Test getting all subjects
const allSubjects = await t.query(api.subjects.getAllSubjects);
expect(allSubjects).toHaveLength(1);
expect(allSubjects[0]).toMatchObject({
name: "Mathematics",
});

// Test getting subject by ID
const mathSubject = await t.query(api.subjects.getSubjectById, {
id: mathId,
});
expect(mathSubject).toMatchObject({
name: "Mathematics",
});
});

// subjects.ts
...
// Mutation to create a new subject
export const createSubject = mutation({
args: {
name: v.string(),
},
handler: async (ctx, args) => {
await requireRole(ctx, ROLES.ADMIN);

const subjectId = await ctx.db.insert("subjects", {
name: args.name,
});

return subjectId;
},
});

// schemas.ts
import { defineSchema, defineTable } from "convex/server";
import { authTables } from "@convex-dev/auth/server";
import { v } from "convex/values";
import { ROLES } from "./lib/permissions";

const schema = defineSchema({
...authTables,

/**
* Users table
*/
users: defineTable({
name: v.optional(v.string()),
...
role: v.optional(
v.union(
v.literal(ROLES.ADMIN),
v.literal(ROLES.TUTOR),
v.literal(ROLES.STUDENT)
)
),
})
.index("email", ["email"])
.index("phone", ["phone"])
.index("role", ["role"]),

/**
* Subjects table
*/
subjects: defineTable({
name: v.string(),
}),

/**
* Levels table
*/
levels: defineTable({
name: v.string(),
subjectId: v.id("subjects"),
}).index("by_subjectId", ["subjectId"]),
});

export default schema;

// permissions.ts
export const ROLES = {
STUDENT: "student",
TUTOR: "tutor",
ADMIN: "admin",
} as const;

export async function checkRole(
ctx: QueryCtx | MutationCtx,
requiredRole: Role
): Promise<boolean> {
const user = await ctx.auth.getUserIdentity();

// If the user doesn't exist or doesn't have a role, return false
if (!user || !user.roles) return false;

// Check if the user has the required role
return user.roles === requiredRole;
}

export async function requireRole(
ctx: QueryCtx | MutationCtx,
requiredRole: Role
): Promise<void> {
const hasRole = await checkRole(ctx, requiredRole);
if (!hasRole) {
throw new Error(`Access denied. Required role: ${requiredRole}`);
}
}
erquhart
erquhartβ€’2w ago
Can you run npm ls @convex-dev/auth and see if there's more than one version resolving, and also see exactly what version you're on in the output
edproton
edprotonβ€’2w ago
// vitest.config.mts
import { defineConfig } from "vitest/config";

export default defineConfig({
test: {
environment: "edge-runtime",
server: { deps: { inline: ["convex-test"] } },
},
});
// vitest.config.mts
import { defineConfig } from "vitest/config";

export default defineConfig({
test: {
environment: "edge-runtime",
server: { deps: { inline: ["convex-test"] } },
},
});
edproton
edprotonβ€’2w ago
No description
erquhart
erquhartβ€’2w ago
@edproton can you share the exact error you're seeing (I understand it's similar or same as OP)
edproton
edprotonβ€’2w ago
No description
TripleSpeeder
TripleSpeederOPβ€’2w ago
I'm on my mobile, will do that when im home
edproton
edprotonβ€’2w ago
is the same, sorry for "stealing the thread" @TripleSpeeder
TripleSpeeder
TripleSpeederOPβ€’2w ago
No worries mate. Hope you guys sort it out until im Home πŸ˜‡
edproton
edprotonβ€’2w ago
Haha, alright, give it a shot, mateβ€”appreciate it!
erquhart
erquhartβ€’2w ago
awesome, I just reproduced
edproton
edprotonβ€’2w ago
Good. Let me know if you need any additional input or further details @erquhart I believe @ballingt is the maintainer of convex/auth, but I'm not entirely sure if the issue is with convex/auth or convex/test.
erquhart
erquhartβ€’2w ago
Files are all there, the only difference I see is the other imports specify the js extension and the checks import doesn't. But I built and linked it locally and everything worked fine. I even diffed the locally built dist to the published one, no difference. But maybe explicitly adding the extension for that import will fix πŸ€·β€β™‚οΈ https://github.com/get-convex/convex-auth/pull/141 was able to reproduce by publishing built package to npm, really not sure why the difference, but the fix makes the buggy import consistent with other imports.
ballingt
ballingtβ€’2w ago
Nice find, we'll get a release with that fix in soon What are the "moduleResolution": "node"/"bundler"/"node16" values everyone with this issue has in their tsconfig.json files? That makes sense, that import was recently added
erquhart
erquhartβ€’2w ago
I have "Bundler" for my root and convex configs
ballingt
ballingtβ€’2w ago
convex-test has some specific requirements, this makes sense or rather the way we suggest you run tests does I think
TripleSpeeder
TripleSpeederOPβ€’2w ago
I also have "bundler".
ballingt
ballingtβ€’7d ago
Just published @convex-dev/auth@0.0.77 with this fix.
edproton
edprotonβ€’7d ago
I have "bundler" too. Thanks @TripleSpeeder @erquhart and @ballingt
TripleSpeeder
TripleSpeederOPβ€’5d ago
Thank you @ballingt , works fine now. I really love the support here, always competent and super fast, no matter if it's weekend. You guys are πŸ‘