1// SPDX-License-Identifier: MIT
2
3package kv
4
5import (
6 "bytes"
7 "errors"
8 "strings"
9 "testing"
10)
11
12func TestBatchCopiesInputs(t *testing.T) {
13 var b Batch
14 key, val := []byte("key"), []byte("value")
15 b.Put(key, val)
16 b.Delete(key)
17 b.RequireAbsent(key)
18 b.RequireVersion(key, 3)
19 key[0], val[0] = 'X', 'X'
20 if got := b.ops[0]; string(got.key) != "key" || string(got.value) != "value" {
21 t.Fatalf("put aliases its inputs: %q=%q", got.key, got.value)
22 }
23 if got := b.ops[1]; string(got.key) != "key" {
24 t.Fatalf("delete aliases its key: %q", got.key)
25 }
26 for i, p := range b.pre {
27 if string(p.key) != "key" {
28 t.Fatalf("precondition %d aliases its key: %q", i, p.key)
29 }
30 }
31 if b.Len() != 4 {
32 t.Fatalf("Len = %d, want 4", b.Len())
33 }
34}
35
36func TestBatchErrors(t *testing.T) {
37 long := bytes.Repeat([]byte("k"), maxKeyLen+1)
38 cases := map[string]func(b *Batch){
39 "put empty": func(b *Batch) { b.Put(nil, []byte("v")) },
40 "put long": func(b *Batch) { b.Put(long, nil) },
41 "delete empty": func(b *Batch) { b.Delete([]byte{}) },
42 "absent empty": func(b *Batch) { b.RequireAbsent(nil) },
43 "version empty": func(b *Batch) { b.RequireVersion(nil, 1) },
44 "version zero": func(b *Batch) { b.RequireVersion([]byte("k"), 0) },
45 "after valid op": func(b *Batch) { b.Put([]byte("a"), nil); b.Put(nil, nil); b.Put([]byte("b"), nil) },
46 }
47 for name, fn := range cases {
48 t.Run(name, func(t *testing.T) {
49 var b Batch
50 fn(&b)
51 if !errors.Is(b.err, ErrInvalidKey) {
52 t.Fatalf("err = %v, want ErrInvalidKey", b.err)
53 }
54 b.Reset()
55 if b.err != nil || b.Len() != 0 || b.encodedSize() != 0 {
56 t.Fatalf("Reset left err=%v len=%d size=%d", b.err, b.Len(), b.encodedSize())
57 }
58 })
59 }
60
61 var b Batch
62 b.Put(bytes.Repeat([]byte("k"), maxKeyLen), nil) // exactly the limit
63 if b.err != nil {
64 t.Fatalf("64 KiB key rejected: %v", b.err)
65 }
66}
67
68func TestBatchEncodedSize(t *testing.T) {
69 var b Batch
70 if b.encodedSize() != 0 {
71 t.Fatal("empty batch has a payload")
72 }
73 b.RequireAbsent([]byte("k"))
74 if b.encodedSize() != 0 {
75 t.Fatal("precondition-only batch has a payload")
76 }
77 b.Put([]byte("k"), []byte(strings.Repeat("v", 200)))
78 b.Delete([]byte("kk"))
79 want := len(appendRecord(nil, 1, b.ops)) - recordFrame - recordHead
80 if got := b.encodedSize(); got != want {
81 t.Fatalf("payload = %d, encoded batch = %d", got, want)
82 }
83}