github.com/google/go-github/v69@v69.2.0/github/code_scanning_test.go (about)

     1  // Copyright 2020 The go-github AUTHORS. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package github
     7  
     8  import (
     9  	"context"
    10  	"encoding/json"
    11  	"fmt"
    12  	"net/http"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/google/go-cmp/cmp"
    17  )
    18  
    19  func TestCodeScanningService_Alert_ID(t *testing.T) {
    20  	t.Parallel()
    21  	// Test: nil Alert ID == 0
    22  	var a *Alert
    23  	id := a.ID()
    24  	var want int64
    25  	if id != want {
    26  		t.Errorf("Alert.ID error returned %+v, want %+v", id, want)
    27  	}
    28  
    29  	// Test: Valid HTMLURL
    30  	a = &Alert{
    31  		HTMLURL: Ptr("https://github.com/o/r/security/code-scanning/88"),
    32  	}
    33  	id = a.ID()
    34  	want = 88
    35  	if !cmp.Equal(id, want) {
    36  		t.Errorf("Alert.ID error returned %+v, want %+v", id, want)
    37  	}
    38  
    39  	// Test: HTMLURL is nil
    40  	a = &Alert{}
    41  	id = a.ID()
    42  	want = 0
    43  	if !cmp.Equal(id, want) {
    44  		t.Errorf("Alert.ID error returned %+v, want %+v", id, want)
    45  	}
    46  
    47  	// Test: ID can't be parsed as an int
    48  	a = &Alert{
    49  		HTMLURL: Ptr("https://github.com/o/r/security/code-scanning/bad88"),
    50  	}
    51  	id = a.ID()
    52  	want = 0
    53  	if !cmp.Equal(id, want) {
    54  		t.Errorf("Alert.ID error returned %+v, want %+v", id, want)
    55  	}
    56  }
    57  
    58  func TestCodeScanningService_UploadSarif(t *testing.T) {
    59  	t.Parallel()
    60  	client, mux, _ := setup(t)
    61  
    62  	expectedSarifID := &SarifID{
    63  		ID:  Ptr("testid"),
    64  		URL: Ptr("https://example.com/testurl"),
    65  	}
    66  
    67  	mux.HandleFunc("/repos/o/r/code-scanning/sarifs", func(w http.ResponseWriter, r *http.Request) {
    68  		v := new(SarifAnalysis)
    69  		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
    70  		testMethod(t, r, "POST")
    71  		want := &SarifAnalysis{CommitSHA: Ptr("abc"), Ref: Ptr("ref/head/main"), Sarif: Ptr("abc"), CheckoutURI: Ptr("uri"), StartedAt: &Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)}, ToolName: Ptr("codeql-cli")}
    72  		if !cmp.Equal(v, want) {
    73  			t.Errorf("Request body = %+v, want %+v", v, want)
    74  		}
    75  
    76  		w.WriteHeader(http.StatusAccepted)
    77  		respBody, _ := json.Marshal(expectedSarifID)
    78  		_, _ = w.Write(respBody)
    79  	})
    80  
    81  	ctx := context.Background()
    82  	sarifAnalysis := &SarifAnalysis{CommitSHA: Ptr("abc"), Ref: Ptr("ref/head/main"), Sarif: Ptr("abc"), CheckoutURI: Ptr("uri"), StartedAt: &Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)}, ToolName: Ptr("codeql-cli")}
    83  	respSarifID, _, err := client.CodeScanning.UploadSarif(ctx, "o", "r", sarifAnalysis)
    84  	if err != nil {
    85  		t.Errorf("CodeScanning.UploadSarif returned error: %v", err)
    86  	}
    87  	if !cmp.Equal(expectedSarifID, respSarifID) {
    88  		t.Errorf("Sarif response = %+v, want %+v", respSarifID, expectedSarifID)
    89  	}
    90  
    91  	const methodName = "UploadSarif"
    92  	testBadOptions(t, methodName, func() (err error) {
    93  		_, _, err = client.CodeScanning.UploadSarif(ctx, "\n", "\n", sarifAnalysis)
    94  		return err
    95  	})
    96  
    97  	testNewRequestAndDoFailureCategory(t, methodName, client, CodeScanningUploadCategory, func() (*Response, error) {
    98  		_, resp, err := client.CodeScanning.UploadSarif(ctx, "o", "r", sarifAnalysis)
    99  		return resp, err
   100  	})
   101  }
   102  
   103  func TestCodeScanningService_GetSARIF(t *testing.T) {
   104  	t.Parallel()
   105  	client, mux, _ := setup(t)
   106  
   107  	mux.HandleFunc("/repos/o/r/code-scanning/sarifs/abc", func(w http.ResponseWriter, r *http.Request) {
   108  		testMethod(t, r, "GET")
   109  		fmt.Fprint(w, `{
   110  			"processing_status": "s",
   111  			"analyses_url": "u"
   112  		}`)
   113  	})
   114  
   115  	ctx := context.Background()
   116  	sarifUpload, _, err := client.CodeScanning.GetSARIF(ctx, "o", "r", "abc")
   117  	if err != nil {
   118  		t.Errorf("CodeScanning.GetSARIF returned error: %v", err)
   119  	}
   120  
   121  	want := &SARIFUpload{
   122  		ProcessingStatus: Ptr("s"),
   123  		AnalysesURL:      Ptr("u"),
   124  	}
   125  	if !cmp.Equal(sarifUpload, want) {
   126  		t.Errorf("CodeScanning.GetSARIF returned %+v, want %+v", sarifUpload, want)
   127  	}
   128  
   129  	const methodName = "GetSARIF"
   130  	testBadOptions(t, methodName, func() (err error) {
   131  		_, _, err = client.CodeScanning.GetSARIF(ctx, "\n", "\n", "\n")
   132  		return err
   133  	})
   134  
   135  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   136  		got, resp, err := client.CodeScanning.GetSARIF(ctx, "o", "r", "abc")
   137  		if got != nil {
   138  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   139  		}
   140  		return resp, err
   141  	})
   142  }
   143  
   144  func TestCodeScanningService_ListAlertsForOrg(t *testing.T) {
   145  	t.Parallel()
   146  	client, mux, _ := setup(t)
   147  
   148  	mux.HandleFunc("/orgs/o/code-scanning/alerts", func(w http.ResponseWriter, r *http.Request) {
   149  		testMethod(t, r, "GET")
   150  		testFormValues(t, r, values{"state": "open", "ref": "heads/master", "severity": "warning", "tool_name": "CodeQL", "tool_guid": "guid", "direction": "asc", "sort": "updated"})
   151  		fmt.Fprint(w, `[{
   152  				"repository": {
   153  					"id": 1,
   154  					"name": "n",
   155  					"url": "url"
   156  				},
   157  				"rule_id":"js/trivial-conditional",
   158  				"rule_severity":"warning",
   159  				"rule_description":"Useless conditional",
   160  				"tool": {
   161  					"name": "CodeQL",
   162  					"guid": "guid",
   163  					"version": "1.4.0"
   164  				},
   165  				"rule": {
   166  					"id": "js/trivial-conditional",
   167  					"severity": "warning",
   168  					"description": "Useless conditional",
   169  					"name": "js/trivial-conditional",
   170  					"full_description": "Expression has no effect",
   171  					"help": "Expression has no effect"
   172  				},
   173  				"most_recent_instance": {
   174  					"ref": "refs/heads/main",
   175  					"state": "open",
   176  					"commit_sha": "abcdefg12345",
   177  					"message": {
   178  						"text": "This path depends on a user-provided value."
   179  					},
   180  					"location": {
   181  						"path": "spec-main/api-session-spec.ts",
   182  						"start_line": 917,
   183  						"end_line": 917,
   184  						"start_column": 7,
   185  						"end_column": 18
   186  					},
   187  					"classifications": [
   188  						"test"
   189  					]
   190  				},
   191  				"created_at":"2020-05-06T12:00:00Z",
   192  				"state":"open",
   193  				"closed_by":null,
   194  				"closed_at":null,
   195  				"url":"https://api.github.com/repos/o/r/code-scanning/alerts/25",
   196  				"html_url":"https://github.com/o/r/security/code-scanning/25"
   197  				},
   198  				{
   199  				"rule_id":"js/useless-expression",
   200  				"rule_severity":"warning",
   201  				"rule_description":"Expression has no effect",
   202  				"tool": {
   203  					"name": "CodeQL",
   204  					"guid": null,
   205  					"version": "1.4.0"
   206  				},
   207  				"rule": {
   208  					"id": "js/useless-expression",
   209  					"severity": "warning",
   210  					"description": "Expression has no effect",
   211  					"name": "js/useless-expression",
   212  					"full_description": "Expression has no effect",
   213  					"help": "Expression has no effect"
   214  				},
   215  				"most_recent_instance": {
   216  					"ref": "refs/heads/main",
   217  					"state": "open",
   218  					"commit_sha": "abcdefg12345",
   219  					"message": {
   220  						"text": "This path depends on a user-provided value."
   221  					},
   222  					"location": {
   223  						"path": "spec-main/api-session-spec.ts",
   224  						"start_line": 917,
   225  						"end_line": 917,
   226  						"start_column": 7,
   227  						"end_column": 18
   228  					},
   229  					"classifications": [
   230  						"test"
   231  					]
   232  				},
   233  				"created_at":"2020-05-06T12:00:00Z",
   234  				"state":"open",
   235  				"closed_by":null,
   236  				"closed_at":null,
   237  				"url":"https://api.github.com/repos/o/r/code-scanning/alerts/88",
   238  				"html_url":"https://github.com/o/r/security/code-scanning/88"
   239  				}]`)
   240  	})
   241  
   242  	opts := &AlertListOptions{State: "open", Ref: "heads/master", Severity: "warning", ToolName: "CodeQL", ToolGUID: "guid", Direction: "asc", Sort: "updated"}
   243  	ctx := context.Background()
   244  	alerts, _, err := client.CodeScanning.ListAlertsForOrg(ctx, "o", opts)
   245  	if err != nil {
   246  		t.Errorf("CodeScanning.ListAlertsForOrg returned error: %v", err)
   247  	}
   248  
   249  	date := Timestamp{time.Date(2020, time.May, 06, 12, 00, 00, 0, time.UTC)}
   250  	want := []*Alert{
   251  		{
   252  			Repository: &Repository{
   253  				ID:   Ptr(int64(1)),
   254  				URL:  Ptr("url"),
   255  				Name: Ptr("n"),
   256  			},
   257  			RuleID:          Ptr("js/trivial-conditional"),
   258  			RuleSeverity:    Ptr("warning"),
   259  			RuleDescription: Ptr("Useless conditional"),
   260  			Tool:            &Tool{Name: Ptr("CodeQL"), GUID: Ptr("guid"), Version: Ptr("1.4.0")},
   261  			Rule: &Rule{
   262  				ID:              Ptr("js/trivial-conditional"),
   263  				Severity:        Ptr("warning"),
   264  				Description:     Ptr("Useless conditional"),
   265  				Name:            Ptr("js/trivial-conditional"),
   266  				FullDescription: Ptr("Expression has no effect"),
   267  				Help:            Ptr("Expression has no effect"),
   268  			},
   269  			CreatedAt: &date,
   270  			State:     Ptr("open"),
   271  			ClosedBy:  nil,
   272  			ClosedAt:  nil,
   273  			URL:       Ptr("https://api.github.com/repos/o/r/code-scanning/alerts/25"),
   274  			HTMLURL:   Ptr("https://github.com/o/r/security/code-scanning/25"),
   275  			MostRecentInstance: &MostRecentInstance{
   276  				Ref:       Ptr("refs/heads/main"),
   277  				State:     Ptr("open"),
   278  				CommitSHA: Ptr("abcdefg12345"),
   279  				Message: &Message{
   280  					Text: Ptr("This path depends on a user-provided value."),
   281  				},
   282  				Location: &Location{
   283  					Path:        Ptr("spec-main/api-session-spec.ts"),
   284  					StartLine:   Ptr(917),
   285  					EndLine:     Ptr(917),
   286  					StartColumn: Ptr(7),
   287  					EndColumn:   Ptr(18),
   288  				},
   289  				Classifications: []string{"test"},
   290  			},
   291  		},
   292  		{
   293  			RuleID:          Ptr("js/useless-expression"),
   294  			RuleSeverity:    Ptr("warning"),
   295  			RuleDescription: Ptr("Expression has no effect"),
   296  			Tool:            &Tool{Name: Ptr("CodeQL"), GUID: nil, Version: Ptr("1.4.0")},
   297  			Rule: &Rule{
   298  				ID:              Ptr("js/useless-expression"),
   299  				Severity:        Ptr("warning"),
   300  				Description:     Ptr("Expression has no effect"),
   301  				Name:            Ptr("js/useless-expression"),
   302  				FullDescription: Ptr("Expression has no effect"),
   303  				Help:            Ptr("Expression has no effect"),
   304  			},
   305  			CreatedAt: &date,
   306  			State:     Ptr("open"),
   307  			ClosedBy:  nil,
   308  			ClosedAt:  nil,
   309  			URL:       Ptr("https://api.github.com/repos/o/r/code-scanning/alerts/88"),
   310  			HTMLURL:   Ptr("https://github.com/o/r/security/code-scanning/88"),
   311  			MostRecentInstance: &MostRecentInstance{
   312  				Ref:       Ptr("refs/heads/main"),
   313  				State:     Ptr("open"),
   314  				CommitSHA: Ptr("abcdefg12345"),
   315  				Message: &Message{
   316  					Text: Ptr("This path depends on a user-provided value."),
   317  				},
   318  				Location: &Location{
   319  					Path:        Ptr("spec-main/api-session-spec.ts"),
   320  					StartLine:   Ptr(917),
   321  					EndLine:     Ptr(917),
   322  					StartColumn: Ptr(7),
   323  					EndColumn:   Ptr(18),
   324  				},
   325  				Classifications: []string{"test"},
   326  			},
   327  		},
   328  	}
   329  	if !cmp.Equal(alerts, want) {
   330  		t.Errorf("CodeScanning.ListAlertsForOrg returned %+v, want %+v", alerts, want)
   331  	}
   332  
   333  	const methodName = "ListAlertsForOrg"
   334  	testBadOptions(t, methodName, func() (err error) {
   335  		_, _, err = client.CodeScanning.ListAlertsForOrg(ctx, "\n", opts)
   336  		return err
   337  	})
   338  
   339  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   340  		got, resp, err := client.CodeScanning.ListAlertsForOrg(ctx, "o", opts)
   341  		if got != nil {
   342  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   343  		}
   344  		return resp, err
   345  	})
   346  }
   347  
   348  func TestCodeScanningService_ListAlertsForOrgLisCursorOptions(t *testing.T) {
   349  	t.Parallel()
   350  	client, mux, _ := setup(t)
   351  
   352  	mux.HandleFunc("/orgs/o/code-scanning/alerts", func(w http.ResponseWriter, r *http.Request) {
   353  		testMethod(t, r, "GET")
   354  		testFormValues(t, r, values{"state": "open", "ref": "heads/master", "severity": "warning", "tool_name": "CodeQL", "per_page": "1", "before": "deadbeefb", "after": "deadbeefa"})
   355  		fmt.Fprint(w, `[{
   356  				"repository": {
   357  					"id": 1,
   358  					"name": "n",
   359  					"url": "url"
   360  				},
   361  				"rule_id":"js/trivial-conditional",
   362  				"rule_severity":"warning",
   363  				"rule_description":"Useless conditional",
   364  				"tool": {
   365  					"name": "CodeQL",
   366  					"guid": null,
   367  					"version": "1.4.0"
   368  				},
   369  				"rule": {
   370  					"id": "js/trivial-conditional",
   371  					"severity": "warning",
   372  					"description": "Useless conditional",
   373  					"name": "js/trivial-conditional",
   374  					"full_description": "Expression has no effect",
   375  					"help": "Expression has no effect"
   376  				},
   377  				"most_recent_instance": {
   378  					"ref": "refs/heads/main",
   379  					"state": "open",
   380  					"commit_sha": "abcdefg12345",
   381  					"message": {
   382  						"text": "This path depends on a user-provided value."
   383  					},
   384  					"location": {
   385  						"path": "spec-main/api-session-spec.ts",
   386  						"start_line": 917,
   387  						"end_line": 917,
   388  						"start_column": 7,
   389  						"end_column": 18
   390  					},
   391  					"classifications": [
   392  						"test"
   393  					]
   394  				},
   395  				"created_at":"2020-05-06T12:00:00Z",
   396  				"state":"open",
   397  				"closed_by":null,
   398  				"closed_at":null,
   399  				"url":"https://api.github.com/repos/o/r/code-scanning/alerts/25",
   400  				"html_url":"https://github.com/o/r/security/code-scanning/25"
   401  				}]`)
   402  	})
   403  
   404  	opts := &AlertListOptions{State: "open", Ref: "heads/master", Severity: "warning", ToolName: "CodeQL", ListCursorOptions: ListCursorOptions{PerPage: 1, Before: "deadbeefb", After: "deadbeefa"}}
   405  	ctx := context.Background()
   406  	alerts, _, err := client.CodeScanning.ListAlertsForOrg(ctx, "o", opts)
   407  	if err != nil {
   408  		t.Errorf("CodeScanning.ListAlertsForOrg returned error: %v", err)
   409  	}
   410  
   411  	date := Timestamp{time.Date(2020, time.May, 06, 12, 00, 00, 0, time.UTC)}
   412  	want := []*Alert{
   413  		{
   414  			Repository: &Repository{
   415  				ID:   Ptr(int64(1)),
   416  				URL:  Ptr("url"),
   417  				Name: Ptr("n"),
   418  			},
   419  			RuleID:          Ptr("js/trivial-conditional"),
   420  			RuleSeverity:    Ptr("warning"),
   421  			RuleDescription: Ptr("Useless conditional"),
   422  			Tool:            &Tool{Name: Ptr("CodeQL"), GUID: nil, Version: Ptr("1.4.0")},
   423  			Rule: &Rule{
   424  				ID:              Ptr("js/trivial-conditional"),
   425  				Severity:        Ptr("warning"),
   426  				Description:     Ptr("Useless conditional"),
   427  				Name:            Ptr("js/trivial-conditional"),
   428  				FullDescription: Ptr("Expression has no effect"),
   429  				Help:            Ptr("Expression has no effect"),
   430  			},
   431  			CreatedAt: &date,
   432  			State:     Ptr("open"),
   433  			ClosedBy:  nil,
   434  			ClosedAt:  nil,
   435  			URL:       Ptr("https://api.github.com/repos/o/r/code-scanning/alerts/25"),
   436  			HTMLURL:   Ptr("https://github.com/o/r/security/code-scanning/25"),
   437  			MostRecentInstance: &MostRecentInstance{
   438  				Ref:       Ptr("refs/heads/main"),
   439  				State:     Ptr("open"),
   440  				CommitSHA: Ptr("abcdefg12345"),
   441  				Message: &Message{
   442  					Text: Ptr("This path depends on a user-provided value."),
   443  				},
   444  				Location: &Location{
   445  					Path:        Ptr("spec-main/api-session-spec.ts"),
   446  					StartLine:   Ptr(917),
   447  					EndLine:     Ptr(917),
   448  					StartColumn: Ptr(7),
   449  					EndColumn:   Ptr(18),
   450  				},
   451  				Classifications: []string{"test"},
   452  			},
   453  		},
   454  	}
   455  	if !cmp.Equal(alerts, want) {
   456  		t.Errorf("CodeScanning.ListAlertsForOrg returned %+v, want %+v", alerts, want)
   457  	}
   458  
   459  	const methodName = "ListAlertsForOrg"
   460  	testBadOptions(t, methodName, func() (err error) {
   461  		_, _, err = client.CodeScanning.ListAlertsForOrg(ctx, "\n", opts)
   462  		return err
   463  	})
   464  
   465  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   466  		got, resp, err := client.CodeScanning.ListAlertsForOrg(ctx, "o", opts)
   467  		if got != nil {
   468  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   469  		}
   470  		return resp, err
   471  	})
   472  }
   473  
   474  func TestCodeScanningService_ListAlertsForRepo(t *testing.T) {
   475  	t.Parallel()
   476  	client, mux, _ := setup(t)
   477  
   478  	mux.HandleFunc("/repos/o/r/code-scanning/alerts", func(w http.ResponseWriter, r *http.Request) {
   479  		testMethod(t, r, "GET")
   480  		testFormValues(t, r, values{"state": "open", "ref": "heads/master", "severity": "warning", "tool_name": "CodeQL", "tool_guid": "guid", "direction": "asc", "sort": "updated"})
   481  		fmt.Fprint(w, `[{
   482  				"rule_id":"js/trivial-conditional",
   483  				"rule_severity":"warning",
   484  				"rule_description":"Useless conditional",
   485  				"tool": {
   486  					"name": "CodeQL",
   487  					"guid": "guid",
   488  					"version": "1.4.0"
   489  				},
   490  				"rule": {
   491  					"id": "js/trivial-conditional",
   492  					"severity": "warning",
   493  					"description": "Useless conditional",
   494  					"name": "js/trivial-conditional",
   495  					"full_description": "Expression has no effect",
   496  					"help": "Expression has no effect"
   497  				},
   498  				"most_recent_instance": {
   499  					"ref": "refs/heads/main",
   500  					"state": "open",
   501  					"commit_sha": "abcdefg12345",
   502  					"message": {
   503  						"text": "This path depends on a user-provided value."
   504  					},
   505  					"location": {
   506  						"path": "spec-main/api-session-spec.ts",
   507  						"start_line": 917,
   508  						"end_line": 917,
   509  						"start_column": 7,
   510  						"end_column": 18
   511  					},
   512  					"classifications": [
   513  						"test"
   514  					]
   515  				},
   516  				"created_at":"2020-05-06T12:00:00Z",
   517  				"state":"open",
   518  				"closed_by":null,
   519  				"closed_at":null,
   520  				"url":"https://api.github.com/repos/o/r/code-scanning/alerts/25",
   521  				"html_url":"https://github.com/o/r/security/code-scanning/25"
   522  				},
   523  				{
   524  				"rule_id":"js/useless-expression",
   525  				"rule_severity":"warning",
   526  				"rule_description":"Expression has no effect",
   527  				"tool": {
   528  					"name": "CodeQL",
   529  					"guid": "guid",
   530  					"version": "1.4.0"
   531  				},
   532  				"rule": {
   533  					"id": "js/useless-expression",
   534  					"severity": "warning",
   535  					"description": "Expression has no effect",
   536  					"name": "js/useless-expression",
   537  					"full_description": "Expression has no effect",
   538  					"help": "Expression has no effect"
   539  				},
   540  				"most_recent_instance": {
   541  					"ref": "refs/heads/main",
   542  					"state": "open",
   543  					"commit_sha": "abcdefg12345",
   544  					"message": {
   545  						"text": "This path depends on a user-provided value."
   546  					},
   547  					"location": {
   548  						"path": "spec-main/api-session-spec.ts",
   549  						"start_line": 917,
   550  						"end_line": 917,
   551  						"start_column": 7,
   552  						"end_column": 18
   553  					},
   554  					"classifications": [
   555  						"test"
   556  					]
   557  				},
   558  				"created_at":"2020-05-06T12:00:00Z",
   559  				"state":"open",
   560  				"closed_by":null,
   561  				"closed_at":null,
   562  				"url":"https://api.github.com/repos/o/r/code-scanning/alerts/88",
   563  				"html_url":"https://github.com/o/r/security/code-scanning/88"
   564  				}]`)
   565  	})
   566  
   567  	opts := &AlertListOptions{State: "open", Ref: "heads/master", Severity: "warning", ToolName: "CodeQL", ToolGUID: "guid", Direction: "asc", Sort: "updated"}
   568  	ctx := context.Background()
   569  	alerts, _, err := client.CodeScanning.ListAlertsForRepo(ctx, "o", "r", opts)
   570  	if err != nil {
   571  		t.Errorf("CodeScanning.ListAlertsForRepo returned error: %v", err)
   572  	}
   573  
   574  	date := Timestamp{time.Date(2020, time.May, 06, 12, 00, 00, 0, time.UTC)}
   575  	want := []*Alert{
   576  		{
   577  			RuleID:          Ptr("js/trivial-conditional"),
   578  			RuleSeverity:    Ptr("warning"),
   579  			RuleDescription: Ptr("Useless conditional"),
   580  			Tool:            &Tool{Name: Ptr("CodeQL"), GUID: Ptr("guid"), Version: Ptr("1.4.0")},
   581  			Rule: &Rule{
   582  				ID:              Ptr("js/trivial-conditional"),
   583  				Severity:        Ptr("warning"),
   584  				Description:     Ptr("Useless conditional"),
   585  				Name:            Ptr("js/trivial-conditional"),
   586  				FullDescription: Ptr("Expression has no effect"),
   587  				Help:            Ptr("Expression has no effect"),
   588  			},
   589  			CreatedAt: &date,
   590  			State:     Ptr("open"),
   591  			ClosedBy:  nil,
   592  			ClosedAt:  nil,
   593  			URL:       Ptr("https://api.github.com/repos/o/r/code-scanning/alerts/25"),
   594  			HTMLURL:   Ptr("https://github.com/o/r/security/code-scanning/25"),
   595  			MostRecentInstance: &MostRecentInstance{
   596  				Ref:       Ptr("refs/heads/main"),
   597  				State:     Ptr("open"),
   598  				CommitSHA: Ptr("abcdefg12345"),
   599  				Message: &Message{
   600  					Text: Ptr("This path depends on a user-provided value."),
   601  				},
   602  				Location: &Location{
   603  					Path:        Ptr("spec-main/api-session-spec.ts"),
   604  					StartLine:   Ptr(917),
   605  					EndLine:     Ptr(917),
   606  					StartColumn: Ptr(7),
   607  					EndColumn:   Ptr(18),
   608  				},
   609  				Classifications: []string{"test"},
   610  			},
   611  		},
   612  		{
   613  			RuleID:          Ptr("js/useless-expression"),
   614  			RuleSeverity:    Ptr("warning"),
   615  			RuleDescription: Ptr("Expression has no effect"),
   616  			Tool:            &Tool{Name: Ptr("CodeQL"), GUID: Ptr("guid"), Version: Ptr("1.4.0")},
   617  			Rule: &Rule{
   618  				ID:              Ptr("js/useless-expression"),
   619  				Severity:        Ptr("warning"),
   620  				Description:     Ptr("Expression has no effect"),
   621  				Name:            Ptr("js/useless-expression"),
   622  				FullDescription: Ptr("Expression has no effect"),
   623  				Help:            Ptr("Expression has no effect"),
   624  			},
   625  			CreatedAt: &date,
   626  			State:     Ptr("open"),
   627  			ClosedBy:  nil,
   628  			ClosedAt:  nil,
   629  			URL:       Ptr("https://api.github.com/repos/o/r/code-scanning/alerts/88"),
   630  			HTMLURL:   Ptr("https://github.com/o/r/security/code-scanning/88"),
   631  			MostRecentInstance: &MostRecentInstance{
   632  				Ref:       Ptr("refs/heads/main"),
   633  				State:     Ptr("open"),
   634  				CommitSHA: Ptr("abcdefg12345"),
   635  				Message: &Message{
   636  					Text: Ptr("This path depends on a user-provided value."),
   637  				},
   638  				Location: &Location{
   639  					Path:        Ptr("spec-main/api-session-spec.ts"),
   640  					StartLine:   Ptr(917),
   641  					EndLine:     Ptr(917),
   642  					StartColumn: Ptr(7),
   643  					EndColumn:   Ptr(18),
   644  				},
   645  				Classifications: []string{"test"},
   646  			},
   647  		},
   648  	}
   649  	if !cmp.Equal(alerts, want) {
   650  		t.Errorf("CodeScanning.ListAlertsForRepo returned %+v, want %+v", alerts, want)
   651  	}
   652  
   653  	const methodName = "ListAlertsForRepo"
   654  	testBadOptions(t, methodName, func() (err error) {
   655  		_, _, err = client.CodeScanning.ListAlertsForRepo(ctx, "\n", "\n", opts)
   656  		return err
   657  	})
   658  
   659  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   660  		got, resp, err := client.CodeScanning.ListAlertsForRepo(ctx, "o", "r", opts)
   661  		if got != nil {
   662  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   663  		}
   664  		return resp, err
   665  	})
   666  }
   667  
   668  func TestCodeScanningService_UpdateAlert(t *testing.T) {
   669  	t.Parallel()
   670  	client, mux, _ := setup(t)
   671  
   672  	mux.HandleFunc("/repos/o/r/code-scanning/alerts/88", func(w http.ResponseWriter, r *http.Request) {
   673  		testMethod(t, r, "PATCH")
   674  		fmt.Fprint(w, `{"rule_id":"js/useless-expression",
   675  				"rule_severity":"warning",
   676  				"rule_description":"Expression has no effect",
   677  				"tool": {
   678  					"name": "CodeQL",
   679  					"guid": null,
   680  					"version": "1.4.0"
   681  				},
   682  				"rule": {
   683  					"id": "useless expression",
   684  					"severity": "warning",
   685  					"description": "Expression has no effect",
   686  					"name": "useless expression",
   687  					"full_description": "Expression has no effect",
   688  					"help": "Expression has no effect"
   689  				},
   690  				"most_recent_instance": {
   691  					"ref": "refs/heads/main",
   692  					"state": "dismissed",
   693  					"commit_sha": "abcdefg12345",
   694  					"message": {
   695  						"text": "This path depends on a user-provided value."
   696  					},
   697  					"location": {
   698  						"path": "spec-main/api-session-spec.ts",
   699  						"start_line": 917,
   700  						"end_line": 917,
   701  						"start_column": 7,
   702  						"end_column": 18
   703  					},
   704  					"classifications": [
   705  						"test"
   706  					]
   707  				},
   708  				"created_at":"2019-01-02T15:04:05Z",
   709  				"state":"dismissed",
   710  				"dismissed_reason": "false positive",
   711  				"dismissed_comment": "This alert is not actually correct as sanitizer is used",
   712  				"closed_by":null,
   713  				"closed_at":null,
   714  				"url":"https://api.github.com/repos/o/r/code-scanning/alerts/88",
   715  				"html_url":"https://github.com/o/r/security/code-scanning/88"}`)
   716  	})
   717  
   718  	ctx := context.Background()
   719  	dismissedComment := Ptr("This alert is not actually correct as sanitizer is used")
   720  	dismissedReason := Ptr("false positive")
   721  	state := Ptr("dismissed")
   722  	stateInfo := &CodeScanningAlertState{State: *state, DismissedReason: dismissedReason, DismissedComment: dismissedComment}
   723  	alert, _, err := client.CodeScanning.UpdateAlert(ctx, "o", "r", 88, stateInfo)
   724  	if err != nil {
   725  		t.Errorf("CodeScanning.UpdateAlert returned error: %v", err)
   726  	}
   727  
   728  	date := Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}
   729  	want := &Alert{
   730  		RuleID:          Ptr("js/useless-expression"),
   731  		RuleSeverity:    Ptr("warning"),
   732  		RuleDescription: Ptr("Expression has no effect"),
   733  		Tool:            &Tool{Name: Ptr("CodeQL"), GUID: nil, Version: Ptr("1.4.0")},
   734  		Rule: &Rule{
   735  			ID:              Ptr("useless expression"),
   736  			Severity:        Ptr("warning"),
   737  			Description:     Ptr("Expression has no effect"),
   738  			Name:            Ptr("useless expression"),
   739  			FullDescription: Ptr("Expression has no effect"),
   740  			Help:            Ptr("Expression has no effect"),
   741  		},
   742  		CreatedAt:        &date,
   743  		State:            state,
   744  		DismissedReason:  dismissedReason,
   745  		DismissedComment: dismissedComment,
   746  		ClosedBy:         nil,
   747  		ClosedAt:         nil,
   748  		URL:              Ptr("https://api.github.com/repos/o/r/code-scanning/alerts/88"),
   749  		HTMLURL:          Ptr("https://github.com/o/r/security/code-scanning/88"),
   750  		MostRecentInstance: &MostRecentInstance{
   751  			Ref:       Ptr("refs/heads/main"),
   752  			State:     Ptr("dismissed"),
   753  			CommitSHA: Ptr("abcdefg12345"),
   754  			Message: &Message{
   755  				Text: Ptr("This path depends on a user-provided value."),
   756  			},
   757  			Location: &Location{
   758  				Path:        Ptr("spec-main/api-session-spec.ts"),
   759  				StartLine:   Ptr(917),
   760  				EndLine:     Ptr(917),
   761  				StartColumn: Ptr(7),
   762  				EndColumn:   Ptr(18),
   763  			},
   764  			Classifications: []string{"test"},
   765  		},
   766  	}
   767  	if !cmp.Equal(alert, want) {
   768  		t.Errorf("CodeScanning.UpdateAlert returned %+v, want %+v", alert, want)
   769  	}
   770  
   771  	const methodName = "UpdateAlert"
   772  	testBadOptions(t, methodName, func() (err error) {
   773  		_, _, err = client.CodeScanning.UpdateAlert(ctx, "\n", "\n", -88, stateInfo)
   774  		return err
   775  	})
   776  
   777  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   778  		got, resp, err := client.CodeScanning.UpdateAlert(ctx, "o", "r", 88, stateInfo)
   779  		if got != nil {
   780  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   781  		}
   782  		return resp, err
   783  	})
   784  }
   785  
   786  func TestCodeScanningService_ListAlertInstances(t *testing.T) {
   787  	t.Parallel()
   788  	client, mux, _ := setup(t)
   789  
   790  	mux.HandleFunc("/repos/o/r/code-scanning/alerts/88/instances", func(w http.ResponseWriter, r *http.Request) {
   791  		testMethod(t, r, "GET")
   792  		fmt.Fprint(w, `[
   793  			{
   794  			  "ref": "refs/heads/main",
   795  			  "analysis_key": ".github/workflows/codeql-analysis.yml:analyze",
   796  			  "environment": "",
   797  			  "category": ".github/workflows/codeql-analysis.yml:analyze",
   798  			  "state": "open",
   799  			  "fixed_at": null,
   800  			  "commit_sha": "abcdefg12345",
   801  			  "message": {
   802  				"text": "This path depends on a user-provided value."
   803  			  },
   804  			  "location": {
   805  				"path": "spec-main/api-session-spec.ts",
   806  				"start_line": 917,
   807  				"end_line": 917,
   808  				"start_column": 7,
   809  				"end_column": 18
   810  			  },
   811  			  "classifications": [
   812  				"test"
   813  			  ]
   814  			}
   815  		  ]`)
   816  	})
   817  
   818  	opts := &AlertInstancesListOptions{Ref: "heads/main", ListOptions: ListOptions{Page: 1}}
   819  	ctx := context.Background()
   820  	instances, _, err := client.CodeScanning.ListAlertInstances(ctx, "o", "r", 88, opts)
   821  	if err != nil {
   822  		t.Errorf("CodeScanning.ListAlertInstances returned error: %v", err)
   823  	}
   824  
   825  	want := []*MostRecentInstance{
   826  		{
   827  			Ref:         Ptr("refs/heads/main"),
   828  			AnalysisKey: Ptr(".github/workflows/codeql-analysis.yml:analyze"),
   829  			Category:    Ptr(".github/workflows/codeql-analysis.yml:analyze"),
   830  			Environment: Ptr(""),
   831  			State:       Ptr("open"),
   832  			CommitSHA:   Ptr("abcdefg12345"),
   833  			Message: &Message{
   834  				Text: Ptr("This path depends on a user-provided value."),
   835  			},
   836  			Location: &Location{
   837  				Path:        Ptr("spec-main/api-session-spec.ts"),
   838  				StartLine:   Ptr(917),
   839  				EndLine:     Ptr(917),
   840  				StartColumn: Ptr(7),
   841  				EndColumn:   Ptr(18),
   842  			},
   843  			Classifications: []string{"test"},
   844  		},
   845  	}
   846  	if !cmp.Equal(instances, want) {
   847  		t.Errorf("CodeScanning.ListAlertInstances returned %+v, want %+v", instances, want)
   848  	}
   849  
   850  	const methodName = "ListAlertInstances"
   851  	testBadOptions(t, methodName, func() (err error) {
   852  		_, _, err = client.CodeScanning.ListAlertInstances(ctx, "\n", "\n", -1, opts)
   853  		return err
   854  	})
   855  
   856  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   857  		got, resp, err := client.CodeScanning.ListAlertInstances(ctx, "o", "r", 88, opts)
   858  		if got != nil {
   859  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   860  		}
   861  		return resp, err
   862  	})
   863  }
   864  
   865  func TestCodeScanningService_GetAlert(t *testing.T) {
   866  	t.Parallel()
   867  	client, mux, _ := setup(t)
   868  
   869  	mux.HandleFunc("/repos/o/r/code-scanning/alerts/88", func(w http.ResponseWriter, r *http.Request) {
   870  		testMethod(t, r, "GET")
   871  		fmt.Fprint(w, `{
   872  			"rule_id":"js/useless-expression",
   873  			"rule_severity":"warning",
   874  			"rule_description":"Expression has no effect",
   875  			"tool": {
   876  				"name": "CodeQL",
   877  				"guid": null,
   878  				"version": "1.4.0"
   879  			},
   880  			"rule": {
   881  				"id": "useless expression",
   882  				"severity": "warning",
   883  				"description": "Expression has no effect",
   884  				"name": "useless expression",
   885  				"full_description": "Expression has no effect",
   886  				"help": "Expression has no effect"
   887  			},
   888  			"most_recent_instance": {
   889  				"ref": "refs/heads/main",
   890  				"state": "open",
   891  				"commit_sha": "abcdefg12345",
   892  				"message": {
   893  					"text": "This path depends on a user-provided value."
   894  				},
   895  				"location": {
   896  					"path": "spec-main/api-session-spec.ts",
   897  					"start_line": 917,
   898  					"end_line": 917,
   899  					"start_column": 7,
   900  					"end_column": 18
   901  				},
   902  				"classifications": [
   903  					"test"
   904  				]
   905  			},
   906  			"created_at":"2019-01-02T15:04:05Z",
   907  			"state":"open",
   908  			"closed_by":null,
   909  			"closed_at":null,
   910  			"url":"https://api.github.com/repos/o/r/code-scanning/alerts/88",
   911  			"html_url":"https://github.com/o/r/security/code-scanning/88"
   912  		}`)
   913  	})
   914  
   915  	ctx := context.Background()
   916  	alert, _, err := client.CodeScanning.GetAlert(ctx, "o", "r", 88)
   917  	if err != nil {
   918  		t.Errorf("CodeScanning.GetAlert returned error: %v", err)
   919  	}
   920  
   921  	date := Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}
   922  	want := &Alert{
   923  		RuleID:          Ptr("js/useless-expression"),
   924  		RuleSeverity:    Ptr("warning"),
   925  		RuleDescription: Ptr("Expression has no effect"),
   926  		Tool:            &Tool{Name: Ptr("CodeQL"), GUID: nil, Version: Ptr("1.4.0")},
   927  		Rule: &Rule{
   928  			ID:              Ptr("useless expression"),
   929  			Severity:        Ptr("warning"),
   930  			Description:     Ptr("Expression has no effect"),
   931  			Name:            Ptr("useless expression"),
   932  			FullDescription: Ptr("Expression has no effect"),
   933  			Help:            Ptr("Expression has no effect"),
   934  		},
   935  		CreatedAt: &date,
   936  		State:     Ptr("open"),
   937  		ClosedBy:  nil,
   938  		ClosedAt:  nil,
   939  		URL:       Ptr("https://api.github.com/repos/o/r/code-scanning/alerts/88"),
   940  		HTMLURL:   Ptr("https://github.com/o/r/security/code-scanning/88"),
   941  		MostRecentInstance: &MostRecentInstance{
   942  			Ref:       Ptr("refs/heads/main"),
   943  			State:     Ptr("open"),
   944  			CommitSHA: Ptr("abcdefg12345"),
   945  			Message: &Message{
   946  				Text: Ptr("This path depends on a user-provided value."),
   947  			},
   948  			Location: &Location{
   949  				Path:        Ptr("spec-main/api-session-spec.ts"),
   950  				StartLine:   Ptr(917),
   951  				EndLine:     Ptr(917),
   952  				StartColumn: Ptr(7),
   953  				EndColumn:   Ptr(18),
   954  			},
   955  			Classifications: []string{"test"},
   956  		},
   957  	}
   958  	if !cmp.Equal(alert, want) {
   959  		t.Errorf("CodeScanning.GetAlert returned %+v, want %+v", alert, want)
   960  	}
   961  
   962  	const methodName = "GetAlert"
   963  	testBadOptions(t, methodName, func() (err error) {
   964  		_, _, err = client.CodeScanning.GetAlert(ctx, "\n", "\n", -88)
   965  		return err
   966  	})
   967  
   968  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   969  		got, resp, err := client.CodeScanning.GetAlert(ctx, "o", "r", 88)
   970  		if got != nil {
   971  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   972  		}
   973  		return resp, err
   974  	})
   975  }
   976  
   977  func TestAlert_Marshal(t *testing.T) {
   978  	t.Parallel()
   979  	testJSONMarshal(t, &Alert{}, "{}")
   980  
   981  	u := &Alert{
   982  		RuleID:          Ptr("rid"),
   983  		RuleSeverity:    Ptr("rs"),
   984  		RuleDescription: Ptr("rd"),
   985  		Tool: &Tool{
   986  			Name:    Ptr("n"),
   987  			GUID:    Ptr("g"),
   988  			Version: Ptr("v"),
   989  		},
   990  		CreatedAt: &Timestamp{referenceTime},
   991  		State:     Ptr("fixed"),
   992  		ClosedBy: &User{
   993  			Login:     Ptr("l"),
   994  			ID:        Ptr(int64(1)),
   995  			NodeID:    Ptr("n"),
   996  			URL:       Ptr("u"),
   997  			ReposURL:  Ptr("r"),
   998  			EventsURL: Ptr("e"),
   999  			AvatarURL: Ptr("a"),
  1000  		},
  1001  		ClosedAt: &Timestamp{referenceTime},
  1002  		URL:      Ptr("url"),
  1003  		HTMLURL:  Ptr("hurl"),
  1004  	}
  1005  
  1006  	want := `{
  1007  		"rule_id": "rid",
  1008  		"rule_severity": "rs",
  1009  		"rule_description": "rd",
  1010  		"tool": {
  1011  			"name": "n",
  1012  			"guid": "g",
  1013  			"version": "v"
  1014  		},
  1015  		"created_at": ` + referenceTimeStr + `,
  1016  		"state": "fixed",
  1017  		"closed_by": {
  1018  			"login": "l",
  1019  			"id": 1,
  1020  			"node_id": "n",
  1021  			"avatar_url": "a",
  1022  			"url": "u",
  1023  			"events_url": "e",
  1024  			"repos_url": "r"
  1025  		},
  1026  		"closed_at": ` + referenceTimeStr + `,
  1027  		"url": "url",
  1028  		"html_url": "hurl"
  1029  	}`
  1030  
  1031  	testJSONMarshal(t, u, want)
  1032  }
  1033  
  1034  func TestLocation_Marshal(t *testing.T) {
  1035  	t.Parallel()
  1036  	testJSONMarshal(t, &Location{}, "{}")
  1037  
  1038  	u := &Location{
  1039  		Path:        Ptr("path"),
  1040  		StartLine:   Ptr(1),
  1041  		EndLine:     Ptr(2),
  1042  		StartColumn: Ptr(3),
  1043  		EndColumn:   Ptr(4),
  1044  	}
  1045  
  1046  	want := `{
  1047  		"path": "path",
  1048  		"start_line": 1,
  1049  		"end_line": 2,
  1050  		"start_column": 3,
  1051  		"end_column": 4
  1052  	}`
  1053  
  1054  	testJSONMarshal(t, u, want)
  1055  }
  1056  
  1057  func TestRule_Marshal(t *testing.T) {
  1058  	t.Parallel()
  1059  	testJSONMarshal(t, &Rule{}, "{}")
  1060  
  1061  	u := &Rule{
  1062  		ID:                    Ptr("1"),
  1063  		Severity:              Ptr("3"),
  1064  		Description:           Ptr("description"),
  1065  		Name:                  Ptr("first"),
  1066  		SecuritySeverityLevel: Ptr("2"),
  1067  		FullDescription:       Ptr("summary"),
  1068  		Tags:                  []string{"tag1", "tag2"},
  1069  		Help:                  Ptr("Help Text"),
  1070  	}
  1071  
  1072  	want := `{
  1073  		"id":                      "1",
  1074  		"severity":                "3",
  1075  		"description":             "description",
  1076  		"name":                    "first",
  1077  		"security_severity_level": "2",
  1078  		"full_description":        "summary",
  1079  		"tags":                    ["tag1", "tag2"],
  1080  		"help":                    "Help Text"
  1081  	}`
  1082  
  1083  	testJSONMarshal(t, u, want)
  1084  }
  1085  
  1086  func TestTool_Marshal(t *testing.T) {
  1087  	t.Parallel()
  1088  	testJSONMarshal(t, &Tool{}, "{}")
  1089  
  1090  	u := &Tool{
  1091  		Name:    Ptr("name"),
  1092  		GUID:    Ptr("guid"),
  1093  		Version: Ptr("ver"),
  1094  	}
  1095  
  1096  	want := `{
  1097  		"name": "name",
  1098  		"guid": "guid",
  1099  		"version": "ver"
  1100  	}`
  1101  
  1102  	testJSONMarshal(t, u, want)
  1103  }
  1104  
  1105  func TestMessage_Marshal(t *testing.T) {
  1106  	t.Parallel()
  1107  	testJSONMarshal(t, &Message{}, "{}")
  1108  
  1109  	u := &Message{
  1110  		Text: Ptr("text"),
  1111  	}
  1112  
  1113  	want := `{
  1114  		"text": "text"
  1115  	}`
  1116  
  1117  	testJSONMarshal(t, u, want)
  1118  }
  1119  
  1120  func TestCodeScanningService_ListAnalysesForRepo(t *testing.T) {
  1121  	t.Parallel()
  1122  	client, mux, _ := setup(t)
  1123  
  1124  	mux.HandleFunc("/repos/o/r/code-scanning/analyses", func(w http.ResponseWriter, r *http.Request) {
  1125  		testMethod(t, r, "GET")
  1126  		testFormValues(t, r, values{"sarif_id": "8981cd8e-b078-4ac3-a3be-1dad7dbd0b582", "ref": "heads/master"})
  1127  		fmt.Fprint(w, `[
  1128  			  {
  1129  				"ref": "refs/heads/main",
  1130  				"commit_sha": "d99612c3e1f2970085cfbaeadf8f010ef69bad83",
  1131  				"analysis_key": ".github/workflows/codeql-analysis.yml:analyze",
  1132  				"environment": "{\"language\":\"python\"}",
  1133  				"error": "",
  1134  				"category": ".github/workflows/codeql-analysis.yml:analyze/language:python",
  1135  				"created_at": "2020-08-27T15:05:21Z",
  1136  				"results_count": 17,
  1137  				"rules_count": 49,
  1138  				"id": 201,
  1139  				"url": "https://api.github.com/repos/o/r/code-scanning/analyses/201",
  1140  				"sarif_id": "8981cd8e-b078-4ac3-a3be-1dad7dbd0b582",
  1141  				"tool": {
  1142  				  "name": "CodeQL",
  1143  				  "guid": null,
  1144  				  "version": "2.4.0"
  1145  				},
  1146  				"deletable": true,
  1147  				"warning": ""
  1148  			  },
  1149  			  {
  1150  				"ref": "refs/heads/my-branch",
  1151  				"commit_sha": "c8cff6510d4d084fb1b4aa13b64b97ca12b07321",
  1152  				"analysis_key": ".github/workflows/shiftleft.yml:build",
  1153  				"environment": "{}",
  1154  				"error": "",
  1155  				"category": ".github/workflows/shiftleft.yml:build/",
  1156  				"created_at": "2020-08-27T15:05:21Z",
  1157  				"results_count": 17,
  1158  				"rules_count": 32,
  1159  				"id": 200,
  1160  				"url": "https://api.github.com/repos/o/r/code-scanning/analyses/200",
  1161  				"sarif_id": "8981cd8e-b078-4ac3-a3be-1dad7dbd0b582",
  1162  				"tool": {
  1163  				  "name": "Python Security ScanningAnalysis",
  1164  				  "guid": null,
  1165  				  "version": "1.2.0"
  1166  				},
  1167  				"deletable": true,
  1168  				"warning": ""
  1169  			  }
  1170  			]`)
  1171  	})
  1172  
  1173  	opts := &AnalysesListOptions{SarifID: Ptr("8981cd8e-b078-4ac3-a3be-1dad7dbd0b582"), Ref: Ptr("heads/master")}
  1174  	ctx := context.Background()
  1175  	analyses, _, err := client.CodeScanning.ListAnalysesForRepo(ctx, "o", "r", opts)
  1176  	if err != nil {
  1177  		t.Errorf("CodeScanning.ListAnalysesForRepo returned error: %v", err)
  1178  	}
  1179  
  1180  	date := &Timestamp{time.Date(2020, time.August, 27, 15, 05, 21, 0, time.UTC)}
  1181  	want := []*ScanningAnalysis{
  1182  		{
  1183  			ID:           Ptr(int64(201)),
  1184  			Ref:          Ptr("refs/heads/main"),
  1185  			CommitSHA:    Ptr("d99612c3e1f2970085cfbaeadf8f010ef69bad83"),
  1186  			AnalysisKey:  Ptr(".github/workflows/codeql-analysis.yml:analyze"),
  1187  			Environment:  Ptr("{\"language\":\"python\"}"),
  1188  			Error:        Ptr(""),
  1189  			Category:     Ptr(".github/workflows/codeql-analysis.yml:analyze/language:python"),
  1190  			CreatedAt:    date,
  1191  			ResultsCount: Ptr(17),
  1192  			RulesCount:   Ptr(49),
  1193  			URL:          Ptr("https://api.github.com/repos/o/r/code-scanning/analyses/201"),
  1194  			SarifID:      Ptr("8981cd8e-b078-4ac3-a3be-1dad7dbd0b582"),
  1195  			Tool: &Tool{
  1196  				Name:    Ptr("CodeQL"),
  1197  				GUID:    nil,
  1198  				Version: Ptr("2.4.0"),
  1199  			},
  1200  			Deletable: Ptr(true),
  1201  			Warning:   Ptr(""),
  1202  		},
  1203  		{
  1204  			ID:           Ptr(int64(200)),
  1205  			Ref:          Ptr("refs/heads/my-branch"),
  1206  			CommitSHA:    Ptr("c8cff6510d4d084fb1b4aa13b64b97ca12b07321"),
  1207  			AnalysisKey:  Ptr(".github/workflows/shiftleft.yml:build"),
  1208  			Environment:  Ptr("{}"),
  1209  			Error:        Ptr(""),
  1210  			Category:     Ptr(".github/workflows/shiftleft.yml:build/"),
  1211  			CreatedAt:    date,
  1212  			ResultsCount: Ptr(17),
  1213  			RulesCount:   Ptr(32),
  1214  			URL:          Ptr("https://api.github.com/repos/o/r/code-scanning/analyses/200"),
  1215  			SarifID:      Ptr("8981cd8e-b078-4ac3-a3be-1dad7dbd0b582"),
  1216  			Tool: &Tool{
  1217  				Name:    Ptr("Python Security ScanningAnalysis"),
  1218  				GUID:    nil,
  1219  				Version: Ptr("1.2.0"),
  1220  			},
  1221  			Deletable: Ptr(true),
  1222  			Warning:   Ptr(""),
  1223  		},
  1224  	}
  1225  	if !cmp.Equal(analyses, want) {
  1226  		t.Errorf("CodeScanning.ListAnalysesForRepo returned %+v, want %+v", analyses, want)
  1227  	}
  1228  
  1229  	const methodName = "ListAnalysesForRepo"
  1230  	testBadOptions(t, methodName, func() (err error) {
  1231  		_, _, err = client.CodeScanning.ListAnalysesForRepo(ctx, "\n", "\n", opts)
  1232  		return err
  1233  	})
  1234  
  1235  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
  1236  		got, resp, err := client.CodeScanning.ListAnalysesForRepo(ctx, "o", "r", opts)
  1237  		if got != nil {
  1238  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
  1239  		}
  1240  		return resp, err
  1241  	})
  1242  }
  1243  
  1244  func TestCodeScanningService_GetAnalysis(t *testing.T) {
  1245  	t.Parallel()
  1246  	client, mux, _ := setup(t)
  1247  
  1248  	mux.HandleFunc("/repos/o/r/code-scanning/analyses/3602840", func(w http.ResponseWriter, r *http.Request) {
  1249  		testMethod(t, r, "GET")
  1250  		fmt.Fprint(w, `{
  1251  			  "ref": "refs/heads/main",
  1252  			  "commit_sha": "c18c69115654ff0166991962832dc2bd7756e655",
  1253  			  "analysis_key": ".github/workflows/codeql-analysis.yml:analyze",
  1254  			  "environment": "{\"language\":\"javascript\"}",
  1255  			  "error": "",
  1256  			  "category": ".github/workflows/codeql-analysis.yml:analyze/language:javascript",
  1257  			  "created_at": "2021-01-13T11:55:49Z",
  1258  			  "results_count": 3,
  1259  			  "rules_count": 67,
  1260  			  "id": 3602840,
  1261  			  "url": "https://api.github.com/repos/o/r/code-scanning/analyses/201",
  1262  			  "sarif_id": "47177e22-5596-11eb-80a1-c1e54ef945c6",
  1263  			  "tool": {
  1264  				"name": "CodeQL",
  1265  				"guid": null,
  1266  				"version": "2.4.0"
  1267  			  },
  1268  			  "deletable": true,
  1269  			  "warning": ""
  1270  			}`)
  1271  	})
  1272  
  1273  	ctx := context.Background()
  1274  	analysis, _, err := client.CodeScanning.GetAnalysis(ctx, "o", "r", 3602840)
  1275  	if err != nil {
  1276  		t.Errorf("CodeScanning.GetAnalysis returned error: %v", err)
  1277  	}
  1278  
  1279  	date := &Timestamp{time.Date(2021, time.January, 13, 11, 55, 49, 0, time.UTC)}
  1280  	want := &ScanningAnalysis{
  1281  		ID:           Ptr(int64(3602840)),
  1282  		Ref:          Ptr("refs/heads/main"),
  1283  		CommitSHA:    Ptr("c18c69115654ff0166991962832dc2bd7756e655"),
  1284  		AnalysisKey:  Ptr(".github/workflows/codeql-analysis.yml:analyze"),
  1285  		Environment:  Ptr("{\"language\":\"javascript\"}"),
  1286  		Error:        Ptr(""),
  1287  		Category:     Ptr(".github/workflows/codeql-analysis.yml:analyze/language:javascript"),
  1288  		CreatedAt:    date,
  1289  		ResultsCount: Ptr(3),
  1290  		RulesCount:   Ptr(67),
  1291  		URL:          Ptr("https://api.github.com/repos/o/r/code-scanning/analyses/201"),
  1292  		SarifID:      Ptr("47177e22-5596-11eb-80a1-c1e54ef945c6"),
  1293  		Tool: &Tool{
  1294  			Name:    Ptr("CodeQL"),
  1295  			GUID:    nil,
  1296  			Version: Ptr("2.4.0"),
  1297  		},
  1298  		Deletable: Ptr(true),
  1299  		Warning:   Ptr(""),
  1300  	}
  1301  	if !cmp.Equal(analysis, want) {
  1302  		t.Errorf("CodeScanning.GetAnalysis returned %+v, want %+v", analysis, want)
  1303  	}
  1304  
  1305  	const methodName = "GetAnalysis"
  1306  	testBadOptions(t, methodName, func() (err error) {
  1307  		_, _, err = client.CodeScanning.GetAnalysis(ctx, "\n", "\n", -123)
  1308  		return err
  1309  	})
  1310  
  1311  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
  1312  		got, resp, err := client.CodeScanning.GetAnalysis(ctx, "o", "r", 3602840)
  1313  		if got != nil {
  1314  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
  1315  		}
  1316  		return resp, err
  1317  	})
  1318  }
  1319  
  1320  func TestCodeScanningService_DeleteAnalysis(t *testing.T) {
  1321  	t.Parallel()
  1322  	client, mux, _ := setup(t)
  1323  
  1324  	mux.HandleFunc("/repos/o/r/code-scanning/analyses/40", func(w http.ResponseWriter, r *http.Request) {
  1325  		testMethod(t, r, "DELETE")
  1326  		fmt.Fprint(w, `{
  1327  			"next_analysis_url": "a",
  1328  			"confirm_delete_url": "b"
  1329  		}`)
  1330  	})
  1331  
  1332  	ctx := context.Background()
  1333  	analysis, _, err := client.CodeScanning.DeleteAnalysis(ctx, "o", "r", 40)
  1334  	if err != nil {
  1335  		t.Errorf("CodeScanning.DeleteAnalysis returned error: %v", err)
  1336  	}
  1337  
  1338  	want := &DeleteAnalysis{
  1339  		NextAnalysisURL:  Ptr("a"),
  1340  		ConfirmDeleteURL: Ptr("b"),
  1341  	}
  1342  	if !cmp.Equal(analysis, want) {
  1343  		t.Errorf("CodeScanning.DeleteAnalysis returned %+v, want %+v", analysis, want)
  1344  	}
  1345  
  1346  	const methodName = "DeleteAnalysis"
  1347  	testBadOptions(t, methodName, func() (err error) {
  1348  		_, _, err = client.CodeScanning.DeleteAnalysis(ctx, "\n", "\n", -123)
  1349  		return err
  1350  	})
  1351  
  1352  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
  1353  		got, resp, err := client.CodeScanning.DeleteAnalysis(ctx, "o", "r", 40)
  1354  		if got != nil {
  1355  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
  1356  		}
  1357  		return resp, err
  1358  	})
  1359  }
  1360  
  1361  func TestCodeScanningService_ListCodeQLDatabases(t *testing.T) {
  1362  	t.Parallel()
  1363  	client, mux, _ := setup(t)
  1364  
  1365  	mux.HandleFunc("/repos/o/r/code-scanning/codeql/databases", func(w http.ResponseWriter, r *http.Request) {
  1366  		testMethod(t, r, "GET")
  1367  		fmt.Fprint(w, `[
  1368  			{
  1369  				"id": 1,
  1370  				"name": "name",
  1371  				"language": "language",
  1372  				"uploader": {
  1373  					"login": "a",
  1374  					"id": 1,
  1375  					"node_id": "b",
  1376  					"avatar_url": "c",
  1377  					"gravatar_id": "d",
  1378  					"url": "e",
  1379  					"html_url": "f",
  1380  					"followers_url": "g",
  1381  					"following_url": "h",
  1382  					"gists_url": "i",
  1383  					"starred_url": "j",
  1384  					"subscriptions_url": "k",
  1385  					"organizations_url": "l",
  1386  					"repos_url": "m",
  1387  					"events_url": "n",
  1388  					"received_events_url": "o",
  1389  					"type": "p",
  1390  					"site_admin": false
  1391  				},
  1392  				"content_type": "r",
  1393  				"size": 1024,
  1394  				"created_at": "2021-01-13T11:55:49Z",
  1395  				"updated_at": "2021-01-13T11:55:49Z",
  1396  				"url": "s"
  1397  			}
  1398  		]`)
  1399  	})
  1400  
  1401  	ctx := context.Background()
  1402  	databases, _, err := client.CodeScanning.ListCodeQLDatabases(ctx, "o", "r")
  1403  	if err != nil {
  1404  		t.Errorf("CodeScanning.ListCodeQLDatabases returned error: %v", err)
  1405  	}
  1406  
  1407  	date := &Timestamp{time.Date(2021, time.January, 13, 11, 55, 49, 0, time.UTC)}
  1408  	want := []*CodeQLDatabase{
  1409  		{
  1410  			ID:       Ptr(int64(1)),
  1411  			Name:     Ptr("name"),
  1412  			Language: Ptr("language"),
  1413  			Uploader: &User{
  1414  				Login:             Ptr("a"),
  1415  				ID:                Ptr(int64(1)),
  1416  				NodeID:            Ptr("b"),
  1417  				AvatarURL:         Ptr("c"),
  1418  				GravatarID:        Ptr("d"),
  1419  				URL:               Ptr("e"),
  1420  				HTMLURL:           Ptr("f"),
  1421  				FollowersURL:      Ptr("g"),
  1422  				FollowingURL:      Ptr("h"),
  1423  				GistsURL:          Ptr("i"),
  1424  				StarredURL:        Ptr("j"),
  1425  				SubscriptionsURL:  Ptr("k"),
  1426  				OrganizationsURL:  Ptr("l"),
  1427  				ReposURL:          Ptr("m"),
  1428  				EventsURL:         Ptr("n"),
  1429  				ReceivedEventsURL: Ptr("o"),
  1430  				Type:              Ptr("p"),
  1431  				SiteAdmin:         Ptr(false),
  1432  			},
  1433  			ContentType: Ptr("r"),
  1434  			Size:        Ptr(int64(1024)),
  1435  			CreatedAt:   date,
  1436  			UpdatedAt:   date,
  1437  			URL:         Ptr("s"),
  1438  		},
  1439  	}
  1440  
  1441  	if !cmp.Equal(databases, want) {
  1442  		t.Errorf("CodeScanning.ListCodeQLDatabases returned %+v, want %+v", databases, want)
  1443  	}
  1444  
  1445  	const methodName = "ListCodeQLDatabases"
  1446  	testBadOptions(t, methodName, func() (err error) {
  1447  		_, _, err = client.CodeScanning.ListCodeQLDatabases(ctx, "\n", "\n")
  1448  		return err
  1449  	})
  1450  
  1451  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
  1452  		got, resp, err := client.CodeScanning.ListCodeQLDatabases(ctx, "o", "r")
  1453  		if got != nil {
  1454  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
  1455  		}
  1456  		return resp, err
  1457  	})
  1458  }
  1459  
  1460  func TestCodeScanningService_GetCodeQLDatabase(t *testing.T) {
  1461  	t.Parallel()
  1462  	client, mux, _ := setup(t)
  1463  
  1464  	mux.HandleFunc("/repos/o/r/code-scanning/codeql/databases/lang", func(w http.ResponseWriter, r *http.Request) {
  1465  		testMethod(t, r, "GET")
  1466  		fmt.Fprint(w, `{
  1467  			"id": 1,
  1468  			"name": "name",
  1469  			"language": "language",
  1470  			"uploader": {
  1471  				"login": "a",
  1472  				"id": 1,
  1473  				"node_id": "b",
  1474  				"avatar_url": "c",
  1475  				"gravatar_id": "d",
  1476  				"url": "e",
  1477  				"html_url": "f",
  1478  				"followers_url": "g",
  1479  				"following_url": "h",
  1480  				"gists_url": "i",
  1481  				"starred_url": "j",
  1482  				"subscriptions_url": "k",
  1483  				"organizations_url": "l",
  1484  				"repos_url": "m",
  1485  				"events_url": "n",
  1486  				"received_events_url": "o",
  1487  				"type": "p",
  1488  				"site_admin": false
  1489  			},
  1490  			"content_type": "r",
  1491  			"size": 1024,
  1492  			"created_at": "2021-01-13T11:55:49Z",
  1493  			"updated_at": "2021-01-13T11:55:49Z",
  1494  			"url": "s"
  1495  		}`)
  1496  	})
  1497  
  1498  	ctx := context.Background()
  1499  	database, _, err := client.CodeScanning.GetCodeQLDatabase(ctx, "o", "r", "lang")
  1500  	if err != nil {
  1501  		t.Errorf("CodeScanning.GetCodeQLDatabase returned error: %v", err)
  1502  	}
  1503  
  1504  	date := &Timestamp{time.Date(2021, time.January, 13, 11, 55, 49, 0, time.UTC)}
  1505  	want := &CodeQLDatabase{
  1506  		ID:       Ptr(int64(1)),
  1507  		Name:     Ptr("name"),
  1508  		Language: Ptr("language"),
  1509  		Uploader: &User{
  1510  			Login:             Ptr("a"),
  1511  			ID:                Ptr(int64(1)),
  1512  			NodeID:            Ptr("b"),
  1513  			AvatarURL:         Ptr("c"),
  1514  			GravatarID:        Ptr("d"),
  1515  			URL:               Ptr("e"),
  1516  			HTMLURL:           Ptr("f"),
  1517  			FollowersURL:      Ptr("g"),
  1518  			FollowingURL:      Ptr("h"),
  1519  			GistsURL:          Ptr("i"),
  1520  			StarredURL:        Ptr("j"),
  1521  			SubscriptionsURL:  Ptr("k"),
  1522  			OrganizationsURL:  Ptr("l"),
  1523  			ReposURL:          Ptr("m"),
  1524  			EventsURL:         Ptr("n"),
  1525  			ReceivedEventsURL: Ptr("o"),
  1526  			Type:              Ptr("p"),
  1527  			SiteAdmin:         Ptr(false),
  1528  		},
  1529  		ContentType: Ptr("r"),
  1530  		Size:        Ptr(int64(1024)),
  1531  		CreatedAt:   date,
  1532  		UpdatedAt:   date,
  1533  		URL:         Ptr("s"),
  1534  	}
  1535  
  1536  	if !cmp.Equal(database, want) {
  1537  		t.Errorf("CodeScanning.GetCodeQLDatabase returned %+v, want %+v", database, want)
  1538  	}
  1539  
  1540  	const methodName = "GetCodeQLDatabase"
  1541  	testBadOptions(t, methodName, func() (err error) {
  1542  		_, _, err = client.CodeScanning.GetCodeQLDatabase(ctx, "\n", "\n", "\n")
  1543  		return err
  1544  	})
  1545  
  1546  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
  1547  		got, resp, err := client.CodeScanning.GetCodeQLDatabase(ctx, "o", "r", "lang")
  1548  		if got != nil {
  1549  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
  1550  		}
  1551  		return resp, err
  1552  	})
  1553  }
  1554  
  1555  func TestCodeScanningService_GetDefaultSetupConfiguration(t *testing.T) {
  1556  	t.Parallel()
  1557  	client, mux, _ := setup(t)
  1558  
  1559  	mux.HandleFunc("/repos/o/r/code-scanning/default-setup", func(w http.ResponseWriter, r *http.Request) {
  1560  		testMethod(t, r, "GET")
  1561  		_, err := fmt.Fprint(w, `{
  1562  		"state": "configured",
  1563  		"languages": [
  1564  			"javascript",
  1565  			"javascript-typescript",
  1566  			"typescript"
  1567  		],
  1568  		"query_suite": "default",
  1569  		"updated_at": "2006-01-02T15:04:05Z"
  1570  		}`)
  1571  		if err != nil {
  1572  			t.Fatal(err)
  1573  		}
  1574  	})
  1575  
  1576  	ctx := context.Background()
  1577  	cfg, _, err := client.CodeScanning.GetDefaultSetupConfiguration(ctx, "o", "r")
  1578  	if err != nil {
  1579  		t.Errorf("CodeScanning.GetDefaultSetupConfiguration returned error: %v", err)
  1580  	}
  1581  
  1582  	date := &Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)}
  1583  	want := &DefaultSetupConfiguration{
  1584  		State:      Ptr("configured"),
  1585  		Languages:  []string{"javascript", "javascript-typescript", "typescript"},
  1586  		QuerySuite: Ptr("default"),
  1587  		UpdatedAt:  date,
  1588  	}
  1589  	if !cmp.Equal(cfg, want) {
  1590  		t.Errorf("CodeScanning.GetDefaultSetupConfiguration returned %+v, want %+v", cfg, want)
  1591  	}
  1592  
  1593  	const methodName = "GetDefaultSetupConfiguration"
  1594  	testBadOptions(t, methodName, func() (err error) {
  1595  		_, _, err = client.CodeScanning.GetDefaultSetupConfiguration(ctx, "\n", "\n")
  1596  		return err
  1597  	})
  1598  
  1599  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
  1600  		got, resp, err := client.CodeScanning.GetDefaultSetupConfiguration(ctx, "o", "r")
  1601  		if got != nil {
  1602  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
  1603  		}
  1604  		return resp, err
  1605  	})
  1606  }
  1607  
  1608  func TestCodeScanningService_UpdateDefaultSetupConfiguration(t *testing.T) {
  1609  	t.Parallel()
  1610  	client, mux, _ := setup(t)
  1611  
  1612  	mux.HandleFunc("/repos/o/r/code-scanning/default-setup", func(w http.ResponseWriter, r *http.Request) {
  1613  		testMethod(t, r, "PATCH")
  1614  		_, err := fmt.Fprint(w, `{
  1615  		"run_id": 5301214200,
  1616  		"run_url": "https://api.github.com/repos/o/r/actions/runs/5301214200"
  1617  		}`)
  1618  		if err != nil {
  1619  			t.Fatal(err)
  1620  		}
  1621  	})
  1622  
  1623  	ctx := context.Background()
  1624  	options := &UpdateDefaultSetupConfigurationOptions{
  1625  		State:      "configured",
  1626  		Languages:  []string{"go"},
  1627  		QuerySuite: Ptr("default"),
  1628  	}
  1629  	got, _, err := client.CodeScanning.UpdateDefaultSetupConfiguration(ctx, "o", "r", options)
  1630  	if err != nil {
  1631  		t.Errorf("CodeScanning.UpdateDefaultSetupConfiguration returned error: %v", err)
  1632  	}
  1633  
  1634  	want := &UpdateDefaultSetupConfigurationResponse{
  1635  		RunID:  Ptr(int64(5301214200)),
  1636  		RunURL: Ptr("https://api.github.com/repos/o/r/actions/runs/5301214200"),
  1637  	}
  1638  	if !cmp.Equal(got, want) {
  1639  		t.Errorf("CodeScanning.UpdateDefaultSetupConfiguration returned %+v, want %+v", got, want)
  1640  	}
  1641  
  1642  	const methodName = "UpdateDefaultSetupConfiguration"
  1643  	testBadOptions(t, methodName, func() (err error) {
  1644  		_, _, err = client.CodeScanning.UpdateDefaultSetupConfiguration(ctx, "\n", "\n", nil)
  1645  		return err
  1646  	})
  1647  
  1648  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
  1649  		got, resp, err := client.CodeScanning.UpdateDefaultSetupConfiguration(ctx, "o", "r", nil)
  1650  		if got != nil {
  1651  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
  1652  		}
  1653  		return resp, err
  1654  	})
  1655  }