gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package unittest
  4. import (
  5. "context"
  6. "reflect"
  7. "strconv"
  8. "strings"
  9. "code.gitea.io/gitea/models/db"
  10. "github.com/stretchr/testify/assert"
  11. "github.com/stretchr/testify/require"
  12. "xorm.io/builder"
  13. )
  14. const (
  15. // these const values are copied from `models` package to prevent from cycle-import
  16. modelsUserTypeOrganization = 1
  17. modelsRepoWatchModeDont = 2
  18. modelsCommentTypeComment = 0
  19. )
  20. var consistencyCheckMap = make(map[string]func(t TestingT, bean any))
  21. // CheckConsistencyFor test that all matching database entries are consistent
  22. func CheckConsistencyFor(t TestingT, beansToCheck ...any) {
  23. for _, bean := range beansToCheck {
  24. sliceType := reflect.SliceOf(reflect.TypeOf(bean))
  25. sliceValue := reflect.MakeSlice(sliceType, 0, 10)
  26. ptrToSliceValue := reflect.New(sliceType)
  27. ptrToSliceValue.Elem().Set(sliceValue)
  28. assert.NoError(t, db.GetEngine(context.TODO()).Table(bean).Find(ptrToSliceValue.Interface()))
  29. sliceValue = ptrToSliceValue.Elem()
  30. for i := 0; i < sliceValue.Len(); i++ {
  31. entity := sliceValue.Index(i).Interface()
  32. checkForConsistency(t, entity)
  33. }
  34. }
  35. }
  36. func checkForConsistency(t TestingT, bean any) {
  37. tb, err := GetXORMEngine().TableInfo(bean)
  38. assert.NoError(t, err)
  39. f := consistencyCheckMap[tb.Name]
  40. require.NotNil(t, f, "unknown bean type: %#v", bean)
  41. f(t, bean)
  42. }
  43. func init() {
  44. parseBool := func(v string) bool {
  45. b, _ := strconv.ParseBool(v)
  46. return b
  47. }
  48. parseInt := func(v string) int {
  49. i, _ := strconv.Atoi(v)
  50. return i
  51. }
  52. checkForUserConsistency := func(t TestingT, bean any) {
  53. user := reflectionWrap(bean)
  54. AssertCountByCond(t, "repository", builder.Eq{"owner_id": user.int("ID")}, user.int("NumRepos"))
  55. AssertCountByCond(t, "star", builder.Eq{"uid": user.int("ID")}, user.int("NumStars"))
  56. AssertCountByCond(t, "org_user", builder.Eq{"org_id": user.int("ID")}, user.int("NumMembers"))
  57. AssertCountByCond(t, "team", builder.Eq{"org_id": user.int("ID")}, user.int("NumTeams"))
  58. AssertCountByCond(t, "follow", builder.Eq{"user_id": user.int("ID")}, user.int("NumFollowing"))
  59. AssertCountByCond(t, "follow", builder.Eq{"follow_id": user.int("ID")}, user.int("NumFollowers"))
  60. if user.int("Type") != modelsUserTypeOrganization {
  61. assert.Equal(t, 0, user.int("NumMembers"), "Unexpected number of members for user id: %d", user.int("ID"))
  62. assert.Equal(t, 0, user.int("NumTeams"), "Unexpected number of teams for user id: %d", user.int("ID"))
  63. }
  64. }
  65. checkForRepoConsistency := func(t TestingT, bean any) {
  66. repo := reflectionWrap(bean)
  67. assert.Equal(t, repo.str("LowerName"), strings.ToLower(repo.str("Name")), "repo: %+v", repo)
  68. AssertCountByCond(t, "star", builder.Eq{"repo_id": repo.int("ID")}, repo.int("NumStars"))
  69. AssertCountByCond(t, "milestone", builder.Eq{"repo_id": repo.int("ID")}, repo.int("NumMilestones"))
  70. AssertCountByCond(t, "repository", builder.Eq{"fork_id": repo.int("ID")}, repo.int("NumForks"))
  71. if repo.bool("IsFork") {
  72. AssertExistsAndLoadMap(t, "repository", builder.Eq{"id": repo.int("ForkID")})
  73. }
  74. actual := GetCountByCond(t, "watch", builder.Eq{"repo_id": repo.int("ID")}.
  75. And(builder.Neq{"mode": modelsRepoWatchModeDont}))
  76. assert.EqualValues(t, repo.int("NumWatches"), actual,
  77. "Unexpected number of watches for repo id: %d", repo.int("ID"))
  78. actual = GetCountByCond(t, "issue", builder.Eq{"is_pull": false, "repo_id": repo.int("ID")})
  79. assert.EqualValues(t, repo.int("NumIssues"), actual,
  80. "Unexpected number of issues for repo id: %d", repo.int("ID"))
  81. actual = GetCountByCond(t, "issue", builder.Eq{"is_pull": false, "is_closed": true, "repo_id": repo.int("ID")})
  82. assert.EqualValues(t, repo.int("NumClosedIssues"), actual,
  83. "Unexpected number of closed issues for repo id: %d", repo.int("ID"))
  84. actual = GetCountByCond(t, "issue", builder.Eq{"is_pull": true, "repo_id": repo.int("ID")})
  85. assert.EqualValues(t, repo.int("NumPulls"), actual,
  86. "Unexpected number of pulls for repo id: %d", repo.int("ID"))
  87. actual = GetCountByCond(t, "issue", builder.Eq{"is_pull": true, "is_closed": true, "repo_id": repo.int("ID")})
  88. assert.EqualValues(t, repo.int("NumClosedPulls"), actual,
  89. "Unexpected number of closed pulls for repo id: %d", repo.int("ID"))
  90. actual = GetCountByCond(t, "milestone", builder.Eq{"is_closed": true, "repo_id": repo.int("ID")})
  91. assert.EqualValues(t, repo.int("NumClosedMilestones"), actual,
  92. "Unexpected number of closed milestones for repo id: %d", repo.int("ID"))
  93. }
  94. checkForIssueConsistency := func(t TestingT, bean any) {
  95. issue := reflectionWrap(bean)
  96. typeComment := modelsCommentTypeComment
  97. actual := GetCountByCond(t, "comment", builder.Eq{"`type`": typeComment, "issue_id": issue.int("ID")})
  98. assert.EqualValues(t, issue.int("NumComments"), actual, "Unexpected number of comments for issue id: %d", issue.int("ID"))
  99. if issue.bool("IsPull") {
  100. prRow := AssertExistsAndLoadMap(t, "pull_request", builder.Eq{"issue_id": issue.int("ID")})
  101. assert.Equal(t, parseInt(prRow["index"]), issue.int("Index"), "Unexpected index for issue id: %d", issue.int("ID"))
  102. }
  103. }
  104. checkForPullRequestConsistency := func(t TestingT, bean any) {
  105. pr := reflectionWrap(bean)
  106. issueRow := AssertExistsAndLoadMap(t, "issue", builder.Eq{"id": pr.int("IssueID")})
  107. assert.True(t, parseBool(issueRow["is_pull"]))
  108. assert.Equal(t, parseInt(issueRow["index"]), pr.int("Index"), "Unexpected index for pull request id: %d", pr.int("ID"))
  109. }
  110. checkForMilestoneConsistency := func(t TestingT, bean any) {
  111. milestone := reflectionWrap(bean)
  112. AssertCountByCond(t, "issue", builder.Eq{"milestone_id": milestone.int("ID")}, milestone.int("NumIssues"))
  113. actual := GetCountByCond(t, "issue", builder.Eq{"is_closed": true, "milestone_id": milestone.int("ID")})
  114. assert.EqualValues(t, milestone.int("NumClosedIssues"), actual, "Unexpected number of closed issues for milestone id: %d", milestone.int("ID"))
  115. completeness := 0
  116. if milestone.int("NumIssues") > 0 {
  117. completeness = milestone.int("NumClosedIssues") * 100 / milestone.int("NumIssues")
  118. }
  119. assert.Equal(t, completeness, milestone.int("Completeness"))
  120. }
  121. checkForLabelConsistency := func(t TestingT, bean any) {
  122. label := reflectionWrap(bean)
  123. issueLabels, err := db.GetEngine(context.TODO()).Table("issue_label").
  124. Where(builder.Eq{"label_id": label.int("ID")}).
  125. Query()
  126. assert.NoError(t, err)
  127. assert.Len(t, issueLabels, label.int("NumIssues"), "Unexpected number of issue for label id: %d", label.int("ID"))
  128. issueIDs := make([]int, len(issueLabels))
  129. for i, issueLabel := range issueLabels {
  130. issueIDs[i], _ = strconv.Atoi(string(issueLabel["issue_id"]))
  131. }
  132. expected := int64(0)
  133. if len(issueIDs) > 0 {
  134. expected = GetCountByCond(t, "issue", builder.In("id", issueIDs).And(builder.Eq{"is_closed": true}))
  135. }
  136. assert.EqualValues(t, expected, label.int("NumClosedIssues"), "Unexpected number of closed issues for label id: %d", label.int("ID"))
  137. }
  138. checkForTeamConsistency := func(t TestingT, bean any) {
  139. team := reflectionWrap(bean)
  140. AssertCountByCond(t, "team_user", builder.Eq{"team_id": team.int("ID")}, team.int("NumMembers"))
  141. AssertCountByCond(t, "team_repo", builder.Eq{"team_id": team.int("ID")}, team.int("NumRepos"))
  142. }
  143. checkForActionConsistency := func(t TestingT, bean any) {
  144. action := reflectionWrap(bean)
  145. if action.int("RepoID") != 1700 { // dangling intentional
  146. repoRow := AssertExistsAndLoadMap(t, "repository", builder.Eq{"id": action.int("RepoID")})
  147. assert.Equal(t, parseBool(repoRow["is_private"]), action.bool("IsPrivate"), "Unexpected is_private field for action id: %d", action.int("ID"))
  148. }
  149. }
  150. consistencyCheckMap["user"] = checkForUserConsistency
  151. consistencyCheckMap["repository"] = checkForRepoConsistency
  152. consistencyCheckMap["issue"] = checkForIssueConsistency
  153. consistencyCheckMap["pull_request"] = checkForPullRequestConsistency
  154. consistencyCheckMap["milestone"] = checkForMilestoneConsistency
  155. consistencyCheckMap["label"] = checkForLabelConsistency
  156. consistencyCheckMap["team"] = checkForTeamConsistency
  157. consistencyCheckMap["action"] = checkForActionConsistency
  158. }