goext/mongo/bson/primitive/objectid_test.go

260 lines
5.8 KiB
Go
Raw Normal View History

2023-06-18 15:50:55 +02:00
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package primitive
import (
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/internal/testutil/assert"
)
func TestNew(t *testing.T) {
// Ensure that objectid.NewObjectID() doesn't panic.
NewObjectID()
}
func TestString(t *testing.T) {
id := NewObjectID()
require.Contains(t, id.String(), id.Hex())
}
func BenchmarkHex(b *testing.B) {
id := NewObjectID()
for i := 0; i < b.N; i++ {
id.Hex()
}
}
func TestFromHex_RoundTrip(t *testing.T) {
before := NewObjectID()
after, err := ObjectIDFromHex(before.Hex())
require.NoError(t, err)
require.Equal(t, before, after)
}
func TestFromHex_InvalidHex(t *testing.T) {
_, err := ObjectIDFromHex("this is not a valid hex string!")
require.Error(t, err)
}
func TestFromHex_WrongLength(t *testing.T) {
_, err := ObjectIDFromHex("deadbeef")
require.Equal(t, ErrInvalidHex, err)
}
func TestIsValidObjectID(t *testing.T) {
testCases := []struct {
givenID string
expected bool
}{
{
givenID: "5ef7fdd91c19e3222b41b839",
expected: true,
},
{
givenID: "5ef7fdd91c19e3222b41b83",
expected: false,
},
}
for _, testcase := range testCases {
got := IsValidObjectID(testcase.givenID)
assert.Equal(t, testcase.expected, got, "expected hex string to be valid ObjectID: %v, got %v", testcase.expected, got)
}
}
func TestTimeStamp(t *testing.T) {
testCases := []struct {
Hex string
Expected string
}{
{
"000000001111111111111111",
"1970-01-01 00:00:00 +0000 UTC",
},
{
"7FFFFFFF1111111111111111",
"2038-01-19 03:14:07 +0000 UTC",
},
{
"800000001111111111111111",
"2038-01-19 03:14:08 +0000 UTC",
},
{
"FFFFFFFF1111111111111111",
"2106-02-07 06:28:15 +0000 UTC",
},
}
for _, testcase := range testCases {
id, err := ObjectIDFromHex(testcase.Hex)
require.NoError(t, err)
secs := int64(binary.BigEndian.Uint32(id[0:4]))
timestamp := time.Unix(secs, 0).UTC()
require.Equal(t, testcase.Expected, timestamp.String())
}
}
func TestCreateFromTime(t *testing.T) {
testCases := []struct {
time string
Expected string
}{
{
"1970-01-01T00:00:00.000Z",
"00000000",
},
{
"2038-01-19T03:14:07.000Z",
"7fffffff",
},
{
"2038-01-19T03:14:08.000Z",
"80000000",
},
{
"2106-02-07T06:28:15.000Z",
"ffffffff",
},
}
layout := "2006-01-02T15:04:05.000Z"
for _, testcase := range testCases {
time, err := time.Parse(layout, testcase.time)
require.NoError(t, err)
id := NewObjectIDFromTimestamp(time)
timeStr := hex.EncodeToString(id[0:4])
require.Equal(t, testcase.Expected, timeStr)
}
}
func TestGenerationTime(t *testing.T) {
testCases := []struct {
hex string
Expected string
}{
{
"000000001111111111111111",
"1970-01-01 00:00:00 +0000 UTC",
},
{
"7FFFFFFF1111111111111111",
"2038-01-19 03:14:07 +0000 UTC",
},
{
"800000001111111111111111",
"2038-01-19 03:14:08 +0000 UTC",
},
{
"FFFFFFFF1111111111111111",
"2106-02-07 06:28:15 +0000 UTC",
},
}
for _, testcase := range testCases {
id, err := ObjectIDFromHex(testcase.hex)
require.NoError(t, err)
genTime := id.Timestamp()
require.Equal(t, testcase.Expected, genTime.String())
}
}
func TestCounterOverflow(t *testing.T) {
objectIDCounter = 0xFFFFFFFF
NewObjectID()
require.Equal(t, uint32(0), objectIDCounter)
}
func TestObjectID_MarshalJSONMap(t *testing.T) {
type mapOID struct {
Map map[ObjectID]string
}
oid := NewObjectID()
expectedJSON := []byte(fmt.Sprintf(`{"Map":{%q:"foo"}}`, oid.Hex()))
data := mapOID{
Map: map[ObjectID]string{oid: "foo"},
}
out, err := json.Marshal(&data)
require.NoError(t, err)
require.Equal(t, expectedJSON, out)
}
func TestObjectID_UnmarshalJSONMap(t *testing.T) {
type mapOID struct {
Map map[ObjectID]string
}
oid := NewObjectID()
mapOIDJSON := []byte(fmt.Sprintf(`{"Map":{%q:"foo"}}`, oid.Hex()))
expectedData := mapOID{
Map: map[ObjectID]string{oid: "foo"},
}
data := mapOID{}
err := json.Unmarshal(mapOIDJSON, &data)
require.NoError(t, err)
require.Equal(t, expectedData, data)
}
func TestObjectID_UnmarshalJSON(t *testing.T) {
oid := NewObjectID()
hexJSON := fmt.Sprintf(`{"foo": %q}`, oid.Hex())
extJSON := fmt.Sprintf(`{"foo": {"$oid": %q}}`, oid.Hex())
emptyStringJSON := `{"foo": ""}`
nullJSON := `{"foo": null}`
testCases := []struct {
name string
jsonString string
expected ObjectID
}{
{"hex bytes", hexJSON, oid},
{"extended JSON", extJSON, oid},
{"empty string", emptyStringJSON, NilObjectID},
{"null", nullJSON, NilObjectID},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var got map[string]ObjectID
err := json.Unmarshal([]byte(tc.jsonString), &got)
assert.Nil(t, err, "Unmarshal error: %v", err)
gotOid := got["foo"]
assert.Equal(t, tc.expected, gotOid, "expected ObjectID %s, got %s", tc.expected, gotOid)
})
}
}
func TestObjectID_MarshalText(t *testing.T) {
oid := ObjectID{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB}
b, err := oid.MarshalText()
assert.Nil(t, err, "MarshalText error: %v", err)
want := "000102030405060708090a0b"
got := string(b)
assert.Equal(t, want, got, "want %v, got %v", want, got)
}
func TestObjectID_UnmarshalText(t *testing.T) {
var oid ObjectID
err := oid.UnmarshalText([]byte("000102030405060708090a0b"))
assert.Nil(t, err, "UnmarshalText error: %v", err)
want := ObjectID{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB}
assert.Equal(t, want, oid, "want %v, got %v", want, oid)
}