@checkstack/auth-backend 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,37 +3,38 @@ import { enrichUser } from "./user";
3
3
  import { User } from "better-auth/types";
4
4
 
5
5
  // Mock Drizzle DB
6
- const createMockDb = (data: { roles?: unknown[]; permissions?: unknown[] }) => {
7
- const mockDb: any = {
6
+ const createMockDb = (data: {
7
+ roles?: unknown[];
8
+ permissions?: unknown[];
9
+ teams?: unknown[];
10
+ }) => {
11
+ const mockDb: unknown = {
8
12
  select: mock(() => mockDb),
9
13
  from: mock(() => mockDb),
10
14
  innerJoin: mock(() => mockDb),
11
15
  where: mock(() => mockDb),
12
16
  };
13
17
 
14
- // Mock thenable for different chains
15
- // eslint-disable-next-line unicorn/no-thenable
16
- mockDb.then = (resolve: (arg0: unknown) => void) => {
17
- // Determine which call this is based on the 'from' call
18
- // const lastFrom =
19
- // mockDb.from.mock.calls[mockDb.from.mock.calls.length - 1][0];
20
-
21
- // We need to look at the schema name or some identifier.
22
- // Since we are mocking the schema as well, we can check equality.
23
- // However, for a simple mock, we can just alternate or use a counter.
24
-
25
- // In enrichUser:
26
- // 1. select from userRole (inner join role)
27
- // 2. select from rolePermission (inner join permission) -> for each role
28
-
29
- // Let's use a simpler approach: track call count for this specific mock instance
30
- if (!mockDb._callCount) mockDb._callCount = 0;
31
- mockDb._callCount++;
18
+ // Track call count for sequential responses
19
+ // Call order in enrichUser: 1=roles, 2+=permissions per role, final=teams
20
+ let callCount = 0;
21
+ const nonAdminRoles = (data.roles || []).filter(
22
+ (r) => (r as { roleId: string }).roleId !== "admin"
23
+ );
32
24
 
33
- if (mockDb._callCount === 1) {
25
+ // eslint-disable-next-line unicorn/no-thenable
26
+ (mockDb as { then: unknown }).then = (resolve: (arg0: unknown) => void) => {
27
+ callCount++;
28
+ if (callCount === 1) {
29
+ // First call: get roles
34
30
  return resolve(data.roles || []);
35
31
  }
36
- return resolve(data.permissions || []);
32
+ if (callCount <= 1 + nonAdminRoles.length && nonAdminRoles.length > 0) {
33
+ // Permission calls for each non-admin role
34
+ return resolve(data.permissions || []);
35
+ }
36
+ // Team memberships (final call)
37
+ return resolve(data.teams || []);
37
38
  };
38
39
 
39
40
  return mockDb;
@@ -52,48 +53,66 @@ describe("enrichUser", () => {
52
53
  it("should enrich user with admin role and wildcard permission", async () => {
53
54
  const mockDb = createMockDb({
54
55
  roles: [{ roleId: "admin" }],
56
+ teams: [{ teamId: "team-1" }],
55
57
  });
56
58
 
57
- const result = await enrichUser(baseUser, mockDb);
59
+ const result = await enrichUser(
60
+ baseUser,
61
+ mockDb as Parameters<typeof enrichUser>[1]
62
+ );
58
63
 
59
64
  expect(result.roles).toContain("admin");
60
65
  expect(result.permissions).toContain("*");
66
+ expect(result.teamIds).toEqual(["team-1"]);
61
67
  });
62
68
 
63
69
  it("should enrich user with custom roles and permissions", async () => {
64
70
  const mockDb = createMockDb({
65
- roles: [{ roleId: "editor" }, { roleId: "viewer" }],
66
- permissions: [{ permissionId: "blog.read" }],
71
+ roles: [{ roleId: "editor" }],
72
+ permissions: [{ permissionId: "blog.edit" }],
73
+ teams: [],
67
74
  });
68
75
 
69
- // Note: Our simple mock returns the same permissions for ALL roles if there are multiple roles.
70
- // In enrichUser, it loops through roles.
71
- // If we have 2 roles, enrichUser will call select from rolePermission twice.
72
- // Our mock needs to handle multiple calls if we want to be precise.
73
-
74
- let callCount = 0;
75
- // eslint-disable-next-line unicorn/no-thenable
76
- mockDb.then = (resolve: (arg0: unknown) => void) => {
77
- callCount++;
78
- if (callCount === 1) return resolve([{ roleId: "editor" }]);
79
- if (callCount === 2) return resolve([{ permissionId: "blog.edit" }]);
80
- return resolve([]);
81
- };
82
-
83
- const result = await enrichUser(baseUser, mockDb);
76
+ const result = await enrichUser(
77
+ baseUser,
78
+ mockDb as Parameters<typeof enrichUser>[1]
79
+ );
84
80
 
85
81
  expect(result.roles).toContain("editor");
86
82
  expect(result.permissions).toContain("blog.edit");
83
+ expect(result.teamIds).toEqual([]);
87
84
  });
88
85
 
89
86
  it("should handle user with no roles", async () => {
90
87
  const mockDb = createMockDb({
91
88
  roles: [],
89
+ teams: [],
92
90
  });
93
91
 
94
- const result = await enrichUser(baseUser, mockDb);
92
+ const result = await enrichUser(
93
+ baseUser,
94
+ mockDb as Parameters<typeof enrichUser>[1]
95
+ );
95
96
 
96
97
  expect(result.roles).toEqual([]);
97
98
  expect(result.permissions).toEqual([]);
99
+ expect(result.teamIds).toEqual([]);
100
+ });
101
+
102
+ it("should include multiple team memberships", async () => {
103
+ const mockDb = createMockDb({
104
+ roles: [{ roleId: "admin" }],
105
+ teams: [{ teamId: "team-1" }, { teamId: "team-2" }, { teamId: "team-3" }],
106
+ });
107
+
108
+ const result = await enrichUser(
109
+ baseUser,
110
+ mockDb as Parameters<typeof enrichUser>[1]
111
+ );
112
+
113
+ expect(result.teamIds).toHaveLength(3);
114
+ expect(result.teamIds).toContain("team-1");
115
+ expect(result.teamIds).toContain("team-2");
116
+ expect(result.teamIds).toContain("team-3");
98
117
  });
99
118
  });
package/src/utils/user.ts CHANGED
@@ -5,7 +5,7 @@ import type { RealUser } from "@checkstack/backend-api";
5
5
  import * as schema from "../schema";
6
6
 
7
7
  /**
8
- * Enriches a better-auth User with roles and permissions from the database.
8
+ * Enriches a better-auth User with roles, permissions, and team memberships from the database.
9
9
  * Returns a RealUser type for use in the RPC context.
10
10
  */
11
11
  export const enrichUser = async (
@@ -48,6 +48,13 @@ export const enrichUser = async (
48
48
  }
49
49
  }
50
50
 
51
+ // 3. Get Team memberships
52
+ const userTeams = await db
53
+ .select({ teamId: schema.userTeam.teamId })
54
+ .from(schema.userTeam)
55
+ .where(eq(schema.userTeam.userId, user.id));
56
+ const teamIds = userTeams.map((t) => t.teamId);
57
+
51
58
  return {
52
59
  // Spread user first to preserve additional properties
53
60
  ...user,
@@ -58,5 +65,6 @@ export const enrichUser = async (
58
65
  name: user.name,
59
66
  roles,
60
67
  permissions: [...permissions],
68
+ teamIds,
61
69
  };
62
70
  };