@aprovan/chat-backend 0.1.0-dev.4d82df8 → 0.1.0-dev.78c0b14

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.
@@ -1,164 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import { Hono } from "hono";
3
- import type { AppVariables } from "../../src/types";
4
- import type { CognitoAccessTokenPayload } from "aws-jwt-verify/jwt-model";
5
-
6
- vi.mock("@aws-sdk/client-dynamodb", () => ({
7
- DynamoDBClient: vi.fn(() => ({})),
8
- }));
9
-
10
- const mockSend = vi.fn();
11
- vi.mock("@aws-sdk/lib-dynamodb", () => ({
12
- DynamoDBDocumentClient: {
13
- from: vi.fn(() => ({ send: mockSend })),
14
- },
15
- QueryCommand: vi.fn((input) => input),
16
- GetCommand: vi.fn((input) => input),
17
- PutCommand: vi.fn((input) => input),
18
- BatchGetCommand: vi.fn((input) => input),
19
- }));
20
-
21
- const { workspacesRoute } = await import("../../src/routes/workspaces");
22
- const { resetMembershipCache } = await import("../../src/middleware/workspace");
23
- const { resetSessionCache } = await import("../../src/session");
24
-
25
- const MEMBERSHIPS_TABLE = "test-memberships";
26
- const WORKSPACE_TABLE = "test-workspaces";
27
- const SESSIONS_TABLE = "test-user-sessions";
28
-
29
- type MockCommand = { TableName?: string; RequestItems?: Record<string, unknown> };
30
-
31
- const fakeClaims = { sub: "user-ws-test" } as unknown as CognitoAccessTokenPayload;
32
-
33
- function buildApp() {
34
- const app = new Hono<{ Variables: AppVariables }>();
35
- app.use("/*", async (c, next) => {
36
- c.set("claims", fakeClaims);
37
- await next();
38
- });
39
- app.route("/workspaces", workspacesRoute);
40
- return app;
41
- }
42
-
43
- describe("GET /workspaces", () => {
44
- beforeEach(() => {
45
- vi.clearAllMocks();
46
- resetMembershipCache();
47
- resetSessionCache();
48
- process.env["MEMBERSHIPS_TABLE_NAME"] = MEMBERSHIPS_TABLE;
49
- process.env["WORKSPACE_TABLE_NAME"] = WORKSPACE_TABLE;
50
- process.env["USER_SESSIONS_TABLE_NAME"] = SESSIONS_TABLE;
51
- process.env["AWS_REGION"] = "us-east-1";
52
- });
53
-
54
- it("returns workspace list with active flag", async () => {
55
- mockSend.mockImplementation((cmd: MockCommand) => {
56
- if (cmd.TableName === SESSIONS_TABLE) {
57
- return Promise.resolve({ Item: { activeWorkspaceId: "ws-b" } });
58
- }
59
- if (cmd.TableName === MEMBERSHIPS_TABLE) {
60
- return Promise.resolve({ Items: [{ workspaceId: "ws-a" }, { workspaceId: "ws-b" }] });
61
- }
62
- if (cmd.RequestItems?.[WORKSPACE_TABLE]) {
63
- return Promise.resolve({
64
- Responses: {
65
- [WORKSPACE_TABLE]: [
66
- { workspaceId: "ws-a", name: "Alpha", plan: "free" },
67
- { workspaceId: "ws-b", name: "Beta", plan: "pro" },
68
- ],
69
- },
70
- });
71
- }
72
- return Promise.resolve({});
73
- });
74
-
75
- const app = buildApp();
76
- const res = await app.request("/workspaces");
77
- expect(res.status).toBe(200);
78
- const body = await res.json();
79
- expect(body.activeWorkspaceId).toBe("ws-b");
80
- const beta = body.workspaces.find((w: { workspaceId: string }) => w.workspaceId === "ws-b");
81
- expect(beta?.active).toBe(true);
82
- });
83
-
84
- it("falls back to first membership when no session preference", async () => {
85
- mockSend.mockImplementation((cmd: MockCommand) => {
86
- if (cmd.TableName === SESSIONS_TABLE) {
87
- return Promise.resolve({ Item: undefined });
88
- }
89
- if (cmd.TableName === MEMBERSHIPS_TABLE) {
90
- return Promise.resolve({ Items: [{ workspaceId: "ws-a" }] });
91
- }
92
- if (cmd.RequestItems?.[WORKSPACE_TABLE]) {
93
- return Promise.resolve({
94
- Responses: { [WORKSPACE_TABLE]: [{ workspaceId: "ws-a", name: "Alpha", plan: "free" }] },
95
- });
96
- }
97
- return Promise.resolve({});
98
- });
99
-
100
- const app = buildApp();
101
- const res = await app.request("/workspaces");
102
- const body = await res.json();
103
- expect(body.activeWorkspaceId).toBe("ws-a");
104
- expect(body.workspaces[0].active).toBe(true);
105
- });
106
- });
107
-
108
- describe("PUT /workspaces/active", () => {
109
- beforeEach(() => {
110
- vi.clearAllMocks();
111
- resetMembershipCache();
112
- resetSessionCache();
113
- process.env["MEMBERSHIPS_TABLE_NAME"] = MEMBERSHIPS_TABLE;
114
- process.env["WORKSPACE_TABLE_NAME"] = WORKSPACE_TABLE;
115
- process.env["USER_SESSIONS_TABLE_NAME"] = SESSIONS_TABLE;
116
- process.env["AWS_REGION"] = "us-east-1";
117
- });
118
-
119
- it("sets active workspace and returns it", async () => {
120
- mockSend.mockImplementation((cmd: MockCommand) => {
121
- if (cmd.TableName === MEMBERSHIPS_TABLE) {
122
- return Promise.resolve({ Items: [{ workspaceId: "ws-a" }, { workspaceId: "ws-b" }] });
123
- }
124
- return Promise.resolve({});
125
- });
126
-
127
- const app = buildApp();
128
- const res = await app.request("/workspaces/active", {
129
- method: "PUT",
130
- headers: { "Content-Type": "application/json" },
131
- body: JSON.stringify({ workspaceId: "ws-b" }),
132
- });
133
- expect(res.status).toBe(200);
134
- const body = await res.json();
135
- expect(body.activeWorkspaceId).toBe("ws-b");
136
- });
137
-
138
- it("returns 403 when workspace is not in user memberships", async () => {
139
- mockSend.mockImplementation((cmd: MockCommand) => {
140
- if (cmd.TableName === MEMBERSHIPS_TABLE) {
141
- return Promise.resolve({ Items: [{ workspaceId: "ws-a" }] });
142
- }
143
- return Promise.resolve({});
144
- });
145
-
146
- const app = buildApp();
147
- const res = await app.request("/workspaces/active", {
148
- method: "PUT",
149
- headers: { "Content-Type": "application/json" },
150
- body: JSON.stringify({ workspaceId: "ws-other" }),
151
- });
152
- expect(res.status).toBe(403);
153
- });
154
-
155
- it("returns 400 for invalid request body", async () => {
156
- const app = buildApp();
157
- const res = await app.request("/workspaces/active", {
158
- method: "PUT",
159
- headers: { "Content-Type": "application/json" },
160
- body: JSON.stringify({ workspaceId: "" }),
161
- });
162
- expect(res.status).toBe(400);
163
- });
164
- });
@@ -1,56 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
-
3
- vi.mock("@aws-sdk/client-dynamodb", () => ({
4
- DynamoDBClient: vi.fn(() => ({})),
5
- }));
6
-
7
- const mockSend = vi.fn();
8
- vi.mock("@aws-sdk/lib-dynamodb", () => ({
9
- DynamoDBDocumentClient: {
10
- from: vi.fn(() => ({ send: mockSend })),
11
- },
12
- GetCommand: vi.fn((input) => input),
13
- PutCommand: vi.fn((input) => input),
14
- }));
15
-
16
- const { getSessionWorkspaceId, setSessionWorkspaceId, resetSessionCache } =
17
- await import("../src/session");
18
-
19
- describe("session", () => {
20
- beforeEach(() => {
21
- vi.clearAllMocks();
22
- resetSessionCache();
23
- process.env["USER_SESSIONS_TABLE_NAME"] = "test-user-sessions";
24
- process.env["AWS_REGION"] = "us-east-1";
25
- });
26
-
27
- it("returns null when no session record exists", async () => {
28
- mockSend.mockResolvedValueOnce({ Item: undefined });
29
- const result = await getSessionWorkspaceId("user-abc");
30
- expect(result).toBeNull();
31
- });
32
-
33
- it("returns activeWorkspaceId from session record", async () => {
34
- mockSend.mockResolvedValueOnce({ Item: { userSub: "user-abc", activeWorkspaceId: "ws-xyz" } });
35
- const result = await getSessionWorkspaceId("user-abc");
36
- expect(result).toBe("ws-xyz");
37
- });
38
-
39
- it("caches the result and skips DDB on second call", async () => {
40
- mockSend.mockResolvedValueOnce({ Item: { activeWorkspaceId: "ws-cached" } });
41
- await getSessionWorkspaceId("user-cached");
42
- await getSessionWorkspaceId("user-cached");
43
- expect(mockSend).toHaveBeenCalledTimes(1);
44
- });
45
-
46
- it("setSessionWorkspaceId writes to DDB and updates cache", async () => {
47
- mockSend.mockResolvedValueOnce({});
48
- await setSessionWorkspaceId("user-set", "ws-new");
49
- expect(mockSend).toHaveBeenCalledTimes(1);
50
-
51
- // Second read should come from cache, not DDB
52
- const result = await getSessionWorkspaceId("user-set");
53
- expect(result).toBe("ws-new");
54
- expect(mockSend).toHaveBeenCalledTimes(1);
55
- });
56
- });