actions_test.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. package database
  2. import (
  3. "context"
  4. "os"
  5. "testing"
  6. "time"
  7. "github.com/gogs/git-module"
  8. "github.com/stretchr/testify/assert"
  9. "github.com/stretchr/testify/require"
  10. "gorm.io/gorm"
  11. "gogs.io/gogs/internal/conf"
  12. )
  13. func TestIssueReferencePattern(t *testing.T) {
  14. tests := []struct {
  15. name string
  16. message string
  17. want []string
  18. }{
  19. {
  20. name: "no match",
  21. message: "Hello world!",
  22. want: nil,
  23. },
  24. {
  25. name: "contains issue numbers",
  26. message: "#123 is fixed, and #456 is WIP",
  27. want: []string{"#123", " #456"},
  28. },
  29. {
  30. name: "contains full issue references",
  31. message: "#123 is fixed, and user/repo#456 is WIP",
  32. want: []string{"#123", " user/repo#456"},
  33. },
  34. }
  35. for _, test := range tests {
  36. t.Run(test.name, func(t *testing.T) {
  37. got := issueReferencePattern.FindAllString(test.message, -1)
  38. assert.Equal(t, test.want, got)
  39. })
  40. }
  41. }
  42. func TestAction_BeforeCreate(t *testing.T) {
  43. now := time.Now()
  44. db := &gorm.DB{
  45. Config: &gorm.Config{
  46. SkipDefaultTransaction: true,
  47. NowFunc: func() time.Time {
  48. return now
  49. },
  50. },
  51. }
  52. t.Run("CreatedUnix has been set", func(t *testing.T) {
  53. action := &Action{
  54. CreatedUnix: 1,
  55. }
  56. _ = action.BeforeCreate(db)
  57. assert.Equal(t, int64(1), action.CreatedUnix)
  58. })
  59. t.Run("CreatedUnix has not been set", func(t *testing.T) {
  60. action := &Action{}
  61. _ = action.BeforeCreate(db)
  62. assert.Equal(t, db.NowFunc().Unix(), action.CreatedUnix)
  63. })
  64. }
  65. func TestAction_AfterFind(t *testing.T) {
  66. now := time.Now()
  67. db := &gorm.DB{
  68. Config: &gorm.Config{
  69. SkipDefaultTransaction: true,
  70. NowFunc: func() time.Time {
  71. return now
  72. },
  73. },
  74. }
  75. action := &Action{
  76. CreatedUnix: now.Unix(),
  77. }
  78. _ = action.AfterFind(db)
  79. assert.Equal(t, action.CreatedUnix, action.Created.Unix())
  80. }
  81. func TestActions(t *testing.T) {
  82. if testing.Short() {
  83. t.Skip()
  84. }
  85. ctx := context.Background()
  86. t.Parallel()
  87. s := &ActionsStore{
  88. db: newTestDB(t, "ActionsStore"),
  89. }
  90. for _, tc := range []struct {
  91. name string
  92. test func(t *testing.T, ctx context.Context, s *ActionsStore)
  93. }{
  94. {"CommitRepo", actionsCommitRepo},
  95. {"ListByOrganization", actionsListByOrganization},
  96. {"ListByUser", actionsListByUser},
  97. {"MergePullRequest", actionsMergePullRequest},
  98. {"MirrorSyncCreate", actionsMirrorSyncCreate},
  99. {"MirrorSyncDelete", actionsMirrorSyncDelete},
  100. {"MirrorSyncPush", actionsMirrorSyncPush},
  101. {"NewRepo", actionsNewRepo},
  102. {"PushTag", actionsPushTag},
  103. {"RenameRepo", actionsRenameRepo},
  104. {"TransferRepo", actionsTransferRepo},
  105. } {
  106. t.Run(tc.name, func(t *testing.T) {
  107. t.Cleanup(func() {
  108. err := clearTables(t, s.db)
  109. require.NoError(t, err)
  110. })
  111. tc.test(t, ctx, s)
  112. })
  113. if t.Failed() {
  114. break
  115. }
  116. }
  117. }
  118. func actionsCommitRepo(t *testing.T, ctx context.Context, s *ActionsStore) {
  119. alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  120. require.NoError(t, err)
  121. repo, err := newReposStore(s.db).Create(ctx,
  122. alice.ID,
  123. CreateRepoOptions{
  124. Name: "example",
  125. },
  126. )
  127. require.NoError(t, err)
  128. now := time.Unix(1588568886, 0).UTC()
  129. conf.SetMockSSH(t, conf.SSHOpts{})
  130. conf.SetMockUI(t, conf.UIOpts{
  131. User: conf.UIUserOpts{
  132. NewsFeedPagingNum: 20,
  133. },
  134. })
  135. t.Run("new commit", func(t *testing.T) {
  136. t.Cleanup(func() {
  137. err := s.db.Session(&gorm.Session{AllowGlobalUpdate: true}).WithContext(ctx).Delete(new(Action)).Error
  138. require.NoError(t, err)
  139. })
  140. err = s.CommitRepo(ctx,
  141. CommitRepoOptions{
  142. PusherName: alice.Name,
  143. Owner: alice,
  144. Repo: repo,
  145. RefFullName: "refs/heads/main",
  146. OldCommitID: "ca82a6dff817ec66f44342007202690a93763949",
  147. NewCommitID: "085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7",
  148. Commits: CommitsToPushCommits(
  149. []*git.Commit{
  150. {
  151. ID: git.MustIDFromString("085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7"),
  152. Author: &git.Signature{
  153. Name: "alice",
  154. Email: "alice@example.com",
  155. When: now,
  156. },
  157. Committer: &git.Signature{
  158. Name: "alice",
  159. Email: "alice@example.com",
  160. When: now,
  161. },
  162. Message: "A random commit",
  163. },
  164. },
  165. ),
  166. },
  167. )
  168. require.NoError(t, err)
  169. got, err := s.ListByUser(ctx, alice.ID, alice.ID, 0, false)
  170. require.NoError(t, err)
  171. require.Len(t, got, 1)
  172. got[0].ID = 0
  173. want := []*Action{
  174. {
  175. UserID: alice.ID,
  176. OpType: ActionCommitRepo,
  177. ActUserID: alice.ID,
  178. ActUserName: alice.Name,
  179. RepoID: repo.ID,
  180. RepoUserName: alice.Name,
  181. RepoName: repo.Name,
  182. RefName: "main",
  183. IsPrivate: false,
  184. Content: `{"Len":1,"Commits":[{"Sha1":"085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7","Message":"A random commit","AuthorEmail":"alice@example.com","AuthorName":"alice","CommitterEmail":"alice@example.com","CommitterName":"alice","Timestamp":"2020-05-04T05:08:06Z"}],"CompareURL":"alice/example/compare/ca82a6dff817ec66f44342007202690a93763949...085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7"}`,
  185. CreatedUnix: s.db.NowFunc().Unix(),
  186. },
  187. }
  188. want[0].Created = time.Unix(want[0].CreatedUnix, 0)
  189. assert.Equal(t, want, got)
  190. })
  191. t.Run("new ref", func(t *testing.T) {
  192. t.Cleanup(func() {
  193. err := s.db.Session(&gorm.Session{AllowGlobalUpdate: true}).WithContext(ctx).Delete(new(Action)).Error
  194. require.NoError(t, err)
  195. })
  196. err = s.CommitRepo(ctx,
  197. CommitRepoOptions{
  198. PusherName: alice.Name,
  199. Owner: alice,
  200. Repo: repo,
  201. RefFullName: "refs/heads/main",
  202. OldCommitID: git.EmptyID,
  203. NewCommitID: "085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7",
  204. Commits: CommitsToPushCommits(
  205. []*git.Commit{
  206. {
  207. ID: git.MustIDFromString("085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7"),
  208. Author: &git.Signature{
  209. Name: "alice",
  210. Email: "alice@example.com",
  211. When: now,
  212. },
  213. Committer: &git.Signature{
  214. Name: "alice",
  215. Email: "alice@example.com",
  216. When: now,
  217. },
  218. Message: "A random commit",
  219. },
  220. },
  221. ),
  222. },
  223. )
  224. require.NoError(t, err)
  225. got, err := s.ListByUser(ctx, alice.ID, alice.ID, 0, false)
  226. require.NoError(t, err)
  227. require.Len(t, got, 2)
  228. got[0].ID = 0
  229. got[1].ID = 0
  230. want := []*Action{
  231. {
  232. UserID: alice.ID,
  233. OpType: ActionCommitRepo,
  234. ActUserID: alice.ID,
  235. ActUserName: alice.Name,
  236. RepoID: repo.ID,
  237. RepoUserName: alice.Name,
  238. RepoName: repo.Name,
  239. RefName: "main",
  240. IsPrivate: false,
  241. Content: `{"Len":1,"Commits":[{"Sha1":"085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7","Message":"A random commit","AuthorEmail":"alice@example.com","AuthorName":"alice","CommitterEmail":"alice@example.com","CommitterName":"alice","Timestamp":"2020-05-04T05:08:06Z"}],"CompareURL":""}`,
  242. CreatedUnix: s.db.NowFunc().Unix(),
  243. },
  244. {
  245. UserID: alice.ID,
  246. OpType: ActionCreateBranch,
  247. ActUserID: alice.ID,
  248. ActUserName: alice.Name,
  249. RepoID: repo.ID,
  250. RepoUserName: alice.Name,
  251. RepoName: repo.Name,
  252. RefName: "main",
  253. IsPrivate: false,
  254. Content: `{"Len":1,"Commits":[{"Sha1":"085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7","Message":"A random commit","AuthorEmail":"alice@example.com","AuthorName":"alice","CommitterEmail":"alice@example.com","CommitterName":"alice","Timestamp":"2020-05-04T05:08:06Z"}],"CompareURL":""}`,
  255. CreatedUnix: s.db.NowFunc().Unix(),
  256. },
  257. }
  258. want[0].Created = time.Unix(want[0].CreatedUnix, 0)
  259. want[1].Created = time.Unix(want[1].CreatedUnix, 0)
  260. assert.Equal(t, want, got)
  261. })
  262. t.Run("delete ref", func(t *testing.T) {
  263. t.Cleanup(func() {
  264. err := s.db.Session(&gorm.Session{AllowGlobalUpdate: true}).WithContext(ctx).Delete(new(Action)).Error
  265. require.NoError(t, err)
  266. })
  267. err = s.CommitRepo(ctx,
  268. CommitRepoOptions{
  269. PusherName: alice.Name,
  270. Owner: alice,
  271. Repo: repo,
  272. RefFullName: "refs/heads/main",
  273. OldCommitID: "ca82a6dff817ec66f44342007202690a93763949",
  274. NewCommitID: git.EmptyID,
  275. },
  276. )
  277. require.NoError(t, err)
  278. got, err := s.ListByUser(ctx, alice.ID, alice.ID, 0, false)
  279. require.NoError(t, err)
  280. require.Len(t, got, 1)
  281. got[0].ID = 0
  282. want := []*Action{
  283. {
  284. UserID: alice.ID,
  285. OpType: ActionDeleteBranch,
  286. ActUserID: alice.ID,
  287. ActUserName: alice.Name,
  288. RepoID: repo.ID,
  289. RepoUserName: alice.Name,
  290. RepoName: repo.Name,
  291. RefName: "main",
  292. IsPrivate: false,
  293. CreatedUnix: s.db.NowFunc().Unix(),
  294. },
  295. }
  296. want[0].Created = time.Unix(want[0].CreatedUnix, 0)
  297. assert.Equal(t, want, got)
  298. })
  299. }
  300. func actionsListByOrganization(t *testing.T, ctx context.Context, s *ActionsStore) {
  301. if os.Getenv("GOGS_DATABASE_TYPE") != "postgres" {
  302. t.Skip("Skipping testing with not using PostgreSQL")
  303. return
  304. }
  305. conf.SetMockUI(t,
  306. conf.UIOpts{
  307. User: conf.UIUserOpts{
  308. NewsFeedPagingNum: 20,
  309. },
  310. },
  311. )
  312. tests := []struct {
  313. name string
  314. orgID int64
  315. actorID int64
  316. afterID int64
  317. want string
  318. }{
  319. {
  320. name: "no afterID",
  321. orgID: 1,
  322. actorID: 1,
  323. afterID: 0,
  324. want: `SELECT * FROM "action" WHERE user_id = 1 AND (true OR id < 0) AND repo_id IN (SELECT repository.id FROM "repository" JOIN team_repo ON repository.id = team_repo.repo_id WHERE team_repo.team_id IN (SELECT team_id FROM "team_user" WHERE team_user.org_id = 1 AND uid = 1) OR (repository.is_private = false AND repository.is_unlisted = false)) ORDER BY id DESC LIMIT 20`,
  325. },
  326. {
  327. name: "has afterID",
  328. orgID: 1,
  329. actorID: 1,
  330. afterID: 5,
  331. want: `SELECT * FROM "action" WHERE user_id = 1 AND (false OR id < 5) AND repo_id IN (SELECT repository.id FROM "repository" JOIN team_repo ON repository.id = team_repo.repo_id WHERE team_repo.team_id IN (SELECT team_id FROM "team_user" WHERE team_user.org_id = 1 AND uid = 1) OR (repository.is_private = false AND repository.is_unlisted = false)) ORDER BY id DESC LIMIT 20`,
  332. },
  333. }
  334. for _, test := range tests {
  335. t.Run(test.name, func(t *testing.T) {
  336. got := s.db.ToSQL(func(tx *gorm.DB) *gorm.DB {
  337. return newActionsStore(tx).listByOrganization(ctx, test.orgID, test.actorID, test.afterID).Find(new(Action))
  338. })
  339. assert.Equal(t, test.want, got)
  340. })
  341. }
  342. }
  343. func actionsListByUser(t *testing.T, ctx context.Context, s *ActionsStore) {
  344. if os.Getenv("GOGS_DATABASE_TYPE") != "postgres" {
  345. t.Skip("Skipping testing with not using PostgreSQL")
  346. return
  347. }
  348. conf.SetMockUI(t,
  349. conf.UIOpts{
  350. User: conf.UIUserOpts{
  351. NewsFeedPagingNum: 20,
  352. },
  353. },
  354. )
  355. tests := []struct {
  356. name string
  357. userID int64
  358. actorID int64
  359. afterID int64
  360. isProfile bool
  361. want string
  362. }{
  363. {
  364. name: "same user no afterID not in profile",
  365. userID: 1,
  366. actorID: 1,
  367. afterID: 0,
  368. isProfile: false,
  369. want: `SELECT * FROM "action" WHERE user_id = 1 AND (true OR id < 0) AND (true OR (is_private = false AND act_user_id = 1)) ORDER BY id DESC LIMIT 20`,
  370. },
  371. {
  372. name: "same user no afterID in profile",
  373. userID: 1,
  374. actorID: 1,
  375. afterID: 0,
  376. isProfile: true,
  377. want: `SELECT * FROM "action" WHERE user_id = 1 AND (true OR id < 0) AND (true OR (is_private = false AND act_user_id = 1)) ORDER BY id DESC LIMIT 20`,
  378. },
  379. {
  380. name: "same user has afterID not in profile",
  381. userID: 1,
  382. actorID: 1,
  383. afterID: 5,
  384. isProfile: false,
  385. want: `SELECT * FROM "action" WHERE user_id = 1 AND (false OR id < 5) AND (true OR (is_private = false AND act_user_id = 1)) ORDER BY id DESC LIMIT 20`,
  386. },
  387. {
  388. name: "different user no afterID in profile",
  389. userID: 1,
  390. actorID: 2,
  391. afterID: 0,
  392. isProfile: true,
  393. want: `SELECT * FROM "action" WHERE user_id = 1 AND (true OR id < 0) AND (false OR (is_private = false AND act_user_id = 1)) ORDER BY id DESC LIMIT 20`,
  394. },
  395. }
  396. for _, test := range tests {
  397. t.Run(test.name, func(t *testing.T) {
  398. got := s.db.ToSQL(func(tx *gorm.DB) *gorm.DB {
  399. return newActionsStore(tx).listByUser(ctx, test.userID, test.actorID, test.afterID, test.isProfile).Find(new(Action))
  400. })
  401. assert.Equal(t, test.want, got)
  402. })
  403. }
  404. }
  405. func actionsMergePullRequest(t *testing.T, ctx context.Context, s *ActionsStore) {
  406. conf.SetMockUI(t, conf.UIOpts{
  407. User: conf.UIUserOpts{
  408. NewsFeedPagingNum: 20,
  409. },
  410. })
  411. alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  412. require.NoError(t, err)
  413. repo, err := newReposStore(s.db).Create(ctx,
  414. alice.ID,
  415. CreateRepoOptions{
  416. Name: "example",
  417. },
  418. )
  419. require.NoError(t, err)
  420. err = s.MergePullRequest(ctx,
  421. alice,
  422. alice,
  423. repo,
  424. &Issue{
  425. Index: 1,
  426. Title: "Fix issue 1",
  427. },
  428. )
  429. require.NoError(t, err)
  430. got, err := s.ListByUser(ctx, alice.ID, alice.ID, 0, false)
  431. require.NoError(t, err)
  432. require.Len(t, got, 1)
  433. got[0].ID = 0
  434. want := []*Action{
  435. {
  436. UserID: alice.ID,
  437. OpType: ActionMergePullRequest,
  438. ActUserID: alice.ID,
  439. ActUserName: alice.Name,
  440. RepoID: repo.ID,
  441. RepoUserName: alice.Name,
  442. RepoName: repo.Name,
  443. IsPrivate: false,
  444. Content: `1|Fix issue 1`,
  445. CreatedUnix: s.db.NowFunc().Unix(),
  446. },
  447. }
  448. want[0].Created = time.Unix(want[0].CreatedUnix, 0)
  449. assert.Equal(t, want, got)
  450. }
  451. func actionsMirrorSyncCreate(t *testing.T, ctx context.Context, s *ActionsStore) {
  452. conf.SetMockUI(t, conf.UIOpts{
  453. User: conf.UIUserOpts{
  454. NewsFeedPagingNum: 20,
  455. },
  456. })
  457. alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  458. require.NoError(t, err)
  459. repo, err := newReposStore(s.db).Create(ctx,
  460. alice.ID,
  461. CreateRepoOptions{
  462. Name: "example",
  463. },
  464. )
  465. require.NoError(t, err)
  466. err = s.MirrorSyncCreate(ctx,
  467. alice,
  468. repo,
  469. "main",
  470. )
  471. require.NoError(t, err)
  472. got, err := s.ListByUser(ctx, alice.ID, alice.ID, 0, false)
  473. require.NoError(t, err)
  474. require.Len(t, got, 1)
  475. got[0].ID = 0
  476. want := []*Action{
  477. {
  478. UserID: alice.ID,
  479. OpType: ActionMirrorSyncCreate,
  480. ActUserID: alice.ID,
  481. ActUserName: alice.Name,
  482. RepoID: repo.ID,
  483. RepoUserName: alice.Name,
  484. RepoName: repo.Name,
  485. RefName: "main",
  486. IsPrivate: false,
  487. CreatedUnix: s.db.NowFunc().Unix(),
  488. },
  489. }
  490. want[0].Created = time.Unix(want[0].CreatedUnix, 0)
  491. assert.Equal(t, want, got)
  492. }
  493. func actionsMirrorSyncDelete(t *testing.T, ctx context.Context, s *ActionsStore) {
  494. conf.SetMockUI(t, conf.UIOpts{
  495. User: conf.UIUserOpts{
  496. NewsFeedPagingNum: 20,
  497. },
  498. })
  499. alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  500. require.NoError(t, err)
  501. repo, err := newReposStore(s.db).Create(ctx,
  502. alice.ID,
  503. CreateRepoOptions{
  504. Name: "example",
  505. },
  506. )
  507. require.NoError(t, err)
  508. err = s.MirrorSyncDelete(ctx,
  509. alice,
  510. repo,
  511. "main",
  512. )
  513. require.NoError(t, err)
  514. got, err := s.ListByUser(ctx, alice.ID, alice.ID, 0, false)
  515. require.NoError(t, err)
  516. require.Len(t, got, 1)
  517. got[0].ID = 0
  518. want := []*Action{
  519. {
  520. UserID: alice.ID,
  521. OpType: ActionMirrorSyncDelete,
  522. ActUserID: alice.ID,
  523. ActUserName: alice.Name,
  524. RepoID: repo.ID,
  525. RepoUserName: alice.Name,
  526. RepoName: repo.Name,
  527. RefName: "main",
  528. IsPrivate: false,
  529. CreatedUnix: s.db.NowFunc().Unix(),
  530. },
  531. }
  532. want[0].Created = time.Unix(want[0].CreatedUnix, 0)
  533. assert.Equal(t, want, got)
  534. }
  535. func actionsMirrorSyncPush(t *testing.T, ctx context.Context, s *ActionsStore) {
  536. conf.SetMockUI(t, conf.UIOpts{
  537. User: conf.UIUserOpts{
  538. NewsFeedPagingNum: 20,
  539. },
  540. })
  541. alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  542. require.NoError(t, err)
  543. repo, err := newReposStore(s.db).Create(ctx,
  544. alice.ID,
  545. CreateRepoOptions{
  546. Name: "example",
  547. },
  548. )
  549. require.NoError(t, err)
  550. now := time.Unix(1588568886, 0).UTC()
  551. err = s.MirrorSyncPush(ctx,
  552. MirrorSyncPushOptions{
  553. Owner: alice,
  554. Repo: repo,
  555. RefName: "main",
  556. OldCommitID: "ca82a6dff817ec66f44342007202690a93763949",
  557. NewCommitID: "085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7",
  558. Commits: CommitsToPushCommits(
  559. []*git.Commit{
  560. {
  561. ID: git.MustIDFromString("085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7"),
  562. Author: &git.Signature{
  563. Name: "alice",
  564. Email: "alice@example.com",
  565. When: now,
  566. },
  567. Committer: &git.Signature{
  568. Name: "alice",
  569. Email: "alice@example.com",
  570. When: now,
  571. },
  572. Message: "A random commit",
  573. },
  574. },
  575. ),
  576. },
  577. )
  578. require.NoError(t, err)
  579. got, err := s.ListByUser(ctx, alice.ID, alice.ID, 0, false)
  580. require.NoError(t, err)
  581. require.Len(t, got, 1)
  582. got[0].ID = 0
  583. want := []*Action{
  584. {
  585. UserID: alice.ID,
  586. OpType: ActionMirrorSyncPush,
  587. ActUserID: alice.ID,
  588. ActUserName: alice.Name,
  589. RepoID: repo.ID,
  590. RepoUserName: alice.Name,
  591. RepoName: repo.Name,
  592. RefName: "main",
  593. IsPrivate: false,
  594. Content: `{"Len":1,"Commits":[{"Sha1":"085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7","Message":"A random commit","AuthorEmail":"alice@example.com","AuthorName":"alice","CommitterEmail":"alice@example.com","CommitterName":"alice","Timestamp":"2020-05-04T05:08:06Z"}],"CompareURL":"alice/example/compare/ca82a6dff817ec66f44342007202690a93763949...085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7"}`,
  595. CreatedUnix: s.db.NowFunc().Unix(),
  596. },
  597. }
  598. want[0].Created = time.Unix(want[0].CreatedUnix, 0)
  599. assert.Equal(t, want, got)
  600. }
  601. func actionsNewRepo(t *testing.T, ctx context.Context, s *ActionsStore) {
  602. conf.SetMockUI(t, conf.UIOpts{
  603. User: conf.UIUserOpts{
  604. NewsFeedPagingNum: 20,
  605. },
  606. })
  607. alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  608. require.NoError(t, err)
  609. repo, err := newReposStore(s.db).Create(ctx,
  610. alice.ID,
  611. CreateRepoOptions{
  612. Name: "example",
  613. },
  614. )
  615. require.NoError(t, err)
  616. t.Run("new repo", func(t *testing.T) {
  617. t.Cleanup(func() {
  618. err := s.db.Session(&gorm.Session{AllowGlobalUpdate: true}).WithContext(ctx).Delete(new(Action)).Error
  619. require.NoError(t, err)
  620. })
  621. err = s.NewRepo(ctx, alice, alice, repo)
  622. require.NoError(t, err)
  623. got, err := s.ListByUser(ctx, alice.ID, alice.ID, 0, false)
  624. require.NoError(t, err)
  625. require.Len(t, got, 1)
  626. got[0].ID = 0
  627. want := []*Action{
  628. {
  629. UserID: alice.ID,
  630. OpType: ActionCreateRepo,
  631. ActUserID: alice.ID,
  632. ActUserName: alice.Name,
  633. RepoID: repo.ID,
  634. RepoUserName: alice.Name,
  635. RepoName: repo.Name,
  636. IsPrivate: false,
  637. CreatedUnix: s.db.NowFunc().Unix(),
  638. },
  639. }
  640. want[0].Created = time.Unix(want[0].CreatedUnix, 0)
  641. assert.Equal(t, want, got)
  642. })
  643. t.Run("fork repo", func(t *testing.T) {
  644. t.Cleanup(func() {
  645. err := s.db.Session(&gorm.Session{AllowGlobalUpdate: true}).WithContext(ctx).Delete(new(Action)).Error
  646. require.NoError(t, err)
  647. })
  648. repo.IsFork = true
  649. err = s.NewRepo(ctx, alice, alice, repo)
  650. require.NoError(t, err)
  651. got, err := s.ListByUser(ctx, alice.ID, alice.ID, 0, false)
  652. require.NoError(t, err)
  653. require.Len(t, got, 1)
  654. got[0].ID = 0
  655. want := []*Action{
  656. {
  657. UserID: alice.ID,
  658. OpType: ActionForkRepo,
  659. ActUserID: alice.ID,
  660. ActUserName: alice.Name,
  661. RepoID: repo.ID,
  662. RepoUserName: alice.Name,
  663. RepoName: repo.Name,
  664. IsPrivate: false,
  665. CreatedUnix: s.db.NowFunc().Unix(),
  666. },
  667. }
  668. want[0].Created = time.Unix(want[0].CreatedUnix, 0)
  669. assert.Equal(t, want, got)
  670. })
  671. }
  672. func actionsPushTag(t *testing.T, ctx context.Context, s *ActionsStore) {
  673. // NOTE: We set a noop mock here to avoid data race with other tests that writes
  674. // to the mock server because this function holds a lock.
  675. conf.SetMockServer(t, conf.ServerOpts{})
  676. conf.SetMockSSH(t, conf.SSHOpts{})
  677. conf.SetMockUI(t, conf.UIOpts{
  678. User: conf.UIUserOpts{
  679. NewsFeedPagingNum: 20,
  680. },
  681. })
  682. alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  683. require.NoError(t, err)
  684. repo, err := newReposStore(s.db).Create(ctx,
  685. alice.ID,
  686. CreateRepoOptions{
  687. Name: "example",
  688. },
  689. )
  690. require.NoError(t, err)
  691. t.Run("new tag", func(t *testing.T) {
  692. t.Cleanup(func() {
  693. err := s.db.Session(&gorm.Session{AllowGlobalUpdate: true}).WithContext(ctx).Delete(new(Action)).Error
  694. require.NoError(t, err)
  695. })
  696. err = s.PushTag(ctx,
  697. PushTagOptions{
  698. Owner: alice,
  699. Repo: repo,
  700. PusherName: alice.Name,
  701. RefFullName: "refs/tags/v1.0.0",
  702. NewCommitID: "085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7",
  703. },
  704. )
  705. require.NoError(t, err)
  706. got, err := s.ListByUser(ctx, alice.ID, alice.ID, 0, false)
  707. require.NoError(t, err)
  708. require.Len(t, got, 1)
  709. got[0].ID = 0
  710. want := []*Action{
  711. {
  712. UserID: alice.ID,
  713. OpType: ActionPushTag,
  714. ActUserID: alice.ID,
  715. ActUserName: alice.Name,
  716. RepoID: repo.ID,
  717. RepoUserName: alice.Name,
  718. RepoName: repo.Name,
  719. RefName: "v1.0.0",
  720. IsPrivate: false,
  721. CreatedUnix: s.db.NowFunc().Unix(),
  722. },
  723. }
  724. want[0].Created = time.Unix(want[0].CreatedUnix, 0)
  725. assert.Equal(t, want, got)
  726. })
  727. t.Run("delete tag", func(t *testing.T) {
  728. t.Cleanup(func() {
  729. err := s.db.Session(&gorm.Session{AllowGlobalUpdate: true}).WithContext(ctx).Delete(new(Action)).Error
  730. require.NoError(t, err)
  731. })
  732. err = s.PushTag(ctx,
  733. PushTagOptions{
  734. Owner: alice,
  735. Repo: repo,
  736. PusherName: alice.Name,
  737. RefFullName: "refs/tags/v1.0.0",
  738. NewCommitID: git.EmptyID,
  739. },
  740. )
  741. require.NoError(t, err)
  742. got, err := s.ListByUser(ctx, alice.ID, alice.ID, 0, false)
  743. require.NoError(t, err)
  744. require.Len(t, got, 1)
  745. got[0].ID = 0
  746. want := []*Action{
  747. {
  748. UserID: alice.ID,
  749. OpType: ActionDeleteTag,
  750. ActUserID: alice.ID,
  751. ActUserName: alice.Name,
  752. RepoID: repo.ID,
  753. RepoUserName: alice.Name,
  754. RepoName: repo.Name,
  755. RefName: "v1.0.0",
  756. IsPrivate: false,
  757. CreatedUnix: s.db.NowFunc().Unix(),
  758. },
  759. }
  760. want[0].Created = time.Unix(want[0].CreatedUnix, 0)
  761. assert.Equal(t, want, got)
  762. })
  763. }
  764. func actionsRenameRepo(t *testing.T, ctx context.Context, s *ActionsStore) {
  765. conf.SetMockUI(t, conf.UIOpts{
  766. User: conf.UIUserOpts{
  767. NewsFeedPagingNum: 20,
  768. },
  769. })
  770. alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  771. require.NoError(t, err)
  772. repo, err := newReposStore(s.db).Create(ctx,
  773. alice.ID,
  774. CreateRepoOptions{
  775. Name: "example",
  776. },
  777. )
  778. require.NoError(t, err)
  779. err = s.RenameRepo(ctx, alice, alice, "oldExample", repo)
  780. require.NoError(t, err)
  781. got, err := s.ListByUser(ctx, alice.ID, alice.ID, 0, false)
  782. require.NoError(t, err)
  783. require.Len(t, got, 1)
  784. got[0].ID = 0
  785. want := []*Action{
  786. {
  787. UserID: alice.ID,
  788. OpType: ActionRenameRepo,
  789. ActUserID: alice.ID,
  790. ActUserName: alice.Name,
  791. RepoID: repo.ID,
  792. RepoUserName: alice.Name,
  793. RepoName: repo.Name,
  794. IsPrivate: false,
  795. Content: "oldExample",
  796. CreatedUnix: s.db.NowFunc().Unix(),
  797. },
  798. }
  799. want[0].Created = time.Unix(want[0].CreatedUnix, 0)
  800. assert.Equal(t, want, got)
  801. }
  802. func actionsTransferRepo(t *testing.T, ctx context.Context, s *ActionsStore) {
  803. conf.SetMockUI(t, conf.UIOpts{
  804. User: conf.UIUserOpts{
  805. NewsFeedPagingNum: 20,
  806. },
  807. })
  808. alice, err := newUsersStore(s.db).Create(ctx, "alice", "alice@example.com", CreateUserOptions{})
  809. require.NoError(t, err)
  810. bob, err := newUsersStore(s.db).Create(ctx, "bob", "bob@example.com", CreateUserOptions{})
  811. require.NoError(t, err)
  812. repo, err := newReposStore(s.db).Create(ctx,
  813. alice.ID,
  814. CreateRepoOptions{
  815. Name: "example",
  816. },
  817. )
  818. require.NoError(t, err)
  819. err = s.TransferRepo(ctx, alice, alice, bob, repo)
  820. require.NoError(t, err)
  821. got, err := s.ListByUser(ctx, alice.ID, alice.ID, 0, false)
  822. require.NoError(t, err)
  823. require.Len(t, got, 1)
  824. got[0].ID = 0
  825. want := []*Action{
  826. {
  827. UserID: alice.ID,
  828. OpType: ActionTransferRepo,
  829. ActUserID: alice.ID,
  830. ActUserName: alice.Name,
  831. RepoID: repo.ID,
  832. RepoUserName: bob.Name,
  833. RepoName: repo.Name,
  834. IsPrivate: false,
  835. Content: "alice/example",
  836. CreatedUnix: s.db.NowFunc().Unix(),
  837. },
  838. }
  839. want[0].Created = time.Unix(want[0].CreatedUnix, 0)
  840. assert.Equal(t, want, got)
  841. }