Categorycosmossdk.io/schema/testing
modulepackage
0.0.1
Repository: https://github.com/cosmos/cosmos-sdk.git
Documentation: pkg.go.dev

# README

Schema & Indexer Testing

This module contains core test utilities and fixtures for testing cosmossdk.io/schema and cosmossdk.io/schema/indexer functionality. It is managed as a separate go module to manage versions better and allow for dependencies on useful testing libraries without imposing those elsewhere.

The two primary intended uses of this library are:

  • testing that indexers can handle all valid app data that they might be asked to index
  • testing that state management frameworks properly map their data to and from schema types

Testing Indexers

Indexers are expected to process all valid schema and appdata types, yet it may be hard to find a data set in the wild that comprehensively represents the full valid range of these types. This library provides utilities for simulating such data. The simplest way for an indexer to leverage this test framework is to implement the appdatasim.HasAppData type against their data store. Then the appdatasim.Simulator can be used to generate deterministically random valid data that can be sent to the indexer and also stored in the simulator. After each generated block is applied, appdatasim.DiffAppData can be used to compare the expected state in the simulator to the actual state in the indexer.

This example code shows how this might look in a test:

func TestMyIndexer(t *testing.T) {
	var myIndexerListener appdata.Listener
	var myIndexerAppData appdatasim.HasAppData
    // do the setup for your indexer and return an appdata.Listener to consume updates and the appdatasim.HasAppData instance to check the actual vs expected data
    myIndexerListener, myIndexerAppData := myIndexer.Setup() 
	
    simulator, err := appdatasim.NewSimulator(appdatatest.SimulatorOptions{
        AppSchema: indexertesting.ExampleAppSchema,
        StateSimOptions: statesim.Options{
            CanRetainDeletions: true,
        },
		Listener: myIndexerListener,
    })
    require.NoError(t, err)
    
    blockDataGen := simulator.BlockDataGen()
    for i := 0; i < 1000; i++ {
		// using Example generates a deterministic data set based
        // on a seed so that regression tests can be created OR rapid.Check can
        // be used for fully random property-based testing
        blockData := blockDataGen.Example(i)
		
        // process the generated block data with the simulator which will also
        // send it to the indexer
        require.NoError(t, simulator.ProcessBlockData(blockData))
		
        // compare the expected state in the simulator to the actual state in the indexer and expect the diff to be empty
		require.Empty(t, appdatasim.DiffAppData(simulator, myIndexerAppData))
    }
}

Testing State Management Frameworks

The compliance of frameworks like cosmossdk.io/collections and cosmossdk.io/orm with cosmossdk.io/schema can be tested with this framework. One example of how this might be done is if there is a KeyCodec that represents an array of schema.Fields then schematesting.ObjectKeyGen might be used to generate a random object key which encoded and then decoded and then schematesting.DiffObjectKeys is used to compare the expected key with the decoded key. If such state management frameworks require that users that schema compliance when implementing things like KeyCodecs then those state management frameworks should specify best practices for users.

# Packages

Package appdatasim contains utilities for simulating valid streams of app data for testing indexer implementations.
Package statesim contains utilities for simulating state based on ObjectType's and ModuleSchema's for testing the conformance of state management libraries and indexers to schema rules.

# Functions

CompareKindValues compares the expected and actual values for the provided kind and returns true if they are equal, false if they are not, and an error if the types are not valid for the kind.
DiffFieldValues compares the values for the provided field and returns a diff if they differ or an empty string if they are equal.
DiffObjectKeys compares the values as object keys for the provided field and returns a diff if they differ or an empty string if they are equal.
DiffObjectValues compares the values as object values for the provided field and returns a diff if they differ or an empty string if they are equal.
EnumType generates random valid EnumTypes.
FieldGen generates random Field's based on the validity criteria of fields.
FieldValueGen generates random valid values for the field, aiming to exercise the full range of possible values for the field.
KeyFieldGen generates random key fields based on the validity criteria of key fields.
ModuleSchemaGen generates random ModuleSchema's based on the validity criteria of module schemas.
ObjectKeyGen generates a value that is valid for the provided object key fields.
ObjectKeyString formats the object key as a string deterministically for storage in a map.
ObjectValueGen generates a value that is valid for the provided object value fields.
StateObjectInsertGen generates object updates that are valid for insertion.
StateObjectTypeGen generates random StateObjectType's based on the validity criteria of object types.
StateObjectUpdateGen generates object updates that are valid for updates using the provided state map as a source of valid existing keys.

# Variables

AppSchemaGen generates random valid app schemas, essentially a map of module names to module schemas.
ExampleAppSchema is an example app schema that intends to cover all schema cases that indexers should handle that can be used in reproducible unit testing and property based testing.
NameGen validates valid names that match the NameFormat regex.