@stellar/typescript-wallet-sdk 1.0.0-alpha.2 → 1.1.0-alpha.1
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/lib/bundle.js +1541 -80
- package/lib/bundle.js.map +1 -1
- package/lib/walletSdk/Watcher/Types.d.ts +65 -0
- package/lib/walletSdk/Watcher/index.d.ts +54 -0
- package/lib/walletSdk/anchor/Types.d.ts +76 -0
- package/lib/walletSdk/anchor/index.d.ts +78 -7
- package/lib/walletSdk/auth/WalletSigner.d.ts +3 -0
- package/lib/walletSdk/auth/index.d.ts +8 -5
- package/lib/walletSdk/exception/index.d.ts +9 -0
- package/lib/walletSdk/horizon/Account.d.ts +20 -0
- package/lib/walletSdk/horizon/AccountService.d.ts +8 -0
- package/lib/walletSdk/horizon/Stellar.d.ts +5 -1
- package/lib/walletSdk/index.d.ts +12 -13
- package/lib/walletSdk/interactive/index.d.ts +16 -5
- package/lib/walletSdk/util/camelToSnakeCase.d.ts +2 -0
- package/lib/walletSdk/util/sleep.d.ts +1 -0
- package/package.json +12 -6
- package/src/walletSdk/Anchor/Types.ts +79 -0
- package/src/walletSdk/Anchor/index.ts +224 -24
- package/src/walletSdk/Auth/WalletSigner.ts +18 -3
- package/src/walletSdk/Auth/index.ts +41 -15
- package/src/walletSdk/Watcher/Types.ts +81 -0
- package/src/walletSdk/Watcher/index.ts +355 -0
- package/src/walletSdk/exception/index.ts +23 -2
- package/src/walletSdk/horizon/Account.ts +52 -0
- package/src/walletSdk/horizon/AccountService.ts +19 -0
- package/src/walletSdk/horizon/Stellar.ts +10 -2
- package/src/walletSdk/index.ts +41 -28
- package/src/walletSdk/interactive/index.ts +32 -43
- package/src/walletSdk/util/camelToSnakeCase.ts +13 -0
- package/test/account.test.ts +36 -0
- package/test/fixtures/TransactionsResponse.ts +230 -0
- package/test/wallet.test.ts +1722 -0
- package/test/index.test.ts +0 -73
|
@@ -0,0 +1,1722 @@
|
|
|
1
|
+
import StellarSdk, { Keypair } from "stellar-sdk";
|
|
2
|
+
import http from "http";
|
|
3
|
+
import sinon from "sinon";
|
|
4
|
+
|
|
5
|
+
import sdk from "../src";
|
|
6
|
+
import { AssetNotSupportedError, ServerRequestFailedError } from "../src/walletSdk/exception";
|
|
7
|
+
import {
|
|
8
|
+
TransactionStatus,
|
|
9
|
+
WatcherResponse,
|
|
10
|
+
} from "../src/walletSdk/Watcher/Types";
|
|
11
|
+
import { Watcher } from "../src/walletSdk/Watcher";
|
|
12
|
+
|
|
13
|
+
import { TransactionsResponse } from "../test/fixtures/TransactionsResponse";
|
|
14
|
+
|
|
15
|
+
const originalSetTimeout = global.setTimeout;
|
|
16
|
+
function sleep(time: number) {
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
originalSetTimeout(resolve, time);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { walletSdk } = sdk;
|
|
23
|
+
describe("Wallet", () => {
|
|
24
|
+
it("should init", () => {
|
|
25
|
+
walletSdk.Wallet.TestNet();
|
|
26
|
+
walletSdk.Wallet.MainNet();
|
|
27
|
+
});
|
|
28
|
+
it("should be able return a client", async () => {
|
|
29
|
+
let appConfig = new walletSdk.ApplicationConfiguration();
|
|
30
|
+
let wal = new walletSdk.Wallet(
|
|
31
|
+
walletSdk.StellarConfiguration.TestNet(),
|
|
32
|
+
appConfig
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
let client = wal.getClient();
|
|
36
|
+
|
|
37
|
+
// custom client
|
|
38
|
+
client = wal.getClient({
|
|
39
|
+
httpAgent: new http.Agent({ keepAlive: false }),
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("SEP-24 flow", () => {
|
|
45
|
+
it("should init a wallet with network and domain", () => {
|
|
46
|
+
const Wal = walletSdk.Wallet.TestNet();
|
|
47
|
+
Wal.anchor("anchor-domain");
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
let anchor;
|
|
52
|
+
let accountKp;
|
|
53
|
+
let authToken;
|
|
54
|
+
describe("Anchor", () => {
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
const Wal = walletSdk.Wallet.TestNet();
|
|
57
|
+
anchor = Wal.anchor("testanchor.stellar.org");
|
|
58
|
+
accountKp = Keypair.fromSecret(
|
|
59
|
+
"SDXC3OHSJZEQIXKEWFDNEZEQ7SW5DWBPW7RKUWI36ILY3QZZ6VER7TXV"
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
it("should give TOML info", async () => {
|
|
63
|
+
const resp = await anchor.getInfo();
|
|
64
|
+
|
|
65
|
+
expect(resp.webAuthEndpoint).toBe("https://testanchor.stellar.org/auth");
|
|
66
|
+
expect(resp.currencies.length).toBe(2);
|
|
67
|
+
});
|
|
68
|
+
it("should be able to authenticate", async () => {
|
|
69
|
+
const auth = await anchor.auth();
|
|
70
|
+
|
|
71
|
+
authToken = await auth.authenticate(accountKp);
|
|
72
|
+
expect(authToken).toBeTruthy();
|
|
73
|
+
expect(typeof authToken).toBe("string");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should be able to authenticate with client domain", async () => {
|
|
77
|
+
const auth = await anchor.auth();
|
|
78
|
+
let signedByClient = false;
|
|
79
|
+
let signedByDomain = false;
|
|
80
|
+
|
|
81
|
+
const walletSigner = {
|
|
82
|
+
signWithClientAccount: (txn, account) => {
|
|
83
|
+
txn.sign(account);
|
|
84
|
+
signedByClient = true;
|
|
85
|
+
return txn;
|
|
86
|
+
},
|
|
87
|
+
signWithDomainAccount: (transactionXDR, networkPassPhrase, account) => {
|
|
88
|
+
// dummy secret key for signing
|
|
89
|
+
const clientDomainKp = Keypair.fromSecret(
|
|
90
|
+
"SC7PKBRGRI5X4XP4QICBZ2NL67VUJJVKFKXDTGSPI3SQYZGC4NZWONIH"
|
|
91
|
+
);
|
|
92
|
+
const transaction = StellarSdk.TransactionBuilder.fromXDR(
|
|
93
|
+
transactionXDR,
|
|
94
|
+
networkPassPhrase
|
|
95
|
+
);
|
|
96
|
+
transaction.sign(clientDomainKp);
|
|
97
|
+
signedByDomain = true;
|
|
98
|
+
return transaction;
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// because using dummy sk and not real demo wallet sk, lets just check that signing is called
|
|
103
|
+
const challengeResponse = await auth.challenge(
|
|
104
|
+
accountKp,
|
|
105
|
+
"",
|
|
106
|
+
"demo-wallet-server.stellar.org"
|
|
107
|
+
);
|
|
108
|
+
const txn = auth.sign(accountKp, challengeResponse, walletSigner);
|
|
109
|
+
expect(txn).toBeTruthy();
|
|
110
|
+
expect(signedByClient).toBe(true);
|
|
111
|
+
expect(signedByDomain).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
it("should get anchor services info", async () => {
|
|
114
|
+
const serviceInfo = await anchor.getServicesInfo();
|
|
115
|
+
|
|
116
|
+
expect(serviceInfo.deposit).toBeTruthy();
|
|
117
|
+
expect(serviceInfo.withdraw).toBeTruthy();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should give interactive deposit url", async () => {
|
|
121
|
+
const assetCode = "SRT";
|
|
122
|
+
const resp = await anchor.interactive().deposit({
|
|
123
|
+
accountAddress: accountKp.publicKey(),
|
|
124
|
+
assetCode,
|
|
125
|
+
authToken,
|
|
126
|
+
lang: "en-US",
|
|
127
|
+
extraFields: {
|
|
128
|
+
wallet_name: "Test Wallet",
|
|
129
|
+
wallet_url: "https://stellar.org/",
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
expect(resp.url).toBeTruthy();
|
|
134
|
+
expect(resp.id).toBeTruthy();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("should give interactive withdraw url", async () => {
|
|
138
|
+
const assetCode = "SRT";
|
|
139
|
+
const resp = await anchor.interactive().withdraw({
|
|
140
|
+
accountAddress: accountKp.publicKey(),
|
|
141
|
+
assetCode,
|
|
142
|
+
authToken,
|
|
143
|
+
lang: "en-US",
|
|
144
|
+
extraFields: {
|
|
145
|
+
wallet_name: "Test Wallet",
|
|
146
|
+
wallet_url: "https://stellar.org/",
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
expect(resp.url).toBeTruthy();
|
|
151
|
+
expect(resp.id).toBeTruthy();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("should fetch new transaction by id", async () => {
|
|
155
|
+
const assetCode = "SRT";
|
|
156
|
+
|
|
157
|
+
// creates new 'incomplete' deposit transaction
|
|
158
|
+
const { id: transactionId } = await anchor.interactive().deposit({
|
|
159
|
+
accountAddress: accountKp.publicKey(),
|
|
160
|
+
assetCode,
|
|
161
|
+
authToken,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// fetches transaction that has just been created
|
|
165
|
+
const transaction = await anchor.getTransactionBy({
|
|
166
|
+
authToken,
|
|
167
|
+
id: transactionId,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const { id, kind, status, amount_in, amount_out } = transaction;
|
|
171
|
+
|
|
172
|
+
expect(transaction).toBeTruthy();
|
|
173
|
+
expect(id === transactionId).toBeTruthy;
|
|
174
|
+
expect(kind === "deposit").toBeTruthy;
|
|
175
|
+
expect(status === "incomplete").toBeTruthy;
|
|
176
|
+
// we expect fresh 'incomplete' transactions to not have amounts set yet
|
|
177
|
+
expect(amount_in).toBeFalsy;
|
|
178
|
+
expect(amount_out).toBeFalsy;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("should error fetching non-existing transaction by id", async () => {
|
|
182
|
+
await expect(async () => {
|
|
183
|
+
const nonExistingTransactionId = "da8575e9-edc6-4f99-98cf-2b302f203cc7";
|
|
184
|
+
await anchor.getTransactionBy({
|
|
185
|
+
authToken,
|
|
186
|
+
id: nonExistingTransactionId,
|
|
187
|
+
});
|
|
188
|
+
}).rejects.toThrowError(ServerRequestFailedError);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("should fetch 5 existing transactions by token code", async () => {
|
|
192
|
+
const transactions = await anchor.getTransactionsForAsset({
|
|
193
|
+
authToken,
|
|
194
|
+
assetCode: "SRT",
|
|
195
|
+
limit: 5,
|
|
196
|
+
lang: "en-US",
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
transactions.forEach(({ status }) => {
|
|
200
|
+
expect(status).toBeTruthy();
|
|
201
|
+
expect(typeof status).toBe("string");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
expect(transactions.length === 5).toBeTruthy();
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("should fetch 3 existing deposit transactions by token code", async () => {
|
|
208
|
+
const transactions = await anchor.getTransactionsForAsset({
|
|
209
|
+
authToken,
|
|
210
|
+
assetCode: "SRT",
|
|
211
|
+
limit: 3,
|
|
212
|
+
kind: "deposit",
|
|
213
|
+
lang: "en-US",
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
transactions.forEach(({ kind }) => {
|
|
217
|
+
expect(kind === "deposit").toBeTruthy();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
expect(transactions.length === 3).toBeTruthy();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("should fetch 2 existing withdrawal transactions by token code", async () => {
|
|
224
|
+
const transactions = await anchor.getTransactionsForAsset({
|
|
225
|
+
authToken,
|
|
226
|
+
assetCode: "SRT",
|
|
227
|
+
limit: 2,
|
|
228
|
+
kind: "withdrawal",
|
|
229
|
+
lang: "en-US",
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
transactions.forEach(({ kind }) => {
|
|
233
|
+
expect(kind === "withdrawal").toBeTruthy();
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
expect(transactions.length === 2).toBeTruthy();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("should error fetching transactions with invalid pading id", async () => {
|
|
240
|
+
await expect(async () => {
|
|
241
|
+
await anchor.getTransactionsForAsset({
|
|
242
|
+
authToken,
|
|
243
|
+
assetCode: "SRT",
|
|
244
|
+
lang: "en-US",
|
|
245
|
+
pagingId: "randomPagingId",
|
|
246
|
+
});
|
|
247
|
+
}).rejects.toThrowError(ServerRequestFailedError);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("should fetch only finished transactions", async () => {
|
|
251
|
+
// mock TOML response to include the "SRT" currency
|
|
252
|
+
jest
|
|
253
|
+
.spyOn(anchor, "getInfo")
|
|
254
|
+
.mockResolvedValue({
|
|
255
|
+
networkPassphrase: 'Test SDF Network ; September 2015',
|
|
256
|
+
transferServerSep24: 'https://testanchor.stellar.org/sep24',
|
|
257
|
+
webAuthEndpoint: 'https://testanchor.stellar.org/auth',
|
|
258
|
+
accounts: [
|
|
259
|
+
'GCSGSR6KQQ5BP2FXVPWRL6SWPUSFWLVONLIBJZUKTVQB5FYJFVL6XOXE',
|
|
260
|
+
'GDRND2IUXVMHZ4XTB2RZ4AJ3AOLON3WTAOC23XEASB56NHDFW3ED57TW'
|
|
261
|
+
],
|
|
262
|
+
documentation: {
|
|
263
|
+
orgName: 'Stellar Development Foundation',
|
|
264
|
+
orgUrl: 'https://stellar.org',
|
|
265
|
+
orgGithub: 'stellar',
|
|
266
|
+
},
|
|
267
|
+
principals: [],
|
|
268
|
+
currencies: [{ code: "SRT" }, { code: "USDC" }],
|
|
269
|
+
validators: []
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// mock transactions response so we have a few completed/refunded
|
|
273
|
+
jest
|
|
274
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
275
|
+
.mockResolvedValue(TransactionsResponse);
|
|
276
|
+
|
|
277
|
+
const transactions = await anchor.getHistory({
|
|
278
|
+
authToken,
|
|
279
|
+
assetCode: "SRT",
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
expect(transactions.length).toBeGreaterThan(0);
|
|
283
|
+
|
|
284
|
+
transactions.forEach(({ status }) => {
|
|
285
|
+
expect([
|
|
286
|
+
TransactionStatus.completed,
|
|
287
|
+
TransactionStatus.refunded
|
|
288
|
+
].includes(status)).toBeTruthy();
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it("should error fetching history for unsupported asset", async () => {
|
|
293
|
+
await expect(async () => {
|
|
294
|
+
await anchor.getHistory({
|
|
295
|
+
authToken,
|
|
296
|
+
assetCode: "ABC",
|
|
297
|
+
});
|
|
298
|
+
}).rejects.toThrowError(AssetNotSupportedError);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
describe("watchAllTransactions", () => {
|
|
302
|
+
let clock: sinon.SinonFakeTimers;
|
|
303
|
+
let watcher: Watcher;
|
|
304
|
+
|
|
305
|
+
beforeEach(async () => {
|
|
306
|
+
clock = sinon.useFakeTimers(0);
|
|
307
|
+
watcher = anchor.watcher();
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
afterEach(() => {
|
|
311
|
+
clock.restore();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test("Return only pending and incomplete", async () => {
|
|
315
|
+
const onMessage = sinon.spy(() => {
|
|
316
|
+
expect(onMessage.callCount).toBeLessThan(6);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const onError = sinon.spy((e) => {
|
|
320
|
+
expect(e).toBeUndefined();
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// mock default transactions response
|
|
324
|
+
jest
|
|
325
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
326
|
+
.mockResolvedValue(TransactionsResponse);
|
|
327
|
+
|
|
328
|
+
// start watching
|
|
329
|
+
const { stop } = watcher.watchAllTransactions({
|
|
330
|
+
authToken,
|
|
331
|
+
assetCode: "SRT",
|
|
332
|
+
lang: "en-US",
|
|
333
|
+
onMessage,
|
|
334
|
+
onError,
|
|
335
|
+
timeout: 1,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// nothing should run at first (async api call in progress)
|
|
339
|
+
expect(onMessage.callCount).toBe(0);
|
|
340
|
+
expect(onError.callCount).toBe(0);
|
|
341
|
+
|
|
342
|
+
await sleep(1);
|
|
343
|
+
|
|
344
|
+
// 5 pending & incomplete
|
|
345
|
+
expect(onMessage.callCount).toBe(5);
|
|
346
|
+
expect(onError.callCount).toBe(0);
|
|
347
|
+
|
|
348
|
+
// stops watching
|
|
349
|
+
stop();
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
test("Return pending and incomplete + watchlist", async () => {
|
|
353
|
+
const onMessage = sinon.spy(() => {
|
|
354
|
+
expect(onMessage.callCount).toBeLessThan(9);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
const onError = sinon.spy((e) => {
|
|
358
|
+
expect(e).toBeUndefined();
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// mock default transactions response
|
|
362
|
+
jest
|
|
363
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
364
|
+
.mockResolvedValue(TransactionsResponse);
|
|
365
|
+
|
|
366
|
+
// start watching
|
|
367
|
+
const { stop } = watcher.watchAllTransactions({
|
|
368
|
+
authToken,
|
|
369
|
+
assetCode: "SRT",
|
|
370
|
+
lang: "en-US",
|
|
371
|
+
onMessage,
|
|
372
|
+
onError,
|
|
373
|
+
// transactions from watchlist should ALWAYS be returned regardless of their statuses
|
|
374
|
+
watchlist: [
|
|
375
|
+
"hytcf7c4-927d-4b7a-8a1f-d7188ebddu8i", // expired
|
|
376
|
+
"def5d166-5a5e-4d5c-ba5d-271c32cd8abc", // refunded
|
|
377
|
+
"uyt1576b-ac28-4c02-a521-ddbfd9ae7oiu", // completed
|
|
378
|
+
],
|
|
379
|
+
timeout: 1,
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// nothing should run at first (async api call in progress)
|
|
383
|
+
expect(onMessage.callCount).toBe(0);
|
|
384
|
+
expect(onError.callCount).toBe(0);
|
|
385
|
+
|
|
386
|
+
await sleep(1);
|
|
387
|
+
|
|
388
|
+
// 5 pending & incomplete + 3 from watchlist
|
|
389
|
+
expect(onMessage.callCount).toBe(8);
|
|
390
|
+
expect(onError.callCount).toBe(0);
|
|
391
|
+
|
|
392
|
+
// stops watching
|
|
393
|
+
stop();
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test("Watch three transaction updates", async () => {
|
|
397
|
+
const onMessage = sinon.spy(() => {
|
|
398
|
+
expect(onMessage.callCount).toBeLessThan(9);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
const onError = sinon.spy((e) => {
|
|
402
|
+
expect(e).toBeUndefined();
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// mock default transactions response
|
|
406
|
+
jest
|
|
407
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
408
|
+
.mockResolvedValue(TransactionsResponse);
|
|
409
|
+
|
|
410
|
+
// start watching
|
|
411
|
+
const { stop } = watcher.watchAllTransactions({
|
|
412
|
+
authToken,
|
|
413
|
+
assetCode: "SRT",
|
|
414
|
+
lang: "en-US",
|
|
415
|
+
onMessage,
|
|
416
|
+
onError,
|
|
417
|
+
timeout: 1,
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// nothing should run at first (async api call in progress)
|
|
421
|
+
expect(onMessage.callCount).toBe(0);
|
|
422
|
+
expect(onError.callCount).toBe(0);
|
|
423
|
+
|
|
424
|
+
await sleep(1);
|
|
425
|
+
|
|
426
|
+
// 5 pending & incomplete
|
|
427
|
+
expect(onMessage.callCount).toBe(5);
|
|
428
|
+
expect(onError.callCount).toBe(0);
|
|
429
|
+
|
|
430
|
+
// change one transaction to "completed"
|
|
431
|
+
const [firstTxA, ...restA] = TransactionsResponse;
|
|
432
|
+
let updatedTransactions = [
|
|
433
|
+
{
|
|
434
|
+
...firstTxA,
|
|
435
|
+
status: TransactionStatus.completed,
|
|
436
|
+
},
|
|
437
|
+
...restA,
|
|
438
|
+
];
|
|
439
|
+
|
|
440
|
+
// update mock with new transaction status
|
|
441
|
+
jest
|
|
442
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
443
|
+
.mockResolvedValue(updatedTransactions);
|
|
444
|
+
|
|
445
|
+
clock.next();
|
|
446
|
+
await sleep(1);
|
|
447
|
+
|
|
448
|
+
// 5 pending & incomplete + 1 recently completed
|
|
449
|
+
expect(onMessage.callCount).toBe(6);
|
|
450
|
+
expect(onError.callCount).toBe(0);
|
|
451
|
+
|
|
452
|
+
await sleep(1);
|
|
453
|
+
|
|
454
|
+
// 5 pending & incomplete + 1 recently completed
|
|
455
|
+
expect(onMessage.callCount).toBe(6);
|
|
456
|
+
expect(onError.callCount).toBe(0);
|
|
457
|
+
|
|
458
|
+
// change another transaction to "refunded"
|
|
459
|
+
const [firstTxB, secondTxB, ...restB] = updatedTransactions;
|
|
460
|
+
updatedTransactions = [
|
|
461
|
+
firstTxB,
|
|
462
|
+
{
|
|
463
|
+
...secondTxB,
|
|
464
|
+
status: TransactionStatus.refunded,
|
|
465
|
+
},
|
|
466
|
+
...restB,
|
|
467
|
+
];
|
|
468
|
+
|
|
469
|
+
// update mock with new transaction status
|
|
470
|
+
jest
|
|
471
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
472
|
+
.mockResolvedValue(updatedTransactions);
|
|
473
|
+
|
|
474
|
+
clock.next();
|
|
475
|
+
await sleep(1);
|
|
476
|
+
|
|
477
|
+
// 5 pending & incomplete + 1 recently completed + 1 recently refunded
|
|
478
|
+
expect(onMessage.callCount).toBe(7);
|
|
479
|
+
expect(onError.callCount).toBe(0);
|
|
480
|
+
|
|
481
|
+
// change another transaction to "pending_user_transfer_start"
|
|
482
|
+
const [firstTxC, secondTxC, thirdTxC, ...restC] = updatedTransactions;
|
|
483
|
+
updatedTransactions = [
|
|
484
|
+
firstTxC,
|
|
485
|
+
secondTxC,
|
|
486
|
+
{
|
|
487
|
+
...thirdTxC,
|
|
488
|
+
status: TransactionStatus.pending_user_transfer_start,
|
|
489
|
+
},
|
|
490
|
+
...restC,
|
|
491
|
+
];
|
|
492
|
+
|
|
493
|
+
// update mock with new transaction status
|
|
494
|
+
jest
|
|
495
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
496
|
+
.mockResolvedValue(updatedTransactions);
|
|
497
|
+
|
|
498
|
+
clock.next();
|
|
499
|
+
await sleep(1);
|
|
500
|
+
|
|
501
|
+
// 5 pending & incomplete + 1 recently completed + 1 recently refunded + 1 recently pending
|
|
502
|
+
expect(onMessage.callCount).toBe(8);
|
|
503
|
+
expect(onError.callCount).toBe(0);
|
|
504
|
+
|
|
505
|
+
// stops watching
|
|
506
|
+
stop();
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
test("Stops watching after 2 transaction updates", async () => {
|
|
510
|
+
const onMessage = sinon.spy(() => {
|
|
511
|
+
expect(onMessage.callCount).toBeLessThan(8);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
const onError = sinon.spy((e) => {
|
|
515
|
+
expect(e).toBeUndefined();
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
// mock default transactions response
|
|
519
|
+
jest
|
|
520
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
521
|
+
.mockResolvedValue(TransactionsResponse);
|
|
522
|
+
|
|
523
|
+
// start watching
|
|
524
|
+
const { stop } = watcher.watchAllTransactions({
|
|
525
|
+
authToken,
|
|
526
|
+
assetCode: "SRT",
|
|
527
|
+
lang: "en-US",
|
|
528
|
+
onMessage,
|
|
529
|
+
onError,
|
|
530
|
+
timeout: 1,
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
// nothing should run at first (async api call in progress)
|
|
534
|
+
expect(onMessage.callCount).toBe(0);
|
|
535
|
+
expect(onError.callCount).toBe(0);
|
|
536
|
+
|
|
537
|
+
await sleep(1);
|
|
538
|
+
|
|
539
|
+
// 5 pending & incomplete
|
|
540
|
+
expect(onMessage.callCount).toBe(5);
|
|
541
|
+
expect(onError.callCount).toBe(0);
|
|
542
|
+
|
|
543
|
+
// change one transaction to "completed"
|
|
544
|
+
const [firstTxA, ...restA] = TransactionsResponse;
|
|
545
|
+
let updatedTransactions = [
|
|
546
|
+
{
|
|
547
|
+
...firstTxA,
|
|
548
|
+
status: TransactionStatus.completed,
|
|
549
|
+
},
|
|
550
|
+
...restA,
|
|
551
|
+
];
|
|
552
|
+
|
|
553
|
+
// update mock with new transaction status
|
|
554
|
+
jest
|
|
555
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
556
|
+
.mockResolvedValue(updatedTransactions);
|
|
557
|
+
|
|
558
|
+
clock.next();
|
|
559
|
+
await sleep(1);
|
|
560
|
+
|
|
561
|
+
// 5 pending & incomplete + 1 recently completed
|
|
562
|
+
expect(onMessage.callCount).toBe(6);
|
|
563
|
+
expect(onError.callCount).toBe(0);
|
|
564
|
+
|
|
565
|
+
await sleep(1);
|
|
566
|
+
|
|
567
|
+
// 5 pending & incomplete + 1 recently completed
|
|
568
|
+
expect(onMessage.callCount).toBe(6);
|
|
569
|
+
expect(onError.callCount).toBe(0);
|
|
570
|
+
|
|
571
|
+
// change another transaction to "refunded"
|
|
572
|
+
const [firstTxB, secondTxB, ...restB] = updatedTransactions;
|
|
573
|
+
updatedTransactions = [
|
|
574
|
+
firstTxB,
|
|
575
|
+
{
|
|
576
|
+
...secondTxB,
|
|
577
|
+
status: TransactionStatus.refunded,
|
|
578
|
+
},
|
|
579
|
+
...restB,
|
|
580
|
+
];
|
|
581
|
+
|
|
582
|
+
// update mock with new transaction status
|
|
583
|
+
jest
|
|
584
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
585
|
+
.mockResolvedValue(updatedTransactions);
|
|
586
|
+
|
|
587
|
+
clock.next();
|
|
588
|
+
await sleep(1);
|
|
589
|
+
|
|
590
|
+
// 5 pending & incomplete + 1 recently completed + 1 recently refunded
|
|
591
|
+
expect(onMessage.callCount).toBe(7);
|
|
592
|
+
expect(onError.callCount).toBe(0);
|
|
593
|
+
|
|
594
|
+
// stops watching before next transaction update
|
|
595
|
+
stop();
|
|
596
|
+
|
|
597
|
+
// change another transaction to "pending_user_transfer_start"
|
|
598
|
+
const [firstTxC, secondTxC, thirdTxC, ...restC] = updatedTransactions;
|
|
599
|
+
updatedTransactions = [
|
|
600
|
+
firstTxC,
|
|
601
|
+
secondTxC,
|
|
602
|
+
{
|
|
603
|
+
...thirdTxC,
|
|
604
|
+
status: TransactionStatus.pending_user_transfer_start,
|
|
605
|
+
},
|
|
606
|
+
...restC,
|
|
607
|
+
];
|
|
608
|
+
|
|
609
|
+
// update mock with new transaction status
|
|
610
|
+
jest
|
|
611
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
612
|
+
.mockResolvedValue(updatedTransactions);
|
|
613
|
+
|
|
614
|
+
clock.next();
|
|
615
|
+
await sleep(1);
|
|
616
|
+
|
|
617
|
+
// nothing should change or happen after watcher has stopped
|
|
618
|
+
expect(onMessage.callCount).toBe(7);
|
|
619
|
+
expect(onError.callCount).toBe(0);
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
test("Immediate completed|refunded|expired should get messages", async () => {
|
|
623
|
+
const onMessage = sinon.spy(() => {
|
|
624
|
+
expect(onMessage.callCount).toBeLessThan(4);
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
const onError = sinon.spy((e) => {
|
|
628
|
+
expect(e).toBeUndefined();
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// mock an empty transactions array
|
|
632
|
+
jest.spyOn(anchor, "getTransactionsForAsset").mockResolvedValue([]);
|
|
633
|
+
|
|
634
|
+
// start watching
|
|
635
|
+
const { stop } = watcher.watchAllTransactions({
|
|
636
|
+
authToken,
|
|
637
|
+
assetCode: "SRT",
|
|
638
|
+
lang: "en-US",
|
|
639
|
+
onMessage,
|
|
640
|
+
onError,
|
|
641
|
+
timeout: 1,
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
// nothing should run at first (async api call in progress)
|
|
645
|
+
expect(onMessage.callCount).toBe(0);
|
|
646
|
+
expect(onError.callCount).toBe(0);
|
|
647
|
+
|
|
648
|
+
await sleep(1);
|
|
649
|
+
|
|
650
|
+
// still nothing
|
|
651
|
+
expect(onMessage.callCount).toBe(0);
|
|
652
|
+
expect(onError.callCount).toBe(0);
|
|
653
|
+
|
|
654
|
+
const completedTransaction = {
|
|
655
|
+
id: "uyt1576b-ac28-4c02-a521-ddbfd9ae7oiu",
|
|
656
|
+
kind: "deposit",
|
|
657
|
+
status: "completed",
|
|
658
|
+
status_eta: null,
|
|
659
|
+
amount_in: "150.45",
|
|
660
|
+
amount_out: "149.45",
|
|
661
|
+
amount_fee: "1.00",
|
|
662
|
+
started_at: "2023-05-22T12:11:27.227597Z",
|
|
663
|
+
completed_at: "2023-05-22T18:11:27.227597Z",
|
|
664
|
+
stellar_transaction_id:
|
|
665
|
+
"pokib2292c4e09e8eb22d036171491e87b8d2086bf8b265874c8d182cb9cbngt",
|
|
666
|
+
external_transaction_id: null,
|
|
667
|
+
more_info_url:
|
|
668
|
+
"https://testanchor.stellar.org/sep24/transaction/more_info?id=uyt1576b-ac28-4c02-a521-ddbfd9ae7oiu",
|
|
669
|
+
refunded: false,
|
|
670
|
+
message: "deposit completed!",
|
|
671
|
+
claimable_balance_id: null,
|
|
672
|
+
to: "GCZSYKPDWAKPGR7GYFBOIQB3TH352X7ELZL27WSJET5PDVFORDMGYTB5",
|
|
673
|
+
from: null,
|
|
674
|
+
deposit_memo_type: "hash",
|
|
675
|
+
deposit_memo: null,
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
// add a new success message
|
|
679
|
+
jest
|
|
680
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
681
|
+
.mockResolvedValue([completedTransaction]);
|
|
682
|
+
|
|
683
|
+
clock.next();
|
|
684
|
+
await sleep(1);
|
|
685
|
+
|
|
686
|
+
// should have a success
|
|
687
|
+
expect(onMessage.callCount).toBe(1);
|
|
688
|
+
expect(onError.callCount).toBe(0);
|
|
689
|
+
|
|
690
|
+
// getting the same thing again should change nothing
|
|
691
|
+
jest
|
|
692
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
693
|
+
.mockResolvedValue([completedTransaction]);
|
|
694
|
+
|
|
695
|
+
clock.next();
|
|
696
|
+
await sleep(1);
|
|
697
|
+
|
|
698
|
+
// 1 immediate completed
|
|
699
|
+
expect(onMessage.callCount).toBe(1);
|
|
700
|
+
expect(onError.callCount).toBe(0);
|
|
701
|
+
|
|
702
|
+
const refundedTransaction = {
|
|
703
|
+
id: "def5d166-5a5e-4d5c-ba5d-271c32cd8abc",
|
|
704
|
+
kind: "withdrawal",
|
|
705
|
+
status: "refunded",
|
|
706
|
+
status_eta: null,
|
|
707
|
+
amount_in: "95.35",
|
|
708
|
+
amount_in_asset:
|
|
709
|
+
"stellar:SRT:GCDNJUBQSX7AJWLJACMJ7I4BC3Z47BQUTMHEICZLE6MU4KQBRYG5JY6B",
|
|
710
|
+
amount_out: "144.48",
|
|
711
|
+
amount_out_asset: "iso4217:USD",
|
|
712
|
+
amount_fee: "1.00",
|
|
713
|
+
amount_fee_asset:
|
|
714
|
+
"stellar:SRT:GCDNJUBQSX7AJWLJACMJ7I4BC3Z47BQUTMHEICZLE6MU4KQBRYG5JY6B",
|
|
715
|
+
started_at: "2023-05-25T13:12:35.128156Z",
|
|
716
|
+
completed_at: "2023-05-26T15:12:35.128156Z",
|
|
717
|
+
stellar_transaction_id:
|
|
718
|
+
"abu0b2292c4e09e8eb22d036171491e87b8d2086bf8b265874c8d182cb9cdega",
|
|
719
|
+
external_transaction_id: null,
|
|
720
|
+
more_info_url:
|
|
721
|
+
"https://testanchor.stellar.org/sep24/transaction/more_info?id=def5d166-5a5e-4d5c-ba5d-271c32cd8abc",
|
|
722
|
+
refunded: true,
|
|
723
|
+
refunds: {
|
|
724
|
+
amount_refunded: "95.35",
|
|
725
|
+
amount_fee: "5",
|
|
726
|
+
payments: [
|
|
727
|
+
{
|
|
728
|
+
id: "1937103",
|
|
729
|
+
id_type: "external",
|
|
730
|
+
amount: "40.0",
|
|
731
|
+
fee: "5",
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
id:
|
|
735
|
+
"b9d0b2292c4e09e8eb22d036171491e87b8d2086bf8b265874c8d182cb9c9020",
|
|
736
|
+
id_type: "stellar",
|
|
737
|
+
amount: "55.35",
|
|
738
|
+
fee: "0",
|
|
739
|
+
},
|
|
740
|
+
],
|
|
741
|
+
},
|
|
742
|
+
message: null,
|
|
743
|
+
to: null,
|
|
744
|
+
from: "GCZSYKPDWAKPGR7GYFBOIQB3TH352X7ELZL27WSJET5PDVFORDMGYTB5",
|
|
745
|
+
withdraw_memo_type: "hash",
|
|
746
|
+
withdraw_memo: "AAAAAAAAAAAAAAAAAAAAANsV0WZaXk1cul0nHDLNjPA=",
|
|
747
|
+
withdraw_anchor_account:
|
|
748
|
+
"GCSGSR6KQQ5BP2FXVPWRL6SWPUSFWLVONLIBJZUKTVQB5FYJFVL6XOXE",
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
// add a new success message
|
|
752
|
+
jest
|
|
753
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
754
|
+
.mockResolvedValue([completedTransaction, refundedTransaction]);
|
|
755
|
+
|
|
756
|
+
clock.next();
|
|
757
|
+
await sleep(1);
|
|
758
|
+
|
|
759
|
+
// 1 immediate completed + 1 immediate refunded
|
|
760
|
+
expect(onMessage.callCount).toBe(2);
|
|
761
|
+
expect(onError.callCount).toBe(0);
|
|
762
|
+
|
|
763
|
+
// getting the same thing again should change nothing
|
|
764
|
+
jest
|
|
765
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
766
|
+
.mockResolvedValue([completedTransaction, refundedTransaction]);
|
|
767
|
+
|
|
768
|
+
clock.next();
|
|
769
|
+
await sleep(1);
|
|
770
|
+
|
|
771
|
+
// no updates expected
|
|
772
|
+
expect(onMessage.callCount).toBe(2);
|
|
773
|
+
expect(onError.callCount).toBe(0);
|
|
774
|
+
|
|
775
|
+
const expiredTransaction = {
|
|
776
|
+
id: "hytcf7c4-927d-4b7a-8a1f-d7188ebddu8i",
|
|
777
|
+
kind: "withdrawal",
|
|
778
|
+
status: "expired",
|
|
779
|
+
status_eta: null,
|
|
780
|
+
amount_in: null,
|
|
781
|
+
amount_out: null,
|
|
782
|
+
amount_fee: null,
|
|
783
|
+
started_at: "2023-05-25T18:56:29.615274Z",
|
|
784
|
+
completed_at: null,
|
|
785
|
+
stellar_transaction_id: null,
|
|
786
|
+
external_transaction_id: null,
|
|
787
|
+
more_info_url:
|
|
788
|
+
"https://testanchor.stellar.org/sep24/transaction/more_info?id=hytcf7c4-927d-4b7a-8a1f-d7188ebddu8i",
|
|
789
|
+
refunded: false,
|
|
790
|
+
message: "transaction has expired",
|
|
791
|
+
to: null,
|
|
792
|
+
from: "GCZSYKPDWAKPGR7GYFBOIQB3TH352X7ELZL27WSJET5PDVFORDMGYTB5",
|
|
793
|
+
withdraw_memo_type: "hash",
|
|
794
|
+
withdraw_memo: null,
|
|
795
|
+
withdraw_anchor_account: null,
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
// add a new success message
|
|
799
|
+
jest
|
|
800
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
801
|
+
.mockResolvedValue([
|
|
802
|
+
completedTransaction,
|
|
803
|
+
refundedTransaction,
|
|
804
|
+
expiredTransaction,
|
|
805
|
+
]);
|
|
806
|
+
|
|
807
|
+
clock.next();
|
|
808
|
+
await sleep(1);
|
|
809
|
+
|
|
810
|
+
// 1 immediate completed + 1 immediate refunded + 1 immediate expired
|
|
811
|
+
expect(onMessage.callCount).toBe(3);
|
|
812
|
+
expect(onError.callCount).toBe(0);
|
|
813
|
+
|
|
814
|
+
// getting the same thing again should change nothing
|
|
815
|
+
jest
|
|
816
|
+
.spyOn(anchor, "getTransactionsForAsset")
|
|
817
|
+
.mockResolvedValue([
|
|
818
|
+
completedTransaction,
|
|
819
|
+
refundedTransaction,
|
|
820
|
+
expiredTransaction,
|
|
821
|
+
]);
|
|
822
|
+
|
|
823
|
+
clock.next();
|
|
824
|
+
await sleep(1);
|
|
825
|
+
|
|
826
|
+
// no updates expected
|
|
827
|
+
expect(onMessage.callCount).toBe(3);
|
|
828
|
+
expect(onError.callCount).toBe(0);
|
|
829
|
+
|
|
830
|
+
// stops watching
|
|
831
|
+
stop();
|
|
832
|
+
});
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
describe("watchOneTransaction", () => {
|
|
836
|
+
let clock: sinon.SinonFakeTimers;
|
|
837
|
+
let watcher: Watcher;
|
|
838
|
+
|
|
839
|
+
const makeTransaction = (eta: number, txStatus: TransactionStatus) => ({
|
|
840
|
+
kind: "deposit",
|
|
841
|
+
id: "TEST",
|
|
842
|
+
status: txStatus,
|
|
843
|
+
status_eta: eta,
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
beforeEach(async () => {
|
|
847
|
+
clock = sinon.useFakeTimers(0);
|
|
848
|
+
watcher = anchor.watcher();
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
afterEach(() => {
|
|
852
|
+
clock.restore();
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
test("One completed / refunded / expired successes", async () => {
|
|
856
|
+
const onMessage = sinon.spy(() => {
|
|
857
|
+
expect(onMessage.callCount).toBe(0);
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
const onSuccess = sinon.spy(() => {
|
|
861
|
+
expect(onSuccess.callCount).toBeLessThanOrEqual(3);
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
const onError = sinon.spy((e) => {
|
|
865
|
+
expect(e).toBeUndefined();
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
const successfulTransaction = makeTransaction(
|
|
869
|
+
0,
|
|
870
|
+
TransactionStatus.completed
|
|
871
|
+
);
|
|
872
|
+
|
|
873
|
+
// queue up a success
|
|
874
|
+
jest
|
|
875
|
+
.spyOn(anchor, "getTransactionBy")
|
|
876
|
+
.mockResolvedValue(successfulTransaction);
|
|
877
|
+
|
|
878
|
+
// start watching
|
|
879
|
+
const { stop: stop1 } = watcher.watchOneTransaction({
|
|
880
|
+
authToken,
|
|
881
|
+
assetCode: "SRT",
|
|
882
|
+
id: successfulTransaction.id,
|
|
883
|
+
onMessage,
|
|
884
|
+
onSuccess,
|
|
885
|
+
onError,
|
|
886
|
+
timeout: 1,
|
|
887
|
+
lang: "en-US",
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
// nothing should run at first
|
|
891
|
+
expect(onMessage.callCount).toBe(0);
|
|
892
|
+
expect(onSuccess.callCount).toBe(0);
|
|
893
|
+
expect(onError.callCount).toBe(0);
|
|
894
|
+
|
|
895
|
+
// wait a second, then onSuccess should call back
|
|
896
|
+
await sleep(1);
|
|
897
|
+
|
|
898
|
+
expect(onMessage.callCount).toBe(0);
|
|
899
|
+
expect(onSuccess.callCount).toBe(1);
|
|
900
|
+
expect(onError.callCount).toBe(0);
|
|
901
|
+
|
|
902
|
+
// stops watching transaction
|
|
903
|
+
stop1();
|
|
904
|
+
|
|
905
|
+
const refundedTransaction = makeTransaction(
|
|
906
|
+
0,
|
|
907
|
+
TransactionStatus.refunded
|
|
908
|
+
);
|
|
909
|
+
|
|
910
|
+
// queue up a success
|
|
911
|
+
jest
|
|
912
|
+
.spyOn(anchor, "getTransactionBy")
|
|
913
|
+
.mockResolvedValue(refundedTransaction);
|
|
914
|
+
|
|
915
|
+
// start watching
|
|
916
|
+
const { stop: stop2 } = watcher.watchOneTransaction({
|
|
917
|
+
authToken,
|
|
918
|
+
assetCode: "SRT",
|
|
919
|
+
id: refundedTransaction.id,
|
|
920
|
+
onMessage,
|
|
921
|
+
onSuccess,
|
|
922
|
+
onError,
|
|
923
|
+
timeout: 1,
|
|
924
|
+
lang: "en-US",
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
// nothing should run at first
|
|
928
|
+
expect(onMessage.callCount).toBe(0);
|
|
929
|
+
expect(onSuccess.callCount).toBe(1);
|
|
930
|
+
expect(onError.callCount).toBe(0);
|
|
931
|
+
|
|
932
|
+
// wait a second, then onSuccess should call back
|
|
933
|
+
await sleep(1);
|
|
934
|
+
|
|
935
|
+
expect(onMessage.callCount).toBe(0);
|
|
936
|
+
expect(onSuccess.callCount).toBe(2);
|
|
937
|
+
expect(onError.callCount).toBe(0);
|
|
938
|
+
|
|
939
|
+
// stops watching transaction
|
|
940
|
+
stop2();
|
|
941
|
+
|
|
942
|
+
const expiredTransaction = makeTransaction(0, TransactionStatus.expired);
|
|
943
|
+
|
|
944
|
+
// queue up a success
|
|
945
|
+
jest
|
|
946
|
+
.spyOn(anchor, "getTransactionBy")
|
|
947
|
+
.mockResolvedValue(expiredTransaction);
|
|
948
|
+
|
|
949
|
+
// start watching
|
|
950
|
+
const { stop: stop3 } = watcher.watchOneTransaction({
|
|
951
|
+
authToken,
|
|
952
|
+
assetCode: "SRT",
|
|
953
|
+
id: expiredTransaction.id,
|
|
954
|
+
onMessage,
|
|
955
|
+
onSuccess,
|
|
956
|
+
onError,
|
|
957
|
+
timeout: 1,
|
|
958
|
+
lang: "en-US",
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
// nothing should run at first
|
|
962
|
+
expect(onMessage.callCount).toBe(0);
|
|
963
|
+
expect(onSuccess.callCount).toBe(2);
|
|
964
|
+
expect(onError.callCount).toBe(0);
|
|
965
|
+
|
|
966
|
+
// wait a second, then onSuccess should call back
|
|
967
|
+
await sleep(1);
|
|
968
|
+
|
|
969
|
+
expect(onMessage.callCount).toBe(0);
|
|
970
|
+
expect(onSuccess.callCount).toBe(3);
|
|
971
|
+
expect(onError.callCount).toBe(0);
|
|
972
|
+
|
|
973
|
+
// stops watching transaction
|
|
974
|
+
stop3();
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
test("One incomplete / pending_user_transfer_start messages", async () => {
|
|
978
|
+
const onMessage = sinon.spy(() => {
|
|
979
|
+
expect(onMessage.callCount).toBeLessThanOrEqual(2);
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
const onSuccess = sinon.spy(() => {
|
|
983
|
+
expect(onSuccess.callCount).toBe(0);
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
const onError = sinon.spy((e) => {
|
|
987
|
+
expect(e).toBeUndefined();
|
|
988
|
+
});
|
|
989
|
+
|
|
990
|
+
const incompleteTransaction = makeTransaction(
|
|
991
|
+
0,
|
|
992
|
+
TransactionStatus.incomplete
|
|
993
|
+
);
|
|
994
|
+
|
|
995
|
+
// queue up an incomplete transaction response
|
|
996
|
+
jest
|
|
997
|
+
.spyOn(anchor, "getTransactionBy")
|
|
998
|
+
.mockResolvedValue(incompleteTransaction);
|
|
999
|
+
|
|
1000
|
+
// start watching
|
|
1001
|
+
watcher.watchOneTransaction({
|
|
1002
|
+
authToken,
|
|
1003
|
+
assetCode: "SRT",
|
|
1004
|
+
id: incompleteTransaction.id,
|
|
1005
|
+
onMessage,
|
|
1006
|
+
onSuccess,
|
|
1007
|
+
onError,
|
|
1008
|
+
timeout: 1,
|
|
1009
|
+
lang: "en-US",
|
|
1010
|
+
});
|
|
1011
|
+
|
|
1012
|
+
// nothing should run at first
|
|
1013
|
+
expect(onMessage.callCount).toBe(0);
|
|
1014
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1015
|
+
expect(onError.callCount).toBe(0);
|
|
1016
|
+
|
|
1017
|
+
// wait a second, then onMessage should call back
|
|
1018
|
+
await sleep(1);
|
|
1019
|
+
|
|
1020
|
+
expect(onMessage.callCount).toBe(1);
|
|
1021
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1022
|
+
expect(onError.callCount).toBe(0);
|
|
1023
|
+
|
|
1024
|
+
const pendingTransaction = makeTransaction(
|
|
1025
|
+
0,
|
|
1026
|
+
TransactionStatus.pending_user_transfer_start
|
|
1027
|
+
);
|
|
1028
|
+
|
|
1029
|
+
// queue up a pending transaction response
|
|
1030
|
+
jest
|
|
1031
|
+
.spyOn(anchor, "getTransactionBy")
|
|
1032
|
+
.mockResolvedValue(pendingTransaction);
|
|
1033
|
+
|
|
1034
|
+
// start watching
|
|
1035
|
+
watcher.watchOneTransaction({
|
|
1036
|
+
authToken,
|
|
1037
|
+
assetCode: "SRT",
|
|
1038
|
+
id: pendingTransaction.id,
|
|
1039
|
+
onMessage,
|
|
1040
|
+
onSuccess,
|
|
1041
|
+
onError,
|
|
1042
|
+
timeout: 1,
|
|
1043
|
+
lang: "en-US",
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
// nothing should run at first
|
|
1047
|
+
expect(onMessage.callCount).toBe(1);
|
|
1048
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1049
|
+
expect(onError.callCount).toBe(0);
|
|
1050
|
+
|
|
1051
|
+
// wait a second, then onMessage should call back
|
|
1052
|
+
await sleep(1);
|
|
1053
|
+
|
|
1054
|
+
expect(onMessage.callCount).toBe(2);
|
|
1055
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1056
|
+
expect(onError.callCount).toBe(0);
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
test("One error / no_market errors", async () => {
|
|
1060
|
+
const onMessage = sinon.spy(() => {
|
|
1061
|
+
expect(onMessage.callCount).toBe(0);
|
|
1062
|
+
});
|
|
1063
|
+
|
|
1064
|
+
const onSuccess = sinon.spy(() => {
|
|
1065
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
const onError = sinon.spy((e) => {
|
|
1069
|
+
expect(e).toBeTruthy();
|
|
1070
|
+
expect(onError.callCount).toBeLessThanOrEqual(2);
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
const errorTransaction = makeTransaction(0, TransactionStatus.error);
|
|
1074
|
+
|
|
1075
|
+
// queue up an error transaction response
|
|
1076
|
+
jest
|
|
1077
|
+
.spyOn(anchor, "getTransactionBy")
|
|
1078
|
+
.mockResolvedValue(errorTransaction);
|
|
1079
|
+
|
|
1080
|
+
// start watching
|
|
1081
|
+
watcher.watchOneTransaction({
|
|
1082
|
+
authToken,
|
|
1083
|
+
assetCode: "SRT",
|
|
1084
|
+
id: errorTransaction.id,
|
|
1085
|
+
onMessage,
|
|
1086
|
+
onSuccess,
|
|
1087
|
+
onError,
|
|
1088
|
+
timeout: 1,
|
|
1089
|
+
lang: "en-US",
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
// nothing should run at first
|
|
1093
|
+
expect(onMessage.callCount).toBe(0);
|
|
1094
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1095
|
+
expect(onError.callCount).toBe(0);
|
|
1096
|
+
|
|
1097
|
+
// wait a second, then onMessage should call back
|
|
1098
|
+
await sleep(1);
|
|
1099
|
+
|
|
1100
|
+
expect(onMessage.callCount).toBe(0);
|
|
1101
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1102
|
+
expect(onError.callCount).toBe(1);
|
|
1103
|
+
|
|
1104
|
+
const noMarketTransaction = makeTransaction(
|
|
1105
|
+
0,
|
|
1106
|
+
TransactionStatus.no_market
|
|
1107
|
+
);
|
|
1108
|
+
|
|
1109
|
+
// queue up a "no market" transaction response
|
|
1110
|
+
jest
|
|
1111
|
+
.spyOn(anchor, "getTransactionBy")
|
|
1112
|
+
.mockResolvedValue(noMarketTransaction);
|
|
1113
|
+
|
|
1114
|
+
// start watching
|
|
1115
|
+
watcher.watchOneTransaction({
|
|
1116
|
+
authToken,
|
|
1117
|
+
assetCode: "SRT",
|
|
1118
|
+
id: noMarketTransaction.id,
|
|
1119
|
+
onMessage,
|
|
1120
|
+
onSuccess,
|
|
1121
|
+
onError,
|
|
1122
|
+
timeout: 1,
|
|
1123
|
+
lang: "en-US",
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
// nothing should run at first
|
|
1127
|
+
expect(onMessage.callCount).toBe(0);
|
|
1128
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1129
|
+
expect(onError.callCount).toBe(1);
|
|
1130
|
+
|
|
1131
|
+
// wait a second, then onMessage should call back
|
|
1132
|
+
await sleep(1);
|
|
1133
|
+
|
|
1134
|
+
expect(onMessage.callCount).toBe(0);
|
|
1135
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1136
|
+
expect(onError.callCount).toBe(2);
|
|
1137
|
+
});
|
|
1138
|
+
|
|
1139
|
+
test("Several pending transactions, one completed, no more after that", async () => {
|
|
1140
|
+
const onMessage = sinon.spy(() => {
|
|
1141
|
+
expect(onMessage.callCount).toBeLessThanOrEqual(8);
|
|
1142
|
+
});
|
|
1143
|
+
|
|
1144
|
+
const onSuccess = sinon.spy(() => {
|
|
1145
|
+
expect(onSuccess.callCount).toBeLessThanOrEqual(1);
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
const onError = sinon.spy((e) => {
|
|
1149
|
+
expect(e).toBeUndefined();
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
// queue up several pending status updates
|
|
1153
|
+
jest
|
|
1154
|
+
.spyOn(anchor, "getTransactionBy")
|
|
1155
|
+
.mockResolvedValueOnce(makeTransaction(0, TransactionStatus.incomplete))
|
|
1156
|
+
.mockResolvedValueOnce(
|
|
1157
|
+
makeTransaction(1, TransactionStatus.pending_user)
|
|
1158
|
+
)
|
|
1159
|
+
.mockResolvedValueOnce(
|
|
1160
|
+
makeTransaction(2, TransactionStatus.pending_anchor)
|
|
1161
|
+
)
|
|
1162
|
+
.mockResolvedValueOnce(
|
|
1163
|
+
makeTransaction(3, TransactionStatus.pending_external)
|
|
1164
|
+
)
|
|
1165
|
+
.mockResolvedValueOnce(
|
|
1166
|
+
makeTransaction(4, TransactionStatus.pending_stellar)
|
|
1167
|
+
)
|
|
1168
|
+
.mockResolvedValueOnce(
|
|
1169
|
+
makeTransaction(5, TransactionStatus.pending_trust)
|
|
1170
|
+
)
|
|
1171
|
+
.mockResolvedValueOnce(
|
|
1172
|
+
makeTransaction(6, TransactionStatus.pending_user_transfer_start)
|
|
1173
|
+
)
|
|
1174
|
+
.mockResolvedValueOnce(
|
|
1175
|
+
makeTransaction(7, TransactionStatus.pending_user_transfer_complete)
|
|
1176
|
+
)
|
|
1177
|
+
.mockResolvedValueOnce(makeTransaction(8, TransactionStatus.completed))
|
|
1178
|
+
.mockResolvedValueOnce(
|
|
1179
|
+
makeTransaction(9, TransactionStatus.pending_anchor)
|
|
1180
|
+
)
|
|
1181
|
+
.mockResolvedValueOnce(
|
|
1182
|
+
makeTransaction(10, TransactionStatus.pending_external)
|
|
1183
|
+
);
|
|
1184
|
+
|
|
1185
|
+
// start watching
|
|
1186
|
+
watcher.watchOneTransaction({
|
|
1187
|
+
authToken,
|
|
1188
|
+
assetCode: "SRT",
|
|
1189
|
+
id: makeTransaction(0, TransactionStatus.incomplete).id,
|
|
1190
|
+
onMessage,
|
|
1191
|
+
onSuccess,
|
|
1192
|
+
onError,
|
|
1193
|
+
timeout: 1,
|
|
1194
|
+
lang: "en-US",
|
|
1195
|
+
});
|
|
1196
|
+
|
|
1197
|
+
// nothing should run at first
|
|
1198
|
+
expect(onMessage.callCount).toBe(0);
|
|
1199
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1200
|
+
expect(onError.callCount).toBe(0);
|
|
1201
|
+
|
|
1202
|
+
// loop through all pending statuses updates
|
|
1203
|
+
await sleep(1);
|
|
1204
|
+
|
|
1205
|
+
clock.next();
|
|
1206
|
+
await sleep(1);
|
|
1207
|
+
|
|
1208
|
+
clock.next();
|
|
1209
|
+
await sleep(1);
|
|
1210
|
+
|
|
1211
|
+
clock.next();
|
|
1212
|
+
await sleep(1);
|
|
1213
|
+
|
|
1214
|
+
clock.next();
|
|
1215
|
+
await sleep(1);
|
|
1216
|
+
|
|
1217
|
+
clock.next();
|
|
1218
|
+
await sleep(1);
|
|
1219
|
+
|
|
1220
|
+
clock.next();
|
|
1221
|
+
await sleep(1);
|
|
1222
|
+
|
|
1223
|
+
clock.next();
|
|
1224
|
+
await sleep(1);
|
|
1225
|
+
|
|
1226
|
+
// 1 incomplete + 7 pending transactions
|
|
1227
|
+
expect(onMessage.callCount).toBe(8);
|
|
1228
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1229
|
+
expect(onError.callCount).toBe(0);
|
|
1230
|
+
|
|
1231
|
+
clock.next();
|
|
1232
|
+
await sleep(1);
|
|
1233
|
+
|
|
1234
|
+
// the next time a success should happen
|
|
1235
|
+
expect(onMessage.callCount).toBe(8);
|
|
1236
|
+
expect(onSuccess.callCount).toBe(1);
|
|
1237
|
+
expect(onError.callCount).toBe(0);
|
|
1238
|
+
|
|
1239
|
+
clock.next();
|
|
1240
|
+
await sleep(1);
|
|
1241
|
+
|
|
1242
|
+
clock.next();
|
|
1243
|
+
await sleep(1);
|
|
1244
|
+
|
|
1245
|
+
// after success, nothing should change or run again
|
|
1246
|
+
expect(onMessage.callCount).toBe(8);
|
|
1247
|
+
expect(onSuccess.callCount).toBe(1);
|
|
1248
|
+
expect(onError.callCount).toBe(0);
|
|
1249
|
+
});
|
|
1250
|
+
|
|
1251
|
+
test("Stops watching after 3 transaction updates", async () => {
|
|
1252
|
+
const onMessage = sinon.spy(() => {
|
|
1253
|
+
expect(onMessage.callCount).toBeLessThanOrEqual(3);
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
const onSuccess = sinon.spy(() => {
|
|
1257
|
+
expect(onSuccess.callCount).toBeLessThanOrEqual(0);
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
const onError = sinon.spy((e) => {
|
|
1261
|
+
expect(e).toBeUndefined();
|
|
1262
|
+
});
|
|
1263
|
+
|
|
1264
|
+
// queue up several pending status updates
|
|
1265
|
+
jest
|
|
1266
|
+
.spyOn(anchor, "getTransactionBy")
|
|
1267
|
+
.mockResolvedValueOnce(makeTransaction(0, TransactionStatus.incomplete))
|
|
1268
|
+
.mockResolvedValueOnce(
|
|
1269
|
+
makeTransaction(1, TransactionStatus.pending_user)
|
|
1270
|
+
)
|
|
1271
|
+
.mockResolvedValueOnce(
|
|
1272
|
+
makeTransaction(2, TransactionStatus.pending_anchor)
|
|
1273
|
+
)
|
|
1274
|
+
.mockResolvedValueOnce(
|
|
1275
|
+
makeTransaction(3, TransactionStatus.pending_external)
|
|
1276
|
+
)
|
|
1277
|
+
.mockResolvedValueOnce(
|
|
1278
|
+
makeTransaction(4, TransactionStatus.pending_stellar)
|
|
1279
|
+
)
|
|
1280
|
+
.mockResolvedValueOnce(
|
|
1281
|
+
makeTransaction(5, TransactionStatus.pending_trust)
|
|
1282
|
+
);
|
|
1283
|
+
|
|
1284
|
+
// start watching
|
|
1285
|
+
const { stop } = watcher.watchOneTransaction({
|
|
1286
|
+
authToken,
|
|
1287
|
+
assetCode: "SRT",
|
|
1288
|
+
id: makeTransaction(0, TransactionStatus.incomplete).id,
|
|
1289
|
+
onMessage,
|
|
1290
|
+
onSuccess,
|
|
1291
|
+
onError,
|
|
1292
|
+
timeout: 1,
|
|
1293
|
+
lang: "en-US",
|
|
1294
|
+
});
|
|
1295
|
+
|
|
1296
|
+
// nothing should run at first
|
|
1297
|
+
expect(onMessage.callCount).toBe(0);
|
|
1298
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1299
|
+
expect(onError.callCount).toBe(0);
|
|
1300
|
+
|
|
1301
|
+
// loop through all pending statuses updates
|
|
1302
|
+
await sleep(1);
|
|
1303
|
+
|
|
1304
|
+
clock.next();
|
|
1305
|
+
await sleep(1);
|
|
1306
|
+
|
|
1307
|
+
clock.next();
|
|
1308
|
+
await sleep(1);
|
|
1309
|
+
|
|
1310
|
+
// 1 incomplete + 2 pending transactions
|
|
1311
|
+
expect(onMessage.callCount).toBe(3);
|
|
1312
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1313
|
+
expect(onError.callCount).toBe(0);
|
|
1314
|
+
|
|
1315
|
+
// stops watching after the third update
|
|
1316
|
+
stop();
|
|
1317
|
+
|
|
1318
|
+
clock.next();
|
|
1319
|
+
await sleep(1);
|
|
1320
|
+
|
|
1321
|
+
clock.next();
|
|
1322
|
+
await sleep(1);
|
|
1323
|
+
|
|
1324
|
+
clock.next();
|
|
1325
|
+
await sleep(1);
|
|
1326
|
+
|
|
1327
|
+
// after stopping, nothing should change or run again
|
|
1328
|
+
expect(onMessage.callCount).toBe(3);
|
|
1329
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1330
|
+
expect(onError.callCount).toBe(0);
|
|
1331
|
+
});
|
|
1332
|
+
|
|
1333
|
+
test("One pending, one completed, no more after that", async () => {
|
|
1334
|
+
const onMessage = sinon.spy(() => {
|
|
1335
|
+
expect(onMessage.callCount).toBeLessThanOrEqual(1);
|
|
1336
|
+
});
|
|
1337
|
+
|
|
1338
|
+
const onSuccess = sinon.spy(() => {
|
|
1339
|
+
expect(onSuccess.callCount).toBeLessThanOrEqual(1);
|
|
1340
|
+
});
|
|
1341
|
+
|
|
1342
|
+
const onError = sinon.spy((e) => {
|
|
1343
|
+
expect(e).toBeUndefined();
|
|
1344
|
+
});
|
|
1345
|
+
|
|
1346
|
+
// queue up transactions
|
|
1347
|
+
jest
|
|
1348
|
+
.spyOn(anchor, "getTransactionBy")
|
|
1349
|
+
.mockResolvedValueOnce(
|
|
1350
|
+
makeTransaction(0, TransactionStatus.pending_user_transfer_complete)
|
|
1351
|
+
)
|
|
1352
|
+
.mockResolvedValueOnce(makeTransaction(1, TransactionStatus.completed))
|
|
1353
|
+
.mockResolvedValueOnce(
|
|
1354
|
+
makeTransaction(2, TransactionStatus.pending_anchor)
|
|
1355
|
+
)
|
|
1356
|
+
.mockResolvedValueOnce(
|
|
1357
|
+
makeTransaction(3, TransactionStatus.pending_external)
|
|
1358
|
+
);
|
|
1359
|
+
|
|
1360
|
+
// start watching
|
|
1361
|
+
watcher.watchOneTransaction({
|
|
1362
|
+
authToken,
|
|
1363
|
+
assetCode: "SRT",
|
|
1364
|
+
id: makeTransaction(0, TransactionStatus.pending_user_transfer_complete)
|
|
1365
|
+
.id,
|
|
1366
|
+
onMessage,
|
|
1367
|
+
onSuccess,
|
|
1368
|
+
onError,
|
|
1369
|
+
timeout: 1,
|
|
1370
|
+
lang: "en-US",
|
|
1371
|
+
});
|
|
1372
|
+
|
|
1373
|
+
// nothing should run at first
|
|
1374
|
+
expect(onMessage.callCount).toBe(0);
|
|
1375
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1376
|
+
expect(onError.callCount).toBe(0);
|
|
1377
|
+
|
|
1378
|
+
await sleep(1);
|
|
1379
|
+
|
|
1380
|
+
// wait a second, then the pending should resolve
|
|
1381
|
+
expect(onMessage.callCount).toBe(1);
|
|
1382
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1383
|
+
expect(onError.callCount).toBe(0);
|
|
1384
|
+
|
|
1385
|
+
clock.next();
|
|
1386
|
+
await sleep(1);
|
|
1387
|
+
|
|
1388
|
+
// the second time, a success should happen
|
|
1389
|
+
expect(onMessage.callCount).toBe(1);
|
|
1390
|
+
expect(onSuccess.callCount).toBe(1);
|
|
1391
|
+
expect(onError.callCount).toBe(0);
|
|
1392
|
+
|
|
1393
|
+
clock.next();
|
|
1394
|
+
await sleep(1);
|
|
1395
|
+
|
|
1396
|
+
clock.next();
|
|
1397
|
+
await sleep(1);
|
|
1398
|
+
|
|
1399
|
+
// after success, nothing should change or run again
|
|
1400
|
+
expect(onMessage.callCount).toBe(1);
|
|
1401
|
+
expect(onSuccess.callCount).toBe(1);
|
|
1402
|
+
expect(onError.callCount).toBe(0);
|
|
1403
|
+
});
|
|
1404
|
+
|
|
1405
|
+
test("One pending, one refunded, no more after that", async () => {
|
|
1406
|
+
const onMessage = sinon.spy(() => {
|
|
1407
|
+
expect(onMessage.callCount).toBeLessThanOrEqual(1);
|
|
1408
|
+
});
|
|
1409
|
+
|
|
1410
|
+
const onSuccess = sinon.spy(() => {
|
|
1411
|
+
expect(onSuccess.callCount).toBeLessThanOrEqual(1);
|
|
1412
|
+
});
|
|
1413
|
+
|
|
1414
|
+
const onError = sinon.spy((e) => {
|
|
1415
|
+
expect(e).toBeUndefined();
|
|
1416
|
+
});
|
|
1417
|
+
|
|
1418
|
+
// queue up transactions
|
|
1419
|
+
jest
|
|
1420
|
+
.spyOn(anchor, "getTransactionBy")
|
|
1421
|
+
.mockResolvedValueOnce(
|
|
1422
|
+
makeTransaction(0, TransactionStatus.pending_user_transfer_complete)
|
|
1423
|
+
)
|
|
1424
|
+
.mockResolvedValueOnce(makeTransaction(1, TransactionStatus.refunded))
|
|
1425
|
+
.mockResolvedValueOnce(
|
|
1426
|
+
makeTransaction(2, TransactionStatus.pending_anchor)
|
|
1427
|
+
)
|
|
1428
|
+
.mockResolvedValueOnce(
|
|
1429
|
+
makeTransaction(3, TransactionStatus.pending_external)
|
|
1430
|
+
);
|
|
1431
|
+
|
|
1432
|
+
// start watching
|
|
1433
|
+
watcher.watchOneTransaction({
|
|
1434
|
+
authToken,
|
|
1435
|
+
assetCode: "SRT",
|
|
1436
|
+
id: makeTransaction(0, TransactionStatus.pending_user_transfer_complete)
|
|
1437
|
+
.id,
|
|
1438
|
+
onMessage,
|
|
1439
|
+
onSuccess,
|
|
1440
|
+
onError,
|
|
1441
|
+
timeout: 1,
|
|
1442
|
+
lang: "en-US",
|
|
1443
|
+
});
|
|
1444
|
+
|
|
1445
|
+
// nothing should run at first
|
|
1446
|
+
expect(onMessage.callCount).toBe(0);
|
|
1447
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1448
|
+
expect(onError.callCount).toBe(0);
|
|
1449
|
+
|
|
1450
|
+
await sleep(1);
|
|
1451
|
+
|
|
1452
|
+
// wait a second, then the pending should resolve
|
|
1453
|
+
expect(onMessage.callCount).toBe(1);
|
|
1454
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1455
|
+
expect(onError.callCount).toBe(0);
|
|
1456
|
+
|
|
1457
|
+
clock.next();
|
|
1458
|
+
await sleep(1);
|
|
1459
|
+
|
|
1460
|
+
// the second time, a success should happen
|
|
1461
|
+
expect(onMessage.callCount).toBe(1);
|
|
1462
|
+
expect(onSuccess.callCount).toBe(1);
|
|
1463
|
+
expect(onError.callCount).toBe(0);
|
|
1464
|
+
|
|
1465
|
+
clock.next();
|
|
1466
|
+
await sleep(1);
|
|
1467
|
+
|
|
1468
|
+
clock.next();
|
|
1469
|
+
await sleep(1);
|
|
1470
|
+
|
|
1471
|
+
// after success, nothing should change or run again
|
|
1472
|
+
expect(onMessage.callCount).toBe(1);
|
|
1473
|
+
expect(onSuccess.callCount).toBe(1);
|
|
1474
|
+
expect(onError.callCount).toBe(0);
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
test("One pending, one error, no more after that", async () => {
|
|
1478
|
+
const onMessage = sinon.spy(() => {
|
|
1479
|
+
expect(onMessage.callCount).toBeLessThanOrEqual(1);
|
|
1480
|
+
});
|
|
1481
|
+
|
|
1482
|
+
const onSuccess = sinon.spy(() => {
|
|
1483
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1484
|
+
});
|
|
1485
|
+
|
|
1486
|
+
const onError = sinon.spy((e) => {
|
|
1487
|
+
expect(e).toBeTruthy();
|
|
1488
|
+
expect(onError.callCount).toBeLessThanOrEqual(1);
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
// queue up transactions
|
|
1492
|
+
jest
|
|
1493
|
+
.spyOn(anchor, "getTransactionBy")
|
|
1494
|
+
.mockResolvedValueOnce(
|
|
1495
|
+
makeTransaction(0, TransactionStatus.pending_user_transfer_start)
|
|
1496
|
+
)
|
|
1497
|
+
.mockResolvedValueOnce(makeTransaction(1, TransactionStatus.error))
|
|
1498
|
+
.mockResolvedValueOnce(
|
|
1499
|
+
makeTransaction(2, TransactionStatus.pending_anchor)
|
|
1500
|
+
)
|
|
1501
|
+
.mockResolvedValueOnce(
|
|
1502
|
+
makeTransaction(3, TransactionStatus.pending_external)
|
|
1503
|
+
);
|
|
1504
|
+
|
|
1505
|
+
// start watching
|
|
1506
|
+
watcher.watchOneTransaction({
|
|
1507
|
+
authToken,
|
|
1508
|
+
assetCode: "SRT",
|
|
1509
|
+
id: makeTransaction(0, TransactionStatus.pending_user_transfer_start)
|
|
1510
|
+
.id,
|
|
1511
|
+
onMessage,
|
|
1512
|
+
onSuccess,
|
|
1513
|
+
onError,
|
|
1514
|
+
timeout: 1,
|
|
1515
|
+
lang: "en-US",
|
|
1516
|
+
});
|
|
1517
|
+
|
|
1518
|
+
// nothing should run at first
|
|
1519
|
+
expect(onMessage.callCount).toBe(0);
|
|
1520
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1521
|
+
expect(onError.callCount).toBe(0);
|
|
1522
|
+
|
|
1523
|
+
await sleep(1);
|
|
1524
|
+
|
|
1525
|
+
// wait a second, then the pending should resolve
|
|
1526
|
+
expect(onMessage.callCount).toBe(1);
|
|
1527
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1528
|
+
expect(onError.callCount).toBe(0);
|
|
1529
|
+
|
|
1530
|
+
clock.next();
|
|
1531
|
+
await sleep(1);
|
|
1532
|
+
|
|
1533
|
+
// the second time, a error should happen
|
|
1534
|
+
expect(onMessage.callCount).toBe(1);
|
|
1535
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1536
|
+
expect(onError.callCount).toBe(1);
|
|
1537
|
+
|
|
1538
|
+
clock.next();
|
|
1539
|
+
await sleep(1);
|
|
1540
|
+
|
|
1541
|
+
clock.next();
|
|
1542
|
+
await sleep(1);
|
|
1543
|
+
|
|
1544
|
+
// after error, nothing should change or run again
|
|
1545
|
+
expect(onMessage.callCount).toBe(1);
|
|
1546
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1547
|
+
expect(onError.callCount).toBe(1);
|
|
1548
|
+
});
|
|
1549
|
+
|
|
1550
|
+
test("One pending, one no_market, no more after that", async () => {
|
|
1551
|
+
const onMessage = sinon.spy(() => {
|
|
1552
|
+
expect(onMessage.callCount).toBeLessThanOrEqual(1);
|
|
1553
|
+
});
|
|
1554
|
+
|
|
1555
|
+
const onSuccess = sinon.spy(() => {
|
|
1556
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1557
|
+
});
|
|
1558
|
+
|
|
1559
|
+
const onError = sinon.spy((e) => {
|
|
1560
|
+
expect(e).toBeTruthy();
|
|
1561
|
+
expect(onError.callCount).toBeLessThanOrEqual(1);
|
|
1562
|
+
});
|
|
1563
|
+
|
|
1564
|
+
// queue up transactions
|
|
1565
|
+
jest
|
|
1566
|
+
.spyOn(anchor, "getTransactionBy")
|
|
1567
|
+
.mockResolvedValueOnce(
|
|
1568
|
+
makeTransaction(0, TransactionStatus.pending_user_transfer_start)
|
|
1569
|
+
)
|
|
1570
|
+
.mockResolvedValueOnce(makeTransaction(1, TransactionStatus.no_market))
|
|
1571
|
+
.mockResolvedValueOnce(
|
|
1572
|
+
makeTransaction(2, TransactionStatus.pending_anchor)
|
|
1573
|
+
)
|
|
1574
|
+
.mockResolvedValueOnce(
|
|
1575
|
+
makeTransaction(3, TransactionStatus.pending_external)
|
|
1576
|
+
);
|
|
1577
|
+
|
|
1578
|
+
// start watching
|
|
1579
|
+
watcher.watchOneTransaction({
|
|
1580
|
+
authToken,
|
|
1581
|
+
assetCode: "SRT",
|
|
1582
|
+
id: makeTransaction(0, TransactionStatus.pending_user_transfer_start)
|
|
1583
|
+
.id,
|
|
1584
|
+
onMessage,
|
|
1585
|
+
onSuccess,
|
|
1586
|
+
onError,
|
|
1587
|
+
timeout: 1,
|
|
1588
|
+
lang: "en-US",
|
|
1589
|
+
});
|
|
1590
|
+
|
|
1591
|
+
// nothing should run at first
|
|
1592
|
+
expect(onMessage.callCount).toBe(0);
|
|
1593
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1594
|
+
expect(onError.callCount).toBe(0);
|
|
1595
|
+
|
|
1596
|
+
await sleep(1);
|
|
1597
|
+
|
|
1598
|
+
// wait a second, then the pending should resolve
|
|
1599
|
+
expect(onMessage.callCount).toBe(1);
|
|
1600
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1601
|
+
expect(onError.callCount).toBe(0);
|
|
1602
|
+
|
|
1603
|
+
clock.next();
|
|
1604
|
+
await sleep(1);
|
|
1605
|
+
|
|
1606
|
+
// the second time, a error should happen
|
|
1607
|
+
expect(onMessage.callCount).toBe(1);
|
|
1608
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1609
|
+
expect(onError.callCount).toBe(1);
|
|
1610
|
+
|
|
1611
|
+
clock.next();
|
|
1612
|
+
await sleep(1);
|
|
1613
|
+
|
|
1614
|
+
clock.next();
|
|
1615
|
+
await sleep(1);
|
|
1616
|
+
|
|
1617
|
+
// after error, nothing should change or run again
|
|
1618
|
+
expect(onMessage.callCount).toBe(1);
|
|
1619
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1620
|
+
expect(onError.callCount).toBe(1);
|
|
1621
|
+
});
|
|
1622
|
+
|
|
1623
|
+
test("Two pending, one error, no more after that", async () => {
|
|
1624
|
+
const onMessage = sinon.spy(() => {
|
|
1625
|
+
expect(onMessage.callCount).toBeLessThanOrEqual(2);
|
|
1626
|
+
});
|
|
1627
|
+
|
|
1628
|
+
const onSuccess = sinon.spy(() => {
|
|
1629
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1630
|
+
});
|
|
1631
|
+
|
|
1632
|
+
const onError = sinon.spy((e) => {
|
|
1633
|
+
expect(e).toBeTruthy();
|
|
1634
|
+
expect(onError.callCount).toBeLessThanOrEqual(1);
|
|
1635
|
+
});
|
|
1636
|
+
|
|
1637
|
+
// queue up transactions
|
|
1638
|
+
jest
|
|
1639
|
+
.spyOn(anchor, "getTransactionBy")
|
|
1640
|
+
.mockResolvedValueOnce(
|
|
1641
|
+
makeTransaction(0, TransactionStatus.pending_user_transfer_start)
|
|
1642
|
+
)
|
|
1643
|
+
.mockResolvedValueOnce(
|
|
1644
|
+
makeTransaction(1, TransactionStatus.pending_user_transfer_complete)
|
|
1645
|
+
)
|
|
1646
|
+
.mockResolvedValueOnce(makeTransaction(2, TransactionStatus.error))
|
|
1647
|
+
.mockResolvedValueOnce(
|
|
1648
|
+
makeTransaction(3, TransactionStatus.pending_anchor)
|
|
1649
|
+
)
|
|
1650
|
+
.mockResolvedValueOnce(
|
|
1651
|
+
makeTransaction(4, TransactionStatus.pending_external)
|
|
1652
|
+
);
|
|
1653
|
+
|
|
1654
|
+
// start watching
|
|
1655
|
+
watcher.watchOneTransaction({
|
|
1656
|
+
authToken,
|
|
1657
|
+
assetCode: "SRT",
|
|
1658
|
+
id: makeTransaction(0, TransactionStatus.pending_user_transfer_start)
|
|
1659
|
+
.id,
|
|
1660
|
+
onMessage,
|
|
1661
|
+
onSuccess,
|
|
1662
|
+
onError,
|
|
1663
|
+
timeout: 1,
|
|
1664
|
+
lang: "en-US",
|
|
1665
|
+
});
|
|
1666
|
+
|
|
1667
|
+
// nothing should run at first
|
|
1668
|
+
expect(onMessage.callCount).toBe(0);
|
|
1669
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1670
|
+
expect(onError.callCount).toBe(0);
|
|
1671
|
+
|
|
1672
|
+
await sleep(1);
|
|
1673
|
+
|
|
1674
|
+
// wait a second, then the pending should resolve
|
|
1675
|
+
expect(onMessage.callCount).toBe(1);
|
|
1676
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1677
|
+
expect(onError.callCount).toBe(0);
|
|
1678
|
+
|
|
1679
|
+
clock.next();
|
|
1680
|
+
await sleep(1);
|
|
1681
|
+
|
|
1682
|
+
// the next time, another pending
|
|
1683
|
+
expect(onMessage.callCount).toBe(2);
|
|
1684
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1685
|
+
expect(onError.callCount).toBe(0);
|
|
1686
|
+
|
|
1687
|
+
clock.next();
|
|
1688
|
+
await sleep(1);
|
|
1689
|
+
|
|
1690
|
+
// the next time, an error
|
|
1691
|
+
expect(onMessage.callCount).toBe(2);
|
|
1692
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1693
|
+
expect(onError.callCount).toBe(1);
|
|
1694
|
+
|
|
1695
|
+
clock.next();
|
|
1696
|
+
await sleep(1);
|
|
1697
|
+
|
|
1698
|
+
clock.next();
|
|
1699
|
+
await sleep(1);
|
|
1700
|
+
|
|
1701
|
+
// after error, nothing should change or run again
|
|
1702
|
+
expect(onMessage.callCount).toBe(2);
|
|
1703
|
+
expect(onSuccess.callCount).toBe(0);
|
|
1704
|
+
expect(onError.callCount).toBe(1);
|
|
1705
|
+
});
|
|
1706
|
+
});
|
|
1707
|
+
});
|
|
1708
|
+
|
|
1709
|
+
describe("Http client", () => {
|
|
1710
|
+
it("should work with http", async () => {
|
|
1711
|
+
const accountKp = Keypair.fromSecret(
|
|
1712
|
+
"SDXC3OHSJZEQIXKEWFDNEZEQ7SW5DWBPW7RKUWI36ILY3QZZ6VER7TXV"
|
|
1713
|
+
);
|
|
1714
|
+
const wal = walletSdk.Wallet.TestNet();
|
|
1715
|
+
const client = wal.getClient();
|
|
1716
|
+
|
|
1717
|
+
const resp = await client.get(
|
|
1718
|
+
`http://testanchor.stellar.org/auth?account=${accountKp.publicKey()}`
|
|
1719
|
+
);
|
|
1720
|
+
expect(resp.data.transaction).toBeTruthy();
|
|
1721
|
+
});
|
|
1722
|
+
});
|