Skip to content

Commit

Permalink
Refactored MosaicService unit test (#510)
Browse files Browse the repository at this point in the history
* Fixed #509

* Added mosaicHttp unit tests

* Refactored mosaicHttp to use abstract caller

* Add error trapping unit test

* removed unused error catcher
  • Loading branch information
rg911 committed Mar 30, 2020
1 parent 847c798 commit cb0039b
Show file tree
Hide file tree
Showing 3 changed files with 259 additions and 90 deletions.
128 changes: 56 additions & 72 deletions src/infrastructure/MosaicHttp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
* limitations under the License.
*/

import { from as observableFrom, Observable, throwError } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { MosaicRoutesApi } from 'symbol-openapi-typescript-node-client';
import { Observable, throwError } from 'rxjs';
import { catchError, mergeMap } from 'rxjs/operators';
import { MosaicRoutesApi, MosaicIds, AccountIds, MosaicInfoDTO, MosaicDTO } from 'symbol-openapi-typescript-node-client';
import { Address } from '../model/account/Address';
import { PublicAccount } from '../model/account/PublicAccount';
import { MosaicFlags } from '../model/mosaic/MosaicFlags';
Expand Down Expand Up @@ -63,21 +63,9 @@ export class MosaicHttp extends Http implements MosaicRepository {
*/
public getMosaic(mosaicId: MosaicId): Observable<MosaicInfo> {
return this.networkTypeObservable.pipe(
mergeMap((networkType) => observableFrom(
this.mosaicRoutesApi.getMosaic(mosaicId.toHex())).pipe(
map(({body}) => new MosaicInfo(
new MosaicId(body.mosaic.id),
UInt64.fromNumericString(body.mosaic.supply),
UInt64.fromNumericString(body.mosaic.startHeight),
PublicAccount.createFromPublicKey(body.mosaic.ownerPublicKey, networkType),
body.mosaic.revision,
new MosaicFlags(body.mosaic.flags),
body.mosaic.divisibility,
UInt64.fromNumericString(body.mosaic.duration),
)),
catchError((error) => throwError(this.errorHandling(error))),
)),
);
mergeMap((networkType) =>
this.call(this.mosaicRoutesApi.getMosaic(mosaicId.toHex()), (body) => this.toMosaicInfo(body, networkType))),
);
}

/**
Expand All @@ -86,27 +74,11 @@ export class MosaicHttp extends Http implements MosaicRepository {
* @returns Observable<MosaicInfo[]>
*/
public getMosaics(mosaicIds: MosaicId[]): Observable<MosaicInfo[]> {
const mosaicIdsBody = {
mosaicIds: mosaicIds.map((id) => id.toHex()),
};
const ids = new MosaicIds();
ids.mosaicIds = mosaicIds.map((id) => id.toHex());
return this.networkTypeObservable.pipe(
mergeMap((networkType) => observableFrom(
this.mosaicRoutesApi.getMosaics(mosaicIdsBody)).pipe(
map(({body}) => body.map((mosaicInfoDTO) => {
return new MosaicInfo(
new MosaicId(mosaicInfoDTO.mosaic.id),
UInt64.fromNumericString(mosaicInfoDTO.mosaic.supply),
UInt64.fromNumericString(mosaicInfoDTO.mosaic.startHeight),
PublicAccount.createFromPublicKey(mosaicInfoDTO.mosaic.ownerPublicKey, networkType),
mosaicInfoDTO.mosaic.revision,
new MosaicFlags(mosaicInfoDTO.mosaic.flags),
mosaicInfoDTO.mosaic.divisibility,
UInt64.fromNumericString(mosaicInfoDTO.mosaic.duration),
);
})),
catchError((error) => throwError(this.errorHandling(error))),
),
),
mergeMap((networkType) =>
this.call(this.mosaicRoutesApi.getMosaics(ids), (body) => body.map((b) => this.toMosaicInfo(b, networkType)))),
);
}

Expand All @@ -117,20 +89,9 @@ export class MosaicHttp extends Http implements MosaicRepository {
*/
public getMosaicsFromAccount(address: Address): Observable<MosaicInfo[]> {
return this.networkTypeObservable.pipe(
mergeMap((networkType) => observableFrom(
this.mosaicRoutesApi.getMosaicsFromAccount(address.plain())).pipe(
map(({body}) => body.mosaics.map((mosaicInfo) =>
new MosaicInfo(
new MosaicId(mosaicInfo.id),
UInt64.fromNumericString(mosaicInfo.supply),
UInt64.fromNumericString(mosaicInfo.startHeight),
PublicAccount.createFromPublicKey(mosaicInfo.ownerPublicKey, networkType),
mosaicInfo.revision,
new MosaicFlags(mosaicInfo.flags),
mosaicInfo.divisibility,
UInt64.fromNumericString(mosaicInfo.duration)))),
catchError((error) => throwError(this.errorHandling(error))),
)),
mergeMap((networkType) =>
this.call(this.mosaicRoutesApi.getMosaicsFromAccount(address.plain()),
(body) => body.mosaics.map((b) => this.toMosaicInfoFromMosaicDto(b, networkType)))),
);
}

Expand All @@ -140,27 +101,50 @@ export class MosaicHttp extends Http implements MosaicRepository {
* @param addresses Array of addresses
*/
public getMosaicsFromAccounts(addresses: Address[]): Observable<MosaicInfo[]> {
const accountIdsBody = {
addresses: addresses.map((address) => address.plain()),
};
const accountIds = new AccountIds();
accountIds.addresses = addresses.map((address) => address.plain());
return this.networkTypeObservable.pipe(
mergeMap((networkType) => observableFrom(
this.mosaicRoutesApi.getMosaicsFromAccounts(accountIdsBody)).pipe(
map(({body}) => body.mosaics.map((mosaicInfoDTO) => {
return new MosaicInfo(
new MosaicId(mosaicInfoDTO.id),
UInt64.fromNumericString(mosaicInfoDTO.supply),
UInt64.fromNumericString(mosaicInfoDTO.startHeight),
PublicAccount.createFromPublicKey(mosaicInfoDTO.ownerPublicKey, networkType),
mosaicInfoDTO.revision,
new MosaicFlags(mosaicInfoDTO.flags),
mosaicInfoDTO.divisibility,
UInt64.fromNumericString(mosaicInfoDTO.duration),
);
})),
catchError((error) => throwError(this.errorHandling(error))),
),
),
mergeMap((networkType) =>
this.call(this.mosaicRoutesApi.getMosaicsFromAccounts(accountIds),
(body) => body.mosaics.map((b) => this.toMosaicInfoFromMosaicDto(b, networkType)))),
);
}

/**
* Maps MosaicInfoDTO to MosaicInfo
*
* @param mosaicInfo the dto object.
* @returns the model object
*/
private toMosaicInfo(mosaicInfo: MosaicInfoDTO, networkType: NetworkType): MosaicInfo {
return new MosaicInfo(
new MosaicId(mosaicInfo.mosaic.id),
UInt64.fromNumericString(mosaicInfo.mosaic.supply),
UInt64.fromNumericString(mosaicInfo.mosaic.startHeight),
PublicAccount.createFromPublicKey(mosaicInfo.mosaic.ownerPublicKey, networkType),
mosaicInfo.mosaic.revision,
new MosaicFlags(mosaicInfo.mosaic.flags),
mosaicInfo.mosaic.divisibility,
UInt64.fromNumericString(mosaicInfo.mosaic.duration),
);
}

/**
* Maps MosaicDTO to MosaicInfo
*
* @param mosaicInfo the dto object.
* @returns the model object
*/
private toMosaicInfoFromMosaicDto(mosaicInfo: MosaicDTO, networkType: NetworkType): MosaicInfo {
return new MosaicInfo(
new MosaicId(mosaicInfo.id),
UInt64.fromNumericString(mosaicInfo.supply),
UInt64.fromNumericString(mosaicInfo.startHeight),
PublicAccount.createFromPublicKey(mosaicInfo.ownerPublicKey, networkType),
mosaicInfo.revision,
new MosaicFlags(mosaicInfo.flags),
mosaicInfo.divisibility,
UInt64.fromNumericString(mosaicInfo.duration),
);
}
}
143 changes: 143 additions & 0 deletions test/infrastructure/MosaicHttp.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* Copyright 2020 NEM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect } from 'chai';
import * as http from 'http';
import {
Mosaic,
MosaicRoutesApi,
MosaicInfoDTO,
MosaicDTO,
MosaicsInfoDTO,
MosaicIds,
} from 'symbol-openapi-typescript-node-client';
import { instance, mock, reset, when, deepEqual } from 'ts-mockito';
import { DtoMapping } from '../../src/core/utils/DtoMapping';
import { MosaicRepository } from '../../src/infrastructure/MosaicRepository';
import { MosaicHttp } from '../../src/infrastructure/MosaicHttp';
import { NetworkType } from '../../src/model/network/NetworkType';
import { MosaicId } from '../../src/model/mosaic/MosaicId';
import { MosaicInfo } from '../../src/model/mosaic/MosaicInfo';
import { PublicAccount } from '../../src/model/account/PublicAccount';
import { AccountIds } from 'symbol-openapi-typescript-node-client';
import { assert } from 'chai';

describe('MosaicHttp', () => {

const publicAccount =
PublicAccount.createFromPublicKey('9801508C58666C746F471538E43002B85B1CD542F9874B2861183919BA8787B6', NetworkType.MIJIN_TEST);
const address = publicAccount.address;
const mosaicId = new MosaicId('941299B2B7E1291C');
const mosaic = new Mosaic();
mosaic.amount = '777';
mosaic.id = mosaicId.toHex();

const mosaicDto = new MosaicDTO();
mosaicDto.divisibility = 6;
mosaicDto.duration = '10';
mosaicDto.flags = 1;
mosaicDto.id = mosaicId.toHex();
mosaicDto.ownerAddress = address.encoded();
mosaicDto.ownerPublicKey = publicAccount.publicKey;
mosaicDto.revision = 0;
mosaicDto.startHeight = '1';
mosaicDto.supply = '100';

const mosaicInfoDto = new MosaicInfoDTO();
const mosaicsInfoDto = new MosaicsInfoDTO();
mosaicInfoDto.mosaic = mosaicDto;
mosaicsInfoDto.mosaics = [mosaicDto];

const url = 'http://someHost';
const response: http.IncomingMessage = mock();
const mosaicRoutesApi: MosaicRoutesApi = mock();
const mosaicRepository: MosaicRepository = DtoMapping.assign(new MosaicHttp(url, NetworkType.MIJIN_TEST),
{mosaicRoutesApi: instance(mosaicRoutesApi)});

before(() => {
reset(response);
reset(mosaicRoutesApi);
});

function assertMosaicInfo(mosaicInfo: MosaicInfo) {
expect(mosaicInfo).to.be.not.null;
expect(mosaicInfo.divisibility).to.be.equals(6);
expect(mosaicInfo.duration.toString()).to.be.equals(mosaicDto.duration);
expect(mosaicInfo.flags.getValue()).to.be.equals(mosaicDto.flags);
expect(mosaicInfo.height.toString()).to.be.equals(mosaicDto.startHeight);
expect(mosaicInfo.supply.toString()).to.be.equals(mosaicDto.supply);
expect(mosaicInfo.owner.publicKey).to.be.equals(mosaicDto.ownerPublicKey);
expect(mosaicInfo.owner.address.encoded()).to.be.equals(mosaicDto.ownerAddress);
expect(mosaicInfo.revision).to.be.equals(mosaicDto.revision);
expect(mosaicInfo.id.toHex()).to.be.equals(mosaicId.toHex());
}

it('getMosaic', async () => {
when(mosaicRoutesApi.getMosaic(mosaicId.toHex())).thenReturn(Promise.resolve({response, body: mosaicInfoDto}));
const mosaicInfo = await mosaicRepository.getMosaic(mosaicId).toPromise();
assertMosaicInfo(mosaicInfo);
});

it('getMosaics', async () => {
const mosaicIds = new MosaicIds();
mosaicIds.mosaicIds = [mosaicId.toHex()];
when(mosaicRoutesApi.getMosaics(deepEqual(mosaicIds))).thenReturn(Promise.resolve({response, body: [mosaicInfoDto]}));
const mosaicInfos = await mosaicRepository.getMosaics([mosaicId]).toPromise();
assertMosaicInfo(mosaicInfos[0]);
});

it('getMosaicsFromAccount', async () => {
when(mosaicRoutesApi.getMosaicsFromAccount(address.plain())).thenReturn(Promise.resolve({response, body: mosaicsInfoDto}));
const mosaicsInfo = await mosaicRepository.getMosaicsFromAccount(address).toPromise();
assertMosaicInfo(mosaicsInfo[0]);
});

it('getMosaicsFromAccounts', async () => {
const accountIds = new AccountIds();
accountIds.addresses = [address.plain()];
when(mosaicRoutesApi.getMosaicsFromAccounts(deepEqual(accountIds)))
.thenReturn(Promise.resolve({response, body: mosaicsInfoDto}));
const mosaicsInfo = await mosaicRepository.getMosaicsFromAccounts([address]).toPromise();
assertMosaicInfo(mosaicsInfo[0]);
});

it('getMosaic - Error', async () => {
when(mosaicRoutesApi.getMosaic(mosaicId.toHex())).thenThrow(new Error('Mocked Error'));
await mosaicRepository.getMosaic(mosaicId).toPromise().catch((error) =>
expect(error).not.to.be.undefined);
});

it('getMosaics - Error', async () => {
const mosaicIds = new MosaicIds();
mosaicIds.mosaicIds = [mosaicId.toHex()];
when(mosaicRoutesApi.getMosaics(deepEqual(mosaicIds))).thenThrow(new Error('Mocked Error'));
await mosaicRepository.getMosaics([mosaicId]).toPromise().catch((error) =>
expect(error).not.to.be.undefined);
});

it('getMosaicsFromAccount - Error', async () => {
when(mosaicRoutesApi.getMosaicsFromAccount(address.plain())).thenThrow(new Error('Mocked Error'));
await mosaicRepository.getMosaicsFromAccount(address).toPromise().catch((error) =>
expect(error).not.to.be.undefined);
});

it('getMosaicsFromAccounts - Error', async () => {
const accountIds = new AccountIds();
accountIds.addresses = [address.plain()];
when(mosaicRoutesApi.getMosaicsFromAccounts(deepEqual(accountIds))).thenThrow(new Error('Mocked Error'));
await mosaicRepository.getMosaicsFromAccounts([address]).toPromise().catch((error) =>
expect(error).not.to.be.undefined);
});
});
Loading

0 comments on commit cb0039b

Please sign in to comment.