mirror of
https://github.com/documize/community.git
synced 2025-08-05 13:35:25 +02:00
Change RefID field size from 16 to 20 with updated algo
Xid dependency updated. Moved to 20 length ID values. Added new revision number to version and meta information. Revision number is timestamp format.
This commit is contained in:
parent
7860bc1808
commit
391c143483
25 changed files with 2123 additions and 878 deletions
396
core/uniqueid/xid/id_test.go
Executable file
396
core/uniqueid/xid/id_test.go
Executable file
|
@ -0,0 +1,396 @@
|
|||
package xid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IDParts struct {
|
||||
id ID
|
||||
timestamp int64
|
||||
machine []byte
|
||||
pid uint16
|
||||
counter int32
|
||||
}
|
||||
|
||||
var IDs = []IDParts{
|
||||
IDParts{
|
||||
ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9},
|
||||
1300816219,
|
||||
[]byte{0x60, 0xf4, 0x86},
|
||||
0xe428,
|
||||
4271561,
|
||||
},
|
||||
IDParts{
|
||||
ID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
0,
|
||||
[]byte{0x00, 0x00, 0x00},
|
||||
0x0000,
|
||||
0,
|
||||
},
|
||||
IDParts{
|
||||
ID{0x00, 0x00, 0x00, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x00, 0x00, 0x01},
|
||||
0,
|
||||
[]byte{0xaa, 0xbb, 0xcc},
|
||||
0xddee,
|
||||
1,
|
||||
},
|
||||
}
|
||||
|
||||
func TestIDPartsExtraction(t *testing.T) {
|
||||
for i, v := range IDs {
|
||||
t.Run(fmt.Sprintf("Test%d", i), func(t *testing.T) {
|
||||
if got, want := v.id.Time(), time.Unix(v.timestamp, 0); got != want {
|
||||
t.Errorf("Time() = %v, want %v", got, want)
|
||||
}
|
||||
if got, want := v.id.Machine(), v.machine; !bytes.Equal(got, want) {
|
||||
t.Errorf("Machine() = %v, want %v", got, want)
|
||||
}
|
||||
if got, want := v.id.Pid(), v.pid; got != want {
|
||||
t.Errorf("Pid() = %v, want %v", got, want)
|
||||
}
|
||||
if got, want := v.id.Counter(), v.counter; got != want {
|
||||
t.Errorf("Counter() = %v, want %v", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
// Generate 10 ids
|
||||
ids := make([]ID, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
ids[i] = New()
|
||||
}
|
||||
for i := 1; i < 10; i++ {
|
||||
prevID := ids[i-1]
|
||||
id := ids[i]
|
||||
// Test for uniqueness among all other 9 generated ids
|
||||
for j, tid := range ids {
|
||||
if j != i {
|
||||
if id.Compare(tid) == 0 {
|
||||
t.Errorf("generated ID is not unique (%d/%d)", i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check that timestamp was incremented and is within 30 seconds of the previous one
|
||||
secs := id.Time().Sub(prevID.Time()).Seconds()
|
||||
if secs < 0 || secs > 30 {
|
||||
t.Error("wrong timestamp in generated ID")
|
||||
}
|
||||
// Check that machine ids are the same
|
||||
if !bytes.Equal(id.Machine(), prevID.Machine()) {
|
||||
t.Error("machine ID not equal")
|
||||
}
|
||||
// Check that pids are the same
|
||||
if id.Pid() != prevID.Pid() {
|
||||
t.Error("pid not equal")
|
||||
}
|
||||
// Test for proper increment
|
||||
if got, want := int(id.Counter()-prevID.Counter()), 1; got != want {
|
||||
t.Errorf("wrong increment in generated ID, delta=%v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIDString(t *testing.T) {
|
||||
id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
|
||||
if got, want := id.String(), "9m4e2mr0ui3e8a215n4g"; got != want {
|
||||
t.Errorf("String() = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromString(t *testing.T) {
|
||||
got, err := FromString("9m4e2mr0ui3e8a215n4g")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
|
||||
if got != want {
|
||||
t.Errorf("FromString() = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromStringInvalid(t *testing.T) {
|
||||
_, err := FromString("invalid")
|
||||
if err != ErrInvalidID {
|
||||
t.Errorf("FromString(invalid) err=%v, want %v", err, ErrInvalidID)
|
||||
}
|
||||
}
|
||||
|
||||
type jsonType struct {
|
||||
ID *ID
|
||||
Str string
|
||||
}
|
||||
|
||||
func TestIDJSONMarshaling(t *testing.T) {
|
||||
id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
|
||||
v := jsonType{ID: &id, Str: "test"}
|
||||
data, err := json.Marshal(&v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := string(data), `{"ID":"9m4e2mr0ui3e8a215n4g","Str":"test"}`; got != want {
|
||||
t.Errorf("json.Marshal() = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIDJSONUnmarshaling(t *testing.T) {
|
||||
data := []byte(`{"ID":"9m4e2mr0ui3e8a215n4g","Str":"test"}`)
|
||||
v := jsonType{}
|
||||
err := json.Unmarshal(data, &v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
|
||||
if got := *v.ID; got.Compare(want) != 0 {
|
||||
t.Errorf("json.Unmarshal() = %v, want %v", got, want)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestIDJSONUnmarshalingError(t *testing.T) {
|
||||
v := jsonType{}
|
||||
err := json.Unmarshal([]byte(`{"ID":"9M4E2MR0UI3E8A215N4G"}`), &v)
|
||||
if err != ErrInvalidID {
|
||||
t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID)
|
||||
}
|
||||
err = json.Unmarshal([]byte(`{"ID":"TYjhW2D0huQoQS"}`), &v)
|
||||
if err != ErrInvalidID {
|
||||
t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID)
|
||||
}
|
||||
err = json.Unmarshal([]byte(`{"ID":"TYjhW2D0huQoQS3kdk"}`), &v)
|
||||
if err != ErrInvalidID {
|
||||
t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIDDriverValue(t *testing.T) {
|
||||
id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
|
||||
got, err := id.Value()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want := "9m4e2mr0ui3e8a215n4g"; got != want {
|
||||
t.Errorf("Value() = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIDDriverScan(t *testing.T) {
|
||||
got := ID{}
|
||||
err := got.Scan("9m4e2mr0ui3e8a215n4g")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
|
||||
if got.Compare(want) != 0 {
|
||||
t.Errorf("Scan() = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIDDriverScanError(t *testing.T) {
|
||||
id := ID{}
|
||||
if got, want := id.Scan(0), errors.New("xid: scanning unsupported type: int"); !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Scan() err=%v, want %v", got, want)
|
||||
}
|
||||
if got, want := id.Scan("0"), ErrInvalidID; got != want {
|
||||
t.Errorf("Scan() err=%v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIDDriverScanByteFromDatabase(t *testing.T) {
|
||||
got := ID{}
|
||||
bs := []byte("9m4e2mr0ui3e8a215n4g")
|
||||
err := got.Scan(bs)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
|
||||
if got.Compare(want) != 0 {
|
||||
t.Errorf("Scan() = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNew(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = New()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkNewString(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_ = New().String()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkFromString(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_, _ = FromString("9m4e2mr0ui3e8a215n4g")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// func BenchmarkUUIDv1(b *testing.B) {
|
||||
// b.RunParallel(func(pb *testing.PB) {
|
||||
// for pb.Next() {
|
||||
// _ = uuid.NewV1().String()
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
// func BenchmarkUUIDv4(b *testing.B) {
|
||||
// b.RunParallel(func(pb *testing.PB) {
|
||||
// for pb.Next() {
|
||||
// _ = uuid.NewV4().String()
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
func TestID_IsNil(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
id ID
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "ID not nil",
|
||||
id: New(),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Nil ID",
|
||||
id: ID{},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got, want := tt.id.IsNil(), tt.want; got != want {
|
||||
t.Errorf("IsNil() = %v, want %v", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNilID(t *testing.T) {
|
||||
got := ID{}
|
||||
if want := NilID(); !reflect.DeepEqual(got, want) {
|
||||
t.Error("NilID() not equal ID{}")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNilID_IsNil(t *testing.T) {
|
||||
if !NilID().IsNil() {
|
||||
t.Error("NilID().IsNil() is not true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromBytes_Invariant(t *testing.T) {
|
||||
want := New()
|
||||
got, err := FromBytes(want.Bytes())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got.Compare(want) != 0 {
|
||||
t.Error("FromBytes(id.Bytes()) != id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromBytes_InvalidBytes(t *testing.T) {
|
||||
cases := []struct {
|
||||
length int
|
||||
shouldFail bool
|
||||
}{
|
||||
{11, true},
|
||||
{12, false},
|
||||
{13, true},
|
||||
}
|
||||
for _, c := range cases {
|
||||
b := make([]byte, c.length, c.length)
|
||||
_, err := FromBytes(b)
|
||||
if got, want := err != nil, c.shouldFail; got != want {
|
||||
t.Errorf("FromBytes() error got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestID_Compare(t *testing.T) {
|
||||
pairs := []struct {
|
||||
left ID
|
||||
right ID
|
||||
expected int
|
||||
}{
|
||||
{IDs[1].id, IDs[0].id, -1},
|
||||
{ID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, IDs[2].id, -1},
|
||||
{IDs[0].id, IDs[0].id, 0},
|
||||
}
|
||||
for _, p := range pairs {
|
||||
if p.expected != p.left.Compare(p.right) {
|
||||
t.Errorf("%s Compare to %s should return %d", p.left, p.right, p.expected)
|
||||
}
|
||||
if -1*p.expected != p.right.Compare(p.left) {
|
||||
t.Errorf("%s Compare to %s should return %d", p.right, p.left, -1*p.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var IDList = []ID{IDs[0].id, IDs[1].id, IDs[2].id}
|
||||
|
||||
func TestSorter_Len(t *testing.T) {
|
||||
if got, want := sorter([]ID{}).Len(), 0; got != want {
|
||||
t.Errorf("Len() %v, want %v", got, want)
|
||||
}
|
||||
if got, want := sorter(IDList).Len(), 3; got != want {
|
||||
t.Errorf("Len() %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSorter_Less(t *testing.T) {
|
||||
sorter := sorter(IDList)
|
||||
if !sorter.Less(1, 0) {
|
||||
t.Errorf("Less(1, 0) not true")
|
||||
}
|
||||
if sorter.Less(2, 1) {
|
||||
t.Errorf("Less(2, 1) true")
|
||||
}
|
||||
if sorter.Less(0, 0) {
|
||||
t.Errorf("Less(0, 0) true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSorter_Swap(t *testing.T) {
|
||||
ids := make([]ID, 0)
|
||||
ids = append(ids, IDList...)
|
||||
sorter := sorter(ids)
|
||||
sorter.Swap(0, 1)
|
||||
if got, want := ids[0], IDList[1]; !reflect.DeepEqual(got, want) {
|
||||
t.Error("ids[0] != IDList[1]")
|
||||
}
|
||||
if got, want := ids[1], IDList[0]; !reflect.DeepEqual(got, want) {
|
||||
t.Error("ids[1] != IDList[0]")
|
||||
}
|
||||
sorter.Swap(2, 2)
|
||||
if got, want := ids[2], IDList[2]; !reflect.DeepEqual(got, want) {
|
||||
t.Error("ids[2], IDList[2]")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSort(t *testing.T) {
|
||||
ids := make([]ID, 0)
|
||||
ids = append(ids, IDList...)
|
||||
Sort(ids)
|
||||
if got, want := ids, []ID{IDList[1], IDList[2], IDList[0]}; !reflect.DeepEqual(got, want) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue