Skip to content

Commit

Permalink
internal/labels: test syncLabels with TestingClient
Browse files Browse the repository at this point in the history
Replace the test of syncLabels with one based on the github
TestingClient.

Change-Id: I89809750d871c21ab10a2a0e10eeb88deefa2e6d
Reviewed-on: https://go-review.googlesource.com/c/oscar/+/637516
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
  • Loading branch information
jba committed Dec 18, 2024
1 parent 956d429 commit 7ba6238
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 86 deletions.
63 changes: 17 additions & 46 deletions internal/labels/labeler.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (l *Labeler) Run(ctx context.Context) error {

// Ensure that labels in GH match our config.
for p := range l.projects {
if err := l.syncLabels(ctx, p); err != nil {
if err := l.syncLabels(ctx, p, config.Categories); err != nil {
return err
}
}
Expand Down Expand Up @@ -182,51 +182,19 @@ func (l *Labeler) skip(e *github.Event) (bool, string) {
return false, ""
}

func (l *Labeler) syncLabels(ctx context.Context, project string) error {
// TODO(jba): generalize to other projects.
if project != "golang/go" {
return errors.New("labeling only supported for golang/go")
}
l.slog.Info("syncing labels", "name", l.name, "project", project)
return syncLabels(ctx, l.slog, config.Categories, ghLabels{l.github, project})
}

// trackerLabels manipulates the set of labels on an issue tracker.
// TODO: remove dependence on GitHub.
type trackerLabels interface {
CreateLabel(ctx context.Context, lab github.Label) error
EditLabel(ctx context.Context, name string, changes github.LabelChanges) error
ListLabels(ctx context.Context) ([]github.Label, error)
}

type ghLabels struct {
gh *github.Client
project string
}

func (g ghLabels) CreateLabel(ctx context.Context, lab github.Label) error {
return g.gh.CreateLabel(ctx, g.project, lab)
}

func (g ghLabels) EditLabel(ctx context.Context, name string, changes github.LabelChanges) error {
return g.gh.EditLabel(ctx, g.project, name, changes)
}

func (g ghLabels) ListLabels(ctx context.Context) ([]github.Label, error) {
return g.gh.ListLabels(ctx, g.project)
}

// labelColor is the color of labels created by syncLabels.
const labelColor = "4d0070"

// syncLabels attempts to reconcile the labels in cats with the labels on the issue tracker,
// syncLabels attempts to reconcile the configured labels in cats with the labels on the issue tracker,
// modifying the issue tracker's labels to match.
// If a label in cats is not on the issue tracker, it is created.
// Otherwise, if the label description on the issue tracker is empty, it is set to the description in the Category.
// Otherwise, if the descriptions don't agree, a warning is logged and nothing is done on the issue tracker.
// This function makes no other changes. In particular, it never deletes labels.
func syncLabels(ctx context.Context, lg *slog.Logger, cats []Category, tl trackerLabels) error {
tlabList, err := tl.ListLabels(ctx)
func (l *Labeler) syncLabels(ctx context.Context, project string, cats []Category) error {
// TODO(jba): generalize to other projects.
if project != "golang/go" {
return errors.New("labeling only supported for golang/go")
}
l.slog.Info("syncing labels", "name", l.name, "project", project)
tlabList, err := l.github.ListLabels(ctx, project)
if err != nil {
return err
}
Expand All @@ -238,26 +206,29 @@ func syncLabels(ctx context.Context, lg *slog.Logger, cats []Category, tl tracke
for _, cat := range cats {
lab, ok := tlabs[cat.Label]
if !ok {
lg.Info("creating label", "label", lab.Name)
if err := tl.CreateLabel(ctx, github.Label{
l.slog.Info("creating label", "label", lab.Name)
if err := l.github.CreateLabel(ctx, project, github.Label{
Name: cat.Label,
Description: cat.Description,
Color: labelColor,
}); err != nil {
return err
}
} else if lab.Description == "" {
lg.Info("setting empty label description", "label", lab.Name)
if err := tl.EditLabel(ctx, lab.Name, github.LabelChanges{Description: cat.Description}); err != nil {
l.slog.Info("setting empty label description", "label", lab.Name)
if err := l.github.EditLabel(ctx, project, lab.Name, github.LabelChanges{Description: cat.Description}); err != nil {
return err
}
} else if lab.Description != cat.Description {
lg.Warn("descriptions disagree", "label", lab.Name)
l.slog.Warn("descriptions disagree", "label", lab.Name)
}
}
return nil
}

// labelColor is the color of labels created by syncLabels.
const labelColor = "4d0070"

type actioner struct {
l *Labeler
}
Expand Down
65 changes: 25 additions & 40 deletions internal/labels/labeler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,72 +6,57 @@ package labels

import (
"context"
"errors"
"maps"
"slices"
"testing"

"github.com/google/go-cmp/cmp"
"golang.org/x/oscar/internal/github"
"golang.org/x/oscar/internal/storage"
"golang.org/x/oscar/internal/testutil"
)

func TestSyncLabels(t *testing.T) {
const project = "golang/go"
lg := testutil.Slogger(t)
db := storage.MemDB()
gh := github.New(lg, db, nil, nil)
labeler := New(lg, nil, gh, "")
m := map[string]github.Label{
"A": {Name: "A", Description: "a", Color: "a"},
"B": {Name: "B", Description: "", Color: "b"},
"C": {Name: "C", Description: "c", Color: "c"},
"D": {Name: "D", Description: "d", Color: "d"},
}
// Add the labels to the testing client in sorted order,
// so ListLabels returns them in that order.
for k := range maps.Keys(m) {
gh.Testing().AddLabel(project, m[k])
}

cats := []Category{
{Label: "A", Description: "a"}, // same as tracker
{Label: "B", Description: "b"}, // set empty tracker description
{Label: "C", Description: "other"}, // different descriptions
// D in tracker but not in cats
{Label: "E", Description: "e"}, // create
}
tl := &testTrackerLabels{m}

if err := syncLabels(context.Background(), testutil.Slogger(t), cats, tl); err != nil {
if err := labeler.syncLabels(context.Background(), project, cats); err != nil {
t.Fatal(err)
}

want := map[string]github.Label{
"A": {Name: "A", Description: "a", Color: "a"},
"B": {Name: "B", Description: "b", Color: "b"}, // added B description
"C": {Name: "C", Description: "c", Color: "c"},
"D": {Name: "D", Description: "d", Color: "d"},
"E": {Name: "E", Description: "e", Color: labelColor}, // added E
}

if got := tl.m; !maps.Equal(got, want) {
t.Errorf("\ngot %v\nwant %v", got, want)
}
}

type testTrackerLabels struct {
m map[string]github.Label
}

func (t *testTrackerLabels) CreateLabel(ctx context.Context, lab github.Label) error {
if _, ok := t.m[lab.Name]; ok {
return errors.New("label exists")
want := []*github.TestingEdit{
{
// add B description
Project: project,
Label: github.Label{Name: "B"},
LabelChanges: &github.LabelChanges{Description: "b"},
},
// create label E
{Project: project, Label: github.Label{Name: "E", Description: "e", Color: labelColor}},
}
t.m[lab.Name] = lab
return nil
}

func (t *testTrackerLabels) ListLabels(ctx context.Context) ([]github.Label, error) {
return slices.Collect(maps.Values(t.m)), nil
}

func (t *testTrackerLabels) EditLabel(ctx context.Context, name string, changes github.LabelChanges) error {
if changes.NewName != "" || changes.Color != "" {
return errors.New("unsupported edit")
}
if lab, ok := t.m[name]; ok {
lab.Description = changes.Description
t.m[name] = lab
return nil
if diff := cmp.Diff(want, gh.Testing().Edits()); diff != "" {
t.Errorf("mismatch (-want, got):\n%s", diff)
}
return errors.New("not found")
}

0 comments on commit 7ba6238

Please sign in to comment.