activity_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. package activity
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http/httptest"
  6. "os"
  7. "reflect"
  8. "testing"
  9. "github.com/qor5/admin/presets"
  10. "github.com/qor5/admin/presets/gorm2op"
  11. "github.com/qor5/web"
  12. "gorm.io/driver/postgres"
  13. "gorm.io/gorm"
  14. )
  15. var (
  16. db *gorm.DB
  17. pb = presets.New()
  18. pageModel = pb.Model(&Page{})
  19. widgetModel = pb.Model(&Widget{})
  20. )
  21. type (
  22. Page struct {
  23. ID uint `gorm:"primary_key"`
  24. VersionName string
  25. Title string
  26. Description string
  27. Widgets Widgets
  28. }
  29. Widgets []Widget
  30. Widget struct {
  31. Name string
  32. Title string
  33. }
  34. TestActivityLog struct {
  35. ActivityLog
  36. }
  37. TestActivityModel struct {
  38. ID uint `gorm:"primary_key"`
  39. VersionName string
  40. Title string
  41. Description string
  42. }
  43. )
  44. func init() {
  45. var err error
  46. db, err = gorm.Open(postgres.Open(os.Getenv("DBURL")), &gorm.Config{})
  47. if err != nil {
  48. panic(err)
  49. }
  50. if err = db.AutoMigrate(&TestActivityModel{}); err != nil {
  51. panic(err)
  52. }
  53. }
  54. func resetDB() {
  55. db.Exec("delete from test_activity_logs;")
  56. db.Exec("delete from test_activity_models;")
  57. }
  58. func TestModelKeys(t *testing.T) {
  59. builder := New(pb, db, &TestActivityLog{})
  60. builder.RegisterModel(pageModel).AddKeys("ID", "VersionName")
  61. resetDB()
  62. builder.AddCreateRecord("creator a", Page{ID: 1, VersionName: "v1", Title: "test"}, db)
  63. record := builder.NewLogModelData().(ActivityLogInterface)
  64. if err := db.First(record).Error; err != nil {
  65. t.Fatal(err)
  66. }
  67. if record.GetModelKeys() != "1:v1" {
  68. t.Errorf("want the keys %v, but got %v", "1:v1", record.GetModelKeys())
  69. }
  70. resetDB()
  71. builder.RegisterModel(widgetModel).AddKeys("Name")
  72. builder.AddCreateRecord("b", Widget{Name: "Text 01", Title: "123"}, db)
  73. record2 := builder.NewLogModelData().(ActivityLogInterface)
  74. if err := db.First(record2).Error; err != nil {
  75. t.Fatal(err)
  76. }
  77. if record2.GetModelKeys() != "Text 01" {
  78. t.Errorf("want the keys %v, but got %v", "Text 01", record2.GetModelKeys())
  79. }
  80. }
  81. func TestModelLink(t *testing.T) {
  82. builder := New(pb, db, &TestActivityLog{})
  83. builder.RegisterModel(pageModel).SetLink(func(v interface{}) string {
  84. page := v.(Page)
  85. return fmt.Sprintf("/admin/pages/%d?version=%s", page.ID, page.VersionName)
  86. })
  87. resetDB()
  88. builder.AddCreateRecord("a", Page{ID: 1, VersionName: "v1", Title: "test"}, db)
  89. record := builder.NewLogModelData().(ActivityLogInterface)
  90. if err := db.First(record).Error; err != nil {
  91. t.Fatal(err)
  92. }
  93. if record.GetModelLink() != "/admin/pages/1?version=v1" {
  94. t.Errorf("want the link %v, but got %v", "/admin/pages/1?version=v1", record.GetModelLink())
  95. }
  96. }
  97. func TestModelTypeHanders(t *testing.T) {
  98. builder := New(pb, db, &TestActivityLog{})
  99. builder.RegisterModel(pageModel).AddTypeHanders(Widgets{}, func(old, now interface{}, prefixField string) (diffs []Diff) {
  100. oldWidgets := old.(Widgets)
  101. nowWidgets := now.(Widgets)
  102. var (
  103. oldLen = len(oldWidgets)
  104. nowLen = len(nowWidgets)
  105. minLen int
  106. added bool
  107. deleted bool
  108. )
  109. if oldLen > nowLen {
  110. minLen = nowLen
  111. deleted = true
  112. }
  113. if oldLen < nowLen {
  114. minLen = oldLen
  115. added = true
  116. }
  117. if oldLen == nowLen {
  118. minLen = oldLen
  119. }
  120. for i := 0; i < minLen; i++ {
  121. if oldWidgets[i].Name != nowWidgets[i].Name {
  122. newPrefixField := fmt.Sprintf("%s.%d", prefixField, i)
  123. diffs = append(diffs, Diff{Field: newPrefixField, Old: oldWidgets[i].Name, Now: nowWidgets[i].Name})
  124. }
  125. }
  126. if added {
  127. for i := minLen; i < nowLen; i++ {
  128. newPrefixField := fmt.Sprintf("%s.%d", prefixField, i)
  129. diffs = append(diffs, Diff{Field: newPrefixField, Old: "", Now: nowWidgets[i].Name})
  130. }
  131. }
  132. if deleted {
  133. for i := minLen; i < oldLen; i++ {
  134. newPrefixField := fmt.Sprintf("%s.%d", prefixField, i)
  135. diffs = append(diffs, Diff{Field: newPrefixField, Old: oldWidgets[i].Name, Now: ""})
  136. }
  137. }
  138. return diffs
  139. })
  140. resetDB()
  141. builder.AddEditRecordWithOld("a",
  142. Page{ID: 1, VersionName: "v1", Title: "test",
  143. Widgets: []Widget{
  144. {Name: "Text 01", Title: "test1"},
  145. {Name: "HeroBanner 02", Title: "banner 1"},
  146. {Name: "Card 03", Title: "cards 1"},
  147. }},
  148. Page{ID: 1, VersionName: "v2", Title: "test1",
  149. Widgets: []Widget{
  150. {Name: "Text 011", Title: "test1"},
  151. {Name: "HeroBanner 022", Title: "banner 1"},
  152. {Name: "Card 03", Title: "cards 1"},
  153. {Name: "Video 03", Title: "video 1"},
  154. },
  155. }, db)
  156. record := builder.NewLogModelData().(ActivityLogInterface)
  157. if err := db.First(record).Error; err != nil {
  158. t.Fatal(err)
  159. }
  160. wants := `[{"Field":"VersionName","Old":"v1","Now":"v2"},{"Field":"Title","Old":"test","Now":"test1"},{"Field":"Widgets.0","Old":"Text 01","Now":"Text 011"},{"Field":"Widgets.1","Old":"HeroBanner 02","Now":"HeroBanner 022"},{"Field":"Widgets.3","Old":"","Now":"Video 03"}]`
  161. if record.GetModelDiffs() != wants {
  162. t.Errorf("want the diffs %v, but got %v", wants, record.GetModelDiffs())
  163. }
  164. }
  165. func TestCreator(t *testing.T) {
  166. builder := New(pb, db, &TestActivityLog{})
  167. builder.RegisterModel(pageModel)
  168. resetDB()
  169. builder.AddCreateRecord("user a", Page{ID: 1, VersionName: "v1", Title: "test"}, db)
  170. record := builder.NewLogModelData().(ActivityLogInterface)
  171. if err := db.First(record).Error; err != nil {
  172. t.Fatal(err)
  173. }
  174. if record.GetCreator() != "user a" {
  175. t.Errorf("want the creator %v, but got %v", "a", record.GetCreator())
  176. }
  177. }
  178. type user struct {
  179. }
  180. func (u user) GetID() uint {
  181. return 10
  182. }
  183. func (u user) GetName() string {
  184. return "user a"
  185. }
  186. func TestCreatorInferface(t *testing.T) {
  187. builder := New(pb, db, &TestActivityLog{})
  188. builder.RegisterModel(pageModel)
  189. resetDB()
  190. builder.AddCreateRecord(user{}, Page{ID: 1, VersionName: "v1", Title: "test"}, db)
  191. record := builder.NewLogModelData().(ActivityLogInterface)
  192. if err := db.First(record).Error; err != nil {
  193. t.Fatal(err)
  194. }
  195. if record.GetCreator() != "user a" {
  196. t.Errorf("want the creator %v, but got %v", "a", record.GetCreator())
  197. }
  198. if record.GetUserID() != 10 {
  199. t.Errorf("want the creator id %v, but got %v", 10, record.GetUserID())
  200. }
  201. }
  202. func TestGetActivityLogs(t *testing.T) {
  203. builder := New(pb, db, &TestActivityLog{})
  204. builder.RegisterModel(pageModel).AddKeys("ID", "VersionName")
  205. resetDB()
  206. builder.AddCreateRecord("creator a", Page{ID: 1, VersionName: "v1", Title: "test"}, db)
  207. builder.AddEditRecordWithOld("creator a", Page{ID: 1, VersionName: "v1", Title: "test"}, Page{ID: 1, VersionName: "v1", Title: "test1"}, db)
  208. builder.AddEditRecordWithOld("creator a", Page{ID: 1, VersionName: "v1", Title: "test1"}, Page{ID: 1, VersionName: "v1", Title: "test2"}, db)
  209. builder.AddEditRecordWithOld("creator a", Page{ID: 2, VersionName: "v1", Title: "test1"}, Page{ID: 2, VersionName: "v1", Title: "test2"}, db)
  210. logs := builder.GetCustomizeActivityLogs(Page{ID: 1, VersionName: "v1"}, db)
  211. testlogs, ok := logs.(*[]*TestActivityLog)
  212. if !ok {
  213. t.Errorf("want the logs type %v, but got %v", "*[]*TestActivityLog", reflect.TypeOf(logs))
  214. }
  215. if len(*testlogs) != 3 {
  216. t.Errorf("want the logs length %v, but got %v", 3, len(*testlogs))
  217. }
  218. if (*testlogs)[0].Action != "Create" || (*testlogs)[0].ModelName != "Page" || (*testlogs)[0].ModelKeys != "1:v1" || (*testlogs)[0].Creator != "creator a" {
  219. t.Errorf("want the logs %v, but got %+v", "Create:Page:1:v1:creator a", (*testlogs)[0])
  220. }
  221. if (*testlogs)[1].Action != "Edit" || (*testlogs)[1].ModelName != "Page" || (*testlogs)[1].ModelKeys != "1:v1" || (*testlogs)[1].Creator != "creator a" {
  222. t.Errorf("want the logs %v, but got %v", "Edit:Page:1:v1:creator a", (*testlogs)[1])
  223. }
  224. if (*testlogs)[2].Action != "Edit" || (*testlogs)[2].ModelName != "Page" || (*testlogs)[2].ModelKeys != "1:v1" || (*testlogs)[2].Creator != "creator a" {
  225. t.Errorf("want the logs %v, but got %v", "Edit:Page:1:v1:creator a", (*testlogs)[2])
  226. }
  227. }
  228. func TestMutliModelBuilder(t *testing.T) {
  229. builder := New(pb, db, &TestActivityLog{}).SetCreatorContextKey("creator")
  230. pb.DataOperator(gorm2op.DataOperator(db))
  231. pageModel2 := pb.Model(&TestActivityModel{}).URIName("page-02").Label("Page-02")
  232. pageModel3 := pb.Model(&TestActivityModel{}).URIName("page-03").Label("Page-03")
  233. builder.RegisterModel(&TestActivityModel{}).SetKeys("ID")
  234. builder.RegisterModel(pageModel2).SetKeys("ID").SkipDelete().AddIgnoredFields("VersionName")
  235. builder.RegisterModel(pageModel3).SetKeys("ID").SkipCreate().AddIgnoredFields("Description")
  236. data1 := &TestActivityModel{ID: 1, VersionName: "v1", Title: "test1", Description: "Description1"}
  237. data2 := &TestActivityModel{ID: 2, VersionName: "v2", Title: "test2", Description: "Description3"}
  238. data3 := &TestActivityModel{ID: 3, VersionName: "v3", Title: "test3", Description: "Description3"}
  239. resetDB()
  240. // add create record
  241. db.Create(data1)
  242. builder.AddCreateRecord("Test User", data1, db)
  243. pageModel2.Editing().Saver(data2, "2", &web.EventContext{R: httptest.NewRequest("POST", "/admin/page-01/2", nil).WithContext(context.WithValue(context.Background(), "creator", "Test User"))})
  244. pageModel3.Editing().Saver(data3, "3", &web.EventContext{R: httptest.NewRequest("POST", "/admin/page-02/3", nil).WithContext(context.WithValue(context.Background(), "creator", "Test User"))})
  245. {
  246. for _, id := range []string{"1", "2"} {
  247. var log TestActivityLog
  248. if db.Where("action = ? AND model_name = ? AND model_keys = ?", "Create", "TestActivityModel", id).Find(&log); log.ID == 0 {
  249. t.Errorf("want the log %v, but got %v", "TestActivityModel:"+id, log)
  250. }
  251. }
  252. var log TestActivityLog
  253. if db.Where("action = ? AND model_name = ? AND model_keys = ?", "Create", "TestActivityModel", 3).Find(&log); log.ID != 0 {
  254. t.Errorf("want skip the create, but still got the record %v", log)
  255. }
  256. }
  257. // add edit record
  258. data1.Title = "test1-1"
  259. data1.Description = "Description1-1"
  260. builder.AddEditRecord("Test User", data1, db)
  261. db.Save(data1)
  262. data2.Title = "test2-1"
  263. data2.Description = "Description2-1"
  264. pageModel2.Editing().Saver(data2, "2", &web.EventContext{R: httptest.NewRequest("POST", "/admin/page-01/2", nil).WithContext(context.WithValue(context.Background(), "creator", "Test User"))})
  265. data3.Title = "test3-1"
  266. data3.Description = "Description3-1"
  267. pageModel3.Editing().Saver(data3, "3", &web.EventContext{R: httptest.NewRequest("POST", "/admin/page-02/3", nil).WithContext(context.WithValue(context.Background(), "creator", "Test User"))})
  268. {
  269. var log1 TestActivityLog
  270. if db.Where("action = ? AND model_name = ? AND model_keys = ?", "Edit", "TestActivityModel", "1").Find(&log1); log1.ID == 0 {
  271. t.Errorf("want the log %v, but got %v", "TestActivityModel:1", log1)
  272. }
  273. if log1.GetModelDiffs() != `[{"Field":"Title","Old":"test1","Now":"test1-1"},{"Field":"Description","Old":"Description1","Now":"Description1-1"}]` {
  274. t.Errorf("want the log %v, but got %v", `[{"Field":"Title","Old":"test1","Now":"test1-1"},{"Field":"Description","Old":"Description1","Now":"Description1-1"}]`, log1.GetModelDiffs())
  275. }
  276. var log2 TestActivityLog
  277. if db.Where("action = ? AND model_name = ? AND model_keys = ?", "Edit", "TestActivityModel", "2").Find(&log2); log2.ID == 0 {
  278. t.Errorf("want the log %v, but got %v", "TestActivityModel:2", log2)
  279. }
  280. if log2.GetModelDiffs() != `[{"Field":"Title","Old":"test2","Now":"test2-1"},{"Field":"Description","Old":"Description3","Now":"Description2-1"}]` {
  281. t.Errorf("want the log %v, but got %v", `[{"Field":"Title","Old":"test2","Now":"test2-1"},{"Field":"Description","Old":"Description3","Now":"Description2-1"}]`, log1.GetModelDiffs())
  282. }
  283. if log2.ModelLabel != "page-02" {
  284. t.Errorf("want the log %v, but got %v", "page-02", log2.ModelLabel)
  285. }
  286. var log3 TestActivityLog
  287. if db.Where("action = ? AND model_name = ? AND model_keys = ?", "Edit", "TestActivityModel", "3").Find(&log3); log3.ID == 0 {
  288. t.Errorf("want the log %v, but got %v", "TestActivityModel:3", log3)
  289. }
  290. if log3.GetModelDiffs() != `[{"Field":"Title","Old":"test3","Now":"test3-1"}]` {
  291. t.Errorf("want the log %v, but got %v", `[{"Field":"Title","Old":"test3","Now":"test3-1"}]`, log1.GetModelDiffs())
  292. }
  293. if log3.ModelLabel != "page-03" {
  294. t.Errorf("want the log %v, but got %v", "page-03", log2.ModelLabel)
  295. }
  296. }
  297. // // add delete record
  298. builder.AddDeleteRecord("Test User", data1, db)
  299. db.Delete(data1)
  300. pageModel2.Editing().Deleter(data2, "2", &web.EventContext{R: httptest.NewRequest("POST", "/admin/page-01/2", nil).WithContext(context.WithValue(context.Background(), "creator", "Test User"))})
  301. pageModel3.Editing().Deleter(data3, "3", &web.EventContext{R: httptest.NewRequest("POST", "/admin/page-02/3", nil).WithContext(context.WithValue(context.Background(), "creator", "Test User"))})
  302. {
  303. for _, id := range []string{"1", "3"} {
  304. var log TestActivityLog
  305. if db.Where("action = ? AND model_name = ? AND model_keys = ?", "Delete", "TestActivityModel", id).Find(&log); log.ID == 0 {
  306. t.Errorf("want the log %v, but got %v", "TestActivityModel:"+id, log)
  307. }
  308. }
  309. var log TestActivityLog
  310. if db.Where("action = ? AND model_name = ? AND model_keys = ?", "Delete", "TestActivityModel", "2").Find(&log); log.ID != 0 {
  311. t.Errorf("want skip the create, but still got the record %v", log)
  312. }
  313. }
  314. }