github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/syz-cluster/pkg/reporter/api_test.go (about)

     1  // Copyright 2025 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  package reporter
     5  
     6  import (
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/google/syzkaller/syz-cluster/pkg/api"
    11  	"github.com/google/syzkaller/syz-cluster/pkg/app"
    12  	"github.com/google/syzkaller/syz-cluster/pkg/controller"
    13  	"github.com/google/syzkaller/syz-cluster/pkg/service"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  func TestAPIReportFlow(t *testing.T) {
    19  	env, ctx := app.TestEnvironment(t)
    20  	client := controller.TestServer(t, env)
    21  
    22  	// Create series/session/test/findings.
    23  	testSeries := controller.DummySeries()
    24  	ids := controller.FakeSeriesWithFindings(t, ctx, env, client, testSeries)
    25  
    26  	generator := NewGenerator(env)
    27  	err := generator.Process(ctx, 1)
    28  	assert.NoError(t, err)
    29  
    30  	reportClient := TestServer(t, env)
    31  	// The same report will be returned multiple times.
    32  	nextResp, err := reportClient.GetNextReport(ctx, api.LKMLReporter)
    33  	assert.NoError(t, err)
    34  	nextResp2, err := reportClient.GetNextReport(ctx, api.LKMLReporter)
    35  	assert.NoError(t, err)
    36  	assert.Equal(t, nextResp2, nextResp)
    37  	// We don't know IDs in advance.
    38  	nextResp.Report.ID = ""
    39  	nextResp.Report.Series.ID = ""
    40  	// For URLs, just check if they are in place.
    41  	for _, finding := range nextResp.Report.Findings {
    42  		assert.NotEmpty(t, finding.LogURL, "%q's LogURL is empty", finding.Title)
    43  		finding.LogURL = ""
    44  		assert.NotEmpty(t, finding.LinkCRepro, "%q's LinkCRepro is empty", finding.Title)
    45  		finding.LinkCRepro = ""
    46  		assert.NotEmpty(t, finding.LinkSyzRepro, "%q's LinkSyzRepro is empty", finding.Title)
    47  		finding.LinkSyzRepro = ""
    48  		assert.NotEmpty(t, finding.Build.ConfigLink, "%q's ConfigLink is empty", finding.Title)
    49  		finding.Build.ConfigLink = ""
    50  	}
    51  
    52  	assert.Equal(t, &api.SessionReport{
    53  		Moderation: true,
    54  		Link:       env.URLs.Series(ids.SeriesID),
    55  		Series: &api.Series{
    56  			ExtID: testSeries.ExtID,
    57  			Title: testSeries.Title,
    58  			Link:  "http://link/to/series",
    59  			Cc:    []string{"first@user.com", "second@user.com"},
    60  			Patches: []api.SeriesPatch{
    61  				{
    62  					Seq:   1,
    63  					Title: "first patch title",
    64  					// Body is empty.
    65  				},
    66  			},
    67  		},
    68  		// These findings relate to controller.DummyFindings().
    69  		Findings: []*api.Finding{
    70  			{
    71  				Title:  "finding 0",
    72  				Report: "report 0",
    73  				Build: api.BuildInfo{
    74  					TreeName:   "mainline",
    75  					TreeURL:    "https://git/repo",
    76  					BaseCommit: "abcd",
    77  					Arch:       "amd64",
    78  					Compiler:   "compiler",
    79  				},
    80  			},
    81  			{
    82  				Title:  "finding 1",
    83  				Report: "report 1",
    84  				Build: api.BuildInfo{
    85  					TreeName:   "mainline",
    86  					TreeURL:    "https://git/repo",
    87  					BaseCommit: "abcd",
    88  					Arch:       "amd64",
    89  					Compiler:   "compiler",
    90  				},
    91  			},
    92  		},
    93  	}, nextResp.Report)
    94  
    95  	// Now confirm it.
    96  	reportID := nextResp2.Report.ID
    97  	err = reportClient.ConfirmReport(ctx, reportID)
    98  	assert.NoError(t, err)
    99  
   100  	// It should no longer appear in Next().
   101  	emptyNext, err := reportClient.GetNextReport(ctx, api.LKMLReporter)
   102  	assert.NoError(t, err)
   103  	assert.Nil(t, emptyNext.Report)
   104  
   105  	// "Upstream" it.
   106  	err = reportClient.UpstreamReport(ctx, reportID, &api.UpstreamReportReq{
   107  		User: "name",
   108  	})
   109  	assert.NoError(t, err)
   110  
   111  	// It should appear again, now with Moderation=false.
   112  	nextResp3, err := reportClient.GetNextReport(ctx, api.LKMLReporter)
   113  	assert.NoError(t, err)
   114  	assert.False(t, nextResp3.Report.Moderation)
   115  	assert.Equal(t, nextResp2.Report.Series, nextResp3.Report.Series)
   116  }
   117  
   118  func TestReplyReporting(t *testing.T) {
   119  	env, ctx := app.TestEnvironment(t)
   120  	client := controller.TestServer(t, env)
   121  
   122  	// Create series/session/test/findings.
   123  	testSeries := controller.DummySeries()
   124  	controller.FakeSeriesWithFindings(t, ctx, env, client, testSeries)
   125  
   126  	generator := NewGenerator(env)
   127  	err := generator.Process(ctx, 1)
   128  	assert.NoError(t, err)
   129  
   130  	// Create a report.
   131  	reportClient := TestServer(t, env)
   132  	nextResp, err := reportClient.GetNextReport(ctx, api.LKMLReporter)
   133  	assert.NoError(t, err)
   134  
   135  	// Confirm the report and set its message ID.
   136  	reportID := nextResp.Report.ID
   137  	err = reportClient.ConfirmReport(ctx, reportID)
   138  	assert.NoError(t, err)
   139  
   140  	const reportMessageID = "message-id"
   141  	_, err = reportClient.RecordReply(ctx, &api.RecordReplyReq{
   142  		MessageID: reportMessageID,
   143  		ReportID:  reportID,
   144  		Reporter:  api.LKMLReporter,
   145  	})
   146  	assert.NoError(t, err)
   147  
   148  	// Direct reply to the report.
   149  	resp, err := reportClient.RecordReply(ctx, &api.RecordReplyReq{
   150  		MessageID: "direct-reply-id",
   151  		InReplyTo: reportMessageID,
   152  		Reporter:  api.LKMLReporter,
   153  		Time:      time.Now(),
   154  	})
   155  	assert.NoError(t, err)
   156  	assert.Equal(t, &api.RecordReplyResp{
   157  		New:      true,
   158  		ReportID: reportID,
   159  	}, resp)
   160  
   161  	// Reply to the reply.
   162  	replyToReply := &api.RecordReplyReq{
   163  		MessageID: "reply-to-reply-id",
   164  		InReplyTo: "direct-reply-id",
   165  		Reporter:  api.LKMLReporter,
   166  		Time:      time.Now(),
   167  	}
   168  	resp, err = reportClient.RecordReply(ctx, replyToReply)
   169  	assert.NoError(t, err)
   170  	assert.Equal(t, &api.RecordReplyResp{
   171  		New:      true,
   172  		ReportID: reportID,
   173  	}, resp)
   174  
   175  	t.Run("dup-report", func(t *testing.T) {
   176  		resp, err := reportClient.RecordReply(ctx, replyToReply)
   177  		assert.NoError(t, err)
   178  		assert.Equal(t, &api.RecordReplyResp{
   179  			New:      false,
   180  			ReportID: reportID,
   181  		}, resp)
   182  	})
   183  
   184  	t.Run("unknown-message", func(t *testing.T) {
   185  		resp, err := reportClient.RecordReply(ctx, &api.RecordReplyReq{
   186  			MessageID: "whatever",
   187  			InReplyTo: "unknown-id",
   188  			Reporter:  api.LKMLReporter,
   189  		})
   190  		assert.NoError(t, err)
   191  		assert.Equal(t, &api.RecordReplyResp{
   192  			New:      false,
   193  			ReportID: "",
   194  		}, resp)
   195  	})
   196  }
   197  
   198  func TestInvalidate(t *testing.T) {
   199  	env, ctx := app.TestEnvironment(t)
   200  	client := controller.TestServer(t, env)
   201  	testSeries := controller.DummySeries()
   202  	ids := controller.FakeSeriesWithFindings(t, ctx, env, client, testSeries)
   203  
   204  	generator := NewGenerator(env)
   205  	err := generator.Process(ctx, 1)
   206  	require.NoError(t, err)
   207  
   208  	// Create a report.
   209  	reportClient := TestServer(t, env)
   210  	nextResp, err := reportClient.GetNextReport(ctx, api.LKMLReporter)
   211  	require.NoError(t, err)
   212  	reportID := nextResp.Report.ID
   213  	err = reportClient.ConfirmReport(ctx, reportID)
   214  	require.NoError(t, err)
   215  
   216  	// Invalidate the findings.
   217  	err = reportClient.InvalidateReport(ctx, reportID)
   218  	require.NoError(t, err)
   219  
   220  	// Report should not appear in Next().
   221  	emptyNext, err := reportClient.GetNextReport(ctx, api.LKMLReporter)
   222  	require.NoError(t, err)
   223  	assert.Nil(t, emptyNext.Report)
   224  
   225  	// All findings must be invalidated.
   226  	findingService := service.NewFindingService(env)
   227  	list, err := findingService.List(ctx, ids.SessionID, 0)
   228  	require.NoError(t, err)
   229  	assert.Len(t, list, 2)
   230  	for i, finding := range list {
   231  		assert.True(t, finding.Invalidated, "finding %d must be invalidated", i)
   232  	}
   233  }