package transaction

import (
	"testing"

	"github.com/pingcap-incubator/tinykv/kv/transaction/mvcc"
	"github.com/pingcap-incubator/tinykv/kv/util/engine_util"
	"github.com/pingcap-incubator/tinykv/proto/pkg/kvrpcpb"
	"github.com/stretchr/testify/assert"
)

// TestGetValue4B getting a value works in the simple case.
func TestGetValue4B(t *testing.T) {
	builder := newBuilder(t)
	builder.init([]kv{
		{cf: engine_util.CfDefault, key: []byte{99}, ts: 50, value: []byte{42}},
		{cf: engine_util.CfWrite, key: []byte{99}, ts: 54, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 50}},
	})

	var req kvrpcpb.GetRequest
	req.Key = []byte{99}
	req.Version = mvcc.TsMax
	resp := builder.runOneRequest(&req).(*kvrpcpb.GetResponse)

	assert.Nil(t, resp.RegionError)
	assert.Nil(t, resp.Error)
	assert.Equal(t, []byte{42}, resp.Value)
}

// TestGetValueTs4B getting a value works with different timestamps.
func TestGetValueTs4B(t *testing.T) {
	builder := newBuilder(t)
	builder.init([]kv{
		{cf: engine_util.CfDefault, key: []byte{99}, ts: 50, value: []byte{42}},
		{cf: engine_util.CfWrite, key: []byte{99}, ts: 54, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 50}},
	})

	var req0 kvrpcpb.GetRequest
	req0.Key = []byte{99}
	req0.Version = 100
	var req1 kvrpcpb.GetRequest
	req1.Key = []byte{99}
	req1.Version = 100
	var req2 kvrpcpb.GetRequest
	req2.Key = []byte{99}
	req2.Version = 100

	resps := builder.runRequests(&req0, &req1, &req2)
	resp0 := resps[0].(*kvrpcpb.GetResponse)
	resp1 := resps[1].(*kvrpcpb.GetResponse)
	resp2 := resps[2].(*kvrpcpb.GetResponse)
	assert.Nil(t, resp0.RegionError)
	assert.Nil(t, resp0.Error)
	assert.Equal(t, []byte{42}, resp0.Value)
	assert.Nil(t, resp1.RegionError)
	assert.Nil(t, resp1.Error)
	assert.Equal(t, []byte{42}, resp1.Value)
	assert.Nil(t, resp2.RegionError)
	assert.Nil(t, resp2.Error)
	assert.Equal(t, []byte{42}, resp2.Value)
}

// TestGetEmpty4B tests that get on an empty DB.
func TestGetEmpty4B(t *testing.T) {
	builder := newBuilder(t)

	var req kvrpcpb.GetRequest
	req.Key = []byte{100}
	req.Version = mvcc.TsMax
	resp := builder.runOneRequest(&req).(*kvrpcpb.GetResponse)

	assert.Nil(t, resp.RegionError)
	assert.Nil(t, resp.Error)
	assert.True(t, resp.NotFound)
}

// TestGetNone4B tests that getting a missing key works.
func TestGetNone4B(t *testing.T) {
	builder := newBuilder(t)
	builder.init([]kv{
		{cf: engine_util.CfDefault, key: []byte{99}, ts: 50, value: []byte{42}},
		{cf: engine_util.CfWrite, key: []byte{99}, ts: 54, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 50}},
		{cf: engine_util.CfDefault, key: []byte{101}, ts: 50, value: []byte{42}},
		{cf: engine_util.CfWrite, key: []byte{101}, ts: 54, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 50}},
	})

	var req kvrpcpb.GetRequest
	req.Key = []byte{100}
	req.Version = mvcc.TsMax

	resp := builder.runOneRequest(&req).(*kvrpcpb.GetResponse)
	assert.Nil(t, resp.RegionError)
	assert.Nil(t, resp.Error)
	assert.True(t, resp.NotFound)
}

