@5ive-tech/sdk 1.1.7 → 1.1.8
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/assets/vm/five_vm_wasm_bg.wasm +0 -0
- package/dist/config/ProgramIdResolver.d.ts +1 -1
- package/dist/config/ProgramIdResolver.js +1 -1
- package/dist/testing/AccountTestFixture.js +3 -3
- package/dist/testing/TestDiscovery.d.ts +6 -0
- package/dist/testing/TestDiscovery.js +62 -57
- package/dist/testing/TestRunner.d.ts +1 -1
- package/dist/testing/TestRunner.js +62 -41
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/package.json +2 -2
|
Binary file
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Baked program ID injected at release time (set by scripts/set-default-program-id.sh)
|
|
7
7
|
* Empty string by default; overridden in npm published packages.
|
|
8
8
|
*/
|
|
9
|
-
export declare const FIVE_BAKED_PROGRAM_ID = "";
|
|
9
|
+
export declare const FIVE_BAKED_PROGRAM_ID = "4Qxf3pbCse2veUgZVMiAm3nWqJrYo2pT4suxHKMJdK1d";
|
|
10
10
|
/**
|
|
11
11
|
* Centralized resolver for program IDs across all SDK operations.
|
|
12
12
|
* Ensures consistent validation and error messaging.
|
|
@@ -7,7 +7,7 @@ import { validator } from '../validation/index.js';
|
|
|
7
7
|
* Baked program ID injected at release time (set by scripts/set-default-program-id.sh)
|
|
8
8
|
* Empty string by default; overridden in npm published packages.
|
|
9
9
|
*/
|
|
10
|
-
export const FIVE_BAKED_PROGRAM_ID = '';
|
|
10
|
+
export const FIVE_BAKED_PROGRAM_ID = '4Qxf3pbCse2veUgZVMiAm3nWqJrYo2pT4suxHKMJdK1d';
|
|
11
11
|
/**
|
|
12
12
|
* Centralized resolver for program IDs across all SDK operations.
|
|
13
13
|
* Ensures consistent validation and error messaging.
|
|
@@ -219,7 +219,7 @@ export class AccountTestFixture {
|
|
|
219
219
|
else if (spec.type === 'state' || spec.type === 'mutable') {
|
|
220
220
|
// Create state/mutable account with initial data
|
|
221
221
|
const space = 1024; // Default space
|
|
222
|
-
const owner = options.fiveVMProgramId || new PublicKey('
|
|
222
|
+
const owner = options.fiveVMProgramId || new PublicKey('4Qxf3pbCse2veUgZVMiAm3nWqJrYo2pT4suxHKMJdK1d');
|
|
223
223
|
// Serialize state data if provided
|
|
224
224
|
let initialData;
|
|
225
225
|
if (spec.state && spec.type === 'state') {
|
|
@@ -242,7 +242,7 @@ export class AccountTestFixture {
|
|
|
242
242
|
else if (spec.type === 'init') {
|
|
243
243
|
// Create init account (will be initialized by script)
|
|
244
244
|
const space = 1024;
|
|
245
|
-
const owner = options.fiveVMProgramId || new PublicKey('
|
|
245
|
+
const owner = options.fiveVMProgramId || new PublicKey('4Qxf3pbCse2veUgZVMiAm3nWqJrYo2pT4suxHKMJdK1d');
|
|
246
246
|
publicKey = await manager.createAccount(space, owner);
|
|
247
247
|
if (options.debug) {
|
|
248
248
|
console.log(` ${spec.name} (init): ${publicKey.toString()}`);
|
|
@@ -256,7 +256,7 @@ export class AccountTestFixture {
|
|
|
256
256
|
else {
|
|
257
257
|
// Create readonly account
|
|
258
258
|
const space = 0;
|
|
259
|
-
const owner = options.fiveVMProgramId || new PublicKey('
|
|
259
|
+
const owner = options.fiveVMProgramId || new PublicKey('4Qxf3pbCse2veUgZVMiAm3nWqJrYo2pT4suxHKMJdK1d');
|
|
260
260
|
publicKey = await manager.createAccount(space, owner);
|
|
261
261
|
if (options.debug) {
|
|
262
262
|
console.log(` ${spec.name} (readonly): ${publicKey.toString()}`);
|
|
@@ -13,6 +13,8 @@ export interface VSourceTest {
|
|
|
13
13
|
file: string;
|
|
14
14
|
functionName: string;
|
|
15
15
|
parameters?: any[];
|
|
16
|
+
expectedResult?: any;
|
|
17
|
+
expectsResult?: boolean;
|
|
16
18
|
description?: string;
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
@@ -36,6 +38,8 @@ export interface DiscoveredTest {
|
|
|
36
38
|
source?: VSourceTest;
|
|
37
39
|
description?: string;
|
|
38
40
|
parameters?: any[];
|
|
41
|
+
expectedResult?: any;
|
|
42
|
+
expectsResult?: boolean;
|
|
39
43
|
}
|
|
40
44
|
/**
|
|
41
45
|
* Discover tests from directory
|
|
@@ -64,6 +68,8 @@ export declare class TestDiscovery {
|
|
|
64
68
|
* Parse .v source file for test functions and parameters
|
|
65
69
|
*/
|
|
66
70
|
private static parseVFile;
|
|
71
|
+
private static splitParamsAndExpectation;
|
|
72
|
+
private static parseTokenValue;
|
|
67
73
|
/**
|
|
68
74
|
* Compile a .v test source file
|
|
69
75
|
*/
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* extracts parameters from @test-params comments, and compiles source files.
|
|
7
7
|
*/
|
|
8
8
|
import { readFile, readdir, stat } from 'fs/promises';
|
|
9
|
-
import {
|
|
9
|
+
import { basename, join } from 'path';
|
|
10
10
|
import { FiveSDK } from '../FiveSDK.js';
|
|
11
11
|
/**
|
|
12
12
|
* Discover tests from directory
|
|
@@ -121,80 +121,62 @@ export class TestDiscovery {
|
|
|
121
121
|
const tests = [];
|
|
122
122
|
try {
|
|
123
123
|
const content = await readFile(file, 'utf8');
|
|
124
|
-
// Find all function definitions with potential test annotations
|
|
125
|
-
// Pattern 1: pub function with #[test] annotation
|
|
126
|
-
// Pattern 2: function with specific naming convention (test_*, *_test)
|
|
127
124
|
const lines = content.split('\n');
|
|
128
|
-
let
|
|
129
|
-
let currentParams = null;
|
|
130
|
-
let currentDescription = null;
|
|
125
|
+
let pendingParams;
|
|
131
126
|
for (let i = 0; i < lines.length; i++) {
|
|
132
127
|
const line = lines[i].trim();
|
|
133
128
|
// Check for @test-params comment
|
|
134
|
-
const paramsMatch = line.match(/@test-params
|
|
129
|
+
const paramsMatch = line.match(/@test-params(?:\s+(.*))?$/);
|
|
135
130
|
if (paramsMatch) {
|
|
136
131
|
try {
|
|
137
|
-
const paramsStr = paramsMatch[1].trim();
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
132
|
+
const paramsStr = (paramsMatch[1] || '').trim();
|
|
133
|
+
if (paramsStr.length === 0) {
|
|
134
|
+
pendingParams = [];
|
|
135
|
+
}
|
|
136
|
+
else if (paramsStr.startsWith('[')) {
|
|
137
|
+
const parsed = JSON.parse(paramsStr);
|
|
138
|
+
pendingParams = Array.isArray(parsed) ? parsed : [];
|
|
141
139
|
}
|
|
142
140
|
else {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return Number(p);
|
|
148
|
-
}
|
|
149
|
-
return p;
|
|
150
|
-
});
|
|
141
|
+
pendingParams = paramsStr
|
|
142
|
+
.split(/\s+/)
|
|
143
|
+
.filter(Boolean)
|
|
144
|
+
.map((token) => this.parseTokenValue(token));
|
|
151
145
|
}
|
|
152
146
|
}
|
|
153
147
|
catch (error) {
|
|
154
148
|
console.warn(`Failed to parse @test-params in ${file}:${i + 1}: ${line}`);
|
|
149
|
+
pendingParams = undefined;
|
|
155
150
|
}
|
|
156
151
|
continue;
|
|
157
152
|
}
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const nextLine = lines[j].trim();
|
|
163
|
-
if (nextLine && !nextLine.startsWith('//')) {
|
|
164
|
-
const funcMatch = nextLine.match(/(?:pub\s+)?(?:fn|instruction|script)\s+(\w+)\s*\(/);
|
|
165
|
-
if (funcMatch) {
|
|
166
|
-
currentFunction = funcMatch[1];
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
// Check for pub function that matches test naming convention
|
|
174
|
-
const funcMatch = line.match(/pub\s+(?:fn|instruction|script)\s+(test_\w+|_?\w+_test)\s*\(/);
|
|
153
|
+
// Match canonical DSL test function forms:
|
|
154
|
+
// pub test_name(...)
|
|
155
|
+
// pub fn test_name(...)
|
|
156
|
+
const funcMatch = line.match(/^pub\s+(?:fn\s+)?(test_[A-Za-z0-9_]*|[A-Za-z0-9_]*_test)\s*\([^)]*\)\s*(?:->\s*([A-Za-z0-9_<>\[\]]+))?/);
|
|
175
157
|
if (funcMatch) {
|
|
176
158
|
const functionName = funcMatch[1];
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
159
|
+
const returnType = funcMatch[2];
|
|
160
|
+
const hasReturnValue = !!returnType;
|
|
161
|
+
const [parameters, expectedResult, expectsResult] = this.splitParamsAndExpectation(pendingParams, hasReturnValue);
|
|
162
|
+
const name = `${basename(file, '.v')}::${functionName}`;
|
|
163
|
+
tests.push({
|
|
164
|
+
name,
|
|
165
|
+
path: file,
|
|
166
|
+
type: 'v-source',
|
|
167
|
+
source: {
|
|
183
168
|
type: 'v-source',
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
currentDescription = null;
|
|
196
|
-
currentFunction = null;
|
|
197
|
-
}
|
|
169
|
+
file,
|
|
170
|
+
functionName,
|
|
171
|
+
parameters: parameters.length > 0 ? parameters : undefined,
|
|
172
|
+
expectedResult,
|
|
173
|
+
expectsResult
|
|
174
|
+
},
|
|
175
|
+
parameters: parameters.length > 0 ? parameters : undefined,
|
|
176
|
+
expectedResult,
|
|
177
|
+
expectsResult
|
|
178
|
+
});
|
|
179
|
+
pendingParams = undefined;
|
|
198
180
|
}
|
|
199
181
|
}
|
|
200
182
|
}
|
|
@@ -203,6 +185,29 @@ export class TestDiscovery {
|
|
|
203
185
|
}
|
|
204
186
|
return tests;
|
|
205
187
|
}
|
|
188
|
+
static splitParamsAndExpectation(values, hasReturnValue) {
|
|
189
|
+
const parsed = Array.isArray(values) ? values : [];
|
|
190
|
+
if (!hasReturnValue || parsed.length === 0) {
|
|
191
|
+
return [parsed, undefined, false];
|
|
192
|
+
}
|
|
193
|
+
const params = parsed.slice(0, parsed.length - 1);
|
|
194
|
+
return [params, parsed[parsed.length - 1], true];
|
|
195
|
+
}
|
|
196
|
+
static parseTokenValue(token) {
|
|
197
|
+
if ((token.startsWith('"') && token.endsWith('"')) ||
|
|
198
|
+
(token.startsWith("'") && token.endsWith("'"))) {
|
|
199
|
+
return token.slice(1, -1);
|
|
200
|
+
}
|
|
201
|
+
if (token === 'true')
|
|
202
|
+
return true;
|
|
203
|
+
if (token === 'false')
|
|
204
|
+
return false;
|
|
205
|
+
const asNumber = Number(token);
|
|
206
|
+
if (!Number.isNaN(asNumber)) {
|
|
207
|
+
return asNumber;
|
|
208
|
+
}
|
|
209
|
+
return token;
|
|
210
|
+
}
|
|
206
211
|
/**
|
|
207
212
|
* Compile a .v test source file
|
|
208
213
|
*/
|
|
@@ -76,6 +76,7 @@ export interface TestRunnerOptions {
|
|
|
76
76
|
*/
|
|
77
77
|
export declare class FiveTestRunner {
|
|
78
78
|
private options;
|
|
79
|
+
private compilationCache;
|
|
79
80
|
constructor(options?: TestRunnerOptions);
|
|
80
81
|
/**
|
|
81
82
|
* Run a single test case
|
|
@@ -111,7 +112,6 @@ export declare class FiveTestRunner {
|
|
|
111
112
|
private getValidationError;
|
|
112
113
|
private matchesPattern;
|
|
113
114
|
private chunkArray;
|
|
114
|
-
private findTestFiles;
|
|
115
115
|
private formatTestResult;
|
|
116
116
|
private formatSuiteResult;
|
|
117
117
|
}
|
|
@@ -4,14 +4,16 @@
|
|
|
4
4
|
* SDK-based test utilities that replace shell script approaches with programmatic
|
|
5
5
|
* Five SDK usage. Provides comprehensive testing capabilities for Five VM scripts.
|
|
6
6
|
*/
|
|
7
|
-
import { readFile
|
|
8
|
-
import {
|
|
7
|
+
import { readFile } from 'fs/promises';
|
|
8
|
+
import { basename } from 'path';
|
|
9
9
|
import { FiveSDK } from '../FiveSDK.js';
|
|
10
|
+
import { TestDiscovery } from './TestDiscovery.js';
|
|
10
11
|
/**
|
|
11
12
|
* Five SDK-based test runner
|
|
12
13
|
*/
|
|
13
14
|
export class FiveTestRunner {
|
|
14
15
|
constructor(options = {}) {
|
|
16
|
+
this.compilationCache = new Map();
|
|
15
17
|
this.options = {
|
|
16
18
|
timeout: 30000,
|
|
17
19
|
maxComputeUnits: 1000000,
|
|
@@ -40,13 +42,22 @@ export class FiveTestRunner {
|
|
|
40
42
|
};
|
|
41
43
|
}
|
|
42
44
|
let bytecode;
|
|
45
|
+
let abi;
|
|
43
46
|
// Get bytecode (compile source or load existing)
|
|
44
47
|
if (testCase.source) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
if (!this.compilationCache.has(testCase.source)) {
|
|
49
|
+
const result = await this.compileToBytecode(testCase.source);
|
|
50
|
+
if (!result.success || !result.bytecode) {
|
|
51
|
+
throw new Error(`Compilation failed: ${result.errors?.join(', ')}`);
|
|
52
|
+
}
|
|
53
|
+
this.compilationCache.set(testCase.source, {
|
|
54
|
+
bytecode: result.bytecode,
|
|
55
|
+
abi: result.abi
|
|
56
|
+
});
|
|
48
57
|
}
|
|
49
|
-
|
|
58
|
+
const cached = this.compilationCache.get(testCase.source);
|
|
59
|
+
bytecode = cached.bytecode;
|
|
60
|
+
abi = cached.abi;
|
|
50
61
|
}
|
|
51
62
|
else if (testCase.bytecode) {
|
|
52
63
|
const data = await readFile(testCase.bytecode);
|
|
@@ -67,7 +78,8 @@ export class FiveTestRunner {
|
|
|
67
78
|
const executionPromise = FiveSDK.executeLocally(bytecode, testCase.function || 0, testCase.parameters || [], {
|
|
68
79
|
debug: this.options.debug,
|
|
69
80
|
trace: this.options.trace,
|
|
70
|
-
computeUnitLimit: this.options.maxComputeUnits
|
|
81
|
+
computeUnitLimit: this.options.maxComputeUnits,
|
|
82
|
+
abi
|
|
71
83
|
});
|
|
72
84
|
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Test timeout')), executionTimeout));
|
|
73
85
|
const result = await Promise.race([executionPromise, timeoutPromise]);
|
|
@@ -187,23 +199,52 @@ export class FiveTestRunner {
|
|
|
187
199
|
* Discover and load test suites from directory
|
|
188
200
|
*/
|
|
189
201
|
async discoverTestSuites(directory, pattern = '**/*.test.json') {
|
|
190
|
-
const
|
|
202
|
+
const discovered = await TestDiscovery.discoverTests(directory, { verbose: this.options.verbose });
|
|
191
203
|
const suites = [];
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
204
|
+
const byFile = new Map();
|
|
205
|
+
const loadedJsonSuites = new Set();
|
|
206
|
+
for (const test of discovered) {
|
|
207
|
+
if (test.type === 'json-suite') {
|
|
208
|
+
if (loadedJsonSuites.has(test.path)) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
try {
|
|
212
|
+
const content = await readFile(test.path, 'utf8');
|
|
213
|
+
const data = JSON.parse(content);
|
|
214
|
+
suites.push({
|
|
215
|
+
name: data.name || basename(test.path, '.test.json'),
|
|
216
|
+
description: data.description,
|
|
217
|
+
testCases: data.tests || data.testCases || []
|
|
218
|
+
});
|
|
219
|
+
loadedJsonSuites.add(test.path);
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
console.warn(`Failed to load test suite ${test.path}: ${error}`);
|
|
223
|
+
}
|
|
224
|
+
continue;
|
|
202
225
|
}
|
|
203
|
-
|
|
204
|
-
|
|
226
|
+
if (test.type === 'v-source' && test.source) {
|
|
227
|
+
const cases = byFile.get(test.path) || [];
|
|
228
|
+
cases.push({
|
|
229
|
+
name: test.name,
|
|
230
|
+
source: test.path,
|
|
231
|
+
function: test.source.functionName,
|
|
232
|
+
parameters: test.parameters || [],
|
|
233
|
+
expected: {
|
|
234
|
+
success: true,
|
|
235
|
+
result: test.expectsResult ? test.expectedResult : undefined
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
byFile.set(test.path, cases);
|
|
205
239
|
}
|
|
206
240
|
}
|
|
241
|
+
for (const [file, testCases] of byFile.entries()) {
|
|
242
|
+
suites.push({
|
|
243
|
+
name: basename(file, '.v'),
|
|
244
|
+
description: `Tests from ${file}`,
|
|
245
|
+
testCases
|
|
246
|
+
});
|
|
247
|
+
}
|
|
207
248
|
return suites;
|
|
208
249
|
}
|
|
209
250
|
/**
|
|
@@ -245,7 +286,7 @@ export class FiveTestRunner {
|
|
|
245
286
|
// Private helper methods
|
|
246
287
|
async compileToBytecode(sourcePath) {
|
|
247
288
|
const source = await readFile(sourcePath, 'utf8');
|
|
248
|
-
return FiveSDK.compile(source, { debug: this.options.debug });
|
|
289
|
+
return FiveSDK.compile({ filename: sourcePath, content: source }, { debug: this.options.debug });
|
|
249
290
|
}
|
|
250
291
|
validateResult(result, expected) {
|
|
251
292
|
// Check success/failure
|
|
@@ -298,26 +339,6 @@ export class FiveTestRunner {
|
|
|
298
339
|
}
|
|
299
340
|
return chunks;
|
|
300
341
|
}
|
|
301
|
-
async findTestFiles(directory, pattern) {
|
|
302
|
-
const files = [];
|
|
303
|
-
try {
|
|
304
|
-
const entries = await readdir(directory, { withFileTypes: true });
|
|
305
|
-
for (const entry of entries) {
|
|
306
|
-
const fullPath = join(directory, entry.name);
|
|
307
|
-
if (entry.isDirectory()) {
|
|
308
|
-
const subFiles = await this.findTestFiles(fullPath, pattern);
|
|
309
|
-
files.push(...subFiles);
|
|
310
|
-
}
|
|
311
|
-
else if (entry.isFile() && entry.name.endsWith('.test.json')) {
|
|
312
|
-
files.push(fullPath);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
catch (error) {
|
|
317
|
-
// Directory doesn't exist or not accessible
|
|
318
|
-
}
|
|
319
|
-
return files;
|
|
320
|
-
}
|
|
321
342
|
formatTestResult(result) {
|
|
322
343
|
const icon = result.passed ? '✅' : '❌';
|
|
323
344
|
const duration = `${result.duration}ms`;
|
package/dist/types.d.ts
CHANGED
|
@@ -203,7 +203,7 @@ export interface CLIError extends Error {
|
|
|
203
203
|
category: string;
|
|
204
204
|
details?: any;
|
|
205
205
|
}
|
|
206
|
-
export declare const FIVE_VM_PROGRAM_ID = "
|
|
206
|
+
export declare const FIVE_VM_PROGRAM_ID = "4Qxf3pbCse2veUgZVMiAm3nWqJrYo2pT4suxHKMJdK1d";
|
|
207
207
|
export interface FiveSDKConfig {
|
|
208
208
|
network?: string;
|
|
209
209
|
connection?: any;
|
package/dist/types.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Type definitions for Five SDK.
|
|
3
3
|
*/
|
|
4
4
|
// ==================== Legacy SDK Types (for compatibility) ====================
|
|
5
|
-
export const FIVE_VM_PROGRAM_ID = "
|
|
5
|
+
export const FIVE_VM_PROGRAM_ID = "4Qxf3pbCse2veUgZVMiAm3nWqJrYo2pT4suxHKMJdK1d";
|
|
6
6
|
export class FiveSDKError extends Error {
|
|
7
7
|
constructor(message, code, details) {
|
|
8
8
|
super(message);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@5ive-tech/sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.8",
|
|
4
4
|
"description": "Client-agnostic TypeScript SDK for Five VM scripts on Solana",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -56,4 +56,4 @@
|
|
|
56
56
|
"publishConfig": {
|
|
57
57
|
"access": "public"
|
|
58
58
|
}
|
|
59
|
-
}
|
|
59
|
+
}
|