Issue
I'm trying to build stub objects to test my hyperledger fabric chaincode written in typescript.
The method I'm interested in stubbing is the getHistoryForKey
method which has a return type of Promise<Iterators.HistoryQueryIterator> & AsyncIterable<Iterators.KeyModification>
.
Can someone please tell me how to generate a stub object for this?
My code:
chai.should();
chai.use(chaiAsPromised);
chai.use(sinonChai);
class TestContext implements Context {
public stub: sinon.SinonStubbedInstance<ChaincodeStub> = sinon.createStubInstance(ChaincodeStub);
public clientIdentity: sinon.SinonStubbedInstance<ClientIdentity> = sinon.createStubInstance(ClientIdentity);
public logger = {
getLogger: sinon.stub().returns(sinon.createStubInstance(winston.createLogger().constructor)),
setLevel: sinon.stub(),
};
}
describe('MyContract', () => {
let ctx: TestContext;
let stubAsyncIterator: AsyncIterable<Iterators.KeyModification> & Iterators.HistoryQueryIterator;
beforeEach(() => {
ctx = new TestContext();
stubAsyncIterator = {
async *[Symbol.asyncIterator]() {
yield { isDelete: false, value: Buffer.from('{"entityID":"1000","data":{}}'), timestamp: { seconds: 123, nanos: 111 } as Timestamp, txId: 'txid' } as Iterators.KeyModification;
},
};
ctx.stub.getHistoryForKey.withArgs('1000').resolves(stubAsyncIterator);
});
...
});
This errors with:
error TS2322: Type '{ [Symbol.asyncIterator](): AsyncGenerator<KeyModification, void, undefined>; }' is not assignable to type 'AsyncIterable<KeyModification> & HistoryQueryIterator'.
Type '{ [Symbol.asyncIterator](): AsyncGenerator<KeyModification, void, undefined>; }' is missing the following properties from type 'CommonIterator<KeyModification>': close, next
Solution
This was a pretty tough one. Found it easier to just call the original code and mock out certain functions than try to recreate the getHistoryForKey
object manually. All this code is inspired from looking at the unit tests.
Here's the code:
import { Context } from 'fabric-contract-api';
import { ChaincodeStub, ClientIdentity, Iterators } from 'fabric-shim';
import { HistoryQueryIterator } from 'fabric-shim/lib/iterators';
import { ChaincodeMessageHandler } from 'fabric-shim/lib/handler';
chai.should();
chai.use(chaiAsPromised);
chai.use(sinonChai);
// test context updated to match fabric-contract-api version 2.4.1
class TestContext implements Context {
public stub: sinon.SinonStubbedInstance<ChaincodeStub> = sinon.createStubInstance(ChaincodeStub);
public clientIdentity: sinon.SinonStubbedInstance<ClientIdentity> = sinon.createStubInstance(ClientIdentity);
public logging = {
getLogger: sinon.stub().returns(sinon.createStubInstance(winston.createLogger().constructor)),
setLevel: sinon.stub(),
};
}
describe('MyContract', () => {
let ctx: TestContext;
let mockHandler: ChaincodeMessageHandler;
let mockResponse: { results: any, hasMore: boolean };
let sandbox;
beforeEach(() => {
...
sandbox = sinon.createSandbox();
mockHandler = sandbox.createStubInstance(ChaincodeMessageHandler);
// we're not actually processing the results, so just fill the results array with the number of items you want to simulate returning in history
mockResponse = {results: [1,2,3,4,5], hasMore: false};
const iterator = new HistoryQueryIterator(mockHandler, 'TestChannelID','txId',mockResponse);
const getResultsFromBytesStub = sinon.stub(iterator, '_getResultFromBytes');
// mocking out the actual responses returned with each iteration
getResultsFromBytesStub.onCall(0).returns({ value: Buffer.from('{"id":"1000","data":{"value": 1}}')});
getResultsFromBytesStub.onCall(1).returns({ value: Buffer.from('{"id":"1000","data":{"value": 2}}')});
getResultsFromBytesStub.onCall(2).returns({ value: Buffer.from('{"id":"1000","data":{"value": 3}}')});
getResultsFromBytesStub.onCall(3).returns({ value: Buffer.from('{"id":"1000","data":{"value": 4}}')});
getResultsFromBytesStub.onCall(4).returns({ value: Buffer.from('{"id":"1000","data":{"value": 5}}')});
ctx.stub.getHistoryForKey.withArgs('1000').resolves(iterator);
});
...
Answered By - PressingOnAlways
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.