From a762812285f9bc69dcb51e5f7f0ff79f32d60407 Mon Sep 17 00:00:00 2001 From: Spencer Schrock Date: Tue, 6 Feb 2024 12:19:17 -0800 Subject: [PATCH] :seedling: Add tests for probe output format (#3841) * rename probe format file Signed-off-by: Spencer Schrock * add option parameter to probe result output function the ability to pretty print will help write a test function Signed-off-by: Spencer Schrock * add test func Signed-off-by: Spencer Schrock * add docstring Signed-off-by: Spencer Schrock * clarify test. add another finding Signed-off-by: Spencer Schrock * force add the test file Signed-off-by: Spencer Schrock --------- Signed-off-by: Spencer Schrock --- pkg/{json_probe_results.go => probe.go} | 14 +++- pkg/probe_test.go | 88 +++++++++++++++++++++++++ pkg/scorecard_result.go | 3 +- pkg/testdata/probe1.json | 27 ++++++++ 4 files changed, 130 insertions(+), 2 deletions(-) rename pkg/{json_probe_results.go => probe.go} (75%) create mode 100644 pkg/probe_test.go create mode 100644 pkg/testdata/probe1.json diff --git a/pkg/json_probe_results.go b/pkg/probe.go similarity index 75% rename from pkg/json_probe_results.go rename to pkg/probe.go index 386d0dc94d7..e3bf5f3987f 100644 --- a/pkg/json_probe_results.go +++ b/pkg/probe.go @@ -31,7 +31,15 @@ type JSONScorecardProbeResult struct { Findings []finding.Finding `json:"findings"` } -func (r *ScorecardResult) AsPJSON(writer io.Writer) error { +// ProbeResultOption provides configuration options for the ScorecardResult probe output format. +type ProbeResultOption struct { + // Indent is used to control the JSON indentation. For example, if you want to pretty print. + Indent string +} + +// AsPJSON writes results as JSON for flat findings without checks. +// It accepts an optional argument to configure the output. +func (r *ScorecardResult) AsPJSON(writer io.Writer, o *ProbeResultOption) error { encoder := json.NewEncoder(writer) out := JSONScorecardProbeResult{ Repo: jsonRepoV2{ @@ -46,6 +54,10 @@ func (r *ScorecardResult) AsPJSON(writer io.Writer) error { Findings: r.Findings, } + if o != nil { + encoder.SetIndent("", o.Indent) + } + if err := encoder.Encode(out); err != nil { return sce.WithMessage(sce.ErrScorecardInternal, fmt.Sprintf("encoder.Encode: %v", err)) } diff --git a/pkg/probe_test.go b/pkg/probe_test.go new file mode 100644 index 00000000000..b89450f59d5 --- /dev/null +++ b/pkg/probe_test.go @@ -0,0 +1,88 @@ +// Copyright 2024 OpenSSF Scorecard Authors +// +// 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. + +package pkg + +import ( + "bytes" + "os" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + + "github.com/ossf/scorecard/v4/finding" +) + +func Test_AsPJSON(t *testing.T) { + t.Parallel() + tests := []struct { + name string + expected string + result ScorecardResult + }{ + { + name: "multiple findings displayed", + expected: "./testdata/probe1.json", + result: ScorecardResult{ + Repo: RepoInfo{ + Name: "foo", + CommitSHA: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + }, + Scorecard: ScorecardInfo{ + Version: "1.2.3", + CommitSHA: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + }, + Date: time.Date(2024, time.February, 1, 13, 48, 0, 0, time.UTC), + Findings: []finding.Finding{ + { + Probe: "check for X", + Outcome: finding.OutcomePositive, + Message: "found X", + Location: &finding.Location{ + Path: "some/path/to/file", + Type: finding.FileTypeText, + }, + }, + { + Probe: "check for Y", + Outcome: finding.OutcomeNegative, + Message: "did not find Y", + }, + }, + }, + }, + } + // pretty print results so the test files are easier to read + opt := &ProbeResultOption{Indent: " "} + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + expected, err := os.ReadFile(tt.expected) + if err != nil { + t.Fatalf("cannot read expected results file: %v", err) + } + + var result bytes.Buffer + if err = tt.result.AsPJSON(&result, opt); err != nil { + t.Fatalf("AsPJSON: %v", err) + } + + if diff := cmp.Diff(expected, result.Bytes()); diff != "" { + t.Errorf("results differ: %s", diff) + } + }) + } +} diff --git a/pkg/scorecard_result.go b/pkg/scorecard_result.go index 102f4e3ec0a..4d6a3176df4 100644 --- a/pkg/scorecard_result.go +++ b/pkg/scorecard_result.go @@ -138,7 +138,8 @@ func FormatResults( case options.FormatFJSON: err = results.AsFJSON(opts.ShowDetails, log.ParseLevel(opts.LogLevel), doc, output) case options.FormatPJSON: - err = results.AsPJSON(output) + var opts *ProbeResultOption + err = results.AsPJSON(output, opts) case options.FormatRaw: err = results.AsRawJSON(output) default: diff --git a/pkg/testdata/probe1.json b/pkg/testdata/probe1.json new file mode 100644 index 00000000000..8666ab81a83 --- /dev/null +++ b/pkg/testdata/probe1.json @@ -0,0 +1,27 @@ +{ + "date": "2024-02-01", + "repo": { + "name": "foo", + "commit": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "scorecard": { + "version": "1.2.3", + "commit": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "findings": [ + { + "location": { + "path": "some/path/to/file", + "type": 3 + }, + "probe": "check for X", + "message": "found X", + "outcome": 12 + }, + { + "probe": "check for Y", + "message": "did not find Y", + "outcome": 0 + } + ] +}