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  }