golang.org/x/build@v0.0.0-20240506185731-218518f32b70/maintner/maintner_test.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package maintner 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 "reflect" 12 "strings" 13 "testing" 14 "time" 15 16 "github.com/davecgh/go-spew/spew" 17 "github.com/golang/protobuf/ptypes" 18 google_protobuf "github.com/golang/protobuf/ptypes/timestamp" 19 "github.com/google/go-github/github" 20 "golang.org/x/build/maintner/maintpb" 21 ) 22 23 var u1 = &GitHubUser{ 24 Login: "gopherbot", 25 ID: 100, 26 } 27 var u2 = &GitHubUser{ 28 Login: "kevinburke", 29 ID: 101, 30 } 31 32 type dummyMutationLogger struct { 33 Mutations []*maintpb.Mutation 34 } 35 36 func (d *dummyMutationLogger) Log(m *maintpb.Mutation) error { 37 if d.Mutations == nil { 38 d.Mutations = []*maintpb.Mutation{} 39 } 40 d.Mutations = append(d.Mutations, m) 41 return nil 42 } 43 44 type mutationTest struct { 45 corpus *Corpus 46 want *Corpus 47 } 48 49 func (mt mutationTest) test(t *testing.T, muts ...*maintpb.Mutation) { 50 c := mt.corpus 51 if c == nil { 52 c = new(Corpus) 53 } 54 for _, m := range muts { 55 c.processMutationLocked(m) 56 } 57 c.github.c = nil 58 mt.want.github.c = nil 59 if !reflect.DeepEqual(c.github, mt.want.github) { 60 t.Errorf("corpus mismatch:\n got: %s\n\nwant: %s\n\ndiff: %v", 61 spew.Sdump(c.github), 62 spew.Sdump(mt.want.github), 63 diffPath(reflect.ValueOf(c.github), reflect.ValueOf(mt.want.github))) 64 } 65 } 66 67 var t1, t2 time.Time 68 var tp1, tp2 *google_protobuf.Timestamp 69 70 func init() { 71 t1, _ = time.Parse(time.RFC3339, "2016-01-02T15:04:00Z") 72 t2, _ = time.Parse(time.RFC3339, "2016-01-02T15:30:00Z") 73 tp1, _ = ptypes.TimestampProto(t1) 74 tp2, _ = ptypes.TimestampProto(t2) 75 } 76 77 func singleIssueGitHubCorpus() *Corpus { 78 c := new(Corpus) 79 github := &GitHub{c: c} 80 c.github = github 81 github.users = map[int64]*GitHubUser{ 82 u1.ID: u1, 83 } 84 github.repos = map[GitHubRepoID]*GitHubRepo{ 85 GitHubRepoID{"golang", "go"}: &GitHubRepo{ 86 github: github, 87 id: GitHubRepoID{"golang", "go"}, 88 issues: map[int32]*GitHubIssue{ 89 3: &GitHubIssue{ 90 Number: 3, 91 User: u1, 92 Title: "some title", 93 Body: "some body", 94 Created: t1, 95 Assignees: nil, 96 }, 97 }, 98 }, 99 } 100 return c 101 } 102 103 func TestProcessMutation_Github_NewIssue(t *testing.T) { 104 mutationTest{want: singleIssueGitHubCorpus()}.test(t, &maintpb.Mutation{ 105 GithubIssue: &maintpb.GithubIssueMutation{ 106 Owner: "golang", 107 Repo: "go", 108 Number: 3, 109 User: &maintpb.GithubUser{ 110 Login: "gopherbot", 111 Id: 100, 112 }, 113 Title: "some title", 114 Body: "some body", 115 Created: tp1, 116 }, 117 }) 118 } 119 120 func TestProcessMutation_Github_RemoveBody(t *testing.T) { 121 c := singleIssueGitHubCorpus() 122 want := singleIssueGitHubCorpus() 123 want.github.repos[GitHubRepoID{"golang", "go"}].issues[3].Body = "" 124 mutationTest{corpus: c, want: want}.test(t, &maintpb.Mutation{ 125 GithubIssue: &maintpb.GithubIssueMutation{ 126 Owner: "golang", 127 Repo: "go", 128 Number: 3, 129 BodyChange: &maintpb.StringChange{Val: ""}, 130 }, 131 }) 132 133 // And test that the old mutation field (Body) still works. 134 want.github.repos[GitHubRepoID{"golang", "go"}].issues[3].Body = "and back" 135 mutationTest{corpus: c, want: want}.test(t, &maintpb.Mutation{ 136 GithubIssue: &maintpb.GithubIssueMutation{ 137 Owner: "golang", 138 Repo: "go", 139 Number: 3, 140 Body: "and back", 141 }, 142 }) 143 } 144 145 func TestProcessMutation_Github(t *testing.T) { 146 c := new(Corpus) 147 github := &GitHub{c: c} 148 c.github = github 149 github.repos = map[GitHubRepoID]*GitHubRepo{ 150 GitHubRepoID{"golang", "go"}: &GitHubRepo{ 151 github: github, 152 id: GitHubRepoID{"golang", "go"}, 153 issues: make(map[int32]*GitHubIssue), 154 }, 155 } 156 mutationTest{want: c}.test(t, &maintpb.Mutation{ 157 Github: &maintpb.GithubMutation{ 158 Owner: "golang", 159 Repo: "go", 160 }, 161 }) 162 } 163 164 func TestNewMutationsFromIssue(t *testing.T) { 165 gh := &github.Issue{ 166 Number: github.Int(5), 167 CreatedAt: &t1, 168 UpdatedAt: &t2, 169 Body: github.String("body of the issue"), 170 State: github.String("closed"), 171 } 172 gr := &GitHubRepo{ 173 id: GitHubRepoID{"golang", "go"}, 174 } 175 is := gr.newMutationFromIssue(nil, gh) 176 want := &maintpb.Mutation{GithubIssue: &maintpb.GithubIssueMutation{ 177 Owner: "golang", 178 Repo: "go", 179 Number: 5, 180 BodyChange: &maintpb.StringChange{Val: "body of the issue"}, 181 Created: tp1, 182 Updated: tp2, 183 Assignees: []*maintpb.GithubUser{}, 184 NoMilestone: true, 185 Closed: &maintpb.BoolChange{Val: true}, 186 }} 187 if !reflect.DeepEqual(is, want) { 188 t.Errorf("issue mismatch\n got: %v\nwant: %v\ndiff path: %v", spew.Sdump(is), spew.Sdump(want), 189 diffPath(reflect.ValueOf(is), reflect.ValueOf(want))) 190 } 191 } 192 193 func TestNewAssigneesHandlesNil(t *testing.T) { 194 users := []*github.User{ 195 &github.User{Login: github.String("foo"), ID: github.Int64(3)}, 196 } 197 got := newAssignees(nil, users) 198 want := []*maintpb.GithubUser{&maintpb.GithubUser{ 199 Id: 3, 200 Login: "foo", 201 }} 202 if !reflect.DeepEqual(got, want) { 203 t.Errorf("assignee mismatch\n got: %#v\nwant: %#v", got, want) 204 } 205 } 206 207 func TestAssigneesDeleted(t *testing.T) { 208 c := new(Corpus) 209 assignees := []*GitHubUser{u1, u2} 210 issue := &GitHubIssue{ 211 Number: 3, 212 User: u1, 213 Body: "some body", 214 Created: t2, 215 Updated: t2, 216 Assignees: assignees, 217 } 218 gr := &GitHubRepo{ 219 id: GitHubRepoID{"golang", "go"}, 220 issues: map[int32]*GitHubIssue{ 221 3: issue, 222 }, 223 } 224 c.github = &GitHub{ 225 users: map[int64]*GitHubUser{ 226 u1.ID: u1, 227 }, 228 repos: map[GitHubRepoID]*GitHubRepo{ 229 GitHubRepoID{"golang", "go"}: gr, 230 }, 231 } 232 233 mutation := gr.newMutationFromIssue(issue, &github.Issue{ 234 Number: github.Int(3), 235 Assignees: []*github.User{&github.User{ID: github.Int64(u2.ID)}}, 236 }) 237 c.addMutation(mutation) 238 gi := gr.issues[3] 239 if len(gi.Assignees) != 1 || gi.Assignees[0].ID != u2.ID { 240 t.Errorf("expected u1 to be deleted, got %v", gi.Assignees) 241 } 242 } 243 244 func TestSync(t *testing.T) { 245 c := new(Corpus) 246 assignees := []*GitHubUser{u1, u2} 247 issue := &GitHubIssue{ 248 Number: 3, 249 User: u1, 250 Body: "some body", 251 Created: t2, 252 Updated: t2, 253 Assignees: assignees, 254 } 255 gr := &GitHubRepo{ 256 id: GitHubRepoID{"golang", "go"}, 257 issues: map[int32]*GitHubIssue{ 258 3: issue, 259 }, 260 } 261 c.github = &GitHub{ 262 users: map[int64]*GitHubUser{ 263 u1.ID: u1, 264 }, 265 repos: map[GitHubRepoID]*GitHubRepo{ 266 GitHubRepoID{"golang", "go"}: gr, 267 }, 268 } 269 270 mutation := gr.newMutationFromIssue(issue, &github.Issue{ 271 Number: github.Int(3), 272 Assignees: []*github.User{&github.User{ID: github.Int64(u2.ID)}}, 273 }) 274 c.addMutation(mutation) 275 ctx := context.Background() 276 err := c.sync(ctx, false) 277 if err != nil { 278 t.Fatal("error: ", err) 279 } 280 } 281 282 func DeepDiff(got, want interface{}) error { 283 return diffPath(reflect.ValueOf(got), reflect.ValueOf(want)) 284 } 285 286 func diffPath(got, want reflect.Value) error { 287 if !got.IsValid() { 288 return errors.New("'got' value invalid") 289 } 290 if !want.IsValid() { 291 return errors.New("'want' value invalid") 292 } 293 294 t := got.Type() 295 if t != want.Type() { 296 return fmt.Errorf("got=%s, want=%s", got.Type(), want.Type()) 297 } 298 299 switch t.Kind() { 300 case reflect.Ptr, reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Slice: 301 if got.IsNil() != want.IsNil() { 302 if got.IsNil() { 303 return fmt.Errorf("got = (%s)(nil), want = non-nil", t) 304 } 305 return fmt.Errorf("got = (%s)(non-nil), want = nil", t) 306 } 307 } 308 309 switch t.Kind() { 310 case reflect.Ptr: 311 if got.IsNil() { 312 return nil 313 } 314 return diffPath(got.Elem(), want.Elem()) 315 316 case reflect.Struct: 317 nf := t.NumField() 318 for i := 0; i < nf; i++ { 319 sf := t.Field(i) 320 if err := diffPath(got.Field(i), want.Field(i)); err != nil { 321 inner := err.Error() 322 sep := "." 323 if strings.HasPrefix(inner, "field ") { 324 inner = strings.TrimPrefix(inner, "field ") 325 } else { 326 sep = ": " 327 } 328 return fmt.Errorf("field %s%s%v", sf.Name, sep, inner) 329 } 330 } 331 return nil 332 case reflect.String: 333 if got.String() != want.String() { 334 return fmt.Errorf("got = %q; want = %q", got.String(), want.String()) 335 } 336 return nil 337 338 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 339 if got.Int() != want.Int() { 340 return fmt.Errorf("got = %v; want = %v", got.Int(), want.Int()) 341 } 342 return nil 343 344 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 345 if got.Uint() != want.Uint() { 346 return fmt.Errorf("got = %v; want = %v", got.Uint(), want.Uint()) 347 } 348 return nil 349 350 case reflect.Bool: 351 if got.Bool() != want.Bool() { 352 return fmt.Errorf("got = %v; want = %v", got.Bool(), want.Bool()) 353 } 354 return nil 355 356 case reflect.Slice: 357 gl, wl := got.Len(), want.Len() 358 if gl != wl { 359 return fmt.Errorf("slice len %v; want %v", gl, wl) 360 } 361 for i := 0; i < gl; i++ { 362 if err := diffPath(got.Index(i), want.Index(i)); err != nil { 363 return fmt.Errorf("index[%d] differs: %v", i, err) 364 } 365 } 366 return nil 367 368 default: 369 return fmt.Errorf("unhandled kind %v", t.Kind()) 370 } 371 }