go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/internal/bugs/monorail/migration/migrate_test.go (about)

     1  // Copyright 2024 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package migration
    16  
    17  import (
    18  	"sort"
    19  	"testing"
    20  	"time"
    21  
    22  	"go.chromium.org/luci/gae/impl/memory"
    23  	"go.chromium.org/luci/server/auth"
    24  	"go.chromium.org/luci/server/auth/authtest"
    25  	"go.chromium.org/luci/server/secrets"
    26  	"go.chromium.org/luci/server/secrets/testsecrets"
    27  	"go.chromium.org/luci/server/span"
    28  
    29  	"go.chromium.org/luci/analysis/internal/bugs"
    30  	"go.chromium.org/luci/analysis/internal/bugs/monorail"
    31  	"go.chromium.org/luci/analysis/internal/bugs/monorail/api_proto"
    32  	migrationpb "go.chromium.org/luci/analysis/internal/bugs/monorail/migration/proto"
    33  	"go.chromium.org/luci/analysis/internal/clustering/rules"
    34  	"go.chromium.org/luci/analysis/internal/config"
    35  	"go.chromium.org/luci/analysis/internal/testutil"
    36  	configpb "go.chromium.org/luci/analysis/proto/config"
    37  
    38  	. "github.com/smartystreets/goconvey/convey"
    39  	. "go.chromium.org/luci/common/testing/assertions"
    40  )
    41  
    42  func TestMigration(t *testing.T) {
    43  	Convey("With Server", t, func() {
    44  		ctx := testutil.IntegrationTestContext(t)
    45  
    46  		// For user identification.
    47  		ctx = authtest.MockAuthConfig(ctx)
    48  		authState := &authtest.FakeState{
    49  			Identity:       "user:someone@example.com",
    50  			IdentityGroups: []string{luciAnalysisAdminGroup},
    51  		}
    52  		ctx = auth.WithState(ctx, authState)
    53  		ctx = secrets.Use(ctx, &testsecrets.Store{})
    54  
    55  		// Service config required by implementation.
    56  		ctx = memory.Use(ctx)
    57  		err := config.SetTestConfig(ctx, &configpb.Config{})
    58  		So(err, ShouldBeNil)
    59  
    60  		// Set up some rules for testing.
    61  		ruleMonorail := rules.NewRule(0).
    62  			WithProject("testproject").
    63  			WithBug(bugs.BugID{System: "monorail", ID: "monorailproject/111"}).Build()
    64  		ruleMonorail2 := rules.NewRule(1).
    65  			WithProject("testproject").
    66  			WithBug(bugs.BugID{System: "monorail", ID: "monorailproject/222"}).Build()
    67  		ruleBuganizer := rules.NewRule(2).
    68  			WithProject("testproject").
    69  			WithBug(bugs.BugID{System: "buganizer", ID: "666"}).
    70  			Build()
    71  		ruleOtherProject := rules.NewRule(3).
    72  			WithProject("anotherproject").
    73  			WithBug(bugs.BugID{System: "monorail", ID: "aproject/111"}).
    74  			Build()
    75  
    76  		err = rules.SetForTesting(ctx, []*rules.Entry{
    77  			ruleMonorail,
    78  			ruleMonorail2,
    79  			ruleBuganizer,
    80  			ruleOtherProject,
    81  		})
    82  		So(err, ShouldBeNil)
    83  
    84  		f := &monorail.FakeIssuesStore{
    85  			NextID:            100,
    86  			PriorityFieldName: "projects/chromium/fieldDefs/11",
    87  			ComponentNames: []string{
    88  				"projects/chromium/componentDefs/Blink",
    89  			},
    90  			Issues: []*monorail.IssueData{
    91  				{
    92  					Issue: &api_proto.Issue{
    93  						Name:       "projects/monorailproject/issues/111",
    94  						MigratedId: "8111",
    95  					},
    96  				},
    97  				{
    98  					Issue: &api_proto.Issue{
    99  						Name:       "projects/monorailproject/issues/222",
   100  						MigratedId: "8222",
   101  					},
   102  				},
   103  			},
   104  		}
   105  
   106  		ctx = monorail.UseFakeIssuesClient(ctx, f, "service-user")
   107  
   108  		srv := NewMonorailMigrationServer()
   109  
   110  		Convey("Unauthorised requests are rejected", func() {
   111  			// Ensure no access to luci-analysis-access.
   112  			ctx = auth.WithState(ctx, &authtest.FakeState{
   113  				Identity: "user:someone@example.com",
   114  				// Not a member of service-luci-analysis-admins.
   115  				IdentityGroups: []string{"other-group"},
   116  			})
   117  
   118  			// Make some request (the request should not matter, as
   119  			// a common decorator is used for all requests.)
   120  			request := &migrationpb.MigrateProjectRequest{
   121  				Project:  "testproject",
   122  				MaxRules: 1,
   123  			}
   124  
   125  			rsp, err := srv.MigrateProject(ctx, request)
   126  			So(err, ShouldBeRPCPermissionDenied, "not a member of service-luci-analysis-admins")
   127  			So(rsp, ShouldBeNil)
   128  		})
   129  		Convey("MigrateProject", func() {
   130  			request := &migrationpb.MigrateProjectRequest{
   131  				Project:  "testproject",
   132  				MaxRules: 1,
   133  			}
   134  			expectedRules := []*rules.Entry{
   135  				ruleMonorail,
   136  				ruleMonorail2,
   137  				ruleBuganizer,
   138  				ruleOtherProject,
   139  			}
   140  
   141  			Convey("Migrated-to bug mapping available", func() {
   142  				rsp, err := srv.MigrateProject(ctx, request)
   143  				So(err, ShouldBeNil)
   144  				So(rsp, ShouldResembleProto, &migrationpb.MigrateProjectResponse{
   145  					RulesNotStarted: 0,
   146  					RuleResults: []*migrationpb.MigrateProjectResponse_RuleResult{
   147  						{
   148  							RuleId:         ruleMonorail.RuleID,
   149  							MonorailBugId:  "monorailproject/111",
   150  							BuganizerBugId: "8111",
   151  						},
   152  					},
   153  				})
   154  
   155  				// Rule should be updated to use buganizer bug.
   156  				expectedRules[0].BugID = bugs.BugID{
   157  					System: "buganizer",
   158  					ID:     "8111",
   159  				}
   160  				expectedRules[0].BugManagementState.RuleAssociationNotified = false
   161  				expectedRules[0].LastAuditableUpdateUser = "system"
   162  
   163  				rs, err := rules.ReadAllForTesting(span.Single(ctx))
   164  				So(err, ShouldBeNil)
   165  				So(sortRulesAndClearTimestamps(rs), ShouldResembleProto, sortRulesAndClearTimestamps(expectedRules))
   166  			})
   167  			Convey("Migrated to bug mapping unavailable", func() {
   168  				for _, issue := range f.Issues {
   169  					issue.Issue.MigratedId = ""
   170  				}
   171  
   172  				rsp, err := srv.MigrateProject(ctx, request)
   173  				So(err, ShouldBeNil)
   174  				So(rsp, ShouldResembleProto, &migrationpb.MigrateProjectResponse{
   175  					RulesNotStarted: 0,
   176  					RuleResults: []*migrationpb.MigrateProjectResponse_RuleResult{
   177  						{
   178  							RuleId:         ruleMonorail.RuleID,
   179  							MonorailBugId:  "monorailproject/111",
   180  							BuganizerBugId: "",
   181  							Error:          "monorail did not return the Buganizer bug this bug was migrated to",
   182  						},
   183  					},
   184  				})
   185  
   186  				rs, err := rules.ReadAllForTesting(span.Single(ctx))
   187  				So(err, ShouldBeNil)
   188  				So(sortRulesAndClearTimestamps(rs), ShouldResembleProto, sortRulesAndClearTimestamps(expectedRules))
   189  			})
   190  		})
   191  	})
   192  }
   193  
   194  func sortRulesAndClearTimestamps(rs []*rules.Entry) []*rules.Entry {
   195  	rsCopy := make([]*rules.Entry, len(rs))
   196  	copy(rsCopy, rs)
   197  
   198  	for _, r := range rsCopy {
   199  		r.LastUpdateTime = time.Time{}
   200  		r.LastAuditableUpdateTime = time.Time{}
   201  	}
   202  
   203  	sort.Slice(rsCopy, func(i, j int) bool {
   204  		return rsCopy[i].RuleID < rsCopy[j].RuleID
   205  	})
   206  	return rsCopy
   207  }