Skip to content

Commit

Permalink
Add transition unit tests cases
Browse files Browse the repository at this point in the history
  • Loading branch information
nazarhussain committed Jul 31, 2023
1 parent ade32b2 commit 59ec5fe
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 4 deletions.
8 changes: 4 additions & 4 deletions packages/beacon-node/src/execution/engine/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export class ExecutionEngineMockJsonRpcClient implements IJsonRpcHttpClient {
}
}

const fatalErrorCodes = ["ECONNREFUSED", "ENOTFOUND", "EAI_AGAIN"];
const connectionErrorCodes = ["ECONNRESET", "ECONNABORTED"];
export const HTTP_FATAL_ERROR_CODES = ["ECONNREFUSED", "ENOTFOUND", "EAI_AGAIN"];
export const HTTP_CONNECTION_ERROR_CODES = ["ECONNRESET", "ECONNABORTED"];

function getExecutionEngineStateForPayloadStatus(payloadStatus: ExecutePayloadStatus): ExecutionEngineState {
switch (payloadStatus) {
Expand Down Expand Up @@ -75,11 +75,11 @@ function getExecutionEngineStateForPayloadError(
return ExecutionEngineState.SYNCING;
}

if (payloadError && isFetchError(payloadError) && fatalErrorCodes.includes(payloadError.code)) {
if (payloadError && isFetchError(payloadError) && HTTP_FATAL_ERROR_CODES.includes(payloadError.code)) {
return ExecutionEngineState.OFFLINE;
}

if (payloadError && isFetchError(payloadError) && connectionErrorCodes.includes(payloadError.code)) {
if (payloadError && isFetchError(payloadError) && HTTP_CONNECTION_ERROR_CODES.includes(payloadError.code)) {
return ExecutionEngineState.AUTH_FAILED;
}

Expand Down
191 changes: 191 additions & 0 deletions packages/beacon-node/test/unit/execution/engine/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import {expect} from "chai";
import {ErrorAborted} from "@lodestar/utils";
import {ExecutePayloadStatus, ExecutionEngineState} from "../../../../src/execution/index.js";
import {
HTTP_CONNECTION_ERROR_CODES,
HTTP_FATAL_ERROR_CODES,
getExecutionEngineState,
} from "../../../../src/execution/engine/utils.js";
import {QueueError, QueueErrorCode} from "../../../../src/util/queue/errors.js";
import {ErrorJsonRpcResponse, HttpRpcError} from "../../../../src/eth1/provider/jsonRpcHttpClient.js";

describe("execution/engine/utils", () => {
describe("getExecutionEngineState", () => {
const testCasesPayload: Record<
ExecutePayloadStatus,
[oldState: ExecutionEngineState, newState: ExecutionEngineState][]
> = {
[ExecutePayloadStatus.ACCEPTED]: [
[ExecutionEngineState.ONLINE, ExecutionEngineState.SYNCED],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.SYNCED],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.SYNCED],
[ExecutionEngineState.SYNCED, ExecutionEngineState.SYNCED],
[ExecutionEngineState.SYNCING, ExecutionEngineState.SYNCED],
],
[ExecutePayloadStatus.VALID]: [
[ExecutionEngineState.ONLINE, ExecutionEngineState.SYNCED],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.SYNCED],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.SYNCED],
[ExecutionEngineState.SYNCED, ExecutionEngineState.SYNCED],
[ExecutionEngineState.SYNCING, ExecutionEngineState.SYNCED],
],
[ExecutePayloadStatus.UNSAFE_OPTIMISTIC_STATUS]: [
[ExecutionEngineState.ONLINE, ExecutionEngineState.SYNCED],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.SYNCED],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.SYNCED],
[ExecutionEngineState.SYNCED, ExecutionEngineState.SYNCED],
[ExecutionEngineState.SYNCING, ExecutionEngineState.SYNCED],
],
[ExecutePayloadStatus.ELERROR]: [
[ExecutionEngineState.ONLINE, ExecutionEngineState.SYNCING],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.SYNCING],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.SYNCING],
[ExecutionEngineState.SYNCED, ExecutionEngineState.SYNCING],
[ExecutionEngineState.SYNCING, ExecutionEngineState.SYNCING],
],
[ExecutePayloadStatus.INVALID]: [
[ExecutionEngineState.ONLINE, ExecutionEngineState.SYNCING],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.SYNCING],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.SYNCING],
[ExecutionEngineState.SYNCED, ExecutionEngineState.SYNCING],
[ExecutionEngineState.SYNCING, ExecutionEngineState.SYNCING],
],
[ExecutePayloadStatus.SYNCING]: [
[ExecutionEngineState.ONLINE, ExecutionEngineState.SYNCING],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.SYNCING],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.SYNCING],
[ExecutionEngineState.SYNCED, ExecutionEngineState.SYNCING],
[ExecutionEngineState.SYNCING, ExecutionEngineState.SYNCING],
],
[ExecutePayloadStatus.INVALID_BLOCK_HASH]: [
[ExecutionEngineState.ONLINE, ExecutionEngineState.SYNCING],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.SYNCING],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.SYNCING],
[ExecutionEngineState.SYNCED, ExecutionEngineState.SYNCING],
[ExecutionEngineState.SYNCING, ExecutionEngineState.SYNCING],
],
[ExecutePayloadStatus.UNAVAILABLE]: [
[ExecutionEngineState.ONLINE, ExecutionEngineState.OFFLINE],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.OFFLINE],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.OFFLINE],
[ExecutionEngineState.SYNCED, ExecutionEngineState.OFFLINE],
[ExecutionEngineState.SYNCING, ExecutionEngineState.OFFLINE],
],
["unknown" as ExecutePayloadStatus]: [
[ExecutionEngineState.ONLINE, ExecutionEngineState.ONLINE],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.ONLINE],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.ONLINE],
[ExecutionEngineState.SYNCED, ExecutionEngineState.SYNCED],
[ExecutionEngineState.SYNCING, ExecutionEngineState.SYNCING],
],
};