// TestGetVersions4B tests we get the correct value when there are multiple versions.
func TestGetVersions4B(t *testing.T) {
	builder := newBuilder(t)
	builder.init([]kv{
		{cf: engine_util.CfDefault, key: []byte{99}, ts: 50, value: []byte{42}},
		{cf: engine_util.CfWrite, key: []byte{99}, ts: 54, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 50}},
		{cf: engine_util.CfDefault, key: []byte{99}, ts: 60, value: []byte{43}},
		{cf: engine_util.CfWrite, key: []byte{99}, ts: 66, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 60}},
		{cf: engine_util.CfDefault, key: []byte{99}, ts: 120, value: []byte{44}},
		{cf: engine_util.CfWrite, key: []byte{99}, ts: 122, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 120}},
	})

	var req0 kvrpcpb.GetRequest
	req0.Key = []byte{99}
	req0.Version = 40
	var req1 kvrpcpb.GetRequest
	req1.Key = []byte{99}
	req1.Version = 56
	var req2 kvrpcpb.GetRequest
	req2.Key = []byte{99}
	req2.Version = 60
	var req3 kvrpcpb.GetRequest
	req3.Key = []byte{99}
	req3.Version = 65
	var req4 kvrpcpb.GetRequest
	req4.Key = []byte{99}
	req4.Version = 66
	var req5 kvrpcpb.GetRequest
	req5.Key = []byte{99}
	req5.Version = 100

	resps := builder.runRequests(&req0, &req1, &req2, &req3, &req4, &req5)
	resp0 := resps[0].(*kvrpcpb.GetResponse)
	resp1 := resps[1].(*kvrpcpb.GetResponse)
	resp2 := resps[2].(*kvrpcpb.GetResponse)
	resp3 := resps[3].(*kvrpcpb.GetResponse)
	resp4 := resps[4].(*kvrpcpb.GetResponse)
	resp5 := resps[5].(*kvrpcpb.GetResponse)

	assert.Nil(t, resp0.RegionError)
	assert.Nil(t, resp0.Error)
	assert.True(t, resp0.NotFound)
	assert.Nil(t, resp1.RegionError)
	assert.Nil(t, resp1.Error)
	assert.Equal(t, []byte{42}, resp1.Value)
	assert.Nil(t, resp2.RegionError)
	assert.Nil(t, resp2.Error)
	assert.Equal(t, []byte{42}, resp2.Value)
	assert.Nil(t, resp3.RegionError)
	assert.Nil(t, resp3.Error)
	assert.Equal(t, []byte{42}, resp3.Value)
	assert.Nil(t, resp4.RegionError)
	assert.Nil(t, resp4.Error)
	assert.Equal(t, []byte{43}, resp4.Value)
	assert.Nil(t, resp5.RegionError)
	assert.Nil(t, resp5.Error)
	assert.Equal(t, []byte{43}, resp5.Value)
}

// TestGetDeleted4B tests we get the correct value when there are multiple versions, including a deletion.
func TestGetDeleted4B(t *testing.T) {
	builder := newBuilder(t)
	builder.init([]kv{
		{cf: engine_util.CfDefault, key: []byte{99}, ts: 50, value: []byte{42}},
		{cf: engine_util.CfWrite, key: []byte{99}, ts: 54, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 50}},
		{cf: engine_util.CfDefault, key: []byte{99}, ts: 60, value: nil},
		{cf: engine_util.CfWrite, key: []byte{99}, ts: 66, value: []byte{2, 0, 0, 0, 0, 0, 0, 0, 60}},
		{cf: engine_util.CfDefault, key: []byte{99}, ts: 120, value: []byte{44}},
		{cf: engine_util.CfWrite, key: []byte{99}, ts: 122, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 120}},
	})

	var req0 kvrpcpb.GetRequest
	req0.Key = []byte{99}
	req0.Version = 54
	var req1 kvrpcpb.GetRequest
	req1.Key = []byte{99}
	req1.Version = 60
	var req2 kvrpcpb.GetRequest
	req2.Key = []byte{99}
	req2.Version = 65
	var req3 kvrpcpb.GetRequest
	req3.Key = []byte{99}
	req3.Version = 66
	var req4 kvrpcpb.GetRequest
	req4.Key = []byte{99}
	req4.Version = 67
	var req5 kvrpcpb.GetRequest
	req5.Key = []byte{99}
	req5.Version = 122

	resps := builder.runRequests(&req0, &req1, &req2, &req3, &req4, &req5)
	resp0 := resps[0].(*kvrpcpb.GetResponse)
	resp1 := resps[1].(*kvrpcpb.GetResponse)
	resp2 := resps[2].(*kvrpcpb.GetResponse)
	resp3 := resps[3].(*kvrpcpb.GetResponse)
	resp4 := resps[4].(*kvrpcpb.GetResponse)
	resp5 := resps[5].(*kvrpcpb.GetResponse)

	assert.Nil(t, resp0.RegionError)
	assert.Nil(t, resp0.Error)
	assert.Equal(t, []byte{42}, resp0.Value)
	assert.Nil(t, resp1.RegionError)
	assert.Nil(t, resp1.Error)
	assert.Equal(t, []byte{42}, resp1.Value)
	assert.Nil(t, resp2.RegionError)
	assert.Nil(t, resp2.Error)
	assert.Equal(t, []byte{42}, resp2.Value)
	assert.Nil(t, resp3.RegionError)
	assert.Nil(t, resp3.Error)
	assert.True(t, resp3.NotFound)
	assert.Nil(t, resp4.RegionError)
	assert.Nil(t, resp4.Error)
	assert.True(t, resp4.NotFound)
	assert.Nil(t, resp5.RegionError)
	assert.Nil(t, resp5.Error)
	assert.Equal(t, []byte{44}, resp5.Value)
}

