@fairmint/canton-node-sdk 0.0.222 → 0.0.224

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 (68) hide show
  1. package/bin/canton-localnet +182 -0
  2. package/build/src/generated/apps/common/src/main/openapi/common-external.d.ts +295 -0
  3. package/build/src/generated/apps/common/src/main/openapi/common-external.d.ts.map +1 -0
  4. package/build/src/generated/apps/common/src/main/openapi/common-external.js +7 -0
  5. package/build/src/generated/apps/common/src/main/openapi/common-external.js.map +1 -0
  6. package/build/src/generated/apps/common/src/main/openapi/common-internal.d.ts +204 -0
  7. package/build/src/generated/apps/common/src/main/openapi/common-internal.d.ts.map +1 -0
  8. package/build/src/generated/apps/common/src/main/openapi/common-internal.js +7 -0
  9. package/build/src/generated/apps/common/src/main/openapi/common-internal.js.map +1 -0
  10. package/build/src/generated/apps/scan/src/main/openapi/scan-stream-server.d.ts +73 -0
  11. package/build/src/generated/apps/scan/src/main/openapi/scan-stream-server.d.ts.map +1 -0
  12. package/build/src/generated/apps/scan/src/main/openapi/scan-stream-server.js +7 -0
  13. package/build/src/generated/apps/scan/src/main/openapi/scan-stream-server.js.map +1 -0
  14. package/build/src/generated/apps/scan/src/main/openapi/scan.d.ts +4969 -0
  15. package/build/src/generated/apps/scan/src/main/openapi/scan.d.ts.map +1 -0
  16. package/build/src/generated/apps/scan/src/main/openapi/scan.js +7 -0
  17. package/build/src/generated/apps/scan/src/main/openapi/scan.js.map +1 -0
  18. package/build/src/generated/apps/splitwell/src/main/openapi/splitwell-internal.d.ts +636 -0
  19. package/build/src/generated/apps/splitwell/src/main/openapi/splitwell-internal.d.ts.map +1 -0
  20. package/build/src/generated/apps/splitwell/src/main/openapi/splitwell-internal.js +7 -0
  21. package/build/src/generated/apps/splitwell/src/main/openapi/splitwell-internal.js.map +1 -0
  22. package/build/src/generated/apps/sv/src/main/openapi/sv-internal.d.ts +2024 -0
  23. package/build/src/generated/apps/sv/src/main/openapi/sv-internal.d.ts.map +1 -0
  24. package/build/src/generated/apps/sv/src/main/openapi/sv-internal.js +7 -0
  25. package/build/src/generated/apps/sv/src/main/openapi/sv-internal.js.map +1 -0
  26. package/build/src/generated/apps/validator/src/main/openapi/ans-external.d.ts +189 -0
  27. package/build/src/generated/apps/validator/src/main/openapi/ans-external.d.ts.map +1 -0
  28. package/build/src/generated/apps/validator/src/main/openapi/ans-external.js +7 -0
  29. package/build/src/generated/apps/validator/src/main/openapi/ans-external.js.map +1 -0
  30. package/build/src/generated/apps/validator/src/main/openapi/scan-proxy.d.ts +969 -0
  31. package/build/src/generated/apps/validator/src/main/openapi/scan-proxy.d.ts.map +1 -0
  32. package/build/src/generated/apps/validator/src/main/openapi/scan-proxy.js +7 -0
  33. package/build/src/generated/apps/validator/src/main/openapi/scan-proxy.js.map +1 -0
  34. package/build/src/generated/apps/validator/src/main/openapi/validator-internal.d.ts +1230 -0
  35. package/build/src/generated/apps/validator/src/main/openapi/validator-internal.d.ts.map +1 -0
  36. package/build/src/generated/apps/validator/src/main/openapi/validator-internal.js +7 -0
  37. package/build/src/generated/apps/validator/src/main/openapi/validator-internal.js.map +1 -0
  38. package/build/src/generated/apps/wallet/src/main/openapi/wallet-external.d.ts +535 -0
  39. package/build/src/generated/apps/wallet/src/main/openapi/wallet-external.d.ts.map +1 -0
  40. package/build/src/generated/apps/wallet/src/main/openapi/wallet-external.js +7 -0
  41. package/build/src/generated/apps/wallet/src/main/openapi/wallet-external.js.map +1 -0
  42. package/build/src/generated/apps/wallet/src/main/openapi/wallet-internal.d.ts +2521 -0
  43. package/build/src/generated/apps/wallet/src/main/openapi/wallet-internal.d.ts.map +1 -0
  44. package/build/src/generated/apps/wallet/src/main/openapi/wallet-internal.js +7 -0
  45. package/build/src/generated/apps/wallet/src/main/openapi/wallet-internal.js.map +1 -0
  46. package/package.json +18 -8
  47. package/scripts/localnet-cloud.sh +180 -17
  48. package/.gitmodules +0 -6
  49. package/.nvmrc +0 -1
  50. package/scripts/collapse-manifest.ts +0 -66
  51. package/scripts/generate-all-client-methods.ts +0 -332
  52. package/scripts/generate-openapi-types.ts +0 -46
  53. package/scripts/generate-operation-tests.ts +0 -198
  54. package/scripts/grant-user-rights.ts +0 -213
  55. package/scripts/list-identity-providers.ts +0 -46
  56. package/scripts/localnet/localnet-cloud.sh +0 -5
  57. package/scripts/localnet/localnet-status.sh +0 -5
  58. package/scripts/localnet/setup-localnet.sh +0 -5
  59. package/scripts/localnet/setup-quickstart-localnet.sh +0 -5
  60. package/scripts/localnet/start-localnet.sh +0 -5
  61. package/scripts/localnet/stop-localnet.sh +0 -5
  62. package/scripts/localnet-status.sh +0 -5
  63. package/scripts/prepare-release.ts +0 -222
  64. package/scripts/revoke-user-rights.ts +0 -219
  65. package/scripts/setup-localnet.sh +0 -5
  66. package/scripts/setup-quickstart-localnet.sh +0 -5
  67. package/scripts/start-localnet.sh +0 -5
  68. package/scripts/stop-localnet.sh +0 -5
