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

expect.to.throw not called for ssz generic test #3480

Merged
merged 1 commit into from
Dec 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
137 changes: 83 additions & 54 deletions packages/spec-test-runner/test/spec/ssz/generic/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,102 @@
import {expect} from "chai";
import {join} from "path";
import path from "path";
import fs from "fs";
// eslint-disable-next-line no-restricted-imports
import {getInvalidTestcases, getValidTestcases} from "@chainsafe/lodestar-spec-test-util/lib/sszGeneric";
import {CompositeValue, isCompositeType} from "@chainsafe/ssz";
import {parseInvalidTestcase, parseValidTestcase} from "@chainsafe/lodestar-spec-test-util/lib/sszGeneric";
import {CompositeType, isCompositeType, toHexString, Type} from "@chainsafe/ssz";
import {SPEC_TEST_LOCATION} from "../../../specTestVersioning";

// Test types defined here
import {types} from "./types";
import {getTestType} from "./types";

for (const type of types) {
// valid testcases
describe(`ssz generic - valid - ${type.prefix}`, () => {
for (const testcase of getValidTestcases(join(type.path, "valid"), type.prefix, type.type)) {
it(`${testcase.path.split("/").pop()}`, () => {
// test struct round trip serialization/deserialization
expect(
type.type.deserialize(type.type.serialize(testcase.value)),
"Invalid struct round-trip serialization/deserialization"
).to.deep.equal(testcase.value);
const rootGenericSszPath = path.join(SPEC_TEST_LOCATION, "tests", "general", "phase0", "ssz_generic");

// test struct serialization
expect(type.type.serialize(testcase.value), "Invalid struct serialization").to.deep.equal(testcase.serialized);
// ssz_generic
// | basic_vector
// | invalid
// | vec_bool_0
// | serialized.ssz_snappy
// | valid
// | vec_bool_1_max
// | meta.yaml
// | serialized.ssz_snappy
// | value.yaml
//
// Docs: https://github.com/ethereum/eth2.0-specs/blob/master/tests/formats/ssz_generic/README.md

// test deserialization to struct
expect(type.type.deserialize(testcase.serialized), "Invalid deserialization to struct").to.deep.equal(
testcase.value
);
for (const testType of fs.readdirSync(rootGenericSszPath)) {
const testTypePath = path.join(rootGenericSszPath, testType);

// test struct merkleization
expect(type.type.hashTreeRoot(testcase.value), "Invalid struct merkleization").to.deep.equal(testcase.root);
describe(`${testType} invalid`, () => {
const invalidCasesPath = path.join(testTypePath, "invalid");
for (const invalidCase of fs.readdirSync(invalidCasesPath)) {
it(invalidCase, () => {
const type = getTestType(testType, invalidCase);
const testData = parseInvalidTestcase(path.join(invalidCasesPath, invalidCase));

// If the type is composite, test tree-backed ops
if (isCompositeType(type.type)) {
const structValue = testcase.value as CompositeValue;
const treebackedValue = type.type.createTreeBackedFromStruct(structValue);
// Unlike the valid suite, invalid encodings do not have any value or hash tree root. The serialized data
// should simply not be decoded without raising an error.
// Note that for some type declarations in the invalid suite, the type itself may technically be invalid.
// This is a valid way of detecting invalid data too. E.g. a 0-length basic vector.
expect(() => type.deserialize(testData.serialized), "Must throw on deserialize").to.throw();
});
}
});

// test struct / tree-backed equality
expect(type.type.equals(structValue, treebackedValue), "Struct and tree-backed not equal").to.be.true;
describe(`${testType} valid`, () => {
const validCasesPath = path.join(testTypePath, "valid");
for (const validCase of fs.readdirSync(validCasesPath)) {
// NOTE: ComplexTestStruct tests are not correctly generated.
// where deserialized .d value is D: '0x00'. However the tests guide mark that field as D: Bytes[256].
// Those test won't be fixed since most implementations staticly compile types.
if (validCase.startsWith("ComplexTestStruct")) {
continue;
}

// test tree-backed to struct
expect(
type.type.tree_convertToStruct(treebackedValue.tree),
"Tree-backed to struct conversion resulted in unequal value"
).to.deep.equal(structValue);
it(validCase, () => {
const type = getTestType(testType, validCase);

// test tree-backed serialization
expect(treebackedValue.serialize(), "Invalid tree-backed serialization").to.deep.equal(testcase.serialized);
const testData = parseValidTestcase(path.join(validCasesPath, validCase), type);
const testDataSerialized = toHexString(testData.serialized);
const testDataRoot = toHexString(testData.root);

// test deserialization to tree-backed
expect(
type.type.tree_convertToStruct(type.type.tree_deserialize(testcase.serialized)),
"Invalid deserialization to tree-backed"
).to.deep.equal(structValue);
const serialized = wrapErr(() => type.serialize(testData.value), "type.serialize()");
const value = wrapErr(() => type.deserialize(testData.serialized), "type.deserialize()");
const root = wrapErr(() => type.hashTreeRoot(testData.value), "type.hashTreeRoot()");
const valueSerdes = wrapErr(() => type.deserialize(serialized), "type.deserialize(serialized)");

// test tree-backed merkleization
expect(treebackedValue.hashTreeRoot(), "Invalid tree-backed merkleization").to.deep.equal(testcase.root);
}
});
}
});
expect(valueSerdes).to.deep.equal(testData.value, "round trip serdes");
expect(toHexString(serialized)).to.equal(testDataSerialized, "struct serialize");
expect(value).to.deep.equal(testData.value, "struct deserialize");
expect(toHexString(root)).to.equal(testDataRoot, "struct hashTreeRoot");

// If the type is composite, test tree-backed ops
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (!isCompositeType(type as Type<any>)) return;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const compositeType = type as CompositeType<any>;

// invalid testcases
describe(`ssz generic - invalid - ${type.prefix}`, () => {
for (const testcase of getInvalidTestcases(join(type.path, "invalid"), type.prefix)) {
it(`${testcase.path.split("/").pop()}`, () => {
// test struct round trip serialization/deserialization
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
expect(() => type.type.deserialize(testcase.serialized), "Invalid data should error during deserialization").to
.throw;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const treebackedValue = compositeType.createTreeBackedFromStruct(testData.value);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
const treeToStruct = compositeType.tree_convertToStruct(treebackedValue.tree);

expect(treeToStruct).to.deep.equal(testData.value, "tree-backed to struct");
expect(type.equals(testData.value, treebackedValue), "struct - tree-backed type.equals()").to.be.true;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
expect(toHexString(treebackedValue.serialize())).to.equal(testDataSerialized, "tree-backed serialize");
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
expect(toHexString(treebackedValue.hashTreeRoot())).to.equal(testDataRoot, "tree-backed hashTreeRoot");
});
}
});
}

function wrapErr<T>(fn: () => T, prefix: string): T {
try {
return fn();
} catch (e) {
(e as Error).message = `${prefix}: ${(e as Error).message}`;
throw e;
}
}
Loading