2024-12-02 10:26:51 +01:00
package main
2024-12-04 08:55:40 +01:00
import (
2024-12-17 23:33:43 +01:00
"errors"
2025-02-14 17:13:51 +01:00
"fmt"
2024-12-17 23:33:43 +01:00
"os"
"os/exec"
"path"
2025-01-29 17:29:09 +01:00
"strings"
2024-12-04 08:55:40 +01:00
"testing"
2024-12-17 23:33:43 +01:00
2024-12-04 08:55:40 +01:00
"go.uber.org/mock/gomock"
2024-12-17 23:33:43 +01:00
"src.opensuse.org/autogits/common"
2024-12-04 08:55:40 +01:00
"src.opensuse.org/autogits/common/gitea-generated/models"
2024-12-17 23:33:43 +01:00
mock_common "src.opensuse.org/autogits/common/mock"
2025-02-14 17:13:51 +01:00
mock_main "src.opensuse.org/workflow-pr/mock"
2024-12-04 08:55:40 +01:00
)
2024-12-02 10:26:51 +01:00
func TestPR ( t * testing . T ) {
2024-12-17 23:33:43 +01:00
baseConfig := common . AutogitConfig {
2025-02-05 18:30:08 +01:00
Reviewers : [ ] string { "+super1" , "*super2" , "m1" , "-m2" } ,
2024-12-17 23:33:43 +01:00
Branch : "branch" ,
2024-12-18 17:30:00 +01:00
Organization : "foo" ,
2024-12-17 23:33:43 +01:00
GitProjectName : "barPrj" ,
}
type prdata struct {
2025-01-11 21:37:59 +01:00
pr * models . PullRequest
pr_err error
reviews [ ] * models . PullReview
review_error error
2024-12-17 23:33:43 +01:00
}
2025-02-18 17:42:55 +01:00
tests := [ ] struct {
name string
data [ ] prdata
api_error string
2025-01-02 13:46:59 +01:00
2025-02-18 17:42:55 +01:00
resLen int
reviewed bool
consistentSet bool
prjGitPRIndex int
2025-01-02 13:46:59 +01:00
2025-02-18 17:42:55 +01:00
reviewSetFetcher func ( * mock_common . MockGiteaPRFetcher ) ( * PRSet , error )
} {
{
name : "Error fetching PullRequest" ,
data : [ ] prdata {
{ pr : & models . PullRequest { Body : "" , Index : 42 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "repo" , Owner : & models . User { UserName : "test" } } } } , pr_err : errors . New ( "Missing PR" ) } ,
2024-12-17 23:33:43 +01:00
} ,
2025-02-18 17:42:55 +01:00
prjGitPRIndex : - 1 ,
} ,
{
name : "Error fetching PullRequest in PrjGit" ,
data : [ ] prdata {
{ pr : & models . PullRequest { Body : "PR: foo/barPrj#22" , Index : 42 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "repo" , Owner : & models . User { UserName : "test" } } } } , pr_err : errors . New ( "missing PR" ) } ,
{ pr : & models . PullRequest { Body : "" , Index : 22 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "barPrj" , Owner : & models . User { UserName : "foo" } } } } } ,
2024-12-18 17:30:00 +01:00
} ,
2025-02-18 17:42:55 +01:00
} ,
{
name : "Error fetching prjgit" ,
data : [ ] prdata {
{ pr : & models . PullRequest { Body : "" , Index : 42 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "repo" , Owner : & models . User { UserName : "test" } } } } } ,
2024-12-17 23:33:43 +01:00
} ,
2025-02-18 17:42:55 +01:00
resLen : 1 ,
prjGitPRIndex : - 1 ,
} ,
{
name : "Review set is consistent" ,
data : [ ] prdata {
{ pr : & models . PullRequest { Body : "" , Index : 42 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "repo" , Owner : & models . User { UserName : "test" } } } } } ,
{ pr : & models . PullRequest { Body : "PR: test/repo#42" , Index : 22 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "barPrj" , Owner : & models . User { UserName : "foo" } } } } } ,
2024-12-17 23:33:43 +01:00
} ,
2025-02-18 17:42:55 +01:00
resLen : 2 ,
prjGitPRIndex : 1 ,
consistentSet : true ,
} ,
2025-01-02 13:46:59 +01:00
2025-02-18 17:42:55 +01:00
{
name : "Review set is consistent: 1pkg" ,
data : [ ] prdata {
{ pr : & models . PullRequest { Body : "PR: foo/barPrj#22" , Index : 42 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "repo" , Owner : & models . User { UserName : "test" } } } } } ,
{ pr : & models . PullRequest { Body : "PR: test/repo#42" , Index : 22 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "barPrj" , Owner : & models . User { UserName : "foo" } } } } } ,
2024-12-17 23:33:43 +01:00
} ,
2025-02-18 17:42:55 +01:00
resLen : 2 ,
prjGitPRIndex : 1 ,
consistentSet : true ,
} ,
{
name : "Review set is consistent: 2pkg" ,
data : [ ] prdata {
{ pr : & models . PullRequest { Body : "some desc" , Index : 42 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "repo" , Owner : & models . User { UserName : "test" } } } } } ,
{ pr : & models . PullRequest { Body : "PR: test/repo#42\nPR: test/repo2#41" , Index : 22 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "barPrj" , Owner : & models . User { UserName : "foo" } } } } } ,
{ pr : & models . PullRequest { Body : "some other desc\nPR: foo/fer#33" , Index : 41 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "repo2" , Owner : & models . User { UserName : "test" } } } } } ,
2025-01-02 13:46:59 +01:00
} ,
2025-02-18 17:42:55 +01:00
resLen : 3 ,
prjGitPRIndex : 1 ,
consistentSet : true ,
} ,
{
name : "Review set of prjgit PR is consistent" ,
data : [ ] prdata {
{
pr : & models . PullRequest { Body : "" , Index : 42 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "barPrj" , Owner : & models . User { UserName : "foo" } } } } ,
reviews : [ ] * models . PullReview {
{ Body : "LGTM" , User : & models . User { UserName : "m2" } , State : common . ReviewStateApproved } ,
{ Body : "LGTM" , User : & models . User { UserName : "super2" } , State : common . ReviewStateApproved } ,
{ Body : "LGTM" , User : & models . User { UserName : common . Bot_BuildReview } , State : common . ReviewStateApproved } ,
2025-01-11 21:37:59 +01:00
} ,
2025-01-02 13:46:59 +01:00
} ,
} ,
2025-02-18 17:42:55 +01:00
resLen : 1 ,
prjGitPRIndex : 0 ,
consistentSet : true ,
reviewed : true ,
reviewSetFetcher : func ( mock * mock_common . MockGiteaPRFetcher ) ( * PRSet , error ) {
return FetchPRSet ( mock , "foo" , "barPrj" , 42 , & baseConfig )
2025-01-02 13:46:59 +01:00
} ,
2025-02-18 17:42:55 +01:00
} ,
{
name : "Review set is consistent: 2pkg" ,
data : [ ] prdata {
{ pr : & models . PullRequest { Body : "PR: foo/barPrj#222" , Index : 42 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "repo" , Owner : & models . User { UserName : "test" } } } } } ,
{ pr : & models . PullRequest { Body : "PR: test/repo2#41" , Index : 20 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "barPrj" , Owner : & models . User { UserName : "foo" } } } } } ,
{ pr : & models . PullRequest { Body : "PR: test/repo#42\nPR: test/repo2#41" , Index : 22 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "barPrj" , Owner : & models . User { UserName : "foo" } } } } } ,
{ pr : & models . PullRequest { Body : "PR: foo/barPrj#20" , Index : 41 , Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "repo2" , Owner : & models . User { UserName : "test" } } } } } ,
2025-01-02 13:46:59 +01:00
} ,
2025-02-18 17:42:55 +01:00
resLen : 3 ,
prjGitPRIndex : 2 ,
consistentSet : true ,
} ,
}
2024-12-17 23:33:43 +01:00
2025-02-18 17:42:55 +01:00
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
ctl := gomock . NewController ( t )
pr_mock := mock_common . NewMockGiteaPRFetcher ( ctl )
review_mock := mock_common . NewMockGiteaPRChecker ( ctl )
// reviewer_mock := mock_common.NewMockGiteaReviewRequester(ctl)
2024-12-17 23:33:43 +01:00
2025-02-18 17:42:55 +01:00
if test . reviewSetFetcher == nil { // if we are fetching the prjgit directly, the these mocks are not called
if test . prjGitPRIndex >= 0 {
pr_mock . EXPECT ( ) . GetAssociatedPrjGitPR ( baseConfig . Organization , baseConfig . GitProjectName , test . data [ 0 ] . pr . Base . Repo . Owner . UserName , test . data [ 0 ] . pr . Base . Repo . Name , test . data [ 0 ] . pr . Index ) .
Return ( test . data [ test . prjGitPRIndex ] . pr , test . data [ test . prjGitPRIndex ] . pr_err )
} else if test . prjGitPRIndex < 0 {
// no prjgit PR
pr_mock . EXPECT ( ) . GetAssociatedPrjGitPR ( baseConfig . Organization , baseConfig . GitProjectName , test . data [ 0 ] . pr . Base . Repo . Owner . UserName , test . data [ 0 ] . pr . Base . Repo . Name , test . data [ 0 ] . pr . Index ) .
Return ( nil , nil )
2024-12-17 23:33:43 +01:00
}
2025-02-18 17:42:55 +01:00
}
2024-12-17 23:33:43 +01:00
2025-02-18 17:42:55 +01:00
var test_err error
for _ , data := range test . data {
pr_mock . EXPECT ( ) . GetPullRequest ( data . pr . Base . Repo . Owner . UserName , data . pr . Base . Repo . Name , data . pr . Index ) . Return ( data . pr , data . pr_err ) . AnyTimes ( )
if data . pr_err != nil {
test_err = data . pr_err
2024-12-18 17:30:00 +01:00
}
2025-02-18 17:42:55 +01:00
review_mock . EXPECT ( ) . GetPullRequestReviews ( data . pr . Base . Repo . Owner . UserName , data . pr . Base . Repo . Name , data . pr . Index ) . Return ( data . reviews , data . review_error ) . AnyTimes ( )
}
2025-01-11 21:37:59 +01:00
2025-02-18 17:42:55 +01:00
var res * PRSet
var err error
2024-12-18 17:30:00 +01:00
2025-02-18 17:42:55 +01:00
if test . reviewSetFetcher != nil {
res , err = test . reviewSetFetcher ( pr_mock )
} else {
res , err = FetchPRSet ( pr_mock , "test" , "repo" , 42 , & baseConfig )
}
2024-12-17 23:33:43 +01:00
2025-02-18 17:42:55 +01:00
if err == nil {
if test_err != nil {
t . Fatal ( "Expected" , test_err , "but got" , err )
2024-12-18 17:30:00 +01:00
}
2025-02-18 17:42:55 +01:00
} else {
if res != nil {
t . Fatal ( "error but got ReviewSet?" )
2024-12-18 17:30:00 +01:00
}
2025-02-18 17:42:55 +01:00
if test . api_error != "" {
if err . Error ( ) != test . api_error {
t . Fatal ( "expected" , test . api_error , "but got" , err )
2025-01-02 13:46:59 +01:00
}
2025-02-18 17:42:55 +01:00
} else if test_err != err {
t . Fatal ( "expected" , test_err , "but got" , err )
2024-12-18 17:30:00 +01:00
}
2025-02-18 17:42:55 +01:00
return
}
if test . resLen != len ( res . prs ) {
t . Error ( "expected result len" , test . resLen , "but got" , len ( res . prs ) )
}
2024-12-18 17:30:00 +01:00
2025-02-18 17:42:55 +01:00
PrjGitPR , err := res . GetPrjGitPR ( )
if test . prjGitPRIndex < 0 {
if err == nil {
t . Error ( "expected error, but nothing" )
2024-12-18 17:30:00 +01:00
}
2025-02-18 17:42:55 +01:00
}
pr_found := false
if test . prjGitPRIndex >= 0 {
for i := range test . data {
if PrjGitPR == test . data [ i ] . pr && i == test . prjGitPRIndex {
t . Log ( "found at index" , i )
pr_found = true
2025-02-14 17:13:51 +01:00
}
2025-01-02 14:44:31 +01:00
}
2025-02-18 17:42:55 +01:00
if ! pr_found {
t . Error ( "Cannot find expected PrjGit location in PR set" , PrjGitPR )
}
} else {
if PrjGitPR != nil {
t . Log ( "Expected prjgit not found, but found?" , PrjGitPR )
}
}
if isConsistent := res . IsConsistent ( ) ; isConsistent != test . consistentSet {
t . Error ( "IsConsistent() returned unexpected:" , isConsistent )
}
/ *
if err := res . AssignReviewers ( reviewer_mock ) ; err != nil {
t . Error ( "expected no errors assigning reviewers:" , err )
}
* /
if isReviewed := res . IsReviewed ( review_mock ) ; isReviewed != test . reviewed {
t . Error ( "expected reviewed to be NOT" , isReviewed )
}
} )
}
2024-12-17 23:33:43 +01:00
}
2025-01-27 17:43:50 +01:00
2025-02-14 17:13:51 +01:00
func TestPRAssignReviewers ( t * testing . T ) {
tests := [ ] struct {
name string
config common . AutogitConfig
reviewers [ ] struct {
org , repo string
num int64
reviewer string
}
2025-02-18 17:42:55 +01:00
pkgReviews [ ] * models . PullReview
prjReviews [ ] * models . PullReview
2025-02-14 17:13:51 +01:00
expectedReviewerCall [ 2 ] [ ] string
} {
{
name : "No reviewers" ,
config : common . AutogitConfig {
GitProjectName : "repo" ,
Organization : "org" ,
Branch : "main" ,
Reviewers : [ ] string { } ,
} ,
expectedReviewerCall : [ 2 ] [ ] string { { "autogits_obs_staging_bot" } } ,
} ,
{
name : "One project reviewer only" ,
config : common . AutogitConfig {
GitProjectName : "repo" ,
Organization : "org" ,
Branch : "main" ,
Reviewers : [ ] string { "-user1" } ,
} ,
expectedReviewerCall : [ 2 ] [ ] string { { "user1" , "autogits_obs_staging_bot" } } ,
} ,
{
name : "One project reviewer and one pkg reviewer only" ,
config : common . AutogitConfig {
GitProjectName : "repo" ,
Organization : "org" ,
Branch : "main" ,
Reviewers : [ ] string { "-user1" , "user2" } ,
} ,
expectedReviewerCall : [ 2 ] [ ] string { { "user1" , "autogits_obs_staging_bot" } , { "user2" } } ,
} ,
{
name : "No need to get reviews of submitter" ,
config : common . AutogitConfig {
GitProjectName : "repo" ,
Organization : "org" ,
Branch : "main" ,
Reviewers : [ ] string { "-user1" , "submitter" } ,
} ,
expectedReviewerCall : [ 2 ] [ ] string { { "user1" , "autogits_obs_staging_bot" } , { } } ,
} ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
ctl := gomock . NewController ( t )
pr_mock := mock_common . NewMockGiteaPRFetcher ( ctl )
2025-02-18 17:42:55 +01:00
review_mock := mock_common . NewMockGiteaReviewFetcherAndRequester ( ctl )
2025-02-14 17:13:51 +01:00
maintainership_mock := mock_main . NewMockMaintainershipData ( ctl )
pr_mock . EXPECT ( ) . GetPullRequest ( "other" , "pkgrepo" , int64 ( 1 ) ) . Return ( & models . PullRequest {
2025-02-18 17:42:55 +01:00
Body : "Some description is here" ,
2025-02-14 17:13:51 +01:00
User : & models . User { UserName : "submitter" } ,
RequestedReviewers : [ ] * models . User { } ,
Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "pkgrepo" , Owner : & models . User { UserName : "other" } } } ,
Head : & models . PRBranchInfo { } ,
Index : 1 ,
} , nil )
2025-02-18 17:42:55 +01:00
review_mock . EXPECT ( ) . GetPullRequestReviews ( "other" , "pkgrepo" , int64 ( 1 ) ) . Return ( [ ] * models . PullReview { } , nil )
pr_mock . EXPECT ( ) . GetAssociatedPrjGitPR ( "org" , "repo" , "other" , "pkgrepo" , int64 ( 1 ) ) . Return ( & models . PullRequest {
Body : fmt . Sprintf ( common . PrPattern , "other" , "pkgrepo" , 1 ) ,
User : & models . User { UserName : "bot1" } ,
RequestedReviewers : [ ] * models . User { { UserName : "main_reviewer" } } ,
Base : & models . PRBranchInfo { Repo : & models . Repository { Name : "repo" , Owner : & models . User { UserName : "org" } } } ,
Head : & models . PRBranchInfo { } ,
Index : 42 ,
} , nil )
review_mock . EXPECT ( ) . GetPullRequestReviews ( "org" , "repo" , int64 ( 42 ) ) . Return ( [ ] * models . PullReview { } , nil )
2025-02-14 17:13:51 +01:00
2025-02-18 17:42:55 +01:00
prs , _ := FetchPRSet ( pr_mock , "other" , "pkgrepo" , int64 ( 1 ) , & test . config )
2025-02-14 17:13:51 +01:00
if len ( prs . prs ) != 2 {
t . Fatal ( "PRs not fetched" )
}
for _ , pr := range prs . prs {
r := test . expectedReviewerCall [ 0 ]
if ! prs . IsPrjGitPR ( pr . pr ) {
r = test . expectedReviewerCall [ 1 ]
}
if len ( r ) > 0 {
review_mock . EXPECT ( ) . RequestReviews ( pr . pr , r ) . Return ( nil , nil )
}
}
prs . AssignReviewers ( review_mock , maintainership_mock )
} )
}
}
2025-01-27 17:43:50 +01:00
func TestPRMerge ( t * testing . T ) {
cwd , _ := os . Getwd ( )
cmd := exec . Command ( "/usr/bin/bash" , path . Join ( cwd , "test_repo_setup.sh" ) )
cmd . Dir = t . TempDir ( )
if out , err := cmd . CombinedOutput ( ) ; err != nil {
t . Fatal ( string ( out ) )
}
2025-01-29 17:29:09 +01:00
common . ExtraGitParams = [ ] string {
"GIT_CONFIG_COUNT=1" ,
"GIT_CONFIG_KEY_0=protocol.file.allow" ,
"GIT_CONFIG_VALUE_0=always" ,
"GIT_AUTHOR_NAME=testname" ,
"GIT_AUTHOR_EMAIL=test@suse.com" ,
"GIT_AUTHOR_DATE='2005-04-07T22:13:13'" ,
"GIT_COMMITTER_NAME=testname" ,
"GIT_COMMITTER_EMAIL=test@suse.com" ,
"GIT_COMMITTER_DATE='2005-04-07T22:13:13'" ,
}
config := & common . AutogitConfig {
2025-02-06 17:17:06 +01:00
Organization : "org" ,
2025-01-29 17:29:09 +01:00
GitProjectName : "prj" ,
}
2025-01-27 17:43:50 +01:00
tests := [ ] struct {
2025-02-06 17:17:06 +01:00
name string
pr * models . PullRequest
mergeError string
2025-01-27 17:43:50 +01:00
} {
{
2025-01-29 17:29:09 +01:00
name : "Merge base not merged in main" ,
pr : & models . PullRequest {
Base : & models . PRBranchInfo {
Sha : "e8b0de43d757c96a9d2c7101f4bff404e322f53a1fa4041fb85d646110c38ad4" , // "base_add_b1"
Repo : & models . Repository {
Name : "prj" ,
Owner : & models . User {
UserName : "org" ,
} ,
SSHURL : path . Join ( cmd . Dir , "prjgit" ) ,
} ,
} ,
2025-02-06 17:17:06 +01:00
Head : & models . PRBranchInfo {
2025-01-29 17:29:09 +01:00
Sha : "88584433de1c917c1d773f62b82381848d882491940b5e9b427a540aa9057d9a" , // "base_add_b2"
} ,
} ,
mergeError : "Aborting merge" ,
} ,
{
name : "Merge conflict in modules" ,
pr : & models . PullRequest {
Base : & models . PRBranchInfo {
Sha : "4fbd1026b2d7462ebe9229a49100c11f1ad6555520a21ba515122d8bc41328a8" ,
Repo : & models . Repository {
Name : "prj" ,
Owner : & models . User {
UserName : "org" ,
} ,
SSHURL : path . Join ( cmd . Dir , "prjgit" ) ,
} ,
} ,
2025-02-06 17:17:06 +01:00
Head : & models . PRBranchInfo {
2025-01-29 17:29:09 +01:00
Sha : "88584433de1c917c1d773f62b82381848d882491940b5e9b427a540aa9057d9a" , // "base_add_b2"
} ,
} ,
2025-01-27 17:43:50 +01:00
} ,
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
2025-01-29 17:29:09 +01:00
ctl := gomock . NewController ( t )
mock := mock_common . NewMockGiteaPRFetcher ( ctl )
mock . EXPECT ( ) . GetPullRequest ( "org" , "prj" , int64 ( 1 ) ) . Return ( test . pr , nil )
set , err := FetchPRSet ( mock , "org" , "prj" , 1 , config )
if err != nil {
t . Fatal ( err )
}
if err = set . Merge ( ) ; err != nil && ( test . mergeError == "" || ( len ( test . mergeError ) > 0 && ! strings . Contains ( err . Error ( ) , test . mergeError ) ) ) {
t . Fatal ( err )
}
2025-01-27 17:43:50 +01:00
} )
}
}