github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/cmd/codeqlExecuteScan_test.go (about)

     1  //go:build unit
     2  // +build unit
     3  
     4  package cmd
     5  
     6  import (
     7  	"fmt"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/SAP/jenkins-library/pkg/codeql"
    12  	"github.com/SAP/jenkins-library/pkg/mock"
    13  	"github.com/SAP/jenkins-library/pkg/orchestrator"
    14  	"github.com/pkg/errors"
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  type codeqlExecuteScanMockUtils struct {
    19  	*mock.ExecMockRunner
    20  	*mock.FilesMock
    21  }
    22  
    23  func newCodeqlExecuteScanTestsUtils() codeqlExecuteScanMockUtils {
    24  	utils := codeqlExecuteScanMockUtils{
    25  		ExecMockRunner: &mock.ExecMockRunner{},
    26  		FilesMock:      &mock.FilesMock{},
    27  	}
    28  	return utils
    29  }
    30  
    31  func TestRunCodeqlExecuteScan(t *testing.T) {
    32  
    33  	t.Run("Valid CodeqlExecuteScan", func(t *testing.T) {
    34  		config := codeqlExecuteScanOptions{BuildTool: "maven", ModulePath: "./"}
    35  		_, err := runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils())
    36  		assert.NoError(t, err)
    37  	})
    38  
    39  	t.Run("No auth token passed on upload results", func(t *testing.T) {
    40  		config := codeqlExecuteScanOptions{BuildTool: "maven", UploadResults: true, ModulePath: "./"}
    41  		_, err := runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils())
    42  		assert.Error(t, err)
    43  	})
    44  
    45  	t.Run("GitCommitID is NA on upload results", func(t *testing.T) {
    46  		config := codeqlExecuteScanOptions{BuildTool: "maven", UploadResults: true, ModulePath: "./", CommitID: "NA"}
    47  		_, err := runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils())
    48  		assert.Error(t, err)
    49  	})
    50  
    51  	t.Run("Custom buildtool", func(t *testing.T) {
    52  		config := codeqlExecuteScanOptions{BuildTool: "custom", Language: "javascript", ModulePath: "./"}
    53  		_, err := runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils())
    54  		assert.NoError(t, err)
    55  	})
    56  
    57  	t.Run("Custom buildtool but no language specified", func(t *testing.T) {
    58  		config := codeqlExecuteScanOptions{BuildTool: "custom", ModulePath: "./", GithubToken: "test"}
    59  		_, err := runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils())
    60  		assert.Error(t, err)
    61  	})
    62  
    63  	t.Run("Invalid buildtool and no language specified", func(t *testing.T) {
    64  		config := codeqlExecuteScanOptions{BuildTool: "test", ModulePath: "./", GithubToken: "test"}
    65  		_, err := runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils())
    66  		assert.Error(t, err)
    67  	})
    68  
    69  	t.Run("Invalid buildtool but language specified", func(t *testing.T) {
    70  		config := codeqlExecuteScanOptions{BuildTool: "test", Language: "javascript", ModulePath: "./", GithubToken: "test"}
    71  		_, err := runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils())
    72  		assert.NoError(t, err)
    73  	})
    74  }
    75  
    76  func TestGetGitRepoInfo(t *testing.T) {
    77  	t.Run("Valid https URL1", func(t *testing.T) {
    78  		var repoInfo RepoInfo
    79  		err := getGitRepoInfo("https://github.hello.test/Testing/fortify.git", &repoInfo)
    80  		assert.NoError(t, err)
    81  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
    82  		assert.Equal(t, "fortify", repoInfo.repo)
    83  		assert.Equal(t, "Testing", repoInfo.owner)
    84  	})
    85  
    86  	t.Run("Valid https URL2", func(t *testing.T) {
    87  		var repoInfo RepoInfo
    88  		err := getGitRepoInfo("https://github.hello.test/Testing/fortify", &repoInfo)
    89  		assert.NoError(t, err)
    90  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
    91  		assert.Equal(t, "fortify", repoInfo.repo)
    92  		assert.Equal(t, "Testing", repoInfo.owner)
    93  	})
    94  	t.Run("Valid https URL1 with dots", func(t *testing.T) {
    95  		var repoInfo RepoInfo
    96  		err := getGitRepoInfo("https://github.hello.test/Testing/com.sap.fortify.git", &repoInfo)
    97  		assert.NoError(t, err)
    98  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
    99  		assert.Equal(t, "com.sap.fortify", repoInfo.repo)
   100  		assert.Equal(t, "Testing", repoInfo.owner)
   101  	})
   102  
   103  	t.Run("Valid https URL2 with dots", func(t *testing.T) {
   104  		var repoInfo RepoInfo
   105  		err := getGitRepoInfo("https://github.hello.test/Testing/com.sap.fortify", &repoInfo)
   106  		assert.NoError(t, err)
   107  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
   108  		assert.Equal(t, "com.sap.fortify", repoInfo.repo)
   109  		assert.Equal(t, "Testing", repoInfo.owner)
   110  	})
   111  	t.Run("Valid https URL1 with username and token", func(t *testing.T) {
   112  		var repoInfo RepoInfo
   113  		err := getGitRepoInfo("https://username:token@github.hello.test/Testing/fortify.git", &repoInfo)
   114  		assert.NoError(t, err)
   115  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
   116  		assert.Equal(t, "fortify", repoInfo.repo)
   117  		assert.Equal(t, "Testing", repoInfo.owner)
   118  	})
   119  
   120  	t.Run("Valid https URL2 with username and token", func(t *testing.T) {
   121  		var repoInfo RepoInfo
   122  		err := getGitRepoInfo("https://username:token@github.hello.test/Testing/fortify", &repoInfo)
   123  		assert.NoError(t, err)
   124  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
   125  		assert.Equal(t, "fortify", repoInfo.repo)
   126  		assert.Equal(t, "Testing", repoInfo.owner)
   127  	})
   128  
   129  	t.Run("Invalid https URL as no org/owner passed", func(t *testing.T) {
   130  		var repoInfo RepoInfo
   131  		assert.Error(t, getGitRepoInfo("https://github.com/fortify", &repoInfo))
   132  	})
   133  
   134  	t.Run("Invalid URL as no protocol passed", func(t *testing.T) {
   135  		var repoInfo RepoInfo
   136  		assert.Error(t, getGitRepoInfo("github.hello.test/Testing/fortify", &repoInfo))
   137  	})
   138  
   139  	t.Run("Valid ssh URL1", func(t *testing.T) {
   140  		var repoInfo RepoInfo
   141  		err := getGitRepoInfo("git@github.hello.test/Testing/fortify.git", &repoInfo)
   142  		assert.NoError(t, err)
   143  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
   144  		assert.Equal(t, "fortify", repoInfo.repo)
   145  		assert.Equal(t, "Testing", repoInfo.owner)
   146  	})
   147  
   148  	t.Run("Valid ssh URL2", func(t *testing.T) {
   149  		var repoInfo RepoInfo
   150  		err := getGitRepoInfo("git@github.hello.test/Testing/fortify", &repoInfo)
   151  		assert.NoError(t, err)
   152  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
   153  		assert.Equal(t, "fortify", repoInfo.repo)
   154  		assert.Equal(t, "Testing", repoInfo.owner)
   155  	})
   156  	t.Run("Valid ssh URL1 with dots", func(t *testing.T) {
   157  		var repoInfo RepoInfo
   158  		err := getGitRepoInfo("git@github.hello.test/Testing/com.sap.fortify.git", &repoInfo)
   159  		assert.NoError(t, err)
   160  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
   161  		assert.Equal(t, "com.sap.fortify", repoInfo.repo)
   162  		assert.Equal(t, "Testing", repoInfo.owner)
   163  	})
   164  
   165  	t.Run("Valid ssh URL2 with dots", func(t *testing.T) {
   166  		var repoInfo RepoInfo
   167  		err := getGitRepoInfo("git@github.hello.test/Testing/com.sap.fortify", &repoInfo)
   168  		assert.NoError(t, err)
   169  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
   170  		assert.Equal(t, "com.sap.fortify", repoInfo.repo)
   171  		assert.Equal(t, "Testing", repoInfo.owner)
   172  	})
   173  
   174  	t.Run("Invalid ssh URL as no org/owner passed", func(t *testing.T) {
   175  		var repoInfo RepoInfo
   176  		assert.Error(t, getGitRepoInfo("git@github.com/fortify", &repoInfo))
   177  	})
   178  }
   179  
   180  func TestInitGitInfo(t *testing.T) {
   181  	t.Run("Valid URL1", func(t *testing.T) {
   182  		config := codeqlExecuteScanOptions{Repository: "https://github.hello.test/Testing/codeql.git", AnalyzedRef: "refs/head/branch", CommitID: "abcd1234"}
   183  		repoInfo, err := initGitInfo(&config)
   184  		assert.NoError(t, err)
   185  		assert.Equal(t, "abcd1234", repoInfo.commitId)
   186  		assert.Equal(t, "Testing", repoInfo.owner)
   187  		assert.Equal(t, "codeql", repoInfo.repo)
   188  		assert.Equal(t, "refs/head/branch", repoInfo.ref)
   189  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
   190  	})
   191  
   192  	t.Run("Valid URL2", func(t *testing.T) {
   193  		config := codeqlExecuteScanOptions{Repository: "https://github.hello.test/Testing/codeql", AnalyzedRef: "refs/head/branch", CommitID: "abcd1234"}
   194  		repoInfo, err := initGitInfo(&config)
   195  		assert.NoError(t, err)
   196  		assert.Equal(t, "abcd1234", repoInfo.commitId)
   197  		assert.Equal(t, "Testing", repoInfo.owner)
   198  		assert.Equal(t, "codeql", repoInfo.repo)
   199  		assert.Equal(t, "refs/head/branch", repoInfo.ref)
   200  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
   201  	})
   202  
   203  	t.Run("Valid url with dots URL1", func(t *testing.T) {
   204  		config := codeqlExecuteScanOptions{Repository: "https://github.hello.test/Testing/com.sap.codeql.git", AnalyzedRef: "refs/head/branch", CommitID: "abcd1234"}
   205  		repoInfo, err := initGitInfo(&config)
   206  		assert.NoError(t, err)
   207  		assert.Equal(t, "abcd1234", repoInfo.commitId)
   208  		assert.Equal(t, "Testing", repoInfo.owner)
   209  		assert.Equal(t, "com.sap.codeql", repoInfo.repo)
   210  		assert.Equal(t, "refs/head/branch", repoInfo.ref)
   211  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
   212  	})
   213  
   214  	t.Run("Valid url with dots URL2", func(t *testing.T) {
   215  		config := codeqlExecuteScanOptions{Repository: "https://github.hello.test/Testing/com.sap.codeql", AnalyzedRef: "refs/head/branch", CommitID: "abcd1234"}
   216  		repoInfo, err := initGitInfo(&config)
   217  		assert.NoError(t, err)
   218  		assert.Equal(t, "abcd1234", repoInfo.commitId)
   219  		assert.Equal(t, "Testing", repoInfo.owner)
   220  		assert.Equal(t, "com.sap.codeql", repoInfo.repo)
   221  		assert.Equal(t, "refs/head/branch", repoInfo.ref)
   222  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
   223  	})
   224  
   225  	t.Run("Valid url with username and token URL1", func(t *testing.T) {
   226  		config := codeqlExecuteScanOptions{Repository: "https://username:token@github.hello.test/Testing/codeql.git", AnalyzedRef: "refs/head/branch", CommitID: "abcd1234"}
   227  		repoInfo, err := initGitInfo(&config)
   228  		assert.NoError(t, err)
   229  		assert.Equal(t, "abcd1234", repoInfo.commitId)
   230  		assert.Equal(t, "Testing", repoInfo.owner)
   231  		assert.Equal(t, "codeql", repoInfo.repo)
   232  		assert.Equal(t, "refs/head/branch", repoInfo.ref)
   233  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
   234  	})
   235  
   236  	t.Run("Valid url with username and token URL2", func(t *testing.T) {
   237  		config := codeqlExecuteScanOptions{Repository: "https://username:token@github.hello.test/Testing/codeql", AnalyzedRef: "refs/head/branch", CommitID: "abcd1234"}
   238  		repoInfo, err := initGitInfo(&config)
   239  		assert.NoError(t, err)
   240  		assert.Equal(t, "abcd1234", repoInfo.commitId)
   241  		assert.Equal(t, "Testing", repoInfo.owner)
   242  		assert.Equal(t, "codeql", repoInfo.repo)
   243  		assert.Equal(t, "refs/head/branch", repoInfo.ref)
   244  		assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl)
   245  	})
   246  
   247  	t.Run("Invalid URL with no org/reponame", func(t *testing.T) {
   248  		config := codeqlExecuteScanOptions{Repository: "https://github.hello.test", AnalyzedRef: "refs/head/branch", CommitID: "abcd1234"}
   249  		repoInfo, err := initGitInfo(&config)
   250  		assert.NoError(t, err)
   251  		_, err = orchestrator.NewOrchestratorSpecificConfigProvider()
   252  		assert.Equal(t, "abcd1234", repoInfo.commitId)
   253  		assert.Equal(t, "refs/head/branch", repoInfo.ref)
   254  		if err != nil {
   255  			assert.Equal(t, "", repoInfo.owner)
   256  			assert.Equal(t, "", repoInfo.repo)
   257  			assert.Equal(t, "", repoInfo.serverUrl)
   258  		}
   259  	})
   260  }
   261  
   262  func TestBuildRepoReference(t *testing.T) {
   263  	t.Run("Valid ref with branch", func(t *testing.T) {
   264  		repository := "https://github.hello.test/Testing/fortify"
   265  		analyzedRef := "refs/head/branch"
   266  		ref, err := buildRepoReference(repository, analyzedRef)
   267  		assert.NoError(t, err)
   268  		assert.Equal(t, "https://github.hello.test/Testing/fortify/tree/branch", ref)
   269  	})
   270  	t.Run("Valid ref with PR", func(t *testing.T) {
   271  		repository := "https://github.hello.test/Testing/fortify"
   272  		analyzedRef := "refs/pull/1/merge"
   273  		ref, err := buildRepoReference(repository, analyzedRef)
   274  		assert.NoError(t, err)
   275  		assert.Equal(t, "https://github.hello.test/Testing/fortify/pull/1", ref)
   276  	})
   277  	t.Run("Invalid ref without branch name", func(t *testing.T) {
   278  		repository := "https://github.hello.test/Testing/fortify"
   279  		analyzedRef := "refs/head"
   280  		ref, err := buildRepoReference(repository, analyzedRef)
   281  		assert.Error(t, err)
   282  		assert.ErrorContains(t, err, "Wrong analyzedRef format")
   283  		assert.Equal(t, "", ref)
   284  	})
   285  	t.Run("Invalid ref without PR id", func(t *testing.T) {
   286  		repository := "https://github.hello.test/Testing/fortify"
   287  		analyzedRef := "refs/pull/merge"
   288  		ref, err := buildRepoReference(repository, analyzedRef)
   289  		assert.Error(t, err)
   290  		assert.ErrorContains(t, err, "Wrong analyzedRef format")
   291  		assert.Equal(t, "", ref)
   292  	})
   293  }
   294  
   295  func getRepoReferences(repoInfo RepoInfo) (string, string, string) {
   296  	repoUrl := fmt.Sprintf("%s/%s/%s", repoInfo.serverUrl, repoInfo.owner, repoInfo.repo)
   297  	repoReference, _ := buildRepoReference(repoUrl, repoInfo.ref)
   298  	repoCodeqlScanUrl := fmt.Sprintf("%s/security/code-scanning?query=is:open+ref:%s", repoUrl, repoInfo.ref)
   299  	return repoUrl, repoReference, repoCodeqlScanUrl
   300  }
   301  func TestCreateToolRecordCodeql(t *testing.T) {
   302  	t.Run("Valid toolrun file", func(t *testing.T) {
   303  		repoInfo := RepoInfo{serverUrl: "https://github.hello.test", commitId: "test", ref: "refs/head/branch", owner: "Testing", repo: "fortify"}
   304  		repoUrl, repoReference, repoCodeqlScanUrl := getRepoReferences(repoInfo)
   305  		toolRecord, err := createToolRecordCodeql(newCodeqlExecuteScanTestsUtils(), repoInfo, repoUrl, repoReference, repoCodeqlScanUrl)
   306  		assert.NoError(t, err)
   307  		assert.Equal(t, toolRecord.ToolName, "codeql")
   308  		assert.Equal(t, toolRecord.ToolInstance, "https://github.hello.test")
   309  		assert.Equal(t, toolRecord.DisplayName, "Testing fortify - refs/head/branch test")
   310  		assert.Equal(t, toolRecord.DisplayURL, "https://github.hello.test/Testing/fortify/security/code-scanning?query=is:open+ref:refs/head/branch")
   311  	})
   312  	t.Run("Empty repository URL", func(t *testing.T) {
   313  		repoInfo := RepoInfo{serverUrl: "", commitId: "test", ref: "refs/head/branch", owner: "Testing", repo: "fortify"}
   314  		repoUrl, repoReference, repoCodeqlScanUrl := getRepoReferences(repoInfo)
   315  		_, err := createToolRecordCodeql(newCodeqlExecuteScanTestsUtils(), repoInfo, repoUrl, repoReference, repoCodeqlScanUrl)
   316  
   317  		assert.Error(t, err)
   318  		assert.ErrorContains(t, err, "Repository not set")
   319  	})
   320  
   321  	t.Run("Empty analyzedRef", func(t *testing.T) {
   322  		repoInfo := RepoInfo{serverUrl: "https://github.hello.test", commitId: "test", ref: "", owner: "Testing", repo: "fortify"}
   323  		repoUrl, repoReference, repoCodeqlScanUrl := getRepoReferences(repoInfo)
   324  		_, err := createToolRecordCodeql(newCodeqlExecuteScanTestsUtils(), repoInfo, repoUrl, repoReference, repoCodeqlScanUrl)
   325  
   326  		assert.Error(t, err)
   327  		assert.ErrorContains(t, err, "Analyzed Reference not set")
   328  	})
   329  
   330  	t.Run("Empty CommitId", func(t *testing.T) {
   331  		repoInfo := RepoInfo{serverUrl: "https://github.hello.test", commitId: "", ref: "refs/head/branch", owner: "Testing", repo: "fortify"}
   332  		repoUrl, repoReference, repoCodeqlScanUrl := getRepoReferences(repoInfo)
   333  		_, err := createToolRecordCodeql(newCodeqlExecuteScanTestsUtils(), repoInfo, repoUrl, repoReference, repoCodeqlScanUrl)
   334  
   335  		assert.Error(t, err)
   336  		assert.ErrorContains(t, err, "CommitId not set")
   337  	})
   338  	t.Run("Invalid analyzedRef", func(t *testing.T) {
   339  		repoInfo := RepoInfo{serverUrl: "https://github.hello.test", commitId: "", ref: "refs/branch", owner: "Testing", repo: "fortify"}
   340  		repoUrl, repoReference, repoCodeqlScanUrl := getRepoReferences(repoInfo)
   341  		_, err := createToolRecordCodeql(newCodeqlExecuteScanTestsUtils(), repoInfo, repoUrl, repoReference, repoCodeqlScanUrl)
   342  
   343  		assert.Error(t, err)
   344  	})
   345  }
   346  
   347  func TestWaitSarifUploaded(t *testing.T) {
   348  	t.Parallel()
   349  	config := codeqlExecuteScanOptions{SarifCheckRetryInterval: 1, SarifCheckMaxRetries: 5}
   350  	t.Run("Fast complete upload", func(t *testing.T) {
   351  		codeqlScanAuditMock := CodeqlSarifUploaderMock{counter: 0}
   352  		timerStart := time.Now()
   353  		err := waitSarifUploaded(&config, &codeqlScanAuditMock)
   354  		assert.Less(t, time.Now().Sub(timerStart), time.Second)
   355  		assert.NoError(t, err)
   356  	})
   357  	t.Run("Long completed upload", func(t *testing.T) {
   358  		codeqlScanAuditMock := CodeqlSarifUploaderMock{counter: 2}
   359  		timerStart := time.Now()
   360  		err := waitSarifUploaded(&config, &codeqlScanAuditMock)
   361  		assert.GreaterOrEqual(t, time.Now().Sub(timerStart), time.Second*2)
   362  		assert.NoError(t, err)
   363  	})
   364  	t.Run("Failed upload", func(t *testing.T) {
   365  		codeqlScanAuditMock := CodeqlSarifUploaderMock{counter: -1}
   366  		err := waitSarifUploaded(&config, &codeqlScanAuditMock)
   367  		assert.Error(t, err)
   368  		assert.ErrorContains(t, err, "failed to upload sarif file")
   369  	})
   370  	t.Run("Error while checking sarif uploading", func(t *testing.T) {
   371  		codeqlScanAuditErrorMock := CodeqlSarifUploaderErrorMock{counter: -1}
   372  		err := waitSarifUploaded(&config, &codeqlScanAuditErrorMock)
   373  		assert.Error(t, err)
   374  		assert.ErrorContains(t, err, "test error")
   375  	})
   376  	t.Run("Completed upload after getting errors from server", func(t *testing.T) {
   377  		codeqlScanAuditErrorMock := CodeqlSarifUploaderErrorMock{counter: 3}
   378  		err := waitSarifUploaded(&config, &codeqlScanAuditErrorMock)
   379  		assert.NoError(t, err)
   380  	})
   381  	t.Run("Max retries reached", func(t *testing.T) {
   382  		codeqlScanAuditErrorMock := CodeqlSarifUploaderErrorMock{counter: 6}
   383  		err := waitSarifUploaded(&config, &codeqlScanAuditErrorMock)
   384  		assert.Error(t, err)
   385  		assert.ErrorContains(t, err, "max retries reached")
   386  	})
   387  }
   388  
   389  type CodeqlSarifUploaderMock struct {
   390  	counter int
   391  }
   392  
   393  func (c *CodeqlSarifUploaderMock) GetSarifStatus() (codeql.SarifFileInfo, error) {
   394  	if c.counter == 0 {
   395  		return codeql.SarifFileInfo{
   396  			ProcessingStatus: "complete",
   397  			Errors:           nil,
   398  		}, nil
   399  	}
   400  	if c.counter == -1 {
   401  		return codeql.SarifFileInfo{
   402  			ProcessingStatus: "failed",
   403  			Errors:           []string{"upload error"},
   404  		}, nil
   405  	}
   406  	c.counter--
   407  	return codeql.SarifFileInfo{
   408  		ProcessingStatus: "pending",
   409  		Errors:           nil,
   410  	}, nil
   411  }
   412  
   413  type CodeqlSarifUploaderErrorMock struct {
   414  	counter int
   415  }
   416  
   417  func (c *CodeqlSarifUploaderErrorMock) GetSarifStatus() (codeql.SarifFileInfo, error) {
   418  	if c.counter == -1 {
   419  		return codeql.SarifFileInfo{}, errors.New("test error")
   420  	}
   421  	if c.counter == 0 {
   422  		return codeql.SarifFileInfo{
   423  			ProcessingStatus: "complete",
   424  			Errors:           nil,
   425  		}, nil
   426  	}
   427  	c.counter--
   428  	return codeql.SarifFileInfo{ProcessingStatus: "Service unavailable"}, nil
   429  }