fabric-samples/token-erc-20/chaincode-javascript/test/tokenERC20.test.js

299 lines
13 KiB
JavaScript

/*
* Copyright IBM Corp. All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
'use strict';
const { Context } = require('fabric-contract-api');
const { ChaincodeStub, ClientIdentity } = require('fabric-shim');
const { TokenERC20Contract } = require('..');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const sinon = require('sinon');
const expect = chai.expect;
chai.should();
chai.use(chaiAsPromised);
describe('Chaincode', () => {
let token;
let ctx;
let mockStub;
let mockClientIdentity;
beforeEach(async () => {
token = new TokenERC20Contract();
ctx = sinon.createStubInstance(Context);
mockStub = sinon.createStubInstance(ChaincodeStub);
ctx.stub = mockStub;
mockClientIdentity = sinon.createStubInstance(ClientIdentity);
ctx.clientIdentity = mockClientIdentity;
mockClientIdentity.getMSPID.returns('Org1MSP');
await token.Initialize(ctx, 'some name', 'some symbol', '2');
mockStub.getState.withArgs('name').resolves(Buffer.from('some name'));
mockStub.getState.withArgs('symbol').resolves(Buffer.from('some symbol'));
mockStub.getState.withArgs('decimals').resolves(Buffer.from('2'));
mockStub.getState.withArgs('totalSupply').resolves(Buffer.from('0'));
console.log('Initialized');
});
describe('#TokenName', () => {
it('should work', async () => {
const response = await token.TokenName(ctx);
sinon.assert.calledWith(mockStub.getState, 'name');
expect(response).to.equals('some name');
});
});
describe('#Symbol', () => {
it('should work', async () => {
const response = await token.Symbol(ctx);
sinon.assert.calledWith(mockStub.getState, 'symbol');
expect(response).to.equals('some symbol');
});
});
describe('#Decimals', () => {
it('should work', async () => {
const response = await token.Decimals(ctx);
sinon.assert.calledWith(mockStub.getState, 'decimals');
expect(response).to.equals(2);
});
});
describe('#TotalSupply', () => {
it('should work', async () => {
const response = await token.TotalSupply(ctx);
sinon.assert.calledWith(mockStub.getState, 'totalSupply');
expect(response).to.equals(0);
});
});
describe('#BalanceOf', () => {
it('should work', async () => {
mockStub.createCompositeKey.returns('balance_Alice');
mockStub.getState.resolves(Buffer.from('1000'));
const response = await token.BalanceOf(ctx, 'Alice');
expect(response).to.equals(1000);
});
});
describe('#_transfer', () => {
it('should fail when the sender and the receipient are the same', async () => {
const response = token._transfer(ctx, 'Alice', 'Alice', '1000');
await expect(response).to.be.rejectedWith(Error, 'cannot transfer to and from same client account');
});
it('should fail when the sender does not have enough token', async () => {
mockStub.createCompositeKey.withArgs('balance', ['Alice']).returns('balance_Alice');
mockStub.getState.withArgs('balance_Alice').resolves(Buffer.from('500'));
const response = token._transfer(ctx, 'Alice', 'Bob', '1000');
await expect(response).to.be.rejectedWith(Error, 'client account Alice has insufficient funds.');
});
it('should transfer to a new account when the sender has enough token', async () => {
mockStub.createCompositeKey.withArgs('balance', ['Alice']).returns('balance_Alice');
mockStub.getState.withArgs('balance_Alice').resolves(Buffer.from('1000'));
mockStub.createCompositeKey.withArgs('balance', ['Bob']).returns('balance_Bob');
mockStub.getState.withArgs('balance_Bob').resolves(null);
await token._transfer(ctx, 'Alice', 'Bob', '1000');
sinon.assert.calledWith(mockStub.putState.getCall(4), 'balance_Alice', Buffer.from('0'));
sinon.assert.calledWith(mockStub.putState.getCall(5), 'balance_Bob', Buffer.from('1000'));
});
it('should transfer to the existing account when the sender has enough token', async () => {
mockStub.createCompositeKey.withArgs('balance', ['Alice']).returns('balance_Alice');
mockStub.getState.withArgs('balance_Alice').resolves(Buffer.from('1000'));
mockStub.createCompositeKey.withArgs('balance', ['Bob']).returns('balance_Bob');
mockStub.getState.withArgs('balance_Bob').resolves(Buffer.from('2000'));
await token._transfer(ctx, 'Alice', 'Bob', '1000');
sinon.assert.calledWith(mockStub.putState.getCall(4), 'balance_Alice', Buffer.from('0'));
sinon.assert.calledWith(mockStub.putState.getCall(5), 'balance_Bob', Buffer.from('3000'));
});
});
describe('#Transfer', () => {
it('should work', async () => {
mockClientIdentity.getID.returns('Alice');
mockStub.createCompositeKey.withArgs('balance', ['Alice']).returns('balance_Alice');
mockStub.getState.withArgs('balance_Alice').resolves(Buffer.from('1000'));
const response = await token.Transfer(ctx, 'Bob', '1000');
const event = { from: 'Alice', to: 'Bob', value: 1000 };
sinon.assert.calledWith(mockStub.setEvent, 'Transfer', Buffer.from(JSON.stringify(event)));
expect(response).to.equals(true);
});
});
describe('#TransferFrom', () => {
it('should fail when the spender is not allowed to spend the token', async () => {
mockClientIdentity.getID.returns('Charlie');
mockStub.createCompositeKey.withArgs('allowance', ['Alice', 'Charlie']).returns('allowance_Alice_Charlie');
mockStub.getState.withArgs('allowance_Alice_Charlie').resolves(Buffer.from('0'));
const response = token.TransferFrom(ctx, 'Alice', 'Bob', '1000');
await expect(response).to.be.rejectedWith(Error, 'The spender does not have enough allowance to spend.');
});
it('should transfer when the spender is allowed to spend the token', async () => {
mockClientIdentity.getID.returns('Charlie');
mockStub.createCompositeKey.withArgs('balance', ['Alice']).returns('balance_Alice');
mockStub.getState.withArgs('balance_Alice').resolves(Buffer.from('1000'));
mockStub.createCompositeKey.withArgs('allowance', ['Alice', 'Charlie']).returns('allowance_Alice_Charlie');
mockStub.getState.withArgs('allowance_Alice_Charlie').resolves(Buffer.from('3000'));
const response = await token.TransferFrom(ctx, 'Alice', 'Bob', '1000');
sinon.assert.calledWith(mockStub.putState, 'allowance_Alice_Charlie', Buffer.from('2000'));
const event = { from: 'Alice', to: 'Bob', value: 1000 };
sinon.assert.calledWith(mockStub.setEvent, 'Transfer', Buffer.from(JSON.stringify(event)));
expect(response).to.equals(true);
});
});
describe('#Approve', () => {
it('should work', async () => {
mockClientIdentity.getID.returns('Dave');
mockStub.createCompositeKey.returns('allowance_Dave_Eve');
const response = await token.Approve(ctx, 'Ellen', '1000');
sinon.assert.calledWith(mockStub.putState, 'allowance_Dave_Eve', Buffer.from('1000'));
expect(response).to.equals(true);
});
});
describe('#Allowance', () => {
it('should work', async () => {
mockStub.createCompositeKey.returns('allowance_Dave_Eve');
mockStub.getState.resolves(Buffer.from('1000'));
const response = await token.Allowance(ctx, 'Dave', 'Eve');
expect(response).to.equals(1000);
});
});
describe('#Initialize', () => {
it('should work', async () => {
// We consider it has already been initialized in the before-each statement
sinon.assert.calledWith(mockStub.putState, 'name', Buffer.from('some name'));
sinon.assert.calledWith(mockStub.putState, 'symbol', Buffer.from('some symbol'));
sinon.assert.calledWith(mockStub.putState, 'decimals', Buffer.from('2'));
});
it('should failed if called a second time', async () => {
// We consider it has already been initialized in the before-each statement
const response = token.Initialize(ctx, 'some name', 'some symbol', '2');
await expect(response).to.be.rejectedWith(Error, 'contract options are already set, client is not authorized to change them');
});
});
describe('#Mint', () => {
it('should add token to a new account and a new total supply', async () => {
mockClientIdentity.getID.returns('Alice');
mockStub.createCompositeKey.returns('balance_Alice');
const response = await token.Mint(ctx, '1000');
sinon.assert.calledWith(mockStub.putState.getCall(4), 'balance_Alice', Buffer.from('1000'));
sinon.assert.calledWith(mockStub.putState.getCall(5), 'totalSupply', Buffer.from('1000'));
expect(response).to.equals(true);
});
it('should add token to the existing account and the existing total supply', async () => {
mockClientIdentity.getID.returns('Alice');
mockStub.createCompositeKey.returns('balance_Alice');
mockStub.getState.withArgs('balance_Alice').resolves(Buffer.from('1000'));
mockStub.getState.withArgs('totalSupply').resolves(Buffer.from('2000'));
const response = await token.Mint(ctx, '1000');
sinon.assert.calledWith(mockStub.putState.getCall(4), 'balance_Alice', Buffer.from('2000'));
sinon.assert.calledWith(mockStub.putState.getCall(5), 'totalSupply', Buffer.from('3000'));
expect(response).to.equals(true);
});
it('should add token to a new account and the existing total supply', async () => {
mockClientIdentity.getID.returns('Alice');
mockStub.createCompositeKey.returns('balance_Alice');
mockStub.getState.withArgs('balance_Alice').resolves(null);
mockStub.getState.withArgs('totalSupply').resolves(Buffer.from('2000'));
const response = await token.Mint(ctx, '1000');
sinon.assert.calledWith(mockStub.putState.getCall(4), 'balance_Alice', Buffer.from('1000'));
sinon.assert.calledWith(mockStub.putState.getCall(5), 'totalSupply', Buffer.from('3000'));
expect(response).to.equals(true);
});
});
describe('#Burn', () => {
it('should work', async () => {
mockClientIdentity.getID.returns('Alice');
mockStub.createCompositeKey.returns('balance_Alice');
mockStub.getState.withArgs('balance_Alice').resolves(Buffer.from('1000'));
mockStub.getState.withArgs('totalSupply').resolves(Buffer.from('2000'));
const response = await token.Burn(ctx, '1000');
sinon.assert.calledWith(mockStub.putState.getCall(4), 'balance_Alice', Buffer.from('0'));
sinon.assert.calledWith(mockStub.putState.getCall(5), 'totalSupply', Buffer.from('1000'));
expect(response).to.equals(true);
});
});
describe('#ClientAccountBalance', () => {
it('should work', async () => {
mockClientIdentity.getID.returns('Alice');
mockStub.createCompositeKey.returns('balance_Alice');
mockStub.getState.resolves(Buffer.from('1000'));
const response = await token.ClientAccountBalance(ctx);
expect(response).to.equals(1000);
});
});
describe('#ClientAccountID', () => {
it('should work', async () => {
mockClientIdentity.getID.returns('x509::{subject DN}::{issuer DN}');
const response = await token.ClientAccountID(ctx);
sinon.assert.calledOnce(mockClientIdentity.getID);
expect(response).to.equals('x509::{subject DN}::{issuer DN}');
});
});
describe('#ClientAccountMSPID', () => {
it('should work', async () => {
const response = await token.ClientAccountMSPID(ctx);
sinon.assert.calledTwice(mockClientIdentity.getMSPID);
expect(response).to.equals('Org1MSP');
});
});
describe('#CheckAuthorization', () => {
it('should work', async () => {
await token.CheckAuthorization(ctx);
});
it('should failed if called by not Org1MSP', () => {
mockClientIdentity.getMSPID.returns('Org2MSP');
expect(() => token.CheckAuthorization(ctx)).to.throw(Error, 'client is not authorized');
});
});
});