Skip to content

Commit

Permalink
test: flaky unit tests (#6398)
Browse files Browse the repository at this point in the history
* Fix clock flaky tests

* Fix api server flaky tests

* Auto assign port for testing server

* Update fake timer
  • Loading branch information
nazarhussain authored Feb 6, 2024
1 parent 274871d commit 2a8342d
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ import {testData} from "../testData/config.js";
/* eslint-disable @typescript-eslint/naming-convention */

describe("beacon / config", () => {
describe("Run generic server test", () => {
runGenericServerTest<Api, ReqTypes>(config, getClient, getRoutes, testData);
});
runGenericServerTest<Api, ReqTypes>(config, getClient, getRoutes, testData);

it("Serialize Partial Spec object", () => {
const returnTypes = getReturnTypes();
Expand Down
28 changes: 19 additions & 9 deletions packages/api/test/unit/beacon/genericServerTest/debug.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {describe, it, expect, MockInstance} from "vitest";
import {describe, it, expect, MockInstance, beforeAll, afterAll} from "vitest";
import {toHexString} from "@chainsafe/ssz";
import {FastifyInstance} from "fastify";
import {ssz} from "@lodestar/types";
import {config} from "@lodestar/config/default";
import {Api, ReqTypes, routesData} from "../../../../src/beacon/routes/debug.js";
Expand All @@ -13,19 +14,28 @@ import {testData} from "../testData/debug.js";

describe(
"beacon / debug",
function () {
describe("Run generic server test", () => {
runGenericServerTest<Api, ReqTypes>(config, getClient, getRoutes, testData);
});
() => {
runGenericServerTest<Api, ReqTypes>(config, getClient, getRoutes, testData);

// Get state by SSZ

describe("getState() in SSZ format", () => {
const {baseUrl, server} = getTestServer();
const mockApi = getMockApi<Api>(routesData);
for (const route of Object.values(getRoutes(config, mockApi))) {
registerRoute(server, route);
}
let baseUrl: string;
let server: FastifyInstance;

beforeAll(async () => {
const res = getTestServer();
server = res.server;
for (const route of Object.values(getRoutes(config, mockApi))) {
registerRoute(server, route);
}
baseUrl = await res.start();
});

afterAll(async () => {
if (server !== undefined) await server.close();
});

for (const method of ["getState" as const, "getStateV2" as const]) {
it(method, async () => {
Expand Down
23 changes: 18 additions & 5 deletions packages/api/test/unit/beacon/genericServerTest/events.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {describe, it, expect, beforeEach, afterEach} from "vitest";
import {describe, it, expect, beforeEach, afterEach, beforeAll, afterAll} from "vitest";
import {FastifyInstance} from "fastify";
import {sleep} from "@lodestar/utils";
import {Api, routesData, EventType, BeaconEvent} from "../../../../src/beacon/routes/events.js";
import {getClient} from "../../../../src/beacon/client/events.js";
Expand All @@ -8,11 +9,23 @@ import {getMockApi, getTestServer} from "../../../utils/utils.js";
import {eventTestData} from "../testData/events.js";

describe("beacon / events", () => {
const {baseUrl, server} = getTestServer();
const mockApi = getMockApi<Api>(routesData);
for (const route of Object.values(getRoutes(mockApi))) {
registerRoute(server, route);
}
let server: FastifyInstance;
let baseUrl: string;

beforeAll(async () => {
const res = getTestServer();
server = res.server;
for (const route of Object.values(getRoutes(mockApi))) {
registerRoute(server, route);
}

baseUrl = await res.start();
});

afterAll(async () => {
if (server !== undefined) await server.close();
});

let controller: AbortController;
beforeEach(() => {
Expand Down
40 changes: 27 additions & 13 deletions packages/api/test/utils/genericServerTest.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {it, expect, MockInstance} from "vitest";
import {it, expect, MockInstance, describe, beforeAll, afterAll} from "vitest";
import {FastifyInstance} from "fastify";
import {ChainForkConfig} from "@lodestar/config";
import {ReqGeneric, Resolves} from "../../src/utils/index.js";
import {FetchOpts, HttpClient, IHttpClient} from "../../src/utils/client/index.js";
Expand Down Expand Up @@ -28,26 +29,39 @@ export function runGenericServerTest<
testCases: GenericServerTestCases<Api>
): void {
const mockApi = getMockApi<Api>(testCases);
const {baseUrl, server} = getTestServer();
let server: FastifyInstance;

const httpClient = new HttpClientSpy({baseUrl});
const client = getClient(config, httpClient);
let client: Api;
let httpClient: HttpClientSpy;

for (const route of Object.values(getRoutes(config, mockApi))) {
registerRoute(server, route);
}
beforeAll(async () => {
const res = getTestServer();
server = res.server;

for (const route of Object.values(getRoutes(config, mockApi))) {
registerRoute(server, route);
}

const baseUrl = await res.start();
httpClient = new HttpClientSpy({baseUrl});
client = getClient(config, httpClient);
});

for (const key of Object.keys(testCases)) {
const routeId = key as keyof Api;
const testCase = testCases[routeId];
afterAll(async () => {
if (server !== undefined) await server.close();
});

describe("run generic server tests", () => {
it.each(Object.keys(testCases))("%s", async (key) => {
const routeId = key as keyof Api;
const testCase = testCases[routeId];

it(routeId as string, async () => {
// Register mock data for this route
// TODO: Look for the type error
(mockApi[routeId] as MockInstance).mockResolvedValue(testCases[routeId].res);

// Do the call
const res = await (client[routeId] as APIClientHandler)(...(testCase.args as any[]));
const res = await client[routeId](...(testCase.args as any[]));

// Use spy to assert argument serialization
if (testCase.query) {
Expand All @@ -64,7 +78,7 @@ export function runGenericServerTest<
// Assert returned value is correct
expect(res.response).toEqual(testCase.res);
});
}
});
}

class HttpClientSpy extends HttpClient {
Expand Down
20 changes: 6 additions & 14 deletions packages/api/test/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import {beforeAll, afterAll, MockedObject, vi} from "vitest";
import {MockedObject, vi} from "vitest";
import qs from "qs";
import fastify, {FastifyInstance} from "fastify";
import {mapValues} from "@lodestar/utils";
import {ServerApi} from "../../src/interfaces.js";

export function getTestServer(): {baseUrl: string; server: FastifyInstance} {
const port = Math.floor(Math.random() * (65535 - 49152)) + 49152;
const baseUrl = `http://localhost:${port}`;

export function getTestServer(): {server: FastifyInstance; start: () => Promise<string>} {
const server = fastify({
ajv: {customOptions: {coerceTypes: "array"}},
querystringParser: (str) => qs.parse(str, {comma: true, parseArrays: false}),
Expand All @@ -19,23 +16,18 @@ export function getTestServer(): {baseUrl: string; server: FastifyInstance} {
done();
});

beforeAll(async () => {
await new Promise((resolve, reject) => {
server.listen({port}, function (err, address) {
const start = (): Promise<string> =>
new Promise<string>((resolve, reject) => {
server.listen({port: 0}, function (err, address) {
if (err !== null && err != undefined) {
reject(err);
} else {
resolve(address);
}
});
});
});

afterAll(async () => {
await server.close();
});

return {baseUrl, server};
return {start, server};
}

export function getMockApi<Api extends Record<string, any>>(
Expand Down
2 changes: 1 addition & 1 deletion packages/validator/src/util/clock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export class Clock implements IClock {
*/
export function getCurrentSlotAround(config: ChainForkConfig, genesisTime: TimeSeconds): Slot {
const diffInSeconds = Date.now() / 1000 - genesisTime;
const slotsSinceGenesis = Math.round(diffInSeconds / config.SECONDS_PER_SLOT);
const slotsSinceGenesis = Math.floor(diffInSeconds / config.SECONDS_PER_SLOT);
return GENESIS_SLOT + slotsSinceGenesis;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/validator/test/unit/utils/clock.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe("util / Clock", function () {

beforeEach(() => {
controller = new AbortController();
vi.useFakeTimers();
vi.useFakeTimers({now: Date.now()});
});

afterEach(() => {
Expand Down

0 comments on commit 2a8342d

Please sign in to comment.