UNPKG

postchain-client

Version:

Client library for accessing a Postchain node through REST.

376 lines 25 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { TxRejectedError, UnexpectedStatusError } from "../../../src/blockchainClient/errors"; import { server } from "../../../mocks/servers"; import { errorHandler } from "../../../mocks/handlers"; import { AnchoringStatus, ChainConfirmationLevel, ResponseStatus, TransactionEvent, } from "../../../src/blockchainClient/enums"; import { exampleOperation, mockBuffer, mockSignatureProvider, mockUnsignedTx, mockUnsignedMultiOperationTx, mockSignerKeyPair, CONFIGURED_POLL_COUNT, mockTransactionReceipts, mockClusterAnchoringConfirmationResponse, mockSystemAnchoringConfirmationResponse, mockGtx, unsignedNonUniqueTxWithTwoSigners, LOCAL_POOL, } from "../../common/mocks"; import { ComplexArgumentObject } from "../../common/testData"; import { getTestsClient, handleTransactionConfirmationMockEvents } from "../../common/setups"; import * as utils from "../../../src/blockchainClient/utils"; import * as httpUtils from "../../../src/blockchainClient/httpUtil"; import { Web3PromiEvent } from "../../../src/promiEvent/promiEvents"; import { http, HttpResponse } from "msw"; import { NumberOfSignersAndSignaturesException } from "../../../src/gtx/errors"; const rejectReason = "[bc-rid=8E:DFB4, chain-id=0] Operation 'news_feed_ch5.news_feed:make_post' failed: Expected at least two operations, make sure that you included auth"; let client; describe("send transactions", () => { beforeAll(() => __awaiter(void 0, void 0, void 0, function* () { server.listen(); client = yield getTestsClient(); })); beforeEach(() => { jest.clearAllMocks(); jest.spyOn(utils, "getAnchoringClientAndSystemChainRid").mockResolvedValue({ anchoringClient: client, systemAnchoringChainBridString: client.config.blockchainRid, }); }); afterEach(() => { jest.restoreAllMocks(); server.resetHandlers(); }); describe("sendTransaction", () => { it("takes a callback", () => __awaiter(void 0, void 0, void 0, function* () { const mockCallback = jest.fn(); yield client.sendTransaction(exampleOperation, true, mockCallback); expect(mockCallback).toHaveBeenCalled(); expect(mockCallback).toHaveBeenCalledWith(null, mockTransactionReceipts.waiting); })); it("throws an error if endpoint returns 400 status", () => __awaiter(void 0, void 0, void 0, function* () { server.use(errorHandler.txError); yield expect(client.sendTransaction(exampleOperation)).rejects.toThrow(new UnexpectedStatusError(400, '{"error": "Could not parse JSON"}')); })); it("throws TxRejectedError with detailed error information if transaction is rejected", () => __awaiter(void 0, void 0, void 0, function* () { server.use(errorHandler.statusRejected); yield expect(client.sendTransaction(exampleOperation)).rejects.toMatchObject({ fullReason: rejectReason, shortReason: "Expected at least two operations, make sure that you included auth", rellLine: "bc-rid=8E:DFB4, chain-id=0", operation: "news_feed_ch5.news_feed:make_post", }); })); it("throws error if transaction gets rejected, rejectReason is not standard format and error will not contain full reason, short reason, operation and rell line", () => __awaiter(void 0, void 0, void 0, function* () { server.use(errorHandler.txStatusInvalidRejectFormat); const invalidFormatRejectReason = "Operation 'news_feed_ch5.news_feed:make_post' failed: Expected at least two operations, make sure that you included auth"; yield expect(client.sendTransaction(exampleOperation)).rejects.toMatchObject({ fullReason: invalidFormatRejectReason, shortReason: undefined, rellLine: undefined, operation: undefined, }); })); it("returns status Confirmed when transaction is confirmed in Dapp", () => __awaiter(void 0, void 0, void 0, function* () { const response = yield client.sendTransaction(exampleOperation); expect(response).toEqual(mockTransactionReceipts.confirmed); })); it("returns status Waiting when transaction is waiting and reaches configured poll count", () => __awaiter(void 0, void 0, void 0, function* () { const clientWithConfiguredDappPollCount = yield getTestsClient({ dappStatusPolling: utils.setStatusPolling({ count: CONFIGURED_POLL_COUNT, }), }); const getTransactionStatusSpy = jest.spyOn(clientWithConfiguredDappPollCount, "getTransactionStatus"); server.use(errorHandler.statusWaiting); const response = yield clientWithConfiguredDappPollCount.sendTransaction(exampleOperation); expect(response).toEqual(mockTransactionReceipts.waiting); expect(getTransactionStatusSpy).toHaveBeenCalledTimes(CONFIGURED_POLL_COUNT); })); it("returns status Unknown when transaction status is unknown and reaches configured poll count", () => __awaiter(void 0, void 0, void 0, function* () { const clientWithConfiguredDappPollCount = yield getTestsClient({ dappStatusPolling: utils.setStatusPolling({ count: CONFIGURED_POLL_COUNT, }), }); const getTransactionStatusSpy = jest.spyOn(clientWithConfiguredDappPollCount, "getTransactionStatus"); server.use(errorHandler.statusUnknown); const response = yield clientWithConfiguredDappPollCount.sendTransaction(exampleOperation); expect(response).toEqual(mockTransactionReceipts.unknown); expect(getTransactionStatusSpy).toHaveBeenCalledTimes(CONFIGURED_POLL_COUNT); })); it("get tx-status when ConfirmationLevel is set to Dapp", () => __awaiter(void 0, void 0, void 0, function* () { const response = yield client.sendTransaction(exampleOperation, true, undefined, ChainConfirmationLevel.Dapp); expect(response).toEqual(mockTransactionReceipts.confirmed); })); it("does NOT poll for tx-status when doStatusPolling flag is false", () => __awaiter(void 0, void 0, void 0, function* () { const getTransactionStatusSpy = jest.spyOn(client, "getTransactionStatus"); const result = yield client.sendTransaction(exampleOperation, false); expect(result).toEqual(mockTransactionReceipts.pending); expect(getTransactionStatusSpy).not.toHaveBeenCalled(); })); it("throws TxRejectedError with FailedAnchoring Anchoring Status", () => __awaiter(void 0, void 0, void 0, function* () { yield expect(client.sendTransaction(exampleOperation, true, undefined, ChainConfirmationLevel.ClusterAnchoring)).rejects.toThrow(new TxRejectedError(AnchoringStatus.FailedAnchoring)); })); it("sends an unsigned transaction and receives dapp confirmed status", () => __awaiter(void 0, void 0, void 0, function* () { const transactionReceipt = yield client.sendTransaction(exampleOperation); expect(transactionReceipt).toEqual(mockTransactionReceipts.confirmed); })); it("throws a NumberOfSignersAndSignaturesException if signers and signatures array have different size", () => __awaiter(void 0, void 0, void 0, function* () { const signedTransaction = yield client.signTransaction(unsignedNonUniqueTxWithTwoSigners, mockSignatureProvider); yield expect(client.sendTransaction(signedTransaction)).rejects.toThrow(NumberOfSignersAndSignaturesException); })); it("signs and sends a transaction", () => __awaiter(void 0, void 0, void 0, function* () { const mockSignedTransaction = mockBuffer; jest.spyOn(client, "signTransaction").mockResolvedValue(mockSignedTransaction); jest.spyOn(utils, "getGTXFromBufferOrTransactionOrOperation").mockReturnValue(mockGtx); jest.spyOn(client, "signTransaction").mockResolvedValue(mockSignedTransaction); jest .spyOn(utils, "handleDappConfirmations") .mockResolvedValue(mockTransactionReceipts.confirmed); const transactionReceipt = yield client.sendTransaction(mockSignedTransaction); expect(transactionReceipt).toEqual(mockTransactionReceipts.confirmed); })); it("sends a transaction with multiple operations", () => __awaiter(void 0, void 0, void 0, function* () { const signedMultiOperationTx = mockBuffer; jest.spyOn(utils, "getGTXFromBufferOrTransactionOrOperation").mockReturnValue(mockGtx); jest.spyOn(client, "signTransaction").mockResolvedValue(signedMultiOperationTx); jest .spyOn(utils, "handleDappConfirmations") .mockResolvedValue(mockTransactionReceipts.confirmed); const transactionReceipt = yield client.sendTransaction(signedMultiOperationTx); expect(transactionReceipt).toEqual(mockTransactionReceipts.confirmed); })); it("receives a Cluster Anchoring Confirmation", () => __awaiter(void 0, void 0, void 0, function* () { jest .spyOn(utils, "handleDappConfirmations") .mockResolvedValue(mockTransactionReceipts.confirmed); jest .spyOn(utils, "handleSystemConfirmations") .mockResolvedValue(mockClusterAnchoringConfirmationResponse); const result = yield client.sendTransaction(exampleOperation, true, undefined, ChainConfirmationLevel.ClusterAnchoring); expect(result).toEqual(mockClusterAnchoringConfirmationResponse); })); it("receives a System Anchoring Confirmation", () => __awaiter(void 0, void 0, void 0, function* () { jest .spyOn(utils, "handleDappConfirmations") .mockResolvedValue(mockTransactionReceipts.confirmed); jest .spyOn(utils, "handleSystemConfirmations") .mockResolvedValue(mockSystemAnchoringConfirmationResponse); const result = yield client.sendTransaction(exampleOperation, true, undefined, ChainConfirmationLevel.SystemAnchoring); expect(result).toEqual(mockSystemAnchoringConfirmationResponse); })); it("triggers DappReceived event when ConfirmationLevel is None", () => __awaiter(void 0, void 0, void 0, function* () { handleTransactionConfirmationMockEvents([ { eventToEmit: TransactionEvent.DappReceived, eventResolution: mockTransactionReceipts.waiting, }, ]); const promiEvent = client.sendTransaction(exampleOperation, true, undefined, ChainConfirmationLevel.None); const promiEventSpy = jest.spyOn(promiEvent, "emit"); yield expect(promiEvent).resolves.toEqual(mockTransactionReceipts.waiting); expect(promiEventSpy).toHaveBeenCalledTimes(1); expect(promiEventSpy).toHaveBeenCalledWith(TransactionEvent.DappReceived, mockTransactionReceipts.waiting); })); it("triggers DappReceived and DappConfirmed events when Confirmation level is set to Dapp", () => __awaiter(void 0, void 0, void 0, function* () { handleTransactionConfirmationMockEvents([ { eventToEmit: TransactionEvent.DappReceived, eventResolution: mockTransactionReceipts.waiting, }, { eventToEmit: TransactionEvent.DappConfirmed, eventResolution: mockTransactionReceipts.confirmed, }, ]); const promiEvent = client.sendTransaction(exampleOperation, true, undefined, ChainConfirmationLevel.Dapp); const promiEventSpy = jest.spyOn(promiEvent, "emit"); yield expect(promiEvent).resolves.toEqual(mockTransactionReceipts.confirmed); expect(promiEventSpy).toHaveBeenCalledTimes(2); expect(promiEventSpy).toHaveBeenCalledWith(TransactionEvent.DappReceived, mockTransactionReceipts.waiting); expect(promiEventSpy).toHaveBeenCalledWith(TransactionEvent.DappConfirmed, mockTransactionReceipts.confirmed); })); it("triggers DappReceived, DappConfirmed and ClusterAnchoringConfirmation events when Confirmation level is set to ClusterAnchoringChain", () => __awaiter(void 0, void 0, void 0, function* () { handleTransactionConfirmationMockEvents([ { eventToEmit: TransactionEvent.DappReceived, eventResolution: mockTransactionReceipts.waiting, }, { eventToEmit: TransactionEvent.DappConfirmed, eventResolution: mockTransactionReceipts.confirmed, }, { eventToEmit: TransactionEvent.ClusterAnchoringConfirmation, eventResolution: mockClusterAnchoringConfirmationResponse, }, ]); const promiEvent = client.sendTransaction(exampleOperation, true, undefined, ChainConfirmationLevel.ClusterAnchoring); const promiEventSpy = jest.spyOn(promiEvent, "emit"); yield expect(promiEvent).resolves.toEqual(mockClusterAnchoringConfirmationResponse); expect(promiEventSpy).toHaveBeenCalledTimes(3); expect(promiEventSpy).toHaveBeenCalledWith(TransactionEvent.DappReceived, mockTransactionReceipts.waiting); expect(promiEventSpy).toHaveBeenCalledWith(TransactionEvent.DappConfirmed, mockTransactionReceipts.confirmed); expect(promiEventSpy).toHaveBeenCalledWith(TransactionEvent.ClusterAnchoringConfirmation, mockClusterAnchoringConfirmationResponse); })); it("triggers DappReceived, DappConfirmed, ClusterAnchoringConfirmation and SystemAnchoringConfirmation events when Confirmation level is set to SystemAnchoring", () => __awaiter(void 0, void 0, void 0, function* () { handleTransactionConfirmationMockEvents([ { eventToEmit: TransactionEvent.DappReceived, eventResolution: mockTransactionReceipts.waiting, }, { eventToEmit: TransactionEvent.DappConfirmed, eventResolution: mockTransactionReceipts.confirmed, }, { eventToEmit: TransactionEvent.ClusterAnchoringConfirmation, eventResolution: mockClusterAnchoringConfirmationResponse, }, { eventToEmit: TransactionEvent.SystemAnchoringConfirmation, eventResolution: mockSystemAnchoringConfirmationResponse, }, ]); const promiEvent = client.sendTransaction(exampleOperation, true, undefined, ChainConfirmationLevel.SystemAnchoring); const promiEventSpy = jest.spyOn(promiEvent, "emit"); yield expect(promiEvent).resolves.toEqual(mockSystemAnchoringConfirmationResponse); expect(promiEventSpy).toHaveBeenCalledTimes(4); expect(promiEventSpy).toHaveBeenCalledWith(TransactionEvent.DappReceived, mockTransactionReceipts.waiting); expect(promiEventSpy).toHaveBeenCalledWith(TransactionEvent.DappConfirmed, mockTransactionReceipts.confirmed); expect(promiEventSpy).toHaveBeenCalledWith(TransactionEvent.ClusterAnchoringConfirmation, mockClusterAnchoringConfirmationResponse); expect(promiEventSpy).toHaveBeenCalledWith(TransactionEvent.SystemAnchoringConfirmation, mockSystemAnchoringConfirmationResponse); })); it("throws TxRejectedError with message FailedAnchoring", () => __awaiter(void 0, void 0, void 0, function* () { jest .spyOn(client, "getClusterAnchoringTransactionConfirmation") .mockResolvedValue(AnchoringStatus.FailedAnchoring); yield expect(client.sendTransaction(exampleOperation, true, undefined, ChainConfirmationLevel.ClusterAnchoring)).rejects.toThrow(new TxRejectedError(AnchoringStatus.FailedAnchoring)); })); it("retries default number of times", () => __awaiter(void 0, void 0, void 0, function* () { const handleRequestSpy = jest.spyOn(httpUtils, "handleRequest"); try { yield client.sendTransaction(exampleOperation); } catch (error) { expect(error).not.toBeNull(); expect(handleRequestSpy).toHaveBeenCalledTimes(9); } }), 20000); it("returns a PromiEvent", () => __awaiter(void 0, void 0, void 0, function* () { jest.spyOn(client, "sendTransaction").mockImplementation(() => { return new Web3PromiEvent(() => undefined); }); const promiEvent = client.sendTransaction(exampleOperation); expect(promiEvent).toBeInstanceOf(Web3PromiEvent); expect(typeof promiEvent.then).toBe("function"); expect(typeof promiEvent.catch).toBe("function"); expect(typeof promiEvent.finally).toBe("function"); expect(typeof promiEvent.on).toBe("function"); expect(typeof promiEvent.once).toBe("function"); })); }); describe("signAndSendUniqueTransaction", () => { it("takes a callback", () => __awaiter(void 0, void 0, void 0, function* () { const mockCallback = jest.fn(); jest.spyOn(client, "signTransaction").mockResolvedValue(mockBuffer); const mockPromiEvent = new Web3PromiEvent(resolve => { mockCallback(null, mockTransactionReceipts.waiting); resolve(mockTransactionReceipts.confirmed); }); jest.spyOn(client, "sendTransaction").mockImplementation(() => mockPromiEvent); yield client.signAndSendUniqueTransaction(exampleOperation, mockSignatureProvider, true, mockCallback); expect(mockCallback).toHaveBeenCalled(); expect(mockCallback).toHaveBeenCalledWith(null, mockTransactionReceipts.waiting); })); it("signs and sends a transaction", () => __awaiter(void 0, void 0, void 0, function* () { jest.spyOn(client, "signTransaction").mockResolvedValue(mockBuffer); const mockPromiEvent = new Web3PromiEvent(resolve => { resolve(mockTransactionReceipts.confirmed); }); jest.spyOn(client, "sendTransaction").mockImplementation(() => mockPromiEvent); const transactionReceipt = yield client.signAndSendUniqueTransaction(mockUnsignedTx, mockSignatureProvider); expect(transactionReceipt).toEqual(mockTransactionReceipts.confirmed); })); it("sends an operation", () => __awaiter(void 0, void 0, void 0, function* () { jest.spyOn(client, "signTransaction").mockResolvedValue(mockBuffer); const mockPromiEvent = new Web3PromiEvent(resolve => { resolve(mockTransactionReceipts.confirmed); }); jest.spyOn(client, "sendTransaction").mockImplementation(() => mockPromiEvent); const transactionReceipt = yield client.signAndSendUniqueTransaction(exampleOperation, mockSignatureProvider); expect(transactionReceipt).toEqual(mockTransactionReceipts.confirmed); })); it("signs if no pubKey was provided", () => __awaiter(void 0, void 0, void 0, function* () { jest.spyOn(client, "signTransaction").mockResolvedValue(mockBuffer); const mockPromiEvent = new Web3PromiEvent(resolve => { resolve(mockTransactionReceipts.confirmed); }); jest.spyOn(client, "sendTransaction").mockImplementation(() => mockPromiEvent); const transactionReceipt = yield client.signAndSendUniqueTransaction(exampleOperation, mockSignerKeyPair); expect(transactionReceipt).toEqual(mockTransactionReceipts.confirmed); })); it("adds a nop", () => __awaiter(void 0, void 0, void 0, function* () { jest.spyOn(client, "signTransaction").mockResolvedValue(mockBuffer); const mockPromiEvent = new Web3PromiEvent(resolve => { resolve(mockTransactionReceipts.confirmed); }); jest.spyOn(client, "sendTransaction").mockImplementation(() => mockPromiEvent); const transactionReceipt = yield client.signAndSendUniqueTransaction(exampleOperation, mockSignerKeyPair); const transactionReceiptCopy = yield client.signAndSendUniqueTransaction(exampleOperation, mockSignerKeyPair); expect(transactionReceipt).toEqual(mockTransactionReceipts.confirmed); expect(transactionReceiptCopy).toEqual(mockTransactionReceipts.confirmed); })); it("signs and sends a transaction with multiple operations", () => __awaiter(void 0, void 0, void 0, function* () { jest.spyOn(client, "signTransaction").mockResolvedValue(mockBuffer); const mockPromiEvent = new Web3PromiEvent(resolve => { resolve(mockTransactionReceipts.confirmed); }); jest.spyOn(client, "sendTransaction").mockImplementation(() => mockPromiEvent); const transactionReceipt = yield client.signAndSendUniqueTransaction(mockUnsignedMultiOperationTx, mockSignatureProvider); expect(transactionReceipt).toEqual(mockTransactionReceipts.confirmed); })); it("signs and sends an operation with large complex arguments", () => __awaiter(void 0, void 0, void 0, function* () { jest.spyOn(client, "signTransaction").mockResolvedValue(mockBuffer); const mockPromiEvent = new Web3PromiEvent(resolve => { resolve(mockTransactionReceipts.confirmed); }); jest.spyOn(client, "sendTransaction").mockImplementation(() => mockPromiEvent); const transactionReceipt = yield client.signAndSendUniqueTransaction({ name: "set_globals", args: [ ComplexArgumentObject.client_data, ComplexArgumentObject.server_data, ComplexArgumentObject.args, ], }, mockSignatureProvider); expect(transactionReceipt).toEqual(mockTransactionReceipts.confirmed); })); it("retries default number of times", () => __awaiter(void 0, void 0, void 0, function* () { const handleRequestSpy = jest.spyOn(httpUtils, "handleRequest"); server.use(http.get(`${LOCAL_POOL}/tx/:blockchainRid/:txHash/status`, () => __awaiter(void 0, void 0, void 0, function* () { return HttpResponse.json({ status: ResponseStatus.Confirmed, }); }))); try { yield client.signAndSendUniqueTransaction(exampleOperation, mockSignerKeyPair); } catch (error) { expect(error).not.toBeNull(); expect(handleRequestSpy).toHaveBeenCalledTimes(9); } })); it("returns a PromiEvent", () => __awaiter(void 0, void 0, void 0, function* () { jest.spyOn(client, "signAndSendUniqueTransaction").mockImplementation(() => { return new Web3PromiEvent(() => undefined); }); const promiEvent = client.signAndSendUniqueTransaction(exampleOperation, mockSignatureProvider); expect(promiEvent).toBeInstanceOf(Web3PromiEvent); expect(typeof promiEvent.then).toBe("function"); expect(typeof promiEvent.catch).toBe("function"); expect(typeof promiEvent.finally).toBe("function"); expect(typeof promiEvent.on).toBe("function"); expect(typeof promiEvent.once).toBe("function"); })); }); }); //# sourceMappingURL=sendTransaction.test.js.map