Skip to content

Commit

Permalink
test(core-blockchain): increase coverage to 100% (#3604)
Browse files Browse the repository at this point in the history
  • Loading branch information
air1one authored Mar 24, 2020
1 parent 937ffa0 commit 6c8c436
Show file tree
Hide file tree
Showing 28 changed files with 2,916 additions and 9 deletions.
910 changes: 910 additions & 0 deletions __tests__/unit/core-blockchain/blockchain.test.ts

Large diffs are not rendered by default.

385 changes: 385 additions & 0 deletions __tests__/unit/core-blockchain/processor/block-processor.test.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Container } from "@arkecosystem/core-kernel";
import { AcceptBlockHandler } from "../../../../../packages/core-blockchain/src/processor/handlers/accept-block-handler";
import { BlockProcessorResult } from "../../../../../packages/core-blockchain/src/processor";
import { Interfaces } from "@arkecosystem/crypto";

describe("AcceptBlockHandler", () => {
const container = new Container.Container();

const logger = { warning: jest.fn(), debug: jest.fn(), info: jest.fn() };
const blockchain = { resetLastDownloadedBlock: jest.fn(), resetWakeUp: jest.fn() };
const state = {
forkedBlock: undefined,
started: undefined,
setLastBlock: jest.fn(),
lastDownloadedBlock: undefined
};
const database = { applyBlock: jest.fn() };
const transactionPool = { acceptForgedTransaction: jest.fn() };

const application = { get: jest.fn() };

beforeAll(() => {
container.unbindAll();
container.bind(Container.Identifiers.Application).toConstantValue(application);
container.bind(Container.Identifiers.LogService).toConstantValue(logger);
container.bind(Container.Identifiers.BlockchainService).toConstantValue(blockchain);
container.bind(Container.Identifiers.StateStore).toConstantValue(state);
container.bind(Container.Identifiers.DatabaseService).toConstantValue(database);
container.bind(Container.Identifiers.TransactionPoolService).toConstantValue(transactionPool);
});

beforeEach(() => {
jest.resetAllMocks();
});

describe("execute", () => {
const block = {
data: { id: "1222", height: 5544 },
transactions: [
{ "id": "11" },
{ "id": "12" },
]
};

it("should apply block to database, transaction pool, blockchain and state", async () => {
const acceptBlockHandler = container.resolve<AcceptBlockHandler>(AcceptBlockHandler);

state.started = true;
const result = await acceptBlockHandler.execute(block as Interfaces.IBlock);

expect(result).toBe(BlockProcessorResult.Accepted);

expect(database.applyBlock).toBeCalledTimes(1);
expect(database.applyBlock).toHaveBeenCalledWith(block);

expect(blockchain.resetWakeUp).toBeCalledTimes(1);

expect(transactionPool.acceptForgedTransaction).toBeCalledTimes(2);
expect(transactionPool.acceptForgedTransaction).toHaveBeenCalledWith(block.transactions[0]);
expect(transactionPool.acceptForgedTransaction).toHaveBeenCalledWith(block.transactions[1]);

expect(state.setLastBlock).toBeCalledTimes(1);
expect(state.setLastBlock).toHaveBeenCalledWith(block);
})

it("should reset state.forkedBlock if incoming block has same height", async () => {
const acceptBlockHandler = container.resolve<AcceptBlockHandler>(AcceptBlockHandler);

state.forkedBlock = { data: { height: block.data.height } };
const result = await acceptBlockHandler.execute(block as Interfaces.IBlock);

expect(result).toBe(BlockProcessorResult.Accepted);

expect(state.forkedBlock).toBeUndefined();
})

it("should set state.lastDownloadedBlock if incoming block height is higher", async () => {
const acceptBlockHandler = container.resolve<AcceptBlockHandler>(AcceptBlockHandler);

state.lastDownloadedBlock = { height: block.data.height - 1 };
const result = await acceptBlockHandler.execute(block as Interfaces.IBlock);

expect(result).toBe(BlockProcessorResult.Accepted);

expect(state.lastDownloadedBlock).toBe(block.data);
})

it("should return Reject and resetLastDownloadedBlock when something throws", async () => {
const acceptBlockHandler = container.resolve<AcceptBlockHandler>(AcceptBlockHandler);

database.applyBlock = jest.fn().mockRejectedValueOnce(new Error("oops"));
const result = await acceptBlockHandler.execute(block as Interfaces.IBlock);

expect(result).toBe(BlockProcessorResult.Rejected);

expect(blockchain.resetLastDownloadedBlock).toBeCalledTimes(1);
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Container } from "@arkecosystem/core-kernel";
import { AlreadyForgedHandler } from "../../../../../packages/core-blockchain/src/processor/handlers/already-forged-handler";
import { BlockProcessorResult } from "../../../../../packages/core-blockchain/src/processor";
import { Interfaces } from "@arkecosystem/crypto";

describe("AlreadyForgedHandler", () => {
const container = new Container.Container();

const blockchain = { resetLastDownloadedBlock: jest.fn() };

const application = { get: jest.fn() };

beforeAll(() => {
container.unbindAll();
container.bind(Container.Identifiers.Application).toConstantValue(application);
container.bind(Container.Identifiers.BlockchainService).toConstantValue(blockchain);
});

beforeEach(() => {
jest.resetAllMocks();
});

describe("execute", () => {
it("should call blockchain.resetLastDownloadedBlock and return DiscardedButCanBeBroadcasted", async () => {
const alreadyForgedHandler = container.resolve<AlreadyForgedHandler>(AlreadyForgedHandler);

const block = {};
const result = await alreadyForgedHandler.execute(block as Interfaces.IBlock);

expect(result).toBe(BlockProcessorResult.DiscardedButCanBeBroadcasted);
expect(blockchain.resetLastDownloadedBlock).toBeCalledTimes(1);
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Container } from "@arkecosystem/core-kernel";
import { ExceptionHandler } from "../../../../../packages/core-blockchain/src/processor/handlers/exception-handler";
import { AcceptBlockHandler } from "../../../../../packages/core-blockchain/src/processor/handlers/accept-block-handler";
import { BlockProcessorResult } from "../../../../../packages/core-blockchain/src/processor";
import { Interfaces } from "@arkecosystem/crypto";

describe("ExceptionHandler", () => {
const container = new Container.Container();

const logger = { warning: jest.fn(), debug: jest.fn(), info: jest.fn() };
const blockchain = { resetLastDownloadedBlock: jest.fn() };
const database = { getBlock: jest.fn() };

const application = { resolve: jest.fn() };

beforeAll(() => {
container.unbindAll();
container.bind(Container.Identifiers.Application).toConstantValue(application);
container.bind(Container.Identifiers.BlockchainService).toConstantValue(blockchain);
container.bind(Container.Identifiers.LogService).toConstantValue(logger);
container.bind(Container.Identifiers.DatabaseService).toConstantValue(database);
});

beforeEach(() => {
jest.resetAllMocks();
});

describe("execute", () => {
const block = { data: { id: "123", height: 4445 } };

it("should return Rejected and resetLastDownloadedBlock when block already forged", async () => {
const exceptionHandler = container.resolve<ExceptionHandler>(ExceptionHandler);

database.getBlock = jest.fn().mockResolvedValueOnce(block);
const result = await exceptionHandler.execute(block as Interfaces.IBlock);

expect(result).toBe(BlockProcessorResult.Rejected);

expect(blockchain.resetLastDownloadedBlock).toBeCalledTimes(1);
})

it("should call AcceptHandler when block not already forged", async () => {
const exceptionHandler = container.resolve<ExceptionHandler>(ExceptionHandler);

const mockAcceptHandler = {
execute: () => BlockProcessorResult.Accepted
};
application.resolve = jest.fn().mockReturnValue(mockAcceptHandler);

const result = await exceptionHandler.execute(block as Interfaces.IBlock);

expect(result).toBe(BlockProcessorResult.Accepted);

expect(application.resolve).toBeCalledTimes(1);
expect(application.resolve).toBeCalledWith(AcceptBlockHandler);
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Container } from "@arkecosystem/core-kernel";
import { IncompatibleTransactionsHandler } from "../../../../../packages/core-blockchain/src/processor/handlers/incompatible-transactions-handler";
import { BlockProcessorResult } from "../../../../../packages/core-blockchain/src/processor";
import { Interfaces } from "@arkecosystem/crypto";

describe("IncompatibleTransactionsHandler", () => {
const container = new Container.Container();

const blockchain = { resetLastDownloadedBlock: jest.fn() };

const application = { get: jest.fn() };

beforeAll(() => {
container.unbindAll();
container.bind(Container.Identifiers.Application).toConstantValue(application);
container.bind(Container.Identifiers.BlockchainService).toConstantValue(blockchain);
});

beforeEach(() => {
jest.resetAllMocks();
});

describe("execute", () => {
it("should call blockchain.resetLastDownloadedBlock and return DiscardedButCanBeBroadcasted", async () => {
const incompatibleTransactionsHandler = container.resolve<IncompatibleTransactionsHandler>(IncompatibleTransactionsHandler);

const block = {};
const result = await incompatibleTransactionsHandler.execute(block as Interfaces.IBlock);

expect(result).toBe(BlockProcessorResult.Rejected);
expect(blockchain.resetLastDownloadedBlock).toBeCalledTimes(1);
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Container } from "@arkecosystem/core-kernel";
import { InvalidGeneratorHandler } from "../../../../../packages/core-blockchain/src/processor/handlers/invalid-generator-handler";
import { BlockProcessorResult } from "../../../../../packages/core-blockchain/src/processor";
import { Interfaces } from "@arkecosystem/crypto";

describe("InvalidGeneratorHandler", () => {
const container = new Container.Container();

const blockchain = { resetLastDownloadedBlock: jest.fn() };

const application = { get: jest.fn() };

beforeAll(() => {
container.unbindAll();
container.bind(Container.Identifiers.Application).toConstantValue(application);
container.bind(Container.Identifiers.BlockchainService).toConstantValue(blockchain);
});

beforeEach(() => {
jest.resetAllMocks();
});

describe("execute", () => {
it("should call blockchain.resetLastDownloadedBlock and return DiscardedButCanBeBroadcasted", async () => {
const invalidGeneratorHandler = container.resolve<InvalidGeneratorHandler>(InvalidGeneratorHandler);

const block = {};
const result = await invalidGeneratorHandler.execute(block as Interfaces.IBlock);

expect(result).toBe(BlockProcessorResult.Rejected);
expect(blockchain.resetLastDownloadedBlock).toBeCalledTimes(1);
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Container } from "@arkecosystem/core-kernel";
import { NonceOutOfOrderHandler } from "../../../../../packages/core-blockchain/src/processor/handlers/nonce-out-of-order-handler";
import { BlockProcessorResult } from "../../../../../packages/core-blockchain/src/processor";
import { Interfaces } from "@arkecosystem/crypto";

describe("NonceOutOfOrderHandler", () => {
const container = new Container.Container();

const blockchain = { resetLastDownloadedBlock: jest.fn() };

const application = { get: jest.fn() };

beforeAll(() => {
container.unbindAll();
container.bind(Container.Identifiers.Application).toConstantValue(application);
container.bind(Container.Identifiers.BlockchainService).toConstantValue(blockchain);
});

beforeEach(() => {
jest.resetAllMocks();
});

describe("execute", () => {
it("should call blockchain.resetLastDownloadedBlock and return DiscardedButCanBeBroadcasted", async () => {
const nonceOutOfOrderHandler = container.resolve<NonceOutOfOrderHandler>(NonceOutOfOrderHandler);

const block = {};
const result = await nonceOutOfOrderHandler.execute(block as Interfaces.IBlock);

expect(result).toBe(BlockProcessorResult.Rejected);
expect(blockchain.resetLastDownloadedBlock).toBeCalledTimes(1);
})
})
})
Loading

0 comments on commit 6c8c436

Please sign in to comment.