// TestGetLocked4B tests getting a value when it is locked by another transaction.
func TestGetLocked4B(t *testing.T) {
	builder := newBuilder(t)
	builder.init([]kv{
		{cf: engine_util.CfDefault, key: []byte{99}, ts: 50, value: []byte{42}},
		{cf: engine_util.CfWrite, key: []byte{99}, ts: 54, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 50}},
		{cf: engine_util.CfLock, key: []byte{99}, value: []byte{99, 1, 0, 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 0, 0, 0, 0, 0}},
	})

	var req0 kvrpcpb.GetRequest
	req0.Key = []byte{99}
	req0.Version = 55
	var req1 kvrpcpb.GetRequest
	req1.Key = []byte{99}
	req1.Version = 300

	resps := builder.runRequests(&req0, &req1)
	resp0 := resps[0].(*kvrpcpb.GetResponse)
	resp1 := resps[1].(*kvrpcpb.GetResponse)

	assert.Nil(t, resp0.RegionError)
	assert.Nil(t, resp0.Error)
	assert.Equal(t, []byte{42}, resp0.Value)

	assert.Nil(t, resp1.RegionError)
	lockInfo := resp1.Error.Locked
	assert.Equal(t, []byte{99}, lockInfo.Key)
	assert.Equal(t, []byte{99}, lockInfo.PrimaryLock)
	assert.Equal(t, uint64(200), lockInfo.LockVersion)
}

// TestEmptyPrewrite4B tests that a Prewrite with no mutations succeeds and changes nothing.
func TestEmptyPrewrite4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.prewriteRequest()
	resp := builder.runOneRequest(cmd).(*kvrpcpb.PrewriteResponse)

	assert.Empty(t, resp.Errors)
	assert.Nil(t, resp.RegionError)
	builder.assertLen(engine_util.CfDefault, 0)
}

// TestSinglePrewrite4B tests a prewrite with one write, it should succeed, we test all the expected values.
func TestSinglePrewrite4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.prewriteRequest(mutation(3, []byte{42}, kvrpcpb.Op_Put))
	cmd.LockTtl = 1000
	resp := builder.runOneRequest(cmd).(*kvrpcpb.PrewriteResponse)

	assert.Empty(t, resp.Errors)
	assert.Nil(t, resp.RegionError)
	builder.assertLens(1, 1, 0)
	builder.assert([]kv{
		{cf: engine_util.CfDefault, key: []byte{3}, value: []byte{42}},
		{cf: engine_util.CfLock, key: []byte{3}, value: []byte{1, 1, 0, 0, 0, 0, 0, 0, 0, builder.ts(), 0, 0, 0, 0, 0, 0, 3, 232}},
	})
}

