TestCounterInt64_EarliestBlockSemantics tests that earliest block counters (ignoreRollbackOf=0) correctly accept decreases, which is needed when detection finds an earlier available block.
(t *testing.T)
| 1199 | // TestCounterInt64_EarliestBlockSemantics tests that earliest block counters (ignoreRollbackOf=0) |
| 1200 | // correctly accept decreases, which is needed when detection finds an earlier available block. |
| 1201 | func TestCounterInt64_EarliestBlockSemantics(t *testing.T) { |
| 1202 | t.Run("earliest block accepts decrease via local update (processNewValue)", func(t *testing.T) { |
| 1203 | counter := &counterInt64{ |
| 1204 | ignoreRollbackOf: 0, // earliest block mode: accept all rollbacks |
| 1205 | registry: &sharedStateRegistry{ |
| 1206 | logger: &log.Logger, |
| 1207 | }, |
| 1208 | } |
| 1209 | counter.value.Store(1500) // Initial earliest block |
| 1210 | |
| 1211 | // Simulate detection that earliest is actually lower (e.g., re-detection found earlier block) |
| 1212 | // This is a DECREASE: 1500 → 1000 |
| 1213 | updated := counter.processNewValue(UpdateSourceTryUpdate, 1000) |
| 1214 | assert.True(t, updated, "should accept decrease with ignoreRollbackOf=0") |
| 1215 | assert.Equal(t, int64(1000), counter.GetValue()) |
| 1216 | |
| 1217 | // Also accepts increases (forward progress due to pruning) |
| 1218 | updated = counter.processNewValue(UpdateSourceTryUpdate, 2000) |
| 1219 | assert.True(t, updated, "should also accept increases") |
| 1220 | assert.Equal(t, int64(2000), counter.GetValue()) |
| 1221 | }) |
| 1222 | |
| 1223 | t.Run("earliest block accepts decrease via remote update (processNewState)", func(t *testing.T) { |
| 1224 | counter := &counterInt64{ |
| 1225 | ignoreRollbackOf: 0, // earliest block mode: accept all rollbacks |
| 1226 | registry: &sharedStateRegistry{ |
| 1227 | logger: &log.Logger, |
| 1228 | instanceId: "test", |
| 1229 | }, |
| 1230 | } |
| 1231 | counter.value.Store(1500) |
| 1232 | counter.updatedAtUnixMs.Store(1) |
| 1233 | |
| 1234 | // Remote update with fresher timestamp and lower value |
| 1235 | // This is a DECREASE: 1500 → 1000 |
| 1236 | updated := counter.processNewState(UpdateSourceRemoteSync, CounterInt64State{ |
| 1237 | Value: 1000, // Decrease from 1500 |
| 1238 | UpdatedAt: 2, // Fresher timestamp |
| 1239 | UpdatedBy: "remote-instance", |
| 1240 | }) |
| 1241 | assert.True(t, updated, "should accept decrease with ignoreRollbackOf=0 via remote") |
| 1242 | assert.Equal(t, int64(1000), counter.GetValue()) |
| 1243 | }) |
| 1244 | |
| 1245 | t.Run("earliest block remote decrease rejected if timestamp is stale", func(t *testing.T) { |
| 1246 | counter := &counterInt64{ |
| 1247 | ignoreRollbackOf: 0, // earliest block mode |
| 1248 | registry: &sharedStateRegistry{ |
| 1249 | logger: &log.Logger, |
| 1250 | instanceId: "test", |
| 1251 | }, |
| 1252 | } |
| 1253 | counter.value.Store(1000) |
| 1254 | counter.updatedAtUnixMs.Store(10) // Local has timestamp 10 |
| 1255 | |
| 1256 | // Remote update with STALE timestamp (should be rejected regardless of value) |
| 1257 | updated := counter.processNewState(UpdateSourceRemoteSync, CounterInt64State{ |
| 1258 | Value: 500, // Even though it's a valid decrease |
nothing calls this directly
no test coverage detected