Skip to content

Commit

Permalink
Add Clone functions on Entry and Group
Browse files Browse the repository at this point in the history
  • Loading branch information
tobischo committed Jan 9, 2023
1 parent 02f8ca0 commit df59614
Show file tree
Hide file tree
Showing 4 changed files with 255 additions and 0 deletions.
35 changes: 35 additions & 0 deletions entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,29 @@ func (e *Entry) setKdbxFormatVersion(version formatVersion) {
}
}

// Clone creates a copy of an Entry struct including its child entities
func (e Entry) Clone() Entry {
clone := e
clone.UUID = NewUUID()
clone.Values = make([]ValueData, len(clone.Values))
for i, value := range e.Values {
clone.Values[i] = value
}
clone.Histories = make([]History, len(clone.Histories))
for i, history := range e.Histories {
clone.Histories[i] = history.Clone()
}
clone.Binaries = make([]BinaryReference, len(clone.Binaries))
for i, binary := range e.Binaries {
clone.Binaries[i] = binary
}
clone.CustomData = make([]CustomData, len(clone.CustomData))
for i, data := range e.CustomData {
clone.CustomData[i] = data
}
return clone
}

// Get returns the value in e corresponding with key k, or an empty string otherwise
func (e *Entry) Get(key string) *ValueData {
for i := range e.Values {
Expand Down Expand Up @@ -108,6 +131,18 @@ func (h *History) setKdbxFormatVersion(version formatVersion) {
}
}

// Clone creates a copy of a History struct including its child entities
func (h History) Clone() History {
clone := h

clone.Entries = make([]Entry, len(h.Entries))
for i, entry := range h.Entries {
clone.Entries[i] = entry.Clone()
}

return clone
}

// ValueData is a structure containing key value pairs of information stored in an entry
type ValueData struct {
Key string `xml:"Key"`
Expand Down
137 changes: 137 additions & 0 deletions entry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package gokeepasslib
import (
"reflect"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"golang.org/x/exp/slices"
)

func TestNewEntry(t *testing.T) {
Expand Down Expand Up @@ -62,6 +66,94 @@ func TestNewEntry(t *testing.T) {
}
}

func compareEntry(a, b Entry) bool {
if !cmp.Equal(
a,
b,
cmpopts.IgnoreFields(Entry{}, "Values", "Histories", "Binaries", "CustomData"),
) {
return false
}

if !slices.EqualFunc(
a.Values,
b.Values,
func(a, b ValueData) bool {
return cmp.Equal(a, b)
},
) {
return false
}

if !slices.EqualFunc(
a.Histories,
b.Histories,
func(a, b History) bool {
return cmp.Equal(a, b)
},
) {
return false
}

if !slices.EqualFunc(
a.Binaries,
b.Binaries,
func(a, b BinaryReference) bool {
return cmp.Equal(a, b)
},
) {
return false
}

if !slices.EqualFunc(
a.CustomData,
b.CustomData,
func(a, b CustomData) bool {
return cmp.Equal(a, b)
},
) {
return false
}

return true
}

func TestEntry_Clone(t *testing.T) {
cases := []struct {
title string
}{
{
title: "success",
},
}

for _, c := range cases {
t.Run(c.title, func(t *testing.T) {
entry := NewEntry()

clone := entry.Clone()

if &clone == &entry {
t.Errorf("clone struct has the same pointer address")
}

if clone.UUID == entry.UUID {
t.Errorf("clone did not receive a new UUID")
}

clone.UUID = entry.UUID
if !compareEntry(entry, clone) {
t.Errorf(
"Did not receive expected Entry %+v, received %+v",
clone,
entry,
)
}

})
}
}

func TestEntrySetKdbxFormatVersion(t *testing.T) {
cases := []struct {
title string
Expand Down Expand Up @@ -127,3 +219,48 @@ func TestEntrySetKdbxFormatVersion(t *testing.T) {
}

}

func TestHistory_Clone(t *testing.T) {
cases := []struct {
title string
}{
{
title: "success",
},
}

for _, c := range cases {
t.Run(c.title, func(t *testing.T) {
entry := NewEntry()
history := History{
Entries: []Entry{entry},
}

clone := history.Clone()

if &clone == &history {
t.Errorf("clone struct has the same pointer address")
}

if clone.Entries[0].UUID == history.Entries[0].UUID {
t.Errorf("cloned entry did not receive a new UUID")
}

clone.Entries[0].UUID = history.Entries[0].UUID
if !slices.EqualFunc(
history.Entries,
clone.Entries,
func(a, b Entry) bool {
return compareEntry(a, b)
},
) {
t.Errorf(
"Did not receive expected History %+v, received %+v",
clone,
history,
)
}

})
}
}
15 changes: 15 additions & 0 deletions group.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ type Group struct {
groupChildOrder int `xml:"-"`
}

// Clone creates a copy of a Group struct including its child entities
func (g Group) Clone() Group {
clone := g
clone.UUID = NewUUID()
clone.Entries = make([]Entry, len(clone.Entries))
for i, entry := range g.Entries {
clone.Entries[i] = entry.Clone()
}
clone.Groups = make([]Group, len(clone.Groups))
for i, group := range g.Groups {
clone.Groups[i] = group.Clone()
}
return clone
}

// UnmarshalXML unmarshals the boolean from d
func (g *Group) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
for {
Expand Down
68 changes: 68 additions & 0 deletions group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import (
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/stretchr/testify/assert"
"golang.org/x/exp/slices"

w "github.com/tobischo/gokeepasslib/v3/wrappers"
)
Expand Down Expand Up @@ -65,6 +68,71 @@ func TestNewGroup(t *testing.T) {
}
}

func compareGroup(a, b Group) bool {
if !cmp.Equal(
a,
b,
cmp.AllowUnexported(Group{}),
cmpopts.IgnoreFields(Group{}, "Entries", "Groups"),
) {
return false
}

if !slices.EqualFunc(
a.Groups,
b.Groups,
compareGroup,
) {
return false
}

if !slices.EqualFunc(
a.Entries,
b.Entries,
compareEntry,
) {
return false
}

return true
}

func TestGroup_Clone(t *testing.T) {
cases := []struct {
title string
}{
{
title: "success",
},
}

for _, c := range cases {
t.Run(c.title, func(t *testing.T) {
group := NewGroup()

clone := group.Clone()

if &clone == &group {
t.Errorf("clone struct has the same pointer address")
}

if clone.UUID == group.UUID {
t.Errorf("clone did not receive a new UUID")
}

clone.UUID = group.UUID
if !compareGroup(clone, group) {
t.Errorf(
"Did not receive expected Group %+v, received %+v",
clone,
group,
)
}

})
}
}

func TestGroupSetKdbxFormatVersion(t *testing.T) {
cases := []struct {
title string
Expand Down

0 comments on commit df59614

Please sign in to comment.