// TestPrewriteLocked4B tests that two prewrites to the same key causes a lock error.
func TestPrewriteLocked4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.prewriteRequest(mutation(3, []byte{42}, kvrpcpb.Op_Put))
	cmd2 := builder.prewriteRequest(mutation(3, []byte{53}, kvrpcpb.Op_Put))
	resps := builder.runRequests(cmd, cmd2)

	assert.Empty(t, resps[0].(*kvrpcpb.PrewriteResponse).Errors)
	assert.Nil(t, resps[0].(*kvrpcpb.PrewriteResponse).RegionError)
	assert.Equal(t, 1, len(resps[1].(*kvrpcpb.PrewriteResponse).Errors))
	assert.Nil(t, resps[1].(*kvrpcpb.PrewriteResponse).RegionError)
	builder.assertLens(1, 1, 0)
	builder.assert([]kv{
		{cf: engine_util.CfDefault, key: []byte{3}, ts: 100, value: []byte{42}},
		{cf: engine_util.CfLock, key: []byte{3}, value: []byte{1, 1, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0}},
	})
}

// TestPrewriteWritten4B tests an attempted prewrite with a write conflict.
func TestPrewriteWritten4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.prewriteRequest(mutation(3, []byte{42}, kvrpcpb.Op_Put))
	builder.init([]kv{
		{cf: engine_util.CfDefault, key: []byte{3}, ts: 80, value: []byte{5}},
		{cf: engine_util.CfWrite, key: []byte{3}, ts: 101, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 80}},
	})
	resp := builder.runOneRequest(cmd).(*kvrpcpb.PrewriteResponse)

	assert.Equal(t, 1, len(resp.Errors))
	assert.NotNil(t, resp.Errors[0].Conflict)
	assert.Nil(t, resp.RegionError)
	builder.assertLens(1, 0, 1)

	builder.assert([]kv{
		{cf: engine_util.CfDefault, key: []byte{3}, ts: 80, value: []byte{5}},
	})
}

// TestPrewriteWrittenNoConflict4B tests an attempted prewrite with a write already present, but no conflict.
func TestPrewriteWrittenNoConflict4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.prewriteRequest(mutation(3, []byte{42}, kvrpcpb.Op_Put))
	builder.init([]kv{
		{cf: engine_util.CfDefault, key: []byte{3}, ts: 80, value: []byte{5}},
		{cf: engine_util.CfWrite, key: []byte{3}, ts: 90, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 80}},
	})
	resp := builder.runOneRequest(cmd).(*kvrpcpb.PrewriteResponse)

	assert.Empty(t, resp.Errors)
	assert.Nil(t, resp.RegionError)
	assert.Nil(t, resp.RegionError)
	builder.assertLens(2, 1, 1)

	builder.assert([]kv{
		{cf: engine_util.CfDefault, key: []byte{3}, value: []byte{5}, ts: 80},
		{cf: engine_util.CfDefault, key: []byte{3}, value: []byte{42}},
		{cf: engine_util.CfLock, key: []byte{3}, value: []byte{1, 1, 0, 0, 0, 0, 0, 0, 0, builder.ts(), 0, 0, 0, 0, 0, 0, 0, 0}},
	})
}

// TestMultiplePrewrites4B tests that multiple prewrites to different keys succeeds.
func TestMultiplePrewrites4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.prewriteRequest(mutation(3, []byte{42}, kvrpcpb.Op_Put))
	cmd2 := builder.prewriteRequest(mutation(4, []byte{53}, kvrpcpb.Op_Put))
	resps := builder.runRequests(cmd, cmd2)

	assert.Empty(t, resps[0].(*kvrpcpb.PrewriteResponse).Errors)
	assert.Nil(t, resps[0].(*kvrpcpb.PrewriteResponse).RegionError)
	assert.Empty(t, resps[1].(*kvrpcpb.PrewriteResponse).Errors)
	assert.Nil(t, resps[1].(*kvrpcpb.PrewriteResponse).RegionError)
	builder.assertLens(2, 2, 0)

	builder.assert([]kv{
		{cf: engine_util.CfDefault, key: []byte{3}, ts: 100, value: []byte{42}},
		{cf: engine_util.CfLock, key: []byte{3}, value: []byte{1, 1, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0}},
		{cf: engine_util.CfDefault, key: []byte{4}, ts: 101, value: []byte{53}},
		{cf: engine_util.CfLock, key: []byte{4}, value: []byte{1, 1, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0}},
	})
}

