123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- package activity
- import (
- "encoding/json"
- "fmt"
- "testing"
- "time"
- "github.com/qor5/admin/media/media_library"
- )
- type (
- Post struct {
- ID uint `gorm:"primarykey"`
- CreatedAt time.Time
- UpdatedAt time.Time
- PublishedDate time.Time
- Image media_library.MediaBox
- Title string
- Content string
- Author Author
- Comments []Comment
- Tags map[string]Tag
- }
- Tag struct {
- Name string
- }
- Author struct {
- Name string
- Age int
- }
- Comment struct {
- Text string
- }
- )
- func TestDiff(t *testing.T) {
- testCases := []struct {
- description string
- modelBuilder *ModelBuilder
- old Post
- now Post
- want []Diff
- }{
- {
- description: "Simple basic update",
- modelBuilder: &ModelBuilder{},
- old: Post{Title: "test", Content: ""},
- now: Post{Title: "test1", Content: "124"},
- want: []Diff{
- {
- Field: "Title",
- Old: "test",
- Now: "test1",
- },
- {
- Field: "Content",
- Old: "",
- Now: "124",
- }},
- },
- {
- description: "Default type handles",
- modelBuilder: &ModelBuilder{},
- old: Post{Image: media_library.MediaBox{ID: json.Number("1"), Url: "https://s3.com/1.jpg", Description: "test"}},
- now: Post{Image: media_library.MediaBox{ID: json.Number("2"), Url: "https://s3.com/2.jpg", Description: "test2"}},
- want: []Diff{
- {
- Field: "Image.Url",
- Old: "https://s3.com/1.jpg",
- Now: "https://s3.com/2.jpg",
- },
- {
- Field: "Image.Description",
- Old: "test",
- Now: "test2",
- },
- },
- },
- {
- description: "Default ingored fields",
- modelBuilder: &ModelBuilder{},
- old: Post{ID: 1, CreatedAt: time.Unix(1257894000, 0)},
- now: Post{ID: 2, CreatedAt: time.Unix(1457894000, 0)},
- want: nil,
- },
- {
- description: "Using model ingored fields",
- modelBuilder: (&ModelBuilder{}).AddIgnoredFields("Name"),
- old: Post{Author: Author{Name: "test", Age: 10}},
- now: Post{Author: Author{Name: "test1", Age: 12}},
- want: []Diff{
- {
- Field: "Author.Age",
- Old: "10",
- Now: "12",
- },
- },
- },
- {
- description: "Using model type handles",
- modelBuilder: (&ModelBuilder{}).AddTypeHanders(Author{}, func(old, now interface{}, prefixField string) (diffs []Diff) {
- oldAuthor := old.(Author)
- nowAuthor := now.(Author)
- if oldAuthor.Name != nowAuthor.Name {
- diffs = append(diffs, Diff{Field: fmt.Sprintf("%s.Name", prefixField), Old: oldAuthor.Name, Now: nowAuthor.Name})
- }
- return diffs
- }),
- old: Post{Author: Author{Name: "test", Age: 10}},
- now: Post{Author: Author{Name: "test1", Age: 12}},
- want: []Diff{
- {
- Field: "Author.Name",
- Old: "test",
- Now: "test1",
- },
- },
- },
- {
- description: "Test slice data",
- modelBuilder: &ModelBuilder{},
- old: Post{Comments: []Comment{{Text: "1"}, {Text: "2"}}},
- now: Post{Comments: []Comment{{Text: "1.1"}, {Text: "2.2"}}},
- want: []Diff{
- {
- Field: "Comments.0.Text",
- Old: "1",
- Now: "1.1",
- },
- {
- Field: "Comments.1.Text",
- Old: "2",
- Now: "2.2",
- },
- },
- },
- {
- description: "Test deleting slice data",
- modelBuilder: &ModelBuilder{},
- old: Post{Comments: []Comment{{Text: "1"}, {Text: "2"}}},
- now: Post{Comments: []Comment{{Text: "1.1"}}},
- want: []Diff{
- {
- Field: "Comments.0.Text",
- Old: "1",
- Now: "1.1",
- },
- {
- Field: "Comments.1",
- Old: "{Text:2}",
- Now: "",
- },
- },
- },
- {
- description: "Test adding slice data",
- modelBuilder: &ModelBuilder{},
- old: Post{Comments: []Comment{{Text: "1"}}},
- now: Post{Comments: []Comment{{Text: "1.1"}, {Text: "2"}}},
- want: []Diff{
- {
- Field: "Comments.0.Text",
- Old: "1",
- Now: "1.1",
- },
- {
- Field: "Comments.1",
- Old: "",
- Now: "{Text:2}",
- },
- },
- },
- {
- description: "Test creating slice data",
- modelBuilder: &ModelBuilder{},
- old: Post{},
- now: Post{Comments: []Comment{{Text: "1.1"}, {Text: "2"}}},
- want: []Diff{
- {
- Field: "Comments",
- Old: "",
- Now: "[{Text:1.1} {Text:2}]",
- },
- },
- },
- {
- description: "Test remove all slice data",
- modelBuilder: &ModelBuilder{},
- old: Post{Comments: []Comment{{Text: "1.1"}, {Text: "2"}}},
- now: Post{},
- want: []Diff{
- {
- Field: "Comments",
- Old: "[{Text:1.1} {Text:2}]",
- Now: "",
- },
- },
- },
- {
- description: "Test map data",
- modelBuilder: &ModelBuilder{},
- old: Post{Tags: map[string]Tag{"tag1": {Name: "tst1"}}},
- now: Post{Tags: map[string]Tag{"tag1": {Name: "tst12"}}},
- want: []Diff{
- {
- Field: "Tags.tag1.Name",
- Old: "tst1",
- Now: "tst12",
- },
- },
- },
- {
- description: "Test adding map data",
- modelBuilder: &ModelBuilder{},
- old: Post{Tags: map[string]Tag{"tag1": {Name: "tst1"}}},
- now: Post{Tags: map[string]Tag{"tag1": {Name: "tst12"}, "tag2": {Name: "tst121"}}},
- want: []Diff{
- {
- Field: "Tags.tag1.Name",
- Old: "tst1",
- Now: "tst12",
- },
- {
- Field: "Tags.tag2",
- Old: "",
- Now: "{Name:tst121}",
- },
- },
- },
- {
- description: "Test deleting map data",
- modelBuilder: &ModelBuilder{},
- old: Post{Tags: map[string]Tag{"tag1": {Name: "tst1"}, "tag2": {Name: "tst1"}}},
- now: Post{Tags: map[string]Tag{"tag1": {Name: "tst1"}}},
- want: []Diff{
- {
- Field: "Tags.tag2",
- Old: "{Name:tst1}",
- Now: "",
- },
- },
- },
- {
- description: "Test creating map data",
- modelBuilder: &ModelBuilder{},
- old: Post{},
- now: Post{Tags: map[string]Tag{"tag1": {Name: "tst1"}}},
- want: []Diff{
- {
- Field: "Tags",
- Old: "",
- Now: "map[tag1:{Name:tst1}]",
- },
- },
- },
- {
- description: "Test remove all map data",
- modelBuilder: &ModelBuilder{},
- old: Post{Tags: map[string]Tag{"tag1": {Name: "tst1"}}},
- now: Post{Tags: nil},
- want: []Diff{
- {
- Field: "Tags",
- Old: "map[tag1:{Name:tst1}]",
- Now: "",
- },
- },
- },
- }
- for _, test := range testCases {
- t.Run(test.description, func(t *testing.T) {
- diffs, err := NewDiffBuilder(test.modelBuilder).Diff(test.old, test.now)
- if err != nil {
- t.Fatalf("want: %v, but got error: %v", test.want, err)
- }
- w, _ := json.Marshal(test.want)
- d, _ := json.Marshal(diffs)
- if string(w) != string(d) {
- t.Fatalf("want: %v, but got: %v", string(w), string(d))
- }
- })
- }
- }
- func TestDiffTypesError(t *testing.T) {
- _, err := NewDiffBuilder(&ModelBuilder{}).Diff(Post{Title: "123"}, Author{Name: "ccc"})
- if err.Error() != "old and now type mismatch: activity.Post != activity.Author" {
- t.Fatalf("difference type error")
- }
- _, err = NewDiffBuilder(&ModelBuilder{}).Diff(Post{Title: "123"}, struct{}{})
- if err.Error() != "old and now type mismatch: activity.Post != struct {}" {
- t.Fatalf("difference type error")
- }
- }
- func BenchmarkSimpleDiff(b *testing.B) {
- builder := NewDiffBuilder(&ModelBuilder{})
- for i := 0; i < b.N; i++ {
- builder.Diff(Author{Name: "test1", Age: 10}, Author{Name: "test12", Age: 18})
- }
- }
- func BenchmarkComplexDiff(b *testing.B) {
- old := Post{
- ID: 1,
- CreatedAt: time.Now(),
- PublishedDate: time.Now(),
- Image: media_library.MediaBox{ID: json.Number("1"), Url: "https://s3.com/1.jpg", Description: "test"},
- Title: "title",
- Content: "content111",
- Author: Author{Name: "author1", Age: 10},
- Comments: []Comment{},
- Tags: map[string]Tag{},
- }
- for i := 0; i < 50; i++ {
- old.Comments = append(old.Comments, Comment{Text: fmt.Sprintf("text - %d", i)})
- old.Tags[fmt.Sprintf("tag - %d", i)] = Tag{Name: fmt.Sprintf("title - %d", i)}
- }
- now := Post{
- ID: 1,
- CreatedAt: time.Now().Add(1 * time.Hour),
- PublishedDate: time.Now().Add(3 * time.Hour),
- Image: media_library.MediaBox{ID: json.Number("2"), Url: "https://s3.com/2.jpg", Description: "test2"},
- Title: "title1",
- Content: "content111",
- Author: Author{
- Name: "author2",
- Age: 19,
- },
- Comments: []Comment{},
- Tags: map[string]Tag{},
- }
- for i := 0; i < 80; i++ {
- now.Comments = append(now.Comments, Comment{Text: fmt.Sprintf("text ---%d", i)})
- old.Tags[fmt.Sprintf("tag - %d", i)] = Tag{Name: fmt.Sprintf("title - %d", i)}
- }
- builder := NewDiffBuilder(&ModelBuilder{})
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- builder.Diff(old, now)
- }
- }
- // goos: darwin
- // goarch: amd64
- // pkg: github.com/qor5/admin/activity
- // cpu: Intel(R) Core(TM) i5-6267U CPU @ 2.90GHz
- // BenchmarkSimpleDiff-4 669444 1869 ns/op
- // BenchmarkComplexDiff-4 1381 729444 ns/op
|