github.com/cosmos/cosmos-sdk@v0.50.10/x/group/internal/orm/primary_key_property_test.go (about)

     1  package orm
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/require"
     7  	"pgregory.net/rapid"
     8  
     9  	storetypes "cosmossdk.io/store/types"
    10  
    11  	"github.com/cosmos/cosmos-sdk/codec"
    12  	"github.com/cosmos/cosmos-sdk/codec/types"
    13  	"github.com/cosmos/cosmos-sdk/testutil/testdata"
    14  )
    15  
    16  func TestPrimaryKeyTable(t *testing.T) {
    17  	rapid.Check(t, testPrimaryKeyMachine)
    18  }
    19  
    20  func testPrimaryKeyMachine(t *rapid.T) {
    21  	// Init creates a new instance of the state machine model by building the real
    22  	// table and making the empty model map
    23  	// Create context
    24  	ctx := NewMockContext()
    25  	store := ctx.KVStore(storetypes.NewKVStoreKey("test"))
    26  
    27  	// Create primary key table
    28  	interfaceRegistry := types.NewInterfaceRegistry()
    29  	cdc := codec.NewProtoCodec(interfaceRegistry)
    30  	table, err := NewPrimaryKeyTable(
    31  		[2]byte{0x1},
    32  		&testdata.TableModel{},
    33  		cdc,
    34  	)
    35  	require.NoError(t, err)
    36  
    37  	// Create model state
    38  	state := make(map[string]*testdata.TableModel)
    39  
    40  	t.Repeat(map[string]func(*rapid.T){
    41  		// Create is one of the model commands. It adds an object to the table, creating
    42  		// an error if it already exists.
    43  		"Create": func(t *rapid.T) {
    44  			g := genTableModel.Draw(t, "g")
    45  			pk := string(PrimaryKey(g))
    46  
    47  			t.Logf("pk: %v", pk)
    48  			t.Logf("state: %v", state)
    49  
    50  			err := table.Create(store, g)
    51  
    52  			if state[pk] != nil {
    53  				require.Error(t, err)
    54  			} else {
    55  				require.NoError(t, err)
    56  				state[pk] = g
    57  			}
    58  		},
    59  
    60  		// Update is one of the model commands. It updates the value at a given primary
    61  		// key and fails if that primary key doesn't already exist in the table.
    62  		"Update": func(t *rapid.T) {
    63  			tm := generateTableModel(state).Draw(t, "tm")
    64  
    65  			newName := rapid.StringN(1, 100, 150).Draw(t, "newName")
    66  			tm.Name = newName
    67  
    68  			// Perform the real Update
    69  			err := table.Update(store, tm)
    70  
    71  			if state[string(PrimaryKey(tm))] == nil {
    72  				// If there's no value in the model, we expect an error
    73  				require.Error(t, err)
    74  			} else {
    75  				// If we have a value in the model, expect no error
    76  				require.NoError(t, err)
    77  
    78  				// Update the model with the new value
    79  				state[string(PrimaryKey(tm))] = tm
    80  			}
    81  		},
    82  
    83  		// Set is one of the model commands. It sets the value at a key in the table
    84  		// whether it exists or not.
    85  		"Set": func(t *rapid.T) {
    86  			g := genTableModel.Draw(t, "g")
    87  			pk := string(PrimaryKey(g))
    88  
    89  			err := table.Set(store, g)
    90  
    91  			require.NoError(t, err)
    92  			state[pk] = g
    93  		},
    94  
    95  		// Delete is one of the model commands. It removes the object with the given
    96  		// primary key from the table and returns an error if that primary key doesn't
    97  		// already exist in the table.
    98  		"Delete": func(t *rapid.T) {
    99  			tm := generateTableModel(state).Draw(t, "tm")
   100  
   101  			// Perform the real Delete
   102  			err := table.Delete(store, tm)
   103  
   104  			if state[string(PrimaryKey(tm))] == nil {
   105  				// If there's no value in the model, we expect an error
   106  				require.Error(t, err)
   107  			} else {
   108  				// If we have a value in the model, expect no error
   109  				require.NoError(t, err)
   110  
   111  				// Delete the value from the model
   112  				delete(state, string(PrimaryKey(tm)))
   113  			}
   114  		},
   115  
   116  		// Has is one of the model commands. It checks whether a key already exists in
   117  		// the table.
   118  		"Has": func(t *rapid.T) {
   119  			pk := PrimaryKey(generateTableModel(state).Draw(t, "g"))
   120  
   121  			realHas := table.Has(store, pk)
   122  			modelHas := state[string(pk)] != nil
   123  
   124  			require.Equal(t, realHas, modelHas)
   125  		},
   126  
   127  		// GetOne is one of the model commands. It fetches an object from the table by
   128  		// its primary key and returns an error if that primary key isn't in the table.
   129  		"GetOne": func(t *rapid.T) {
   130  			pk := PrimaryKey(generateTableModel(state).Draw(t, "tm"))
   131  
   132  			var tm testdata.TableModel
   133  
   134  			err := table.GetOne(store, pk, &tm)
   135  			t.Logf("tm: %v", tm)
   136  
   137  			if state[string(pk)] == nil {
   138  				require.Error(t, err)
   139  			} else {
   140  				require.NoError(t, err)
   141  				require.Equal(t, *state[string(pk)], tm)
   142  			}
   143  		},
   144  
   145  		// Check that the real values match the state values.
   146  		"": func(t *rapid.T) {
   147  			for i := range state {
   148  				has := table.Has(store, []byte(i))
   149  				require.Equal(t, true, has)
   150  			}
   151  		},
   152  	})
   153  }
   154  
   155  // stateKeys gets all the keys in the model map
   156  func stateKeys(state map[string]*testdata.TableModel) []string {
   157  	keys := make([]string, len(state))
   158  
   159  	i := 0
   160  	for k := range state {
   161  		keys[i] = k
   162  		i++
   163  	}
   164  
   165  	return keys
   166  }
   167  
   168  // generateTableModel a TableModel that has a 50% chance of being a part of the existing
   169  // state
   170  func generateTableModel(state map[string]*testdata.TableModel) *rapid.Generator[*testdata.TableModel] {
   171  	genStateTableModel := rapid.Custom(func(t *rapid.T) *testdata.TableModel {
   172  		pk := rapid.SampledFrom(stateKeys(state)).Draw(t, "key")
   173  		return state[pk]
   174  	})
   175  
   176  	if len(stateKeys(state)) == 0 {
   177  		return genTableModel
   178  	}
   179  	return rapid.OneOf(genTableModel, genStateTableModel)
   180  }