Skip to content

Commit

Permalink
🔧 add testing for providers (#1729)
Browse files Browse the repository at this point in the history
This creates the `mockprovider`, a way to test resources and fields that
behave in non-standard ways, like returning null-values where we would
expect to see a resource returned. They avoid polluting providers with
fields and data that we may only need for testing.

---------

Signed-off-by: Dominik Richter <[email protected]>
  • Loading branch information
arlimus authored Sep 14, 2023
1 parent b47e80e commit 909b8d3
Show file tree
Hide file tree
Showing 10 changed files with 585 additions and 2 deletions.
4 changes: 4 additions & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,7 @@ vmknics
Vtpm
vulnerabilityassessmentsettings
wil
mgroup
muser
testutils
nullgroup
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ gha-creds-*.json
providers/*/dist
providers/*/resources/*.resources.json
providers/*/resources/*.manifest.json
providers-sdk/*/testutils/mockprovider/resources/*.resources.json
!providers/core/resources/*.resources.json
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ providers/lr:

.PHONY: providers/build
# Note we need \ to escape the target line into multiple lines
providers/build: providers/build/core \
providers/build: providers/build/mock \
providers/build/core \
providers/build/network \
providers/build/os \
providers/build/ipmi \
Expand All @@ -173,6 +174,9 @@ providers/build: providers/build/core \
providers/build/ms365 \
providers/build/aws

providers/build/mock: providers/lr
./lr go providers-sdk/v1/testutils/mockprovider/resources/mockprovider.lr

providers/build/core: providers/lr
@$(call buildProvider, providers/core)

Expand Down
35 changes: 35 additions & 0 deletions mql/mql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"go.mondoo.com/cnquery/llx"
"go.mondoo.com/cnquery/mql"
"go.mondoo.com/cnquery/providers-sdk/v1/testutils"
"go.mondoo.com/cnquery/types"
)

var features cnquery.Features
Expand Down Expand Up @@ -154,3 +155,37 @@ func TestResourceAliases(t *testing.T) {
},
})
}

func TestNullResources(t *testing.T) {
x := testutils.InitTester(testutils.LinuxMock())
x.TestSimple(t, []testutils.SimpleTest{
{
Code: "muser.group",
ResultIndex: 0,
Expectation: &llx.MockResource{Name: "mgroup", ID: "group one"},
},
{
Code: "muser.nullgroup",
ResultIndex: 0,
Expectation: nil,
},
{
Code: "muser.groups",
ResultIndex: 0,
Expectation: []interface{}{
&llx.MockResource{Name: "mgroup", ID: "group one"},
nil,
},
},
{
Code: "muser { nullgroup }",
ResultIndex: 0,
Expectation: map[string]interface{}{
"_": &llx.RawData{Type: types.Resource("muser"), Value: &llx.MockResource{Name: "muser"}},
"__s": llx.NilData,
"__t": llx.BoolTrue,
"A8qiFMpyfjKsr3OzVu+L+43W0BvYXoCPiwM7zu8AFQkBYEBMvZfR73ZsdfIqswmN1n9Qs/Soc1D7qxJipXv/ZA==": llx.ResourceData(nil, "mgroup"),
},
},
})
}
130 changes: 130 additions & 0 deletions providers-sdk/v1/testutils/mockprovider/mockprovider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright (c) Mondoo, Inc.
// SPDX-License-Identifier: BUSL-1.1

package mockprovider

import (
"errors"
"strconv"
"strings"

"go.mondoo.com/cnquery/llx"
"go.mondoo.com/cnquery/providers-sdk/v1/plugin"
"go.mondoo.com/cnquery/providers-sdk/v1/testutils/mockprovider/resources"
)

var Config = plugin.Provider{
Name: "mock",
ID: "go.mondoo.com/cnquery/providers-sdk/v1/testutils/mockprovider",
Version: "0.0.0",
Connectors: []plugin.Connector{},
}

type Service struct {
runtimes map[uint32]*plugin.Runtime
lastConnectionID uint32
}

func Init() *Service {
return &Service{
runtimes: map[uint32]*plugin.Runtime{},
}
}

func (s *Service) ParseCLI(req *plugin.ParseCLIReq) (*plugin.ParseCLIRes, error) {
return nil, errors.New("core doesn't offer any connectors")
}

func (s *Service) Connect(req *plugin.ConnectReq, callback plugin.ProviderCallback) (*plugin.ConnectRes, error) {
if req == nil || req.Asset == nil {
return nil, errors.New("no connection data provided")
}

s.lastConnectionID++
connID := s.lastConnectionID
runtime := &plugin.Runtime{
Callback: callback,
HasRecording: req.HasRecording,
}
s.runtimes[connID] = runtime

return &plugin.ConnectRes{
Id: connID,
Name: "mockprovider",
}, nil
}

// Shutdown is automatically called when the shell closes.
// It is not necessary to implement this method.
// If you want to do some cleanup, you can do it here.
func (s *Service) Shutdown(req *plugin.ShutdownReq) (*plugin.ShutdownRes, error) {
return &plugin.ShutdownRes{}, nil
}

func (s *Service) GetData(req *plugin.DataReq) (*plugin.DataRes, error) {
runtime, ok := s.runtimes[req.Connection]
if !ok {
return nil, errors.New("connection " + strconv.FormatUint(uint64(req.Connection), 10) + " not found")
}

args := plugin.PrimitiveArgsToRawDataArgs(req.Args, runtime)

if req.ResourceId == "" && req.Field == "" {
res, err := resources.CreateResource(runtime, req.Resource, args)
if err != nil {
return nil, err
}

rd := llx.ResourceData(res, req.Resource).Result()
return &plugin.DataRes{
Data: rd.Data,
}, nil
}

resource, ok := runtime.Resources.Get(req.Resource + "\x00" + req.ResourceId)
if !ok {
return nil, errors.New("resource '" + req.Resource + "' (id: " + req.ResourceId + ") doesn't exist")
}

return resources.GetData(resource, req.Field, args), nil
}

func (s *Service) StoreData(req *plugin.StoreReq) (*plugin.StoreRes, error) {
runtime, ok := s.runtimes[req.Connection]
if !ok {
return nil, errors.New("connection " + strconv.FormatUint(uint64(req.Connection), 10) + " not found")
}

var errs []string
for i := range req.Resources {
info := req.Resources[i]

args, err := plugin.ProtoArgsToRawDataArgs(info.Fields)
if err != nil {
errs = append(errs, "failed to add cached "+info.Name+" (id: "+info.Id+"), failed to parse arguments")
continue
}

resource, ok := runtime.Resources.Get(info.Name + "\x00" + info.Id)
if !ok {
resource, err = resources.CreateResource(runtime, info.Name, args)
if err != nil {
errs = append(errs, "failed to add cached "+info.Name+" (id: "+info.Id+"), creation failed: "+err.Error())
continue
}

runtime.Resources.Set(info.Name+"\x00"+info.Id, resource)
}

for k, v := range args {
if err := resources.SetData(resource, k, v); err != nil {
errs = append(errs, "failed to add cached "+info.Name+" (id: "+info.Id+"), field error: "+err.Error())
}
}
}

if len(errs) != 0 {
return nil, errors.New(strings.Join(errs, ", "))
}
return &plugin.StoreRes{}, nil
}
45 changes: 45 additions & 0 deletions providers-sdk/v1/testutils/mockprovider/resources/all.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) Mondoo, Inc.
// SPDX-License-Identifier: BUSL-1.1

package resources

import (
"go.mondoo.com/cnquery/llx"
"go.mondoo.com/cnquery/providers-sdk/v1/plugin"
)

func (c *mqlMuser) id() (string, error) {
return c.Name.Data, nil
}

func (c *mqlMuser) group() (*mqlMgroup, error) {
o, err := CreateResource(c.MqlRuntime, "mgroup", map[string]*llx.RawData{
"name": llx.StringData("group one"),
})
if err != nil {
return nil, err
}
return o.(*mqlMgroup), nil
}

func (c *mqlMuser) nullgroup() (*mqlMgroup, error) {
c.Nullgroup.State = plugin.StateIsSet | plugin.StateIsNull
return nil, nil
}

func (c *mqlMuser) groups() ([]interface{}, error) {
one, err := CreateResource(c.MqlRuntime, "mgroup", map[string]*llx.RawData{
"name": llx.StringData("group one"),
})
if err != nil {
return nil, err
}

return []interface{}{
one, nil,
}, nil
}

func (c *mqlMgroup) id() (string, error) {
return c.Name.Data, nil
}
16 changes: 16 additions & 0 deletions providers-sdk/v1/testutils/mockprovider/resources/mockprovider.lr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Mondoo, Inc.
// SPDX-License-Identifier: BUSL-1.1

option provider = "go.mondoo.com/cnquery/providers-sdk/v1/testutils/mockprovider"
option go_package = "go.mondoo.com/cnquery/providers-sdk/v1/testutils/mockprovider"

muser {
name string
group() mgroup
nullgroup() mgroup
groups() []mgroup
}

mgroup {
name string
}
Loading

0 comments on commit 909b8d3

Please sign in to comment.