blob: bbe87341bb8050e4df8e9d33ba38749e619bf2a8 [file] [log] [blame]
// Copyright 2023 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
package main
import (
"fmt"
"reflect"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/syzkaller/dashboard/dashapi"
"github.com/google/syzkaller/pkg/email"
)
func TestDiscussionAccess(t *testing.T) {
c := NewCtx(t)
defer c.Close()
client := c.makeClient(clientPublic, keyPublic, true)
build := testBuild(1)
client.UploadBuild(build)
// Bug at the first (AccesUser) stage of reporting.
crash := testCrash(build, 1)
client.ReportCrash(crash)
rep1 := client.pollBug()
// Bug at the second (AccessPublic) stage.
crash2 := testCrash(build, 2)
client.ReportCrash(crash2)
rep2user := client.pollBug()
client.updateBug(rep2user.ID, dashapi.BugStatusUpstream, "")
rep2 := client.pollBug()
// Patch to both bugs.
firstTime := timeNow(c.ctx)
c.advanceTime(time.Hour)
c.expectOK(client.SaveDiscussion(&dashapi.SaveDiscussionReq{
Discussion: &dashapi.Discussion{
ID: "123",
Source: dashapi.DiscussionLore,
Type: dashapi.DiscussionPatch,
Subject: "Patch for both bugs",
BugIDs: []string{rep1.ID, rep2.ID},
Messages: []dashapi.DiscussionMessage{
{
ID: "123",
External: true,
Time: firstTime,
},
},
},
}))
// Discussion about the second bug.
secondTime := timeNow(c.ctx)
c.advanceTime(time.Hour)
c.expectOK(client.SaveDiscussion(&dashapi.SaveDiscussionReq{
Discussion: &dashapi.Discussion{
ID: "456",
Source: dashapi.DiscussionLore,
Type: dashapi.DiscussionReport,
Subject: "Second bug reported",
BugIDs: []string{rep2.ID},
Messages: []dashapi.DiscussionMessage{
{
ID: "456",
External: false,
Time: secondTime,
},
},
},
}))
firstBug, _, err := findBugByReportingID(c.ctx, rep1.ID)
c.expectOK(err)
// Verify discussion that spans only one bug.
got, err := getBugDiscussionsUI(c.ctx, firstBug)
c.expectOK(err)
if diff := cmp.Diff([]*uiBugDiscussion{
{
Subject: "Patch for both bugs",
Link: "https://lore.kernel.org/all/123/T/",
Total: 1,
External: 1,
Last: firstTime,
},
}, got); diff != "" {
t.Fatal(diff)
}
secondBug, _, err := findBugByReportingID(c.ctx, rep2.ID)
c.expectOK(err)
// Verify that we also show discussions for several bugs.
got, err = getBugDiscussionsUI(c.ctx, secondBug)
c.expectOK(err)
if diff := cmp.Diff([]*uiBugDiscussion{
{
Subject: "Second bug reported",
Link: "https://lore.kernel.org/all/456/T/",
Total: 1,
External: 0,
Last: secondTime,
},
{
Subject: "Patch for both bugs",
Link: "https://lore.kernel.org/all/123/T/",
Total: 1,
External: 1,
Last: firstTime,
},
}, got); diff != "" {
t.Fatal(diff)
}
// Verify the summary.
summary := secondBug.discussionSummary()
if diff := cmp.Diff(DiscussionSummary{
AllMessages: 2,
ExternalMessages: 1,
LastMessage: secondTime,
LastPatchMessage: firstTime,
}, summary); diff != "" {
t.Fatal(diff)
}
}
func TestEmailOwnDiscussions(t *testing.T) {
c := NewCtx(t)
defer c.Close()
client := c.publicClient
build := testBuild(1)
client.UploadBuild(build)
crash := testCrash(build, 1)
client.ReportCrash(crash)
msg := client.pollEmailBug()
_, extBugID, err := email.RemoveAddrContext(msg.Sender)
c.expectOK(err)
// Start a discussion.
incoming1 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
Date: Tue, 15 Aug 2017 14:59:00 -0700
Message-ID: <1234>
Subject: Bug reported
From: %v
To: foo@bar.com, linux-kernel@vger.kernel.org
Content-Type: text/plain
Hello`, msg.Sender)
_, err = c.POST("/_ah/mail/lore@email.com", incoming1)
c.expectOK(err)
bug, _, err := findBugByReportingID(c.ctx, extBugID)
c.expectOK(err)
zone := time.FixedZone("", -7*60*60)
got, err := getBugDiscussionsUI(c.ctx, bug)
c.expectOK(err)
if diff := cmp.Diff([]*uiBugDiscussion{
{
Subject: "Bug reported",
Link: "https://lore.kernel.org/all/1234/T/",
Total: 1,
External: 0,
Last: time.Date(2017, time.August, 15, 14, 59, 0, 0, zone),
},
}, got); diff != "" {
t.Fatal(diff)
}
// Emulate some user-reply to the discussion.
incoming2 := fmt.Sprintf(`Sender: user@user.com
Date: Tue, 16 Aug 2017 14:59:00 -0700
Message-ID: <2345>
Subject: Re. Bug reported
From: user@user.com
In-Reply-To: <1234>
Cc: %v, linux-kernel@vger.kernel.org
Content-Type: text/plain
Hello`, msg.Sender)
_, err = c.POST("/_ah/mail/lore@email.com", incoming2)
c.expectOK(err)
bug, _, err = findBugByReportingID(c.ctx, extBugID)
c.expectOK(err)
got, err = getBugDiscussionsUI(c.ctx, bug)
c.expectOK(err)
if diff := cmp.Diff([]*uiBugDiscussion{
{
Subject: "Bug reported",
Link: "https://lore.kernel.org/all/1234/T/",
Total: 2,
External: 1,
Last: time.Date(2017, time.August, 16, 14, 59, 0, 0, zone),
},
}, got); diff != "" {
t.Fatal(diff)
}
}
func TestEmailUnrelatedDiscussion(t *testing.T) {
c := NewCtx(t)
defer c.Close()
client := c.publicClient
build := testBuild(1)
client.UploadBuild(build)
crash := testCrash(build, 1)
client.ReportCrash(crash)
msg := client.pollEmailBug()
_, extBugID, err := email.RemoveAddrContext(msg.Sender)
c.expectOK(err)
// An email that's not sent to the target email address.
incoming1 := fmt.Sprintf(`Date: Tue, 15 Aug 2017 14:59:00 -0700
Message-ID: <1234>
Subject: Some discussion
In-Reply-To: <2345>
From: user@user.com
To: %v, lore@email.com
Content-Type: text/plain
Hello`, msg.Sender)
_, err = c.POST("/_ah/mail/"+msg.Sender, incoming1)
c.expectOK(err)
bug, _, err := findBugByReportingID(c.ctx, extBugID)
c.expectOK(err)
// The discussion should go ignored.
got, err := getBugDiscussionsUI(c.ctx, bug)
c.expectOK(err)
if diff := cmp.Diff([]*uiBugDiscussion(nil), got); diff != "" {
t.Fatal(diff)
}
}
func TestEmailSubdiscussion(t *testing.T) {
c := NewCtx(t)
defer c.Close()
client := c.publicClient
build := testBuild(1)
client.UploadBuild(build)
crash := testCrash(build, 1)
client.ReportCrash(crash)
msg := client.pollEmailBug()
_, extBugID, err := email.RemoveAddrContext(msg.Sender)
c.expectOK(err)
incoming1 := fmt.Sprintf(`Date: Tue, 15 Aug 2017 14:59:00 -0700
Message-ID: <2345>
Subject: Some discussion
In-Reply-To: <1234>
From: user@user.com
To: %v
Cc: lore@email.com
Content-Type: text/plain
Hello`, msg.Sender)
_, err = c.POST("/_ah/mail/lore@email.com", incoming1)
c.expectOK(err)
bug, _, err := findBugByReportingID(c.ctx, extBugID)
c.expectOK(err)
// We have not seen the start of the discussion, but it should not go ignored.
got, err := getBugDiscussionsUI(c.ctx, bug)
c.expectOK(err)
client.expectEQ(len(got), 1)
client.expectEQ(got[0].Link, "https://lore.kernel.org/all/2345/T/")
}
func TestEmailPatchWithLink(t *testing.T) {
c := NewCtx(t)
defer c.Close()
client := c.publicClient
build := testBuild(1)
client.UploadBuild(build)
crash := testCrash(build, 1)
client.ReportCrash(crash)
msg := client.pollEmailBug()
_, extBugID, err := email.RemoveAddrContext(msg.Sender)
c.expectOK(err)
incoming1 := fmt.Sprintf(`Date: Tue, 15 Aug 2017 14:59:00 -0700
Message-ID: <2345>
Subject: [PATCH v3] A lot of fixes
From: user@user.com
To: lore@email.com
Content-Type: text/plain
Hello,
Link: https://testapp.appspot.com/bug?extid=%v
`, extBugID)
_, err = c.POST("/_ah/mail/lore@email.com", incoming1)
c.expectOK(err)
bug, _, err := findBugByReportingID(c.ctx, extBugID)
c.expectOK(err)
// We have not seen the start of the discussion, but it should not go ignored.
got, err := getBugDiscussionsUI(c.ctx, bug)
c.expectOK(err)
client.expectEQ(len(got), 1)
client.expectEQ(got[0].Link, "https://lore.kernel.org/all/2345/T/")
client.expectEQ(got[0].Subject, "[PATCH v3] A lot of fixes")
}
func TestIgnoreBotReplies(t *testing.T) {
c := NewCtx(t)
defer c.Close()
client := c.publicClient
build := testBuild(1)
client.UploadBuild(build)
crash := testCrash(build, 1)
client.ReportCrash(crash)
msg := client.pollEmailBug()
_, extBugID, err := email.RemoveAddrContext(msg.Sender)
c.expectOK(err)
incoming1 := fmt.Sprintf(`Date: Tue, 15 Aug 2017 14:59:00 -0700
Message-ID: <2345>
Subject: Re: Patch testing request
From: %v
To: lore@email.com
In-Reply-To: <1234>
Content-Type: text/plain
Hello!
`, msg.Sender)
_, err = c.POST("/_ah/mail/lore@email.com", incoming1)
c.expectOK(err)
bug, _, err := findBugByReportingID(c.ctx, extBugID)
c.expectOK(err)
// We have not seen the start of the discussion, but it should not go ignored.
got, err := getBugDiscussionsUI(c.ctx, bug)
c.expectOK(err)
client.expectEQ(len(got), 0)
}
func TestMessageOverflow(t *testing.T) {
date := time.Date(2000, time.January, 1, 1, 0, 0, 0, time.UTC)
d := &Discussion{}
first, last := dashapi.DiscussionMessage{
ID: date.String(),
Time: date,
}, dashapi.DiscussionMessage{}
d.addMessages([]dashapi.DiscussionMessage{first})
const blockSize = 100
for i := 0; i < 2*maxMessagesInDiscussion; i += blockSize {
block := []dashapi.DiscussionMessage{}
for j := 0; j < blockSize; j++ {
date = date.Add(time.Minute)
last = dashapi.DiscussionMessage{
ID: date.String(),
Time: date,
}
block = append(block, last)
}
// Make sure that the first message always remains in place and the last one
// is the latest one.
d.addMessages(block)
if !reflect.DeepEqual(first.ID, d.Messages[0].ID) {
t.Fatalf("unexpected first messages")
}
if !reflect.DeepEqual(last.ID, d.Messages[len(d.Messages)-1].ID) {
t.Fatalf("unexpected last messages")
}
}
if len(d.Messages) != maxMessagesInDiscussion {
t.Fatalf("expected len to be equal to %d, got %d",
maxMessagesInDiscussion, len(d.Messages))
}
}