github.com/influx6/npkg@v0.8.8/nxid/id_test.go (about) 1 package nxid 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "reflect" 9 "testing" 10 "time" 11 ) 12 13 type IDParts struct { 14 id ID 15 timestamp int64 16 machine []byte 17 pid uint16 18 counter int32 19 } 20 21 var IDs = []IDParts{ 22 IDParts{ 23 ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}, 24 1300816219, 25 []byte{0x60, 0xf4, 0x86}, 26 0xe428, 27 4271561, 28 }, 29 IDParts{ 30 ID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 31 0, 32 []byte{0x00, 0x00, 0x00}, 33 0x0000, 34 0, 35 }, 36 IDParts{ 37 ID{0x00, 0x00, 0x00, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x00, 0x00, 0x01}, 38 0, 39 []byte{0xaa, 0xbb, 0xcc}, 40 0xddee, 41 1, 42 }, 43 } 44 45 func TestIDPartsExtraction(t *testing.T) { 46 for i, v := range IDs { 47 t.Run(fmt.Sprintf("Test%d", i), func(t *testing.T) { 48 if got, want := v.id.Time(), time.Unix(v.timestamp, 0); got != want { 49 t.Errorf("Time() = %v, want %v", got, want) 50 } 51 if got, want := v.id.Machine(), v.machine; !bytes.Equal(got, want) { 52 t.Errorf("Machine() = %v, want %v", got, want) 53 } 54 if got, want := v.id.Pid(), v.pid; got != want { 55 t.Errorf("Pid() = %v, want %v", got, want) 56 } 57 if got, want := v.id.Counter(), v.counter; got != want { 58 t.Errorf("Counter() = %v, want %v", got, want) 59 } 60 }) 61 } 62 } 63 64 func TestNew(t *testing.T) { 65 // Generate 10 ids 66 ids := make([]ID, 10) 67 for i := 0; i < 10; i++ { 68 ids[i] = New() 69 } 70 for i := 1; i < 10; i++ { 71 prevID := ids[i-1] 72 id := ids[i] 73 // Test for uniqueness among all other 9 generated ids 74 for j, tid := range ids { 75 if j != i { 76 if id.Compare(tid) == 0 { 77 t.Errorf("generated Id is not unique (%d/%d)", i, j) 78 } 79 } 80 } 81 // Check that timestamp was incremented and is within 30 seconds of the previous one 82 secs := id.Time().Sub(prevID.Time()).Seconds() 83 if secs < 0 || secs > 30 { 84 t.Error("wrong timestamp in generated Id") 85 } 86 // Check that machine ids are the same 87 if !bytes.Equal(id.Machine(), prevID.Machine()) { 88 t.Error("machine Id not equal") 89 } 90 // Check that pids are the same 91 if id.Pid() != prevID.Pid() { 92 t.Error("pid not equal") 93 } 94 // Test for proper increment 95 if got, want := int(id.Counter()-prevID.Counter()), 1; got != want { 96 t.Errorf("wrong increment in generated Id, delta=%v, want %v", got, want) 97 } 98 } 99 } 100 101 func TestIDString(t *testing.T) { 102 id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9} 103 if got, want := id.String(), "9m4e2mr0ui3e8a215n4g"; got != want { 104 t.Errorf("String() = %v, want %v", got, want) 105 } 106 } 107 108 func TestFromString(t *testing.T) { 109 got, err := FromString("9m4e2mr0ui3e8a215n4g") 110 if err != nil { 111 t.Fatal(err) 112 } 113 want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9} 114 if got != want { 115 t.Errorf("FromString() = %v, want %v", got, want) 116 } 117 } 118 119 func TestFromStringInvalid(t *testing.T) { 120 _, err := FromString("invalid") 121 if err != ErrInvalidID { 122 t.Errorf("FromString(invalid) err=%v, want %v", err, ErrInvalidID) 123 } 124 } 125 126 type jsonType struct { 127 ID *ID 128 Str string 129 } 130 131 func TestIDJSONMarshaling(t *testing.T) { 132 id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9} 133 v := jsonType{ID: &id, Str: "test"} 134 data, err := json.Marshal(&v) 135 if err != nil { 136 t.Fatal(err) 137 } 138 if got, want := string(data), `{"Id":"9m4e2mr0ui3e8a215n4g","Str":"test"}`; got != want { 139 t.Errorf("json.Marshal() = %v, want %v", got, want) 140 } 141 } 142 143 func TestIDJSONUnmarshaling(t *testing.T) { 144 data := []byte(`{"Id":"9m4e2mr0ui3e8a215n4g","Str":"test"}`) 145 v := jsonType{} 146 err := json.Unmarshal(data, &v) 147 if err != nil { 148 t.Fatal(err) 149 } 150 want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9} 151 if got := *v.ID; got.Compare(want) != 0 { 152 t.Errorf("json.Unmarshal() = %v, want %v", got, want) 153 } 154 155 } 156 157 func TestIDJSONUnmarshalingError(t *testing.T) { 158 v := jsonType{} 159 err := json.Unmarshal([]byte(`{"Id":"9M4E2MR0UI3E8A215N4G"}`), &v) 160 if err != ErrInvalidID { 161 t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID) 162 } 163 err = json.Unmarshal([]byte(`{"Id":"TYjhW2D0huQoQS"}`), &v) 164 if err != ErrInvalidID { 165 t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID) 166 } 167 err = json.Unmarshal([]byte(`{"Id":"TYjhW2D0huQoQS3kdk"}`), &v) 168 if err != ErrInvalidID { 169 t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID) 170 } 171 } 172 173 func TestIDDriverValue(t *testing.T) { 174 id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9} 175 got, err := id.Value() 176 if err != nil { 177 t.Fatal(err) 178 } 179 if want := "9m4e2mr0ui3e8a215n4g"; got != want { 180 t.Errorf("Value() = %v, want %v", got, want) 181 } 182 } 183 184 func TestIDDriverScan(t *testing.T) { 185 got := ID{} 186 err := got.Scan("9m4e2mr0ui3e8a215n4g") 187 if err != nil { 188 t.Fatal(err) 189 } 190 want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9} 191 if got.Compare(want) != 0 { 192 t.Errorf("Scan() = %v, want %v", got, want) 193 } 194 } 195 196 func TestIDDriverScanError(t *testing.T) { 197 id := ID{} 198 if got, want := id.Scan(0), errors.New("nxid: scanning unsupported type: int"); !reflect.DeepEqual(got, want) { 199 t.Errorf("Scan() err=%v, want %v", got, want) 200 } 201 if got, want := id.Scan("0"), ErrInvalidID; got != want { 202 t.Errorf("Scan() err=%v, want %v", got, want) 203 } 204 } 205 206 func TestIDDriverScanByteFromDatabase(t *testing.T) { 207 got := ID{} 208 bs := []byte("9m4e2mr0ui3e8a215n4g") 209 err := got.Scan(bs) 210 if err != nil { 211 t.Fatal(err) 212 } 213 want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9} 214 if got.Compare(want) != 0 { 215 t.Errorf("Scan() = %v, want %v", got, want) 216 } 217 } 218 219 func BenchmarkNew(b *testing.B) { 220 b.RunParallel(func(pb *testing.PB) { 221 for pb.Next() { 222 _ = New() 223 } 224 }) 225 } 226 227 func BenchmarkNewString(b *testing.B) { 228 b.RunParallel(func(pb *testing.PB) { 229 for pb.Next() { 230 _ = New().String() 231 } 232 }) 233 } 234 235 func BenchmarkFromString(b *testing.B) { 236 b.RunParallel(func(pb *testing.PB) { 237 for pb.Next() { 238 _, _ = FromString("9m4e2mr0ui3e8a215n4g") 239 } 240 }) 241 } 242 243 // func BenchmarkUUIDv1(b *testing.B) { 244 // b.RunParallel(func(pb *testing.PB) { 245 // for pb.Next() { 246 // _ = uuid.NewV1().String() 247 // } 248 // }) 249 // } 250 251 // func BenchmarkUUIDv4(b *testing.B) { 252 // b.RunParallel(func(pb *testing.PB) { 253 // for pb.Next() { 254 // _ = uuid.NewV4().String() 255 // } 256 // }) 257 // } 258 259 func TestID_IsNil(t *testing.T) { 260 tests := []struct { 261 name string 262 id ID 263 want bool 264 }{ 265 { 266 name: "Id not nil", 267 id: New(), 268 want: false, 269 }, 270 { 271 name: "Nil Id", 272 id: ID{}, 273 want: true, 274 }, 275 } 276 for _, tt := range tests { 277 tt := tt 278 t.Run(tt.name, func(t *testing.T) { 279 if got, want := tt.id.IsNil(), tt.want; got != want { 280 t.Errorf("IsNil() = %v, want %v", got, want) 281 } 282 }) 283 } 284 } 285 286 func TestNilID(t *testing.T) { 287 got := ID{} 288 if want := NilID(); !reflect.DeepEqual(got, want) { 289 t.Error("NilID() not equal Id{}") 290 } 291 } 292 293 func TestNilID_IsNil(t *testing.T) { 294 if !NilID().IsNil() { 295 t.Error("NilID().IsNil() is not true") 296 } 297 } 298 299 func TestFromBytes_Invariant(t *testing.T) { 300 want := New() 301 got, err := FromBytes(want.Bytes()) 302 if err != nil { 303 t.Fatal(err) 304 } 305 if got.Compare(want) != 0 { 306 t.Error("FromBytes(id.Bytes()) != id") 307 } 308 } 309 310 func TestFromBytes_InvalidBytes(t *testing.T) { 311 cases := []struct { 312 length int 313 shouldFail bool 314 }{ 315 {11, true}, 316 {12, false}, 317 {13, true}, 318 } 319 for _, c := range cases { 320 b := make([]byte, c.length, c.length) 321 _, err := FromBytes(b) 322 if got, want := err != nil, c.shouldFail; got != want { 323 t.Errorf("FromBytes() error got %v, want %v", got, want) 324 } 325 } 326 } 327 328 func TestID_Compare(t *testing.T) { 329 pairs := []struct { 330 left ID 331 right ID 332 expected int 333 }{ 334 {IDs[1].id, IDs[0].id, -1}, 335 {ID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, IDs[2].id, -1}, 336 {IDs[0].id, IDs[0].id, 0}, 337 } 338 for _, p := range pairs { 339 if p.expected != p.left.Compare(p.right) { 340 t.Errorf("%s Compare to %s should return %d", p.left, p.right, p.expected) 341 } 342 if -1*p.expected != p.right.Compare(p.left) { 343 t.Errorf("%s Compare to %s should return %d", p.right, p.left, -1*p.expected) 344 } 345 } 346 } 347 348 var IDList = []ID{IDs[0].id, IDs[1].id, IDs[2].id} 349 350 func TestSorter_Len(t *testing.T) { 351 if got, want := sorter([]ID{}).Len(), 0; got != want { 352 t.Errorf("Len() %v, want %v", got, want) 353 } 354 if got, want := sorter(IDList).Len(), 3; got != want { 355 t.Errorf("Len() %v, want %v", got, want) 356 } 357 } 358 359 func TestSorter_Less(t *testing.T) { 360 sorter := sorter(IDList) 361 if !sorter.Less(1, 0) { 362 t.Errorf("Less(1, 0) not true") 363 } 364 if sorter.Less(2, 1) { 365 t.Errorf("Less(2, 1) true") 366 } 367 if sorter.Less(0, 0) { 368 t.Errorf("Less(0, 0) true") 369 } 370 } 371 372 func TestSorter_Swap(t *testing.T) { 373 ids := make([]ID, 0) 374 ids = append(ids, IDList...) 375 sorter := sorter(ids) 376 sorter.Swap(0, 1) 377 if got, want := ids[0], IDList[1]; !reflect.DeepEqual(got, want) { 378 t.Error("ids[0] != IDList[1]") 379 } 380 if got, want := ids[1], IDList[0]; !reflect.DeepEqual(got, want) { 381 t.Error("ids[1] != IDList[0]") 382 } 383 sorter.Swap(2, 2) 384 if got, want := ids[2], IDList[2]; !reflect.DeepEqual(got, want) { 385 t.Error("ids[2], IDList[2]") 386 } 387 } 388 389 func TestSort(t *testing.T) { 390 ids := make([]ID, 0) 391 ids = append(ids, IDList...) 392 Sort(ids) 393 if got, want := ids, []ID{IDList[1], IDList[2], IDList[0]}; !reflect.DeepEqual(got, want) { 394 t.Fail() 395 } 396 }