postchain-client
Version:
Client library for accessing a Postchain node through REST.
376 lines • 25 kB
JavaScript
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