@jackchuka/gql-ingest 2.1.0 → 2.2.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.
- package/README.md +7 -7
- package/dist/cli.js +237 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/dependency-resolver.d.ts.map +1 -1
- package/dist/graphql-client.d.ts.map +1 -1
- package/dist/mapper.d.ts +1 -1
- package/dist/mapper.d.ts.map +1 -1
- package/dist/metrics.d.ts.map +1 -1
- package/dist/readers/data-reader.d.ts.map +1 -1
- package/dist/readers/json.d.ts.map +1 -1
- package/dist/readers/jsonl.d.ts.map +1 -1
- package/dist/readers/yaml.d.ts.map +1 -1
- package/package.json +31 -25
- package/bin/cli.js +0 -231
- package/src/cli.ts +0 -187
- package/src/config.test.ts +0 -272
- package/src/config.ts +0 -125
- package/src/dependency-resolver.test.ts +0 -211
- package/src/dependency-resolver.ts +0 -102
- package/src/graphql-client.test.ts +0 -219
- package/src/graphql-client.ts +0 -151
- package/src/mapper.test.ts +0 -607
- package/src/mapper.ts +0 -489
- package/src/metrics.test.ts +0 -207
- package/src/metrics.ts +0 -161
- package/src/readers/csv.test.ts +0 -82
- package/src/readers/csv.ts +0 -29
- package/src/readers/data-reader.test.ts +0 -104
- package/src/readers/data-reader.ts +0 -61
- package/src/readers/index.ts +0 -18
- package/src/readers/json.test.ts +0 -80
- package/src/readers/json.ts +0 -27
- package/src/readers/jsonl.test.ts +0 -96
- package/src/readers/jsonl.ts +0 -28
- package/src/readers/yaml.test.ts +0 -95
- package/src/readers/yaml.ts +0 -28
package/src/mapper.test.ts
DELETED
|
@@ -1,607 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { DataMapper } from "./mapper";
|
|
4
|
-
import { GraphQLClientWrapper } from "./graphql-client";
|
|
5
|
-
import { MetricsCollector } from "./metrics";
|
|
6
|
-
import { readCsvFile, DataReaderFactory } from "./readers";
|
|
7
|
-
|
|
8
|
-
jest.mock("fs");
|
|
9
|
-
jest.mock("./readers", () => ({
|
|
10
|
-
...jest.requireActual("./readers"),
|
|
11
|
-
readCsvFile: jest.fn(),
|
|
12
|
-
DataReaderFactory: {
|
|
13
|
-
getReader: jest.fn().mockReturnValue({
|
|
14
|
-
readFile: jest.fn(),
|
|
15
|
-
}),
|
|
16
|
-
},
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
const mockFs = fs as jest.Mocked<typeof fs>;
|
|
20
|
-
|
|
21
|
-
describe("DataMapper", () => {
|
|
22
|
-
let mockClient: jest.Mocked<GraphQLClientWrapper>;
|
|
23
|
-
let mockMetrics: jest.Mocked<MetricsCollector>;
|
|
24
|
-
let dataMapper: DataMapper;
|
|
25
|
-
const testBasePath = "/test/base/path";
|
|
26
|
-
|
|
27
|
-
beforeEach(() => {
|
|
28
|
-
mockClient = {
|
|
29
|
-
executeMutation: jest.fn(),
|
|
30
|
-
setHeaders: jest.fn(),
|
|
31
|
-
} as any;
|
|
32
|
-
|
|
33
|
-
mockMetrics = {
|
|
34
|
-
startEntityProcessing: jest.fn(),
|
|
35
|
-
recordSuccess: jest.fn(),
|
|
36
|
-
recordFailure: jest.fn(),
|
|
37
|
-
finishEntityProcessing: jest.fn(),
|
|
38
|
-
getMetrics: jest.fn(),
|
|
39
|
-
} as any;
|
|
40
|
-
|
|
41
|
-
dataMapper = new DataMapper(mockClient, testBasePath, mockMetrics);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
afterEach(() => {
|
|
45
|
-
jest.clearAllMocks();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe("discoverMappings", () => {
|
|
49
|
-
it("should discover mapping files in alphabetical order", () => {
|
|
50
|
-
const mockFiles = ["users.json", "items.json", "orders.json"];
|
|
51
|
-
mockFs.readdirSync.mockReturnValue(mockFiles as any);
|
|
52
|
-
|
|
53
|
-
const consoleSpy = jest.spyOn(console, "log").mockImplementation();
|
|
54
|
-
|
|
55
|
-
const result = dataMapper.discoverMappings("configs/test");
|
|
56
|
-
|
|
57
|
-
expect(mockFs.readdirSync).toHaveBeenCalledWith(
|
|
58
|
-
path.resolve(testBasePath, "configs/test", "mappings")
|
|
59
|
-
);
|
|
60
|
-
expect(result).toEqual([
|
|
61
|
-
"configs/test/mappings/items.json",
|
|
62
|
-
"configs/test/mappings/orders.json",
|
|
63
|
-
"configs/test/mappings/users.json",
|
|
64
|
-
]);
|
|
65
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
66
|
-
"Discovered 3 mapping files: items.json, orders.json, users.json"
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
consoleSpy.mockRestore();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("should filter only JSON files", () => {
|
|
73
|
-
const mockFiles = ["users.json", "items.txt", "orders.json", "readme.md"];
|
|
74
|
-
mockFs.readdirSync.mockReturnValue(mockFiles as any);
|
|
75
|
-
|
|
76
|
-
const result = dataMapper.discoverMappings("configs/test");
|
|
77
|
-
|
|
78
|
-
expect(result).toEqual([
|
|
79
|
-
"configs/test/mappings/orders.json",
|
|
80
|
-
"configs/test/mappings/users.json",
|
|
81
|
-
]);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("should handle directory read errors", () => {
|
|
85
|
-
mockFs.readdirSync.mockImplementation(() => {
|
|
86
|
-
throw new Error("Directory not found");
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const consoleSpy = jest.spyOn(console, "error").mockImplementation();
|
|
90
|
-
|
|
91
|
-
const result = dataMapper.discoverMappings("configs/nonexistent");
|
|
92
|
-
|
|
93
|
-
expect(result).toEqual([]);
|
|
94
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
95
|
-
expect.stringContaining("Error reading mappings directory"),
|
|
96
|
-
expect.any(Error)
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
consoleSpy.mockRestore();
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
describe("processEntity", () => {
|
|
104
|
-
it("should process entity successfully", async () => {
|
|
105
|
-
const mockConfig = {
|
|
106
|
-
csvFile: "data/users.csv",
|
|
107
|
-
graphqlFile: "graphql/users.graphql",
|
|
108
|
-
mapping: {
|
|
109
|
-
name: "user_name",
|
|
110
|
-
email: "user_email",
|
|
111
|
-
},
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const mockCsvData = [
|
|
115
|
-
{ user_name: "John", user_email: "john@example.com" },
|
|
116
|
-
{ user_name: "Jane", user_email: "jane@example.com" },
|
|
117
|
-
];
|
|
118
|
-
|
|
119
|
-
const mockMutation =
|
|
120
|
-
"mutation CreateUser($name: String!, $email: String!) { createUser(input: { name: $name, email: $email }) { id } }";
|
|
121
|
-
|
|
122
|
-
mockFs.readFileSync
|
|
123
|
-
.mockReturnValueOnce(JSON.stringify(mockConfig))
|
|
124
|
-
.mockReturnValueOnce(mockMutation);
|
|
125
|
-
|
|
126
|
-
const { DataReaderFactory } = require("./readers");
|
|
127
|
-
DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
|
|
128
|
-
|
|
129
|
-
mockClient.executeMutation.mockResolvedValue({
|
|
130
|
-
createUser: { id: "123" },
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
const consoleSpy = jest.spyOn(console, "log").mockImplementation();
|
|
134
|
-
|
|
135
|
-
await dataMapper.processEntity("configs/test/mappings/users.json");
|
|
136
|
-
|
|
137
|
-
expect(mockFs.readFileSync).toHaveBeenCalledWith(
|
|
138
|
-
path.resolve(testBasePath, "configs/test/mappings/users.json"),
|
|
139
|
-
"utf8"
|
|
140
|
-
);
|
|
141
|
-
expect(DataReaderFactory.getReader).toHaveBeenCalledWith(
|
|
142
|
-
path.resolve(testBasePath, "configs/test", "data/users.csv"),
|
|
143
|
-
undefined
|
|
144
|
-
);
|
|
145
|
-
expect(mockFs.readFileSync).toHaveBeenCalledWith(
|
|
146
|
-
path.resolve(testBasePath, "configs/test", "graphql/users.graphql"),
|
|
147
|
-
"utf8"
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
expect(mockClient.executeMutation).toHaveBeenCalledTimes(2);
|
|
151
|
-
expect(mockClient.executeMutation).toHaveBeenCalledWith(
|
|
152
|
-
mockMutation,
|
|
153
|
-
{
|
|
154
|
-
name: "John",
|
|
155
|
-
email: "john@example.com",
|
|
156
|
-
},
|
|
157
|
-
undefined
|
|
158
|
-
);
|
|
159
|
-
expect(mockClient.executeMutation).toHaveBeenCalledWith(
|
|
160
|
-
mockMutation,
|
|
161
|
-
{
|
|
162
|
-
name: "Jane",
|
|
163
|
-
email: "jane@example.com",
|
|
164
|
-
},
|
|
165
|
-
undefined
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
consoleSpy.mockRestore();
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it("should handle GraphQL execution errors gracefully", async () => {
|
|
172
|
-
const mockConfig = {
|
|
173
|
-
csvFile: "data/users.csv",
|
|
174
|
-
graphqlFile: "graphql/users.graphql",
|
|
175
|
-
mapping: { name: "user_name" },
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
const mockCsvData = [{ user_name: "John" }];
|
|
179
|
-
const mockMutation =
|
|
180
|
-
"mutation CreateUser($name: String!) { createUser(input: { name: $name }) { id } }";
|
|
181
|
-
|
|
182
|
-
mockFs.readFileSync
|
|
183
|
-
.mockReturnValueOnce(JSON.stringify(mockConfig))
|
|
184
|
-
.mockReturnValueOnce(mockMutation);
|
|
185
|
-
|
|
186
|
-
const { DataReaderFactory } = require("./readers");
|
|
187
|
-
DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
|
|
188
|
-
|
|
189
|
-
mockClient.executeMutation.mockRejectedValue(new Error("GraphQL error"));
|
|
190
|
-
|
|
191
|
-
const consoleSpy = jest.spyOn(console, "error").mockImplementation();
|
|
192
|
-
|
|
193
|
-
await dataMapper.processEntity("configs/test/mappings/users.json");
|
|
194
|
-
|
|
195
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
196
|
-
"✗ Failed to create entity for row 1:",
|
|
197
|
-
{ user_name: "John" },
|
|
198
|
-
expect.any(Error)
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
consoleSpy.mockRestore();
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it("should map CSV columns to GraphQL variables correctly", async () => {
|
|
205
|
-
const mockConfig = {
|
|
206
|
-
csvFile: "data/products.csv",
|
|
207
|
-
graphqlFile: "graphql/products.graphql",
|
|
208
|
-
mapping: {
|
|
209
|
-
name: "product_name",
|
|
210
|
-
price: "product_price",
|
|
211
|
-
sku: "product_sku",
|
|
212
|
-
},
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
const mockCsvData = [
|
|
216
|
-
{
|
|
217
|
-
product_name: "Widget",
|
|
218
|
-
product_price: "19.99",
|
|
219
|
-
product_sku: "W001",
|
|
220
|
-
extra_column: "ignored",
|
|
221
|
-
},
|
|
222
|
-
];
|
|
223
|
-
|
|
224
|
-
const mockMutation =
|
|
225
|
-
"mutation CreateProduct($name: String!, $price: String!, $sku: String!) { createProduct(input: { name: $name, price: $price, sku: $sku }) { id } }";
|
|
226
|
-
|
|
227
|
-
mockFs.readFileSync
|
|
228
|
-
.mockReturnValueOnce(JSON.stringify(mockConfig))
|
|
229
|
-
.mockReturnValueOnce(mockMutation);
|
|
230
|
-
|
|
231
|
-
const { DataReaderFactory } = require("./readers");
|
|
232
|
-
DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
|
|
233
|
-
|
|
234
|
-
mockClient.executeMutation.mockResolvedValue({
|
|
235
|
-
createProduct: { id: "456" },
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
await dataMapper.processEntity("configs/test/mappings/products.json");
|
|
239
|
-
|
|
240
|
-
expect(mockClient.executeMutation).toHaveBeenCalledWith(
|
|
241
|
-
mockMutation,
|
|
242
|
-
{
|
|
243
|
-
name: "Widget",
|
|
244
|
-
price: "19.99",
|
|
245
|
-
sku: "W001",
|
|
246
|
-
},
|
|
247
|
-
undefined
|
|
248
|
-
);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
it("should handle missing CSV columns gracefully", async () => {
|
|
252
|
-
const mockConfig = {
|
|
253
|
-
csvFile: "data/users.csv",
|
|
254
|
-
graphqlFile: "graphql/users.graphql",
|
|
255
|
-
mapping: {
|
|
256
|
-
name: "user_name",
|
|
257
|
-
email: "user_email",
|
|
258
|
-
phone: "user_phone",
|
|
259
|
-
},
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
const mockCsvData = [
|
|
263
|
-
{ user_name: "John", user_email: "john@example.com" },
|
|
264
|
-
];
|
|
265
|
-
|
|
266
|
-
const mockMutation =
|
|
267
|
-
"mutation CreateUser($name: String!, $email: String, $phone: String) { createUser(input: { name: $name, email: $email, phone: $phone }) { id } }";
|
|
268
|
-
|
|
269
|
-
mockFs.readFileSync
|
|
270
|
-
.mockReturnValueOnce(JSON.stringify(mockConfig))
|
|
271
|
-
.mockReturnValueOnce(mockMutation);
|
|
272
|
-
|
|
273
|
-
const { DataReaderFactory } = require("./readers");
|
|
274
|
-
DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
|
|
275
|
-
|
|
276
|
-
mockClient.executeMutation.mockResolvedValue({
|
|
277
|
-
createUser: { id: "789" },
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
await dataMapper.processEntity("configs/test/mappings/users.json");
|
|
281
|
-
|
|
282
|
-
expect(mockClient.executeMutation).toHaveBeenCalledWith(
|
|
283
|
-
mockMutation,
|
|
284
|
-
{
|
|
285
|
-
name: "John",
|
|
286
|
-
email: "john@example.com",
|
|
287
|
-
},
|
|
288
|
-
undefined
|
|
289
|
-
);
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
it("should call metrics methods during successful processing", async () => {
|
|
293
|
-
const mockConfig = {
|
|
294
|
-
csvFile: "data/users.csv",
|
|
295
|
-
graphqlFile: "graphql/users.graphql",
|
|
296
|
-
mapping: { name: "user_name" },
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
const mockCsvData = [{ user_name: "John" }, { user_name: "Jane" }];
|
|
300
|
-
const mockMutation =
|
|
301
|
-
"mutation CreateUser($name: String!) { createUser(input: { name: $name }) { id } }";
|
|
302
|
-
|
|
303
|
-
mockFs.readFileSync
|
|
304
|
-
.mockReturnValueOnce(JSON.stringify(mockConfig))
|
|
305
|
-
.mockReturnValueOnce(mockMutation);
|
|
306
|
-
|
|
307
|
-
const { DataReaderFactory } = require("./readers");
|
|
308
|
-
DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
|
|
309
|
-
|
|
310
|
-
mockClient.executeMutation.mockResolvedValue({
|
|
311
|
-
createUser: { id: "123" },
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
await dataMapper.processEntity("configs/test/mappings/users.json");
|
|
315
|
-
|
|
316
|
-
expect(mockMetrics.startEntityProcessing).toHaveBeenCalledWith("users");
|
|
317
|
-
expect(mockMetrics.recordSuccess).toHaveBeenCalledTimes(2);
|
|
318
|
-
expect(mockMetrics.recordSuccess).toHaveBeenCalledWith("users");
|
|
319
|
-
expect(mockMetrics.finishEntityProcessing).toHaveBeenCalledWith("users");
|
|
320
|
-
expect(mockMetrics.recordFailure).not.toHaveBeenCalled();
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
it("should call metrics methods during failed processing", async () => {
|
|
324
|
-
const mockConfig = {
|
|
325
|
-
csvFile: "data/users.csv",
|
|
326
|
-
graphqlFile: "graphql/users.graphql",
|
|
327
|
-
mapping: { name: "user_name" },
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
const mockCsvData = [{ user_name: "John" }];
|
|
331
|
-
const mockMutation =
|
|
332
|
-
"mutation CreateUser($name: String!) { createUser(input: { name: $name }) { id } }";
|
|
333
|
-
|
|
334
|
-
mockFs.readFileSync
|
|
335
|
-
.mockReturnValueOnce(JSON.stringify(mockConfig))
|
|
336
|
-
.mockReturnValueOnce(mockMutation);
|
|
337
|
-
|
|
338
|
-
const { DataReaderFactory } = require("./readers");
|
|
339
|
-
DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
|
|
340
|
-
|
|
341
|
-
mockClient.executeMutation.mockRejectedValue(new Error("GraphQL error"));
|
|
342
|
-
|
|
343
|
-
const consoleSpy = jest.spyOn(console, "error").mockImplementation();
|
|
344
|
-
|
|
345
|
-
await dataMapper.processEntity("configs/test/mappings/users.json");
|
|
346
|
-
|
|
347
|
-
expect(mockMetrics.startEntityProcessing).toHaveBeenCalledWith("users");
|
|
348
|
-
expect(mockMetrics.recordFailure).toHaveBeenCalledTimes(1);
|
|
349
|
-
expect(mockMetrics.recordFailure).toHaveBeenCalledWith("users");
|
|
350
|
-
expect(mockMetrics.finishEntityProcessing).toHaveBeenCalledWith("users");
|
|
351
|
-
expect(mockMetrics.recordSuccess).not.toHaveBeenCalled();
|
|
352
|
-
|
|
353
|
-
consoleSpy.mockRestore();
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
it("should expose metrics through getMetrics method", () => {
|
|
357
|
-
const metrics = dataMapper.getMetrics();
|
|
358
|
-
expect(metrics).toBe(mockMetrics);
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
it("should convert numeric types from CSV strings to proper GraphQL types", async () => {
|
|
362
|
-
const mockConfig = {
|
|
363
|
-
csvFile: "data/products.csv",
|
|
364
|
-
graphqlFile: "graphql/products.graphql",
|
|
365
|
-
mapping: {
|
|
366
|
-
name: "product_name",
|
|
367
|
-
price: "product_price",
|
|
368
|
-
quantity: "product_quantity",
|
|
369
|
-
active: "product_active",
|
|
370
|
-
},
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
const mockCsvData = [
|
|
374
|
-
{
|
|
375
|
-
product_name: "Widget",
|
|
376
|
-
product_price: "19.99",
|
|
377
|
-
product_quantity: "10",
|
|
378
|
-
product_active: "true",
|
|
379
|
-
},
|
|
380
|
-
];
|
|
381
|
-
|
|
382
|
-
const mockMutation = `
|
|
383
|
-
mutation CreateProduct($name: String!, $price: Float!, $quantity: Int!, $active: Boolean!) {
|
|
384
|
-
createProduct(input: { name: $name, price: $price, quantity: $quantity, active: $active }) {
|
|
385
|
-
id
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
`;
|
|
389
|
-
|
|
390
|
-
mockFs.readFileSync
|
|
391
|
-
.mockReturnValueOnce(JSON.stringify(mockConfig))
|
|
392
|
-
.mockReturnValueOnce(mockMutation);
|
|
393
|
-
|
|
394
|
-
const { DataReaderFactory } = require("./readers");
|
|
395
|
-
DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
|
|
396
|
-
|
|
397
|
-
mockClient.executeMutation.mockResolvedValue({
|
|
398
|
-
createProduct: { id: "123" },
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
await dataMapper.processEntity("configs/test/mappings/products.json");
|
|
402
|
-
|
|
403
|
-
expect(mockClient.executeMutation).toHaveBeenCalledWith(
|
|
404
|
-
mockMutation,
|
|
405
|
-
{
|
|
406
|
-
name: "Widget",
|
|
407
|
-
price: 19.99,
|
|
408
|
-
quantity: 10,
|
|
409
|
-
active: true,
|
|
410
|
-
},
|
|
411
|
-
undefined
|
|
412
|
-
);
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
it("should handle invalid numeric conversions gracefully", async () => {
|
|
416
|
-
const mockConfig = {
|
|
417
|
-
csvFile: "data/products.csv",
|
|
418
|
-
graphqlFile: "graphql/products.graphql",
|
|
419
|
-
mapping: {
|
|
420
|
-
name: "product_name",
|
|
421
|
-
price: "product_price",
|
|
422
|
-
quantity: "product_quantity",
|
|
423
|
-
},
|
|
424
|
-
};
|
|
425
|
-
|
|
426
|
-
const mockCsvData = [
|
|
427
|
-
{
|
|
428
|
-
product_name: "Widget",
|
|
429
|
-
product_price: "invalid_price",
|
|
430
|
-
product_quantity: "invalid_quantity",
|
|
431
|
-
},
|
|
432
|
-
];
|
|
433
|
-
|
|
434
|
-
const mockMutation = `
|
|
435
|
-
mutation CreateProduct($name: String!, $price: Float!, $quantity: Int!) {
|
|
436
|
-
createProduct(input: { name: $name, price: $price, quantity: $quantity }) {
|
|
437
|
-
id
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
`;
|
|
441
|
-
|
|
442
|
-
mockFs.readFileSync
|
|
443
|
-
.mockReturnValueOnce(JSON.stringify(mockConfig))
|
|
444
|
-
.mockReturnValueOnce(mockMutation);
|
|
445
|
-
|
|
446
|
-
const { DataReaderFactory } = require("./readers");
|
|
447
|
-
DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
|
|
448
|
-
|
|
449
|
-
mockClient.executeMutation.mockResolvedValue({
|
|
450
|
-
createProduct: { id: "123" },
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
const consoleSpy = jest.spyOn(console, "warn").mockImplementation();
|
|
454
|
-
|
|
455
|
-
await dataMapper.processEntity("configs/test/mappings/products.json");
|
|
456
|
-
|
|
457
|
-
expect(mockClient.executeMutation).toHaveBeenCalledWith(
|
|
458
|
-
mockMutation,
|
|
459
|
-
{
|
|
460
|
-
name: "Widget",
|
|
461
|
-
price: "invalid_price",
|
|
462
|
-
quantity: "invalid_quantity",
|
|
463
|
-
},
|
|
464
|
-
undefined
|
|
465
|
-
);
|
|
466
|
-
|
|
467
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
468
|
-
'Warning: Cannot convert "invalid_price" to Float for variable $price. Expected a valid number. Using original value.'
|
|
469
|
-
);
|
|
470
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
471
|
-
'Warning: Cannot convert "invalid_quantity" to Int for variable $quantity. Expected a valid integer. Using original value.'
|
|
472
|
-
);
|
|
473
|
-
|
|
474
|
-
consoleSpy.mockRestore();
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
it("should handle edge cases in numeric conversion safely", async () => {
|
|
478
|
-
const mockConfig = {
|
|
479
|
-
csvFile: "data/products.csv",
|
|
480
|
-
graphqlFile: "graphql/products.graphql",
|
|
481
|
-
mapping: {
|
|
482
|
-
int_field: "int_value",
|
|
483
|
-
float_field: "float_value",
|
|
484
|
-
},
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
const mockCsvData = [
|
|
488
|
-
{
|
|
489
|
-
int_value: "1.5", // Float in Int field - should remain string
|
|
490
|
-
float_value: "Infinity", // Invalid float - should remain string
|
|
491
|
-
},
|
|
492
|
-
{
|
|
493
|
-
int_value: "not_a_number", // Invalid int - should remain string
|
|
494
|
-
float_value: "1.2.3", // Invalid number format - should remain string
|
|
495
|
-
},
|
|
496
|
-
];
|
|
497
|
-
|
|
498
|
-
const mockMutation = `
|
|
499
|
-
mutation CreateProduct($int_field: Int!, $float_field: Float!) {
|
|
500
|
-
createProduct(input: { int_field: $int_field, float_field: $float_field }) {
|
|
501
|
-
id
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
`;
|
|
505
|
-
|
|
506
|
-
mockFs.readFileSync
|
|
507
|
-
.mockReturnValueOnce(JSON.stringify(mockConfig))
|
|
508
|
-
.mockReturnValueOnce(mockMutation);
|
|
509
|
-
|
|
510
|
-
const { DataReaderFactory } = require("./readers");
|
|
511
|
-
DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
|
|
512
|
-
|
|
513
|
-
mockClient.executeMutation.mockResolvedValue({
|
|
514
|
-
createProduct: { id: "123" },
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
const consoleSpy = jest.spyOn(console, "warn").mockImplementation();
|
|
518
|
-
|
|
519
|
-
await dataMapper.processEntity("configs/test/mappings/products.json");
|
|
520
|
-
|
|
521
|
-
// Should keep invalid values as strings
|
|
522
|
-
expect(mockClient.executeMutation).toHaveBeenCalledWith(
|
|
523
|
-
mockMutation,
|
|
524
|
-
{
|
|
525
|
-
int_field: "1.5",
|
|
526
|
-
float_field: "Infinity",
|
|
527
|
-
},
|
|
528
|
-
undefined
|
|
529
|
-
);
|
|
530
|
-
|
|
531
|
-
expect(mockClient.executeMutation).toHaveBeenCalledWith(
|
|
532
|
-
mockMutation,
|
|
533
|
-
{
|
|
534
|
-
int_field: "not_a_number",
|
|
535
|
-
float_field: "1.2.3",
|
|
536
|
-
},
|
|
537
|
-
undefined
|
|
538
|
-
);
|
|
539
|
-
|
|
540
|
-
consoleSpy.mockRestore();
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
it("should keep unknown scalar types as strings", async () => {
|
|
544
|
-
const mockConfig = {
|
|
545
|
-
csvFile: "data/products.csv",
|
|
546
|
-
graphqlFile: "graphql/products.graphql",
|
|
547
|
-
mapping: {
|
|
548
|
-
name: "product_name",
|
|
549
|
-
custom_field: "custom_value",
|
|
550
|
-
},
|
|
551
|
-
};
|
|
552
|
-
|
|
553
|
-
const mockCsvData = [
|
|
554
|
-
{
|
|
555
|
-
product_name: "Widget",
|
|
556
|
-
custom_value: "123",
|
|
557
|
-
},
|
|
558
|
-
];
|
|
559
|
-
|
|
560
|
-
const mockMutation = `
|
|
561
|
-
mutation CreateProduct($name: String!, $custom_field: CustomScalar!) {
|
|
562
|
-
createProduct(input: { name: $name, custom_field: $custom_field }) {
|
|
563
|
-
id
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
`;
|
|
567
|
-
|
|
568
|
-
mockFs.readFileSync
|
|
569
|
-
.mockReturnValueOnce(JSON.stringify(mockConfig))
|
|
570
|
-
.mockReturnValueOnce(mockMutation);
|
|
571
|
-
|
|
572
|
-
const { DataReaderFactory } = require("./readers");
|
|
573
|
-
DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
|
|
574
|
-
|
|
575
|
-
mockClient.executeMutation.mockResolvedValue({
|
|
576
|
-
createProduct: { id: "123" },
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
// Create verbose mapper to test the logging
|
|
580
|
-
const verboseMapper = new DataMapper(
|
|
581
|
-
mockClient,
|
|
582
|
-
testBasePath,
|
|
583
|
-
mockMetrics,
|
|
584
|
-
true
|
|
585
|
-
);
|
|
586
|
-
const consoleSpy = jest.spyOn(console, "log").mockImplementation();
|
|
587
|
-
|
|
588
|
-
await verboseMapper.processEntity("configs/test/mappings/products.json");
|
|
589
|
-
|
|
590
|
-
// Should keep custom scalar as string
|
|
591
|
-
expect(mockClient.executeMutation).toHaveBeenCalledWith(
|
|
592
|
-
mockMutation,
|
|
593
|
-
{
|
|
594
|
-
name: "Widget",
|
|
595
|
-
custom_field: "123",
|
|
596
|
-
},
|
|
597
|
-
undefined
|
|
598
|
-
);
|
|
599
|
-
|
|
600
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
601
|
-
'Unknown GraphQL type "CustomScalar" for variable $custom_field. Keeping value as string.'
|
|
602
|
-
);
|
|
603
|
-
|
|
604
|
-
consoleSpy.mockRestore();
|
|
605
|
-
});
|
|
606
|
-
});
|
|
607
|
-
});
|