@@ -1,332 +0,0 @@
1
- #!/usr/bin/env ts-node
2
-
3
- import * as fs from 'fs';
4
- import * as path from 'path';
5
-
6
- interface ClientConfig {
7
- name: string;
8
- clientFile: string;
9
- operationsDir: string;
10
- baseClass: string;
11
- }
12
-
13
- const CLIENTS: ClientConfig[] = [
14
- {
15
- name: 'ValidatorApiClient',
16
- clientFile: path.join(__dirname, '../src/clients/validator-api/ValidatorApiClient.generated.ts'),
17
- operationsDir: path.join(__dirname, '../src/clients/validator-api/operations/v0'),
18
- baseClass: 'BaseClient',
19
- },
20
- {
21
- name: 'LedgerJsonApiClient',
22
- clientFile: path.join(__dirname, '../src/clients/ledger-json-api/LedgerJsonApiClient.generated.ts'),
23
- operationsDir: path.join(__dirname, '../src/clients/ledger-json-api/operations/v2'),
24
- baseClass: 'BaseClient',
25
- },
26
- {
27
- name: 'ScanApiClient',
28
- clientFile: path.join(__dirname, '../src/clients/scan-api/ScanApiClient.generated.ts'),
29
- operationsDir: path.join(__dirname, '../src/clients/scan-api/operations/v0'),
30
- baseClass: 'BaseClient',
31
- },
32
- ];
33
-
34
- // Recursively get all .ts files in a directory
35
- function getAllTsFiles(dir: string): string[] {
36
- let results: string[] = [];
37
- const list = fs.readdirSync(dir);
38
- list.forEach((file) => {
39
- const filePath = path.join(dir, file);
40
- const stat = fs.statSync(filePath);
41
- if (stat.isDirectory()) {
42
- results = results.concat(getAllTsFiles(filePath));
43
- } else if (file.endsWith('.ts')) {
44
- results.push(filePath);
45
- }
46
- });
47
- return results;
48
- }
49
-
50
- type OperationInfo =
51
- | {
52
- kind: 'api';
53
- operationName: string;
54
- paramsType: string;
55
- responseType: string;
56
- methodName?: string | undefined;
57
- jsdoc?: string | undefined;
58
- }
59
- | {
60
- kind: 'ws';
61
- operationName: string;
62
- paramsType: string;
63
- requestType: string;
64
- messageType: string;
65
- jsdoc?: string | undefined;
66
- };
67
-
68
- /** Extracts JSDoc comment that directly precedes an export declaration (no code between them) */
69
- function extractJsDoc(fileContent: string, operationName: string): string | undefined {
70
- // Match JSDoc comment that is immediately before export const/class (only whitespace allowed between)
71
- // Must not have any code between the JSDoc closing */ and the export
72
- const jsdocRegex = new RegExp(`(/\\*\\*(?:(?!\\*/).)*.?\\*/)\\s*\\nexport (?:const|class) ${operationName}\\b`, 's');
73
- const match = jsdocRegex.exec(fileContent);
74
- const jsdoc = match?.[1];
75
-
76
- // Validate it's actually a JSDoc (starts with /** and ends with */)
77
- if (jsdoc && jsdoc.startsWith('/**') && jsdoc.includes('*/')) {
78
- // Make sure it doesn't contain code-like patterns that indicate it's not a pure JSDoc
79
- if (jsdoc.includes('z.string()') || jsdoc.includes('z.object(')) {
80
- return undefined;
81
- }
82
- return jsdoc;
83
- }
84
- return undefined;
85
- }
86
-
87
- /** Generate a human-readable description from an operation name */
88
- function generateDescriptionFromName(operationName: string): string {
89
- // Split camelCase/PascalCase into words
90
- const words = operationName
91
- .replace(/([A-Z])/g, ' $1')
92
- .trim()
93
- .toLowerCase()
94
- .split(' ');
95
-
96
- // Handle common prefixes
97
- const prefix = words[0];
98
- const rest = words.slice(1).join(' ');
99
-
100
- if (prefix === 'get') return `Gets ${rest}`;
101
- if (prefix === 'list') return `Lists ${rest}`;
102
- if (prefix === 'create') return `Creates ${rest}`;
103
- if (prefix === 'delete') return `Deletes ${rest}`;
104
- if (prefix === 'update') return `Updates ${rest}`;
105
- if (prefix === 'submit') return `Submits ${rest}`;
106
- if (prefix === 'subscribe') return `Subscribes to ${rest}`;
107
- if (prefix === 'allocate') return `Allocates ${rest}`;
108
- if (prefix === 'lookup') return `Looks up ${rest}`;
109
- if (prefix === 'revoke') return `Revokes ${rest}`;
110
- if (prefix === 'grant') return `Grants ${rest}`;
111
-
112
- // Default: just capitalize first letter
113
- return (
114
- operationName.charAt(0).toUpperCase() +
115
- operationName
116
- .slice(1)
117
- .replace(/([A-Z])/g, ' $1')
118
- .trim()
119
- );
120
- }
121
-
122
- // Extract operation info from a file (supports REST, WebSocket, and class-based operations)
123
- function extractOperationInfos(fileContent: string): OperationInfo[] {
124
- const results: OperationInfo[] = [];
125
-
126
- // REST operations: createApiOperation or createSimpleApiOperation
127
- const apiRegex = /export const (\w+)\s*=\s*create(?:Simple)?ApiOperation<\s*([^,]+),\s*([^>]+)\s*>/gs;
128
- for (const match of fileContent.matchAll(apiRegex)) {
129
- const operationName = match[1];
130
- const paramsType = match[2];
131
- const responseType = match[3];
132
- if (operationName && paramsType && responseType) {
133
- results.push({
134
- kind: 'api',
135
- operationName,
136
- paramsType: paramsType.trim(),
137
- responseType: responseType.trim(),
138
- jsdoc: extractJsDoc(fileContent, operationName),
139
- });
140
- }
141
- }
142
-
143
- // Class-based operations: export class OperationName { ... execute(...) }
144
- const classRegex = /export class (\w+)[^{]*{[\s\S]*?public async (execute|connect)\(/s;
145
- const classMatch = classRegex.exec(fileContent);
146
- if (classMatch?.[1]) {
147
- const methodName = classMatch[2]; // 'execute' or 'connect'
148
- results.push({
149
- kind: 'api',
150
- operationName: classMatch[1],
151
- paramsType: 'unknown',
152
- responseType: 'unknown',
153
- ...(methodName && { methodName }), // Only include if defined
154
- jsdoc: extractJsDoc(fileContent, classMatch[1]),
155
- });
156
- }
157
-
158
- // WebSocket operations: createWebSocketOperation<Params, RequestMessage, InboundMessage>
159
- // Note: Generic types can contain nested angle brackets (e.g., z.infer<...>),
160
- // which are hard to parse reliably with a simple regex. We only need the
161
- // operation name for generation, so match by name and ignore generic details.
162
- const wsNameRegex = /export const (\w+)\s*=\s*createWebSocketOperation</s;
163
- const wsNameMatch = wsNameRegex.exec(fileContent);
164
- if (wsNameMatch?.[1]) {
165
- results.push({
166
- kind: 'ws',
167
- operationName: wsNameMatch[1],
168
- paramsType: 'unknown',
169
- requestType: 'unknown',
170
- messageType: 'unknown',
171
- jsdoc: extractJsDoc(fileContent, wsNameMatch[1]),
172
- });
173
- }
174
-
175
- return results;
176
- }
177
-
178
- function relativeImportPath(from: string, to: string): string {
179
- let rel = path.relative(path.dirname(from), to).replace(/\\/g, '/');
180
- if (!rel.startsWith('.')) rel = `./${rel}`;
181
- return rel.replace(/\.ts$/, '');
182
- }
183
-
184
- /** Extract the first line of a JSDoc comment (the description) */
185
- function extractJsDocDescription(jsdoc: string | undefined): string | undefined {
186
- if (!jsdoc) return undefined;
187
-
188
- // Remove /** and */ and trim
189
- const content = jsdoc
190
- .replace(/^\/\*\*\s*/s, '')
191
- .replace(/\s*\*\/$/s, '')
192
- .split('\n')
193
- .map((line) => line.replace(/^\s*\*\s?/, ''))
194
- .join('\n')
195
- .trim();
196
-
197
- // Get first paragraph (up to first blank line or @tag)
198
- const firstParagraph = content.split(/\n\s*\n|\n\s*@/)[0];
199
- return firstParagraph?.trim();
200
- }
201
-
202
- function generateMethodDeclarations(ops: Array<OperationInfo & { importPath: string }>): string {
203
- return ops
204
- .map((op) => {
205
- const methodName = operationNameToMethodName(op.operationName);
206
- const description = extractJsDocDescription(op.jsdoc) ?? generateDescriptionFromName(op.operationName);
207
-
208
- // Generate JSDoc for the method
209
- const jsdocComment = ` /** ${description} */`;
210
-
211
- if (op.kind === 'api') {
212
- const opMethodName = op.methodName ?? 'execute';
213
- const methodParamsType = `Parameters<InstanceType<typeof ${op.operationName}>['${opMethodName}']>[0]`;
214
- const methodReturnType = `ReturnType<InstanceType<typeof ${op.operationName}>['${opMethodName}']>`;
215
- if (op.paramsType === 'void') {
216
- return `${jsdocComment}\n public ${methodName}!: () => ${methodReturnType};`;
217
- }
218
- return `${jsdocComment}\n public ${methodName}!: (params: ${methodParamsType}) => ${methodReturnType};`;
219
- }
220
- // WebSocket subscribe methods
221
- const paramsType = `Parameters<InstanceType<typeof ${op.operationName}>['subscribe']>[0]`;
222
- const messageType = `Parameters<Parameters<InstanceType<typeof ${op.operationName}>['subscribe']>[1]['onMessage']>[0]`;
223
- return `${jsdocComment}\n public ${methodName}!: (params: ${paramsType}, handlers: WebSocketHandlers<${messageType}>) => Promise<WebSocketSubscription>;`;
224
- })
225
- .join('\n');
226
- }
227
-
228
- function generateMethodImplementations(ops: Array<OperationInfo & { importPath: string }>): string {
229
- return ops
230
- .map((op) => {
231
- const methodName = operationNameToMethodName(op.operationName);
232
- if (op.kind === 'api') {
233
- const params = op.paramsType === 'void' ? '' : 'params';
234
- const opMethodName = op.methodName ?? 'execute';
235
- return ` this.${methodName} = (${params}) => new ${op.operationName}(this).${opMethodName}(${params});`;
236
- }
237
- return ` this.${methodName} = (params, handlers) => new ${op.operationName}(this).subscribe(params, handlers);`;
238
- })
239
- .join('\n');
240
- }
241
-
242
- function generateOperationImports(opFiles: Array<{ operationName: string; importPath: string }>): string {
243
- return opFiles.map((op) => `import { ${op.operationName} } from '${op.importPath}';`).join('\n');
244
- }
245
-
246
- // Convert operation name to method name (e.g., GetEventsByContractId -> getEventsByContractId)
247
- function operationNameToMethodName(operationName: string): string {
248
- return operationName.charAt(0).toLowerCase() + operationName.slice(1);
249
- }
250
-
251
- function generateClientFile(clientConfig: ClientConfig): void {
252
- const { name, clientFile, operationsDir, baseClass } = clientConfig;
253
-
254
- // 1. Scan all operation files
255
- const files = getAllTsFiles(operationsDir);
256
-
257
- const allOps = files
258
- .map((file) => {
259
- const content = fs.readFileSync(file, 'utf8');
260
- return {
261
- infos: extractOperationInfos(content),
262
- importPath: relativeImportPath(clientFile, file),
263
- };
264
- })
265
- .flatMap((item) => item.infos.map((info) => ({ ...info, importPath: item.importPath })));
266
-
267
- if (allOps.length === 0) {
268
- return;
269
- }
270
-
271
- // 2. Generate method declarations, implementations, and imports
272
- const methodDecls = generateMethodDeclarations(allOps);
273
- const methodImpls = generateMethodImplementations(allOps);
274
- const opImports = generateOperationImports(allOps);
275
-
276
- // 3. Generate the complete client file content
277
- const baseClassImport = baseClass === 'SimpleBaseClient' ? 'SimpleBaseClient' : 'BaseClient';
278
- const baseClassPath = baseClass === 'SimpleBaseClient' ? '../../core' : '../../core';
279
-
280
- // Fix API type for ledger client
281
- const apiType =
282
- name === 'LedgerJsonApiClient'
283
- ? 'LEDGER_JSON_API'
284
- : name === 'ValidatorApiClient'
285
- ? 'VALIDATOR_API'
286
- : name === 'ScanApiClient'
287
- ? 'SCAN_API'
288
- : undefined;
289
-
290
- const needsWsImports = allOps.some((op) => op.kind === 'ws');
291
- const wsImports = needsWsImports ? `\nimport { WebSocketHandlers, WebSocketSubscription } from '../../core/ws';` : '';
292
-
293
- const content = `import { ${baseClassImport}, CantonRuntime } from '${baseClassPath}';
294
- ${opImports}
295
- ${wsImports}
296
-
297
- /** Client for interacting with Canton's ${name.replace('Client', '')} */
298
- export class ${name} extends ${baseClass} {
299
- ${methodDecls}
300
-
301
- constructor(runtime: CantonRuntime) {
302
- super('${apiType}', runtime);
303
- this.initializeMethods();
304
- }
305
-
306
- private initializeMethods(): void {
307
- ${methodImpls}
308
- }
309
- }
310
- `;
311
-
312
- // 4. Write the generated file
313
- fs.writeFileSync(clientFile, content);
314
- }
315
-
316
- function generateAllClients(): void {
317
- console.log('Generating all client files...');
318
-
319
- CLIENTS.forEach((clientConfig) => {
320
- try {
321
- generateClientFile(clientConfig);
322
- } catch (_error) {
323
- console.error(`Error generating ${clientConfig.name}:`, _error);
324
- }
325
- });
326
-
327
- console.log('Client generation complete');
328
- }
329
-
330
- if (require.main === module) {
331
- generateAllClients();
332
- }
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env tsx
2
-
3
- import { execSync } from 'child_process';
4
- import * as fs from 'fs';
5
- import { glob } from 'glob';
6
- import * as path from 'path';
7
-
8
- function ensureDirectoryExists(filePath: string): void {
9
- const dir = path.dirname(filePath);
10
- if (!fs.existsSync(dir)) {
11
- fs.mkdirSync(dir, { recursive: true });
12
- }
13
- }
14
-
15
- function generateForFile(yamlPath: string): void {
16
- const relativePath = yamlPath.replace(/^libs\/splice\//, '').replace(/\.yaml$/, '.ts');
17
- const outputPath = path.join('src/generated', relativePath);
18
- ensureDirectoryExists(outputPath);
19
-
20
- execSync(`npx openapi-typescript "${yamlPath}" -o "${outputPath}"`, {
21
- stdio: 'inherit',
22
- });
23
- }
24
-
25
- function main(): void {
26
- const openapiUnderDir = glob.sync('libs/splice/**/openapi/**/*.yaml', {
27
- nodir: true,
28
- });
29
- const openapiRootFiles = glob.sync('libs/splice/**/openapi.yaml', {
30
- nodir: true,
31
- });
32
-
33
- // Merge and de-duplicate
34
- const allFiles = Array.from(new Set([...openapiUnderDir, ...openapiRootFiles]));
35
-
36
- if (allFiles.length === 0) {
37
- return;
38
- }
39
-
40
- // Generate types sequentially for clearer logs
41
- for (const file of allFiles) {
42
- generateForFile(file);
43
- }
44
- }
45
-
46
- main();
@@ -1,198 +0,0 @@
1
- #!/usr/bin/env tsx
2
-
3
- import * as fs from 'fs';
4
- import { glob } from 'glob';
5
- import * as path from 'path';
6
-
7
- interface OperationInfo {
8
- name: string;
9
- filePath: string;
10
- testPath: string;
11
- paramsSchema: string;
12
- method: string;
13
- endpoint: string;
14
- }
15
-
16
- async function findOperations(): Promise<OperationInfo[]> {
17
- const operations: OperationInfo[] = [];
18
-
19
- // Find all operation files
20
- const operationFiles = await glob('src/clients/**/*.ts', {
21
- ignore: ['**/index.ts', '**/register.ts', '**/schemas/**', '**/*.generated.ts'],
22
- });
23
-
24
- for (const file of operationFiles) {
25
- const content = fs.readFileSync(file, 'utf-8');
26
-
27
- // Check if this is an operation file by looking for createApiOperation
28
- if (content.includes('createApiOperation')) {
29
- const nameMatch = content.match(/export const (\w+) = createApiOperation/);
30
- if (nameMatch?.[1]) {
31
- const name = nameMatch[1];
32
- const methodMatch = content.match(/method:\s*['"`]([A-Z]+)['"`]/);
33
- const endpointMatch = content.match(/endpoint\s*=\s*['"`]([^'"`]+)['"`]/);
34
- const paramsSchemaMatch = content.match(/paramsSchema:\s*(\w+)/);
35
-
36
- const method = methodMatch?.[1] ?? 'GET';
37
- const endpoint = endpointMatch?.[1] ?? '/unknown';
38
- const paramsSchema = paramsSchemaMatch?.[1] ?? 'z.any()';
39
-
40
- // Generate test path
41
- const relativePath = path.relative('src', file);
42
- const testPath = `test/${relativePath.replace('.ts', '.test.ts')}`;
43
-
44
- operations.push({
45
- name,
46
- filePath: file,
47
- testPath,
48
- paramsSchema,
49
- method,
50
- endpoint,
51
- });
52
- }
53
- }
54
- }
55
-
56
- return operations;
57
- }
58
-
59
- function generateTestContent(operation: OperationInfo): string {
60
- const importPath = path
61
- .relative(path.dirname(operation.testPath), path.dirname(operation.filePath))
62
- .replace(/\\/g, '/');
63
- const mockClientPath = path.relative(path.dirname(operation.testPath), 'test/utils').replace(/\\/g, '/');
64
- const configPath = path.relative(path.dirname(operation.testPath), 'test/config').replace(/\\/g, '/');
65
-
66
- const isValidatorApi = operation.filePath.includes('validator-api');
67
-
68
- let mockClientClass = 'MockLedgerJsonApiClient';
69
- let apiUrlKey = 'LEDGER_JSON_API';
70
-
71
- if (isValidatorApi) {
72
- mockClientClass = 'MockValidatorApiClient';
73
- apiUrlKey = 'VALIDATOR_API';
74
- }
75
-
76
- return `import { ${mockClientClass} } from '${mockClientPath}';
77
- import { ${operation.name} } from '${importPath}/${operation.name}';
78
- import { mockClientConfig, mockApiUrls } from '${configPath}/testConfig';
79
-
80
- describe('${operation.name} Operation', () => {
81
- let mockClient: ${mockClientClass};
82
- let operation: InstanceType<typeof ${operation.name}>;
83
-
84
- beforeEach(() => {
85
- mockClient = new ${mockClientClass}(mockClientConfig);
86
- operation = new ${operation.name}(mockClient);
87
- mockClient.clearMocks();
88
- });
89
-
90
- describe('Network Activity Validation', () => {
91
- it('should make a ${operation.method} request to the correct endpoint', async () => {
92
- // Arrange
93
- const params = ${operation.paramsSchema === 'z.any()' ? '{}' : '{}'};
94
- const mockResponse = {};
95
- const expectedUrl = \`\${mockApiUrls.${apiUrlKey}}${operation.endpoint}\`;
96
-
97
- mockClient.setMockResponse(expectedUrl, mockResponse);
98
-
99
- // Act
100
- const result = await operation.execute(params);
101
-
102
- // Assert
103
- expect(result).toEqual(mockResponse);
104
-
105
- const requests = mockClient.getRequests();
106
- expect(requests).toHaveLength(1);
107
-
108
- const request = requests[0];
109
- expect(request).toBeDefined();
110
- expect(request!.method).toBe('${operation.method}');
111
- expect(request!.url).toBe(expectedUrl);
112
- });
113
-
114
- it('should include proper headers in the request', async () => {
115
- // Arrange
116
- const params = ${operation.paramsSchema === 'z.any()' ? '{}' : '{}'};
117
- const mockResponse = {};
118
- const expectedUrl = \`\${mockApiUrls.${apiUrlKey}}${operation.endpoint}\`;
119
-
120
- mockClient.setMockResponse(expectedUrl, mockResponse);
121
-
122
- // Act
123
- await operation.execute(params);
124
-
125
- // Assert
126
- const request = mockClient.getLastRequest();
127
- expect(request).toBeDefined();
128
- expect(request?.headers).toBeDefined();
129
- });
130
- });
131
-
132
- describe('Error Handling', () => {
133
- it('should propagate network errors', async () => {
134
- // Arrange
135
- const params = ${operation.paramsSchema === 'z.any()' ? '{}' : '{}'};
136
- const expectedUrl = \`\${mockApiUrls.${apiUrlKey}}${operation.endpoint}\`;
137
- const networkError = new Error('Network error');
138
-
139
- mockClient.setMockError(expectedUrl, networkError);
140
-
141
- // Act & Assert
142
- await expect(operation.execute(params)).rejects.toThrow('Network error');
143
-
144
- const requests = mockClient.getRequests();
145
- expect(requests).toHaveLength(1);
146
- });
147
- });
148
-
149
- describe('Parameter Validation', () => {
150
- it('should accept valid parameters', async () => {
151
- // Arrange
152
- const params = ${operation.paramsSchema === 'z.any()' ? '{}' : '{}'};
153
- const mockResponse = {};
154
- const expectedUrl = \`\${mockApiUrls.${apiUrlKey}}${operation.endpoint}\`;
155
-
156
- mockClient.setMockResponse(expectedUrl, mockResponse);
157
-
158
- // Act & Assert
159
- await expect(operation.execute(params)).resolves.toEqual(mockResponse);
160
- });
161
- });
162
- });
163
- `;
164
- }
165
-
166
- async function generateTests(): Promise<void> {
167
- console.log('Finding operations...');
168
- const operations = await findOperations();
169
-
170
- console.log(`Found ${operations.length} operations`);
171
-
172
- for (const operation of operations) {
173
- // Create test directory if it doesn't exist
174
- const testDir = path.dirname(operation.testPath);
175
- if (!fs.existsSync(testDir)) {
176
- fs.mkdirSync(testDir, { recursive: true });
177
- }
178
-
179
- // Check if test already exists
180
- if (fs.existsSync(operation.testPath)) {
181
- console.log(`Test already exists: ${operation.testPath}`);
182
- continue;
183
- }
184
-
185
- const testContent = generateTestContent(operation);
186
- fs.writeFileSync(operation.testPath, testContent);
187
- console.log(`Generated test: ${operation.testPath}`);
188
- }
189
-
190
- console.log('Test generation complete');
191
- }
192
-
193
- if (require.main === module) {
194
- generateTests().catch((error) => {
195
- console.error('Error generating tests:', error);
196
- process.exit(1);
197
- });
198
- }