Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add integration plugin tests #7299

Merged
merged 5 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions integration/plugin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//go:build integration

package integration

import (
"io"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/require"

"github.com/aquasecurity/trivy/pkg/utils/fsutils"
)

func TestPlugin(t *testing.T) {
tests := []struct {
name string
plugin string
pluginArgs string
golden string
}{
{
name: "count plugin installed from `index`",
plugin: "[email protected]",
golden: "testdata/count-0.2.0-plugin.txt.golden",
},
{
name: "count plugin installed from github archive",
plugin: "https://github.com/aquasecurity/trivy-plugin-count/archive/refs/tags/v0.1.0.zip",
pluginArgs: "--published-before=2020-01-01",
golden: "testdata/count-0.1.0-plugin-with-before-flag.txt.golden",
},
}

// Set up testing DB
cacheDir := initDB(t)
tempStdOut := setTempStdout(t)

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// We can overwrite stdout for `_default_Manager` only once.
// So we need to clear the temporary stdout file before each test case.
clearFile(t, tempStdOut)

t.Setenv("XDG_DATA_HOME", t.TempDir())

// Install plugin
err := execute([]string{
"plugin",
"install",
tt.plugin,
})
require.NoError(t, err)

// Get list of plugins
err = execute([]string{
"plugin",
"list",
})
require.NoError(t, err)

// Run Trivy with plugin as output
args := []string{
"--cache-dir",
cacheDir,
"fs",
"-f",
"json",
"-o",
"plugin=count",
"testdata/fixtures/repo/pip",
}

if tt.pluginArgs != "" {
args = append(args, "--output-plugin-arg", tt.pluginArgs)
}

err = execute(args)

if *update {
fsutils.CopyFile(tempStdOut.Name(), tt.golden)
}

compareRawFiles(t, tt.golden, tempStdOut.Name())
})
}
}

func setTempStdout(t *testing.T) *os.File {
tmpFile := filepath.Join(t.TempDir(), "output.txt")
f, err := os.Create(tmpFile)
require.NoError(t, err)

// Overwrite Stdout to get output of plugin
defaultStdout := os.Stdout
os.Stdout = f
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if passing io.Writer here?

app.SetOut(io.Discard)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean

func execute(osArgs []string, out io.Writer) error {
    if out == nil {
        out = io.Discard
    }
    ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to use your idea.
But we use os.Stdout in plugin package.

I used NewManager instead of defaultManager in app.go to use Stdout from cobra.
It works, but there is problem with case when plugin is output.
We need to add output for plugin in Options:

trivy/pkg/flag/options.go

Lines 511 to 533 in 0c6687d

func (o *Options) outputPluginWriter(ctx context.Context) (io.Writer, func() error, error) {
pluginName := strings.TrimPrefix(o.Output, "plugin=")
pr, pw := io.Pipe()
wait, err := plugin.Start(ctx, pluginName, plugin.Options{
Args: o.OutputPluginArgs,
Stdin: pr,
})
if err != nil {
return nil, nil, xerrors.Errorf("plugin start: %w", err)
}
cleanup := func() error {
if err = pw.Close(); err != nil {
return xerrors.Errorf("failed to close pipe: %w", err)
}
if err = wait(); err != nil {
return xerrors.Errorf("plugin error: %w", err)
}
return nil
}
return pw, cleanup, nil
}

i am not sure that we need to do that now.
I would leave this workaround until the tests become more numerous and complex, which would require these changes.

But if you want to do it - tell me and I will continue the research

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, we can improve it later.

t.Cleanup(func() {
os.Stdout = defaultStdout
f.Close()
})
return f
}

func clearFile(t *testing.T, file *os.File) {
_, err := file.Seek(0, io.SeekStart)
require.NoError(t, err)

_, err = file.Write([]byte{})
require.NoError(t, err)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Installed Plugins:
Name: count
Version: 0.1.0

Number of vulnerabilities: 1
5 changes: 5 additions & 0 deletions integration/testdata/count-0.2.0-plugin.txt.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Installed Plugins:
Name: count
Version: 0.2.0

Number of vulnerabilities: 2