// TestPrewriteOverwrite4B tests that two writes in the same prewrite succeed and we see the second write.
func TestPrewriteOverwrite4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.prewriteRequest(mutation(3, []byte{42}, kvrpcpb.Op_Put), mutation(3, []byte{45}, kvrpcpb.Op_Put))
	resp := builder.runOneRequest(cmd).(*kvrpcpb.PrewriteResponse)

	assert.Empty(t, resp.Errors)
	assert.Nil(t, resp.RegionError)
	builder.assertLens(1, 1, 0)

	builder.assert([]kv{
		{cf: engine_util.CfDefault, key: []byte{3}, value: []byte{45}},
		{cf: engine_util.CfLock, key: []byte{3}, value: []byte{1, 1, 0, 0, 0, 0, 0, 0, 0, builder.ts(), 0, 0, 0, 0, 0, 0, 0, 0}},
	})
}

// TestPrewriteMultiple4B tests that a prewrite with multiple mutations succeeds.
func TestPrewriteMultiple4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.prewriteRequest(
		mutation(3, []byte{42}, kvrpcpb.Op_Put),
		mutation(4, []byte{43}, kvrpcpb.Op_Put),
		mutation(5, []byte{44}, kvrpcpb.Op_Put),
		mutation(4, nil, kvrpcpb.Op_Del),
		mutation(4, []byte{1, 3, 5}, kvrpcpb.Op_Put),
		mutation(255, []byte{45}, kvrpcpb.Op_Put),
	)
	resp := builder.runOneRequest(cmd).(*kvrpcpb.PrewriteResponse)

	assert.Empty(t, resp.Errors)
	assert.Nil(t, resp.RegionError)
	builder.assertLens(4, 4, 0)

	builder.assert([]kv{
		{cf: engine_util.CfDefault, key: []byte{4}, value: []byte{1, 3, 5}},
	})
}

// TestEmptyCommit4B tests a commit request with no keys to commit.
func TestEmptyCommit4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.commitRequest([][]byte{}...)
	resp := builder.runOneRequest(cmd).(*kvrpcpb.CommitResponse)

	assert.Nil(t, resp.Error)
	assert.Nil(t, resp.RegionError)
	builder.assertLens(0, 0, 0)
}

// TestSimpleCommit4B tests committing a single key.
func TestSingleCommit4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.commitRequest([]byte{3})
	builder.init([]kv{
		{cf: engine_util.CfDefault, key: []byte{3}, value: []byte{42}},
		{cf: engine_util.CfLock, key: []byte{3}, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, builder.ts(), 0, 0, 0, 0, 0, 0, 0, 0}},
	})
	resp := builder.runOneRequest(cmd).(*kvrpcpb.CommitResponse)

	assert.Nil(t, resp.Error)
	assert.Nil(t, resp.RegionError)
	builder.assertLens(1, 0, 1)
	builder.assert([]kv{
		{cf: engine_util.CfWrite, key: []byte{3}, ts: 110, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, builder.ts()}},
		{cf: engine_util.CfDefault, key: []byte{3}},
	})
}

// TestCommitOverwrite4B tests committing where there is already a write.
func TestCommitOverwrite4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.commitRequest([]byte{3})
	builder.init([]kv{
		// A previous, committed write.
		{cf: engine_util.CfDefault, key: []byte{3}, ts: 80, value: []byte{15}},
		{cf: engine_util.CfWrite, key: []byte{3}, ts: 84, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 80}},

		// The current, pre-written write.
		{cf: engine_util.CfDefault, key: []byte{3}, value: []byte{42}},
		{cf: engine_util.CfLock, key: []byte{3}, value: []byte{1, 1, 0, 0, 0, 0, 0, 0, 0, builder.ts(), 0, 0, 0, 0, 0, 0, 0, 0}},
	})
	resp := builder.runOneRequest(cmd).(*kvrpcpb.CommitResponse)

	assert.Nil(t, resp.Error)
	assert.Nil(t, resp.RegionError)
	builder.assertLens(2, 0, 2)
	builder.assert([]kv{
		{cf: engine_util.CfWrite, key: []byte{3}, ts: 110, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, builder.ts()}},
		{cf: engine_util.CfDefault, key: []byte{3}},
	})
}

