@partylayer/conformance-runner 0.2.8 → 0.2.10
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/dist/cip0103-tests.d.ts +9 -6
- package/dist/cip0103-tests.js +209 -256
- package/dist/cip0103-tests.js.map +1 -1
- package/dist/index.d.ts +0 -7
- package/dist/index.js +573 -85
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
- package/CHANGELOG.md +0 -168
- package/dist/cip0103-tests.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/loader.d.ts +0 -11
- package/dist/loader.d.ts.map +0 -1
- package/dist/loader.js +0 -65
- package/dist/loader.js.map +0 -1
- package/dist/report.d.ts +0 -39
- package/dist/report.d.ts.map +0 -1
- package/dist/report.js +0 -72
- package/dist/report.js.map +0 -1
- package/dist/tests.d.ts +0 -50
- package/dist/tests.d.ts.map +0 -1
- package/dist/tests.js +0 -233
- package/dist/tests.js.map +0 -1
- package/src/cip0103-tests.ts +0 -340
- package/src/index.ts +0 -116
- package/src/loader.ts +0 -78
- package/src/report.ts +0 -107
- package/src/tests.test.ts +0 -74
- package/src/tests.ts +0 -277
- package/tsconfig.esm.json +0 -10
- package/tsconfig.esm.tsbuildinfo +0 -1
- package/tsconfig.json +0 -14
- package/tsconfig.tsbuildinfo +0 -1
package/dist/index.js
CHANGED
|
@@ -1,97 +1,585 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
* PartyLayer Conformance Runner CLI
|
|
4
|
-
*
|
|
5
|
-
* Runs adapter conformance tests and generates reports.
|
|
6
|
-
*/
|
|
2
|
+
import { CapabilityNotSupportedError, CIP0103_MANDATORY_METHODS, CIP0103_EVENTS } from '@partylayer/core';
|
|
7
3
|
import { program } from 'commander';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
4
|
+
import { writeFileSync, existsSync, readFileSync } from 'fs';
|
|
5
|
+
import { createRequire } from 'module';
|
|
6
|
+
import { resolve } from 'path';
|
|
7
|
+
import { pathToFileURL } from 'url';
|
|
8
|
+
|
|
9
|
+
var __defProp = Object.defineProperty;
|
|
10
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
11
|
+
var __esm = (fn, res) => function __init() {
|
|
12
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
13
|
+
};
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/cip0103-tests.ts
|
|
20
|
+
var cip0103_tests_exports = {};
|
|
21
|
+
__export(cip0103_tests_exports, {
|
|
22
|
+
formatCIP0103Report: () => formatCIP0103Report,
|
|
23
|
+
runCIP0103ConformanceTests: () => runCIP0103ConformanceTests
|
|
24
|
+
});
|
|
25
|
+
async function runCIP0103ConformanceTests(provider) {
|
|
26
|
+
const results = [];
|
|
27
|
+
results.push({
|
|
28
|
+
name: "Provider has request() method",
|
|
29
|
+
category: "interface",
|
|
30
|
+
passed: typeof provider.request === "function"
|
|
31
|
+
});
|
|
32
|
+
results.push({
|
|
33
|
+
name: "Provider has on() method",
|
|
34
|
+
category: "interface",
|
|
35
|
+
passed: typeof provider.on === "function"
|
|
36
|
+
});
|
|
37
|
+
results.push({
|
|
38
|
+
name: "Provider has emit() method",
|
|
39
|
+
category: "interface",
|
|
40
|
+
passed: typeof provider.emit === "function"
|
|
41
|
+
});
|
|
42
|
+
results.push({
|
|
43
|
+
name: "Provider has removeListener() method",
|
|
44
|
+
category: "interface",
|
|
45
|
+
passed: typeof provider.removeListener === "function"
|
|
46
|
+
});
|
|
47
|
+
for (const method of CIP0103_MANDATORY_METHODS) {
|
|
24
48
|
try {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
console.log('\n' + formatReportText(report));
|
|
41
|
-
// Exit with error code if tests failed
|
|
42
|
-
if (report.results.failed > 0) {
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch (err) {
|
|
47
|
-
console.error('Error running conformance tests:', err);
|
|
48
|
-
process.exit(1);
|
|
49
|
+
await provider.request({ method, params: {} });
|
|
50
|
+
results.push({
|
|
51
|
+
name: `Method "${method}" is handled`,
|
|
52
|
+
category: "method",
|
|
53
|
+
passed: true
|
|
54
|
+
});
|
|
55
|
+
} catch (err) {
|
|
56
|
+
const isValidError = isNumericCodeError(err);
|
|
57
|
+
results.push({
|
|
58
|
+
name: `Method "${method}" is handled`,
|
|
59
|
+
category: "method",
|
|
60
|
+
passed: isValidError,
|
|
61
|
+
error: isValidError ? void 0 : `Expected ProviderRpcError with numeric code, got: ${String(err)}`,
|
|
62
|
+
details: isValidError ? { code: err.code } : void 0
|
|
63
|
+
});
|
|
49
64
|
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
}
|
|
66
|
+
const noopHandler = () => {
|
|
67
|
+
};
|
|
68
|
+
const onResult = provider.on("statusChanged", noopHandler);
|
|
69
|
+
results.push({
|
|
70
|
+
name: "on() returns Provider for chaining",
|
|
71
|
+
category: "event",
|
|
72
|
+
passed: onResult === provider,
|
|
73
|
+
error: onResult === provider ? void 0 : "on() did not return the Provider instance"
|
|
74
|
+
});
|
|
75
|
+
const removeResult = provider.removeListener("statusChanged", noopHandler);
|
|
76
|
+
results.push({
|
|
77
|
+
name: "removeListener() returns Provider for chaining",
|
|
78
|
+
category: "event",
|
|
79
|
+
passed: removeResult === provider,
|
|
80
|
+
error: removeResult === provider ? void 0 : "removeListener() did not return the Provider instance"
|
|
81
|
+
});
|
|
82
|
+
try {
|
|
83
|
+
await provider.request({ method: "__cip0103_conformance_nonexistent__" });
|
|
84
|
+
results.push({
|
|
85
|
+
name: "Unsupported method returns ProviderRpcError",
|
|
86
|
+
category: "error",
|
|
87
|
+
passed: false,
|
|
88
|
+
error: "Expected error for unsupported method, got success"
|
|
89
|
+
});
|
|
90
|
+
} catch (err) {
|
|
91
|
+
const hasNumericCode = isNumericCodeError(err);
|
|
92
|
+
results.push({
|
|
93
|
+
name: "Unsupported method returns error with numeric code",
|
|
94
|
+
category: "error",
|
|
95
|
+
passed: hasNumericCode,
|
|
96
|
+
error: hasNumericCode ? void 0 : `Expected numeric code, got: ${String(err)}`,
|
|
97
|
+
details: hasNumericCode ? { code: err.code } : void 0
|
|
98
|
+
});
|
|
99
|
+
if (hasNumericCode) {
|
|
100
|
+
const code = err.code;
|
|
101
|
+
const isCorrectCode = code === 4200 || code === -32601;
|
|
102
|
+
results.push({
|
|
103
|
+
name: "Unsupported method uses code 4200 or -32601",
|
|
104
|
+
category: "error",
|
|
105
|
+
passed: isCorrectCode,
|
|
106
|
+
error: isCorrectCode ? void 0 : `Expected code 4200 or -32601, got ${code}`,
|
|
107
|
+
details: { code }
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const emitResult = provider.emit("__cip0103_conformance_test__");
|
|
112
|
+
results.push({
|
|
113
|
+
name: "emit() returns boolean",
|
|
114
|
+
category: "interface",
|
|
115
|
+
passed: typeof emitResult === "boolean",
|
|
116
|
+
error: typeof emitResult === "boolean" ? void 0 : `emit() returned ${typeof emitResult}`
|
|
117
|
+
});
|
|
118
|
+
{
|
|
119
|
+
const txEvents = [];
|
|
120
|
+
const txHandler = (...args) => txEvents.push(...args);
|
|
121
|
+
provider.on(CIP0103_EVENTS.TX_CHANGED, txHandler);
|
|
56
122
|
try {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
123
|
+
await provider.request({
|
|
124
|
+
method: "prepareExecute",
|
|
125
|
+
params: { tx: { _conformance: true } }
|
|
126
|
+
});
|
|
127
|
+
results.push({
|
|
128
|
+
name: 'prepareExecute emits txChanged with "pending"',
|
|
129
|
+
category: "lifecycle",
|
|
130
|
+
passed: txEvents.some(
|
|
131
|
+
(e) => typeof e === "object" && e !== null && e.status === "pending"
|
|
132
|
+
),
|
|
133
|
+
error: txEvents.length === 0 ? "No txChanged events emitted" : void 0
|
|
134
|
+
});
|
|
135
|
+
const signedEvent = txEvents.find(
|
|
136
|
+
(e) => typeof e === "object" && e !== null && e.status === "signed"
|
|
137
|
+
);
|
|
138
|
+
results.push({
|
|
139
|
+
name: 'prepareExecute emits txChanged with "signed"',
|
|
140
|
+
category: "lifecycle",
|
|
141
|
+
passed: !!signedEvent
|
|
142
|
+
});
|
|
143
|
+
if (signedEvent) {
|
|
144
|
+
const payload = signedEvent.payload;
|
|
145
|
+
results.push({
|
|
146
|
+
name: "signed event has payload.signature (string)",
|
|
147
|
+
category: "lifecycle",
|
|
148
|
+
passed: typeof payload?.signature === "string"
|
|
149
|
+
});
|
|
150
|
+
results.push({
|
|
151
|
+
name: "signed event has payload.signedBy (string)",
|
|
152
|
+
category: "lifecycle",
|
|
153
|
+
passed: typeof payload?.signedBy === "string"
|
|
154
|
+
});
|
|
155
|
+
results.push({
|
|
156
|
+
name: "signed event has payload.party (string)",
|
|
157
|
+
category: "lifecycle",
|
|
158
|
+
passed: typeof payload?.party === "string"
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
const hasTerminal = txEvents.some(
|
|
162
|
+
(e) => typeof e === "object" && e !== null && (e.status === "executed" || e.status === "failed")
|
|
163
|
+
);
|
|
164
|
+
results.push({
|
|
165
|
+
name: "prepareExecute reaches terminal tx state (executed or failed)",
|
|
166
|
+
category: "lifecycle",
|
|
167
|
+
passed: hasTerminal
|
|
168
|
+
});
|
|
169
|
+
const executedEvent = txEvents.find(
|
|
170
|
+
(e) => typeof e === "object" && e !== null && e.status === "executed"
|
|
171
|
+
);
|
|
172
|
+
if (executedEvent) {
|
|
173
|
+
const payload = executedEvent.payload;
|
|
174
|
+
results.push({
|
|
175
|
+
name: "executed event has payload.updateId (string)",
|
|
176
|
+
category: "lifecycle",
|
|
177
|
+
passed: typeof payload?.updateId === "string"
|
|
178
|
+
});
|
|
179
|
+
results.push({
|
|
180
|
+
name: "executed event has payload.completionOffset (number)",
|
|
181
|
+
category: "lifecycle",
|
|
182
|
+
passed: typeof payload?.completionOffset === "number"
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
} catch {
|
|
186
|
+
results.push({
|
|
187
|
+
name: "prepareExecute emits txChanged events (despite error)",
|
|
188
|
+
category: "lifecycle",
|
|
189
|
+
passed: txEvents.length > 0,
|
|
190
|
+
error: txEvents.length === 0 ? "No txChanged events emitted before error" : void 0
|
|
191
|
+
});
|
|
192
|
+
} finally {
|
|
193
|
+
provider.removeListener(CIP0103_EVENTS.TX_CHANGED, txHandler);
|
|
71
194
|
}
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
await provider.request({
|
|
198
|
+
method: "ledgerApi",
|
|
199
|
+
params: { requestMethod: "GET", resource: "/v1/state/acs" }
|
|
200
|
+
});
|
|
201
|
+
results.push({
|
|
202
|
+
name: "ledgerApi returns result (supported)",
|
|
203
|
+
category: "method",
|
|
204
|
+
passed: true
|
|
205
|
+
});
|
|
206
|
+
} catch (err) {
|
|
207
|
+
const isHandled = isNumericCodeError(err);
|
|
208
|
+
results.push({
|
|
209
|
+
name: "ledgerApi is handled (returns result or ProviderRpcError)",
|
|
210
|
+
category: "method",
|
|
211
|
+
passed: isHandled,
|
|
212
|
+
error: isHandled ? void 0 : `Unexpected error type: ${String(err)}`
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
const passed = results.filter((r) => r.passed).length;
|
|
216
|
+
return {
|
|
217
|
+
total: results.length,
|
|
218
|
+
passed,
|
|
219
|
+
failed: results.length - passed,
|
|
220
|
+
results
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function isNumericCodeError(err) {
|
|
224
|
+
if (typeof err !== "object" || err === null) return false;
|
|
225
|
+
return typeof err.code === "number";
|
|
226
|
+
}
|
|
227
|
+
function formatCIP0103Report(report) {
|
|
228
|
+
const lines = [
|
|
229
|
+
"\u2550\u2550\u2550 CIP-0103 Conformance Report \u2550\u2550\u2550",
|
|
230
|
+
"",
|
|
231
|
+
`Total: ${report.total} Passed: ${report.passed} Failed: ${report.failed}`,
|
|
232
|
+
""
|
|
233
|
+
];
|
|
234
|
+
const categories = ["interface", "method", "event", "error", "lifecycle"];
|
|
235
|
+
for (const cat of categories) {
|
|
236
|
+
const catResults = report.results.filter((r) => r.category === cat);
|
|
237
|
+
if (catResults.length === 0) continue;
|
|
238
|
+
lines.push(`\u2500\u2500 ${cat.toUpperCase()} \u2500\u2500`);
|
|
239
|
+
for (const r of catResults) {
|
|
240
|
+
const icon = r.passed ? "\u2713" : "\u2717";
|
|
241
|
+
lines.push(` ${icon} ${r.name}`);
|
|
242
|
+
if (r.error) {
|
|
243
|
+
lines.push(` \u2192 ${r.error}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
lines.push("");
|
|
247
|
+
}
|
|
248
|
+
return lines.join("\n");
|
|
249
|
+
}
|
|
250
|
+
var init_cip0103_tests = __esm({
|
|
251
|
+
"src/cip0103-tests.ts"() {
|
|
252
|
+
}
|
|
72
253
|
});
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
254
|
+
var nodeRequire = createRequire(import.meta.url);
|
|
255
|
+
async function loadAdapter(packageNameOrPath) {
|
|
256
|
+
let adapterPath;
|
|
257
|
+
if (packageNameOrPath.startsWith(".") || packageNameOrPath.startsWith("/")) {
|
|
258
|
+
adapterPath = resolve(packageNameOrPath);
|
|
259
|
+
} else {
|
|
78
260
|
try {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
261
|
+
adapterPath = nodeRequire.resolve(packageNameOrPath);
|
|
262
|
+
} catch {
|
|
263
|
+
throw new Error(`Cannot resolve adapter: ${packageNameOrPath}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
const moduleUrl = pathToFileURL(adapterPath).href;
|
|
268
|
+
const module = await import(moduleUrl);
|
|
269
|
+
const adapter = module.default || module[Object.keys(module)[0]];
|
|
270
|
+
if (!adapter) {
|
|
271
|
+
throw new Error("No adapter export found");
|
|
272
|
+
}
|
|
273
|
+
if (typeof adapter === "function") {
|
|
274
|
+
return new adapter();
|
|
275
|
+
}
|
|
276
|
+
return adapter;
|
|
277
|
+
} catch (err) {
|
|
278
|
+
try {
|
|
279
|
+
const module = nodeRequire(adapterPath);
|
|
280
|
+
const adapter = module.default || module[Object.keys(module)[0]];
|
|
281
|
+
if (!adapter) {
|
|
282
|
+
throw new Error("No adapter export found");
|
|
283
|
+
}
|
|
284
|
+
if (typeof adapter === "function") {
|
|
285
|
+
return new adapter();
|
|
286
|
+
}
|
|
287
|
+
return adapter;
|
|
288
|
+
} catch (cjsErr) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
`Failed to load adapter: ${err instanceof Error ? err.message : String(err)}`
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function createMockContext() {
|
|
296
|
+
return {
|
|
297
|
+
appName: "Conformance Test",
|
|
298
|
+
origin: "https://test.example.com",
|
|
299
|
+
network: "devnet",
|
|
300
|
+
logger: {
|
|
301
|
+
debug: () => {
|
|
302
|
+
},
|
|
303
|
+
info: () => {
|
|
304
|
+
},
|
|
305
|
+
warn: () => {
|
|
306
|
+
},
|
|
307
|
+
error: () => {
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
telemetry: void 0,
|
|
311
|
+
registry: {
|
|
312
|
+
getWallet: async () => ({})
|
|
313
|
+
},
|
|
314
|
+
crypto: {
|
|
315
|
+
encrypt: async (data) => data,
|
|
316
|
+
decrypt: async (data) => data,
|
|
317
|
+
generateKey: async () => "test-key"
|
|
318
|
+
},
|
|
319
|
+
storage: {
|
|
320
|
+
get: async () => null,
|
|
321
|
+
set: async () => {
|
|
322
|
+
},
|
|
323
|
+
remove: async () => {
|
|
324
|
+
},
|
|
325
|
+
clear: async () => {
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
timeout: async (ms) => {
|
|
329
|
+
await new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
330
|
+
throw new Error("Timeout");
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
async function runConformanceTests(adapter, context) {
|
|
335
|
+
const results = [];
|
|
336
|
+
results.push({
|
|
337
|
+
name: "Adapter has walletId",
|
|
338
|
+
passed: typeof adapter.walletId === "string" && adapter.walletId.length > 0,
|
|
339
|
+
details: { walletId: adapter.walletId }
|
|
340
|
+
});
|
|
341
|
+
results.push({
|
|
342
|
+
name: "Adapter has name",
|
|
343
|
+
passed: typeof adapter.name === "string" && adapter.name.length > 0,
|
|
344
|
+
details: { name: adapter.name }
|
|
345
|
+
});
|
|
346
|
+
let capabilities;
|
|
347
|
+
try {
|
|
348
|
+
capabilities = adapter.getCapabilities();
|
|
349
|
+
results.push({
|
|
350
|
+
name: "getCapabilities returns array",
|
|
351
|
+
passed: Array.isArray(capabilities),
|
|
352
|
+
details: { capabilities }
|
|
353
|
+
});
|
|
354
|
+
const hasConnect = capabilities.includes("connect");
|
|
355
|
+
results.push({
|
|
356
|
+
name: 'Capabilities include "connect"',
|
|
357
|
+
passed: hasConnect,
|
|
358
|
+
details: { capabilities }
|
|
359
|
+
});
|
|
360
|
+
} catch (err) {
|
|
361
|
+
results.push({
|
|
362
|
+
name: "getCapabilities throws error",
|
|
363
|
+
passed: false,
|
|
364
|
+
error: err instanceof Error ? err.message : String(err)
|
|
365
|
+
});
|
|
366
|
+
return results;
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
const detect = await adapter.detectInstalled();
|
|
370
|
+
results.push({
|
|
371
|
+
name: "detectInstalled returns valid result",
|
|
372
|
+
passed: typeof detect === "object" && typeof detect.installed === "boolean" && (detect.installed || typeof detect.reason === "string"),
|
|
373
|
+
details: { detect }
|
|
374
|
+
});
|
|
375
|
+
} catch (err) {
|
|
376
|
+
results.push({
|
|
377
|
+
name: "detectInstalled throws error",
|
|
378
|
+
passed: false,
|
|
379
|
+
error: err instanceof Error ? err.message : String(err)
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
if (capabilities.includes("signMessage")) {
|
|
383
|
+
if (typeof adapter.signMessage !== "function") {
|
|
384
|
+
results.push({
|
|
385
|
+
name: "signMessage capability declared but method missing",
|
|
386
|
+
passed: false,
|
|
387
|
+
error: "signMessage capability declared but method not implemented"
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
if (typeof adapter.signMessage === "function") {
|
|
392
|
+
try {
|
|
393
|
+
await adapter.signMessage(context, {}, { message: "test" });
|
|
394
|
+
results.push({
|
|
395
|
+
name: "signMessage throws CapabilityNotSupportedError when not supported",
|
|
396
|
+
passed: false,
|
|
397
|
+
error: "signMessage did not throw error"
|
|
398
|
+
});
|
|
399
|
+
} catch (err) {
|
|
400
|
+
const passed = err instanceof CapabilityNotSupportedError;
|
|
401
|
+
results.push({
|
|
402
|
+
name: "signMessage throws CapabilityNotSupportedError when not supported",
|
|
403
|
+
passed,
|
|
404
|
+
error: passed ? void 0 : err instanceof Error ? err.message : String(err)
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (capabilities.includes("signTransaction")) {
|
|
410
|
+
if (typeof adapter.signTransaction !== "function") {
|
|
411
|
+
results.push({
|
|
412
|
+
name: "signTransaction capability declared but method missing",
|
|
413
|
+
passed: false,
|
|
414
|
+
error: "signTransaction capability declared but method not implemented"
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (capabilities.includes("submitTransaction")) {
|
|
419
|
+
if (typeof adapter.submitTransaction !== "function") {
|
|
420
|
+
results.push({
|
|
421
|
+
name: "submitTransaction capability declared but method missing",
|
|
422
|
+
passed: false,
|
|
423
|
+
error: "submitTransaction capability declared but method not implemented"
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
{
|
|
428
|
+
const hasRestoreMethod = typeof adapter.restore === "function";
|
|
429
|
+
const declaresRestore = capabilities.includes("restore");
|
|
430
|
+
if (declaresRestore && !hasRestoreMethod) {
|
|
431
|
+
results.push({
|
|
432
|
+
name: "restore capability declared but method missing",
|
|
433
|
+
passed: false,
|
|
434
|
+
error: `${adapter.name}: declares 'restore' capability but does not implement restore()`
|
|
435
|
+
});
|
|
436
|
+
} else if (hasRestoreMethod && !declaresRestore) {
|
|
437
|
+
results.push({
|
|
438
|
+
name: "restore method implemented but capability not declared",
|
|
439
|
+
passed: false,
|
|
440
|
+
error: `${adapter.name}: implements restore() but getCapabilities() does not include 'restore'. Add 'restore' to the returned array so consumers can discover the feature.`
|
|
441
|
+
});
|
|
442
|
+
} else {
|
|
443
|
+
results.push({
|
|
444
|
+
name: "restore capability / method symmetry",
|
|
445
|
+
passed: true,
|
|
446
|
+
details: { hasRestoreMethod, declaresRestore }
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return results;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// src/report.ts
|
|
454
|
+
function generateReport(adapter, tests, startTime) {
|
|
455
|
+
const passed = tests.filter((t) => t.passed).length;
|
|
456
|
+
const failed = tests.filter((t) => !t.passed).length;
|
|
457
|
+
return {
|
|
458
|
+
adapter,
|
|
459
|
+
environment: {
|
|
460
|
+
nodeVersion: process.version,
|
|
461
|
+
platform: process.platform,
|
|
462
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
463
|
+
},
|
|
464
|
+
results: {
|
|
465
|
+
total: tests.length,
|
|
466
|
+
passed,
|
|
467
|
+
failed,
|
|
468
|
+
duration: Date.now() - startTime
|
|
469
|
+
},
|
|
470
|
+
tests
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function formatReportText(report) {
|
|
474
|
+
const lines = [];
|
|
475
|
+
lines.push("=".repeat(60));
|
|
476
|
+
lines.push("PartyLayer Adapter Conformance Report");
|
|
477
|
+
lines.push("=".repeat(60));
|
|
478
|
+
lines.push("");
|
|
479
|
+
lines.push(`Adapter: ${report.adapter.name} (${report.adapter.walletId})`);
|
|
480
|
+
if (report.adapter.version) {
|
|
481
|
+
lines.push(`Version: ${report.adapter.version}`);
|
|
482
|
+
}
|
|
483
|
+
lines.push(`Environment: Node ${report.environment.nodeVersion} on ${report.environment.platform}`);
|
|
484
|
+
lines.push(`Timestamp: ${report.environment.timestamp}`);
|
|
485
|
+
lines.push("");
|
|
486
|
+
lines.push("Summary:");
|
|
487
|
+
lines.push(` Total Tests: ${report.results.total}`);
|
|
488
|
+
lines.push(` Passed: ${report.results.passed}`);
|
|
489
|
+
lines.push(` Failed: ${report.results.failed}`);
|
|
490
|
+
lines.push(` Duration: ${report.results.duration}ms`);
|
|
491
|
+
lines.push("");
|
|
492
|
+
lines.push("Test Results:");
|
|
493
|
+
lines.push("-".repeat(60));
|
|
494
|
+
report.tests.forEach((test, index) => {
|
|
495
|
+
const status = test.passed ? "\u2713" : "\u2717";
|
|
496
|
+
lines.push(`${index + 1}. ${status} ${test.name}`);
|
|
497
|
+
if (test.details) {
|
|
498
|
+
Object.entries(test.details).forEach(([key, value]) => {
|
|
499
|
+
lines.push(` ${key}: ${JSON.stringify(value)}`);
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
if (test.error) {
|
|
503
|
+
lines.push(` Error: ${test.error}`);
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
lines.push("-".repeat(60));
|
|
507
|
+
lines.push("");
|
|
508
|
+
if (report.results.failed === 0) {
|
|
509
|
+
lines.push("\u2713 All tests passed!");
|
|
510
|
+
} else {
|
|
511
|
+
lines.push(`\u2717 ${report.results.failed} test(s) failed`);
|
|
512
|
+
}
|
|
513
|
+
return lines.join("\n");
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// src/index.ts
|
|
517
|
+
program.name("partylayer-conformance").description("Run conformance tests for PartyLayer wallet adapters").version("0.1.0");
|
|
518
|
+
program.command("run").description("Run conformance tests for an adapter").requiredOption("--adapter <packageOrPath>", "Adapter package name or path").option("--network <network>", "Network to use", "devnet").option("--registryUrl <url>", "Registry URL (optional)").option("--output <path>", "Output JSON report path", "conformance-report.json").action(async (options) => {
|
|
519
|
+
try {
|
|
520
|
+
console.log(`Loading adapter: ${options.adapter}`);
|
|
521
|
+
const adapter = await loadAdapter(options.adapter);
|
|
522
|
+
console.log(`Running conformance tests for: ${adapter.name} (${adapter.walletId})`);
|
|
523
|
+
const context = createMockContext();
|
|
524
|
+
context.network = options.network;
|
|
525
|
+
const startTime = Date.now();
|
|
526
|
+
const testResults = await runConformanceTests(adapter, context);
|
|
527
|
+
const report = generateReport(
|
|
528
|
+
{
|
|
529
|
+
walletId: adapter.walletId,
|
|
530
|
+
name: adapter.name
|
|
531
|
+
},
|
|
532
|
+
testResults,
|
|
533
|
+
startTime
|
|
534
|
+
);
|
|
535
|
+
writeFileSync(options.output, JSON.stringify(report, null, 2));
|
|
536
|
+
console.log(`
|
|
537
|
+
Report written to: ${options.output}`);
|
|
538
|
+
console.log("\n" + formatReportText(report));
|
|
539
|
+
if (report.results.failed > 0) {
|
|
540
|
+
process.exit(1);
|
|
541
|
+
}
|
|
542
|
+
} catch (err) {
|
|
543
|
+
console.error("Error running conformance tests:", err);
|
|
544
|
+
process.exit(1);
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
program.command("report").description("Format and display a conformance report").requiredOption("--input <path>", "Input JSON report path").action((options) => {
|
|
548
|
+
try {
|
|
549
|
+
if (!existsSync(options.input)) {
|
|
550
|
+
console.error(`Report file not found: ${options.input}`);
|
|
551
|
+
process.exit(1);
|
|
552
|
+
}
|
|
553
|
+
const reportJson = readFileSync(options.input, "utf-8");
|
|
554
|
+
const report = JSON.parse(reportJson);
|
|
555
|
+
console.log(formatReportText(report));
|
|
556
|
+
if (report.results.failed > 0) {
|
|
557
|
+
process.exit(1);
|
|
558
|
+
}
|
|
559
|
+
} catch (err) {
|
|
560
|
+
console.error("Error reading report:", err);
|
|
561
|
+
process.exit(1);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
program.command("run-cip0103").description("Run CIP-0103 conformance tests against a Provider").option("--output <path>", "Output JSON report path", "cip0103-report.json").action(async (options) => {
|
|
565
|
+
try {
|
|
566
|
+
const { runCIP0103ConformanceTests: runCIP0103ConformanceTests2, formatCIP0103Report: formatCIP0103Report2 } = await Promise.resolve().then(() => (init_cip0103_tests(), cip0103_tests_exports));
|
|
567
|
+
const { PartyLayerProvider } = await import('@partylayer/provider');
|
|
568
|
+
console.log("Running CIP-0103 conformance tests...");
|
|
569
|
+
const provider = new PartyLayerProvider();
|
|
570
|
+
const report = await runCIP0103ConformanceTests2(provider);
|
|
571
|
+
writeFileSync(options.output, JSON.stringify(report, null, 2));
|
|
572
|
+
console.log(`
|
|
573
|
+
Report written to: ${options.output}`);
|
|
574
|
+
console.log("\n" + formatCIP0103Report2(report));
|
|
575
|
+
if (report.failed > 0) {
|
|
576
|
+
process.exit(1);
|
|
94
577
|
}
|
|
578
|
+
} catch (err) {
|
|
579
|
+
console.error("Error running CIP-0103 conformance tests:", err);
|
|
580
|
+
process.exit(1);
|
|
581
|
+
}
|
|
95
582
|
});
|
|
96
583
|
program.parse();
|
|
584
|
+
//# sourceMappingURL=index.js.map
|
|
97
585
|
//# sourceMappingURL=index.js.map
|