Skip to content

Commit

Permalink
add README for tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rmgaray committed Jan 9, 2025
1 parent f6c7390 commit b6cc407
Showing 1 changed file with 174 additions and 0 deletions.
174 changes: 174 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Introduction

cardano-data-lite (CDL) was written as a web-native replacement to
[cardano-serialization-lib](https://github.com/emurgo/cardano-serialization-lib), hence it
uses CSL itself for testing compatibility.

CDL should be able to deserialize most existing Cardano transactions and re-create them with
bit-by-bit compatibility. It also features a nearly identical interface to CSL, making it
ideally a very low-effort task to replace one by the other.

## Compatibility goals

CDL makes the following compatility claims:

1. If a class or method is available in CSL, *in most cases* it will also be provided
by CDL with a nearly identical interface (see [Types and methods normalization]).

However, some deprecated or utility classes that CSL offers are deliberately not implemented
(see the [The API suite]).

2. CDL should be able to deserialize any Conway-era transaction and serialize it with an
*identical* wire representation (the CBOR representation should be the same).

Note that this applies to *whole* transactions. This means CDL can and does encode certain
transaction parts differently to CSL (see [Encoding of tagged sum variants]).
This is allowed because it simplifies the implementation of the library and it does not
compromise the ability of CDL of handling arbitrary transactions.

To test the achievement of each goal, a test suite has been implemented.

## The API suite

The CDL interface is tested by the API suite, implemented in `api/test.ts`. What the suite does is:

1. Parse the declaration files of the CSL and CDL libraries and collect all
method declarations (For each class: method name, argument types, return type).
2. Perform a normalization on these declarations (see [Types and methods normalization])
3. Compare the type declaration of each method in the CSL interface to the corresponding
type declaration in the CDL interface. If they are equal, the test passes.

The suite also performs a coverage test which checks that all methods in the CSL
interface are also available in the CDL interface. However, some coverage exceptions
exist, and these are specified in the `class-info.json` file, under the `api_ignore_classes`
and `api_ignore_methods` fields.

TODO: Check that these ignored classes/methods are up to date and we are not ignoring
unnecessary parts of the API.

## The serialization suite

The (de)serialization capabilities are tested in `serialization/serialization.test.ts`.

The suite runs on a set of `staging` and `regression` transactions. Transactions can be fetched
from the mainnet and put automatically in the `staging` folder by running the script:

```
npm run get-transactions
```

Staging TXs are not checked in the repository, while regression TXs _are_. If
a staging TX fails at any point of the tests, it is automatically promoted to a
regression TX (and it should be checked into the repository).


Both testing and regression transactions are tested in the same way:

┌──────┐ ┌──────┐
│ │ │ │
┌───── (CSL) Deserialize ────► │ TX ├───── (CSL) Serialize ─────► │ TX ┼──────┐
│ │( CSL)│ │(CBOR)│ │
┌──┴───┐ └──────┘ └──────┘ ▼
│ │
│ TX │ equal?
│(CBOR)│
└──┬───┘ ┌──────┐ ┌──────┐ ▲
│ │ │ │ │ │
└───── (CDL) Deserialize ────► │ TX ┼───── (CDL) Serialize ─────► │ TX ┼──────┘
│( CDL)│ │(CBOR)│
└──────┘ └──────┘

The transactions fetched from the blockchain are both deserialized and serialized by CDL
and CSL. The resulting CBOR encoding generated by each library are then tested for equality.

In practice, however, the TXs are not monolitically tested. There is an intermediate step
between deserialization and serialization in which transactions are split into their constituent parts
(code in `serialization_utils.ts`).

This allows us to test each component separately and also lets us detect which TX components
are the ultimate cause of a given error. For example, the suite will inform if a deserialization
error in the `TransactionWitnessSet` is actually due to a child `VkeyWitness` not being parsed correctly.
This is normally reported as a `Child failed to deserialize` error.

Just like for the API suite, there are some exceptions specified in `class-info.json` pertaining to
the serialization suite:

* The `extraction_unsupported_fields` contain transaction components that can't be extracted
and tested separately because our extraction code can't handle them. These components are still
test, just not separately.

* The `extraction_unsupported_types` contains types that we are interested in testing, like `boolean`
or `bignum`. Their correctness is just given.

## Types and methods normalization

The API test suite "normalizes" CDL and CSL method declarations and types before comparing
them. This is for two reasons:

* CDL usually passes an additional optional argument called `path[]: [string]`
to `deserialize` and related functions while CSL does not.

This is what allows CDL to pin-point with precision what exact component of a
transaction is failing to be deserialized and is very useful for debugging
purposes.

* Some methods in CSL may throw or return `undefined`, while their CDL
counterparts do not (and viceversa).

We consider these minor differences, because the behaviour of the method when
it returns succesfully is still the same.

## Encoding of tagged sum variants

CDL, just like CSL, uses code generation to generate most class and method implementations
that can be found in `generated.ts`. The schema for all classes are the YAML files that
can be found under the `conway-cddl/yaml` directory, which are half procedurally generated,
half manually written, based on the official CDDL files that describe the Cardano ledger.

Most of the time, CDL encodes things just the same as CSL. The main exception to these are
_sum variants_. If a transaction component is described in the CDDL ledger file as a tagged
sum, CDL will encode it correctly. But the variants of the specific sum type *will not* be encoded
as specified in the ledger spec.

For example: the `pool_params` component has the following definition in the Conway spec:

```
certificate = [stake_registration
// stake_deregistration
// stake_delegation
// pool_registration
... (and many other variants)
]
```

`certificate` is a sum type that is encoded an array with two elements: the variant's "tag"
and the variant's content. The tag and the content depend on the specific variant that is being encoded.

Both CDL and CSL serialize this class (`Certificate`) exactly the same. However, let's look at one of the
variants inside `certificate`:

```
pool_registration = (3, pool_params)
```

This specific variant has a tag equal to 3, and the content is a `pool_params`. If CSL is asked to
serialize this class (`PoolRegistration`), it will produce a certificate like this:

```
[3, pool_params]
```

(Note that the variant is encoded exactly the same as the type that contains it, `certificate`)

While CDL will produce a

```
pool_params
```

(Note the absence of the tag)

Because of this discrepancy, all tagged sum variants are *not* tested separately, but rather as
constituents of the sum that contains them. The list of variants not tested are contained in the
`serialization_bad_variants` field of the `class-info.json` file.

0 comments on commit b6cc407

Please sign in to comment.