// TestCommitMultipleKeys4B tests committing multiple keys in the same commit. Also puts some other data in the DB and test
// that it is unchanged.
func TestCommitMultipleKeys4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.commitRequest([]byte{3}, []byte{12, 4, 0}, []byte{15})
	builder.init([]kv{
		// Current, pre-written.
		{cf: engine_util.CfDefault, key: []byte{3}, value: []byte{42}},
		{cf: engine_util.CfLock, key: []byte{3}, value: []byte{1, 1, 0, 0, 0, 0, 0, 0, 0, builder.ts(), 0, 0, 0, 0, 0, 0, 0, 0}},
		{cf: engine_util.CfDefault, key: []byte{12, 4, 0}, value: []byte{1, 1, 0, 0, 1, 5}},
		{cf: engine_util.CfLock, key: []byte{12, 4, 0}, value: []byte{1, 1, 0, 0, 0, 0, 0, 0, 0, builder.ts(), 0, 0, 0, 0, 0, 0, 0, 0}},
		{cf: engine_util.CfDefault, key: []byte{15}, value: []byte{0}},
		{cf: engine_util.CfLock, key: []byte{15}, value: []byte{1, 1, 0, 0, 0, 0, 0, 0, 0, builder.ts(), 0, 0, 0, 0, 0, 0, 0, 0}},

		// Some committed data.
		{cf: engine_util.CfDefault, key: []byte{4}, ts: 80, value: []byte{15}},
		{cf: engine_util.CfWrite, key: []byte{4}, ts: 84, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 80}},
		{cf: engine_util.CfDefault, key: []byte{3, 0}, ts: 80, value: []byte{150}},
		{cf: engine_util.CfWrite, key: []byte{3, 0}, ts: 84, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 80}},

		// Another pre-written transaction.
		{cf: engine_util.CfDefault, key: []byte{2}, ts: 99, value: []byte{0, 0, 0, 8}},
		{cf: engine_util.CfLock, key: []byte{2}, value: []byte{1, 2, 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0}},
		{cf: engine_util.CfDefault, key: []byte{43, 6}, ts: 99, value: []byte{1, 1, 0, 0, 1, 5}},
		{cf: engine_util.CfLock, key: []byte{43, 6}, value: []byte{1, 2, 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0}},
	})
	resp := builder.runOneRequest(cmd).(*kvrpcpb.CommitResponse)

	assert.Nil(t, resp.Error)
	assert.Nil(t, resp.RegionError)
	builder.assertLens(7, 2, 5)
	builder.assert([]kv{
		// The newly committed data.
		{cf: engine_util.CfWrite, key: []byte{3}, ts: 110, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, builder.ts()}},
		{cf: engine_util.CfWrite, key: []byte{12, 4, 0}, ts: 110, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, builder.ts()}},
		{cf: engine_util.CfWrite, key: []byte{15}, ts: 110, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, builder.ts()}},

		// Committed data is untouched.
		{cf: engine_util.CfDefault, key: []byte{4}, ts: 80},
		{cf: engine_util.CfWrite, key: []byte{4}, ts: 84},
		{cf: engine_util.CfDefault, key: []byte{3, 0}, ts: 80},
		{cf: engine_util.CfWrite, key: []byte{3, 0}, ts: 84},

		// Pre-written data is untouched.
		{cf: engine_util.CfDefault, key: []byte{2}, ts: 99},
		{cf: engine_util.CfLock, key: []byte{2}},
		{cf: engine_util.CfDefault, key: []byte{43, 6}, ts: 99},
		{cf: engine_util.CfLock, key: []byte{43, 6}},
	})
}

// TestRecommitKey4B tests committing the same key multiple times in one commit.
func TestRecommitKey4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.commitRequest([]byte{3}, []byte{3})
	builder.init([]kv{
		// The current, pre-written write.
		{cf: engine_util.CfDefault, key: []byte{3}, value: []byte{42}},
		{cf: engine_util.CfLock, key: []byte{3}, value: []byte{1, 1, 0, 0, 0, 0, 0, 0, 0, builder.ts(), 0, 0, 0, 0, 0, 0, 0, 0}},
	})
	resp := builder.runOneRequest(cmd).(*kvrpcpb.CommitResponse)

	assert.Nil(t, resp.Error)
	assert.Nil(t, resp.RegionError)
	builder.assertLens(1, 0, 1)
	builder.assert([]kv{
		{cf: engine_util.CfWrite, key: []byte{3}, ts: 110, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, builder.ts()}},
		{cf: engine_util.CfDefault, key: []byte{3}},
	})
}

