kubernetes-fluent-client 3.0.3 → 4.0.0-rc-http2-watch

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.
Files changed (42) hide show
  1. package/.prettierignore +4 -0
  2. package/README.md +24 -0
  3. package/dist/cli.js +21 -1
  4. package/dist/fileSystem.d.ts +11 -0
  5. package/dist/fileSystem.d.ts.map +1 -0
  6. package/dist/fileSystem.js +42 -0
  7. package/dist/fileSystem.test.d.ts +2 -0
  8. package/dist/fileSystem.test.d.ts.map +1 -0
  9. package/dist/fileSystem.test.js +75 -0
  10. package/dist/fluent/watch.d.ts +2 -0
  11. package/dist/fluent/watch.d.ts.map +1 -1
  12. package/dist/fluent/watch.js +147 -27
  13. package/dist/generate.d.ts +71 -11
  14. package/dist/generate.d.ts.map +1 -1
  15. package/dist/generate.js +130 -117
  16. package/dist/generate.test.js +293 -346
  17. package/dist/postProcessing.d.ts +246 -0
  18. package/dist/postProcessing.d.ts.map +1 -0
  19. package/dist/postProcessing.js +497 -0
  20. package/dist/postProcessing.test.d.ts +2 -0
  21. package/dist/postProcessing.test.d.ts.map +1 -0
  22. package/dist/postProcessing.test.js +550 -0
  23. package/e2e/cli.e2e.test.ts +127 -0
  24. package/e2e/crds/policyreports.default.expected/policyreport-v1alpha1.ts +332 -0
  25. package/e2e/crds/policyreports.default.expected/policyreport-v1alpha2.ts +360 -0
  26. package/e2e/crds/policyreports.default.expected/policyreport-v1beta1.ts +360 -0
  27. package/e2e/crds/policyreports.no.post.expected/policyreport-v1alpha1.ts +331 -0
  28. package/e2e/crds/policyreports.no.post.expected/policyreport-v1alpha2.ts +360 -0
  29. package/e2e/crds/policyreports.no.post.expected/policyreport-v1beta1.ts +360 -0
  30. package/e2e/crds/test.yaml/policyreports.test.yaml +1008 -0
  31. package/e2e/crds/test.yaml/uds-podmonitors.test.yaml +1245 -0
  32. package/e2e/crds/uds-podmonitors.default.expected/podmonitor-v1.ts +1333 -0
  33. package/e2e/crds/uds-podmonitors.no.post.expected/podmonitor-v1.ts +1360 -0
  34. package/package.json +6 -5
  35. package/src/cli.ts +25 -1
  36. package/src/fileSystem.test.ts +67 -0
  37. package/src/fileSystem.ts +25 -0
  38. package/src/fluent/watch.ts +174 -35
  39. package/src/generate.test.ts +368 -358
  40. package/src/generate.ts +173 -154
  41. package/src/postProcessing.test.ts +742 -0
  42. package/src/postProcessing.ts +568 -0
