github.com/koko1123/flow-go-1@v0.29.6/module/mempool/stdmap/incorporated_result_seals_test.go (about) 1 package stdmap 2 3 import ( 4 "testing" 5 6 "github.com/stretchr/testify/assert" 7 "github.com/stretchr/testify/require" 8 9 "pgregory.net/rapid" 10 11 "github.com/koko1123/flow-go-1/model/flow" 12 "github.com/koko1123/flow-go-1/utils/unittest" 13 ) 14 15 // icrSealsMachine is a description of a state machine for testing IncorporatedresultSeals 16 type icrSealsMachine struct { 17 icrs *IncorporatedResultSeals // icrSeals being tested 18 state []*flow.IncorporatedResultSeal // model of the icrSeals 19 } 20 21 // Init is an action for initializing a icrSeals instance. 22 func (m *icrSealsMachine) Init(t *rapid.T) { 23 m.icrs = NewIncorporatedResultSeals(1000) 24 } 25 26 // Add is a conditional action which adds an item to the icrSeals. 27 func (m *icrSealsMachine) Add(t *rapid.T) { 28 i := rapid.Uint64().Draw(t, "i").(uint64) 29 30 seal := unittest.IncorporatedResultSeal.Fixture(func(s *flow.IncorporatedResultSeal) { 31 s.Header.Height = i 32 }) 33 34 _, err := m.icrs.Add(seal) 35 require.NoError(t, err) 36 37 // we do not re-add already present seals 38 unmet := true 39 for _, v := range m.state { 40 if v.ID() == seal.ID() { 41 unmet = false 42 } 43 } 44 45 if unmet && seal.Header.Height >= m.icrs.lowestHeight { 46 m.state = append(m.state, seal) 47 } 48 } 49 50 // Prune is a Conditional action that removes elements of height strictly lower than its argument 51 func (m *icrSealsMachine) PruneUpToHeight(t *rapid.T) { 52 h := rapid.Uint64().Draw(t, "h").(uint64) 53 err := m.icrs.PruneUpToHeight(h) 54 if h >= m.icrs.lowestHeight { 55 require.NoError(t, err) 56 assert.Equal(t, m.icrs.lowestHeight, h) 57 } 58 59 filtered_state := make([]*flow.IncorporatedResultSeal, 0) 60 for _, v := range m.state { 61 if v.Header.Height >= h { 62 filtered_state = append(filtered_state, v) 63 } 64 } 65 m.state = filtered_state 66 } 67 68 // Get is an action that retrieves an element from the icrSeals 69 func (m *icrSealsMachine) Get(t *rapid.T) { 70 n := len(m.state) 71 // skip if the store is empty 72 if n == 0 { 73 return 74 } 75 i := rapid.IntRange(0, n-1).Draw(t, "i").(int) 76 77 s := m.state[i] 78 actual, ok := m.icrs.ByID(s.ID()) 79 require.True(t, ok) 80 require.Equal(t, s, actual) 81 82 } 83 84 // GetUnknown is an action that removes an unknown element from the icrSeals 85 // This mostly tests ByID has no insertion side-effects 86 func (m *icrSealsMachine) GetUnknown(t *rapid.T) { 87 n := len(m.state) 88 // skip if the store is empty 89 if n == 0 { 90 return 91 } 92 i := rapid.IntRange(0, n-1).Draw(t, "i").(int) 93 seal := unittest.IncorporatedResultSeal.Fixture(func(s *flow.IncorporatedResultSeal) { 94 s.Header.Height = uint64(i) 95 }) 96 97 // check seal is unknown 98 unknown := true 99 for _, v := range m.state { 100 if v.ID() == seal.ID() { 101 unknown = false 102 } 103 } 104 105 if unknown { 106 _, found := m.icrs.ByID(seal.ID()) 107 require.False(t, found) 108 } 109 // no modification of state 110 111 } 112 113 // Remove is a conditional action that removes a known element from the icrSeals 114 func (m *icrSealsMachine) Remove(t *rapid.T) { 115 n := len(m.state) 116 // skip if the store is empty 117 if n == 0 { 118 return 119 } 120 i := rapid.IntRange(0, n-1).Draw(t, "i").(int) 121 122 s := m.state[i] 123 ok := m.icrs.Remove(s.ID()) 124 require.True(t, ok) 125 126 // remove m[i], we don't care about ordering here 127 m.state[n-1], m.state[i] = m.state[i], m.state[n-1] 128 m.state = m.state[:n-1] 129 130 } 131 132 // RemoveUnknown is an action that removes an unknown element from the icrSeals 133 // This mostly tests Remove has no insertion side-effects 134 func (m *icrSealsMachine) RemoveUnknown(t *rapid.T) { 135 n := len(m.state) 136 // skip if the store is empty 137 if n == 0 { 138 return 139 } 140 i := rapid.IntRange(0, n-1).Draw(t, "i").(int) 141 seal := unittest.IncorporatedResultSeal.Fixture(func(s *flow.IncorporatedResultSeal) { 142 s.Header.Height = uint64(i) 143 }) 144 145 // check seal is unknown 146 unknown := true 147 for _, v := range m.state { 148 if v.ID() == seal.ID() { 149 unknown = false 150 } 151 } 152 153 if unknown { 154 removed := m.icrs.Remove(seal.ID()) 155 require.False(t, removed) 156 } 157 // no modification of state 158 159 } 160 161 // Check runs after every action and verifies that all required invariants hold. 162 func (m *icrSealsMachine) Check(t *rapid.T) { 163 if int(m.icrs.Size()) != len(m.state) { 164 t.Fatalf("store size mismatch: %v vs expected %v", m.icrs.Size(), len(m.state)) 165 } 166 assert.ElementsMatch(t, m.icrs.All(), m.state) 167 } 168 169 // Run the icrSeals state machine and test it against its model 170 func TestIcrs(t *testing.T) { 171 rapid.Check(t, rapid.Run(&icrSealsMachine{})) 172 } 173 174 func TestIncorporatedResultSeals(t *testing.T) { 175 t.Parallel() 176 177 // after adding one receipt, should be able to query it back by previous result id 178 // after removing, should not be able to query it back. 179 t.Run("add remove get", func(t *testing.T) { 180 pool := NewIncorporatedResultSeals(1000) 181 182 seal := unittest.IncorporatedResultSeal.Fixture() 183 184 ok, err := pool.Add(seal) 185 require.NoError(t, err) 186 require.True(t, ok) 187 188 actual, ok := pool.ByID(seal.ID()) 189 require.True(t, ok) 190 require.Equal(t, seal, actual) 191 192 deleted := pool.Remove(seal.ID()) 193 require.True(t, deleted) 194 195 _, ok = pool.ByID(seal.ID()) 196 require.False(t, ok) 197 }) 198 199 t.Run("add 100 prune by height", func(t *testing.T) { 200 pool := NewIncorporatedResultSeals(1000) 201 202 seals := make([]*flow.IncorporatedResultSeal, 0, 100) 203 for i := 0; i < 100; i++ { 204 seal := unittest.IncorporatedResultSeal.Fixture(func(s *flow.IncorporatedResultSeal) { 205 s.Header.Height = uint64(i) 206 }) 207 208 seals = append(seals, seal) 209 } 210 211 for _, seal := range seals { 212 ok, err := pool.Add(seal) 213 require.NoError(t, err) 214 require.True(t, ok, "seal at height %d was not added", seal.Header.Height) 215 } 216 verifyPresent(t, pool, seals...) 217 218 err := pool.PruneUpToHeight(5) 219 require.NoError(t, err) 220 verifyAbsent(t, pool, seals[:5]...) 221 verifyPresent(t, pool, seals[5:]...) 222 223 err = pool.PruneUpToHeight(10) 224 require.NoError(t, err) 225 verifyAbsent(t, pool, seals[:10]...) 226 verifyPresent(t, pool, seals[10:]...) 227 }) 228 229 t.Run("prune with some nonexisting heights", func(t *testing.T) { 230 pool := NewIncorporatedResultSeals(1000) 231 232 // create seals, but NOT for heights 2 and 3 233 seals := make([]*flow.IncorporatedResultSeal, 0, 6) 234 for _, h := range []uint64{0, 1, 4, 5, 6, 7} { 235 seal := unittest.IncorporatedResultSeal.Fixture(func(s *flow.IncorporatedResultSeal) { 236 s.Header.Height = h 237 }) 238 seals = append(seals, seal) 239 ok, err := pool.Add(seal) 240 require.NoError(t, err) 241 require.True(t, ok) 242 } 243 244 err := pool.PruneUpToHeight(5) 245 require.NoError(t, err) 246 verifyAbsent(t, pool, seals[:3]...) 247 verifyPresent(t, pool, seals[3:]...) 248 }) 249 } 250 251 func verifyPresent(t *testing.T, pool *IncorporatedResultSeals, seals ...*flow.IncorporatedResultSeal) { 252 for _, seal := range seals { 253 _, ok := pool.ByID(seal.ID()) 254 require.True(t, ok, "seal at height %d should be in mempool", seal.Header.Height) 255 } 256 } 257 258 func verifyAbsent(t *testing.T, pool *IncorporatedResultSeals, seals ...*flow.IncorporatedResultSeal) { 259 for _, seal := range seals { 260 _, ok := pool.ByID(seal.ID()) 261 require.False(t, ok, "seal at height %d should not be in mempool", seal.Header.Height) 262 } 263 }