Commit cf66fc30be
Verified · cmc
Layout: unified · split
internal/control/dashboard.go +17 −7
| @@ -226,11 +226,16 @@ func runDashboard(c *Ctx, args []string) int { | |||
| 226 | } | 226 | } |
| 227 | section("pinned:", []string{"PATH", "VISIBILITY", "DESCRIPTION"}, pinnedRows) | 227 | section("pinned:", []string{"PATH", "VISIBILITY", "DESCRIPTION"}, pinnedRows) |
| 228 | 228 | ||
| 229 | activityRows := make([][]cell, len(d.Activity)) | 229 | lines := FeedLines(events) |
| 230 | for i, e := range d.Activity { | 230 | activityRows := make([][]cell, len(lines)) |
| 231 | activityRows[i] = []cell{cAge(e.CreatedAt), cText(e.Actor), cText(e.Kind), cRef(e.Repo), cFlex(string(e.Data))} | 231 | for i, l := range lines { |
| 232 | sentence := fmt.Sprintf("%s %s %s%s", l.Actor, l.Verb, l.Repo, l.Ref) | ||
| 233 | if l.Extra != "" { | ||
| 234 | sentence += " " + l.Extra | ||
| 235 | } | ||
| 236 | activityRows[i] = []cell{cAge(l.When), cFlex(sentence)} | ||
| 232 | } | 237 | } |
| 233 | section("recent activity:", []string{"WHEN", "ACTOR", "KIND", "REPO", "DATA"}, activityRows) | 238 | section("recent activity:", []string{"WHEN", "EVENT"}, activityRows) |
| 234 | 239 | ||
| 235 | buildRows := make([][]cell, len(d.Builds)) | 240 | buildRows := make([][]cell, len(d.Builds)) |
| 236 | for i, b := range d.Builds { | 241 | for i, b := range d.Builds { |
| @@ -343,10 +348,15 @@ func runFeed(c *Ctx, args []string) int { | |||
| 343 | return strconv.FormatInt(e.ID, 10) | 348 | return strconv.FormatInt(e.ID, 10) |
| 344 | }) | 349 | }) |
| 345 | ds := feedOutputs(events) | 350 | ds := feedOutputs(events) |
| 351 | lines := FeedLines(events) | ||
| 346 | return c.emitPage(p, ds, next, func(w io.Writer) { | 352 | return c.emitPage(p, ds, next, func(w io.Writer) { |
| 347 | tb := c.table(w, "WHEN", "ACTOR", "KIND", "REPO", "DATA") | 353 | tb := c.table(w, "WHEN", "EVENT") |
| 348 | for _, d := range ds { | 354 | for _, l := range lines { |
| 349 | tb.row(cAge(d.CreatedAt), cText(d.Actor), cText(d.Kind), cRef(d.Repo), cFlex(string(d.Data))) | 355 | sentence := fmt.Sprintf("%s %s %s%s", l.Actor, l.Verb, l.Repo, l.Ref) |
| 356 | if l.Extra != "" { | ||
| 357 | sentence += " " + l.Extra | ||
| 358 | } | ||
| 359 | tb.row(cAge(l.When), cFlex(sentence)) | ||
| 350 | } | 360 | } |
| 351 | tb.flush() | 361 | tb.flush() |
| 352 | }) | 362 | }) |
internal/control/dashboard_test.go +47
| @@ -23,6 +23,53 @@ func TestDashboardEmptySectionsSayNone(t *testing.T) { | |||
| 23 | } | 23 | } |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | func TestDashboardActivityIsASentence(t *testing.T) { | ||
| 27 | c := notifTestCtx(t, "cmc") | ||
| 28 | repoID, err := c.Store.CreateRepo("user", c.User.ID, "gitbay", "public") | ||
| 29 | if err != nil { | ||
| 30 | t.Fatal(err) | ||
| 31 | } | ||
| 32 | repo, err := c.Store.RepoByID(repoID) | ||
| 33 | if err != nil { | ||
| 34 | t.Fatal(err) | ||
| 35 | } | ||
| 36 | c.Store.RecordEvent(repo.ID, c.User.ID, "issue.labeled", `{"number":262,"labels":["ops","security"]}`) | ||
| 37 | |||
| 38 | var out bytes.Buffer | ||
| 39 | c.Stdout, c.Stderr = &out, &out | ||
| 40 | if code := runDashboard(c, nil); code != 0 { | ||
| 41 | t.Fatalf("exit %d: %s", code, out.String()) | ||
| 42 | } | ||
| 43 | if strings.Contains(out.String(), `{"number"`) { | ||
| 44 | t.Errorf("raw payload leaked into plain output:\n%s", out.String()) | ||
| 45 | } | ||
| 46 | if !strings.Contains(out.String(), "cmc labelled cmc/gitbay#262 ops, security") { | ||
| 47 | t.Errorf("no sentence in output:\n%s", out.String()) | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | func TestFeedIsASentence(t *testing.T) { | ||
| 52 | c := notifTestCtx(t, "cmc") | ||
| 53 | repoID, err := c.Store.CreateRepo("user", c.User.ID, "gitbay", "public") | ||
| 54 | if err != nil { | ||
| 55 | t.Fatal(err) | ||
| 56 | } | ||
| 57 | repo, err := c.Store.RepoByID(repoID) | ||
| 58 | if err != nil { | ||
| 59 | t.Fatal(err) | ||
| 60 | } | ||
| 61 | c.Store.RecordEvent(repo.ID, c.User.ID, "issue.created", `{"number":1}`) | ||
| 62 | |||
| 63 | var out bytes.Buffer | ||
| 64 | c.Stdout, c.Stderr = &out, &out | ||
| 65 | if code := runFeed(c, nil); code != 0 { | ||
| 66 | t.Fatalf("exit %d: %s", code, out.String()) | ||
| 67 | } | ||
| 68 | if !strings.Contains(out.String(), "cmc opened issue cmc/gitbay#1") { | ||
| 69 | t.Errorf("no sentence in output:\n%s", out.String()) | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 26 | // The push queue is the one worker queue whose worst failure — a key_id | 73 | // The push queue is the one worker queue whose worst failure — a key_id |
| 27 | // or team_id Apple did not issue, which config validation cannot check — | 74 | // or team_id Apple did not issue, which config validation cannot check — |
| 28 | // dead-letters every row on its first attempt with nothing but a log | 75 | // dead-letters every row on its first attempt with nothing but a log |