Repository: google/osv-scanner
Pull request #554 (2023-09-26 20:26:38)

ci: combine lint and test workflows
While working on other stuff I noticed how spread out the workflows are - personally I find it's easier to maintain a small number of large workflows than a large number of smaller ones, because jobs are often shared; I guess an alternative way of framing its about focusing on the actual _workflow_ rather than each action (i.e. you have a "checks" workflow which runs all jobs for checking PRs, a "release" workflow which has all the jobs you want to run for a release, etc).

I think there a few more improvements that could be made, but for now this I've just merged the `test` and `lint` workflows as they feel the most superfluous

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/google/osv-scanner/pull/554?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) Report
> Merging [#554](https://app.codecov.io/gh/google/osv-scanner/pull/554?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (80f8839) into [main](https://app.codecov.io/gh/google/osv-scanner/commit/5862bfd4c6b7a0b77609fd7b5f07701f3dafb92c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (5862bfd) will **not change** coverage.
> The diff coverage is `n/a`.

```diff
@@           Coverage Diff           @@
##             main     #554   +/-   ##
=======================================
  Coverage   78.45%   78.45%           
=======================================
  Files          77       77           
  Lines        5273     5273           
=======================================
  Hits         4137     4137           
  Misses        971      971           
  Partials      165      165           
```





Patch:
diff --git a/.github/workflows/lint.yaml b/.github/workflows/checks.yml
similarity index 71%
rename from .github/workflows/lint.yaml
rename to .github/workflows/checks.yml
index a616a49..fed714b 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/checks.yml
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-name: lint
+name: Checks
 
 on:
   push:
@@ -43,3 +43,19 @@ jobs:
           check-latest: true
       - name: Run lint action
         uses: ./.github/workflows/lint-action
+  test:
+    name: Run unit tests
+    runs-on: ubuntu-latest
+    steps:
+      - name: Check out code
+        uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
+        with:
+          persist-credentials: false
+          fetch-depth: 0
+      - name: Set up Go
+        uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
+        with:
+          go-version: '1.19'
+          check-latest: true
+      - name: Run test action
+        uses: ./.github/workflows/test-action


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
deleted file mode 100644
index de4a180..0000000
--- a/.github/workflows/test.yml
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2023 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-name: test
-
-on:
-  push:
-    branches: [ main ]
-  pull_request:
-    # The branches below must be a subset of the branches above
-    branches: [ main ]
-  merge_group:
-    branches: [ main ]
-
-permissions:
-  contents: read # to fetch code (actions/checkout)
-
-jobs:
-  lint:
-    name: Run unit tests
-    runs-on: ubuntu-latest
-    steps:
-      - name: Check out code
-        uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
-        with:
-          persist-credentials: false
-          fetch-depth: 0
-      - name: Set up Go
-        uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
-        with:
-          go-version: '1.19'
-          check-latest: true
-      - name: Run test action
-        uses: ./.github/workflows/test-action
diff --git a/cmd/osv-scanner/main_test.go b/cmd/osv-scanner/main_test.go
index 103617a..f12f275 100644
--- a/cmd/osv-scanner/main_test.go
+++ b/cmd/osv-scanner/main_test.go
@@ -446,6 +446,16 @@ func TestRun(t *testing.T) {
 			`,
 			wantStderr: "",
 		},
+		// output format: unsupported
+		{
+			name:         "",
+			args:         []string{"", "--format", "unknown", "./fixtures/locks-many/composer.lock"},
+			wantExitCode: 127,
+			wantStdout:   "",
+			wantStderr: `
+				unsupported output format "unknown" - must be one of: table, json, markdown, sarif, gh-annotations
+			`,
+		},
 	}
 	for _, tt := range tests {
 		tt := tt
diff --git a/pkg/config/config_internal_test.go b/pkg/config/config_internal_test.go
index 95e3bee..e271543 100644
--- a/pkg/config/config_internal_test.go
+++ b/pkg/config/config_internal_test.go
@@ -2,7 +2,9 @@ package config
 
 import (
 	"path/filepath"
+	"reflect"
 	"testing"
+	"time"
 
 	"github.com/google/go-cmp/cmp"
 )
@@ -74,3 +76,118 @@ func TestTryLoadConfig(t *testing.T) {
 		}
 	}
 }
+
+func TestConfig_ShouldIgnore(t *testing.T) {
+	t.Parallel()
+
+	type args struct {
+		vulnID string
+	}
+	tests := []struct {
+		name      string
+		config    Config
+		args      args
+		wantOk    bool
+		wantEntry IgnoreEntry
+	}{
+		// entry exists
+		{
+			name: "",
+			config: Config{
+				IgnoredVulns: []IgnoreEntry{
+					{
+						ID:          "GHSA-123",
+						IgnoreUntil: time.Time{},
+						Reason:      "",
+					},
+				},
+			},
+			args: args{
+				vulnID: "GHSA-123",
+			},
+			wantOk: true,
+			wantEntry: IgnoreEntry{
+				ID:          "GHSA-123",
+				IgnoreUntil: time.Time{},
+				Reason:      "",
+			},
+		},
+		// entry does not exist
+		{
+			name: "",
+			config: Config{
+				IgnoredVulns: []IgnoreEntry{
+					{
+						ID:          "GHSA-123",
+						IgnoreUntil: time.Time{},
+						Reason:      "",
+					},
+				},
+			},
+			args: args{
+				vulnID: "nonexistent",
+			},
+			wantOk:    false,
+			wantEntry: IgnoreEntry{},
+		},
+		// ignored until a time in the past
+		{
+			name: "",
+			config: Config{
+				IgnoredVulns: []IgnoreEntry{
+					{
+						ID:          "GHSA-123",
+						IgnoreUntil: time.Now().Add(-time.Hour).Round(time.Second),
+						Reason:      "",
+					},
+				},
+			},
+			args: args{
+				vulnID: "GHSA-123",
+			},
+			wantOk: false,
+			wantEntry: IgnoreEntry{
+				ID:          "GHSA-123",
+				IgnoreUntil: time.Now().Add(-time.Hour).Round(time.Second),
+				Reason:      "",
+			},
+		},
+		// ignored until a time in the future
+		{
+			name: "",
+			config: Config{
+				IgnoredVulns: []IgnoreEntry{
+					{
+						ID:          "GHSA-123",
+						IgnoreUntil: time.Now().Add(time.Hour).Round(time.Second),
+						Reason:      "",
+					},
+				},
+			},
+			args: args{
+				vulnID: "GHSA-123",
+			},
+			wantOk: true,
+			wantEntry: IgnoreEntry{
+				ID:          "GHSA-123",
+				IgnoreUntil: time.Now().Add(time.Hour).Round(time.Second),
+				Reason:      "",
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+
+			gotOk, gotEntry := tt.config.ShouldIgnore(tt.args.vulnID)
+			if gotOk != tt.wantOk {
+				t.Errorf("ShouldIgnore() gotOk = %v, wantOk %v", gotOk, tt.wantOk)
+			}
+			if !reflect.DeepEqual(gotEntry, tt.wantEntry) {
+				t.Errorf("ShouldIgnore() gotEntry = %v, wantEntry %v", gotEntry, tt.wantEntry)
+			}
+		})
+	}
+}
diff --git a/pkg/lockfile/extract_test.go b/pkg/lockfile/extract_test.go
index a05042e..b014e1e 100644
--- a/pkg/lockfile/extract_test.go
+++ b/pkg/lockfile/extract_test.go
@@ -134,6 +134,20 @@ func TestExtractDeps_ExtractorNotFound(t *testing.T) {
 	}
 }
 
+func TestExtractDeps_ExtractorNotFound_WithExplicitExtractAs(t *testing.T) {
+	t.Parallel()
+
+	_, err := lockfile.ExtractDeps(openTestDepFile("/path/to/my/"), "unsupported")
+
+	if err == nil {
+		t.Errorf("Expected to get an error but did not")
+	}
+
+	if !errors.Is(err, lockfile.ErrExtractorNotFound) {
+		t.Errorf("Did not get the expected ErrExtractorNotFound error - got %v instead", err)
+	}
+}
+
 func TestListExtractors(t *testing.T) {
 	t.Parallel()
 
diff --git a/pkg/lockfile/parse_test.go b/pkg/lockfile/parse_test.go
index cdda673..8d5ecae 100644
--- a/pkg/lockfile/parse_test.go
+++ b/pkg/lockfile/parse_test.go
@@ -143,6 +143,20 @@ func TestParse_ParserNotFound(t *testing.T) {
 	}
 }
 
+func TestParse_ParserNotFound_WithExplicitParseAs(t *testing.T) {
+	t.Parallel()
+
+	_, err := lockfile.Parse("/path/to/my/", "unsupported")
+
+	if err == nil {
+		t.Errorf("Expected to get an error but did not")
+	}
+
+	if !errors.Is(err, lockfile.ErrParserNotFound) {
+		t.Errorf("Did not get the expected ErrParserNotFound error - got %v instead", err)
+	}
+}
+
 func TestListParsers(t *testing.T) {
 	t.Parallel()
 
diff --git a/pkg/reporter/format_test.go b/pkg/reporter/format_test.go
index 027356c..8e385f6 100644
--- a/pkg/reporter/format_test.go
+++ b/pkg/reporter/format_test.go
@@ -7,7 +7,7 @@ import (
 	"github.com/google/osv-scanner/pkg/reporter"
 )
 
-func TestGetReporter(t *testing.T) {
+func TestNew(t *testing.T) {
 	t.Parallel()
 
 	for _, format := range reporter.Format() {
@@ -20,3 +20,16 @@ func TestGetReporter(t *testing.T) {
 		}
 	}
 }
+
+func TestNew_UnsupportedFormatter(t *testing.T) {
+	t.Parallel()
+
+	stdout := &bytes.Buffer{}
+	stderr := &bytes.Buffer{}
+
+	_, err := reporter.New("unsupported", stdout, stderr, 0)
+
+	if err == nil {
+		t.Errorf("Did not get expected error")
+	}
+}