From e30f059fce4242956bd289e6690b150ddc6cd0d5 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Thu, 15 Jan 2026 12:50:59 +0100 Subject: dashboard: add a manuallyUpstreamed helper This helper function can be used in the reporting filtering rules to skip certain reporting stages depending on whether the previous stage(s) have been manually upstreamed. Add tests that it does have the intended effect. Cc #6554. --- dashboard/app/app_test.go | 40 +++++++++++++++++++++++++++++++++ dashboard/app/reporting.go | 8 +++++++ dashboard/app/reporting_test.go | 50 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+) diff --git a/dashboard/app/app_test.go b/dashboard/app/app_test.go index d4c9f765f..96861dfa5 100644 --- a/dashboard/app/app_test.go +++ b/dashboard/app/app_test.go @@ -631,6 +631,44 @@ var testConfig = &GlobalConfig{ }, }, }, + "skip-stage": { + AccessLevel: AccessPublic, + Key: "publickeypublickeypublickey", + Clients: map[string]string{ + clientSkipStage: keySkipStage, + }, + Repos: []KernelRepo{ + { + URL: "git://syzkaller.org/access-public.git", + Branch: "access-public", + Alias: "access-public", + }, + }, + Reporting: []Reporting{ + { + Name: "reporting1", + DailyLimit: 1000, + Config: &TestConfig{Index: 1}, + Embargo: 4 * 24 * time.Hour, + }, + { + Name: "reporting2", + DailyLimit: 1000, + Config: &TestConfig{Index: 2}, + Filter: func(bug *Bug) FilterResult { + if bug.manuallyUpstreamed("reporting1") { + return FilterSkip + } + return FilterReport + }, + }, + { + Name: "reporting3", + DailyLimit: 1000, + Config: &TestConfig{Index: 3}, + }, + }, + }, }, } @@ -683,6 +721,8 @@ const ( keyTreeTests = "keyTreeTestskeyTreeTestskeyTreeTests" clientAI = "client-ai" keyAI = "clientaikeyclientaikeyclientaikey" + clientSkipStage = "client-skip-stage" + keySkipStage = "skipstagekeyskipstagekeyskipstagekey" restrictedManager = "restricted-manager" noFixBisectionManager = "no-fix-bisection-manager" diff --git a/dashboard/app/reporting.go b/dashboard/app/reporting.go index 61bee541d..ad55ac6c3 100644 --- a/dashboard/app/reporting.go +++ b/dashboard/app/reporting.go @@ -387,6 +387,14 @@ func (bug *Bug) managerConfig(c context.Context) *ConfigManager { return &mgr } +func (bug *Bug) manuallyUpstreamed(name string) bool { + reporting := bugReportingByName(bug, name) + if reporting == nil { + return false + } + return !reporting.Closed.IsZero() && !reporting.Auto +} + func createNotification(c context.Context, typ dashapi.BugNotif, public bool, text string, bug *Bug, reporting *Reporting, bugReporting *BugReporting) (*dashapi.BugNotification, error) { reportingConfig, err := json.Marshal(reporting.Config) diff --git a/dashboard/app/reporting_test.go b/dashboard/app/reporting_test.go index b1400c705..211797488 100644 --- a/dashboard/app/reporting_test.go +++ b/dashboard/app/reporting_test.go @@ -1417,3 +1417,53 @@ Blocks diff, Path `, msg.Body) } + +func TestSkipStage(t *testing.T) { + // The test ensures that manuallyUpstreamed works as intended in reporting filters. + c := NewCtx(t) + defer c.Close() + client := c.makeClient(clientSkipStage, keySkipStage, true) + + build := testBuild(1) + client.UploadBuild(build) + + { + // Normal scenario - manual upstreaming. + client.ReportCrash(testCrash(build, 1)) + rep := client.pollBug() + c.expectEQ(string(rep.Config), `{"Index":1}`) + c.client.updateBug(rep.ID, dashapi.BugStatusUpstream, "") + client.pollNotifs(0) + rep = client.pollBug() + c.expectEQ(string(rep.Config), `{"Index":3}`) + c.client.updateBug(rep.ID, dashapi.BugStatusInvalid, "") + } + + { + // Auto-upstreamed. + client.ReportCrash(testCrash(build, 2)) + rep := client.pollBug() + c.expectEQ(string(rep.Config), `{"Index":1}`) + c.advanceTime(5 * 24 * time.Hour) + notifs := client.pollNotifs(1) + reply, _ := client.ReportingUpdate(&dashapi.BugUpdate{ + ID: notifs[0].ID, + Status: dashapi.BugStatusUpstream, + Notification: true, + }) + c.expectEQ(reply.OK, true) + rep = client.pollBug() + c.expectEQ(string(rep.Config), `{"Index":2}`) + c.client.updateBug(rep.ID, dashapi.BugStatusInvalid, "") + } + + { + // Manually invalidated. + client.ReportCrash(testCrash(build, 3)) + rep := client.pollBug() + c.expectEQ(string(rep.Config), `{"Index":1}`) + c.client.updateBug(rep.ID, dashapi.BugStatusInvalid, "") + client.pollNotifs(0) + client.pollBugs(0) + } +} -- cgit mrf-deployment