Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: introduce beacon api test ignore list #6171

Merged
merged 27 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
91cd2af
Fix operationId of light_client routes
jeluard Dec 8, 2023
ad53c2d
Fix operationId of bls_to_execution_changes routes
jeluard Dec 8, 2023
3608df8
Update beacon api spec version to 2.4.0
jeluard Dec 8, 2023
429a783
Push case change
jeluard Dec 8, 2023
d23ffe2
Remove now useless file
jeluard Dec 8, 2023
32f9388
fix: lints
jeluard Dec 8, 2023
bb424b4
fix: filter broken test
jeluard Dec 8, 2023
9f3b06a
Revert "Fix operationId of light_client routes"
jeluard Dec 11, 2023
0dfdcab
Revert "Fix operationId of bls_to_execution_changes routes"
jeluard Dec 11, 2023
87f354f
test: ignore missing routes
jeluard Dec 11, 2023
7352a56
test: allow to filter required properties from testing
jeluard Dec 12, 2023
12c7ef4
fix: incorrect case
jeluard Dec 12, 2023
9cc40ca
test: fixed incorrect test filtering
jeluard Dec 12, 2023
0c48269
fix: lints
jeluard Dec 12, 2023
7f3bcde
fix: cleanup
jeluard Dec 12, 2023
e471e68
test: allow more fine grain API tests filtering
jeluard Dec 13, 2023
6c2a15f
fix: lints
jeluard Dec 13, 2023
20267c1
test: increase JSON schema validation strictness
jeluard Dec 13, 2023
b85a4c6
fix: restore removed keyword implementation
jeluard Dec 13, 2023
5d3ce8c
test: improve filtering semantic
jeluard Dec 13, 2023
73eec2b
test: add support for JSONPath syntax to filtering
jeluard Dec 14, 2023
ff673d8
fix: typo
jeluard Dec 14, 2023
4fa3ff9
fix: wording
jeluard Dec 14, 2023
49e757c
test: improve semantic
jeluard Dec 14, 2023
a3e0eb6
test: added issue for context
jeluard Dec 14, 2023
7787236
fix: improved issues references
jeluard Dec 19, 2023
25e8aa5
fix: incorrect dotted property parsing
jeluard Dec 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions packages/api/src/beacon/routes/beacon/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export type Api = {
* @returns any Successful response
* @throws ApiError
*/
getPoolBlsToExecutionChanges(): Promise<
getPoolBLSToExecutionChanges(): Promise<
jeluard marked this conversation as resolved.
Show resolved Hide resolved
ApiClientResponse<{[HttpStatusCode.OK]: {data: capella.SignedBLSToExecutionChange[]}}>
>;

Expand Down Expand Up @@ -123,7 +123,7 @@ export type Api = {
* @returns any BLSToExecutionChange is stored in node and broadcasted to network
* @throws ApiError
*/
submitPoolBlsToExecutionChange(
submitPoolBLSToExecutionChange(
blsToExecutionChange: capella.SignedBLSToExecutionChange[]
): Promise<ApiClientResponse<{[HttpStatusCode.OK]: void}, HttpStatusCode.BAD_REQUEST>>;

Expand All @@ -143,12 +143,12 @@ export const routesData: RoutesData<Api> = {
getPoolAttesterSlashings: {url: "/eth/v1/beacon/pool/attester_slashings", method: "GET"},
getPoolProposerSlashings: {url: "/eth/v1/beacon/pool/proposer_slashings", method: "GET"},
getPoolVoluntaryExits: {url: "/eth/v1/beacon/pool/voluntary_exits", method: "GET"},
getPoolBlsToExecutionChanges: {url: "/eth/v1/beacon/pool/bls_to_execution_changes", method: "GET"},
getPoolBLSToExecutionChanges: {url: "/eth/v1/beacon/pool/bls_to_execution_changes", method: "GET"},
submitPoolAttestations: {url: "/eth/v1/beacon/pool/attestations", method: "POST"},
submitPoolAttesterSlashings: {url: "/eth/v1/beacon/pool/attester_slashings", method: "POST"},
submitPoolProposerSlashings: {url: "/eth/v1/beacon/pool/proposer_slashings", method: "POST"},
submitPoolVoluntaryExit: {url: "/eth/v1/beacon/pool/voluntary_exits", method: "POST"},
submitPoolBlsToExecutionChange: {url: "/eth/v1/beacon/pool/bls_to_execution_changes", method: "POST"},
submitPoolBLSToExecutionChange: {url: "/eth/v1/beacon/pool/bls_to_execution_changes", method: "POST"},
submitPoolSyncCommitteeSignatures: {url: "/eth/v1/beacon/pool/sync_committees", method: "POST"},
};

Expand All @@ -158,12 +158,12 @@ export type ReqTypes = {
getPoolAttesterSlashings: ReqEmpty;
getPoolProposerSlashings: ReqEmpty;
getPoolVoluntaryExits: ReqEmpty;
getPoolBlsToExecutionChanges: ReqEmpty;
getPoolBLSToExecutionChanges: ReqEmpty;
submitPoolAttestations: {body: unknown};
submitPoolAttesterSlashings: {body: unknown};
submitPoolProposerSlashings: {body: unknown};
submitPoolVoluntaryExit: {body: unknown};
submitPoolBlsToExecutionChange: {body: unknown};
submitPoolBLSToExecutionChange: {body: unknown};
submitPoolSyncCommitteeSignatures: {body: unknown};
};

Expand All @@ -177,12 +177,12 @@ export function getReqSerializers(): ReqSerializers<Api, ReqTypes> {
getPoolAttesterSlashings: reqEmpty,
getPoolProposerSlashings: reqEmpty,
getPoolVoluntaryExits: reqEmpty,
getPoolBlsToExecutionChanges: reqEmpty,
getPoolBLSToExecutionChanges: reqEmpty,
submitPoolAttestations: reqOnlyBody(ArrayOf(ssz.phase0.Attestation), Schema.ObjectArray),
submitPoolAttesterSlashings: reqOnlyBody(ssz.phase0.AttesterSlashing, Schema.Object),
submitPoolProposerSlashings: reqOnlyBody(ssz.phase0.ProposerSlashing, Schema.Object),
submitPoolVoluntaryExit: reqOnlyBody(ssz.phase0.SignedVoluntaryExit, Schema.Object),
submitPoolBlsToExecutionChange: reqOnlyBody(ArrayOf(ssz.capella.SignedBLSToExecutionChange), Schema.ObjectArray),
submitPoolBLSToExecutionChange: reqOnlyBody(ArrayOf(ssz.capella.SignedBLSToExecutionChange), Schema.ObjectArray),
submitPoolSyncCommitteeSignatures: reqOnlyBody(ArrayOf(ssz.altair.SyncCommitteeMessage), Schema.ObjectArray),
};
}
Expand All @@ -193,6 +193,6 @@ export function getReturnTypes(): ReturnTypes<Api> {
getPoolAttesterSlashings: ContainerData(ArrayOf(ssz.phase0.AttesterSlashing)),
getPoolProposerSlashings: ContainerData(ArrayOf(ssz.phase0.ProposerSlashing)),
getPoolVoluntaryExits: ContainerData(ArrayOf(ssz.phase0.SignedVoluntaryExit)),
getPoolBlsToExecutionChanges: ContainerData(ArrayOf(ssz.capella.SignedBLSToExecutionChange)),
getPoolBLSToExecutionChanges: ContainerData(ArrayOf(ssz.capella.SignedBLSToExecutionChange)),
};
}
52 changes: 26 additions & 26 deletions packages/api/src/beacon/routes/lightclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export type Api = {
* - Has most bits
* - Oldest update
*/
getUpdates(
getLightClientUpdatesByRange(
startPeriod: SyncPeriod,
count: number
): Promise<
Expand All @@ -39,15 +39,15 @@ export type Api = {
* Returns the latest optimistic head update available. Clients should use the SSE type `light_client_optimistic_update`
* unless to get the very first head update after syncing, or if SSE are not supported by the server.
*/
getOptimisticUpdate(): Promise<
getLightClientOptimisticUpdate(): Promise<
ApiClientResponse<{
[HttpStatusCode.OK]: {
version: ForkName;
data: allForks.LightClientOptimisticUpdate;
};
}>
>;
getFinalityUpdate(): Promise<
getLightClientFinalityUpdate(): Promise<
ApiClientResponse<{
[HttpStatusCode.OK]: {
version: ForkName;
Expand All @@ -60,7 +60,7 @@ export type Api = {
* The trusted block root should be fetched with similar means to a weak subjectivity checkpoint.
* Only block roots for checkpoints are guaranteed to be available.
*/
getBootstrap(blockRoot: string): Promise<
getLightClientBootstrap(blockRoot: string): Promise<
ApiClientResponse<{
[HttpStatusCode.OK]: {
version: ForkName;
Expand All @@ -71,7 +71,7 @@ export type Api = {
/**
* Returns an array of sync committee hashes based on the provided period and count
*/
getCommitteeRoot(
getLightClientCommitteeRoot(
startPeriod: SyncPeriod,
count: number
): Promise<
Expand All @@ -87,39 +87,39 @@ export type Api = {
* Define javascript values for each route
*/
export const routesData: RoutesData<Api> = {
getUpdates: {url: "/eth/v1/beacon/light_client/updates", method: "GET"},
getOptimisticUpdate: {url: "/eth/v1/beacon/light_client/optimistic_update", method: "GET"},
getFinalityUpdate: {url: "/eth/v1/beacon/light_client/finality_update", method: "GET"},
getBootstrap: {url: "/eth/v1/beacon/light_client/bootstrap/{block_root}", method: "GET"},
getCommitteeRoot: {url: "/eth/v0/beacon/light_client/committee_root", method: "GET"},
getLightClientUpdatesByRange: {url: "/eth/v1/beacon/light_client/updates", method: "GET"},
getLightClientOptimisticUpdate: {url: "/eth/v1/beacon/light_client/optimistic_update", method: "GET"},
getLightClientFinalityUpdate: {url: "/eth/v1/beacon/light_client/finality_update", method: "GET"},
getLightClientBootstrap: {url: "/eth/v1/beacon/light_client/bootstrap/{block_root}", method: "GET"},
getLightClientCommitteeRoot: {url: "/eth/v0/beacon/light_client/committee_root", method: "GET"},
};

/* eslint-disable @typescript-eslint/naming-convention */
export type ReqTypes = {
getUpdates: {query: {start_period: number; count: number}};
getOptimisticUpdate: ReqEmpty;
getFinalityUpdate: ReqEmpty;
getBootstrap: {params: {block_root: string}};
getCommitteeRoot: {query: {start_period: number; count: number}};
getLightClientUpdatesByRange: {query: {start_period: number; count: number}};
getLightClientOptimisticUpdate: ReqEmpty;
getLightClientFinalityUpdate: ReqEmpty;
getLightClientBootstrap: {params: {block_root: string}};
getLightClientCommitteeRoot: {query: {start_period: number; count: number}};
};

export function getReqSerializers(): ReqSerializers<Api, ReqTypes> {
return {
getUpdates: {
getLightClientUpdatesByRange: {
writeReq: (start_period, count) => ({query: {start_period, count}}),
parseReq: ({query}) => [query.start_period, query.count],
schema: {query: {start_period: Schema.UintRequired, count: Schema.UintRequired}},
},

getOptimisticUpdate: reqEmpty,
getFinalityUpdate: reqEmpty,
getLightClientOptimisticUpdate: reqEmpty,
getLightClientFinalityUpdate: reqEmpty,

getBootstrap: {
getLightClientBootstrap: {
writeReq: (block_root) => ({params: {block_root}}),
parseReq: ({params}) => [params.block_root],
schema: {params: {block_root: Schema.StringRequired}},
},
getCommitteeRoot: {
getLightClientCommitteeRoot: {
writeReq: (start_period, count) => ({query: {start_period, count}}),
parseReq: ({query}) => [query.start_period, query.count],
schema: {query: {start_period: Schema.UintRequired, count: Schema.UintRequired}},
Expand All @@ -132,27 +132,27 @@ export function getReturnTypes(): ReturnTypes<Api> {
const VersionedUpdate = WithVersion((fork: ForkName) =>
isForkLightClient(fork) ? ssz.allForksLightClient[fork].LightClientUpdate : ssz.altair.LightClientUpdate
);
const getUpdates = {
const getLightClientUpdatesByRange = {
toJson: (updates: {version: ForkName; data: allForks.LightClientUpdate}[]) =>
updates.map((data) => VersionedUpdate.toJson(data)),
fromJson: (updates: unknown[]) => updates.map((data) => VersionedUpdate.fromJson(data)),
};

return {
getUpdates,
getOptimisticUpdate: WithVersion((fork: ForkName) =>
getLightClientUpdatesByRange,
getLightClientOptimisticUpdate: WithVersion((fork: ForkName) =>
isForkLightClient(fork)
? ssz.allForksLightClient[fork].LightClientOptimisticUpdate
: ssz.altair.LightClientOptimisticUpdate
),
getFinalityUpdate: WithVersion((fork: ForkName) =>
getLightClientFinalityUpdate: WithVersion((fork: ForkName) =>
isForkLightClient(fork)
? ssz.allForksLightClient[fork].LightClientFinalityUpdate
: ssz.altair.LightClientFinalityUpdate
),
getBootstrap: WithVersion((fork: ForkName) =>
getLightClientBootstrap: WithVersion((fork: ForkName) =>
isForkLightClient(fork) ? ssz.allForksLightClient[fork].LightClientBootstrap : ssz.altair.LightClientBootstrap
),
getCommitteeRoot: ContainerData(ArrayOf(ssz.Root)),
getLightClientCommitteeRoot: ContainerData(ArrayOf(ssz.Root)),
};
}
86 changes: 80 additions & 6 deletions packages/api/test/unit/beacon/oapiSpec.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {testData as validatorTestData} from "./testData/validator.js";
// eslint-disable-next-line @typescript-eslint/naming-convention
const __dirname = path.dirname(fileURLToPath(import.meta.url));

const version = "v2.3.0";
const version = "v2.4.0";
const openApiFile: OpenApiFile = {
url: `https://github.com/ethereum/beacon-APIs/releases/download/${version}/beacon-node-oapi.json`,
filepath: path.join(__dirname, "../../../oapi-schemas/beacon-node-oapi.json"),
Expand Down Expand Up @@ -84,11 +84,83 @@ const testDatas = {
...validatorTestData,
};

const ignoredOperationsIds = [
/*
#5693
missing finalized
*/
"getStateRoot",
jeluard marked this conversation as resolved.
Show resolved Hide resolved
"getStateFork",
"getStateFinalityCheckpoints",
"getStateValidators",
"getStateValidator",
"getStateValidatorBalances",
"getEpochCommittees",
"getEpochSyncCommittees",
"getStateRandao",
"getBlockHeaders",
"getBlockHeader",
"getBlockV2",
"getBlockRoot",
"getBlockAttestations",
"getStateV2",

/* missing route */
/* #5694 */
"getSyncCommitteeRewards",
"getBlockRewards",
"getAttestationsRewards",
"getDepositSnapshot", // Won't fix for now, see https://github.com/ChainSafe/lodestar/issues/5697
"getBlindedBlock", // #5699
"getNextWithdrawals", // #5696
"getDebugForkChoice", // #5700

/*
#6168
/query/syncing_status - must be integer
*/
"getHealth",
jeluard marked this conversation as resolved.
Show resolved Hide resolved

/*
#4638
jeluard marked this conversation as resolved.
Show resolved Hide resolved
/query - must have required property 'skip_randao_verification'
*/
"produceBlockV2",
"produceBlindedBlock",
];

const openApiJson = await fetchOpenApiSpec(openApiFile);
runTestCheckAgainstSpec(openApiJson, routesData, reqSerializers, returnTypes, testDatas, {
// TODO: Investigate why schema validation fails otherwise
routesDropOneOf: ["produceBlockV2", "produceBlindedBlock", "publishBlindedBlock"],
});
runTestCheckAgainstSpec(
openApiJson,
routesData,
reqSerializers,
returnTypes,
testDatas,
{
// TODO: Investigate why schema validation fails otherwise
jeluard marked this conversation as resolved.
Show resolved Hide resolved
routesDropOneOf: ["produceBlockV2", "produceBlindedBlock", "publishBlindedBlock"],
},
ignoredOperationsIds
);

const ignoredTopics = [
/*
#6167
eventTestData[bls_to_execution_change] does not match spec's example
*/
"bls_to_execution_change",
/*
#6170
Error: Invalid slot=0 fork=phase0 for lightclient fork types
*/
"light_client_finality_update",
"light_client_optimistic_update",
/*
https://github.com/ethereum/beacon-APIs/pull/379
SyntaxError: Unexpected non-whitespace character after JSON at position 629 (line 1 column 630)
*/
"payload_attributes",
];

// eventstream types are defined as comments in the description of "examples".
// The function runTestCheckAgainstSpec() can't handle those, so the custom code before:
Expand All @@ -113,7 +185,9 @@ describe("eventstream event data", () => {
const eventSerdes = routes.events.getEventSerdes(config);
const knownTopics = new Set<string>(Object.values(routes.events.eventTypes));

for (const [topic, {value}] of Object.entries(eventstreamExamples ?? {})) {
for (const [topic, {value}] of Object.entries(eventstreamExamples ?? {}).filter(
([topic]) => !ignoredTopics.includes(topic)
)) {
it(topic, () => {
if (!knownTopics.has(topic)) {
throw Error(`topic ${topic} not implemented`);
Expand Down
4 changes: 2 additions & 2 deletions packages/api/test/unit/beacon/testData/beacon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export const testData: GenericServerTestCases<Api> = {
args: [],
res: {data: [ssz.phase0.SignedVoluntaryExit.defaultValue()]},
},
getPoolBlsToExecutionChanges: {
getPoolBLSToExecutionChanges: {
args: [],
res: {data: [ssz.capella.SignedBLSToExecutionChange.defaultValue()]},
},
Expand All @@ -113,7 +113,7 @@ export const testData: GenericServerTestCases<Api> = {
args: [ssz.phase0.SignedVoluntaryExit.defaultValue()],
res: undefined,
},
submitPoolBlsToExecutionChange: {
submitPoolBLSToExecutionChange: {
args: [[ssz.capella.SignedBLSToExecutionChange.defaultValue()]],
res: undefined,
},
Expand Down
10 changes: 5 additions & 5 deletions packages/api/test/unit/beacon/testData/lightclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ const header = ssz.altair.LightClientHeader.defaultValue();
const signatureSlot = ssz.Slot.defaultValue();

export const testData: GenericServerTestCases<Api> = {
getUpdates: {
getLightClientUpdatesByRange: {
args: [1, 2],
res: [{version: ForkName.bellatrix, data: lightClientUpdate}],
},
getOptimisticUpdate: {
getLightClientOptimisticUpdate: {
args: [],
res: {version: ForkName.bellatrix, data: {syncAggregate, attestedHeader: header, signatureSlot}},
},
getFinalityUpdate: {
getLightClientFinalityUpdate: {
args: [],
res: {
version: ForkName.bellatrix,
Expand All @@ -33,7 +33,7 @@ export const testData: GenericServerTestCases<Api> = {
},
},
},
getBootstrap: {
getLightClientBootstrap: {
args: [toHexString(root)],
res: {
version: ForkName.bellatrix,
Expand All @@ -44,7 +44,7 @@ export const testData: GenericServerTestCases<Api> = {
},
},
},
getCommitteeRoot: {
getLightClientCommitteeRoot: {
args: [1, 2],
res: {data: [Buffer.alloc(32, 0), Buffer.alloc(32, 1)]},
},
Expand Down
7 changes: 5 additions & 2 deletions packages/api/test/utils/checkAgainstSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ export function runTestCheckAgainstSpec(
reqSerializers: Record<string, ReqSerializer<any, any>>,
returnTypes: Record<string, ReturnTypes<any>[string]>,
testDatas: Record<string, GenericServerTestCases<any>[string]>,
opts?: ParseOpenApiSpecOpts
opts?: ParseOpenApiSpecOpts,
filteredOperationsIds: string[] = []
): void {
const openApiSpec = parseOpenApiSpec(openApiJson, opts);

for (const [operationId, routeSpec] of openApiSpec.entries()) {
for (const [operationId, routeSpec] of [...openApiSpec.entries()].filter(
([operationId]) => !filteredOperationsIds.includes(operationId)
)) {
describe(operationId, () => {
const {requestSchema, responseOkSchema} = routeSpec;
const routeId = operationId;
Expand Down
Loading