// Copyright 2020 The go-github AUTHORS. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package github import ( "context" "encoding/json" "fmt" "net/http" "testing" "time" "github.com/google/go-cmp/cmp" ) func TestCodeScanningService_Alert_ID(t *testing.T) { // Test: nil Alert ID == 0 var a *Alert id := a.ID() var want int64 if id != want { t.Errorf("Alert.ID error returned %+v, want %+v", id, want) } // Test: Valid HTMLURL a = &Alert{ HTMLURL: String("https://github.com/o/r/security/code-scanning/88"), } id = a.ID() want = 88 if !cmp.Equal(id, want) { t.Errorf("Alert.ID error returned %+v, want %+v", id, want) } // Test: HTMLURL is nil a = &Alert{} id = a.ID() want = 0 if !cmp.Equal(id, want) { t.Errorf("Alert.ID error returned %+v, want %+v", id, want) } // Test: ID can't be parsed as an int a = &Alert{ HTMLURL: String("https://github.com/o/r/security/code-scanning/bad88"), } id = a.ID() want = 0 if !cmp.Equal(id, want) { t.Errorf("Alert.ID error returned %+v, want %+v", id, want) } } func TestCodeScanningService_UploadSarif(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/repos/o/r/code-scanning/sarifs", func(w http.ResponseWriter, r *http.Request) { v := new(SarifAnalysis) json.NewDecoder(r.Body).Decode(v) testMethod(t, r, "POST") want := &SarifAnalysis{CommitSHA: String("abc"), Ref: String("ref/head/main"), Sarif: String("abc"), CheckoutURI: String("uri"), StartedAt: &Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)}, ToolName: String("codeql-cli")} if !cmp.Equal(v, want) { t.Errorf("Request body = %+v, want %+v", v, want) } fmt.Fprint(w, `{"commit_sha":"abc","ref":"ref/head/main","sarif":"abc"}`) }) ctx := context.Background() sarifAnalysis := &SarifAnalysis{CommitSHA: String("abc"), Ref: String("ref/head/main"), Sarif: String("abc"), CheckoutURI: String("uri"), StartedAt: &Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)}, ToolName: String("codeql-cli")} _, _, err := client.CodeScanning.UploadSarif(ctx, "o", "r", sarifAnalysis) if err != nil { t.Errorf("CodeScanning.UploadSarif returned error: %v", err) } const methodName = "UploadSarif" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.UploadSarif(ctx, "\n", "\n", sarifAnalysis) return err }) testNewRequestAndDoFailureCategory(t, methodName, client, codeScanningUploadCategory, func() (*Response, error) { _, resp, err := client.CodeScanning.UploadSarif(ctx, "o", "r", sarifAnalysis) return resp, err }) } func TestCodeScanningService_GetSARIF(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/repos/o/r/code-scanning/sarifs/abc", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{ "processing_status": "s", "analyses_url": "u" }`) }) ctx := context.Background() sarifUpload, _, err := client.CodeScanning.GetSARIF(ctx, "o", "r", "abc") if err != nil { t.Errorf("CodeScanning.GetSARIF returned error: %v", err) } want := &SARIFUpload{ ProcessingStatus: String("s"), AnalysesURL: String("u"), } if !cmp.Equal(sarifUpload, want) { t.Errorf("CodeScanning.GetSARIF returned %+v, want %+v", sarifUpload, want) } const methodName = "GetSARIF" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.GetSARIF(ctx, "\n", "\n", "\n") return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { got, resp, err := client.CodeScanning.GetSARIF(ctx, "o", "r", "abc") if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } return resp, err }) } func TestCodeScanningService_ListAlertsForOrg(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/orgs/o/code-scanning/alerts", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testFormValues(t, r, values{"state": "open", "ref": "heads/master", "severity": "warning", "tool_name": "CodeQL"}) fmt.Fprint(w, `[{ "repository": { "id": 1, "name": "n", "url": "url" }, "rule_id":"js/trivial-conditional", "rule_severity":"warning", "rule_description":"Useless conditional", "tool": { "name": "CodeQL", "guid": null, "version": "1.4.0" }, "rule": { "id": "js/trivial-conditional", "severity": "warning", "description": "Useless conditional", "name": "js/trivial-conditional", "full_description": "Expression has no effect", "help": "Expression has no effect" }, "most_recent_instance": { "ref": "refs/heads/main", "state": "open", "commit_sha": "abcdefg12345", "message": { "text": "This path depends on a user-provided value." }, "location": { "path": "spec-main/api-session-spec.ts", "start_line": 917, "end_line": 917, "start_column": 7, "end_column": 18 }, "classifications": [ "test" ] }, "created_at":"2020-05-06T12:00:00Z", "state":"open", "closed_by":null, "closed_at":null, "url":"https://api.github.com/repos/o/r/code-scanning/alerts/25", "html_url":"https://github.com/o/r/security/code-scanning/25" }, { "rule_id":"js/useless-expression", "rule_severity":"warning", "rule_description":"Expression has no effect", "tool": { "name": "CodeQL", "guid": null, "version": "1.4.0" }, "rule": { "id": "js/useless-expression", "severity": "warning", "description": "Expression has no effect", "name": "js/useless-expression", "full_description": "Expression has no effect", "help": "Expression has no effect" }, "most_recent_instance": { "ref": "refs/heads/main", "state": "open", "commit_sha": "abcdefg12345", "message": { "text": "This path depends on a user-provided value." }, "location": { "path": "spec-main/api-session-spec.ts", "start_line": 917, "end_line": 917, "start_column": 7, "end_column": 18 }, "classifications": [ "test" ] }, "created_at":"2020-05-06T12:00:00Z", "state":"open", "closed_by":null, "closed_at":null, "url":"https://api.github.com/repos/o/r/code-scanning/alerts/88", "html_url":"https://github.com/o/r/security/code-scanning/88" }]`) }) opts := &AlertListOptions{State: "open", Ref: "heads/master", Severity: "warning", ToolName: "CodeQL"} ctx := context.Background() alerts, _, err := client.CodeScanning.ListAlertsForOrg(ctx, "o", opts) if err != nil { t.Errorf("CodeScanning.ListAlertsForOrg returned error: %v", err) } date := Timestamp{time.Date(2020, time.May, 06, 12, 00, 00, 0, time.UTC)} want := []*Alert{ { Repository: &Repository{ ID: Int64(1), URL: String("url"), Name: String("n"), }, RuleID: String("js/trivial-conditional"), RuleSeverity: String("warning"), RuleDescription: String("Useless conditional"), Tool: &Tool{Name: String("CodeQL"), GUID: nil, Version: String("1.4.0")}, Rule: &Rule{ ID: String("js/trivial-conditional"), Severity: String("warning"), Description: String("Useless conditional"), Name: String("js/trivial-conditional"), FullDescription: String("Expression has no effect"), Help: String("Expression has no effect"), }, CreatedAt: &date, State: String("open"), ClosedBy: nil, ClosedAt: nil, URL: String("https://api.github.com/repos/o/r/code-scanning/alerts/25"), HTMLURL: String("https://github.com/o/r/security/code-scanning/25"), MostRecentInstance: &MostRecentInstance{ Ref: String("refs/heads/main"), State: String("open"), CommitSHA: String("abcdefg12345"), Message: &Message{ Text: String("This path depends on a user-provided value."), }, Location: &Location{ Path: String("spec-main/api-session-spec.ts"), StartLine: Int(917), EndLine: Int(917), StartColumn: Int(7), EndColumn: Int(18), }, Classifications: []string{"test"}, }, }, { RuleID: String("js/useless-expression"), RuleSeverity: String("warning"), RuleDescription: String("Expression has no effect"), Tool: &Tool{Name: String("CodeQL"), GUID: nil, Version: String("1.4.0")}, Rule: &Rule{ ID: String("js/useless-expression"), Severity: String("warning"), Description: String("Expression has no effect"), Name: String("js/useless-expression"), FullDescription: String("Expression has no effect"), Help: String("Expression has no effect"), }, CreatedAt: &date, State: String("open"), ClosedBy: nil, ClosedAt: nil, URL: String("https://api.github.com/repos/o/r/code-scanning/alerts/88"), HTMLURL: String("https://github.com/o/r/security/code-scanning/88"), MostRecentInstance: &MostRecentInstance{ Ref: String("refs/heads/main"), State: String("open"), CommitSHA: String("abcdefg12345"), Message: &Message{ Text: String("This path depends on a user-provided value."), }, Location: &Location{ Path: String("spec-main/api-session-spec.ts"), StartLine: Int(917), EndLine: Int(917), StartColumn: Int(7), EndColumn: Int(18), }, Classifications: []string{"test"}, }, }, } if !cmp.Equal(alerts, want) { t.Errorf("CodeScanning.ListAlertsForOrg returned %+v, want %+v", *&alerts, *&want) } const methodName = "ListAlertsForOrg" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.ListAlertsForOrg(ctx, "\n", opts) return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { got, resp, err := client.CodeScanning.ListAlertsForOrg(ctx, "o", opts) if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } return resp, err }) } func TestCodeScanningService_ListAlertsForOrgLisCursorOptions(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/orgs/o/code-scanning/alerts", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testFormValues(t, r, values{"state": "open", "ref": "heads/master", "severity": "warning", "tool_name": "CodeQL", "per_page": "1", "before": "deadbeefb", "after": "deadbeefa"}) fmt.Fprint(w, `[{ "repository": { "id": 1, "name": "n", "url": "url" }, "rule_id":"js/trivial-conditional", "rule_severity":"warning", "rule_description":"Useless conditional", "tool": { "name": "CodeQL", "guid": null, "version": "1.4.0" }, "rule": { "id": "js/trivial-conditional", "severity": "warning", "description": "Useless conditional", "name": "js/trivial-conditional", "full_description": "Expression has no effect", "help": "Expression has no effect" }, "most_recent_instance": { "ref": "refs/heads/main", "state": "open", "commit_sha": "abcdefg12345", "message": { "text": "This path depends on a user-provided value." }, "location": { "path": "spec-main/api-session-spec.ts", "start_line": 917, "end_line": 917, "start_column": 7, "end_column": 18 }, "classifications": [ "test" ] }, "created_at":"2020-05-06T12:00:00Z", "state":"open", "closed_by":null, "closed_at":null, "url":"https://api.github.com/repos/o/r/code-scanning/alerts/25", "html_url":"https://github.com/o/r/security/code-scanning/25" }]`) }) opts := &AlertListOptions{State: "open", Ref: "heads/master", Severity: "warning", ToolName: "CodeQL", ListCursorOptions: ListCursorOptions{PerPage: 1, Before: "deadbeefb", After: "deadbeefa"}} ctx := context.Background() alerts, _, err := client.CodeScanning.ListAlertsForOrg(ctx, "o", opts) if err != nil { t.Errorf("CodeScanning.ListAlertsForOrg returned error: %v", err) } date := Timestamp{time.Date(2020, time.May, 06, 12, 00, 00, 0, time.UTC)} want := []*Alert{ { Repository: &Repository{ ID: Int64(1), URL: String("url"), Name: String("n"), }, RuleID: String("js/trivial-conditional"), RuleSeverity: String("warning"), RuleDescription: String("Useless conditional"), Tool: &Tool{Name: String("CodeQL"), GUID: nil, Version: String("1.4.0")}, Rule: &Rule{ ID: String("js/trivial-conditional"), Severity: String("warning"), Description: String("Useless conditional"), Name: String("js/trivial-conditional"), FullDescription: String("Expression has no effect"), Help: String("Expression has no effect"), }, CreatedAt: &date, State: String("open"), ClosedBy: nil, ClosedAt: nil, URL: String("https://api.github.com/repos/o/r/code-scanning/alerts/25"), HTMLURL: String("https://github.com/o/r/security/code-scanning/25"), MostRecentInstance: &MostRecentInstance{ Ref: String("refs/heads/main"), State: String("open"), CommitSHA: String("abcdefg12345"), Message: &Message{ Text: String("This path depends on a user-provided value."), }, Location: &Location{ Path: String("spec-main/api-session-spec.ts"), StartLine: Int(917), EndLine: Int(917), StartColumn: Int(7), EndColumn: Int(18), }, Classifications: []string{"test"}, }, }, } if !cmp.Equal(alerts, want) { t.Errorf("CodeScanning.ListAlertsForOrg returned %+v, want %+v", *&alerts, *&want) } const methodName = "ListAlertsForOrg" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.ListAlertsForOrg(ctx, "\n", opts) return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { got, resp, err := client.CodeScanning.ListAlertsForOrg(ctx, "o", opts) if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } return resp, err }) } func TestCodeScanningService_ListAlertsForRepo(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/repos/o/r/code-scanning/alerts", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testFormValues(t, r, values{"state": "open", "ref": "heads/master", "severity": "warning", "tool_name": "CodeQL"}) fmt.Fprint(w, `[{ "rule_id":"js/trivial-conditional", "rule_severity":"warning", "rule_description":"Useless conditional", "tool": { "name": "CodeQL", "guid": null, "version": "1.4.0" }, "rule": { "id": "js/trivial-conditional", "severity": "warning", "description": "Useless conditional", "name": "js/trivial-conditional", "full_description": "Expression has no effect", "help": "Expression has no effect" }, "most_recent_instance": { "ref": "refs/heads/main", "state": "open", "commit_sha": "abcdefg12345", "message": { "text": "This path depends on a user-provided value." }, "location": { "path": "spec-main/api-session-spec.ts", "start_line": 917, "end_line": 917, "start_column": 7, "end_column": 18 }, "classifications": [ "test" ] }, "created_at":"2020-05-06T12:00:00Z", "state":"open", "closed_by":null, "closed_at":null, "url":"https://api.github.com/repos/o/r/code-scanning/alerts/25", "html_url":"https://github.com/o/r/security/code-scanning/25" }, { "rule_id":"js/useless-expression", "rule_severity":"warning", "rule_description":"Expression has no effect", "tool": { "name": "CodeQL", "guid": null, "version": "1.4.0" }, "rule": { "id": "js/useless-expression", "severity": "warning", "description": "Expression has no effect", "name": "js/useless-expression", "full_description": "Expression has no effect", "help": "Expression has no effect" }, "most_recent_instance": { "ref": "refs/heads/main", "state": "open", "commit_sha": "abcdefg12345", "message": { "text": "This path depends on a user-provided value." }, "location": { "path": "spec-main/api-session-spec.ts", "start_line": 917, "end_line": 917, "start_column": 7, "end_column": 18 }, "classifications": [ "test" ] }, "created_at":"2020-05-06T12:00:00Z", "state":"open", "closed_by":null, "closed_at":null, "url":"https://api.github.com/repos/o/r/code-scanning/alerts/88", "html_url":"https://github.com/o/r/security/code-scanning/88" }]`) }) opts := &AlertListOptions{State: "open", Ref: "heads/master", Severity: "warning", ToolName: "CodeQL"} ctx := context.Background() alerts, _, err := client.CodeScanning.ListAlertsForRepo(ctx, "o", "r", opts) if err != nil { t.Errorf("CodeScanning.ListAlertsForRepo returned error: %v", err) } date := Timestamp{time.Date(2020, time.May, 06, 12, 00, 00, 0, time.UTC)} want := []*Alert{ { RuleID: String("js/trivial-conditional"), RuleSeverity: String("warning"), RuleDescription: String("Useless conditional"), Tool: &Tool{Name: String("CodeQL"), GUID: nil, Version: String("1.4.0")}, Rule: &Rule{ ID: String("js/trivial-conditional"), Severity: String("warning"), Description: String("Useless conditional"), Name: String("js/trivial-conditional"), FullDescription: String("Expression has no effect"), Help: String("Expression has no effect"), }, CreatedAt: &date, State: String("open"), ClosedBy: nil, ClosedAt: nil, URL: String("https://api.github.com/repos/o/r/code-scanning/alerts/25"), HTMLURL: String("https://github.com/o/r/security/code-scanning/25"), MostRecentInstance: &MostRecentInstance{ Ref: String("refs/heads/main"), State: String("open"), CommitSHA: String("abcdefg12345"), Message: &Message{ Text: String("This path depends on a user-provided value."), }, Location: &Location{ Path: String("spec-main/api-session-spec.ts"), StartLine: Int(917), EndLine: Int(917), StartColumn: Int(7), EndColumn: Int(18), }, Classifications: []string{"test"}, }, }, { RuleID: String("js/useless-expression"), RuleSeverity: String("warning"), RuleDescription: String("Expression has no effect"), Tool: &Tool{Name: String("CodeQL"), GUID: nil, Version: String("1.4.0")}, Rule: &Rule{ ID: String("js/useless-expression"), Severity: String("warning"), Description: String("Expression has no effect"), Name: String("js/useless-expression"), FullDescription: String("Expression has no effect"), Help: String("Expression has no effect"), }, CreatedAt: &date, State: String("open"), ClosedBy: nil, ClosedAt: nil, URL: String("https://api.github.com/repos/o/r/code-scanning/alerts/88"), HTMLURL: String("https://github.com/o/r/security/code-scanning/88"), MostRecentInstance: &MostRecentInstance{ Ref: String("refs/heads/main"), State: String("open"), CommitSHA: String("abcdefg12345"), Message: &Message{ Text: String("This path depends on a user-provided value."), }, Location: &Location{ Path: String("spec-main/api-session-spec.ts"), StartLine: Int(917), EndLine: Int(917), StartColumn: Int(7), EndColumn: Int(18), }, Classifications: []string{"test"}, }, }, } if !cmp.Equal(alerts, want) { t.Errorf("CodeScanning.ListAlertsForRepo returned %+v, want %+v", alerts, want) } const methodName = "ListAlertsForRepo" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.ListAlertsForRepo(ctx, "\n", "\n", opts) return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { got, resp, err := client.CodeScanning.ListAlertsForRepo(ctx, "o", "r", opts) if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } return resp, err }) } func TestCodeScanningService_UpdateAlert(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/repos/o/r/code-scanning/alerts/88", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PATCH") fmt.Fprint(w, `{"rule_id":"js/useless-expression", "rule_severity":"warning", "rule_description":"Expression has no effect", "tool": { "name": "CodeQL", "guid": null, "version": "1.4.0" }, "rule": { "id": "useless expression", "severity": "warning", "description": "Expression has no effect", "name": "useless expression", "full_description": "Expression has no effect", "help": "Expression has no effect" }, "most_recent_instance": { "ref": "refs/heads/main", "state": "dismissed", "commit_sha": "abcdefg12345", "message": { "text": "This path depends on a user-provided value." }, "location": { "path": "spec-main/api-session-spec.ts", "start_line": 917, "end_line": 917, "start_column": 7, "end_column": 18 }, "classifications": [ "test" ] }, "created_at":"2019-01-02T15:04:05Z", "state":"dismissed", "dismissed_reason": "false positive", "dismissed_comment": "This alert is not actually correct as sanitizer is used", "closed_by":null, "closed_at":null, "url":"https://api.github.com/repos/o/r/code-scanning/alerts/88", "html_url":"https://github.com/o/r/security/code-scanning/88"}`) }) ctx := context.Background() dismissedComment := String("This alert is not actually correct as sanitizer is used") dismissedReason := String("false positive") state := String("dismissed") stateInfo := &CodeScanningAlertState{State: *state, DismissedReason: dismissedReason, DismissedComment: dismissedComment} alert, _, err := client.CodeScanning.UpdateAlert(ctx, "o", "r", 88, stateInfo) if err != nil { t.Errorf("CodeScanning.UpdateAlert returned error: %v", err) } date := Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)} want := &Alert{ RuleID: String("js/useless-expression"), RuleSeverity: String("warning"), RuleDescription: String("Expression has no effect"), Tool: &Tool{Name: String("CodeQL"), GUID: nil, Version: String("1.4.0")}, Rule: &Rule{ ID: String("useless expression"), Severity: String("warning"), Description: String("Expression has no effect"), Name: String("useless expression"), FullDescription: String("Expression has no effect"), Help: String("Expression has no effect"), }, CreatedAt: &date, State: state, DismissedReason: dismissedReason, DismissedComment: dismissedComment, ClosedBy: nil, ClosedAt: nil, URL: String("https://api.github.com/repos/o/r/code-scanning/alerts/88"), HTMLURL: String("https://github.com/o/r/security/code-scanning/88"), MostRecentInstance: &MostRecentInstance{ Ref: String("refs/heads/main"), State: String("dismissed"), CommitSHA: String("abcdefg12345"), Message: &Message{ Text: String("This path depends on a user-provided value."), }, Location: &Location{ Path: String("spec-main/api-session-spec.ts"), StartLine: Int(917), EndLine: Int(917), StartColumn: Int(7), EndColumn: Int(18), }, Classifications: []string{"test"}, }, } if !cmp.Equal(alert, want) { t.Errorf("CodeScanning.UpdateAlert returned %+v, want %+v", alert, want) } const methodName = "UpdateAlert" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.UpdateAlert(ctx, "\n", "\n", -88, stateInfo) return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { got, resp, err := client.CodeScanning.UpdateAlert(ctx, "o", "r", 88, stateInfo) if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } return resp, err }) } func TestCodeScanningService_ListAlertInstances(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/repos/o/r/code-scanning/alerts/88/instances", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[ { "ref": "refs/heads/main", "analysis_key": ".github/workflows/codeql-analysis.yml:analyze", "environment": "", "category": ".github/workflows/codeql-analysis.yml:analyze", "state": "open", "fixed_at": null, "commit_sha": "abcdefg12345", "message": { "text": "This path depends on a user-provided value." }, "location": { "path": "spec-main/api-session-spec.ts", "start_line": 917, "end_line": 917, "start_column": 7, "end_column": 18 }, "classifications": [ "test" ] } ]`) }) opts := &AlertInstancesListOptions{Ref: "heads/main", ListOptions: ListOptions{Page: 1}} ctx := context.Background() instances, _, err := client.CodeScanning.ListAlertInstances(ctx, "o", "r", 88, opts) if err != nil { t.Errorf("CodeScanning.ListAlertInstances returned error: %v", err) } want := []*MostRecentInstance{ { Ref: String("refs/heads/main"), AnalysisKey: String(".github/workflows/codeql-analysis.yml:analyze"), Category: String(".github/workflows/codeql-analysis.yml:analyze"), Environment: String(""), State: String("open"), CommitSHA: String("abcdefg12345"), Message: &Message{ Text: String("This path depends on a user-provided value."), }, Location: &Location{ Path: String("spec-main/api-session-spec.ts"), StartLine: Int(917), EndLine: Int(917), StartColumn: Int(7), EndColumn: Int(18), }, Classifications: []string{"test"}, }, } if !cmp.Equal(instances, want) { t.Errorf("CodeScanning.ListAlertInstances returned %+v, want %+v", instances, want) } const methodName = "ListAlertInstances" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.ListAlertInstances(ctx, "\n", "\n", -1, opts) return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { got, resp, err := client.CodeScanning.ListAlertInstances(ctx, "o", "r", 88, opts) if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } return resp, err }) } func TestCodeScanningService_GetAlert(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/repos/o/r/code-scanning/alerts/88", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{ "rule_id":"js/useless-expression", "rule_severity":"warning", "rule_description":"Expression has no effect", "tool": { "name": "CodeQL", "guid": null, "version": "1.4.0" }, "rule": { "id": "useless expression", "severity": "warning", "description": "Expression has no effect", "name": "useless expression", "full_description": "Expression has no effect", "help": "Expression has no effect" }, "most_recent_instance": { "ref": "refs/heads/main", "state": "open", "commit_sha": "abcdefg12345", "message": { "text": "This path depends on a user-provided value." }, "location": { "path": "spec-main/api-session-spec.ts", "start_line": 917, "end_line": 917, "start_column": 7, "end_column": 18 }, "classifications": [ "test" ] }, "created_at":"2019-01-02T15:04:05Z", "state":"open", "closed_by":null, "closed_at":null, "url":"https://api.github.com/repos/o/r/code-scanning/alerts/88", "html_url":"https://github.com/o/r/security/code-scanning/88" }`) }) ctx := context.Background() alert, _, err := client.CodeScanning.GetAlert(ctx, "o", "r", 88) if err != nil { t.Errorf("CodeScanning.GetAlert returned error: %v", err) } date := Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)} want := &Alert{ RuleID: String("js/useless-expression"), RuleSeverity: String("warning"), RuleDescription: String("Expression has no effect"), Tool: &Tool{Name: String("CodeQL"), GUID: nil, Version: String("1.4.0")}, Rule: &Rule{ ID: String("useless expression"), Severity: String("warning"), Description: String("Expression has no effect"), Name: String("useless expression"), FullDescription: String("Expression has no effect"), Help: String("Expression has no effect"), }, CreatedAt: &date, State: String("open"), ClosedBy: nil, ClosedAt: nil, URL: String("https://api.github.com/repos/o/r/code-scanning/alerts/88"), HTMLURL: String("https://github.com/o/r/security/code-scanning/88"), MostRecentInstance: &MostRecentInstance{ Ref: String("refs/heads/main"), State: String("open"), CommitSHA: String("abcdefg12345"), Message: &Message{ Text: String("This path depends on a user-provided value."), }, Location: &Location{ Path: String("spec-main/api-session-spec.ts"), StartLine: Int(917), EndLine: Int(917), StartColumn: Int(7), EndColumn: Int(18), }, Classifications: []string{"test"}, }, } if !cmp.Equal(alert, want) { t.Errorf("CodeScanning.GetAlert returned %+v, want %+v", alert, want) } const methodName = "GetAlert" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.GetAlert(ctx, "\n", "\n", -88) return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { got, resp, err := client.CodeScanning.GetAlert(ctx, "o", "r", 88) if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } return resp, err }) } func TestAlert_Marshal(t *testing.T) { testJSONMarshal(t, &Alert{}, "{}") u := &Alert{ RuleID: String("rid"), RuleSeverity: String("rs"), RuleDescription: String("rd"), Tool: &Tool{ Name: String("n"), GUID: String("g"), Version: String("v"), }, CreatedAt: &Timestamp{referenceTime}, State: String("fixed"), ClosedBy: &User{ Login: String("l"), ID: Int64(1), NodeID: String("n"), URL: String("u"), ReposURL: String("r"), EventsURL: String("e"), AvatarURL: String("a"), }, ClosedAt: &Timestamp{referenceTime}, URL: String("url"), HTMLURL: String("hurl"), } want := `{ "rule_id": "rid", "rule_severity": "rs", "rule_description": "rd", "tool": { "name": "n", "guid": "g", "version": "v" }, "created_at": ` + referenceTimeStr + `, "state": "fixed", "closed_by": { "login": "l", "id": 1, "node_id": "n", "avatar_url": "a", "url": "u", "events_url": "e", "repos_url": "r" }, "closed_at": ` + referenceTimeStr + `, "url": "url", "html_url": "hurl" }` testJSONMarshal(t, u, want) } func TestLocation_Marshal(t *testing.T) { testJSONMarshal(t, &Location{}, "{}") u := &Location{ Path: String("path"), StartLine: Int(1), EndLine: Int(2), StartColumn: Int(3), EndColumn: Int(4), } want := `{ "path": "path", "start_line": 1, "end_line": 2, "start_column": 3, "end_column": 4 }` testJSONMarshal(t, u, want) } func TestRule_Marshal(t *testing.T) { testJSONMarshal(t, &Rule{}, "{}") u := &Rule{ ID: String("1"), Severity: String("3"), Description: String("description"), Name: String("first"), SecuritySeverityLevel: String("2"), FullDescription: String("summary"), Tags: []string{"tag1", "tag2"}, Help: String("Help Text"), } want := `{ "id": "1", "severity": "3", "description": "description", "name": "first", "security_severity_level": "2", "full_description": "summary", "tags": ["tag1", "tag2"], "help": "Help Text" }` testJSONMarshal(t, u, want) } func TestTool_Marshal(t *testing.T) { testJSONMarshal(t, &Tool{}, "{}") u := &Tool{ Name: String("name"), GUID: String("guid"), Version: String("ver"), } want := `{ "name": "name", "guid": "guid", "version": "ver" }` testJSONMarshal(t, u, want) } func TestMessage_Marshal(t *testing.T) { testJSONMarshal(t, &Message{}, "{}") u := &Message{ Text: String("text"), } want := `{ "text": "text" }` testJSONMarshal(t, u, want) } func TestCodeScanningService_ListAnalysesForRepo(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/repos/o/r/code-scanning/analyses", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testFormValues(t, r, values{"sarif_id": "8981cd8e-b078-4ac3-a3be-1dad7dbd0b582", "ref": "heads/master"}) fmt.Fprint(w, `[ { "ref": "refs/heads/main", "commit_sha": "d99612c3e1f2970085cfbaeadf8f010ef69bad83", "analysis_key": ".github/workflows/codeql-analysis.yml:analyze", "environment": "{\"language\":\"python\"}", "error": "", "category": ".github/workflows/codeql-analysis.yml:analyze/language:python", "created_at": "2020-08-27T15:05:21Z", "results_count": 17, "rules_count": 49, "id": 201, "url": "https://api.github.com/repos/o/r/code-scanning/analyses/201", "sarif_id": "8981cd8e-b078-4ac3-a3be-1dad7dbd0b582", "tool": { "name": "CodeQL", "guid": null, "version": "2.4.0" }, "deletable": true, "warning": "" }, { "ref": "refs/heads/my-branch", "commit_sha": "c8cff6510d4d084fb1b4aa13b64b97ca12b07321", "analysis_key": ".github/workflows/shiftleft.yml:build", "environment": "{}", "error": "", "category": ".github/workflows/shiftleft.yml:build/", "created_at": "2020-08-27T15:05:21Z", "results_count": 17, "rules_count": 32, "id": 200, "url": "https://api.github.com/repos/o/r/code-scanning/analyses/200", "sarif_id": "8981cd8e-b078-4ac3-a3be-1dad7dbd0b582", "tool": { "name": "Python Security ScanningAnalysis", "guid": null, "version": "1.2.0" }, "deletable": true, "warning": "" } ]`) }) opts := &AnalysesListOptions{SarifID: String("8981cd8e-b078-4ac3-a3be-1dad7dbd0b582"), Ref: String("heads/master")} ctx := context.Background() analyses, _, err := client.CodeScanning.ListAnalysesForRepo(ctx, "o", "r", opts) if err != nil { t.Errorf("CodeScanning.ListAnalysesForRepo returned error: %v", err) } date := &Timestamp{time.Date(2020, time.August, 27, 15, 05, 21, 0, time.UTC)} want := []*ScanningAnalysis{ { ID: Int64(201), Ref: String("refs/heads/main"), CommitSHA: String("d99612c3e1f2970085cfbaeadf8f010ef69bad83"), AnalysisKey: String(".github/workflows/codeql-analysis.yml:analyze"), Environment: String("{\"language\":\"python\"}"), Error: String(""), Category: String(".github/workflows/codeql-analysis.yml:analyze/language:python"), CreatedAt: date, ResultsCount: Int(17), RulesCount: Int(49), URL: String("https://api.github.com/repos/o/r/code-scanning/analyses/201"), SarifID: String("8981cd8e-b078-4ac3-a3be-1dad7dbd0b582"), Tool: &Tool{ Name: String("CodeQL"), GUID: nil, Version: String("2.4.0"), }, Deletable: Bool(true), Warning: String(""), }, { ID: Int64(200), Ref: String("refs/heads/my-branch"), CommitSHA: String("c8cff6510d4d084fb1b4aa13b64b97ca12b07321"), AnalysisKey: String(".github/workflows/shiftleft.yml:build"), Environment: String("{}"), Error: String(""), Category: String(".github/workflows/shiftleft.yml:build/"), CreatedAt: date, ResultsCount: Int(17), RulesCount: Int(32), URL: String("https://api.github.com/repos/o/r/code-scanning/analyses/200"), SarifID: String("8981cd8e-b078-4ac3-a3be-1dad7dbd0b582"), Tool: &Tool{ Name: String("Python Security ScanningAnalysis"), GUID: nil, Version: String("1.2.0"), }, Deletable: Bool(true), Warning: String(""), }, } if !cmp.Equal(analyses, want) { t.Errorf("CodeScanning.ListAnalysesForRepo returned %+v, want %+v", analyses, want) } const methodName = "ListAnalysesForRepo" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.ListAnalysesForRepo(ctx, "\n", "\n", opts) return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { got, resp, err := client.CodeScanning.ListAnalysesForRepo(ctx, "o", "r", opts) if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } return resp, err }) } func TestCodeScanningService_GetAnalysis(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/repos/o/r/code-scanning/analyses/3602840", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{ "ref": "refs/heads/main", "commit_sha": "c18c69115654ff0166991962832dc2bd7756e655", "analysis_key": ".github/workflows/codeql-analysis.yml:analyze", "environment": "{\"language\":\"javascript\"}", "error": "", "category": ".github/workflows/codeql-analysis.yml:analyze/language:javascript", "created_at": "2021-01-13T11:55:49Z", "results_count": 3, "rules_count": 67, "id": 3602840, "url": "https://api.github.com/repos/o/r/code-scanning/analyses/201", "sarif_id": "47177e22-5596-11eb-80a1-c1e54ef945c6", "tool": { "name": "CodeQL", "guid": null, "version": "2.4.0" }, "deletable": true, "warning": "" }`) }) ctx := context.Background() analysis, _, err := client.CodeScanning.GetAnalysis(ctx, "o", "r", 3602840) if err != nil { t.Errorf("CodeScanning.GetAnalysis returned error: %v", err) } date := &Timestamp{time.Date(2021, time.January, 13, 11, 55, 49, 0, time.UTC)} want := &ScanningAnalysis{ ID: Int64(3602840), Ref: String("refs/heads/main"), CommitSHA: String("c18c69115654ff0166991962832dc2bd7756e655"), AnalysisKey: String(".github/workflows/codeql-analysis.yml:analyze"), Environment: String("{\"language\":\"javascript\"}"), Error: String(""), Category: String(".github/workflows/codeql-analysis.yml:analyze/language:javascript"), CreatedAt: date, ResultsCount: Int(3), RulesCount: Int(67), URL: String("https://api.github.com/repos/o/r/code-scanning/analyses/201"), SarifID: String("47177e22-5596-11eb-80a1-c1e54ef945c6"), Tool: &Tool{ Name: String("CodeQL"), GUID: nil, Version: String("2.4.0"), }, Deletable: Bool(true), Warning: String(""), } if !cmp.Equal(analysis, want) { t.Errorf("CodeScanning.GetAnalysis returned %+v, want %+v", analysis, want) } const methodName = "GetAnalysis" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.GetAnalysis(ctx, "\n", "\n", -123) return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { got, resp, err := client.CodeScanning.GetAnalysis(ctx, "o", "r", 3602840) if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } return resp, err }) } func TestCodeScanningService_DeleteAnalysis(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/repos/o/r/code-scanning/analyses/40", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") fmt.Fprint(w, `{ "next_analysis_url": "a", "confirm_delete_url": "b" }`) }) ctx := context.Background() analysis, _, err := client.CodeScanning.DeleteAnalysis(ctx, "o", "r", 40) if err != nil { t.Errorf("CodeScanning.DeleteAnalysis returned error: %v", err) } want := &DeleteAnalysis{ NextAnalysisURL: String("a"), ConfirmDeleteURL: String("b"), } if !cmp.Equal(analysis, want) { t.Errorf("CodeScanning.DeleteAnalysis returned %+v, want %+v", analysis, want) } const methodName = "DeleteAnalysis" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.DeleteAnalysis(ctx, "\n", "\n", -123) return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { got, resp, err := client.CodeScanning.DeleteAnalysis(ctx, "o", "r", 40) if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } return resp, err }) } func TestCodeScanningService_ListCodeQLDatabases(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/repos/o/r/code-scanning/codeql/databases", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[ { "id": 1, "name": "name", "language": "language", "uploader": { "login": "a", "id": 1, "node_id": "b", "avatar_url": "c", "gravatar_id": "d", "url": "e", "html_url": "f", "followers_url": "g", "following_url": "h", "gists_url": "i", "starred_url": "j", "subscriptions_url": "k", "organizations_url": "l", "repos_url": "m", "events_url": "n", "received_events_url": "o", "type": "p", "site_admin": false }, "content_type": "r", "size": 1024, "created_at": "2021-01-13T11:55:49Z", "updated_at": "2021-01-13T11:55:49Z", "url": "s" } ]`) }) ctx := context.Background() databases, _, err := client.CodeScanning.ListCodeQLDatabases(ctx, "o", "r") if err != nil { t.Errorf("CodeScanning.ListCodeQLDatabases returned error: %v", err) } date := &Timestamp{time.Date(2021, time.January, 13, 11, 55, 49, 0, time.UTC)} want := []*CodeQLDatabase{ { ID: Int64(1), Name: String("name"), Language: String("language"), Uploader: &User{ Login: String("a"), ID: Int64(1), NodeID: String("b"), AvatarURL: String("c"), GravatarID: String("d"), URL: String("e"), HTMLURL: String("f"), FollowersURL: String("g"), FollowingURL: String("h"), GistsURL: String("i"), StarredURL: String("j"), SubscriptionsURL: String("k"), OrganizationsURL: String("l"), ReposURL: String("m"), EventsURL: String("n"), ReceivedEventsURL: String("o"), Type: String("p"), SiteAdmin: Bool(false), }, ContentType: String("r"), Size: Int64(1024), CreatedAt: date, UpdatedAt: date, URL: String("s"), }, } if !cmp.Equal(databases, want) { t.Errorf("CodeScanning.ListCodeQLDatabases returned %+v, want %+v", databases, want) } const methodName = "ListCodeQLDatabases" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.ListCodeQLDatabases(ctx, "\n", "\n") return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { got, resp, err := client.CodeScanning.ListCodeQLDatabases(ctx, "o", "r") if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } return resp, err }) } func TestCodeScanningService_GetCodeQLDatabase(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/repos/o/r/code-scanning/codeql/databases/lang", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{ "id": 1, "name": "name", "language": "language", "uploader": { "login": "a", "id": 1, "node_id": "b", "avatar_url": "c", "gravatar_id": "d", "url": "e", "html_url": "f", "followers_url": "g", "following_url": "h", "gists_url": "i", "starred_url": "j", "subscriptions_url": "k", "organizations_url": "l", "repos_url": "m", "events_url": "n", "received_events_url": "o", "type": "p", "site_admin": false }, "content_type": "r", "size": 1024, "created_at": "2021-01-13T11:55:49Z", "updated_at": "2021-01-13T11:55:49Z", "url": "s" }`) }) ctx := context.Background() database, _, err := client.CodeScanning.GetCodeQLDatabase(ctx, "o", "r", "lang") if err != nil { t.Errorf("CodeScanning.GetCodeQLDatabase returned error: %v", err) } date := &Timestamp{time.Date(2021, time.January, 13, 11, 55, 49, 0, time.UTC)} want := &CodeQLDatabase{ ID: Int64(1), Name: String("name"), Language: String("language"), Uploader: &User{ Login: String("a"), ID: Int64(1), NodeID: String("b"), AvatarURL: String("c"), GravatarID: String("d"), URL: String("e"), HTMLURL: String("f"), FollowersURL: String("g"), FollowingURL: String("h"), GistsURL: String("i"), StarredURL: String("j"), SubscriptionsURL: String("k"), OrganizationsURL: String("l"), ReposURL: String("m"), EventsURL: String("n"), ReceivedEventsURL: String("o"), Type: String("p"), SiteAdmin: Bool(false), }, ContentType: String("r"), Size: Int64(1024), CreatedAt: date, UpdatedAt: date, URL: String("s"), } if !cmp.Equal(database, want) { t.Errorf("CodeScanning.GetCodeQLDatabase returned %+v, want %+v", database, want) } const methodName = "GetCodeQLDatabase" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.GetCodeQLDatabase(ctx, "\n", "\n", "\n") return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { got, resp, err := client.CodeScanning.GetCodeQLDatabase(ctx, "o", "r", "lang") if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } return resp, err }) } func TestCodeScanningService_GetDefaultSetupConfiguration(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/repos/o/r/code-scanning/default-setup", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") _, err := fmt.Fprint(w, `{ "state": "configured", "languages": [ "javascript", "javascript-typescript", "typescript" ], "query_suite": "default", "updated_at": "2006-01-02T15:04:05Z" }`) if err != nil { t.Fatal(err) } }) ctx := context.Background() cfg, _, err := client.CodeScanning.GetDefaultSetupConfiguration(ctx, "o", "r") if err != nil { t.Errorf("CodeScanning.GetDefaultSetupConfiguration returned error: %v", err) } date := &Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)} want := &DefaultSetupConfiguration{ State: String("configured"), Languages: []string{"javascript", "javascript-typescript", "typescript"}, QuerySuite: String("default"), UpdatedAt: date, } if !cmp.Equal(cfg, want) { t.Errorf("CodeScanning.GetDefaultSetupConfiguration returned %+v, want %+v", cfg, want) } const methodName = "GetDefaultSetupConfiguration" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.GetDefaultSetupConfiguration(ctx, "\n", "\n") return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { got, resp, err := client.CodeScanning.GetDefaultSetupConfiguration(ctx, "o", "r") if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } return resp, err }) } func TestCodeScanningService_UpdateDefaultSetupConfiguration(t *testing.T) { client, mux, _, teardown := setup() defer teardown() mux.HandleFunc("/repos/o/r/code-scanning/default-setup", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PATCH") _, err := fmt.Fprint(w, `{ "run_id": 5301214200, "run_url": "https://api.github.com/repos/o/r/actions/runs/5301214200" }`) if err != nil { t.Fatal(err) } }) ctx := context.Background() options := &UpdateDefaultSetupConfigurationOptions{ State: "configured", Languages: []string{"go"}, QuerySuite: String("default"), } got, _, err := client.CodeScanning.UpdateDefaultSetupConfiguration(ctx, "o", "r", options) if err != nil { t.Errorf("CodeScanning.UpdateDefaultSetupConfiguration returned error: %v", err) } want := &UpdateDefaultSetupConfigurationResponse{ RunID: Int64(5301214200), RunURL: String("https://api.github.com/repos/o/r/actions/runs/5301214200"), } if !cmp.Equal(got, want) { t.Errorf("CodeScanning.UpdateDefaultSetupConfiguration returned %+v, want %+v", got, want) } const methodName = "UpdateDefaultSetupConfiguration" testBadOptions(t, methodName, func() (err error) { _, _, err = client.CodeScanning.UpdateDefaultSetupConfiguration(ctx, "\n", "\n", nil) return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { got, resp, err := client.CodeScanning.UpdateDefaultSetupConfiguration(ctx, "o", "r", nil) if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } return resp, err }) }