@ellcrys/spell
Version:
The official JavaScript library for Ellcrys
289 lines (253 loc) • 7.9 kB
text/typescript
import chai = require("chai");
import Decimal from "decimal.js";
import sinon = require("sinon");
import sinonChai = require("sinon-chai");
import { Transaction } from "../../../..";
import { Address, PrivateKey } from "../../../lib";
import {
NumDecimals,
TxBalanceBuilder,
TxPayloadVersion,
} from "../../../lib/builders/transaction_builder";
import errors from "../../../lib/errors";
import RPCClient from "../../../lib/rpcclient";
import Spell from "../../../lib/spell";
const b58 = require("bs58check");
const expect = chai.expect;
chai.use(sinonChai);
describe("#TransactionBuilder", () => {
let spell: Spell;
let client: RPCClient;
let pk: PrivateKey;
let testTx: Transaction;
function makeClientStub(err: Error | null, resp: any) {
return sinon.stub(client.client, "call" as never).callsArgWith(2, err, resp);
}
class WrappedTxBalanceBuilder extends TxBalanceBuilder {
constructor(mClient?: RPCClient) {
super(mClient);
}
public getClient(): RPCClient | undefined {
return this.client;
}
public getData(): Transaction {
return this.data;
}
public setData(tx: Transaction) {
this.data = tx;
}
public finalize(sk?: PrivateKey): Promise<string> {
return super.finalize(sk);
}
}
beforeEach(() => {
spell = new Spell();
client = spell.rpcClient;
client.client = {
call: (method: string, params: any, cb: (err: any, res: any) => {}): any => {
cb(null, null);
},
};
pk = PrivateKey.from(
"wntaQsep5UAL3WAsThJx3jtJ2Ge79fjuzVvisKBhBrA4ps24o" +
"stkmKA9egwH3o7nUYxB37Kn9Ac23UEym8u81AmgUn6Zuq",
);
testTx = {
from: "e4hbAT45QeddPj3XpJiSHxb9APhuQHcMUW",
to: "e4hbAT45QeddPj3XpJiSHxb9APhuQHcMUW",
type: 1,
senderPubKey: pk.publicKey().toBase58(),
value: "1",
fee: "1",
timestamp: 1547285790,
nonce: 1,
};
});
describe("TxBalanceBuilder", () => {
describe(".constructor", () => {
it("should set client", () => {
const b = new WrappedTxBalanceBuilder(client);
expect(b.getClient()).to.not.be.undefined;
});
it("should set type to 0x1", () => {
const b = new WrappedTxBalanceBuilder(client);
expect(b.getData().type).to.eql(0x1);
});
});
describe(".from", () => {
it("should set `from` field", () => {
const b = new WrappedTxBalanceBuilder(client);
const data = "e4hbAT45QeddPj3XpJiSHxb9APhuQHcMUW";
b.from(data);
expect(b.getData().from).to.eq(data);
});
specify("when Address is provided, it should set from field", () => {
const b = new WrappedTxBalanceBuilder(client);
const data = Address.from("e4hbAT45QeddPj3XpJiSHxb9APhuQHcMUW");
b.from(data.toString());
expect(b.getData().from).to.eq(data.toString());
});
});
describe(".to", () => {
it("should set `to` field", () => {
const b = new WrappedTxBalanceBuilder(client);
const data = "e4hbAT45QeddPj3XpJiSHxb9APhuQHcMUW";
b.to(data);
expect(b.getData().to).to.eq(data);
});
specify("when Address is provided, it should set to field", () => {
const b = new WrappedTxBalanceBuilder(client);
const data = Address.from("e4hbAT45QeddPj3XpJiSHxb9APhuQHcMUW");
b.to(data.toString());
expect(b.getData().to).to.eq(data.toString());
});
});
describe(".nonce", () => {
it("should set `nonce` field", () => {
const b = new WrappedTxBalanceBuilder(client);
const data = 10;
b.nonce(data);
expect(b.getData().nonce).to.eq(data);
});
});
describe(".value", () => {
it("should set `value` field", () => {
const b = new WrappedTxBalanceBuilder(client);
const data = "10.40";
b.value(data);
expect(b.getData().value).to.eq(data);
});
specify("when Decimal is provided, it should set `value` field", () => {
const b = new WrappedTxBalanceBuilder(client);
const data = new Decimal("10.40");
b.value(data);
expect(b.getData().value).to.eq(data.toFixed(NumDecimals));
});
});
describe(".fee", () => {
it("should set `fee` field", () => {
const b = new WrappedTxBalanceBuilder(client);
const data = "10.40";
b.fee(data);
expect(b.getData().fee).to.eq(data);
});
specify("when Decimal is provided, it should set `fee` field", () => {
const b = new WrappedTxBalanceBuilder(client);
const data = new Decimal("10.40");
b.fee(data);
expect(b.getData().fee).to.eq(data.toFixed(NumDecimals));
});
});
describe(".reset", () => {
it("should set internal data to zero value (empty object)", () => {
const b = new WrappedTxBalanceBuilder(client);
const data = "10.40";
b.fee(data);
b.value(data);
expect(b.getData().fee).to.eq(data);
expect(b.getData().value).to.eq(data);
b.reset();
expect(b.getData()).to.be.empty;
});
});
describe(".finalize", () => {
it("should throw RequirePrivateKey error when private key is not provided", () => {
const b = new WrappedTxBalanceBuilder(client);
b.finalize().catch((e) => {
expect(e).to.eq(errors.RequirePrivateKey);
});
});
describe("when key is provided", () => {
describe("when nonce field is not set", () => {
it("should throw ClientNotInitialized error when client is not initialized", () => {
const b = new WrappedTxBalanceBuilder();
delete testTx.nonce;
b.setData(testTx);
b.finalize(pk).catch((e) => {
expect(e).to.eq(errors.ClientNotInitialized);
});
});
it(
"should throw UnknownSenderAccount error when sender " +
"account is not known",
async () => {
const b = new WrappedTxBalanceBuilder(client);
delete testTx.nonce;
b.setData(testTx);
makeClientStub(
{
data: `{ "error": { "code": 30001 }}`,
statusCode: 400,
} as any,
null,
);
b.finalize(pk).catch((e) => {
expect(e).to.eq(errors.UnknownSenderAccount);
});
},
);
it("should successfully call finalize()", async () => {
makeClientStub(null, 2);
const b = new WrappedTxBalanceBuilder(client);
delete testTx.nonce;
b.setData(testTx);
const hash = await b.finalize(pk);
const data = b.getData();
expect(data.timestamp).to.not.be.undefined;
expect(data.nonce).to.eq(2);
expect(data.hash).to.not.undefined;
expect(data.sig).to.not.undefined;
expect(data.hash).to.eq(hash);
});
});
});
});
describe(".payload", () => {
it("should return object", async () => {
const b = new WrappedTxBalanceBuilder(client);
expect(b.getData().timestamp).to.be.undefined;
const data = await b.payload();
expect(data.timestamp).to.not.be.undefined;
});
});
describe(".send", () => {
it(
"should throw ClientNotInitialized error when client is" +
" not initialized",
(done) => {
const b = new WrappedTxBalanceBuilder();
b.setData(testTx);
b.send(pk).catch((e) => {
expect(e.message).to.equal(errors.ClientNotInitialized.message);
done();
});
},
);
it("should successfully send signed transaction data", async () => {
const b = new WrappedTxBalanceBuilder(client);
b.setData(testTx);
const mock = makeClientStub(null, {
id: "0x000",
});
const res = await b.send(pk);
expect(res.id).to.equal("0x000");
expect(mock).to.have.been.calledWith("ell_send", b.getData());
});
});
describe(".packed", () => {
it(
"should successfully return a valid base58 serialized " +
"version of the transaction",
async () => {
const b = new WrappedTxBalanceBuilder(client);
b.setData(testTx);
const res = await b.packed(pk);
const decoded = b58.decode(res);
expect(decoded[0]).to.eql(TxPayloadVersion[0]);
const tx = JSON.parse(decoded.slice(1).toString());
expect(b.getData()).to.deep.eq(tx);
},
);
});
});
});