@gethmy/mcp 2.4.7 → 2.5.1

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,285 +0,0 @@
1
- /**
2
- * Unit tests for graph-expansion.ts (autoExpandGraph).
3
- *
4
- * Run with: bun test packages/mcp-server/src/__tests__/graph-expansion.test.ts
5
- */
6
-
7
- import { describe, expect, mock, test } from "bun:test";
8
- import { autoExpandGraph } from "../graph-expansion.js";
9
-
10
- // ---- Mock helpers ----
11
-
12
- interface MockEntity {
13
- id: string;
14
- confidence?: number;
15
- }
16
-
17
- function makeMockClient(
18
- entities: MockEntity[] = [],
19
- relationError?: { status?: number } | Error,
20
- ) {
21
- const createdRelations: Array<Record<string, unknown>> = [];
22
-
23
- return {
24
- createdRelations,
25
- searchMemoryEntities: mock(async () => ({
26
- entities,
27
- count: entities.length,
28
- })),
29
- createMemoryRelation: mock(async (data: Record<string, unknown>) => {
30
- if (relationError) throw relationError;
31
- createdRelations.push(data);
32
- return { relation: { id: "rel-1", ...data } };
33
- }),
34
- };
35
- }
36
-
37
- // ---- Tests ----
38
-
39
- describe("autoExpandGraph", () => {
40
- describe("basic relation creation", () => {
41
- test("creates relations for each matching entity", async () => {
42
- const client = makeMockClient([
43
- { id: "other-1", confidence: 0.9 },
44
- { id: "other-2", confidence: 0.8 },
45
- ]);
46
-
47
- const result = await autoExpandGraph(
48
- client as never,
49
- "new-entity",
50
- "React hooks guide",
51
- "How to use useState and useEffect effectively.",
52
- ["react"],
53
- "ws-1",
54
- );
55
-
56
- expect(result.relationsCreated).toBe(2);
57
- expect(client.createdRelations).toHaveLength(2);
58
- expect(client.createdRelations[0]).toMatchObject({
59
- source_id: "new-entity",
60
- target_id: "other-1",
61
- relation_type: "relates_to",
62
- confidence: 0.6,
63
- });
64
- });
65
-
66
- test("excludes self from relations", async () => {
67
- const client = makeMockClient([
68
- { id: "new-entity", confidence: 1.0 }, // same as entityId
69
- { id: "other-1", confidence: 0.9 },
70
- ]);
71
-
72
- const result = await autoExpandGraph(
73
- client as never,
74
- "new-entity",
75
- "Title",
76
- "Content",
77
- [],
78
- "ws-1",
79
- );
80
-
81
- expect(result.relationsCreated).toBe(1);
82
- expect(client.createdRelations[0].target_id).toBe("other-1");
83
- });
84
-
85
- test("excludes entities with confidence below 0.4", async () => {
86
- const client = makeMockClient([
87
- { id: "low-conf", confidence: 0.3 },
88
- { id: "ok-conf", confidence: 0.4 },
89
- { id: "no-conf" }, // undefined confidence → treated as 1
90
- ]);
91
-
92
- const result = await autoExpandGraph(
93
- client as never,
94
- "new-entity",
95
- "Title",
96
- "Content",
97
- [],
98
- "ws-1",
99
- );
100
-
101
- expect(result.relationsCreated).toBe(2);
102
- const targetIds = client.createdRelations.map((r) => r.target_id);
103
- expect(targetIds).toContain("ok-conf");
104
- expect(targetIds).toContain("no-conf");
105
- expect(targetIds).not.toContain("low-conf");
106
- });
107
-
108
- test("respects maxRelations cap", async () => {
109
- const entities: MockEntity[] = Array.from({ length: 10 }, (_, i) => ({
110
- id: `entity-${i}`,
111
- confidence: 0.9,
112
- }));
113
- const client = makeMockClient(entities);
114
-
115
- const result = await autoExpandGraph(
116
- client as never,
117
- "new-entity",
118
- "Title",
119
- "Content",
120
- [],
121
- "ws-1",
122
- undefined,
123
- 3, // maxRelations = 3
124
- );
125
-
126
- expect(result.relationsCreated).toBe(3);
127
- expect(client.createdRelations).toHaveLength(3);
128
- });
129
-
130
- test("returns 0 when no entities returned by search", async () => {
131
- const client = makeMockClient([]);
132
-
133
- const result = await autoExpandGraph(
134
- client as never,
135
- "new-entity",
136
- "Unique topic",
137
- "Nothing similar exists yet.",
138
- [],
139
- "ws-1",
140
- );
141
-
142
- expect(result.relationsCreated).toBe(0);
143
- expect(client.createMemoryRelation).not.toHaveBeenCalled();
144
- });
145
- });
146
-
147
- describe("search query construction", () => {
148
- test("builds query from title + content snippet", async () => {
149
- const client = makeMockClient([]);
150
-
151
- await autoExpandGraph(
152
- client as never,
153
- "eid",
154
- "Authentication guide",
155
- "How to implement JWT-based authentication in Next.js applications using Supabase.",
156
- ["auth"],
157
- "ws-1",
158
- );
159
-
160
- const [calledWorkspaceId, calledQuery] = client.searchMemoryEntities.mock
161
- .calls[0] as [string, string];
162
- expect(calledWorkspaceId).toBe("ws-1");
163
- expect(calledQuery).toContain("Authentication guide");
164
- expect(calledQuery).toContain("JWT");
165
- });
166
-
167
- test("content snippet is limited to 200 characters", async () => {
168
- const longContent = "X".repeat(500);
169
- const client = makeMockClient([]);
170
-
171
- await autoExpandGraph(
172
- client as never,
173
- "eid",
174
- "Title",
175
- longContent,
176
- [],
177
- "ws-1",
178
- );
179
-
180
- const [, calledQuery] = client.searchMemoryEntities.mock.calls[0] as [
181
- string,
182
- string,
183
- ];
184
- // Query = "Title " + 200 chars max from content
185
- expect(calledQuery.length).toBeLessThanOrEqual("Title".length + 1 + 200);
186
- });
187
-
188
- test("passes projectId to search", async () => {
189
- const client = makeMockClient([]);
190
-
191
- await autoExpandGraph(
192
- client as never,
193
- "eid",
194
- "Title",
195
- "Content",
196
- [],
197
- "ws-1",
198
- "proj-42",
199
- );
200
-
201
- const [, , opts] = client.searchMemoryEntities.mock.calls[0] as [
202
- string,
203
- string,
204
- { project_id?: string },
205
- ];
206
- expect(opts?.project_id).toBe("proj-42");
207
- });
208
- });
209
-
210
- describe("error handling", () => {
211
- test("returns 0 when searchMemoryEntities throws", async () => {
212
- const client = {
213
- searchMemoryEntities: mock(async () => {
214
- throw new Error("Network error");
215
- }),
216
- createMemoryRelation: mock(async () => ({ relation: {} })),
217
- };
218
-
219
- const result = await autoExpandGraph(
220
- client as never,
221
- "eid",
222
- "Title",
223
- "Content",
224
- [],
225
- "ws-1",
226
- );
227
-
228
- expect(result.relationsCreated).toBe(0);
229
- expect(client.createMemoryRelation).not.toHaveBeenCalled();
230
- });
231
-
232
- test("skips individual relation failures, counts successes", async () => {
233
- let callCount = 0;
234
- const client = {
235
- searchMemoryEntities: mock(async () => ({
236
- entities: [
237
- { id: "a", confidence: 0.9 },
238
- { id: "b", confidence: 0.9 },
239
- { id: "c", confidence: 0.9 },
240
- ],
241
- count: 3,
242
- })),
243
- createMemoryRelation: mock(async () => {
244
- callCount++;
245
- if (callCount === 2) throw new Error("Relation failed");
246
- return { relation: {} };
247
- }),
248
- };
249
-
250
- const result = await autoExpandGraph(
251
- client as never,
252
- "new-entity",
253
- "Title",
254
- "Content",
255
- [],
256
- "ws-1",
257
- );
258
-
259
- // a succeeds, b fails, c succeeds → 2 created
260
- expect(result.relationsCreated).toBe(2);
261
- });
262
-
263
- test("silently handles 409 conflict (duplicate relation)", async () => {
264
- const conflictError = Object.assign(new Error("Conflict"), {
265
- status: 409,
266
- });
267
- const client = makeMockClient(
268
- [{ id: "existing", confidence: 0.9 }],
269
- conflictError,
270
- );
271
-
272
- const result = await autoExpandGraph(
273
- client as never,
274
- "new-entity",
275
- "Title",
276
- "Content",
277
- [],
278
- "ws-1",
279
- );
280
-
281
- // 409 is swallowed, count stays 0
282
- expect(result.relationsCreated).toBe(0);
283
- });
284
- });
285
- });