Skip to content

Commit

Permalink
🎉 add integration tests
Browse files Browse the repository at this point in the history
Signed-off-by: Salim Afiune Maya <afiune@mondoo.com>
  • Loading branch information
afiune committed Dec 4, 2024
1 parent 75a1872 commit b936e1b
Show file tree
Hide file tree
Showing 9 changed files with 382 additions and 5 deletions.
4 changes: 2 additions & 2 deletions cli/reporter/cnspec_report.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion policy/cnspec_policy.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions policy/scan/scan.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added test/providers/cnspec
Binary file not shown.
317 changes: 317 additions & 0 deletions test/providers/os_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
// Copyright (c) Mondoo, Inc.
// SPDX-License-Identifier: BUSL-1.1

package providers

import (
"log"
"os"
"os/exec"
"path/filepath"
"sync"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.mondoo.com/cnquery/v11/test"
"go.mondoo.com/cnspec/v11/policy"
)

var once sync.Once

// setup builds cnspec locally
func setup() {
// build cnspec
if err := exec.Command("go", "build", "../../apps/cnspec/cnspec.go").Run(); err != nil {
log.Fatalf("building cnspec: %v", err)
}

// // install local provider
// if err := exec.Command("bash", "-c", "cd ../.. && make providers/build/os providers/install/os").Run(); err != nil {
// log.Fatalf("building os provider: %v", err)
// }

providersPATH := os.Getenv("PROVIDERS_PATH")
if providersPATH != "" {

// provider install places the provider in the "$(HOME)/.config/mondoo/providers/${$@_NAME}") but we
// want to test it in isolation. Therefore, we copy the provider to the current directory .providers
osProviderPath := filepath.Join(providersPATH, "os")
if err := os.MkdirAll(osProviderPath, 0755); err != nil {
log.Fatalf("creating directory: %v", err)
}

if err := exec.Command("cp", "-r", os.ExpandEnv("../../providers/os/dist"), osProviderPath).Run(); err != nil {
log.Fatalf("copying provider: %v", err)
}
}
}

const mqlPackagesQuery = "packages"

type mqlPackages []struct {
Packages []struct {
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
} `json:"packages.list,omitempty"`
}

const mqlPlatformQuery = "asset.platform"

type mqlPlatform []struct {
Platform string `json:"asset.platform,omitempty"`
}

type connections []struct {
name string
binary string
args []string
tests []mqlTest
}

type mqlTest struct {
query string
expected func(*testing.T, test.Runner)
}

func TestOsProviderSharedTests(t *testing.T) {
once.Do(setup)

connections := connections{
{
name: "local",
binary: "./cnspec",
args: []string{"run", "local"},
tests: []mqlTest{
{
mqlPackagesQuery,
func(t *testing.T, r test.Runner) {
var c mqlPackages
err := r.Json(&c)
assert.NoError(t, err)

x := c[0]
assert.NotNil(t, x.Packages)
assert.True(t, len(x.Packages) > 0)
},
},
{
mqlPlatformQuery,
func(t *testing.T, r test.Runner) {
var c mqlPlatform
err := r.Json(&c)
assert.NoError(t, err)

x := c[0]
assert.True(t, len(x.Platform) > 0)
},
},
},
},
{
name: "fs",
binary: "./cnspec",
args: []string{"run", "fs", "--path", "./testdata/fs"},
tests: []mqlTest{
{
mqlPackagesQuery,
func(t *testing.T, r test.Runner) {
var c mqlPackages
err := r.Json(&c)
assert.NoError(t, err)

x := c[0]
assert.NotNil(t, x.Packages)
assert.True(t, len(x.Packages) > 0)
},
},
{
mqlPlatformQuery,
func(t *testing.T, r test.Runner) {
var c mqlPlatform
err := r.Json(&c)
assert.NoError(t, err)

x := c[0]
assert.Equal(t, "debian", x.Platform)
},
},
},
},
{
name: "docker",
binary: "./cnspec",
args: []string{"run", "docker", "alpine:latest"},
tests: []mqlTest{
{
mqlPackagesQuery,
func(t *testing.T, r test.Runner) {
var c mqlPackages
err := r.Json(&c)
assert.NoError(t, err)

x := c[0]
assert.NotNil(t, x.Packages)
assert.True(t, len(x.Packages) > 0)
},
},
{
mqlPlatformQuery,
func(t *testing.T, r test.Runner) {
var c mqlPlatform
err := r.Json(&c)
assert.NoError(t, err)

x := c[0]
assert.Equal(t, "alpine", x.Platform)
},
},
},
},
}

// iterate over all tests for all connections
for _, cc := range connections {
for _, tt := range cc.tests {

t.Run(cc.name+"/"+tt.query, func(t *testing.T) {
r := test.NewCliTestRunner(cc.binary, append(cc.args, "-c", tt.query, "-j")...)
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 0, r.ExitCode())
assert.NotNil(t, r.Stdout())
assert.NotNil(t, r.Stderr())

tt.expected(t, r)
})
}
}
}

