Skip to content

Commit e19b965

Browse files
realaravinthLoïc Dachary
and
Loïc Dachary
authored
GitLab reviews may not have the updated_at field set (#18450)
* GitLab reviews may not have the updated_at field set Fallback to created_at if that the case and to time.Now() if it is also missing. Fixes: 18434 * use assert.WithinDuration Co-authored-by: Loïc Dachary <[email protected]>
1 parent 2ad74a5 commit e19b965

File tree

3 files changed

+159
-10
lines changed

3 files changed

+159
-10
lines changed

services/migrations/gitlab.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -640,13 +640,22 @@ func (g *GitlabDownloader) GetReviews(context base.IssueContext) ([]*base.Review
640640
return nil, err
641641
}
642642

643+
var createdAt time.Time
644+
if approvals.CreatedAt != nil {
645+
createdAt = *approvals.CreatedAt
646+
} else if approvals.UpdatedAt != nil {
647+
createdAt = *approvals.UpdatedAt
648+
} else {
649+
createdAt = time.Now()
650+
}
651+
643652
reviews := make([]*base.Review, 0, len(approvals.ApprovedBy))
644653
for _, user := range approvals.ApprovedBy {
645654
reviews = append(reviews, &base.Review{
646655
IssueIndex: context.LocalID(),
647656
ReviewerID: int64(user.User.ID),
648657
ReviewerName: user.User.Username,
649-
CreatedAt: *approvals.UpdatedAt,
658+
CreatedAt: createdAt,
650659
// All we get are approvals
651660
State: base.ReviewStateApproved,
652661
})

services/migrations/gitlab_test.go

+140
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ import (
88
"context"
99
"fmt"
1010
"net/http"
11+
"net/http/httptest"
1112
"os"
13+
"strconv"
1214
"testing"
1315
"time"
1416

1517
base "code.gitea.io/gitea/modules/migration"
1618

1719
"github.com/stretchr/testify/assert"
20+
"github.com/xanzy/go-gitlab"
1821
)
1922

2023
func TestGitlabDownloadRepo(t *testing.T) {
@@ -310,12 +313,14 @@ func TestGitlabDownloadRepo(t *testing.T) {
310313
assert.NoError(t, err)
311314
assertReviewsEqual(t, []*base.Review{
312315
{
316+
IssueIndex: 1,
313317
ReviewerID: 4102996,
314318
ReviewerName: "zeripath",
315319
CreatedAt: time.Date(2019, 11, 28, 16, 2, 8, 377000000, time.UTC),
316320
State: "APPROVED",
317321
},
318322
{
323+
IssueIndex: 1,
319324
ReviewerID: 527793,
320325
ReviewerName: "axifive",
321326
CreatedAt: time.Date(2019, 11, 28, 16, 2, 8, 377000000, time.UTC),
@@ -327,10 +332,145 @@ func TestGitlabDownloadRepo(t *testing.T) {
327332
assert.NoError(t, err)
328333
assertReviewsEqual(t, []*base.Review{
329334
{
335+
IssueIndex: 2,
330336
ReviewerID: 4575606,
331337
ReviewerName: "real6543",
332338
CreatedAt: time.Date(2020, 4, 19, 19, 24, 21, 108000000, time.UTC),
333339
State: "APPROVED",
334340
},
335341
}, rvs)
336342
}
343+
344+
func gitlabClientMockSetup(t *testing.T) (*http.ServeMux, *httptest.Server, *gitlab.Client) {
345+
// mux is the HTTP request multiplexer used with the test server.
346+
mux := http.NewServeMux()
347+
348+
// server is a test HTTP server used to provide mock API responses.
349+
server := httptest.NewServer(mux)
350+
351+
// client is the Gitlab client being tested.
352+
client, err := gitlab.NewClient("", gitlab.WithBaseURL(server.URL))
353+
if err != nil {
354+
server.Close()
355+
t.Fatalf("Failed to create client: %v", err)
356+
}
357+
358+
return mux, server, client
359+
}
360+
361+
func gitlabClientMockTeardown(server *httptest.Server) {
362+
server.Close()
363+
}
364+
365+
type reviewTestCase struct {
366+
repoID, prID, reviewerID int
367+
reviewerName string
368+
createdAt, updatedAt *time.Time
369+
expectedCreatedAt time.Time
370+
}
371+
372+
func convertTestCase(t reviewTestCase) (func(w http.ResponseWriter, r *http.Request), base.Review) {
373+
var updatedAtField string
374+
if t.updatedAt == nil {
375+
updatedAtField = ""
376+
} else {
377+
updatedAtField = `"updated_at": "` + t.updatedAt.Format(time.RFC3339) + `",`
378+
}
379+
380+
var createdAtField string
381+
if t.createdAt == nil {
382+
createdAtField = ""
383+
} else {
384+
createdAtField = `"created_at": "` + t.createdAt.Format(time.RFC3339) + `",`
385+
}
386+
387+
handler := func(w http.ResponseWriter, r *http.Request) {
388+
fmt.Fprint(w, `
389+
{
390+
"id": 5,
391+
"iid": `+strconv.Itoa(t.prID)+`,
392+
"project_id": `+strconv.Itoa(t.repoID)+`,
393+
"title": "Approvals API",
394+
"description": "Test",
395+
"state": "opened",
396+
`+createdAtField+`
397+
`+updatedAtField+`
398+
"merge_status": "cannot_be_merged",
399+
"approvals_required": 2,
400+
"approvals_left": 1,
401+
"approved_by": [
402+
{
403+
"user": {
404+
"name": "Administrator",
405+
"username": "`+t.reviewerName+`",
406+
"id": `+strconv.Itoa(t.reviewerID)+`,
407+
"state": "active",
408+
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
409+
"web_url": "http://localhost:3000/root"
410+
}
411+
}
412+
]
413+
}`)
414+
}
415+
review := base.Review{
416+
IssueIndex: int64(t.prID),
417+
ReviewerID: int64(t.reviewerID),
418+
ReviewerName: t.reviewerName,
419+
CreatedAt: t.expectedCreatedAt,
420+
State: "APPROVED",
421+
}
422+
423+
return handler, review
424+
}
425+
426+
func TestGitlabGetReviews(t *testing.T) {
427+
mux, server, client := gitlabClientMockSetup(t)
428+
defer gitlabClientMockTeardown(server)
429+
430+
repoID := 1324
431+
432+
downloader := &GitlabDownloader{
433+
ctx: context.Background(),
434+
client: client,
435+
repoID: repoID,
436+
}
437+
438+
createdAt := time.Date(2020, 4, 19, 19, 24, 21, 0, time.UTC)
439+
440+
for _, testCase := range []reviewTestCase{
441+
{
442+
repoID: repoID,
443+
prID: 1,
444+
reviewerID: 801,
445+
reviewerName: "someone1",
446+
createdAt: nil,
447+
updatedAt: &createdAt,
448+
expectedCreatedAt: createdAt,
449+
},
450+
{
451+
repoID: repoID,
452+
prID: 2,
453+
reviewerID: 802,
454+
reviewerName: "someone2",
455+
createdAt: &createdAt,
456+
updatedAt: nil,
457+
expectedCreatedAt: createdAt,
458+
},
459+
{
460+
repoID: repoID,
461+
prID: 3,
462+
reviewerID: 803,
463+
reviewerName: "someone3",
464+
createdAt: nil,
465+
updatedAt: nil,
466+
expectedCreatedAt: time.Now(),
467+
},
468+
} {
469+
mock, review := convertTestCase(testCase)
470+
mux.HandleFunc(fmt.Sprintf("/api/v4/projects/%d/merge_requests/%d/approvals", testCase.repoID, testCase.prID), mock)
471+
472+
rvs, err := downloader.GetReviews(base.BasicIssueContext(testCase.prID))
473+
assert.NoError(t, err)
474+
assertReviewsEqual(t, []*base.Review{&review}, rvs)
475+
}
476+
}

services/migrations/main_test.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -223,15 +223,15 @@ func assertRepositoryEqual(t *testing.T, expected, actual *base.Repository) {
223223
}
224224

225225
func assertReviewEqual(t *testing.T, expected, actual *base.Review) {
226-
assert.Equal(t, expected.ID, actual.ID)
227-
assert.Equal(t, expected.IssueIndex, actual.IssueIndex)
228-
assert.Equal(t, expected.ReviewerID, actual.ReviewerID)
229-
assert.Equal(t, expected.ReviewerName, actual.ReviewerName)
230-
assert.Equal(t, expected.Official, actual.Official)
231-
assert.Equal(t, expected.CommitID, actual.CommitID)
232-
assert.Equal(t, expected.Content, actual.Content)
233-
assertTimeEqual(t, expected.CreatedAt, actual.CreatedAt)
234-
assert.Equal(t, expected.State, actual.State)
226+
assert.Equal(t, expected.ID, actual.ID, "ID")
227+
assert.Equal(t, expected.IssueIndex, actual.IssueIndex, "IsssueIndex")
228+
assert.Equal(t, expected.ReviewerID, actual.ReviewerID, "ReviewerID")
229+
assert.Equal(t, expected.ReviewerName, actual.ReviewerName, "ReviewerName")
230+
assert.Equal(t, expected.Official, actual.Official, "Official")
231+
assert.Equal(t, expected.CommitID, actual.CommitID, "CommitID")
232+
assert.Equal(t, expected.Content, actual.Content, "Content")
233+
assert.WithinDuration(t, expected.CreatedAt, actual.CreatedAt, 10*time.Second)
234+
assert.Equal(t, expected.State, actual.State, "State")
235235
assertReviewCommentsEqual(t, expected.Comments, actual.Comments)
236236
}
237237

0 commit comments

Comments
 (0)