From d415a2a830287fe37c71ecf9799f20f641144e7f Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Wed, 6 Dec 2023 19:51:28 +0100 Subject: [PATCH] chore(refact): assembled bson-dependent methods and tests This code layout rehashing is a preparatory work to remove the hard dependency on the mongo driver. Signed-off-by: Frederic BIDON --- bson.go | 39 --- bson_test.go | 7 + date.go | 24 -- date_test.go | 11 - default.go | 385 ----------------------------- default_test.go | 238 ++++++++++-------- duration.go | 24 -- duration_test.go | 10 - format.go | 27 +- ifaces.go | 29 +++ mongo.go | 622 +++++++++++++++++++++++++++++++++++++++++++++++ mongo_test.go | 282 +++++++++++++++++++++ time.go | 68 ------ time_test.go | 30 --- ulid.go | 24 -- ulid_test.go | 40 --- 16 files changed, 1078 insertions(+), 782 deletions(-) create mode 100644 ifaces.go create mode 100644 mongo.go create mode 100644 mongo_test.go diff --git a/bson.go b/bson.go index 78d6e07..6088d85 100644 --- a/bson.go +++ b/bson.go @@ -18,9 +18,6 @@ import ( "database/sql/driver" "fmt" - "go.mongodb.org/mongo-driver/bson" - - "go.mongodb.org/mongo-driver/bson/bsontype" bsonprim "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -113,42 +110,6 @@ func (id *ObjectId) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON renders the object id as a BSON document -func (id ObjectId) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": bsonprim.ObjectID(id)}) -} - -// UnmarshalBSON reads the objectId from a BSON document -func (id *ObjectId) UnmarshalBSON(data []byte) error { - var obj struct { - Data bsonprim.ObjectID - } - if err := bson.Unmarshal(data, &obj); err != nil { - return err - } - *id = ObjectId(obj.Data) - return nil -} - -// MarshalBSONValue is an interface implemented by types that can marshal themselves -// into a BSON document represented as bytes. The bytes returned must be a valid -// BSON document if the error is nil. -func (id ObjectId) MarshalBSONValue() (bsontype.Type, []byte, error) { - oid := bsonprim.ObjectID(id) - return bson.TypeObjectID, oid[:], nil -} - -// UnmarshalBSONValue is an interface implemented by types that can unmarshal a -// BSON value representation of themselves. The BSON bytes and type can be -// assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it -// wishes to retain the data after returning. -func (id *ObjectId) UnmarshalBSONValue(_ bsontype.Type, data []byte) error { - var oid bsonprim.ObjectID - copy(oid[:], data) - *id = ObjectId(oid) - return nil -} - // DeepCopyInto copies the receiver and writes its value into out. func (id *ObjectId) DeepCopyInto(out *ObjectId) { *out = *id diff --git a/bson_test.go b/bson_test.go index 7a367ad..a4a2c85 100644 --- a/bson_test.go +++ b/bson_test.go @@ -23,10 +23,14 @@ import ( ) func TestBSONObjectId_fullCycle(t *testing.T) { + const str = "507f1f77bcf86cd799439011" + id := NewObjectId("507f1f77bcf86cd799439011") bytes, err := id.MarshalText() require.NoError(t, err) + require.True(t, IsBSONObjectID(str)) + var idCopy ObjectId err = idCopy.Scan(bytes) @@ -37,6 +41,8 @@ func TestBSONObjectId_fullCycle(t *testing.T) { require.NoError(t, err) assert.Equal(t, id, idCopy) + require.Equal(t, str, idCopy.String()) + jsonBytes, err := id.MarshalJSON() require.NoError(t, err) @@ -50,6 +56,7 @@ func TestBSONObjectId_fullCycle(t *testing.T) { err = bson.Unmarshal(bsonBytes, &idCopy) require.NoError(t, err) assert.Equal(t, id, idCopy) + } func TestDeepCopyObjectId(t *testing.T) { diff --git a/date.go b/date.go index a8f52ff..b48e701 100644 --- a/date.go +++ b/date.go @@ -19,8 +19,6 @@ import ( "encoding/json" "fmt" "time" - - "go.mongodb.org/mongo-driver/bson" ) func init() { @@ -114,28 +112,6 @@ func (d *Date) UnmarshalJSON(data []byte) error { return nil } -func (d Date) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": d.String()}) -} - -func (d *Date) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if data, ok := m["data"].(string); ok { - rd, err := time.ParseInLocation(RFC3339FullDate, data, DefaultTimeLocation) - if err != nil { - return err - } - *d = Date(rd) - return nil - } - - return fmt.Errorf("couldn't unmarshal bson bytes value as Date: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (d *Date) DeepCopyInto(out *Date) { *out = *d diff --git a/date_test.go b/date_test.go index bd7c3ca..1083665 100644 --- a/date_test.go +++ b/date_test.go @@ -24,7 +24,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" ) var _ sql.Scanner = &Date{} @@ -57,16 +56,6 @@ func TestDate(t *testing.T) { require.NoError(t, err) assert.Equal(t, bj, b) - dateOriginal := Date(time.Date(2014, 10, 10, 0, 0, 0, 0, time.UTC)) - - bsonData, err := bson.Marshal(&dateOriginal) - require.NoError(t, err) - - var dateCopy Date - err = bson.Unmarshal(bsonData, &dateCopy) - require.NoError(t, err) - assert.Equal(t, dateOriginal, dateCopy) - var dateZero Date err = dateZero.UnmarshalJSON([]byte(jsonNull)) require.NoError(t, err) diff --git a/default.go b/default.go index e53df17..56f76ad 100644 --- a/default.go +++ b/default.go @@ -28,7 +28,6 @@ import ( "strings" "github.com/google/uuid" - "go.mongodb.org/mongo-driver/bson" "golang.org/x/net/idna" ) @@ -535,29 +534,6 @@ func (b *Base64) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (b Base64) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": b.String()}) -} - -// UnmarshalBSON document into this value -func (b *Base64) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if bd, ok := m["data"].(string); ok { - vb, err := base64.StdEncoding.DecodeString(bd) - if err != nil { - return err - } - *b = Base64(vb) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as base64: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (b *Base64) DeepCopyInto(out *Base64) { *out = *b @@ -627,25 +603,6 @@ func (u *URI) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u URI) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *URI) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = URI(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as uri: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *URI) DeepCopyInto(out *URI) { *out = *u @@ -715,25 +672,6 @@ func (e *Email) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (e Email) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": e.String()}) -} - -// UnmarshalBSON document into this value -func (e *Email) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *e = Email(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as email: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (e *Email) DeepCopyInto(out *Email) { *out = *e @@ -803,25 +741,6 @@ func (h *Hostname) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (h Hostname) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": h.String()}) -} - -// UnmarshalBSON document into this value -func (h *Hostname) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *h = Hostname(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as hostname: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (h *Hostname) DeepCopyInto(out *Hostname) { *out = *h @@ -891,25 +810,6 @@ func (u *IPv4) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u IPv4) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *IPv4) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = IPv4(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as ipv4: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *IPv4) DeepCopyInto(out *IPv4) { *out = *u @@ -979,25 +879,6 @@ func (u *IPv6) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u IPv6) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *IPv6) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = IPv6(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as ipv6: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *IPv6) DeepCopyInto(out *IPv6) { *out = *u @@ -1067,25 +948,6 @@ func (u *CIDR) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u CIDR) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *CIDR) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = CIDR(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as CIDR: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *CIDR) DeepCopyInto(out *CIDR) { *out = *u @@ -1155,25 +1017,6 @@ func (u *MAC) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u MAC) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *MAC) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = MAC(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as MAC: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *MAC) DeepCopyInto(out *MAC) { *out = *u @@ -1246,25 +1089,6 @@ func (u *UUID) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u UUID) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *UUID) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = UUID(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as UUID: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *UUID) DeepCopyInto(out *UUID) { *out = *u @@ -1337,25 +1161,6 @@ func (u *UUID3) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u UUID3) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *UUID3) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = UUID3(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as UUID3: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *UUID3) DeepCopyInto(out *UUID3) { *out = *u @@ -1428,25 +1233,6 @@ func (u *UUID4) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u UUID4) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *UUID4) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = UUID4(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as UUID4: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *UUID4) DeepCopyInto(out *UUID4) { *out = *u @@ -1519,25 +1305,6 @@ func (u *UUID5) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u UUID5) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *UUID5) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = UUID5(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as UUID5: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *UUID5) DeepCopyInto(out *UUID5) { *out = *u @@ -1610,25 +1377,6 @@ func (u *ISBN) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u ISBN) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *ISBN) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = ISBN(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as ISBN: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *ISBN) DeepCopyInto(out *ISBN) { *out = *u @@ -1701,25 +1449,6 @@ func (u *ISBN10) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u ISBN10) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *ISBN10) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = ISBN10(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as ISBN10: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *ISBN10) DeepCopyInto(out *ISBN10) { *out = *u @@ -1792,25 +1521,6 @@ func (u *ISBN13) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u ISBN13) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *ISBN13) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = ISBN13(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as ISBN13: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *ISBN13) DeepCopyInto(out *ISBN13) { *out = *u @@ -1883,25 +1593,6 @@ func (u *CreditCard) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u CreditCard) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *CreditCard) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = CreditCard(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as CreditCard: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *CreditCard) DeepCopyInto(out *CreditCard) { *out = *u @@ -1974,25 +1665,6 @@ func (u *SSN) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u SSN) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *SSN) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = SSN(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as SSN: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *SSN) DeepCopyInto(out *SSN) { *out = *u @@ -2065,25 +1737,6 @@ func (h *HexColor) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (h HexColor) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": h.String()}) -} - -// UnmarshalBSON document into this value -func (h *HexColor) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *h = HexColor(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as HexColor: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (h *HexColor) DeepCopyInto(out *HexColor) { *out = *h @@ -2156,25 +1809,6 @@ func (r *RGBColor) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (r RGBColor) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": r.String()}) -} - -// UnmarshalBSON document into this value -func (r *RGBColor) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *r = RGBColor(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as RGBColor: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (r *RGBColor) DeepCopyInto(out *RGBColor) { *out = *r @@ -2248,25 +1882,6 @@ func (r *Password) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (r Password) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": r.String()}) -} - -// UnmarshalBSON document into this value -func (r *Password) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *r = Password(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as Password: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (r *Password) DeepCopyInto(out *Password) { *out = *r diff --git a/default_test.go b/default_test.go index ab1d422..344a365 100644 --- a/default_test.go +++ b/default_test.go @@ -30,7 +30,6 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" ) func TestFormatURI(t *testing.T) { @@ -39,10 +38,8 @@ func TestFormatURI(t *testing.T) { testStringFormat(t, &uri, "uri", str, []string{}, []string{"somewhere.com"}) } -func TestFormatEmail(t *testing.T) { - email := Email("somebody@somewhere.com") - str := string("somebodyelse@somewhere.com") - validEmails := []string{ +func validEmails() []string { + return []string{ "blah@gmail.com", "test@d.verylongtoplevel", "email+tag@gmail.com", @@ -62,17 +59,21 @@ func TestFormatEmail(t *testing.T) { "john@com", "api@piston.ninja", } +} - testStringFormat(t, &email, "email", str, validEmails, []string{"somebody@somewhere@com"}) +func TestFormatEmail(t *testing.T) { + email := Email("somebody@somewhere.com") + str := string("somebodyelse@somewhere.com") + + testStringFormat(t, &email, "email", str, validEmails(), []string{"somebody@somewhere@com"}) } -func TestFormatHostname(t *testing.T) { - hostname := Hostname("somewhere.com") - str := string("somewhere.com") +func invalidHostnames() []string { veryLongStr := strings.Repeat("a", 256) longStr := strings.Repeat("a", 64) longAddrSegment := strings.Join([]string{"x", "y", longStr}, ".") - invalidHostnames := []string{ + + return []string{ "somewhere.com!", "user@email.domain", veryLongStr, @@ -144,8 +145,10 @@ func TestFormatHostname(t *testing.T) { "0o07.2.3.4", // unsupported alternated octal notation "localhost:81", } +} - validHostnames := []string{ +func validHostnames() []string { + return []string{ "somewhere.com", "Somewhere.Com", "888.com", @@ -207,9 +210,14 @@ func TestFormatHostname(t *testing.T) { "0300.0250.0340.001", // octal IP v4 "1.2.3.00", // leading 0, valid octal value } +} + +func TestFormatHostname(t *testing.T) { + hostname := Hostname("somewhere.com") + str := string("somewhere.com") - testStringFormat(t, &hostname, "hostname", str, []string{}, invalidHostnames) - testStringFormat(t, &hostname, "hostname", str, validHostnames, []string{}) + testStringFormat(t, &hostname, "hostname", str, []string{}, invalidHostnames()) + testStringFormat(t, &hostname, "hostname", str, validHostnames(), []string{}) } func TestFormatIPv4(t *testing.T) { @@ -237,28 +245,39 @@ func TestFormatMAC(t *testing.T) { testStringFormat(t, &mac, "mac", str, []string{}, []string{"01:02:03:04:05"}) } -func TestFormatUUID3(t *testing.T) { - first3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhere.com")) +func validUUID3s() []string { + other3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhereelse.com")) + + return []string{ + other3.String(), + strings.ReplaceAll(other3.String(), "-", ""), + } +} + +func invalidUUID3s() []string { other3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhereelse.com")) other4 := uuid.Must(uuid.NewRandom()) other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + + return []string{ + "not-a-uuid", + other4.String(), + other5.String(), + strings.ReplaceAll(other4.String(), "-", ""), + strings.ReplaceAll(other5.String(), "-", ""), + strings.Replace(other3.String(), "-", "", 2), + strings.Replace(other4.String(), "-", "", 2), + strings.Replace(other5.String(), "-", "", 2), + } +} + +func TestFormatUUID3(t *testing.T) { + first3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhere.com")) uuid3 := UUID3(first3.String()) - str := other3.String() + str := first3.String() testStringFormat(t, &uuid3, "uuid3", str, - []string{ - other3.String(), - strings.ReplaceAll(other3.String(), "-", ""), - }, - []string{ - "not-a-uuid", - other4.String(), - other5.String(), - strings.ReplaceAll(other4.String(), "-", ""), - strings.ReplaceAll(other5.String(), "-", ""), - strings.Replace(other3.String(), "-", "", 2), - strings.Replace(other4.String(), "-", "", 2), - strings.Replace(other5.String(), "-", "", 2), - }, + validUUID3s(), + invalidUUID3s(), ) // special case for zero UUID @@ -268,28 +287,39 @@ func TestFormatUUID3(t *testing.T) { assert.Equal(t, UUID3(""), uuidZero) } -func TestFormatUUID4(t *testing.T) { - first4 := uuid.Must(uuid.NewRandom()) +func validUUID4s() []string { + other4 := uuid.Must(uuid.NewRandom()) + + return []string{ + other4.String(), + strings.ReplaceAll(other4.String(), "-", ""), + } +} + +func invalidUUID4s() []string { other3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhere.com")) other4 := uuid.Must(uuid.NewRandom()) other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + + return []string{ + "not-a-uuid", + other3.String(), + other5.String(), + strings.ReplaceAll(other3.String(), "-", ""), + strings.ReplaceAll(other5.String(), "-", ""), + strings.Replace(other3.String(), "-", "", 2), + strings.Replace(other4.String(), "-", "", 2), + strings.Replace(other5.String(), "-", "", 2), + } +} +func TestFormatUUID4(t *testing.T) { + first4 := uuid.Must(uuid.NewRandom()) + other4 := uuid.Must(uuid.NewRandom()) uuid4 := UUID4(first4.String()) str := other4.String() testStringFormat(t, &uuid4, "uuid4", str, - []string{ - other4.String(), - strings.ReplaceAll(other4.String(), "-", ""), - }, - []string{ - "not-a-uuid", - other3.String(), - other5.String(), - strings.ReplaceAll(other3.String(), "-", ""), - strings.ReplaceAll(other5.String(), "-", ""), - strings.Replace(other3.String(), "-", "", 2), - strings.Replace(other4.String(), "-", "", 2), - strings.Replace(other5.String(), "-", "", 2), - }, + validUUID4s(), + invalidUUID4s(), ) // special case for zero UUID @@ -299,28 +329,40 @@ func TestFormatUUID4(t *testing.T) { assert.Equal(t, UUID4(""), uuidZero) } -func TestFormatUUID5(t *testing.T) { - first5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhere.com")) +func validUUID5s() []string { + other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + + return []string{ + other5.String(), + strings.ReplaceAll(other5.String(), "-", ""), + } +} + +func invalidUUID5s() []string { other3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhere.com")) other4 := uuid.Must(uuid.NewRandom()) other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + + return []string{ + "not-a-uuid", + other3.String(), + other4.String(), + strings.ReplaceAll(other3.String(), "-", ""), + strings.ReplaceAll(other4.String(), "-", ""), + strings.Replace(other3.String(), "-", "", 2), + strings.Replace(other4.String(), "-", "", 2), + strings.Replace(other5.String(), "-", "", 2), + } +} + +func TestFormatUUID5(t *testing.T) { + first5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhere.com")) + other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) uuid5 := UUID5(first5.String()) str := other5.String() testStringFormat(t, &uuid5, "uuid5", str, - []string{ - other5.String(), - strings.ReplaceAll(other5.String(), "-", ""), - }, - []string{ - "not-a-uuid", - other3.String(), - other4.String(), - strings.ReplaceAll(other3.String(), "-", ""), - strings.ReplaceAll(other4.String(), "-", ""), - strings.Replace(other3.String(), "-", "", 2), - strings.Replace(other4.String(), "-", "", 2), - strings.Replace(other5.String(), "-", "", 2), - }, + validUUID5s(), + invalidUUID5s(), ) // special case for zero UUID @@ -330,8 +372,7 @@ func TestFormatUUID5(t *testing.T) { assert.Equal(t, UUID5(""), uuidZero) } -func TestFormatUUID(t *testing.T) { - first5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhere.com")) +func validUUIDs() []string { other3 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) other4 := uuid.Must(uuid.NewRandom()) other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) @@ -339,26 +380,39 @@ func TestFormatUUID(t *testing.T) { other7 := uuid.Must(uuid.NewV7()) microsoft := "0" + other4.String() + "f" + return []string{ + other3.String(), + other4.String(), + other5.String(), + strings.ReplaceAll(other3.String(), "-", ""), + strings.ReplaceAll(other4.String(), "-", ""), + strings.ReplaceAll(other5.String(), "-", ""), + other6.String(), + other7.String(), + microsoft, + } +} + +func invalidUUIDs() []string { + other3 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + other4 := uuid.Must(uuid.NewRandom()) + other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + + return []string{ + "not-a-uuid", + strings.Replace(other3.String(), "-", "", 2), + strings.Replace(other4.String(), "-", "", 2), + strings.Replace(other5.String(), "-", "", 2), + } +} +func TestFormatUUID(t *testing.T) { + first5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhere.com")) + other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) uuid := UUID(first5.String()) str := other5.String() testStringFormat(t, &uuid, "uuid", str, - []string{ - other3.String(), - other4.String(), - other5.String(), - strings.ReplaceAll(other3.String(), "-", ""), - strings.ReplaceAll(other4.String(), "-", ""), - strings.ReplaceAll(other5.String(), "-", ""), - other6.String(), - other7.String(), - microsoft, - }, - []string{ - "not-a-uuid", - strings.Replace(other3.String(), "-", "", 2), - strings.Replace(other4.String(), "-", "", 2), - strings.Replace(other5.String(), "-", "", 2), - }, + validUUIDs(), + invalidUUIDs(), ) // special case for zero UUID @@ -440,14 +494,6 @@ func TestFormatBase64(t *testing.T) { require.NoError(t, err) assert.Equal(t, bj, b) - bsonData, err := bson.Marshal(subj2) - require.NoError(t, err) - - var b64Copy Base64 - err = bson.Unmarshal(bsonData, &b64Copy) - require.NoError(t, err) - assert.Equal(t, subj2, b64Copy) - testValid(t, "byte", str) testInvalid(t, "byte", "ZWxpemFiZXRocG9zZXk") // missing pad char @@ -478,8 +524,6 @@ type testableFormat interface { encoding.TextUnmarshaler json.Marshaler json.Unmarshaler - bson.Marshaler - bson.Unmarshaler fmt.Stringer sql.Scanner driver.Valuer @@ -517,18 +561,6 @@ func testStringFormat(t *testing.T, what testableFormat, format, with string, va require.NoError(t, err) assert.Equalf(t, bj, b, "[%s]MarshalJSON: expected %v and %v to be value equal as []byte", format, string(b), with) - // bson encoding interface - bsonData, err := bson.Marshal(what) - require.NoError(t, err) - - resetValue(t, format, what) - - err = bson.Unmarshal(bsonData, what) - require.NoError(t, err) - val = reflect.Indirect(reflect.ValueOf(what)) - strVal = val.String() - assert.Equal(t, with, strVal, "[%s]bson.Unmarshal: expected %v and %v to be equal (reset value) ", format, what, with) - // Scanner interface resetValue(t, format, what) err = what.Scan(with) diff --git a/duration.go b/duration.go index 749c4b5..15fcbd5 100644 --- a/duration.go +++ b/duration.go @@ -23,8 +23,6 @@ import ( "strconv" "strings" "time" - - "go.mongodb.org/mongo-driver/bson" ) func init() { @@ -203,28 +201,6 @@ func (d *Duration) UnmarshalJSON(data []byte) error { return nil } -func (d Duration) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": d.String()}) -} - -func (d *Duration) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if data, ok := m["data"].(string); ok { - rd, err := ParseDuration(data) - if err != nil { - return err - } - *d = Duration(rd) - return nil - } - - return fmt.Errorf("couldn't unmarshal bson bytes value as Date: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (d *Duration) DeepCopyInto(out *Duration) { *out = *d diff --git a/duration_test.go b/duration_test.go index d76fa36..83f17ab 100644 --- a/duration_test.go +++ b/duration_test.go @@ -21,7 +21,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" ) func TestDuration(t *testing.T) { @@ -65,15 +64,6 @@ func TestDuration(t *testing.T) { b, err = pp.MarshalJSON() require.NoError(t, err) assert.Equal(t, bj, b) - - dur := Duration(42) - bsonData, err := bson.Marshal(&dur) - require.NoError(t, err) - - var durCopy Duration - err = bson.Unmarshal(bsonData, &durCopy) - require.NoError(t, err) - assert.Equal(t, dur, durCopy) } func testDurationParser(t *testing.T, toParse string, expected time.Duration) { diff --git a/format.go b/format.go index 73f83af..c79063a 100644 --- a/format.go +++ b/format.go @@ -18,6 +18,7 @@ import ( "encoding" "fmt" "reflect" + "slices" "strings" "sync" "time" @@ -32,27 +33,6 @@ var Default = NewSeededFormats(nil, nil) // Validator represents a validator for a string format. type Validator func(string) bool -// Format represents a string format. -// -// All implementations of Format provide a string representation and text -// marshaling/unmarshaling interface to be used by encoders (e.g. encoding/json). -type Format interface { - String() string - encoding.TextMarshaler - encoding.TextUnmarshaler -} - -// Registry is a registry of string formats, with a validation method. -type Registry interface { - Add(string, Format, Validator) bool - DelByName(string) bool - GetType(string) (reflect.Type, bool) - ContainsName(string) bool - Validates(string, string) bool - Parse(string, string) (any, error) - MapStructureHookFunc() mapstructure.DecodeHookFunc -} - // NewFormats creates a new formats registry seeded with the values from the default func NewFormats() Registry { //nolint:forcetypeassert @@ -64,10 +44,9 @@ func NewSeededFormats(seeds []knownFormat, normalizer NameNormalizer) Registry { if normalizer == nil { normalizer = DefaultNameNormalizer } - // copy here, don't modify original - d := append([]knownFormat(nil), seeds...) + // copy here, don't modify the original return &defaultFormats{ - data: d, + data: slices.Clone(seeds), normalizeName: normalizer, } } diff --git a/ifaces.go b/ifaces.go new file mode 100644 index 0000000..ecd7efa --- /dev/null +++ b/ifaces.go @@ -0,0 +1,29 @@ +package strfmt + +import ( + "encoding" + "reflect" + + "github.com/go-viper/mapstructure/v2" +) + +// Format represents a string format. +// +// All implementations of Format provide a string representation and text +// marshaling/unmarshaling interface to be used by encoders (e.g. encoding/json). +type Format interface { + String() string + encoding.TextMarshaler + encoding.TextUnmarshaler +} + +// Registry is a registry of string formats, with a validation method. +type Registry interface { + Add(string, Format, Validator) bool + DelByName(string) bool + GetType(string) (reflect.Type, bool) + ContainsName(string) bool + Validates(string, string) bool + Parse(string, string) (any, error) + MapStructureHookFunc() mapstructure.DecodeHookFunc +} diff --git a/mongo.go b/mongo.go new file mode 100644 index 0000000..b616680 --- /dev/null +++ b/mongo.go @@ -0,0 +1,622 @@ +package strfmt + +import ( + "encoding/base64" + "encoding/binary" + "fmt" + "time" + + "github.com/oklog/ulid" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" + bsonprim "go.mongodb.org/mongo-driver/bson/primitive" +) + +var ( + _ bson.Marshaler = Date{} + _ bson.Unmarshaler = &Date{} + _ bson.Marshaler = Base64{} + _ bson.Unmarshaler = &Base64{} + _ bson.Marshaler = Duration(0) + _ bson.Unmarshaler = (*Duration)(nil) + _ bson.Marshaler = DateTime{} + _ bson.Unmarshaler = &DateTime{} + _ bson.Marshaler = ULID{} + _ bson.Unmarshaler = &ULID{} + _ bson.Marshaler = URI("") + _ bson.Unmarshaler = (*URI)(nil) + _ bson.Marshaler = Email("") + _ bson.Unmarshaler = (*Email)(nil) + _ bson.Marshaler = Hostname("") + _ bson.Unmarshaler = (*Hostname)(nil) + _ bson.Marshaler = IPv4("") + _ bson.Unmarshaler = (*IPv4)(nil) + _ bson.Marshaler = IPv6("") + _ bson.Unmarshaler = (*IPv6)(nil) + _ bson.Marshaler = CIDR("") + _ bson.Unmarshaler = (*CIDR)(nil) + _ bson.Marshaler = MAC("") + _ bson.Unmarshaler = (*MAC)(nil) + _ bson.Marshaler = Password("") + _ bson.Unmarshaler = (*Password)(nil) + _ bson.Marshaler = UUID("") + _ bson.Unmarshaler = (*UUID)(nil) + _ bson.Marshaler = UUID3("") + _ bson.Unmarshaler = (*UUID3)(nil) + _ bson.Marshaler = UUID4("") + _ bson.Unmarshaler = (*UUID4)(nil) + _ bson.Marshaler = UUID5("") + _ bson.Unmarshaler = (*UUID5)(nil) + _ bson.Marshaler = ISBN("") + _ bson.Unmarshaler = (*ISBN)(nil) + _ bson.Marshaler = ISBN10("") + _ bson.Unmarshaler = (*ISBN10)(nil) + _ bson.Marshaler = ISBN13("") + _ bson.Unmarshaler = (*ISBN13)(nil) + _ bson.Marshaler = CreditCard("") + _ bson.Unmarshaler = (*CreditCard)(nil) + _ bson.Marshaler = SSN("") + _ bson.Unmarshaler = (*SSN)(nil) + _ bson.Marshaler = HexColor("") + _ bson.Unmarshaler = (*HexColor)(nil) + _ bson.Marshaler = RGBColor("") + _ bson.Unmarshaler = (*RGBColor)(nil) + _ bson.Marshaler = ObjectId{} + _ bson.Unmarshaler = &ObjectId{} + + _ bson.ValueMarshaler = DateTime{} + _ bson.ValueUnmarshaler = &DateTime{} + _ bson.ValueMarshaler = ObjectId{} + _ bson.ValueUnmarshaler = &ObjectId{} +) + +const ( + millisec = 1000 + microsec = 1_000_000 + bsonDateTimeSize = 8 +) + +func (d Date) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": d.String()}) +} + +func (d *Date) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if data, ok := m["data"].(string); ok { + rd, err := time.ParseInLocation(RFC3339FullDate, data, DefaultTimeLocation) + if err != nil { + return err + } + *d = Date(rd) + return nil + } + + return fmt.Errorf("couldn't unmarshal bson bytes value as Date: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (b Base64) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": b.String()}) +} + +// UnmarshalBSON document into this value +func (b *Base64) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if bd, ok := m["data"].(string); ok { + vb, err := base64.StdEncoding.DecodeString(bd) + if err != nil { + return err + } + *b = Base64(vb) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as base64: %w", ErrFormat) +} + +func (d Duration) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": d.String()}) +} + +func (d *Duration) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if data, ok := m["data"].(string); ok { + rd, err := ParseDuration(data) + if err != nil { + return err + } + *d = Duration(rd) + return nil + } + + return fmt.Errorf("couldn't unmarshal bson bytes value as Date: %w", ErrFormat) +} + +// MarshalBSON renders the DateTime as a BSON document +func (t DateTime) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": t}) +} + +// UnmarshalBSON reads the DateTime from a BSON document +func (t *DateTime) UnmarshalBSON(data []byte) error { + var obj struct { + Data DateTime + } + + if err := bson.Unmarshal(data, &obj); err != nil { + return err + } + + *t = obj.Data + + return nil +} + +// MarshalBSONValue is an interface implemented by types that can marshal themselves +// into a BSON document represented as bytes. The bytes returned must be a valid +// BSON document if the error is nil. +// +// Marshals a DateTime as a bson.TypeDateTime, an int64 representing +// milliseconds since epoch. +func (t DateTime) MarshalBSONValue() (bsontype.Type, []byte, error) { + // UnixNano cannot be used directly, the result of calling UnixNano on the zero + // Time is undefined. Thats why we use time.Nanosecond() instead. + + tNorm := NormalizeTimeForMarshal(time.Time(t)) + i64 := tNorm.Unix()*millisec + int64(tNorm.Nanosecond())/microsec + buf := make([]byte, bsonDateTimeSize) + binary.LittleEndian.PutUint64(buf, uint64(i64)) //nolint:gosec // it's okay to handle negative int64 this way + + return bson.TypeDateTime, buf, nil +} + +// UnmarshalBSONValue is an interface implemented by types that can unmarshal a +// BSON value representation of themselves. The BSON bytes and type can be +// assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it +// wishes to retain the data after returning. +func (t *DateTime) UnmarshalBSONValue(tpe bsontype.Type, data []byte) error { + if tpe == bson.TypeNull { + *t = DateTime{} + return nil + } + + if len(data) != bsonDateTimeSize { + return fmt.Errorf("bson date field length not exactly %d bytes: %w", bsonDateTimeSize, ErrFormat) + } + + i64 := int64(binary.LittleEndian.Uint64(data)) //nolint:gosec // it's okay if we overflow and get a negative datetime + *t = DateTime(time.Unix(i64/millisec, i64%millisec*microsec)) + + return nil +} + +// MarshalBSON document from this value +func (u ULID) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *ULID) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + id, err := ulid.ParseStrict(ud) + if err != nil { + return fmt.Errorf("couldn't parse bson bytes as ULID: %w: %w", err, ErrFormat) + } + u.ULID = id + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as ULID: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u URI) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *URI) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = URI(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as uri: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (e Email) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": e.String()}) +} + +// UnmarshalBSON document into this value +func (e *Email) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *e = Email(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as email: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (h Hostname) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": h.String()}) +} + +// UnmarshalBSON document into this value +func (h *Hostname) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *h = Hostname(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as hostname: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u IPv4) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *IPv4) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = IPv4(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as ipv4: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u IPv6) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *IPv6) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = IPv6(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as ipv6: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u CIDR) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *CIDR) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = CIDR(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as CIDR: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u MAC) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *MAC) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = MAC(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as MAC: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (r Password) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": r.String()}) +} + +// UnmarshalBSON document into this value +func (r *Password) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *r = Password(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as Password: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u UUID) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *UUID) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = UUID(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as UUID: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u UUID3) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *UUID3) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = UUID3(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as UUID3: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u UUID4) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *UUID4) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = UUID4(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as UUID4: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u UUID5) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *UUID5) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = UUID5(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as UUID5: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u ISBN) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *ISBN) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = ISBN(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as ISBN: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u ISBN10) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *ISBN10) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = ISBN10(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as ISBN10: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u ISBN13) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *ISBN13) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = ISBN13(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as ISBN13: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u CreditCard) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *CreditCard) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = CreditCard(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as CreditCard: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u SSN) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *SSN) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = SSN(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as SSN: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (h HexColor) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": h.String()}) +} + +// UnmarshalBSON document into this value +func (h *HexColor) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *h = HexColor(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as HexColor: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (r RGBColor) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": r.String()}) +} + +// UnmarshalBSON document into this value +func (r *RGBColor) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *r = RGBColor(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as RGBColor: %w", ErrFormat) +} + +// MarshalBSON renders the object id as a BSON document +func (id ObjectId) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": bsonprim.ObjectID(id)}) +} + +// UnmarshalBSON reads the objectId from a BSON document +func (id *ObjectId) UnmarshalBSON(data []byte) error { + var obj struct { + Data bsonprim.ObjectID + } + if err := bson.Unmarshal(data, &obj); err != nil { + return err + } + *id = ObjectId(obj.Data) + return nil +} + +// MarshalBSONValue is an interface implemented by types that can marshal themselves +// into a BSON document represented as bytes. The bytes returned must be a valid +// BSON document if the error is nil. +func (id ObjectId) MarshalBSONValue() (bsontype.Type, []byte, error) { + oid := bsonprim.ObjectID(id) + return bson.TypeObjectID, oid[:], nil +} + +// UnmarshalBSONValue is an interface implemented by types that can unmarshal a +// BSON value representation of themselves. The BSON bytes and type can be +// assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it +// wishes to retain the data after returning. +func (id *ObjectId) UnmarshalBSONValue(_ bsontype.Type, data []byte) error { + var oid bsonprim.ObjectID + copy(oid[:], data) + *id = ObjectId(oid) + return nil +} diff --git a/mongo_test.go b/mongo_test.go new file mode 100644 index 0000000..7dc02ee --- /dev/null +++ b/mongo_test.go @@ -0,0 +1,282 @@ +package strfmt + +import ( + "reflect" + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.mongodb.org/mongo-driver/bson" +) + +type testableBSONFormat interface { + testableFormat + + bson.Marshaler + bson.Unmarshaler +} + +func TestBSONDate(t *testing.T) { + dateOriginal := Date(time.Date(2014, 10, 10, 0, 0, 0, 0, time.UTC)) + + bsonData, err := bson.Marshal(&dateOriginal) + require.NoError(t, err) + + var dateCopy Date + err = bson.Unmarshal(bsonData, &dateCopy) + require.NoError(t, err) + assert.Equal(t, dateOriginal, dateCopy) +} + +func TestBSONBase64(t *testing.T) { + const b64 string = "This is a byte array with unprintable chars, but it also isn" + b := []byte(b64) + subj := Base64(b) + + bsonData, err := bson.Marshal(subj) + require.NoError(t, err) + + var b64Copy Base64 + err = bson.Unmarshal(bsonData, &b64Copy) + require.NoError(t, err) + assert.Equal(t, subj, b64Copy) +} + +func TestBSONDuration(t *testing.T) { + dur := Duration(42) + bsonData, err := bson.Marshal(&dur) + require.NoError(t, err) + + var durCopy Duration + err = bson.Unmarshal(bsonData, &durCopy) + require.NoError(t, err) + assert.Equal(t, dur, durCopy) +} + +func TestBSONDateTime(t *testing.T) { + for caseNum, example := range testCases { + t.Logf("Case #%d", caseNum) + dt := DateTime(example.time) + + bsonData, err := bson.Marshal(&dt) + require.NoError(t, err) + + var dtCopy DateTime + err = bson.Unmarshal(bsonData, &dtCopy) + require.NoError(t, err) + // BSON DateTime type loses timezone information, so compare UTC() + assert.Equal(t, time.Time(dt).UTC(), time.Time(dtCopy).UTC()) + + // Check value marshaling explicitly + m := bson.M{"data": dt} + bsonData, err = bson.Marshal(&m) + require.NoError(t, err) + + var mCopy bson.M + err = bson.Unmarshal(bsonData, &mCopy) + require.NoError(t, err) + + data, ok := m["data"].(DateTime) + assert.True(t, ok) + assert.Equal(t, time.Time(dt).UTC(), time.Time(data).UTC()) + } +} + +func TestBSONULID(t *testing.T) { + t.Parallel() + t.Run("positive", func(t *testing.T) { + t.Parallel() + ulid, _ := ParseULID(testUlid) + + bsonData, err := bson.Marshal(&ulid) + require.NoError(t, err) + + var ulidUnmarshaled ULID + err = bson.Unmarshal(bsonData, &ulidUnmarshaled) + require.NoError(t, err) + assert.Equal(t, ulid, ulidUnmarshaled) + + // Check value marshaling explicitly + m := bson.M{"data": ulid} + bsonData, err = bson.Marshal(&m) + require.NoError(t, err) + + var mUnmarshaled bson.M + err = bson.Unmarshal(bsonData, &mUnmarshaled) + require.NoError(t, err) + + data, ok := m["data"].(ULID) + assert.True(t, ok) + assert.Equal(t, ulid, data) + }) + t.Run("negative", func(t *testing.T) { + t.Parallel() + uuid := UUID("00000000-0000-0000-0000-000000000000") + bsonData, err := bson.Marshal(&uuid) + require.NoError(t, err) + + var ulidUnmarshaled ULID + err = bson.Unmarshal(bsonData, &ulidUnmarshaled) + require.Error(t, err) + }) +} + +func TestFormatBSON(t *testing.T) { + t.Run("with URI", func(t *testing.T) { + t.Run("should bson.Marshal and bson.Unmarshal", func(t *testing.T) { + uri := URI("http://somewhere.com") + str := "http://somewhereelse.com" + testBSONStringFormat(t, &uri, "uri", str, []string{}, []string{"somewhere.com"}) + }) + }) + + t.Run("with Email", func(t *testing.T) { + email := Email("somebody@somewhere.com") + str := string("somebodyelse@somewhere.com") + + testBSONStringFormat(t, &email, "email", str, validEmails(), []string{"somebody@somewhere@com"}) + }) + + t.Run("with Hostname", func(t *testing.T) { + hostname := Hostname("somewhere.com") + str := string("somewhere.com") + + testBSONStringFormat(t, &hostname, "hostname", str, []string{}, invalidHostnames()) + testBSONStringFormat(t, &hostname, "hostname", str, validHostnames(), []string{}) + }) + + t.Run("with IPv4", func(t *testing.T) { + ipv4 := IPv4("192.168.254.1") + str := string("192.168.254.2") + testBSONStringFormat(t, &ipv4, "ipv4", str, []string{}, []string{"198.168.254.2.2"}) + }) + + t.Run("with IPv6", func(t *testing.T) { + ipv6 := IPv6("::1") + str := string("::2") + testBSONStringFormat(t, &ipv6, "ipv6", str, []string{}, []string{"127.0.0.1"}) + }) + + t.Run("with CIDR", func(t *testing.T) { + cidr := CIDR("192.168.254.1/24") + str := string("192.168.254.2/24") + testBSONStringFormat(t, &cidr, "cidr", str, []string{"192.0.2.1/24", "2001:db8:a0b:12f0::1/32"}, []string{"198.168.254.2", "2001:db8:a0b:12f0::1"}) + }) + + t.Run("with MAC", func(t *testing.T) { + mac := MAC("01:02:03:04:05:06") + str := string("06:05:04:03:02:01") + testBSONStringFormat(t, &mac, "mac", str, []string{}, []string{"01:02:03:04:05"}) + }) + + t.Run("with UUID3", func(t *testing.T) { + first3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhere.com")) + uuid3 := UUID3(first3.String()) + str := first3.String() + testBSONStringFormat(t, &uuid3, "uuid3", str, + validUUID3s(), + invalidUUID3s(), + ) + }) + + t.Run("with UUID4", func(t *testing.T) { + first4 := uuid.Must(uuid.NewRandom()) + other4 := uuid.Must(uuid.NewRandom()) + uuid4 := UUID4(first4.String()) + str := other4.String() + testBSONStringFormat(t, &uuid4, "uuid4", str, + validUUID4s(), + invalidUUID4s(), + ) + }) + + t.Run("with UUID5", func(t *testing.T) { + first5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhere.com")) + other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + uuid5 := UUID5(first5.String()) + str := other5.String() + testBSONStringFormat(t, &uuid5, "uuid5", str, + validUUID5s(), + invalidUUID5s(), + ) + }) + + t.Run("with UUID", func(t *testing.T) { + first5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhere.com")) + other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + uuid := UUID(first5.String()) + str := other5.String() + testBSONStringFormat(t, &uuid, "uuid", str, + validUUIDs(), + invalidUUIDs(), + ) + }) + + t.Run("with ISBN", func(t *testing.T) { + isbn := ISBN("0321751043") + str := string("0321751043") + testBSONStringFormat(t, &isbn, "isbn", str, []string{}, []string{"836217463"}) // bad checksum + }) + + t.Run("with ISBN10", func(t *testing.T) { + isbn10 := ISBN10("0321751043") + str := string("0321751043") + testBSONStringFormat(t, &isbn10, "isbn10", str, []string{}, []string{"836217463"}) // bad checksum + }) + + t.Run("with ISBN13", func(t *testing.T) { + isbn13 := ISBN13("978-0321751041") + str := string("978-0321751041") + testBSONStringFormat(t, &isbn13, "isbn13", str, []string{}, []string{"978-0321751042"}) // bad checksum + }) + + t.Run("with HexColor", func(t *testing.T) { + hexColor := HexColor("#FFFFFF") + str := string("#000000") + testBSONStringFormat(t, &hexColor, "hexcolor", str, []string{}, []string{"#fffffffz"}) + }) + + t.Run("with RGBColor", func(t *testing.T) { + rgbColor := RGBColor("rgb(255,255,255)") + str := string("rgb(0,0,0)") + testBSONStringFormat(t, &rgbColor, "rgbcolor", str, []string{}, []string{"rgb(300,0,0)"}) + }) + + t.Run("with SSN", func(t *testing.T) { + ssn := SSN("111-11-1111") + str := string("999 99 9999") + testBSONStringFormat(t, &ssn, "ssn", str, []string{}, []string{"999 99 999"}) + }) + + t.Run("with CreditCard", func(t *testing.T) { + creditCard := CreditCard("4111-1111-1111-1111") + str := string("4012-8888-8888-1881") + testBSONStringFormat(t, &creditCard, "creditcard", str, []string{}, []string{"9999-9999-9999-999"}) + }) + + t.Run("with Password", func(t *testing.T) { + password := Password("super secret stuff here") + testBSONStringFormat(t, &password, "password", "super secret!!!", []string{"even more secret"}, []string{}) + }) +} + +func testBSONStringFormat(t *testing.T, what testableBSONFormat, format, with string, _, _ []string) { + t.Helper() + b := []byte(with) + err := what.UnmarshalText(b) + require.NoError(t, err) + + // bson encoding interface + bsonData, err := bson.Marshal(what) + require.NoError(t, err) + + resetValue(t, format, what) + + err = bson.Unmarshal(bsonData, what) + require.NoError(t, err) + val := reflect.Indirect(reflect.ValueOf(what)) + strVal := val.String() + assert.Equal(t, with, strVal, "[%s]bson.Unmarshal: expected %v and %v to be equal (reset value) ", format, what, with) +} diff --git a/time.go b/time.go index 84e2412..0d39237 100644 --- a/time.go +++ b/time.go @@ -16,16 +16,11 @@ package strfmt import ( "database/sql/driver" - "encoding/binary" "encoding/json" "fmt" "regexp" "strings" "time" - - "go.mongodb.org/mongo-driver/bson" - - "go.mongodb.org/mongo-driver/bson/bsontype" ) var ( @@ -224,69 +219,6 @@ func (t *DateTime) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON renders the DateTime as a BSON document -func (t DateTime) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": t}) -} - -// UnmarshalBSON reads the DateTime from a BSON document -func (t *DateTime) UnmarshalBSON(data []byte) error { - var obj struct { - Data DateTime - } - - if err := bson.Unmarshal(data, &obj); err != nil { - return err - } - - *t = obj.Data - - return nil -} - -const bsonDateLength = 8 - -// MarshalBSONValue is an interface implemented by types that can marshal themselves -// into a BSON document represented as bytes. The bytes returned must be a valid -// BSON document if the error is nil. -// -// Marshals a DateTime as a bsontype.DateTime, an int64 representing -// milliseconds since epoch. -func (t DateTime) MarshalBSONValue() (bsontype.Type, []byte, error) { - // UnixNano cannot be used directly, the result of calling UnixNano on the zero - // Time is undefined. That's why we use time.Nanosecond() instead. - tNorm := NormalizeTimeForMarshal(time.Time(t)) - i64 := tNorm.UnixMilli() - - buf := make([]byte, bsonDateLength) - // int64 -> uint64 conversion is safe here - binary.LittleEndian.PutUint64(buf, uint64(i64)) //nolint:gosec - - return bson.TypeDateTime, buf, nil -} - -// UnmarshalBSONValue is an interface implemented by types that can unmarshal a -// BSON value representation of themselves. The BSON bytes and type can be -// assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it -// wishes to retain the data after returning. -func (t *DateTime) UnmarshalBSONValue(tpe bsontype.Type, data []byte) error { - if tpe == bson.TypeNull { - *t = DateTime{} - return nil - } - - if len(data) != bsonDateLength { - return fmt.Errorf("bson date field length not exactly 8 bytes: %w", ErrFormat) - } - - // it's ok to get negative values after conversion - i64 := int64(binary.LittleEndian.Uint64(data)) //nolint:gosec - // TODO: Use bsonprim.DateTime.Time() method - *t = DateTime(time.UnixMilli(i64)) - - return nil -} - // DeepCopyInto copies the receiver and writes its value into out. func (t *DateTime) DeepCopyInto(out *DateTime) { *out = *t diff --git a/time_test.go b/time_test.go index e95f13f..0247f42 100644 --- a/time_test.go +++ b/time_test.go @@ -22,7 +22,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" ) var ( @@ -280,35 +279,6 @@ func TestDateTime_Scan_Failed(t *testing.T) { require.Error(t, err) } -func TestDateTime_BSON(t *testing.T) { - for caseNum, example := range testCases { - t.Logf("Case #%d", caseNum) - dt := DateTime(example.time) - - bsonData, err := bson.Marshal(&dt) - require.NoError(t, err) - - var dtCopy DateTime - err = bson.Unmarshal(bsonData, &dtCopy) - require.NoError(t, err) - // BSON DateTime type loses timezone information, so compare UTC() - assert.Equal(t, time.Time(dt).UTC(), time.Time(dtCopy).UTC()) - - // Check value marshaling explicitly - m := bson.M{"data": dt} - bsonData, err = bson.Marshal(&m) - require.NoError(t, err) - - var mCopy bson.M - err = bson.Unmarshal(bsonData, &mCopy) - require.NoError(t, err) - - data, ok := m["data"].(DateTime) - assert.True(t, ok) - assert.Equal(t, time.Time(dt).UTC(), time.Time(data).UTC()) - } -} - func TestDeepCopyDateTime(t *testing.T) { p, err := ParseDateTime("2011-08-18T19:03:37.000000000+01:00") require.NoError(t, err) diff --git a/ulid.go b/ulid.go index 434eb01..62ff52b 100644 --- a/ulid.go +++ b/ulid.go @@ -9,7 +9,6 @@ import ( "sync" "github.com/oklog/ulid" - "go.mongodb.org/mongo-driver/bson" ) // ULID represents a ulid string format @@ -165,29 +164,6 @@ func (u *ULID) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u ULID) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *ULID) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - id, err := ulid.ParseStrict(ud) - if err != nil { - return fmt.Errorf("couldn't parse bson bytes as ULID: %w: %w", err, ErrFormat) - } - u.ULID = id - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as ULID: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *ULID) DeepCopyInto(out *ULID) { *out = *u diff --git a/ulid_test.go b/ulid_test.go index 8be6714..8e97941 100644 --- a/ulid_test.go +++ b/ulid_test.go @@ -10,7 +10,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" ) const testUlid = string("01EYXZVGBHG26MFTG4JWR4K558") @@ -51,45 +50,6 @@ func TestFormatULID_Text(t *testing.T) { }) } -func TestFormatULID_BSON(t *testing.T) { - t.Parallel() - t.Run("positive", func(t *testing.T) { - t.Parallel() - ulid, _ := ParseULID(testUlid) - - bsonData, err := bson.Marshal(&ulid) - require.NoError(t, err) - - var ulidUnmarshaled ULID - err = bson.Unmarshal(bsonData, &ulidUnmarshaled) - require.NoError(t, err) - assert.Equal(t, ulid, ulidUnmarshaled) - - // Check value marshaling explicitly - m := bson.M{"data": ulid} - bsonData, err = bson.Marshal(&m) - require.NoError(t, err) - - var mUnmarshaled bson.M - err = bson.Unmarshal(bsonData, &mUnmarshaled) - require.NoError(t, err) - - data, ok := m["data"].(ULID) - assert.True(t, ok) - assert.Equal(t, ulid, data) - }) - t.Run("negative", func(t *testing.T) { - t.Parallel() - uuid := UUID("00000000-0000-0000-0000-000000000000") - bsonData, err := bson.Marshal(&uuid) - require.NoError(t, err) - - var ulidUnmarshaled ULID - err = bson.Unmarshal(bsonData, &ulidUnmarshaled) - require.Error(t, err) - }) -} - func TestFormatULID_JSON(t *testing.T) { t.Parallel() t.Run("positive", func(t *testing.T) {