// TestCommitConflictRollback4B tests committing a rolled back transaction.
func TestCommitConflictRollback4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.commitRequest([]byte{3})
	builder.init([]kv{
		{cf: engine_util.CfWrite, key: []byte{3}, ts: 110, value: []byte{3, 0, 0, 0, 0, 0, 0, 0, builder.ts()}},
	})
	resp := builder.runOneRequest(cmd).(*kvrpcpb.CommitResponse)

	assert.Nil(t, resp.Error)
	assert.Nil(t, resp.RegionError)
	builder.assertLens(0, 0, 1)
	builder.assert([]kv{
		{cf: engine_util.CfWrite, key: []byte{3}, ts: 110},
	})
}

// TestCommitConflictRace4B tests committing where a key is pre-written by a different transaction.
func TestCommitConflictRace4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.commitRequest([]byte{3})
	builder.init([]kv{
		{cf: engine_util.CfDefault, key: []byte{3}, ts: 90, value: []byte{110}},
		{cf: engine_util.CfLock, key: []byte{3}, value: []byte{1, 3, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, 0, 0, 0, 0}},
	})
	resp := builder.runOneRequest(cmd).(*kvrpcpb.CommitResponse)

	assert.NotNil(t, resp.Error.Retryable)
	assert.Nil(t, resp.RegionError)
	builder.assertLens(1, 1, 0)
	builder.assert([]kv{
		{cf: engine_util.CfLock, key: []byte{3}},
		{cf: engine_util.CfDefault, key: []byte{3}, ts: 90},
	})
}

// TestCommitConflictRepeat4B tests recommitting a transaction (i.e., the same commit request is received twice).
func TestCommitConflictRepeat4B(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.commitRequest([]byte{3})
	builder.init([]kv{
		{cf: engine_util.CfDefault, key: []byte{3}, value: []byte{42}},
		{cf: engine_util.CfWrite, key: []byte{3}, ts: 110, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, builder.ts()}},
	})
	resp := builder.runOneRequest(cmd).(*kvrpcpb.CommitResponse)

	assert.Nil(t, resp.Error)
	assert.Nil(t, resp.RegionError)
	builder.assertLens(1, 0, 1)
	builder.assert([]kv{
		{cf: engine_util.CfWrite, key: []byte{3}, ts: 110},
		{cf: engine_util.CfDefault, key: []byte{3}},
	})
}

// TestCommitMissingPrewrite4a tests committing a transaction which was not prewritten (i.e., a request was lost, but
// the commit request was not).
func TestCommitMissingPrewrite4a(t *testing.T) {
	builder := newBuilder(t)
	cmd := builder.commitRequest([]byte{3})
	builder.init([]kv{
		// Some committed data.
		{cf: engine_util.CfDefault, key: []byte{4}, ts: 80, value: []byte{15}},
		{cf: engine_util.CfWrite, key: []byte{4}, ts: 84, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 80}},
		{cf: engine_util.CfDefault, key: []byte{3, 0}, ts: 80, value: []byte{150}},
		{cf: engine_util.CfWrite, key: []byte{3, 0}, ts: 84, value: []byte{1, 0, 0, 0, 0, 0, 0, 0, 80}},
		// Note no prewrite.
	})
	resp := builder.runOneRequest(cmd).(*kvrpcpb.CommitResponse)

	assert.Nil(t, resp.Error)
	assert.Nil(t, resp.RegionError)
	builder.assertLens(2, 0, 2)
	builder.assert([]kv{
		{cf: engine_util.CfDefault, key: []byte{4}, ts: 80},
		{cf: engine_util.CfWrite, key: []byte{4}, ts: 84},
		{cf: engine_util.CfDefault, key: []byte{3, 0}, ts: 80},
		{cf: engine_util.CfWrite, key: []byte{3, 0}, ts: 84},
	})
}
