Skip to content

Commit bfc9dbc

Browse files
committed
Add more tests
1 parent ec86f82 commit bfc9dbc

File tree

3 files changed

+347
-8
lines changed

3 files changed

+347
-8
lines changed

packages/graph-explorer/src/connector/gremlin/neighborCounts.test.ts

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createVertexId } from "@/core";
1+
import { createVertexId, EntityRawId } from "@/core";
22
import { neighborCounts } from "./neighborCounts";
33
import { query } from "@/utils";
44
import { NeighborCount } from "../useGEFetchTypes";
@@ -7,6 +7,7 @@ import {
77
createGremlinResponse,
88
createRandomVertexId,
99
} from "@/utils/testing";
10+
import { GMap } from "./types";
1011

1112
describe("neighborCounts", () => {
1213
it("should return empty for an empty request", async () => {
@@ -117,18 +118,86 @@ describe("neighborCounts", () => {
117118
)
118119
`);
119120
});
121+
122+
it("should handle error response", async () => {
123+
const mockFetch = vi.fn().mockResolvedValue({
124+
code: 500,
125+
detailedMessage: "Internal server error occurred",
126+
});
127+
128+
await expect(
129+
neighborCounts(mockFetch, {
130+
vertexIds: [createVertexId("123")],
131+
})
132+
).rejects.toThrow("Internal server error occurred");
133+
});
134+
135+
it("should handle empty response data", async () => {
136+
const mockFetch = vi
137+
.fn()
138+
.mockResolvedValue(createGremlinResponse(createGMap({})));
139+
140+
const result = await neighborCounts(mockFetch, {
141+
vertexIds: [createVertexId("123")],
142+
});
143+
144+
expect(result.counts).toEqual([]);
145+
});
146+
147+
it("should handle vertex with zero neighbors", async () => {
148+
const vertexId = createRandomVertexId();
149+
const expected: NeighborCount = {
150+
vertexId,
151+
totalCount: 0,
152+
counts: {},
153+
};
154+
const response = createResponse(expected);
155+
const mockFetch = vi.fn().mockResolvedValue(response);
156+
157+
const result = await neighborCounts(mockFetch, {
158+
vertexIds: [vertexId],
159+
});
160+
161+
expect(result.counts).toEqual([expected]);
162+
});
163+
164+
it("should handle mixed vertex ID types", async () => {
165+
const stringId = createVertexId("string-id");
166+
const numberId = createVertexId(42);
167+
const expected1: NeighborCount = {
168+
vertexId: stringId,
169+
totalCount: 5,
170+
counts: { type1: 5 },
171+
};
172+
const expected2: NeighborCount = {
173+
vertexId: numberId, // Gremlin converts number IDs to strings
174+
totalCount: 3,
175+
counts: { type2: 3 },
176+
};
177+
const response = createResponse(expected1, expected2);
178+
const mockFetch = vi.fn().mockResolvedValue(response);
179+
180+
const result = await neighborCounts(mockFetch, {
181+
vertexIds: [stringId, numberId],
182+
});
183+
184+
expect(result.counts).toHaveLength(2);
185+
expect(result.counts).toEqual(
186+
expect.arrayContaining([
187+
expect.objectContaining(expected1),
188+
expect.objectContaining(expected2),
189+
])
190+
);
191+
});
120192
});
121193

122194
function createResponse(...counts: NeighborCount[]) {
123195
return createGremlinResponse(
124196
createGMap(
125-
counts.reduce(
126-
(prev, curr) => {
127-
prev[String(curr.vertexId)] = createGMap(curr.counts);
128-
return prev;
129-
},
130-
{} as Record<string, any>
131-
)
197+
counts.reduce((prev, curr) => {
198+
prev.set(curr.vertexId, createGMap(curr.counts));
199+
return prev;
200+
}, new Map<EntityRawId, GMap>())
132201
)
133202
);
134203
}

packages/graph-explorer/src/connector/openCypher/neighborCounts.test.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,134 @@ describe("neighborCounts", () => {
9797
});
9898
expect(result.counts).toEqual([expected1, expected2]);
9999
});
100+
101+
it("should handle error response", async () => {
102+
const mockFetch = vi.fn().mockResolvedValue({
103+
code: 500,
104+
detailedMessage: "openCypher query failed",
105+
});
106+
107+
await expect(
108+
neighborCounts(mockFetch, {
109+
vertexIds: [createVertexId("123")],
110+
})
111+
).rejects.toThrow("openCypher query failed");
112+
});
113+
114+
it("should handle empty results", async () => {
115+
const mockFetch = vi.fn().mockResolvedValue({ results: [] });
116+
117+
const result = await neighborCounts(mockFetch, {
118+
vertexIds: [createVertexId("123")],
119+
});
120+
121+
expect(result.counts).toEqual([]);
122+
});
123+
124+
it("should handle vertex with zero neighbors", async () => {
125+
const vertexId = createRandomVertexId();
126+
const response = {
127+
results: [
128+
{
129+
id: String(vertexId),
130+
counts: [],
131+
},
132+
],
133+
};
134+
const mockFetch = vi.fn().mockResolvedValue(response);
135+
136+
const result = await neighborCounts(mockFetch, {
137+
vertexIds: [vertexId],
138+
});
139+
140+
expect(result.counts).toEqual([
141+
{
142+
vertexId,
143+
totalCount: 0,
144+
counts: {},
145+
},
146+
]);
147+
});
148+
149+
it("should handle malformed result data", async () => {
150+
const vertexId = createRandomVertexId();
151+
const response = {
152+
results: [
153+
{
154+
id: String(vertexId),
155+
// Missing counts property
156+
},
157+
],
158+
};
159+
const mockFetch = vi.fn().mockResolvedValue(response);
160+
161+
const result = await neighborCounts(mockFetch, {
162+
vertexIds: [vertexId],
163+
});
164+
165+
expect(result.counts).toEqual([]);
166+
});
167+
168+
it("should handle single label neighbors", async () => {
169+
const vertexId = createRandomVertexId();
170+
const expected: NeighborCount = {
171+
vertexId,
172+
totalCount: 5,
173+
counts: {
174+
Person: 5,
175+
},
176+
};
177+
const response = {
178+
results: [
179+
{
180+
id: String(vertexId),
181+
counts: [
182+
{
183+
label: ["Person"],
184+
count: 5,
185+
},
186+
],
187+
},
188+
],
189+
};
190+
const mockFetch = vi.fn().mockResolvedValue(response);
191+
192+
const result = await neighborCounts(mockFetch, {
193+
vertexIds: [vertexId],
194+
});
195+
196+
expect(result.counts).toEqual([expected]);
197+
});
198+
199+
it("should handle empty label arrays", async () => {
200+
const vertexId = createRandomVertexId();
201+
const response = {
202+
results: [
203+
{
204+
id: String(vertexId),
205+
counts: [
206+
{
207+
label: [],
208+
count: 3,
209+
},
210+
],
211+
},
212+
],
213+
};
214+
const mockFetch = vi.fn().mockResolvedValue(response);
215+
216+
const result = await neighborCounts(mockFetch, {
217+
vertexIds: [vertexId],
218+
});
219+
220+
expect(result.counts).toEqual([
221+
{
222+
vertexId,
223+
totalCount: 3,
224+
counts: {},
225+
},
226+
]);
227+
});
100228
});
101229

102230
function createResponse(...counts: NeighborCount[]) {

packages/graph-explorer/src/connector/sparql/neighborCounts.test.ts

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,148 @@ describe("neighborCounts", () => {
205205
edgeTo,
206206
]);
207207
});
208+
209+
it("should handle error response for total counts", async () => {
210+
const blankNodes: BlankNodesMap = new Map();
211+
const mockFetch = vi.fn().mockResolvedValue({
212+
code: 500,
213+
detailedMessage: "SPARQL query failed",
214+
});
215+
216+
await expect(
217+
neighborCounts(
218+
mockFetch,
219+
{ vertexIds: [createVertexId(createRandomUrlString())] },
220+
blankNodes
221+
)
222+
).rejects.toThrow("Total neighbor count request failed");
223+
});
224+
225+
it("should handle malformed response for total counts", async () => {
226+
const blankNodes: BlankNodesMap = new Map();
227+
const mockFetch = vi
228+
.fn()
229+
.mockResolvedValueOnce({
230+
head: { vars: ["resource", "totalCount"] },
231+
results: {
232+
bindings: [
233+
{
234+
resource: createUriValue("invalid"),
235+
totalCount: { type: "invalid", value: "not-a-number" },
236+
},
237+
],
238+
},
239+
})
240+
.mockResolvedValueOnce({
241+
head: { vars: ["resource", "type", "typeCount"] },
242+
results: { bindings: [] },
243+
});
244+
245+
await expect(
246+
neighborCounts(
247+
mockFetch,
248+
{ vertexIds: [createVertexId(createRandomUrlString())] },
249+
blankNodes
250+
)
251+
).rejects.toThrow();
252+
});
253+
254+
it("should handle vertices with no neighbors", async () => {
255+
const blankNodes: BlankNodesMap = new Map();
256+
const vertexId = createVertexId(createRandomUrlString());
257+
const expected: NeighborCount = {
258+
vertexId,
259+
totalCount: 0,
260+
counts: {},
261+
};
262+
const totalCountResponse = createTotalCountResponse();
263+
const countsByTypeResponse = createCountsByTypeResponse();
264+
const mockFetch = vi
265+
.fn()
266+
.mockResolvedValueOnce(totalCountResponse)
267+
.mockResolvedValueOnce(countsByTypeResponse);
268+
269+
const result = await neighborCounts(
270+
mockFetch,
271+
{ vertexIds: [vertexId] },
272+
blankNodes
273+
);
274+
275+
expect(result.counts).toEqual([expected]);
276+
});
277+
278+
it("should handle mixed blank and non-blank nodes", async () => {
279+
const blankNodes: BlankNodesMap = new Map();
280+
const blankVertex = createRandomVertexForRdf();
281+
blankVertex.isBlankNode = true;
282+
const nonBlankVertex = createVertexId(createRandomUrlString());
283+
284+
const expectedBlank: NeighborCount = {
285+
vertexId: blankVertex.id,
286+
totalCount: 2,
287+
counts: { [createRandomUrlString()]: 2 },
288+
};
289+
const expectedNonBlank: NeighborCount = {
290+
vertexId: nonBlankVertex,
291+
totalCount: 3,
292+
counts: { [createRandomUrlString()]: 3 },
293+
};
294+
295+
blankNodes.set(blankVertex.id, {
296+
id: blankVertex.id,
297+
neighbors: {
298+
vertices: createArray(2, createRandomVertexForRdf),
299+
edges: createArray(2, () =>
300+
createRandomEdgeForRdf(
301+
createRandomVertexForRdf(),
302+
createRandomVertexForRdf()
303+
)
304+
),
305+
},
306+
neighborCounts: {
307+
counts: expectedBlank.counts,
308+
totalCount: expectedBlank.totalCount,
309+
},
310+
vertex: blankVertex,
311+
subQueryTemplate: "",
312+
});
313+
314+
const totalCountResponse = createTotalCountResponse(expectedNonBlank);
315+
const countsByTypeResponse = createCountsByTypeResponse(expectedNonBlank);
316+
const mockFetch = vi
317+
.fn()
318+
.mockResolvedValueOnce(totalCountResponse)
319+
.mockResolvedValueOnce(countsByTypeResponse);
320+
321+
const result = await neighborCounts(
322+
mockFetch,
323+
{ vertexIds: [blankVertex.id, nonBlankVertex] },
324+
blankNodes
325+
);
326+
327+
expect(result.counts).toEqual(
328+
expect.arrayContaining([expectedBlank, expectedNonBlank])
329+
);
330+
});
331+
332+
it("should handle blank node fetch failure", async () => {
333+
const blankNodes: BlankNodesMap = new Map();
334+
const vertex = createRandomVertexForRdf();
335+
vertex.isBlankNode = true;
336+
337+
blankNodes.set(vertex.id, {
338+
id: vertex.id,
339+
neighborCounts: { totalCount: 0, counts: {} },
340+
vertex,
341+
subQueryTemplate: createRandomName("subQuery"),
342+
});
343+
344+
const mockFetch = vi.fn().mockRejectedValue(new Error("Network error"));
345+
346+
await expect(
347+
neighborCounts(mockFetch, { vertexIds: [vertex.id] }, blankNodes)
348+
).rejects.toThrow("Network error");
349+
});
208350
});
209351

210352
function createTotalCountResponse(...counts: NeighborCount[]) {

0 commit comments

Comments
 (0)