type ErrorTestCase = [
string,
Error,
[oldState: ExecutionEngineState, newState: ExecutionEngineState | undefined][],
];
const testCasesError: ErrorTestCase[] = [
[
"abort error",
new ErrorAborted(),
[
[ExecutionEngineState.ONLINE, ExecutionEngineState.ONLINE],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.AUTH_FAILED],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.OFFLINE],
[ExecutionEngineState.SYNCED, ExecutionEngineState.SYNCED],
[ExecutionEngineState.SYNCING, ExecutionEngineState.SYNCING],
],
],
[
"queue aborted error",
new QueueError({code: QueueErrorCode.QUEUE_ABORTED}),
[
[ExecutionEngineState.ONLINE, ExecutionEngineState.ONLINE],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.AUTH_FAILED],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.OFFLINE],
[ExecutionEngineState.SYNCED, ExecutionEngineState.SYNCED],
[ExecutionEngineState.SYNCING, ExecutionEngineState.SYNCING],
],
],
[
"rpc error",
new HttpRpcError(12, "error"),
[
[ExecutionEngineState.ONLINE, ExecutionEngineState.SYNCING],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.SYNCING],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.SYNCING],
[ExecutionEngineState.SYNCED, ExecutionEngineState.SYNCING],
[ExecutionEngineState.SYNCING, ExecutionEngineState.SYNCING],
],
],
[
"rpc response error",
new ErrorJsonRpcResponse({jsonrpc: "2.0", id: 123, error: {code: 123, message: "error"}}, "error"),
[
[ExecutionEngineState.ONLINE, ExecutionEngineState.SYNCING],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.SYNCING],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.SYNCING],
[ExecutionEngineState.SYNCED, ExecutionEngineState.SYNCING],
[ExecutionEngineState.SYNCING, ExecutionEngineState.SYNCING],
],
],
...HTTP_FATAL_ERROR_CODES.map(
(code) =>
[
`http error with code '${code}'`,
{code: code, errno: "error"} as unknown as Error,
[
[ExecutionEngineState.ONLINE, ExecutionEngineState.OFFLINE],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.OFFLINE],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.OFFLINE],
[ExecutionEngineState.SYNCED, ExecutionEngineState.OFFLINE],
[ExecutionEngineState.SYNCING, ExecutionEngineState.OFFLINE],
],
] as ErrorTestCase
),
...HTTP_CONNECTION_ERROR_CODES.map(
(code) =>
[
`http error with code '${code}'`,
{code: code, errno: "error"} as unknown as Error,
[
[ExecutionEngineState.ONLINE, ExecutionEngineState.AUTH_FAILED],
[ExecutionEngineState.AUTH_FAILED, ExecutionEngineState.AUTH_FAILED],
[ExecutionEngineState.OFFLINE, ExecutionEngineState.AUTH_FAILED],
[ExecutionEngineState.SYNCED, ExecutionEngineState.AUTH_FAILED],
[ExecutionEngineState.SYNCING, ExecutionEngineState.AUTH_FAILED],
],
] as ErrorTestCase
),
[
"unknown error",
new Error("unknown error"),
[
[ExecutionEngineState.ONLINE, undefined],
[ExecutionEngineState.AUTH_FAILED, undefined],
[ExecutionEngineState.OFFLINE, undefined],
[ExecutionEngineState.SYNCED, undefined],
[ExecutionEngineState.SYNCING, undefined],
],
],
];

for (const payloadStatus of Object.keys(testCasesPayload) as ExecutePayloadStatus[]) {
for (const [oldState, newState] of testCasesPayload[payloadStatus]) {
it(`should transition from "${oldState}" to "${newState}" on payload status "${payloadStatus}"`, () => {
expect(getExecutionEngineState({payloadStatus, oldState})).to.equal(newState);
});
}
}

for (const testCase of testCasesError) {
const [message, payloadError, errorCases] = testCase;
for (const [oldState, newState] of errorCases) {
it(`should transition from "${oldState}" to "${newState}" on error "${message}"`, () => {
expect(getExecutionEngineState({payloadError, oldState})).to.equal(newState);
});
}
}
});
});

0 comments on commit 59ec5fe

Please sign in to comment.