@@ -0,0 +1,742 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2023-Present The Kubernetes Fluent Client Authors
3
+
4
+ import * as postProcessingModule from "./postProcessing";
5
+ import { NodeFileSystem } from "./fileSystem";
6
+ import { GenerateOptions } from "./generate";
7
+ import { jest, beforeEach, test, expect, describe, afterEach } from "@jest/globals";
8
+ //import { SpyInstance } from "jest-mock";
9
+ import { CustomResourceDefinition } from "./upstream";
10
+ import * as fs from "fs"; // We'll mock fs
11
+
12
+ // Mock fs
13
+ jest.mock("fs");
14
+
15
+ // Mock path.join
16
+ jest.mock("path", () => ({
17
+ join: (...args: string[]) => args.join("/"), // Simulates path.join behavior
18
+ }));
19
+
20
+ // Mock NodeFileSystem methods
21
+ jest.mock("./fileSystem", () => ({
22
+ NodeFileSystem: jest.fn().mockImplementation(() => ({
23
+ readdirSync: jest.fn(),
24
+ readFile: jest.fn(),
25
+ writeFile: jest.fn(),
26
+ })),
27
+ }));
28
+
29
+ jest.mock("./types", () => ({
30
+ GenericKind: jest.fn().mockImplementation(() => ({
31
+ kind: "MockKind",
32
+ apiVersion: "v1",
33
+ })),
34
+ }));
35
+
36
+ jest.mock("./postProcessing", () => {
37
+ const originalModule = jest.requireActual("./postProcessing");
38
+ return {
39
+ ...(typeof originalModule === "object" ? originalModule : {}),
40
+ processAndModifySingleFile: jest.fn(), // Mock the specific function
41
+ mapFilesToCRD: jest.fn(), // Mock mapFilesToCRD to avoid conflict
42
+ };
43
+ });
44
+
45
+ const mockFileSystem = new NodeFileSystem();
46
+
47
+ const mockCRDResults = [
48
+ {
49
+ name: "TestKind",
50
+ crd: {
51
+ spec: {
52
+ group: "test.group",
53
+ names: { kind: "TestKind", plural: "TestKinds" },
54
+ scope: "Namespaced",
55
+ versions: [{ name: "v1", served: true, storage: true }],
56
+ },
57
+ },
58
+ version: "v1",
59
+ },
60
+ ];
61
+
62
+ // Define the mock data
63
+ /* const mockLines = ["line1", "line2"];
64
+ const mockName = "TestKind";
65
+ const mockCRD: CustomResourceDefinition = {
66
+ spec: {
67
+ group: "test.group",
68
+ names: { kind: "TestKind", plural: "testkinds" },
69
+ scope: "Namespaced",
70
+ versions: [{ name: "v1", served: true, storage: true }],
71
+ },
72
+ };
73
+ const mockVersion = "v1"; */
74
+ const mockOpts: GenerateOptions = {
75
+ directory: "mockDir",
76
+ logFn: jest.fn(), // Mock logging function
77
+ language: "ts",
78
+ plain: false,
79
+ npmPackage: "mockPackage",
80
+ source: "",
81
+ };
82
+
83
+ describe("postProcessing", () => {
84
+ beforeEach(() => {
85
+ jest.clearAllMocks(); // Clear mocks before each test
86
+ });
87
+
88
+ afterEach(() => {
89
+ jest.restoreAllMocks(); // Restore all mocks after each test
90
+ });
91
+
92
+ test("should log error when directory is not defined", async () => {
93
+ const optsWithoutDirectory = { ...mockOpts, directory: undefined };
94
+
95
+ await postProcessingModule.postProcessing(mockCRDResults, optsWithoutDirectory, mockFileSystem);
96
+
97
+ expect(mockOpts.logFn).toHaveBeenCalledWith("⚠️ Error: Directory is not defined.");
98
+ });
99
+
100
+ test("should read files from directory and process them", async () => {
101
+ const mockFileResultMap = { "TestKind-v1.ts": mockCRDResults[0] };
102
+ jest.spyOn(mockFileSystem, "readFile").mockReturnValue("mock content");
103
+ jest.spyOn(mockFileSystem, "writeFile");
104
+
105
+ await postProcessingModule.processFiles(
106
+ ["TestKind-v1.ts"],
107
+ mockFileResultMap,
108
+ mockOpts,
109
+ mockFileSystem,
110
+ );
111
+
112
+ expect(mockFileSystem.readFile).toHaveBeenCalledWith("mockDir/TestKind-v1.ts");
113
+ expect(mockFileSystem.writeFile).toHaveBeenCalled();
114
+ });
115
+
116
+ test("should log error when failing to read the file", async () => {
117
+ // Mock a situation where the file exists but reading it fails
118
+ const mockFileResultMap = { "TestKind-v1.ts": mockCRDResults[0] };
119
+
120
+ // Simulate readFile throwing an error
121
+ jest.spyOn(mockFileSystem, "readFile").mockImplementation(() => {
122
+ throw new Error("File read error");
123
+ });
124
+
125
+ await postProcessingModule.processFiles(
126
+ ["TestKind-v1.ts"],
127
+ mockFileResultMap,
128
+ mockOpts,
129
+ mockFileSystem,
130
+ );
131
+
132
+ // Verify the error log
133
+ expect(mockOpts.logFn).toHaveBeenCalledWith(
134
+ "❌ Error processing file: mockDir/TestKind-v1.ts - File read error",
135
+ );
136
+ });
137
+
138
+ test("should log start and completion messages", async () => {
139
+ jest.spyOn(mockFileSystem, "readdirSync").mockReturnValue(["TestKind-v1.ts"]);
140
+ jest
141
+ .spyOn(postProcessingModule, "mapFilesToCRD")
142
+ .mockReturnValue({ "TestKind-v1.ts": mockCRDResults[0] });
143
+ //jest.spyOn(postProcessingModule, "processFiles").mockImplementation(() => Promise.resolve());
144
+
145
+ await postProcessingModule.postProcessing(mockCRDResults, mockOpts, mockFileSystem);
146
+
147
+ // Verify the start message was logged
148
+ expect(mockOpts.logFn).toHaveBeenCalledWith("\n🔧 Post-processing started...");
149
+
150
+ // Verify the completion message was logged
151
+ expect(mockOpts.logFn).toHaveBeenCalledWith("🔧 Post-processing completed.\n");
152
+ });
153
+
154
+ test("should handle readdirSync error gracefully", async () => {
155
+ // Simulate an error when reading the directory
156
+ jest.spyOn(mockFileSystem, "readdirSync").mockImplementation(() => {
157
+ throw new Error("Directory read error");
158
+ });
159
+
160
+ await expect(
161
+ postProcessingModule.postProcessing(mockCRDResults, mockOpts, mockFileSystem),
162
+ ).rejects.toThrow("Directory read error");
163
+
164
+ // Ensure the process is not continued after the error
165
+ expect(mockOpts.logFn).not.toHaveBeenCalledWith("🔧 Post-processing completed.\n");
166
+ });
167
+ });
168
+
169
+ describe("mapFilesToCRD", () => {
170
+ beforeEach(() => {
171
+ jest.clearAllMocks(); // Clear mocks before each test
172
+ });
173
+
174
+ afterEach(() => {
175
+ jest.restoreAllMocks(); // Restore all mocks after each test
176
+ });
177
+
178
+ test("should map files to corresponding CRD results", () => {
179
+ const result = postProcessingModule.mapFilesToCRD(mockCRDResults);
180
+ expect(result).toEqual({
181
+ "TestKind-v1.ts": mockCRDResults[0],
182
+ });
183
+ });
184
+
185
+ test("should log a warning if no matching CRD result found for a file", async () => {
186
+ const mockFiles = ["NonExistingKind.ts"];
187
+ const mockFileResultMap = {};
188
+
189
+ await postProcessingModule.processFiles(mockFiles, mockFileResultMap, mockOpts, mockFileSystem);
190
+
191
+ expect(mockOpts.logFn).toHaveBeenCalledWith(
192
+ "⚠️ Warning: No matching CRD result found for file: mockDir/NonExistingKind.ts",
193
+ );
194
+ });
195
+ });
196
+
197
+ describe("applyCRDPostProcessing", () => {
198
+ const mockContent = "mock content";
199
+ const mockOpts = {
200
+ directory: "mockDir",
201
+ logFn: jest.fn(),
202
+ language: "ts",
203
+ plain: false,
204
+ npmPackage: "mockPackage",
205
+ source: "",
206
+ };
207
+
208
+ beforeEach(() => {
209
+ jest.clearAllMocks(); // Clear mocks before each test
210
+ });
211
+
212
+ afterEach(() => {
213
+ jest.restoreAllMocks(); // Restore all mocks after each test
214
+ });
215
+
216
+ test("should process TypeScript file content", () => {
217
+ const result = postProcessingModule.applyCRDPostProcessing(
218
+ mockContent,
219
+ "TestKind",
220
+ mockCRDResults[0].crd,
221
+ "v1",
222
+ mockOpts,
223
+ );
224
+
225
+ expect(result).toContain("mock content");
226
+ // Add more assertions based on what is expected after processing
227
+ });
228
+
229
+ test("should process TypeScript file content", () => {
230
+ const result = postProcessingModule.applyCRDPostProcessing(
231
+ mockContent,
232
+ "TestKind",
233
+ mockCRDResults[0].crd,
234
+ "v1",
235
+ mockOpts,
236
+ );
237
+
238
+ expect(result).toContain("mock content");
239
+ // Add more assertions based on what is expected after processing
240
+ });
241
+ });
242
+
243
+ describe("processFiles", () => {
244
+ const mockOptsWithoutDirectory = { ...mockOpts, directory: undefined };
245
+
246
+ beforeEach(() => {
247
+ jest.clearAllMocks(); // Clear mocks before each test
248
+ });
249
+
250
+ afterEach(() => {
251
+ jest.restoreAllMocks(); // Restore all mocks after each test
252
+ });
253
+
254
+ test("should process files in directory", async () => {
255
+ const mockFileResultMap = { "TestKind-v1.ts": mockCRDResults[0] };
256
+ jest.spyOn(mockFileSystem, "readFile").mockReturnValue("mock content");
257
+ jest.spyOn(mockFileSystem, "writeFile");
258
+
259
+ await postProcessingModule.processFiles(
260
+ ["TestKind-v1.ts"],
261
+ mockFileResultMap,
262
+ mockOpts,
263
+ mockFileSystem,
264
+ );
265
+
266
+ expect(mockFileSystem.readFile).toHaveBeenCalledWith("mockDir/TestKind-v1.ts");
267
+ expect(mockFileSystem.writeFile).toHaveBeenCalled();
268
+ });
269
+
270
+ test("should throw an error if directory is not defined", async () => {
271
+ const mockFiles = ["TestKind-v1.ts"];
272
+ const mockFileResultMap = { "TestKind-v1.ts": mockCRDResults[0] };
273
+
274
+ await expect(
275
+ postProcessingModule.processFiles(
276
+ mockFiles,
277
+ mockFileResultMap,
278
+ mockOptsWithoutDirectory,
279
+ mockFileSystem,
280
+ ),
281
+ ).rejects.toThrow("Directory is not defined");
282
+ });
283
+ });
284
+
285
+ describe("wrapWithFluentClient", () => {
286
+ /* const mockLines = ["line1", "line2"];
287
+ const mockName = "TestKind";
288
+ const mockCRD = {
289
+ spec: {
290
+ group: "test.group",
291
+ names: { kind: "TestKind", plural: "testkinds" },
292
+ scope: "Namespaced",
293
+ versions: [{ name: "v1", served: true, storage: true }],
294
+ },
295
+ };
296
+ const mockVersion = "v1";
297
+ const mockOpts = {
298
+ directory: "mockDir",
299
+ logFn: jest.fn(),
300
+ language: "ts",
301
+ plain: false,
302
+ npmPackage: "mockPackage",
303
+ source: "",
304
+ }; */
305
+
306
+ beforeEach(() => {
307
+ jest.clearAllMocks(); // Clear mocks before each test
308
+ });
309
+
310
+ test("should replace interface declaration with class extending GenericKind", () => {
311
+ const inputLines = ["export interface TestKind {", " prop: string;", "}"];
312
+
313
+ const crd = {
314
+ spec: {
315
+ group: "test.group",
316
+ names: { plural: "testkinds" },
317
+ },
318
+ } as CustomResourceDefinition; // mock the CRD
319
+
320
+ const expectedOutputLines = [
321
+ "// This file is auto-generated by mockPackage, do not edit manually",
322
+ 'import { GenericKind, RegisterKind } from "mockPackage";',
323
+ "export class TestKind extends GenericKind {",
324
+ " prop: string;",
325
+ "}",
326
+ "RegisterKind(TestKind, {",
327
+ ' group: "test.group",',
328
+ ' version: "v1",',
329
+ ' kind: "TestKind",',
330
+ ' plural: "testkinds",',
331
+ "});",
332
+ ];
333
+
334
+ const result = postProcessingModule.wrapWithFluentClient(
335
+ inputLines,
336
+ "TestKind",
337
+ crd,
338
+ "v1",
339
+ "mockPackage",
340
+ );
341
+
342
+ expect(result).toEqual(expectedOutputLines);
343
+ });
344
+ });
345
+
346
+ describe("getGenericKindProperties", () => {
347
+ beforeEach(() => {
348
+ jest.clearAllMocks(); // Clear mocks before each test
349
+ });
350
+
351
+ afterEach(() => {
352
+ jest.restoreAllMocks(); // Restore all mocks after each test
353
+ });
354
+
355
+ test("should retrieve properties from GenericKind", () => {
356
+ const result = postProcessingModule.getGenericKindProperties();
357
+ expect(result).toContain("kind");
358
+ expect(result).toContain("apiVersion");
359
+ expect(result).not.toContain("[key: string]");
360
+ });
361
+ });
362
+
363
+ describe("processLines", () => {
364
+ const mockLines = ["export class TestKind extends GenericKind {", " kind: string;", "}"];
365
+
366
+ const mockFoundInterfaces = new Set<string>(["TestKind"]);
367
+ const mockGenericKindProperties = ["kind", "apiVersion"];
368
+
369
+ beforeEach(() => {
370
+ jest.clearAllMocks(); // Clear mocks before each test
371
+ });
372
+
373
+ afterEach(() => {
374
+ jest.restoreAllMocks(); // Restore all mocks after each test
375
+ });
376
+
377
+ test("should process lines and modify properties of classes extending GenericKind", () => {
378
+ const result = postProcessingModule.processLines(
379
+ mockLines,
380
+ mockGenericKindProperties,
381
+ mockFoundInterfaces,
382
+ );
383
+ expect(result).toContain(" declare kind: string;");
384
+ });
385
+ });
386
+
387
+ describe("processClassContext", () => {
388
+ const mockGenericKindProperties = ["kind"];
389
+ const mockFoundInterfaces = new Set<string>();
390
+
391
+ beforeEach(() => {
392
+ jest.clearAllMocks(); // Clear mocks before each test
393
+ });
394
+
395
+ afterEach(() => {
396
+ jest.restoreAllMocks(); // Restore all mocks after each test
397
+ });
398
+
399
+ test("should detect class extending GenericKind and modify context", () => {
400
+ const line = "export class TestKind extends GenericKind {";
401
+ const result = postProcessingModule.processClassContext(
402
+ line,
403
+ false,
404
+ 0,
405
+ mockGenericKindProperties,
406
+ mockFoundInterfaces,
407
+ );
408
+ expect(result.insideClass).toBe(true);
409
+ expect(result.braceBalance).toBe(1);
410
+ });
411
+
412
+ test("should update brace balance when closing braces are found", () => {
413
+ const line = "}";
414
+ const result = postProcessingModule.processClassContext(
415
+ line,
416
+ true,
417
+ 1,
418
+ mockGenericKindProperties,
419
+ mockFoundInterfaces,
420
+ );
421
+ expect(result.insideClass).toBe(false);
422
+ expect(result.braceBalance).toBe(0);
423
+ });
424
+ });
425
+
426
+ describe("normalizeIndentationAndSpacing", () => {
427
+ const mockOpts = {
428
+ language: "ts",
429
+ source: "",
430
+ logFn: jest.fn(),
431
+ };
432
+
433
+ beforeEach(() => {
434
+ jest.clearAllMocks(); // Clear mocks before each test
435
+ });
436
+
437
+ afterEach(() => {
438
+ jest.restoreAllMocks(); // Restore all mocks after each test
439
+ });
440
+
441
+ test("should normalize indentation to two spaces", () => {
442
+ const mockLines = [
443
+ " indentedWithFourSpaces: string;", // Line with 4 spaces, should be normalized
444
+ " alreadyTwoSpaces: string;", // Line with 2 spaces, should remain unchanged
445
+ " sixSpacesIndent: string;", // Line with 6 spaces, only first 4 should be normalized
446
+ "noIndent: string;", // Line with no indentation, should remain unchanged
447
+ ];
448
+
449
+ const expectedResult = [
450
+ " indentedWithFourSpaces: string;", // Normalized to 2 spaces
451
+ " alreadyTwoSpaces: string;", // No change
452
+ " sixSpacesIndent: string;", // Only first 4 spaces should be normalized to 2
453
+ "noIndent: string;", // No change
454
+ ];
455
+
456
+ const result = postProcessingModule.normalizeIndentation(mockLines);
457
+
458
+ expect(result).toEqual(expectedResult);
459
+ });
460
+
461
+ test("should normalize single line indentation to two spaces", () => {
462
+ const cases = [
463
+ { input: " indentedWithFourSpaces;", expected: " indentedWithFourSpaces;" }, // 4 spaces to 2 spaces
464
+ { input: " alreadyTwoSpaces;", expected: " alreadyTwoSpaces;" }, // 2 spaces, no change
465
+ { input: " sixSpacesIndent;", expected: " sixSpacesIndent;" }, // First 4 spaces to 2
466
+ { input: "noIndent;", expected: "noIndent;" }, // No indentation, no change
467
+ ];
468
+
469
+ cases.forEach(({ input, expected }) => {
470
+ const result = postProcessingModule.normalizeLineIndentation(input);
471
+ expect(result).toBe(expected);
472
+ });
473
+ });
474
+
475
+ test("should normalize property spacing", () => {
476
+ const cases = [
477
+ {
478
+ input: "optionalProp ? : string;",
479
+ expected: "optionalProp?: string;",
480
+ }, // Extra spaces around ? and :
481
+ {
482
+ input: "optionalProp?: string;",
483
+ expected: "optionalProp?: string;",
484
+ }, // Already normalized
485
+ {
486
+ input: "optionalProp ? :string;",
487
+ expected: "optionalProp?: string;",
488
+ }, // No space after colon
489
+ {
490
+ input: "nonOptionalProp: string;",
491
+ expected: "nonOptionalProp: string;",
492
+ }, // Non-optional property, should remain unchanged
493
+ ];
494
+
495
+ const inputLines = cases.map(c => c.input);
496
+ const expectedLines = cases.map(c => c.expected);
497
+
498
+ const result = postProcessingModule.normalizePropertySpacing(inputLines);
499
+
500
+ expect(result).toEqual(expectedLines);
501
+ });
502
+
503
+ test('should remove lines containing "[property: string]: any;" when language is "ts" or "typescript"', () => {
504
+ const inputLines = [
505
+ "someProp: string;",
506
+ "[property: string]: any;",
507
+ "anotherProp: number;",
508
+ "[property: string]: any;",
509
+ ];
510
+
511
+ // Test for TypeScript
512
+ const tsOpts: GenerateOptions = { ...mockOpts, language: "ts" };
513
+ const resultTs = postProcessingModule.removePropertyStringAny(inputLines, tsOpts);
514
+ const expectedTs = ["someProp: string;", "anotherProp: number;"];
515
+ expect(resultTs).toEqual(expectedTs);
516
+
517
+ // Test for TypeScript with "typescript" as language
518
+ const typescriptOpts: GenerateOptions = { ...mockOpts, language: "typescript" };
519
+ const resultTypescript = postProcessingModule.removePropertyStringAny(
520
+ inputLines,
521
+ typescriptOpts,
522
+ );
523
+ expect(resultTypescript).toEqual(expectedTs);
524
+ });
525
+
526
+ describe("processEslintDisable", () => {
527
+ beforeEach(() => {
528
+ jest.clearAllMocks(); // Clear mocks before each test
529
+ });
530
+
531
+ afterEach(() => {
532
+ jest.restoreAllMocks(); // Restore all mocks after each test
533
+ });
534
+
535
+ test('should add ESLint disable comment if line contains "[key: string]: any" and is not part of genericKindProperties', () => {
536
+ const line = "[key: string]: any;";
537
+ const genericKindProperties = ["kind", "apiVersion"]; // No "[key: string]" present
538
+
539
+ const result = postProcessingModule.processEslintDisable(line, genericKindProperties);
540
+
541
+ expect(result).toEqual(
542
+ " // eslint-disable-next-line @typescript-eslint/no-explicit-any\n[key: string]: any;",
543
+ );
544
+ });
545
+
546
+ test('should not add ESLint disable comment if "[key: string]" is in genericKindProperties', () => {
547
+ const line = "[key: string]: any;";
548
+ const genericKindProperties = ["[key: string]", "kind", "apiVersion"]; // "[key: string]" present
549
+
550
+ const result = postProcessingModule.processEslintDisable(line, genericKindProperties);
551
+
552
+ expect(result).toEqual("[key: string]: any;"); // No comment added
553
+ });
554
+
555
+ test('should not add ESLint disable comment if line does not contain "[key: string]: any"', () => {
556
+ const line = "prop: string;";
557
+ const genericKindProperties = ["kind", "apiVersion"]; // Normal properties
558
+
559
+ const result = postProcessingModule.processEslintDisable(line, genericKindProperties);
560
+
561
+ expect(result).toEqual("prop: string;"); // No change in the line
562
+ });
563
+
564
+ test('should not add ESLint disable comment if line contains "[key: string]: any" but is part of genericKindProperties', () => {
565
+ const line = "[key: string]: any;";
566
+ const genericKindProperties = ["[key: string]"];
567
+
568
+ const result = postProcessingModule.processEslintDisable(line, genericKindProperties);
569
+
570
+ expect(result).toEqual("[key: string]: any;"); // No comment added since it's in genericKindProperties
571
+ });
572
+ });
573
+
574
+ test('should not remove lines when language is not "ts" or "typescript"', () => {
575
+ const inputLines = ["someProp: string;", "[property: string]: any;", "anotherProp: number;"];
576
+
577
+ // Test for other languages
578
+ const otherOpts: GenerateOptions = { ...mockOpts, language: "js" }; // Not TypeScript
579
+ const resultOther = postProcessingModule.removePropertyStringAny(inputLines, otherOpts);
580
+ expect(resultOther).toEqual(inputLines); // Should return the original lines
581
+ });
582
+ });
583
+
584
+ describe("makePropertiesOptional", () => {
585
+ beforeEach(() => {
586
+ jest.clearAllMocks(); // Clear mocks before each test
587
+ });
588
+
589
+ afterEach(() => {
590
+ jest.restoreAllMocks(); // Restore all mocks after each test
591
+ });
592
+
593
+ test("should make property optional if type is found in interfaces and not already optional", () => {
594
+ const line = "myProp: MyInterface;";
595
+ const foundInterfaces = new Set(["MyInterface"]); // Matching interface
596
+
597
+ const result = postProcessingModule.makePropertiesOptional(line, foundInterfaces);
598
+
599
+ expect(result).toEqual("myProp?: MyInterface;"); // The colon is replaced by `?:`
600
+ });
601
+
602
+ test("should not make property optional if type is not found in interfaces", () => {
603
+ const line = "myProp: AnotherType;";
604
+ const foundInterfaces = new Set(["MyInterface"]); // No match for this type
605
+
606
+ const result = postProcessingModule.makePropertiesOptional(line, foundInterfaces);
607
+
608
+ expect(result).toEqual("myProp: AnotherType;"); // No change
609
+ });
610
+
611
+ test("should not make property optional if already optional", () => {
612
+ const line = "myProp?: MyInterface;";
613
+ const foundInterfaces = new Set(["MyInterface"]); // Matching interface, but already optional
614
+
615
+ const result = postProcessingModule.makePropertiesOptional(line, foundInterfaces);
616
+
617
+ expect(result).toEqual("myProp?: MyInterface;"); // No change since it's already optional
618
+ });
619
+
620
+ test("should not change line if it does not match the property pattern", () => {
621
+ const line = "function test() {}";
622
+ const foundInterfaces = new Set(["MyInterface"]); // Matching interface, but the line is not a property
623
+
624
+ const result = postProcessingModule.makePropertiesOptional(line, foundInterfaces);
625
+
626
+ expect(result).toEqual("function test() {}"); // No change
627
+ });
628
+ });
629
+
630
+ describe("collectInterfaceNames", () => {
631
+ beforeEach(() => {
632
+ jest.clearAllMocks(); // Clear mocks before each test
633
+ });
634
+
635
+ afterEach(() => {
636
+ jest.restoreAllMocks(); // Restore all mocks after each test
637
+ });
638
+
639
+ test("should collect interface names from lines", () => {
640
+ const lines = [
641
+ "export interface MyInterface {",
642
+ "export interface AnotherInterface {",
643
+ "some other line",
644
+ "export interface YetAnotherInterface {",
645
+ ];
646
+
647
+ const result = postProcessingModule.collectInterfaceNames(lines);
648
+
649
+ expect(result).toEqual(new Set(["MyInterface", "AnotherInterface", "YetAnotherInterface"]));
650
+ });
651
+
652
+ test("should return an empty set if no interfaces are found", () => {
653
+ const lines = ["some other line", "function test() {}", "const value = 42;"];
654
+
655
+ const result = postProcessingModule.collectInterfaceNames(lines);
656
+
657
+ expect(result).toEqual(new Set());
658
+ });
659
+
660
+ test("should not add duplicate interface names", () => {
661
+ const lines = ["export interface MyInterface {", "export interface MyInterface {"];
662
+
663
+ const result = postProcessingModule.collectInterfaceNames(lines);
664
+
665
+ expect(result).toEqual(new Set(["MyInterface"]));
666
+ });
667
+ });
668
+
669
+ describe("writeFile", () => {
670
+ const mockFilePath = "test/path/to/file.ts";
671
+ const mockContent = "export const test = 'Test content';";
672
+
673
+ beforeEach(() => {
674
+ jest.clearAllMocks(); // Clear mocks before each test
675
+ });
676
+
677
+ afterEach(() => {
678
+ jest.restoreAllMocks(); // Restore all mocks after each test
679
+ });
680
+
681
+ test("should write file content successfully", () => {
682
+ // Simulate fs.writeFileSync working as expected
683
+ (fs.writeFileSync as jest.Mock).mockImplementation(() => {});
684
+
685
+ // Call the function
686
+ postProcessingModule.writeFile(mockFilePath, mockContent);
687
+
688
+ // Assert that writeFileSync was called with the correct arguments
689
+ expect(fs.writeFileSync).toHaveBeenCalledWith(mockFilePath, mockContent, "utf8");
690
+ });
691
+
692
+ test("should throw an error when writeFileSync fails", () => {
693
+ // Simulate fs.writeFileSync throwing an error
694
+ (fs.writeFileSync as jest.Mock).mockImplementation(() => {
695
+ throw new Error("File write error");
696
+ });
697
+
698
+ // Expect the function to throw the error
699
+ expect(() => postProcessingModule.writeFile(mockFilePath, mockContent)).toThrow(
700
+ `Failed to write file at ${mockFilePath}: File write error`,
701
+ );
702
+ });
703
+ });
704
+
705
+ describe("readFile", () => {
706
+ const mockFilePath = "test/path/to/file.ts";
707
+ const mockContent = "export const test = 'Test content';";
708
+
709
+ beforeEach(() => {
710
+ jest.clearAllMocks(); // Clear mocks before each test
711
+ });
712
+
713
+ afterEach(() => {
714
+ jest.restoreAllMocks(); // Restore all mocks after each test
715
+ });
716
+
717
+ test("should read file content successfully", () => {
718
+ // Simulate fs.readFileSync returning content
719
+ (fs.readFileSync as jest.Mock).mockReturnValue(mockContent);
720
+
721
+ // Call the function
722
+ const result = postProcessingModule.readFile(mockFilePath);
723
+
724
+ // Assert that readFileSync was called with the correct arguments
725
+ expect(fs.readFileSync).toHaveBeenCalledWith(mockFilePath, "utf8");
726
+
727
+ // Assert that the result matches the mock content
728
+ expect(result).toBe(mockContent);
729
+ });
730
+
731
+ test("should throw an error when readFileSync fails", () => {
732
+ // Simulate fs.readFileSync throwing an error
733
+ (fs.readFileSync as jest.Mock).mockImplementation(() => {
734
+ throw new Error("File read error");
735
+ });
736
+
737
+ // Expect the function to throw the error
738
+ expect(() => postProcessingModule.readFile(mockFilePath)).toThrow(
739
+ `Failed to read file at ${mockFilePath}: File read error`,
740
+ );
741
+ });
742
+ });