github.com/jfrog/frogbot/v2@v2.21.0/utils/outputwriter/outputcontent_test.go (about)

     1  package outputwriter
     2  
     3  import (
     4  	"path/filepath"
     5  	"testing"
     6  
     7  	"github.com/jfrog/froggit-go/vcsutils"
     8  	"github.com/jfrog/jfrog-cli-security/formats"
     9  	"github.com/jfrog/jfrog-cli-security/utils"
    10  	"github.com/stretchr/testify/assert"
    11  )
    12  
    13  func TestGetPRSummaryContent(t *testing.T) {
    14  	testCases := []struct {
    15  		name         string
    16  		cases        []OutputTestCase
    17  		issuesExists bool
    18  		isComment    bool
    19  	}{
    20  		{
    21  			name:         "Summary comment No issues found",
    22  			issuesExists: false,
    23  			isComment:    true,
    24  			cases: []OutputTestCase{
    25  				{
    26  					name:               "Pull Request not entitled (Standard output)",
    27  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true}},
    28  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_no_issues_pr_not_entitled.md")},
    29  				},
    30  				{
    31  					name:               "Pull Request entitled (Standard output)",
    32  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, entitledForJas: true}},
    33  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_no_issues_pr_entitled.md")},
    34  				},
    35  				{
    36  					name:               "Merge Request not entitled (Standard output)",
    37  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, vcsProvider: vcsutils.GitLab}},
    38  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_no_issues_mr_not_entitled.md")},
    39  				},
    40  				{
    41  					name:               "Merge Request entitled (Standard output)",
    42  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, vcsProvider: vcsutils.GitLab, entitledForJas: true}},
    43  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_no_issues_mr_entitled.md")},
    44  				},
    45  				{
    46  					name:               "Simplified output not entitled",
    47  					writer:             &SimplifiedOutput{MarkdownOutput{hasInternetConnection: true}},
    48  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_no_issues_simplified_not_entitled.md")},
    49  				},
    50  				{
    51  					name:               "Simplified output entitled",
    52  					writer:             &SimplifiedOutput{MarkdownOutput{hasInternetConnection: true, entitledForJas: true}},
    53  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_no_issues_simplified_entitled.md")},
    54  				},
    55  				{
    56  					name:               "Pull request not entitled custom title (Standard output)",
    57  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, pullRequestCommentTitle: "Custom title"}},
    58  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_no_issues_pr_not_entitled_with_title.md")},
    59  				},
    60  				{
    61  					name:               "Pull Request not entitled custom title avoid extra messages (Standard output)",
    62  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, pullRequestCommentTitle: "Custom title", avoidExtraMessages: true}},
    63  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_no_issues_pr_entitled_with_title.md")},
    64  				},
    65  				{
    66  					name:               "Pull request not entitled custom title (Simplified output)",
    67  					writer:             &SimplifiedOutput{MarkdownOutput{hasInternetConnection: true, pullRequestCommentTitle: "Custom title"}},
    68  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_no_issues_simplified_not_entitled_with_title.md")},
    69  				},
    70  				{
    71  					name:               "Merge Request not entitled avoid extra messages (Standard output)",
    72  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, vcsProvider: vcsutils.GitLab, avoidExtraMessages: true}},
    73  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_no_issues_mr_entitled.md")},
    74  				},
    75  			},
    76  		},
    77  		{
    78  			name:         "Summary comment Found issues",
    79  			issuesExists: true,
    80  			isComment:    true,
    81  			cases: []OutputTestCase{
    82  				{
    83  					name:               "Pull Request not entitled (Standard output)",
    84  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true}},
    85  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_issues_pr_not_entitled.md")},
    86  				},
    87  				{
    88  					name:               "Pull Request entitled (Standard output)",
    89  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, entitledForJas: true}},
    90  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_issues_pr_entitled.md")},
    91  				},
    92  				{
    93  					name:               "Merge Request not entitled (Standard output)",
    94  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, vcsProvider: vcsutils.GitLab}},
    95  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_issues_mr_not_entitled.md")},
    96  				},
    97  				{
    98  					name:               "Merge Request entitled (Standard output)",
    99  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, vcsProvider: vcsutils.GitLab, entitledForJas: true}},
   100  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_issues_mr_entitled.md")},
   101  				},
   102  				{
   103  					name:               "Simplified output not entitled",
   104  					writer:             &SimplifiedOutput{MarkdownOutput{hasInternetConnection: true}},
   105  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_issues_simplified_not_entitled.md")},
   106  				},
   107  				{
   108  					name:               "Pull Request not entitled avoid extra messages (Standard output)",
   109  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, avoidExtraMessages: true}},
   110  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_issues_pr_entitled.md")},
   111  				},
   112  				{
   113  					name:               "Simplified output not entitled avoid extra messages",
   114  					writer:             &SimplifiedOutput{MarkdownOutput{hasInternetConnection: true, avoidExtraMessages: true}},
   115  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_issues_simplified_entitled.md")},
   116  				},
   117  				{
   118  					name:               "Simplified output entitled",
   119  					writer:             &SimplifiedOutput{MarkdownOutput{hasInternetConnection: true, entitledForJas: true}},
   120  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_issues_simplified_entitled.md")},
   121  				},
   122  				{
   123  					name:               "Merge Request entitled custom title (Standard output)",
   124  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, vcsProvider: vcsutils.GitLab, entitledForJas: true, pullRequestCommentTitle: "Custom title"}},
   125  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_issues_mr_entitled_with_title.md")},
   126  				},
   127  				{
   128  					name:               "Pull Request not entitled custom title (Standard output)",
   129  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, pullRequestCommentTitle: "Custom title"}},
   130  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_issues_pr_not_entitled_with_title.md")},
   131  				},
   132  				{
   133  					name:               "Pull request entitled custom title (Simplified output)",
   134  					writer:             &SimplifiedOutput{MarkdownOutput{hasInternetConnection: true, entitledForJas: true, pullRequestCommentTitle: "Custom title"}},
   135  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "summary_comment_issues_simplified_entitled_with_title.md")},
   136  				},
   137  			},
   138  		},
   139  		{
   140  			name:         "Frogbot Fix issues details content",
   141  			issuesExists: true,
   142  			isComment:    false,
   143  			cases: []OutputTestCase{
   144  				{
   145  					name:               "Pull Request not entitled (Standard output)",
   146  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true}},
   147  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "fix_pr_not_entitled.md")},
   148  				},
   149  				{
   150  					name:               "Pull Request entitled (Standard output)",
   151  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, entitledForJas: true}},
   152  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "fix_pr_entitled.md")},
   153  				},
   154  				{
   155  					name:               "Merge Request not entitled (Standard output)",
   156  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, vcsProvider: vcsutils.GitLab}},
   157  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "fix_mr_not_entitled.md")},
   158  				},
   159  				{
   160  					name:               "Merge Request entitled (Standard output)",
   161  					writer:             &StandardOutput{MarkdownOutput{hasInternetConnection: true, vcsProvider: vcsutils.GitLab, entitledForJas: true}},
   162  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "fix_mr_entitled.md")},
   163  				},
   164  				{
   165  					name:               "Simplified output not entitled",
   166  					writer:             &SimplifiedOutput{MarkdownOutput{hasInternetConnection: true}},
   167  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "fix_simplified_not_entitled.md")},
   168  				},
   169  				{
   170  					name:               "Simplified output not entitled ",
   171  					writer:             &SimplifiedOutput{MarkdownOutput{hasInternetConnection: true}},
   172  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "fix_simplified_not_entitled.md")},
   173  				},
   174  				{
   175  					name:               "Simplified output entitled avoid extra messages",
   176  					writer:             &SimplifiedOutput{MarkdownOutput{hasInternetConnection: true, avoidExtraMessages: true}},
   177  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "structure", "fix_simplified_entitled.md")},
   178  				},
   179  			},
   180  		},
   181  	}
   182  
   183  	for _, tc := range testCases {
   184  		for _, test := range tc.cases {
   185  			t.Run(tc.name+"_"+test.name, func(t *testing.T) {
   186  				expectedOutput := GetExpectedTestOutput(t, test)
   187  				output := GetPRSummaryContent([]string{MarkAsCodeSnippet("some content")}, tc.issuesExists, tc.isComment, test.writer)
   188  				assert.Len(t, output, 1)
   189  				assert.Equal(t, expectedOutput, output[0])
   190  			})
   191  		}
   192  	}
   193  }
   194  
   195  func TestVulnerabilitiesContent(t *testing.T) {
   196  	testCases := []struct {
   197  		name            string
   198  		vulnerabilities []formats.VulnerabilityOrViolationRow
   199  		cases           []OutputTestCase
   200  	}{
   201  		{
   202  			name:            "No vulnerabilities",
   203  			vulnerabilities: []formats.VulnerabilityOrViolationRow{},
   204  			cases: []OutputTestCase{
   205  				{
   206  					name:           "Standard output",
   207  					writer:         &StandardOutput{},
   208  					expectedOutput: []string{""},
   209  				},
   210  				{
   211  					name:           "Simplified output",
   212  					writer:         &SimplifiedOutput{},
   213  					expectedOutput: []string{""},
   214  				},
   215  			},
   216  		},
   217  		{
   218  			name: "One vulnerability",
   219  			vulnerabilities: []formats.VulnerabilityOrViolationRow{
   220  				{
   221  					Summary: "Summary CVE-2022-26652",
   222  					ImpactedDependencyDetails: formats.ImpactedDependencyDetails{
   223  						SeverityDetails:           formats.SeverityDetails{Severity: "Medium"},
   224  						ImpactedDependencyName:    "github.com/nats-io/nats-streaming-server",
   225  						ImpactedDependencyVersion: "v0.21.0",
   226  						Components: []formats.ComponentRow{
   227  							{
   228  								Name:    "github.com/nats-io/nats-streaming-server",
   229  								Version: "v0.21.0",
   230  							},
   231  						},
   232  					},
   233  					Applicable:    "Undetermined",
   234  					FixedVersions: []string{"[0.24.3]"},
   235  					JfrogResearchInformation: &formats.JfrogResearchInformation{
   236  						Details:     "Research CVE-2022-26652 details",
   237  						Remediation: "some remediation",
   238  					},
   239  					Cves: []formats.CveRow{{Id: "CVE-2022-26652"}},
   240  				},
   241  			},
   242  			cases: []OutputTestCase{
   243  				{
   244  					name:               "Standard output",
   245  					writer:             &StandardOutput{},
   246  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "vulnerabilities", "one_vulnerability_standard.md")},
   247  				},
   248  				{
   249  					name:               "Simplified output",
   250  					writer:             &SimplifiedOutput{},
   251  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "vulnerabilities", "one_vulnerability_simplified.md")},
   252  				},
   253  			},
   254  		},
   255  		{
   256  			name: "One vulnerability, no Details",
   257  			vulnerabilities: []formats.VulnerabilityOrViolationRow{
   258  				{
   259  					ImpactedDependencyDetails: formats.ImpactedDependencyDetails{
   260  						SeverityDetails:           formats.SeverityDetails{Severity: "Medium"},
   261  						ImpactedDependencyName:    "github.com/nats-io/nats-streaming-server",
   262  						ImpactedDependencyVersion: "v0.21.0",
   263  						Components: []formats.ComponentRow{
   264  							{
   265  								Name:    "github.com/nats-io/nats-streaming-server",
   266  								Version: "v0.21.0",
   267  							},
   268  						},
   269  					},
   270  					Applicable:    "Undetermined",
   271  					FixedVersions: []string{"[0.24.3]"},
   272  					Cves:          []formats.CveRow{{Id: "CVE-2022-26652"}},
   273  				},
   274  			},
   275  			cases: []OutputTestCase{
   276  				{
   277  					name:               "Standard output",
   278  					writer:             &StandardOutput{},
   279  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "vulnerabilities", "one_vulnerability_no_details_standard.md")},
   280  				},
   281  				{
   282  					name:               "Simplified output",
   283  					writer:             &SimplifiedOutput{},
   284  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "vulnerabilities", "one_vulnerability_no_details_simplified.md")},
   285  				},
   286  			},
   287  		},
   288  		{
   289  			name: "multiple Vulnerabilities with Contextual Analysis",
   290  			vulnerabilities: []formats.VulnerabilityOrViolationRow{
   291  				{
   292  					ImpactedDependencyDetails: formats.ImpactedDependencyDetails{
   293  						SeverityDetails:           formats.SeverityDetails{Severity: "Critical", SeverityNumValue: utils.GetSeverity("Critical", utils.NotApplicable).SeverityNumValue},
   294  						ImpactedDependencyName:    "impacted",
   295  						ImpactedDependencyVersion: "3.0.0",
   296  						Components: []formats.ComponentRow{
   297  							{Name: "dep1", Version: "1.0.0"},
   298  							{Name: "dep2", Version: "2.0.0"},
   299  						},
   300  					},
   301  					Applicable:    "Not Applicable",
   302  					FixedVersions: []string{"4.0.0", "5.0.0"},
   303  					Cves:          []formats.CveRow{{Id: "CVE-1111-11111", Applicability: &formats.Applicability{Status: "Not Applicable"}}},
   304  				},
   305  				{
   306  					Summary: "Summary XRAY-122345",
   307  					ImpactedDependencyDetails: formats.ImpactedDependencyDetails{
   308  						SeverityDetails:           formats.SeverityDetails{Severity: "High", SeverityNumValue: utils.GetSeverity("High", utils.ApplicabilityUndetermined).SeverityNumValue},
   309  						ImpactedDependencyName:    "github.com/nats-io/nats-streaming-server",
   310  						ImpactedDependencyVersion: "v0.21.0",
   311  						Components: []formats.ComponentRow{
   312  							{
   313  								Name:    "github.com/nats-io/nats-streaming-server",
   314  								Version: "v0.21.0",
   315  							},
   316  						},
   317  					},
   318  					Applicable:    "Undetermined",
   319  					FixedVersions: []string{"[0.24.1]"},
   320  					IssueId:       "XRAY-122345",
   321  					JfrogResearchInformation: &formats.JfrogResearchInformation{
   322  						Remediation: "some remediation",
   323  					},
   324  					Cves: []formats.CveRow{{}},
   325  				},
   326  				{
   327  					ImpactedDependencyDetails: formats.ImpactedDependencyDetails{
   328  						SeverityDetails:           formats.SeverityDetails{Severity: "Medium", SeverityNumValue: utils.GetSeverity("Medium", utils.Applicable).SeverityNumValue},
   329  						ImpactedDependencyName:    "component-D",
   330  						ImpactedDependencyVersion: "v0.21.0",
   331  						Components: []formats.ComponentRow{
   332  							{
   333  								Name:    "component-D",
   334  								Version: "v0.21.0",
   335  							},
   336  						},
   337  					},
   338  					Applicable:    "Applicable",
   339  					FixedVersions: []string{"[0.24.3]"},
   340  					JfrogResearchInformation: &formats.JfrogResearchInformation{
   341  						Remediation: "some remediation",
   342  					},
   343  					Cves: []formats.CveRow{
   344  						{Id: "CVE-2022-26652"},
   345  						{Id: "CVE-2023-4321", Applicability: &formats.Applicability{Status: "Applicable"}},
   346  					},
   347  				},
   348  				{
   349  					Summary: "Summary",
   350  					ImpactedDependencyDetails: formats.ImpactedDependencyDetails{
   351  						SeverityDetails:           formats.SeverityDetails{Severity: "Low", SeverityNumValue: utils.GetSeverity("Low", utils.ApplicabilityUndetermined).SeverityNumValue},
   352  						ImpactedDependencyName:    "github.com/mholt/archiver/v3",
   353  						ImpactedDependencyVersion: "v3.5.1",
   354  						Components: []formats.ComponentRow{
   355  							{
   356  								Name:    "github.com/mholt/archiver/v3",
   357  								Version: "v3.5.1",
   358  							},
   359  						},
   360  					},
   361  					Applicable: "Undetermined",
   362  					Cves:       []formats.CveRow{},
   363  				},
   364  			},
   365  			cases: []OutputTestCase{
   366  				{
   367  					name:               "Standard output",
   368  					writer:             &StandardOutput{MarkdownOutput{showCaColumn: true}},
   369  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "vulnerabilities", "vulnerabilities_standard.md")},
   370  				},
   371  				{
   372  					name:               "Simplified output",
   373  					writer:             &SimplifiedOutput{MarkdownOutput{showCaColumn: true}},
   374  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "vulnerabilities", "vulnerabilities_simplified.md")},
   375  				},
   376  				{
   377  					name:   "Split Standard output",
   378  					writer: &StandardOutput{MarkdownOutput{showCaColumn: true, descriptionSizeLimit: 1720, commentSizeLimit: 1720}},
   379  					expectedOutputPath: []string{
   380  						filepath.Join(testSummaryCommentDir, "vulnerabilities", "vulnerabilities_standard_split1.md"),
   381  						filepath.Join(testSummaryCommentDir, "vulnerabilities", "vulnerabilities_standard_split2.md"),
   382  					},
   383  				},
   384  				{
   385  					name:   "Split Simplified output",
   386  					writer: &SimplifiedOutput{MarkdownOutput{showCaColumn: true, descriptionSizeLimit: 1000, commentSizeLimit: 1000}},
   387  					expectedOutputPath: []string{
   388  						filepath.Join(testSummaryCommentDir, "vulnerabilities", "vulnerabilities_simplified_split1.md"),
   389  						filepath.Join(testSummaryCommentDir, "vulnerabilities", "vulnerabilities_simplified_split2.md"),
   390  					},
   391  				},
   392  			},
   393  		},
   394  		{
   395  			name: "Split vulnerabilities content",
   396  		},
   397  	}
   398  	for _, tc := range testCases {
   399  		for _, test := range tc.cases {
   400  			t.Run(tc.name+"_"+test.name, func(t *testing.T) {
   401  				expectedOutput := GetExpectedTestCaseOutput(t, test)
   402  				output := ConvertContentToComments(VulnerabilitiesContent(tc.vulnerabilities, test.writer), test.writer)
   403  				assert.Len(t, output, len(expectedOutput))
   404  				assert.ElementsMatch(t, expectedOutput, output)
   405  			})
   406  		}
   407  	}
   408  }
   409  
   410  func TestLicensesContent(t *testing.T) {
   411  	testCases := []struct {
   412  		name     string
   413  		licenses []formats.LicenseRow
   414  		cases    []OutputTestCase
   415  	}{
   416  		{
   417  			name:     "No license violations",
   418  			licenses: []formats.LicenseRow{},
   419  			cases: []OutputTestCase{
   420  				{
   421  					name:           "Standard output",
   422  					writer:         &StandardOutput{},
   423  					expectedOutput: []string{""},
   424  				},
   425  				{
   426  					name:           "Simplified output",
   427  					writer:         &SimplifiedOutput{},
   428  					expectedOutput: []string{""},
   429  				},
   430  			},
   431  		},
   432  		{
   433  			name: "License violations",
   434  			licenses: []formats.LicenseRow{
   435  				{
   436  					LicenseKey: "License1",
   437  					ImpactedDependencyDetails: formats.ImpactedDependencyDetails{
   438  
   439  						Components:                []formats.ComponentRow{{Name: "Comp1", Version: "1.0"}},
   440  						ImpactedDependencyName:    "Dep1",
   441  						ImpactedDependencyVersion: "2.0",
   442  						SeverityDetails: formats.SeverityDetails{
   443  							Severity: "High",
   444  						},
   445  					},
   446  				},
   447  				{
   448  					LicenseKey: "License2",
   449  					ImpactedDependencyDetails: formats.ImpactedDependencyDetails{
   450  						Components: []formats.ComponentRow{
   451  							{
   452  								Name:    "root",
   453  								Version: "1.0.0",
   454  							},
   455  							{
   456  								Name:    "minimatch",
   457  								Version: "1.2.3",
   458  							},
   459  						},
   460  						ImpactedDependencyName:    "Dep2",
   461  						ImpactedDependencyVersion: "3.0",
   462  						SeverityDetails: formats.SeverityDetails{
   463  							Severity: "High",
   464  						},
   465  					},
   466  				},
   467  			},
   468  			cases: []OutputTestCase{
   469  				{
   470  					name:               "Standard output",
   471  					writer:             &StandardOutput{},
   472  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "license", "license_violation_standard.md")},
   473  				},
   474  				{
   475  					name:               "Simplified output",
   476  					writer:             &SimplifiedOutput{},
   477  					expectedOutputPath: []string{filepath.Join(testSummaryCommentDir, "license", "license_violation_simplified.md")},
   478  				},
   479  			},
   480  		},
   481  	}
   482  	for _, tc := range testCases {
   483  		for _, test := range tc.cases {
   484  			t.Run(tc.name+"_"+test.name, func(t *testing.T) {
   485  				assert.Equal(t, GetExpectedTestOutput(t, test), LicensesContent(tc.licenses, test.writer))
   486  			})
   487  		}
   488  	}
   489  }
   490  
   491  func TestIsFrogbotReviewComment(t *testing.T) {
   492  	testCases := []struct {
   493  		name           string
   494  		content        string
   495  		expectedOutput bool
   496  	}{
   497  		{
   498  			name:           "Not frogbot comments",
   499  			content:        "This comment is unrelated to Frogbot",
   500  			expectedOutput: false,
   501  		},
   502  		{
   503  			name:           "Frogbot review comment",
   504  			content:        MarkdownComment(ReviewCommentId) + "This is a review comment",
   505  			expectedOutput: true,
   506  		},
   507  	}
   508  	for _, tc := range testCases {
   509  		t.Run(tc.name, func(t *testing.T) {
   510  			assert.Equal(t, tc.expectedOutput, IsFrogbotComment(tc.content))
   511  		})
   512  	}
   513  }
   514  
   515  func TestGenerateReviewComment(t *testing.T) {
   516  	testCases := []struct {
   517  		name     string
   518  		location *formats.Location
   519  		cases    []OutputTestCase
   520  	}{
   521  		{
   522  			name: "Review comment structure",
   523  			cases: []OutputTestCase{
   524  				{
   525  					name:               "Standard output",
   526  					writer:             &StandardOutput{},
   527  					expectedOutputPath: []string{filepath.Join(testReviewCommentDir, "review_comment_standard.md")},
   528  				},
   529  				{
   530  					name:               "Simplified output",
   531  					writer:             &SimplifiedOutput{},
   532  					expectedOutputPath: []string{filepath.Join(testReviewCommentDir, "review_comment_simplified.md")},
   533  				},
   534  			},
   535  		},
   536  		{
   537  			name: "Fallback review comment structure",
   538  			location: &formats.Location{
   539  				File:        "file",
   540  				StartLine:   11,
   541  				StartColumn: 22,
   542  				EndLine:     33,
   543  				EndColumn:   44,
   544  				Snippet:     "snippet",
   545  			},
   546  			cases: []OutputTestCase{
   547  				{
   548  					name:               "Standard output",
   549  					writer:             &StandardOutput{},
   550  					expectedOutputPath: []string{filepath.Join(testReviewCommentDir, "review_comment_fallback_standard.md")},
   551  				},
   552  				{
   553  					name:               "Simplified output",
   554  					writer:             &SimplifiedOutput{},
   555  					expectedOutputPath: []string{filepath.Join(testReviewCommentDir, "review_comment_fallback_simplified.md")},
   556  				},
   557  			},
   558  		},
   559  	}
   560  
   561  	content := "\n" + MarkAsCodeSnippet("some review content")
   562  
   563  	for _, tc := range testCases {
   564  		for _, test := range tc.cases {
   565  			t.Run(tc.name+"_"+test.name, func(t *testing.T) {
   566  				expectedOutput := GetExpectedTestOutput(t, test)
   567  				output := GenerateReviewCommentContent(content, test.writer)
   568  				if tc.location != nil {
   569  					output = GetFallbackReviewCommentContent(content, *tc.location, test.writer)
   570  				}
   571  				assert.Equal(t, expectedOutput, output)
   572  			})
   573  		}
   574  	}
   575  }
   576  
   577  func TestApplicableReviewContent(t *testing.T) {
   578  	testCases := []struct {
   579  		name                                                                             string
   580  		severity, finding, fullDetails, cve, cveDetails, impactedDependency, remediation string
   581  		cases                                                                            []OutputTestCase
   582  	}{
   583  		{
   584  			name:               "Applicable CVE review comment content",
   585  			severity:           "Critical",
   586  			finding:            "The vulnerable function flask.Flask.run is called",
   587  			fullDetails:        "The scanner checks whether the vulnerable `Development Server` of the `werkzeug` library is used by looking for calls to `werkzeug.serving.run_simple()`.",
   588  			cve:                "CVE-2022-29361",
   589  			cveDetails:         "cveDetails",
   590  			impactedDependency: "werkzeug:1.0.1",
   591  			remediation:        "some remediation",
   592  			cases: []OutputTestCase{
   593  				{
   594  					name:               "Standard output",
   595  					writer:             &StandardOutput{},
   596  					expectedOutputPath: []string{filepath.Join(testReviewCommentDir, "applicable", "applicable_review_content_standard.md")},
   597  				},
   598  				{
   599  					name:               "Simplified output",
   600  					writer:             &SimplifiedOutput{},
   601  					expectedOutputPath: []string{filepath.Join(testReviewCommentDir, "applicable", "applicable_review_content_simplified.md")},
   602  				},
   603  			},
   604  		},
   605  		{
   606  			name:               "No remediation",
   607  			severity:           "Critical",
   608  			finding:            "The vulnerable function flask.Flask.run is called",
   609  			fullDetails:        "The scanner checks whether the vulnerable `Development Server` of the `werkzeug` library is used by looking for calls to `werkzeug.serving.run_simple()`.",
   610  			cve:                "CVE-2022-29361",
   611  			cveDetails:         "cveDetails",
   612  			impactedDependency: "werkzeug:1.0.1",
   613  			cases: []OutputTestCase{
   614  				{
   615  					name:               "Standard output",
   616  					writer:             &StandardOutput{},
   617  					expectedOutputPath: []string{filepath.Join(testReviewCommentDir, "applicable", "applicable_review_content_no_remediation_standard.md")},
   618  				},
   619  				{
   620  					name:               "Simplified output",
   621  					writer:             &SimplifiedOutput{},
   622  					expectedOutputPath: []string{filepath.Join(testReviewCommentDir, "applicable", "applicable_review_content_no_remediation_simplified.md")},
   623  				},
   624  			},
   625  		},
   626  	}
   627  
   628  	for _, tc := range testCases {
   629  		for _, test := range tc.cases {
   630  			t.Run(tc.name+"_"+test.name, func(t *testing.T) {
   631  				expectedOutput := GetExpectedTestOutput(t, test)
   632  				assert.Equal(t, expectedOutput, ApplicableCveReviewContent(tc.severity, tc.finding, tc.fullDetails, tc.cve, tc.cveDetails, tc.impactedDependency, tc.remediation, test.writer))
   633  			})
   634  		}
   635  	}
   636  }
   637  
   638  func TestIacReviewContent(t *testing.T) {
   639  	testCases := []struct {
   640  		name                           string
   641  		severity, finding, fullDetails string
   642  		cases                          []OutputTestCase
   643  	}{
   644  		{
   645  			name:        "Iac review comment content",
   646  			severity:    "Medium",
   647  			finding:     "Missing auto upgrade was detected",
   648  			fullDetails: "Resource `google_container_node_pool` should have `management.auto_upgrade=true`\n\nVulnerable example - \n```\nresource \"google_container_node_pool\" \"vulnerable_example\" {\n    management {\n     auto_upgrade = false\n   }\n}\n```\n",
   649  			cases: []OutputTestCase{
   650  				{
   651  					name:               "Standard output",
   652  					writer:             &StandardOutput{},
   653  					expectedOutputPath: []string{filepath.Join(testReviewCommentDir, "iac", "iac_review_content_standard.md")},
   654  				},
   655  				{
   656  					name:               "Simplified output",
   657  					writer:             &SimplifiedOutput{},
   658  					expectedOutputPath: []string{filepath.Join(testReviewCommentDir, "iac", "iac_review_content_simplified.md")},
   659  				},
   660  			},
   661  		},
   662  	}
   663  
   664  	for _, tc := range testCases {
   665  		for _, test := range tc.cases {
   666  			t.Run(tc.name+"_"+test.name, func(t *testing.T) {
   667  				expectedOutput := GetExpectedTestOutput(t, test)
   668  				assert.Equal(t, expectedOutput, IacReviewContent(tc.severity, tc.finding, tc.fullDetails, test.writer))
   669  			})
   670  		}
   671  	}
   672  }
   673  
   674  func TestSastReviewContent(t *testing.T) {
   675  	testCases := []struct {
   676  		name        string
   677  		severity    string
   678  		finding     string
   679  		fullDetails string
   680  		codeFlows   [][]formats.Location
   681  		cases       []OutputTestCase
   682  	}{
   683  		{
   684  			name:        "Sast review comment content",
   685  			severity:    "Low",
   686  			finding:     "Stack Trace Exposure",
   687  			fullDetails: "\n### Overview\nStack trace exposure is a type of security vulnerability that occurs when a program reveals\nsensitive information, such as the names and locations of internal files and variables,\nin error messages or other diagnostic output. This can happen when a program crashes or\nencounters an error, and the stack trace (a record of the program's call stack at the time\nof the error) is included in the output.",
   688  			codeFlows: [][]formats.Location{
   689  				{
   690  					{
   691  						File:        "file2",
   692  						StartLine:   1,
   693  						StartColumn: 2,
   694  						EndLine:     3,
   695  						EndColumn:   4,
   696  						Snippet:     "other-snippet",
   697  					},
   698  					{
   699  						File:        "file",
   700  						StartLine:   0,
   701  						StartColumn: 0,
   702  						EndLine:     0,
   703  						EndColumn:   0,
   704  						Snippet:     "snippet",
   705  					},
   706  				},
   707  				{
   708  					{
   709  						File:        "file",
   710  						StartLine:   10,
   711  						StartColumn: 20,
   712  						EndLine:     10,
   713  						EndColumn:   30,
   714  						Snippet:     "a-snippet",
   715  					},
   716  					{
   717  						File:        "file",
   718  						StartLine:   0,
   719  						StartColumn: 0,
   720  						EndLine:     0,
   721  						EndColumn:   0,
   722  						Snippet:     "snippet",
   723  					},
   724  				},
   725  			},
   726  			cases: []OutputTestCase{
   727  				{
   728  					name:               "Standard output",
   729  					writer:             &StandardOutput{},
   730  					expectedOutputPath: []string{filepath.Join(testReviewCommentDir, "sast", "sast_review_content_standard.md")},
   731  				},
   732  				{
   733  					name:               "Simplified output",
   734  					writer:             &SimplifiedOutput{},
   735  					expectedOutputPath: []string{filepath.Join(testReviewCommentDir, "sast", "sast_review_content_simplified.md")},
   736  				},
   737  			},
   738  		},
   739  		{
   740  			name:        "No code flows",
   741  			severity:    "Low",
   742  			finding:     "Stack Trace Exposure",
   743  			fullDetails: "\n### Overview\nStack trace exposure is a type of security vulnerability that occurs when a program reveals\nsensitive information, such as the names and locations of internal files and variables,\nin error messages or other diagnostic output. This can happen when a program crashes or\nencounters an error, and the stack trace (a record of the program's call stack at the time\nof the error) is included in the output.",
   744  			cases: []OutputTestCase{
   745  				{
   746  					name:               "Standard output",
   747  					writer:             &StandardOutput{},
   748  					expectedOutputPath: []string{filepath.Join(testReviewCommentDir, "sast", "sast_review_content_no_code_flow_standard.md")},
   749  				},
   750  				{
   751  					name:               "Simplified output",
   752  					writer:             &SimplifiedOutput{},
   753  					expectedOutputPath: []string{filepath.Join(testReviewCommentDir, "sast", "sast_review_content_no_code_flow_simplified.md")},
   754  				},
   755  			},
   756  		},
   757  	}
   758  
   759  	for _, tc := range testCases {
   760  		for _, test := range tc.cases {
   761  			t.Run(tc.name+"_"+test.name, func(t *testing.T) {
   762  				expectedOutput := GetExpectedTestOutput(t, test)
   763  				assert.Equal(t, expectedOutput, SastReviewContent(tc.severity, tc.finding, tc.fullDetails, tc.codeFlows, test.writer))
   764  			})
   765  		}
   766  	}
   767  }