@neuraiproject/neurai-assets 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +522 -0
- package/examples/01-create-root-asset.js +71 -0
- package/examples/02-create-sub-asset.js +79 -0
- package/examples/03-create-nfts.js +140 -0
- package/examples/04-reissue-asset.js +164 -0
- package/examples/05-create-qualifier-and-tag.js +209 -0
- package/examples/06-create-restricted-asset.js +223 -0
- package/examples/07-freeze-and-unfreeze.js +292 -0
- package/examples/08-query-assets.js +332 -0
- package/examples/09-wallet-integration.js +320 -0
- package/examples/README.md +319 -0
- package/package.json +43 -0
- package/src/NeuraiAssets.js +468 -0
- package/src/builders/BaseAssetTransactionBuilder.js +303 -0
- package/src/builders/FreezeAddressBuilder.js +271 -0
- package/src/builders/IssueQualifierBuilder.js +251 -0
- package/src/builders/IssueRestrictedBuilder.js +187 -0
- package/src/builders/IssueRootBuilder.js +173 -0
- package/src/builders/IssueSubBuilder.js +237 -0
- package/src/builders/IssueUniqueBuilder.js +255 -0
- package/src/builders/ReissueBuilder.js +246 -0
- package/src/builders/ReissueRestrictedBuilder.js +264 -0
- package/src/builders/TagAddressBuilder.js +243 -0
- package/src/builders/index.js +38 -0
- package/src/constants/assetTypes.js +23 -0
- package/src/constants/burnAddresses.js +65 -0
- package/src/constants/fees.js +61 -0
- package/src/constants/index.js +44 -0
- package/src/constants/networks.js +112 -0
- package/src/errors/AssetErrors.js +135 -0
- package/src/errors/ValidationErrors.js +87 -0
- package/src/errors/index.js +56 -0
- package/src/index.js +68 -0
- package/src/managers/BurnManager.js +222 -0
- package/src/managers/OutputOrderer.js +289 -0
- package/src/managers/OwnerTokenManager.js +265 -0
- package/src/managers/UTXOSelector.js +309 -0
- package/src/managers/index.js +16 -0
- package/src/queries/AssetQueries.js +447 -0
- package/src/queries/index.js +10 -0
- package/src/utils/amountConverter.js +115 -0
- package/src/utils/assetNameParser.js +203 -0
- package/src/utils/index.js +16 -0
- package/src/utils/networkDetector.js +144 -0
- package/src/utils/outputFormatter.js +292 -0
- package/src/validators/amountValidator.js +149 -0
- package/src/validators/assetNameValidator.js +296 -0
- package/src/validators/index.js +16 -0
- package/src/validators/ipfsValidator.js +101 -0
- package/src/validators/verifierValidator.js +146 -0
- package/tests/README.md +126 -0
- package/tests/integration/assetLifecycle.test.js +244 -0
- package/tests/mocks/rpcMock.js +156 -0
- package/tests/unit/NeuraiAssets.test.js +217 -0
- package/tests/unit/utils/amountConverter.test.js +171 -0
- package/tests/unit/utils/assetNameParser.test.js +203 -0
- package/tests/unit/validators/amountValidator.test.js +143 -0
- package/tests/unit/validators/assetNameValidator.test.js +228 -0
package/tests/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Tests para @neuraiproject/neurai-assets
|
|
2
|
+
|
|
3
|
+
Esta carpeta contiene la suite completa de tests para la librería de gestión de activos de Neurai.
|
|
4
|
+
|
|
5
|
+
## Estructura de Tests
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
tests/
|
|
9
|
+
├── mocks/ # Mocks para RPC y dependencias
|
|
10
|
+
│ └── rpcMock.js # Mock del RPC de Neurai
|
|
11
|
+
├── unit/ # Tests unitarios
|
|
12
|
+
│ ├── validators/ # Tests para validadores
|
|
13
|
+
│ ├── utils/ # Tests para utilidades
|
|
14
|
+
│ └── NeuraiAssets.test.js # Tests de la clase principal
|
|
15
|
+
└── integration/ # Tests de integración
|
|
16
|
+
└── assetLifecycle.test.js # Tests del ciclo de vida completo
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Ejecutar Tests
|
|
20
|
+
|
|
21
|
+
### Todos los tests
|
|
22
|
+
```bash
|
|
23
|
+
npm test
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Solo tests unitarios
|
|
27
|
+
```bash
|
|
28
|
+
npm run test:unit
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Solo tests de integración
|
|
32
|
+
```bash
|
|
33
|
+
npm run test:integration
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Tests en modo watch (re-ejecuta al guardar cambios)
|
|
37
|
+
```bash
|
|
38
|
+
npm run test:watch
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Cobertura de Tests
|
|
42
|
+
|
|
43
|
+
### Validators (Validadores)
|
|
44
|
+
- **assetNameValidator.test.js**: Valida nombres de activos ROOT, SUB, UNIQUE, QUALIFIER, RESTRICTED y OWNER
|
|
45
|
+
- **amountValidator.test.js**: Valida cantidades, unidades y rangos de activos
|
|
46
|
+
|
|
47
|
+
### Utils (Utilidades)
|
|
48
|
+
- **assetNameParser.test.js**: Prueba el parsing y detección de tipos de activos
|
|
49
|
+
- **amountConverter.test.js**: Prueba conversiones entre cantidades y satoshis
|
|
50
|
+
|
|
51
|
+
### Clase Principal
|
|
52
|
+
- **NeuraiAssets.test.js**: Prueba la clase principal y sus métodos
|
|
53
|
+
|
|
54
|
+
### Integración
|
|
55
|
+
- **assetLifecycle.test.js**: Prueba workflows completos de creación y gestión de activos
|
|
56
|
+
|
|
57
|
+
## Tests Actuales
|
|
58
|
+
|
|
59
|
+
Total: **124 tests pasando**
|
|
60
|
+
|
|
61
|
+
### Desglose por módulo:
|
|
62
|
+
- AmountConverter: 24 tests
|
|
63
|
+
- AssetNameParser: 29 tests
|
|
64
|
+
- AmountValidator: 21 tests
|
|
65
|
+
- AssetNameValidator: 33 tests
|
|
66
|
+
- NeuraiAssets: 10 tests
|
|
67
|
+
- Integration Tests: 7 tests
|
|
68
|
+
|
|
69
|
+
## Mocks Disponibles
|
|
70
|
+
|
|
71
|
+
### RPCMock
|
|
72
|
+
Mock flexible del RPC de Neurai que permite:
|
|
73
|
+
- Configurar respuestas personalizadas para métodos específicos
|
|
74
|
+
- Rastrear llamadas realizadas
|
|
75
|
+
- Simular errores y casos edge
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
const { createMockRPC, createMockUTXO, createMockAssetData } = require('./mocks/rpcMock');
|
|
79
|
+
|
|
80
|
+
// Crear mock con respuestas personalizadas
|
|
81
|
+
const mockRPC = createMockRPC({
|
|
82
|
+
'getassetdata': { name: 'MYTOKEN', amount: 1000000 }
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Usar en tests
|
|
86
|
+
const assets = new NeuraiAssets(mockRPC);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Agregar Nuevos Tests
|
|
90
|
+
|
|
91
|
+
Para agregar nuevos tests:
|
|
92
|
+
|
|
93
|
+
1. **Tests unitarios**: Coloca el archivo en `tests/unit/[modulo]/`
|
|
94
|
+
2. **Tests de integración**: Coloca el archivo en `tests/integration/`
|
|
95
|
+
3. **Nombra el archivo**: `*.test.js` para que Mocha lo detecte automáticamente
|
|
96
|
+
4. **Usa la estructura estándar**:
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
const { expect } = require('chai');
|
|
100
|
+
|
|
101
|
+
describe('NombreDelModulo', () => {
|
|
102
|
+
describe('nombreDelMetodo', () => {
|
|
103
|
+
it('debería hacer algo específico', () => {
|
|
104
|
+
// Arrange
|
|
105
|
+
const input = 'valor';
|
|
106
|
+
|
|
107
|
+
// Act
|
|
108
|
+
const result = someFunction(input);
|
|
109
|
+
|
|
110
|
+
// Assert
|
|
111
|
+
expect(result).to.equal('esperado');
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Frameworks Utilizados
|
|
118
|
+
|
|
119
|
+
- **Mocha**: Framework de testing
|
|
120
|
+
- **Chai**: Librería de assertions (expect)
|
|
121
|
+
|
|
122
|
+
## Notas
|
|
123
|
+
|
|
124
|
+
- Los tests no requieren conexión a un nodo Neurai real
|
|
125
|
+
- Todos los tests usan mocks para simular llamadas RPC
|
|
126
|
+
- Los tests verifican tanto casos exitosos como errores esperados
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Tests - Asset Lifecycle
|
|
3
|
+
* Tests the complete workflow of asset operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { expect } = require('chai');
|
|
7
|
+
const NeuraiAssets = require('../../src/NeuraiAssets');
|
|
8
|
+
const AssetNameValidator = require('../../src/validators/assetNameValidator');
|
|
9
|
+
const AssetNameParser = require('../../src/utils/assetNameParser');
|
|
10
|
+
const AmountConverter = require('../../src/utils/amountConverter');
|
|
11
|
+
const { createMockRPC, createMockUTXO, createMockAssetData } = require('../mocks/rpcMock');
|
|
12
|
+
|
|
13
|
+
describe('Integration: Asset Lifecycle', () => {
|
|
14
|
+
describe('Validator and Parser Integration', () => {
|
|
15
|
+
it('should validate and parse ROOT assets correctly', () => {
|
|
16
|
+
const assetName = 'MYTOKEN';
|
|
17
|
+
|
|
18
|
+
expect(AssetNameValidator.validateRoot(assetName)).to.be.true;
|
|
19
|
+
|
|
20
|
+
const parsed = AssetNameParser.parse(assetName);
|
|
21
|
+
expect(parsed.type).to.equal(0);
|
|
22
|
+
expect(parsed.name).to.equal(assetName);
|
|
23
|
+
expect(parsed.isOwner).to.be.false;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should validate and parse UNIQUE assets correctly', () => {
|
|
27
|
+
const assetName = 'MYTOKEN#NFT1';
|
|
28
|
+
|
|
29
|
+
expect(AssetNameValidator.validateUnique(assetName)).to.be.true;
|
|
30
|
+
|
|
31
|
+
const parsed = AssetNameParser.parse(assetName);
|
|
32
|
+
expect(parsed.type).to.equal(2);
|
|
33
|
+
expect(parsed.parent).to.equal('MYTOKEN');
|
|
34
|
+
expect(parsed.tag).to.equal('NFT1');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should validate and parse QUALIFIER assets correctly', () => {
|
|
38
|
+
const assetName = '#KYC_VERIFIED';
|
|
39
|
+
|
|
40
|
+
expect(AssetNameValidator.validateQualifier(assetName)).to.be.true;
|
|
41
|
+
|
|
42
|
+
const parsed = AssetNameParser.parse(assetName);
|
|
43
|
+
expect(parsed.type).to.equal(4);
|
|
44
|
+
expect(parsed.isQualifier).to.be.true;
|
|
45
|
+
expect(parsed.prefix).to.equal('#');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should validate and parse RESTRICTED assets correctly', () => {
|
|
49
|
+
const assetName = '$SECURITY_TOKEN';
|
|
50
|
+
|
|
51
|
+
expect(AssetNameValidator.validateRestricted(assetName)).to.be.true;
|
|
52
|
+
|
|
53
|
+
const parsed = AssetNameParser.parse(assetName);
|
|
54
|
+
expect(parsed.type).to.equal(6);
|
|
55
|
+
expect(parsed.isRestricted).to.be.true;
|
|
56
|
+
expect(parsed.prefix).to.equal('$');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('Amount Conversion Integration', () => {
|
|
61
|
+
it('should convert amounts correctly for different units', () => {
|
|
62
|
+
const testCases = [
|
|
63
|
+
{ amount: 100, units: 0, expected: 100 },
|
|
64
|
+
{ amount: 1.5, units: 1, expected: 15 },
|
|
65
|
+
{ amount: 1.5, units: 2, expected: 150 },
|
|
66
|
+
{ amount: 1.00000001, units: 8, expected: 100000001 }
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
testCases.forEach(({ amount, units, expected }) => {
|
|
70
|
+
const satoshis = AmountConverter.toSatoshis(amount, units);
|
|
71
|
+
expect(satoshis).to.equal(expected);
|
|
72
|
+
|
|
73
|
+
const back = AmountConverter.fromSatoshis(satoshis, units);
|
|
74
|
+
expect(back).to.equal(amount);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should format and parse amounts correctly', () => {
|
|
79
|
+
const amount = 1.5;
|
|
80
|
+
const units = 2;
|
|
81
|
+
|
|
82
|
+
const formatted = AmountConverter.format(amount, units);
|
|
83
|
+
expect(formatted).to.equal('1.50');
|
|
84
|
+
|
|
85
|
+
const parsed = AmountConverter.parse(formatted);
|
|
86
|
+
expect(parsed).to.equal(amount);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('NeuraiAssets Integration', () => {
|
|
91
|
+
let assets;
|
|
92
|
+
let mockRPC;
|
|
93
|
+
|
|
94
|
+
beforeEach(() => {
|
|
95
|
+
mockRPC = createMockRPC({
|
|
96
|
+
'listunspent': [
|
|
97
|
+
createMockUTXO('N123...', 100),
|
|
98
|
+
createMockUTXO('N123...', 50, 'MYTOKEN')
|
|
99
|
+
],
|
|
100
|
+
'getassetdata': createMockAssetData('MYTOKEN', {
|
|
101
|
+
amount: 1000000,
|
|
102
|
+
units: 2,
|
|
103
|
+
reissuable: true
|
|
104
|
+
})
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
assets = new NeuraiAssets(mockRPC, {
|
|
108
|
+
network: 'xna',
|
|
109
|
+
addresses: ['N123...'],
|
|
110
|
+
changeAddress: 'N123...',
|
|
111
|
+
toAddress: 'N456...'
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should validate asset name before operations', async () => {
|
|
116
|
+
expect(() => assets.getAssetType('invalid-name'))
|
|
117
|
+
.to.not.throw();
|
|
118
|
+
|
|
119
|
+
const type = assets.getAssetType('MYTOKEN');
|
|
120
|
+
expect(type).to.equal('ROOT');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should query asset data successfully', async () => {
|
|
124
|
+
const data = await assets.getAssetData('MYTOKEN');
|
|
125
|
+
expect(data.name).to.equal('MYTOKEN');
|
|
126
|
+
expect(data.amount).to.equal(1000000);
|
|
127
|
+
expect(data.units).to.equal(2);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should detect different asset types', () => {
|
|
131
|
+
const testCases = [
|
|
132
|
+
{ name: 'MYTOKEN', expected: 'ROOT' },
|
|
133
|
+
{ name: 'MYTOKEN/SUB', expected: 'SUB' },
|
|
134
|
+
{ name: 'MYTOKEN#NFT', expected: 'UNIQUE' },
|
|
135
|
+
{ name: '#KYC', expected: 'QUALIFIER' },
|
|
136
|
+
{ name: '$SECURITY', expected: 'RESTRICTED' },
|
|
137
|
+
{ name: 'MYTOKEN!', expected: 'OWNER' }
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
testCases.forEach(({ name, expected }) => {
|
|
141
|
+
const type = assets.getAssetType(name);
|
|
142
|
+
expect(type).to.equal(expected);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe('Complex Workflows', () => {
|
|
148
|
+
it('should handle owner token derivation', () => {
|
|
149
|
+
const assetName = 'MYTOKEN';
|
|
150
|
+
const ownerToken = AssetNameParser.getOwnerTokenName(assetName);
|
|
151
|
+
|
|
152
|
+
expect(ownerToken).to.equal('MYTOKEN!');
|
|
153
|
+
expect(AssetNameParser.isOwnerToken(ownerToken)).to.be.true;
|
|
154
|
+
|
|
155
|
+
const baseAsset = AssetNameParser.getBaseAssetName(ownerToken);
|
|
156
|
+
expect(baseAsset).to.equal(assetName);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should handle sub-asset creation workflow', () => {
|
|
160
|
+
const rootName = 'MYTOKEN';
|
|
161
|
+
const subName = 'ALPHA';
|
|
162
|
+
|
|
163
|
+
expect(AssetNameValidator.validateRoot(rootName)).to.be.true;
|
|
164
|
+
|
|
165
|
+
const fullSubName = AssetNameParser.buildSubName(rootName, subName);
|
|
166
|
+
expect(fullSubName).to.equal('MYTOKEN/ALPHA');
|
|
167
|
+
|
|
168
|
+
expect(AssetNameValidator.validateSub(fullSubName)).to.be.true;
|
|
169
|
+
|
|
170
|
+
const parsed = AssetNameParser.parse(fullSubName);
|
|
171
|
+
expect(parsed.parent).to.equal(rootName);
|
|
172
|
+
expect(parsed.subName).to.equal(subName);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should handle unique asset creation workflow', () => {
|
|
176
|
+
const rootName = 'MYTOKEN';
|
|
177
|
+
const tag = 'NFT001';
|
|
178
|
+
|
|
179
|
+
expect(AssetNameValidator.validateRoot(rootName)).to.be.true;
|
|
180
|
+
|
|
181
|
+
const uniqueName = AssetNameParser.buildUniqueName(rootName, tag);
|
|
182
|
+
expect(uniqueName).to.equal('MYTOKEN#NFT001');
|
|
183
|
+
|
|
184
|
+
expect(AssetNameValidator.validateUnique(uniqueName)).to.be.true;
|
|
185
|
+
|
|
186
|
+
const parsed = AssetNameParser.parse(uniqueName);
|
|
187
|
+
expect(parsed.parent).to.equal(rootName);
|
|
188
|
+
expect(parsed.tag).to.equal(tag);
|
|
189
|
+
expect(parsed.type).to.equal(2);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should handle restricted asset workflow', () => {
|
|
193
|
+
const assetName = '$SECURITY';
|
|
194
|
+
|
|
195
|
+
expect(AssetNameValidator.validateRestricted(assetName)).to.be.true;
|
|
196
|
+
expect(AssetNameParser.isRestricted(assetName)).to.be.true;
|
|
197
|
+
|
|
198
|
+
const parsed = AssetNameParser.parse(assetName);
|
|
199
|
+
expect(parsed.type).to.equal(6);
|
|
200
|
+
expect(parsed.prefix).to.equal('$');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should handle qualifier workflow', () => {
|
|
204
|
+
const qualifierName = '#KYC_VERIFIED';
|
|
205
|
+
|
|
206
|
+
expect(AssetNameValidator.validateQualifier(qualifierName)).to.be.true;
|
|
207
|
+
expect(AssetNameParser.isQualifier(qualifierName)).to.be.true;
|
|
208
|
+
|
|
209
|
+
const parsed = AssetNameParser.parse(qualifierName);
|
|
210
|
+
expect(parsed.type).to.equal(4);
|
|
211
|
+
expect(parsed.isQualifier).to.be.true;
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
describe('Error Handling Integration', () => {
|
|
216
|
+
it('should propagate validation errors correctly', () => {
|
|
217
|
+
expect(() => AssetNameValidator.validateRoot('ab'))
|
|
218
|
+
.to.throw();
|
|
219
|
+
|
|
220
|
+
expect(() => AssetNameValidator.validateSub('INVALID'))
|
|
221
|
+
.to.throw();
|
|
222
|
+
|
|
223
|
+
expect(() => AssetNameValidator.validateUnique('INVALID'))
|
|
224
|
+
.to.throw();
|
|
225
|
+
|
|
226
|
+
expect(() => AssetNameValidator.validateQualifier('INVALID'))
|
|
227
|
+
.to.throw();
|
|
228
|
+
|
|
229
|
+
expect(() => AssetNameValidator.validateRestricted('INVALID'))
|
|
230
|
+
.to.throw();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should handle amount conversion errors', () => {
|
|
234
|
+
expect(() => AmountConverter.toSatoshis('invalid', 0))
|
|
235
|
+
.to.throw();
|
|
236
|
+
|
|
237
|
+
expect(() => AmountConverter.toSatoshis(100, -1))
|
|
238
|
+
.to.throw();
|
|
239
|
+
|
|
240
|
+
expect(() => AmountConverter.fromSatoshis('invalid', 0))
|
|
241
|
+
.to.throw();
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock RPC Function for Testing
|
|
3
|
+
* Simulates Neurai RPC calls without requiring a live node
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class RPCMock {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.mockResponses = new Map();
|
|
9
|
+
this.callHistory = [];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Set mock response for a specific RPC method
|
|
14
|
+
* @param {string} method - RPC method name
|
|
15
|
+
* @param {*} response - Response to return
|
|
16
|
+
*/
|
|
17
|
+
setMockResponse(method, response) {
|
|
18
|
+
this.mockResponses.set(method, response);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Mock RPC function
|
|
23
|
+
* @param {string} method - RPC method name
|
|
24
|
+
* @param {Array} params - RPC parameters
|
|
25
|
+
* @returns {Promise<*>} Mock response
|
|
26
|
+
*/
|
|
27
|
+
async call(method, ...params) {
|
|
28
|
+
this.callHistory.push({ method, params });
|
|
29
|
+
|
|
30
|
+
if (this.mockResponses.has(method)) {
|
|
31
|
+
const response = this.mockResponses.get(method);
|
|
32
|
+
|
|
33
|
+
// If response is a function, call it with params
|
|
34
|
+
if (typeof response === 'function') {
|
|
35
|
+
return response(...params);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return response;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Default responses for common methods
|
|
42
|
+
return this.getDefaultResponse(method, params);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get default response for common RPC methods
|
|
47
|
+
*/
|
|
48
|
+
getDefaultResponse(method, params) {
|
|
49
|
+
const defaults = {
|
|
50
|
+
'listunspent': [],
|
|
51
|
+
'getassetdata': { name: params[0], amount: 1000000 },
|
|
52
|
+
'listassets': [],
|
|
53
|
+
'listmyassets': {},
|
|
54
|
+
'listaddressesbyasset': [],
|
|
55
|
+
'listassetbalancesbyaddress': [],
|
|
56
|
+
'checkaddresstag': false,
|
|
57
|
+
'checkaddressrestriction': true,
|
|
58
|
+
'isaddressfrozen': false,
|
|
59
|
+
'checkglobalrestriction': false,
|
|
60
|
+
'getverifierstring': '',
|
|
61
|
+
'isvalidverifierstring': true
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return defaults[method] || null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Clear call history
|
|
69
|
+
*/
|
|
70
|
+
clearHistory() {
|
|
71
|
+
this.callHistory = [];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get number of times a method was called
|
|
76
|
+
*/
|
|
77
|
+
getCallCount(method) {
|
|
78
|
+
return this.callHistory.filter(call => call.method === method).length;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get last call for a method
|
|
83
|
+
*/
|
|
84
|
+
getLastCall(method) {
|
|
85
|
+
const calls = this.callHistory.filter(call => call.method === method);
|
|
86
|
+
return calls[calls.length - 1];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Reset all mocks
|
|
91
|
+
*/
|
|
92
|
+
reset() {
|
|
93
|
+
this.mockResponses.clear();
|
|
94
|
+
this.callHistory = [];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Create a simple mock RPC function
|
|
100
|
+
*/
|
|
101
|
+
function createMockRPC(responses = {}) {
|
|
102
|
+
const mock = new RPCMock();
|
|
103
|
+
|
|
104
|
+
Object.entries(responses).forEach(([method, response]) => {
|
|
105
|
+
mock.setMockResponse(method, response);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return mock.call.bind(mock);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Create mock UTXO data
|
|
113
|
+
*/
|
|
114
|
+
function createMockUTXO(address, amount = 100, assetName = null) {
|
|
115
|
+
const utxo = {
|
|
116
|
+
txid: '0000000000000000000000000000000000000000000000000000000000000001',
|
|
117
|
+
vout: 0,
|
|
118
|
+
address: address,
|
|
119
|
+
scriptPubKey: '76a914...',
|
|
120
|
+
amount: assetName ? undefined : amount,
|
|
121
|
+
confirmations: 10,
|
|
122
|
+
spendable: true,
|
|
123
|
+
solvable: true,
|
|
124
|
+
safe: true
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
if (assetName) {
|
|
128
|
+
utxo.assetName = assetName;
|
|
129
|
+
utxo.assetAmount = amount;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return utxo;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Create mock asset data
|
|
137
|
+
*/
|
|
138
|
+
function createMockAssetData(assetName, options = {}) {
|
|
139
|
+
return {
|
|
140
|
+
name: assetName,
|
|
141
|
+
amount: options.amount || 1000000,
|
|
142
|
+
units: options.units || 0,
|
|
143
|
+
reissuable: options.reissuable !== undefined ? options.reissuable : true,
|
|
144
|
+
has_ipfs: options.hasIpfs || false,
|
|
145
|
+
ipfs_hash: options.ipfsHash || '',
|
|
146
|
+
block_height: 1000,
|
|
147
|
+
blockhash: '0000000000000000000000000000000000000000000000000000000000000001'
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
RPCMock,
|
|
153
|
+
createMockRPC,
|
|
154
|
+
createMockUTXO,
|
|
155
|
+
createMockAssetData
|
|
156
|
+
};
|