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

     1  // Copyright 2024 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 controller
     5  
     6  import (
     7  	"bytes"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/google/syzkaller/syz-cluster/pkg/api"
    12  	"github.com/google/syzkaller/syz-cluster/pkg/app"
    13  	"github.com/google/syzkaller/syz-cluster/pkg/db"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  func TestAPIGetSeries(t *testing.T) {
    19  	env, ctx := app.TestEnvironment(t)
    20  	client := TestServer(t, env)
    21  	ids := UploadTestSeries(t, ctx, client, testSeries)
    22  
    23  	ret, err := client.GetSessionSeries(ctx, ids.SessionID)
    24  	assert.NoError(t, err)
    25  	ret.ID = ""
    26  	assert.Equal(t, testSeries, ret)
    27  
    28  	ret, err = client.GetSeries(ctx, ids.SeriesID)
    29  	assert.NoError(t, err)
    30  	ret.ID = ""
    31  	assert.Equal(t, testSeries, ret)
    32  }
    33  
    34  func TestAPISuccessfulBuild(t *testing.T) {
    35  	env, ctx := app.TestEnvironment(t)
    36  	client := TestServer(t, env)
    37  	UploadTestBuild(t, ctx, client, testBuild)
    38  	info, err := client.LastBuild(ctx, &api.LastBuildReq{
    39  		Arch:       testBuild.Arch,
    40  		TreeName:   testBuild.TreeName,
    41  		ConfigName: testBuild.ConfigName,
    42  		Status:     api.BuildSuccess,
    43  	})
    44  	assert.NoError(t, err)
    45  	assert.Equal(t, testBuild, info)
    46  }
    47  
    48  func TestAPISaveFinding(t *testing.T) {
    49  	env, ctx := app.TestEnvironment(t)
    50  	client := TestServer(t, env)
    51  
    52  	ids := UploadTestSeries(t, ctx, client, testSeries)
    53  	buildResp := UploadTestBuild(t, ctx, client, testBuild)
    54  	err := client.UploadTestResult(ctx, &api.TestResult{
    55  		SessionID:   ids.SessionID,
    56  		BaseBuildID: buildResp.ID,
    57  		TestName:    "test",
    58  		Result:      api.TestRunning,
    59  		Log:         []byte("some log"),
    60  	})
    61  	assert.NoError(t, err)
    62  
    63  	t.Run("not existing test", func(t *testing.T) {
    64  		err = client.UploadFinding(ctx, &api.NewFinding{
    65  			SessionID: ids.SessionID,
    66  			TestName:  "unknown test",
    67  		})
    68  		assert.Error(t, err)
    69  	})
    70  
    71  	t.Run("must succeed", func(t *testing.T) {
    72  		finding := &api.NewFinding{
    73  			SessionID:    ids.SessionID,
    74  			TestName:     "test",
    75  			Title:        "title",
    76  			Report:       []byte("report"),
    77  			Log:          []byte("log"),
    78  			SyzRepro:     []byte("syz repro"),
    79  			SyzReproOpts: []byte("syz_repro_opts"),
    80  		}
    81  		err = client.UploadFinding(ctx, finding)
    82  		assert.NoError(t, err)
    83  		// Even if the same finding is reported the second time, it must still not fail.
    84  		err = client.UploadFinding(ctx, finding)
    85  		assert.NoError(t, err)
    86  	})
    87  
    88  	t.Run("add C repro", func(t *testing.T) {
    89  		finding := &api.NewFinding{
    90  			SessionID:    ids.SessionID,
    91  			TestName:     "test",
    92  			Title:        "title",
    93  			Report:       []byte("report"),
    94  			Log:          []byte("log"),
    95  			SyzRepro:     []byte("syz repro"),
    96  			SyzReproOpts: []byte("syz_repro_opts"),
    97  			CRepro:       []byte("C repro"),
    98  		}
    99  		err = client.UploadFinding(ctx, finding)
   100  		assert.NoError(t, err)
   101  		// Verify that C repro has appeared indeed.
   102  		findingRepo := db.NewFindingRepository(env.Spanner)
   103  		findings, err := findingRepo.ListForSession(ctx, ids.SessionID, db.NoLimit)
   104  		require.NoError(t, err)
   105  		require.Len(t, findings, 1)
   106  		assert.NotEmpty(t, findings[0].CReproURI)
   107  	})
   108  
   109  	t.Run("session stopped", func(t *testing.T) {
   110  		MarkSessionFinished(t, env, ids.SessionID)
   111  		finding := &api.NewFinding{
   112  			SessionID: ids.SessionID,
   113  			TestName:  "test",
   114  			Title:     "new title",
   115  			Report:    []byte("report"),
   116  			Log:       []byte("log"),
   117  			SyzRepro:  []byte("syz repro"),
   118  		}
   119  		err = client.UploadFinding(ctx, finding)
   120  		assert.ErrorContains(t, err, "session is already finished")
   121  	})
   122  }
   123  
   124  func TestAPIUploadTestArtifacts(t *testing.T) {
   125  	env, ctx := app.TestEnvironment(t)
   126  	client := TestServer(t, env)
   127  
   128  	ids := UploadTestSeries(t, ctx, client, testSeries)
   129  	buildResp := UploadTestBuild(t, ctx, client, testBuild)
   130  	err := client.UploadTestResult(ctx, &api.TestResult{
   131  		SessionID:   ids.SessionID,
   132  		BaseBuildID: buildResp.ID,
   133  		TestName:    "test",
   134  		Result:      api.TestRunning,
   135  		Log:         []byte("some log"),
   136  	})
   137  	assert.NoError(t, err)
   138  	err = client.UploadTestArtifacts(ctx, ids.SessionID, "test", bytes.NewReader([]byte("artifacts content")))
   139  	assert.NoError(t, err)
   140  }
   141  
   142  func TestAPIBaseFindings(t *testing.T) {
   143  	env, ctx := app.TestEnvironment(t)
   144  	client := TestServer(t, env)
   145  	buildResp := UploadTestBuild(t, ctx, client, testBuild)
   146  
   147  	err := client.UploadBaseFinding(ctx, &api.BaseFindingInfo{
   148  		BuildID: buildResp.ID,
   149  		Title:   "title 1",
   150  	})
   151  	assert.NoError(t, err)
   152  
   153  	// Let's upload a different build for the same revision.
   154  	buildResp2 := UploadTestBuild(t, ctx, client, testBuild)
   155  	assert.NotEqual(t, buildResp.ID, buildResp2.ID)
   156  
   157  	resp, err := client.BaseFindingStatus(ctx, &api.BaseFindingInfo{
   158  		BuildID: buildResp2.ID,
   159  		Title:   "title 1",
   160  	})
   161  	assert.NoError(t, err)
   162  	assert.True(t, resp.Observed)
   163  
   164  	t.Run("unseen title", func(t *testing.T) {
   165  		resp, err := client.BaseFindingStatus(ctx, &api.BaseFindingInfo{
   166  			BuildID: buildResp2.ID,
   167  			Title:   "title 2",
   168  		})
   169  		assert.NoError(t, err)
   170  		assert.False(t, resp.Observed)
   171  	})
   172  
   173  	t.Run("invalid build id", func(t *testing.T) {
   174  		_, err := client.BaseFindingStatus(ctx, &api.BaseFindingInfo{
   175  			BuildID: "unknown id",
   176  			Title:   "title 1",
   177  		})
   178  		assert.Error(t, err)
   179  	})
   180  }
   181  
   182  var testSeries = &api.Series{
   183  	ExtID:       "ext-id",
   184  	AuthorEmail: "some@email.com",
   185  	Title:       "test series name",
   186  	Version:     2,
   187  	PublishedAt: time.Date(2020, time.January, 1, 3, 0, 0, 0, time.UTC),
   188  	Cc:          []string{"email"},
   189  	SubjectTags: []string{"tag"},
   190  	Patches: []api.SeriesPatch{
   191  		{
   192  			Seq:  1,
   193  			Body: []byte("first content"),
   194  		},
   195  		{
   196  			Seq:  2,
   197  			Body: []byte("second content"),
   198  		},
   199  	},
   200  }
   201  
   202  var testBuild = &api.Build{
   203  	Arch:         "amd64",
   204  	TreeName:     "mainline",
   205  	TreeURL:      "https://git/tree",
   206  	ConfigName:   "config",
   207  	CommitHash:   "abcd",
   208  	CommitDate:   time.Date(2020, time.January, 1, 3, 0, 0, 0, time.UTC),
   209  	BuildSuccess: true,
   210  }