github.com/tsuna/gohbase@v0.0.0-20250731002811-4ffcadfba63e/region/info_test.go (about) 1 // Copyright (C) 2015 The GoHBase Authors. All rights reserved. 2 // This file is part of GoHBase. 3 // Use of this source code is governed by the Apache License 2.0 4 // that can be found in the COPYING file. 5 6 package region 7 8 import ( 9 "bytes" 10 "context" 11 "encoding/json" 12 "strconv" 13 "strings" 14 "testing" 15 "time" 16 17 "github.com/stretchr/testify/assert" 18 "github.com/tsuna/gohbase/hrpc" 19 "github.com/tsuna/gohbase/pb" 20 "github.com/tsuna/gohbase/test" 21 "github.com/tsuna/gohbase/test/mock" 22 "google.golang.org/protobuf/proto" 23 ) 24 25 // Test parsing the contents of a cell found in meta. 26 func TestInfoFromMeta(t *testing.T) { 27 put := pb.CellType_PUT 28 regionName := []byte("table,,1431921690563.53e41f94d5c3087af0d13259b8c4186d.") 29 buf := []byte("PBUF\010\303\217\274\251\326)\022\020\n\007default" + 30 "\022\005table\032\000\"\000(\0000\0008\000") 31 cell := &hrpc.Cell{ 32 Row: regionName, 33 Family: []byte("info"), 34 Qualifier: []byte("regioninfo"), 35 Timestamp: proto.Uint64(1431921690626), 36 CellType: &put, 37 } 38 _, err := infoFromCell(cell) 39 if err == nil || !strings.HasPrefix(err.Error(), "empty value") { 40 t.Errorf("Unexpected error on empty value: %s", err) 41 } 42 cell.Value = buf 43 info, err := infoFromCell(cell) 44 if err != nil { 45 t.Fatalf("Failed to parse cell: %s", err) 46 } 47 if !bytes.Equal(info.Name(), regionName) { 48 t.Errorf("Unexpected regionName name: %q", info.Name()) 49 } 50 if len(info.StopKey()) != 0 { 51 t.Errorf("Expected empty StopKey but got %q", info.StopKey()) 52 } 53 54 expected := `RegionInfo{Name: "table,,1431921690563.53e41f94d5c3087af0d13259b8c4186d.", ` + 55 `ID: 1431921690563, Namespace: "", Table: "table", StartKey: "", StopKey: ""}` 56 if s := info.String(); s != expected { 57 t.Errorf("Unexpected string representation.\nExpected: %q\n Actual: %q", expected, s) 58 } 59 60 // Set region to be offline 61 buf[34] = 0x01 62 _, err = infoFromCell(cell) 63 if _, ok := err.(OfflineRegionError); !ok { 64 t.Fatalf("Unexpected error on offline region: %s", err) 65 } 66 67 // Corrupt the protobuf. 68 buf[4] = 0xFF 69 _, err = infoFromCell(cell) 70 if err == nil || !strings.HasPrefix(err.Error(), "failed to decode") { 71 t.Errorf("Unexpected error on corrupt protobuf: %s", err) 72 } 73 74 // Corrupt the magic number. 75 buf[1] = 0xFF 76 _, err = infoFromCell(cell) 77 if err == nil || !strings.HasPrefix(err.Error(), "invalid magic number") { 78 t.Errorf("Unexpected error on invalid magic number %s", err) 79 } 80 81 // Corrupt the magic number (first byte). 82 buf[0] = 0xFF 83 _, err = infoFromCell(cell) 84 if err == nil || !strings.HasPrefix(err.Error(), "unsupported region info version") { 85 t.Errorf("Unexpected error on invalid magic number %s", err) 86 } 87 } 88 89 func TestCompare(t *testing.T) { 90 // Test cases from AsyncHBase 91 testcases := []struct { 92 a, b []byte // Region names, where a > b 93 }{{ 94 // Different table names. 95 []byte("table,,1234567890"), []byte(".META.,,1234567890"), 96 }, { 97 // Different table names but same prefix. 98 []byte("tabl2,,1234567890"), []byte("tabl1,,1234567890"), 99 }, { 100 // Different table names (different lengths). 101 []byte("table,,1234567890"), []byte("tabl,,1234567890"), 102 }, { 103 // Any key is greater than the start key. 104 []byte("table,foo,1234567890"), []byte("table,,1234567890"), 105 }, { 106 // Different keys. 107 []byte("table,foo,1234567890"), []byte("table,bar,1234567890"), 108 }, { 109 // Shorter key is smaller than longer key. 110 []byte("table,fool,1234567890"), []byte("table,foo,1234567890"), 111 }, { 112 // Properly handle keys that contain commas. 113 []byte("table,a,,c,1234567890"), []byte("table,a,,b,1234567890"), 114 }, { 115 // If keys are equal, then start code should break the tie. 116 []byte("table,foo,1234567891"), []byte("table,foo,1234567890"), 117 }, { 118 // Make sure that a start code being a prefix of another is handled. 119 []byte("table,foo,1234567890"), []byte("table,foo,123456789"), 120 }, { 121 // If both are start keys, then start code should break the tie. 122 []byte("table,,1234567891"), []byte("table,,1234567890"), 123 }, { 124 // The value `:' is always greater than any start code. 125 []byte("table,foo,:"), []byte("table,foo,9999999999"), 126 }, { 127 // Issue 27: searching for key "8,\001" and region key is "8". 128 []byte("table,8,\001,:"), []byte("table,8,1339667458224"), 129 }} 130 131 for _, tcase := range testcases { 132 if i := Compare(tcase.a, tcase.b); i <= 0 { 133 t.Errorf("%q was found to be less than %q (%d)", tcase.a, tcase.b, i) 134 } 135 if i := Compare(tcase.b, tcase.a); i >= 0 { 136 t.Errorf("%q was found to be greater than %q (%d)", tcase.b, tcase.a, i) 137 } 138 } 139 } 140 141 func TestCompareBogusName(t *testing.T) { 142 defer func() { 143 expected := `no comma found in "bogus" after offset 5` 144 v := recover() 145 if v == nil { 146 t.Errorf("Should have panic'ed") 147 } else if e, ok := v.(error); !ok { 148 t.Errorf("panic'ed with a %T instead of an error (%#v)", v, v) 149 } else if e.Error() != expected { 150 t.Errorf("Expected panic(%q) but got %q", expected, e) 151 } 152 }() 153 Compare([]byte("bogus"), []byte("bogus")) 154 } 155 156 func TestRegionInfoMarshalJson(t *testing.T) { 157 ctrl := test.NewController(t) 158 defer ctrl.Finish() 159 160 var id uint64 = 1 161 namespace := []byte("test") 162 table := []byte("testTable") 163 name := []byte("testTableName") 164 startKey := []byte("startKey") 165 stopKey := []byte("stopKey") 166 queueSize := 30 167 flushInterval := 20 * time.Millisecond 168 169 mockConn := mock.NewMockConn(ctrl) 170 171 c := &client{ 172 conn: mockConn, 173 addr: "testAddr", 174 ctype: RegionClient, 175 rpcs: make(chan []hrpc.Call), 176 done: make(chan struct{}), 177 sent: make(map[uint32]hrpc.Call), 178 inFlight: 20, 179 effectiveUser: "effectiveUser", 180 rpcQueueSize: queueSize, 181 flushInterval: flushInterval, 182 readTimeout: DefaultReadTimeout, 183 } 184 185 ctx, cancel := context.WithCancel(context.Background()) 186 info := &info{ 187 id: id, 188 ctx: ctx, 189 cancel: cancel, 190 namespace: namespace, 191 table: table, 192 name: name, 193 startKey: startKey, 194 stopKey: stopKey, 195 specifier: &pb.RegionSpecifier{ 196 Type: pb.RegionSpecifier_REGION_NAME.Enum(), 197 Value: []byte("test"), 198 }, 199 client: c, 200 available: nil, 201 } 202 203 jsonVal, err := info.MarshalJSON() 204 205 if err != nil { 206 t.Fatalf("Should not have thrown an error: %v", err) 207 } 208 209 var jsonUnMarshal map[string]interface{} 210 err = json.Unmarshal(jsonVal, &jsonUnMarshal) 211 212 if err != nil { 213 t.Fatalf("Error while unmarshalling JSON, %v", err) 214 } 215 216 assert.Equal(t, float64(id), jsonUnMarshal["Id"]) 217 assert.Equal(t, strconv.QuoteToASCII(string(table)), jsonUnMarshal["Table"]) 218 assert.Equal(t, strconv.QuoteToASCII(string(name)), jsonUnMarshal["Name"]) 219 assert.Equal(t, strconv.QuoteToASCII(string(startKey)), jsonUnMarshal["StartKey"]) 220 assert.Equal(t, strconv.QuoteToASCII(string(stopKey)), jsonUnMarshal["StopKey"]) 221 assert.Equal(t, true, jsonUnMarshal["Available"]) 222 assert.Equal(t, strconv.QuoteToASCII(string(namespace)), jsonUnMarshal["Namespace"]) 223 224 } 225 226 func TestRegionInfoMarshalJsonNilValues(t *testing.T) { 227 ctrl := test.NewController(t) 228 defer ctrl.Finish() 229 info := &info{ 230 id: 0, 231 ctx: nil, 232 cancel: nil, 233 namespace: nil, 234 table: nil, 235 name: nil, 236 startKey: nil, 237 stopKey: nil, 238 specifier: nil, 239 client: nil, 240 available: nil, 241 } 242 243 _, err := info.MarshalJSON() 244 245 if err != nil { 246 t.Fatalf("Should not have thrown an error: %v", err) 247 } 248 }