Repository: etcd-io/etcd
File: etcdutl/snapshot/v3_snapshot_test.go (Go)

Code before:
// TestSnapshotStatusNegativeRevisionSub tests if snapshot status command fails when sub revision number is negative.
func TestSnapshotStatusNegativeRevisionSub(t *testing.T) {
	dbpath := createDB(t, insertKeys(t, 1, 0))

	db, err := bbolt.Open(dbpath, 0o666, nil)
	require.NoError(t, err)
	defer db.Close()

	err = db.Update(func(tx *bbolt.Tx) error {
		b := tx.Bucket([]byte("key"))
		if b == nil {
			return errors.New("key bucket not found")
		}
		bytes := mvcc.NewRevBytes()
		mvcc.RevToBytes(mvcc.Revision{Sub: -1}, bytes)
		return b.Put(bytes, []byte{})
	})
	require.NoError(t, err)
	db.Close()

	_, err = NewV3(zap.NewNop()).Status(dbpath)
	require.ErrorContains(t, err, "negative revision")
}

// TestSnapshotStatusDuplicateKeys tests if TotalKey in status remains unchanged when inserting the same key multiple times
func TestSnapshotStatusDuplicateKeys(t *testing.T) {
	dbpath := createDB(t, func(srv *etcdserver.EtcdServer) {
		keys := []string{"key1", "key2", "key1"}
		val := make([]byte, len(keys))
		for _, key := range keys {
			for i := 0; i < 3; i++ {
				req := etcdserverpb.PutRequest{
					Key:   []byte(key),
					Value: val,
				}
				_, err := srv.Put(context.TODO(), &req)
				require.NoError(t, err)
			}
		}
	})

	status, err := NewV3(zap.NewNop()).Status(dbpath)
	require.NoError(t, err)
	// unique keys: key1, key2
	assert.Equalf(t, 2, status.TotalKey, "mismatch on duplicate inserts, expected 2 unique keys (key1/key2)")
}

// TestSnapshotStatusMixedRevisions tests if key count respects latest revision state
func TestSnapshotStatusMixedRevisions(t *testing.T) {
	dbpath := createDB(t, func(srv *etcdserver.EtcdServer) {
		// key1: create -> put -> delete

Review comment (suggestion):
All the test cases should be able to be consolidated into one table-driven sub tests. But not a big problem.


Code after:
// TestSnapshotStatusNegativeRevisionSub tests if snapshot status command fails when sub revision number is negative.
func TestSnapshotStatusNegativeRevisionSub(t *testing.T) {
	dbpath := createDB(t, insertKeys(t, 1, 0))

	db, err := bbolt.Open(dbpath, 0o666, nil)
	require.NoError(t, err)
	defer db.Close()

	err = db.Update(func(tx *bbolt.Tx) error {
		b := tx.Bucket([]byte("key"))
		if b == nil {
			return errors.New("key bucket not found")
		}
		bytes := mvcc.NewRevBytes()
		mvcc.RevToBytes(mvcc.Revision{Sub: -1}, bytes)
		return b.Put(bytes, []byte{})
	})
	require.NoError(t, err)
	db.Close()

	_, err = NewV3(zap.NewNop()).Status(dbpath)
	require.ErrorContains(t, err, "negative revision")
}

// TestSnapshotStatusTotalKey tests if snapshot status command correctly reports total number of valid keys.
func TestSnapshotStatusTotalKey(t *testing.T) {
	cases := []struct {
		name     string
		prepare  func(srv *etcdserver.EtcdServer)
		expected int
	}{
		{
			name: "duplicate keys",
			prepare: func(srv *etcdserver.EtcdServer) {
				keys := []string{"key1", "key2", "key1"}
				val := make([]byte, len(keys))
				for _, key := range keys {
					for i := 0; i < 3; i++ {
						req := etcdserverpb.PutRequest{
							Key:   []byte(key),
							Value: val,
						}
						_, err := srv.Put(context.TODO(), &req)
						require.NoError(t, err)
					}
				}
			},
			expected: 2,
		},
		{
			name: "mixed revisions",