func TestProvidersEnvVarsLoading(t *testing.T) {
t.Run("command WITHOUT path should not find any package", func(t *testing.T) {
r := test.NewCliTestRunner("./cnspec", "run", "fs", "-c", mqlPackagesQuery, "-j")
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 0, r.ExitCode())
assert.NotNil(t, r.Stdout())
assert.NotNil(t, r.Stderr())

var c mqlPackages
err = r.Json(&c)
assert.NoError(t, err)

// No packages
assert.Empty(t, c)
})
t.Run("command WITH path should find packages", func(t *testing.T) {
os.Setenv("MONDOO_PATH", "./testdata/fs")
defer os.Unsetenv("MONDOO_PATH")
// Note we are not passing the flag "--path ./testdata/fs"
r := test.NewCliTestRunner("./cnspec", "run", "fs", "-c", mqlPackagesQuery, "-j")
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 0, r.ExitCode())
assert.NotNil(t, r.Stdout())
assert.NotNil(t, r.Stderr())

var c mqlPackages
err = r.Json(&c)
assert.NoError(t, err)

// Should have packages
if assert.NotEmpty(t, c) {
x := c[0]
assert.NotNil(t, x.Packages)
assert.True(t, len(x.Packages) > 0)
}
})

t.Run("command with flags set to not bind to config (ConfigEntry=\"-\")", func(t *testing.T) {
t.Run("should work via direct flag", func(t *testing.T) {
r := test.NewCliTestRunner("./cnspec", "run", "ssh", "localhost", "-c", "ls", "-p", "test", "-v")
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 0, r.ExitCode())
assert.NotNil(t, r.Stdout())
if assert.NotNil(t, r.Stderr()) {
assert.Contains(t, string(r.Stderr()), "skipping config binding for password")
assert.Contains(t, string(r.Stderr()), "enabled ssh password authentication")
}
})
t.Run("should NOT work via config/env-vars", func(t *testing.T) {
os.Setenv("MONDOO_PASSWORD", "test")
defer os.Unsetenv("MONDOO_PASSWORD")
r := test.NewCliTestRunner("./cnspec", "run", "ssh", "localhost", "-c", "ls", "-v")
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 0, r.ExitCode())
assert.NotNil(t, r.Stdout())
if assert.NotNil(t, r.Stderr()) {
assert.Contains(t, string(r.Stderr()), "skipping config binding for password")
assert.NotContains(t, string(r.Stderr()), "enabled ssh password authentication")
}
})
})
}

func TestScanFlags(t *testing.T) {
t.Run("successful scan without flags", func(t *testing.T) {
r := test.NewCliTestRunner("./cnspec", "scan", "docker", "alpine:latest", "--json")
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 0, r.ExitCode())
assert.NotNil(t, r.Stdout())
assert.NotNil(t, r.Stderr())

var c policy.ReportCollection
err = r.Json(&c)
assert.NoError(t, err)

// Assest must be found
assert.NotEmpty(t, c.Assets)
})
t.Run("github scan WITHOUT flags", func(t *testing.T) {
// NOTE this will fail but, it will load the flags and fail with the right message
r := test.NewCliTestRunner("./cnspec", "scan", "github", "repo", "foo")
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 0, r.ExitCode())
assert.NotNil(t, r.Stdout())
assert.NotNil(t, r.Stderr())

assert.Contains(t, string(r.Stderr()),
"a valid GitHub authentication is required",
)
})
t.Run("github scan WITH flags but missing app auth key", func(t *testing.T) {
// NOTE this will fail but, it will load the flags and fail with the right message
r := test.NewCliTestRunner("./cnspec", "scan", "github", "repo", "foo",
"--app-id", "123", "--app-installation-id", "456",
)
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 1, r.ExitCode())
assert.NotNil(t, r.Stdout())
assert.NotNil(t, r.Stderr())

assert.Contains(t, string(r.Stderr()),
"app-private-key is required for GitHub App authentication", // expected! it means we loaded the flags
)
})
t.Run("github scan WITH all required flags for app auth", func(t *testing.T) {
// NOTE this will fail but, it will load the flags and fail with the right message
r := test.NewCliTestRunner("./cnspec", "scan", "github", "repo", "foo",
"--app-id", "123", "--app-installation-id", "456", "--app-private-key", "private-key.pem",
)
err := r.Run()
require.NoError(t, err)
assert.Equal(t, 1, r.ExitCode())
assert.NotNil(t, r.Stdout())
assert.NotNil(t, r.Stderr())

assert.Contains(t, string(r.Stderr()),
"could not read private key", // expected! it means we loaded the flags
)
})
}
1 change: 1 addition & 0 deletions test/providers/testdata/fs/etc/debian_version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
12.5
1 change: 1 addition & 0 deletions test/providers/testdata/fs/etc/hostname
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
debianfs
9 changes: 9 additions & 0 deletions test/providers/testdata/fs/etc/os-release
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
Loading

0 comments on commit b936e1b

Please sign in to comment.