Repository: etcd-io/etcd
File: cache/store_test.go (Go)

Code before:
			eventBatches: [][]*clientv3.Event{
				{makePutEvent("/a", "newA", 10), makePutEvent("/b", "newB", 10)},
			},
			expectedLatestRev: 10,
			expectedSnapshot:  []*mvccpb.KeyValue{makeKV("/a", "newA", 10), makeKV("/b", "newB", 10)},
		},
		{
			name:       "delete_same_key",
			initialKVs: []*mvccpb.KeyValue{makeKV("/a", "X", 10)},
			initialRev: 10,
			eventBatches: [][]*clientv3.Event{
				{makeDelEvent("/a", 11)},
			},
			expectedLatestRev: 11,
			expectedSnapshot:  nil,
		},
		{
			name:              "delete_nonexistent",
			initialKVs:        []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
			initialRev:        5,
			eventBatches:      [][]*clientv3.Event{{makeDelEvent("/zzz", 6)}},
			expectedLatestRev: 6,
			expectedSnapshot:  []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
		},
		{
			name:              "stale_batch_rejected",
			initialKVs:        []*mvccpb.KeyValue{makeKV("/x", "1", 20)},
			initialRev:        20,
			eventBatches:      [][]*clientv3.Event{{makePutEvent("/x", "2", 19)}},
			expectedLatestRev: 20,
			expectedSnapshot:  []*mvccpb.KeyValue{makeKV("/x", "1", 20)},
			expectErr:         true,
		},
		{
			name:       "mixed_stale_batch_rejected",
			initialKVs: []*mvccpb.KeyValue{makeKV("/x", "1", 20)},
			initialRev: 20,
			eventBatches: [][]*clientv3.Event{
				{makePutEvent("/x", "should-not-apply", 19), makePutEvent("/y", "new", 21)},
			},
			expectedLatestRev: 20,
			expectedSnapshot:  []*mvccpb.KeyValue{makeKV("/x", "1", 20)},
			expectErr:         true,
		},
	}

	for _, tt := range tests {
		test := tt
		t.Run(test.name, func(t *testing.T) {
			s := newStore(4)
			s.Restore(test.initialKVs, test.initialRev)

Review comment (bug):
Note, events with single revision should be applied at once, within single lock, same like etcd. If not, cache will expose partially applied revision which is invalid. So this test case is incorrect, also please make sure we correctly apply revision. We could add validation in storage that prevents same revision being applied twice to prevent this kind of bug.

Code after:
			expectedSnapshot:  []*mvccpb.KeyValue{makeKV("/a", "A2", 25)},
		},
		{
			name:       "put_multiple_non_contiguous_batches",
			initialKVs: []*mvccpb.KeyValue{makeKV("/a", "A1", 21), makeKV("/b", "B1", 22)},
			initialRev: 22,
			eventBatches: [][]*clientv3.Event{
				{makePutEvent("/a", "A2", 25)},
				{makePutEvent("/b", "B2", 27)},
			},
			expectedLatestRev: 27,
			expectedSnapshot:  []*mvccpb.KeyValue{makeKV("/a", "A2", 25), makeKV("/b", "B2", 27)},
		},
		{
			name:       "apply_mixed_operations",
			initialKVs: []*mvccpb.KeyValue{makeKV("/a", "A1", 20)},
			initialRev: 20,
			eventBatches: [][]*clientv3.Event{
				{makePutEvent("/a", "A2", 21), makePutEvent("/b", "B1", 21), makePutEvent("/c", "C1", 21)},
				{makePutEvent("/b", "B2", 22)},
				{makeDelEvent("/c", 23), makePutEvent("/a", "A3", 23)},
				{makePutEvent("/b", "B3", 24)},
			},
			expectedLatestRev: 24,
			expectedSnapshot:  []*mvccpb.KeyValue{makeKV("/a", "A3", 23), makeKV("/b", "B3", 24)},
		},
		{
			name:       "delete_same_key",
			initialKVs: []*mvccpb.KeyValue{makeKV("/a", "X", 10)},
			initialRev: 10,
			eventBatches: [][]*clientv3.Event{
				{makeDelEvent("/a", 11)},
			},
			expectedLatestRev: 11,
			expectedSnapshot:  nil,
		},
		{
			name:              "delete_nonexistent_returns_error",
			initialKVs:        []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
			initialRev:        5,
			eventBatches:      [][]*clientv3.Event{{makeDelEvent("/zzz", 6)}},
			expectedLatestRev: 5,
			expectedSnapshot:  []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
			expectErr:         true,
		},
		{
			name:              "mixed_delete_nonexistent_returns_error",
			initialKVs:        []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
			initialRev:        5,
			eventBatches:      [][]*clientv3.Event{{makeDelEvent("/zzz", 6), makePutEvent("/r", "Y", 6)}},
			expectedLatestRev: 5,