mirror of
https://github.com/github/codeql.git
synced 2026-02-12 05:01:06 +01:00
Merge pull request #21214 from github/mbg/go/diagnostics-unit-tests
Go: Make diagnostics unit-testable and add test for `EmitCannotFindPackages`
This commit is contained in:
9
go/extractor/diagnostics/BUILD.bazel
generated
9
go/extractor/diagnostics/BUILD.bazel
generated
@@ -1,6 +1,6 @@
|
||||
# generated running `bazel run //go/gazelle`, do not edit
|
||||
|
||||
load("@rules_go//go:def.bzl", "go_library")
|
||||
load("@rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "diagnostics",
|
||||
@@ -9,3 +9,10 @@ go_library(
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//go/extractor/util"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "diagnostics_test",
|
||||
srcs = ["diagnostics_test.go"],
|
||||
embed = [":diagnostics"],
|
||||
deps = ["@com_github_stretchr_testify//assert"],
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ package diagnostics
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -56,19 +56,66 @@ type diagnostic struct {
|
||||
var diagnosticsEmitted, diagnosticsLimit uint = 0, 100
|
||||
var noDiagnosticDirPrinted bool = false
|
||||
|
||||
func emitDiagnostic(sourceid, sourcename, markdownMessage string, severity diagnosticSeverity, visibility *visibilityStruct, location *locationStruct) {
|
||||
type DiagnosticsWriter interface {
|
||||
WriteDiagnostic(d diagnostic)
|
||||
}
|
||||
|
||||
type FileDiagnosticsWriter struct {
|
||||
diagnosticDir string
|
||||
}
|
||||
|
||||
func (writer *FileDiagnosticsWriter) WriteDiagnostic(d diagnostic) {
|
||||
if writer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := json.Marshal(d)
|
||||
if err != nil {
|
||||
slog.Error("Failed to encode diagnostic as JSON", slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
|
||||
targetFile, err := os.CreateTemp(writer.diagnosticDir, "go-extractor.*.json")
|
||||
if err != nil {
|
||||
slog.Error("Failed to create diagnostic file", slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := targetFile.Close(); err != nil {
|
||||
slog.Error("Failed to close diagnostic file", slog.Any("err", err))
|
||||
}
|
||||
}()
|
||||
|
||||
_, err = targetFile.Write(content)
|
||||
if err != nil {
|
||||
slog.Error("Failed to write to diagnostic file", slog.Any("err", err))
|
||||
}
|
||||
}
|
||||
|
||||
var DefaultWriter *FileDiagnosticsWriter = nil
|
||||
|
||||
func NewFileDiagnosticsWriter() *FileDiagnosticsWriter {
|
||||
diagnosticDir := os.Getenv("CODEQL_EXTRACTOR_GO_DIAGNOSTIC_DIR")
|
||||
if diagnosticDir == "" {
|
||||
if !noDiagnosticDirPrinted {
|
||||
slog.Warn("No diagnostic directory set, so not emitting diagnostics")
|
||||
noDiagnosticDirPrinted = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return &FileDiagnosticsWriter{diagnosticDir}
|
||||
}
|
||||
|
||||
func init() {
|
||||
DefaultWriter = NewFileDiagnosticsWriter()
|
||||
}
|
||||
|
||||
// Emits a diagnostic using the specified `DiagnosticsWriter`.
|
||||
func emitDiagnosticTo(writer DiagnosticsWriter, sourceid, sourcename, markdownMessage string, severity diagnosticSeverity, visibility *visibilityStruct, location *locationStruct) {
|
||||
if diagnosticsEmitted < diagnosticsLimit {
|
||||
diagnosticsEmitted += 1
|
||||
|
||||
diagnosticDir := os.Getenv("CODEQL_EXTRACTOR_GO_DIAGNOSTIC_DIR")
|
||||
if diagnosticDir == "" {
|
||||
if !noDiagnosticDirPrinted {
|
||||
log.Println("No diagnostic directory set, so not emitting diagnostic")
|
||||
noDiagnosticDirPrinted = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
timestamp := time.Now().UTC().Format("2006-01-02T15:04:05.000") + "Z"
|
||||
|
||||
var d diagnostic
|
||||
@@ -93,33 +140,15 @@ func emitDiagnostic(sourceid, sourcename, markdownMessage string, severity diagn
|
||||
}
|
||||
}
|
||||
|
||||
content, err := json.Marshal(d)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
targetFile, err := os.CreateTemp(diagnosticDir, "go-extractor.*.json")
|
||||
if err != nil {
|
||||
log.Println("Failed to create diagnostic file: ")
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := targetFile.Close(); err != nil {
|
||||
log.Println("Failed to close diagnostic file:")
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
_, err = targetFile.Write(content)
|
||||
if err != nil {
|
||||
log.Println("Failed to write to diagnostic file: ")
|
||||
log.Println(err)
|
||||
}
|
||||
writer.WriteDiagnostic(d)
|
||||
}
|
||||
}
|
||||
|
||||
// Emits a diagnostic using the default `DiagnosticsWriter`.
|
||||
func emitDiagnostic(sourceid, sourcename, markdownMessage string, severity diagnosticSeverity, visibility *visibilityStruct, location *locationStruct) {
|
||||
emitDiagnosticTo(DefaultWriter, sourceid, sourcename, markdownMessage, severity, visibility, location)
|
||||
}
|
||||
|
||||
func EmitPackageDifferentOSArchitecture(pkgPath string) {
|
||||
emitDiagnostic(
|
||||
"go/autobuilder/package-different-os-architecture",
|
||||
@@ -141,7 +170,7 @@ func plural(n int, singular, plural string) string {
|
||||
|
||||
const maxNumPkgPaths = 5
|
||||
|
||||
func EmitCannotFindPackages(pkgPaths []string) {
|
||||
func EmitCannotFindPackages(writer DiagnosticsWriter, pkgPaths []string) {
|
||||
numPkgPaths := len(pkgPaths)
|
||||
|
||||
numPrinted := numPkgPaths
|
||||
@@ -188,7 +217,8 @@ func EmitCannotFindPackages(pkgPaths []string) {
|
||||
"If any of the packages are already present in the repository, but were not found, then you may need a [custom build command](https://docs.github.com/en/code-security/how-tos/scan-code-for-vulnerabilities/manage-your-configuration/codeql-code-scanning-for-compiled-languages)."
|
||||
}
|
||||
|
||||
emitDiagnostic(
|
||||
emitDiagnosticTo(
|
||||
writer,
|
||||
"go/autobuilder/package-not-found",
|
||||
"Some packages could not be found",
|
||||
message,
|
||||
|
||||
85
go/extractor/diagnostics/diagnostics_test.go
Normal file
85
go/extractor/diagnostics/diagnostics_test.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package diagnostics
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type memoryDiagnosticsWriter struct {
|
||||
diagnostics []diagnostic
|
||||
}
|
||||
|
||||
func newMemoryDiagnosticsWriter() *memoryDiagnosticsWriter {
|
||||
return &memoryDiagnosticsWriter{[]diagnostic{}}
|
||||
}
|
||||
|
||||
func (writer *memoryDiagnosticsWriter) WriteDiagnostic(d diagnostic) {
|
||||
writer.diagnostics = append(writer.diagnostics, d)
|
||||
}
|
||||
|
||||
func Test_EmitCannotFindPackages_Default(t *testing.T) {
|
||||
writer := newMemoryDiagnosticsWriter()
|
||||
|
||||
// Clear environment variables that affect the diagnostic message.
|
||||
t.Setenv("GITHUB_EVENT_NAME", "")
|
||||
t.Setenv("GITHUB_ACTIONS", "")
|
||||
|
||||
EmitCannotFindPackages(writer, []string{"github.com/github/foo"})
|
||||
|
||||
assert.Len(t, writer.diagnostics, 1, "Expected one diagnostic to be emitted")
|
||||
|
||||
d := writer.diagnostics[0]
|
||||
assert.Equal(t, d.Source.Id, "go/autobuilder/package-not-found")
|
||||
assert.Equal(t, d.Severity, string(severityWarning))
|
||||
assert.True(t, d.Visibility.CliSummaryTable)
|
||||
assert.True(t, d.Visibility.StatusPage)
|
||||
assert.True(t, d.Visibility.Telemetry)
|
||||
// Non-Actions suggestion for private registries
|
||||
assert.Contains(t, d.MarkdownMessage, "ensure that the necessary credentials and environment variables are set up")
|
||||
// Custom build command suggestion
|
||||
assert.Contains(t, d.MarkdownMessage, "If any of the packages are already present in the repository")
|
||||
}
|
||||
|
||||
func Test_EmitCannotFindPackages_Dynamic(t *testing.T) {
|
||||
writer := newMemoryDiagnosticsWriter()
|
||||
|
||||
// Set environment variables that affect the diagnostic message.
|
||||
t.Setenv("GITHUB_EVENT_NAME", "dynamic")
|
||||
t.Setenv("GITHUB_ACTIONS", "true")
|
||||
|
||||
EmitCannotFindPackages(writer, []string{"github.com/github/foo"})
|
||||
|
||||
assert.Len(t, writer.diagnostics, 1, "Expected one diagnostic to be emitted")
|
||||
|
||||
d := writer.diagnostics[0]
|
||||
assert.Equal(t, d.Source.Id, "go/autobuilder/package-not-found")
|
||||
assert.Equal(t, d.Severity, string(severityWarning))
|
||||
// Dynamic workflow suggestion for private registries
|
||||
assert.Contains(t, d.MarkdownMessage, "can grant access to private registries for GitHub security products")
|
||||
// No default suggestions for private registries and custom build command
|
||||
assert.NotContains(t, d.MarkdownMessage, "ensure that the necessary credentials and environment variables are set up")
|
||||
assert.NotContains(t, d.MarkdownMessage, "If any of the packages are already present in the repository")
|
||||
}
|
||||
|
||||
func Test_EmitCannotFindPackages_Actions(t *testing.T) {
|
||||
writer := newMemoryDiagnosticsWriter()
|
||||
|
||||
// Set environment variables that affect the diagnostic message.
|
||||
t.Setenv("GITHUB_EVENT_NAME", "push")
|
||||
t.Setenv("GITHUB_ACTIONS", "true")
|
||||
|
||||
EmitCannotFindPackages(writer, []string{"github.com/github/foo"})
|
||||
|
||||
assert.Len(t, writer.diagnostics, 1, "Expected one diagnostic to be emitted")
|
||||
|
||||
d := writer.diagnostics[0]
|
||||
assert.Equal(t, d.Source.Id, "go/autobuilder/package-not-found")
|
||||
assert.Equal(t, d.Severity, string(severityWarning))
|
||||
// Advanced workflow suggestion for private registries
|
||||
assert.Contains(t, d.MarkdownMessage, "add a step to your workflow which sets up")
|
||||
// No default suggestion for private registries
|
||||
assert.NotContains(t, d.MarkdownMessage, "ensure that the necessary credentials and environment variables are set up")
|
||||
// Custom build command suggestion
|
||||
assert.Contains(t, d.MarkdownMessage, "If any of the packages are already present in the repository")
|
||||
}
|
||||
@@ -223,7 +223,7 @@ func ExtractWithFlags(buildFlags []string, patterns []string, extractTests bool,
|
||||
})
|
||||
|
||||
if len(pkgsNotFound) > 0 {
|
||||
diagnostics.EmitCannotFindPackages(pkgsNotFound)
|
||||
diagnostics.EmitCannotFindPackages(diagnostics.DefaultWriter, pkgsNotFound)
|
||||
}
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
|
||||
@@ -13,4 +13,10 @@ require (
|
||||
golang.org/x/tools v0.41.0
|
||||
)
|
||||
|
||||
require golang.org/x/sync v0.19.0 // indirect
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.11.1 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
Reference in New Issue
Block a user