go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/mql/internal/builder_test.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package internal
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  	"go.mondoo.com/cnquery/llx"
    12  	"go.mondoo.com/cnquery/types"
    13  )
    14  
    15  func TestPrioritizeNode(t *testing.T) {
    16  	// ┌──────┐┌───┐
    17  	// │eq1   ││eq2│
    18  	// └┬────┬┘└──┬┘
    19  	// ┌▽──┐┌▽──┐┌▽──┐
    20  	// │dp1││dp2││dp3│
    21  	// └─┬┬┘└─┬┬┘└┬─┬┘
    22  	//   ││   ││  │ │
    23  	//  ┌││───││──┘ │
    24  	//  ││└──┐││    │
    25  	//  ││┌──│┘│    │
    26  	// ┌▽▽▽┐┌▽─▽┐┌──▽┐
    27  	// │dps││rj1││rj2│
    28  	// └───┘└┬──┘└┬──┘
    29  	// ┌─────▽────▽┐
    30  	// │scores     │
    31  	// └───────────┘
    32  
    33  	nodes := map[NodeID]*Node{
    34  		"eq1":    {id: "eq1"},
    35  		"eq2":    {id: "eq1"},
    36  		"dp1":    {id: "eq1"},
    37  		"dp2":    {id: "eq1"},
    38  		"dp3":    {id: "eq1"},
    39  		"rj1":    {id: "rj1"},
    40  		"rj2":    {id: "rj2"},
    41  		"scores": {id: "scores"},
    42  		"dps":    {id: "dps"},
    43  	}
    44  	edges := map[NodeID][]NodeID{
    45  		"eq1": {"dp1", "dp2"},
    46  		"eq2": {"dp3"},
    47  		"dp1": {"rj1", "dps"},
    48  		"dp2": {"rj1", "dps"},
    49  		"dp3": {"rj2", "dps"},
    50  		"rj1": {"scores"},
    51  		"rj2": {"scores"},
    52  	}
    53  
    54  	priorityMap := map[NodeID]int{}
    55  	for n := range nodes {
    56  		prioritizeNode(nodes, edges, priorityMap, n)
    57  	}
    58  
    59  	require.Equal(t, len(nodes), len(priorityMap))
    60  	assert.Equal(t, 1, priorityMap["scores"])
    61  	assert.Equal(t, 1, priorityMap["dps"])
    62  	assert.Greater(t, priorityMap["eq1"], priorityMap["dp1"])
    63  	assert.Greater(t, priorityMap["eq1"], priorityMap["dp2"])
    64  	assert.Greater(t, priorityMap["eq2"], priorityMap["dp3"])
    65  	assert.Greater(t, priorityMap["dp1"], priorityMap["dps"])
    66  	assert.Greater(t, priorityMap["dp1"], priorityMap["rj1"])
    67  	assert.Greater(t, priorityMap["dp2"], priorityMap["dps"])
    68  	assert.Greater(t, priorityMap["dp2"], priorityMap["rj1"])
    69  	assert.Greater(t, priorityMap["dp3"], priorityMap["dps"])
    70  	assert.Greater(t, priorityMap["dp3"], priorityMap["rj2"])
    71  	assert.Greater(t, priorityMap["rj1"], priorityMap["scores"])
    72  	assert.Greater(t, priorityMap["rj2"], priorityMap["scores"])
    73  }
    74  
    75  func TestBuilder(t *testing.T) {
    76  	b := NewBuilder()
    77  
    78  	b.AddQuery(
    79  		&llx.CodeBundle{
    80  			CodeV2: &llx.CodeV2{
    81  				Id: "propertyquery",
    82  				Blocks: []*llx.Block{{
    83  					Entrypoints: []uint64{1},
    84  					Datapoints:  []uint64{2},
    85  				}},
    86  				Checksums: map[uint64]string{1: "checksum1", 2: "pqep"},
    87  			},
    88  		}, nil, nil)
    89  
    90  	b.AddQuery(
    91  		&llx.CodeBundle{
    92  			CodeV2: &llx.CodeV2{
    93  				Id: "query1",
    94  				Blocks: []*llx.Block{{
    95  					Entrypoints: []uint64{1},
    96  				}},
    97  				Checksums: map[uint64]string{1: "checksum2"},
    98  			},
    99  		}, map[string]string{"prop": "checksum1"}, nil)
   100  
   101  	b.AddQuery(
   102  		&llx.CodeBundle{
   103  			CodeV2: &llx.CodeV2{
   104  				Id: "query2",
   105  				Blocks: []*llx.Block{{
   106  					Entrypoints: []uint64{1},
   107  					Datapoints:  []uint64{2},
   108  				}},
   109  				Checksums: map[uint64]string{1: "checksum3", 2: "checksum4"},
   110  			},
   111  		}, nil, map[string]*llx.Primitive{
   112  			"resolvedprop": llx.StringPrimitive("hello"),
   113  		})
   114  	b.AddDatapointType("checksum3", string(types.Bool))
   115  
   116  	b.AddQuery(
   117  		&llx.CodeBundle{
   118  			CodeV2: &llx.CodeV2{
   119  				Id: "query3",
   120  				Blocks: []*llx.Block{{
   121  					Datapoints: []uint64{1},
   122  				}},
   123  				Checksums: map[uint64]string{1: "checksum5"},
   124  			},
   125  		}, nil, nil)
   126  	b.CollectDatapoint("checksum5")
   127  
   128  	b.AddQuery(
   129  		&llx.CodeBundle{
   130  			CodeV2: &llx.CodeV2{
   131  				Id: "query4",
   132  				Blocks: []*llx.Block{{
   133  					Datapoints: []uint64{1},
   134  				}},
   135  				Checksums: map[uint64]string{1: "checksum6"},
   136  			},
   137  		}, nil, nil)
   138  
   139  	b.AddQuery(
   140  		&llx.CodeBundle{
   141  			CodeV2: &llx.CodeV2{
   142  				Id: "query5",
   143  				Blocks: []*llx.Block{{
   144  					Datapoints: []uint64{1, 2},
   145  				}},
   146  				Checksums: map[uint64]string{1: "checksum5", 2: "checksum7"},
   147  			},
   148  			MinMondooVersion: "9999.9999.9999",
   149  		}, nil, nil)
   150  
   151  	b.WithMondooVersion("100.0.0")
   152  
   153  	ge, err := b.Build(nil, nil, "assetMrn")
   154  	require.NoError(t, err)
   155  
   156  	hasNode(t, ge, "execution_query/propertyquery", ExecutionQueryNodeType)
   157  	hasOutEdges(t, ge, "execution_query/propertyquery", "checksum1", "pqep")
   158  
   159  	hasNode(t, ge, "execution_query/query1", ExecutionQueryNodeType)
   160  	hasOutEdges(t, ge, "execution_query/query1", "checksum2")
   161  
   162  	hasNode(t, ge, "execution_query/query2", ExecutionQueryNodeType)
   163  	hasOutEdges(t, ge, "execution_query/query2", "checksum3", "checksum4")
   164  
   165  	hasNode(t, ge, "execution_query/query3", ExecutionQueryNodeType)
   166  	hasOutEdges(t, ge, "execution_query/query3", "checksum5")
   167  
   168  	hasNode(t, ge, "execution_query/query4", ExecutionQueryNodeType)
   169  	hasOutEdges(t, ge, "execution_query/query4", "checksum6")
   170  
   171  	assert.NotContains(t, ge.nodes, "execution_query/query5")
   172  	assert.Nil(t, ge.nodes["checksum5"].data.(*DatapointNodeData).res)
   173  	if assert.NotNil(t, ge.nodes["checksum7"].data.(*DatapointNodeData).res) {
   174  		assert.Error(t, ge.nodes["checksum7"].data.(*DatapointNodeData).res.Data.Error)
   175  	}
   176  
   177  	hasNode(t, ge, "pqep", DatapointNodeType)
   178  	hasOutEdges(t, ge, "pqep", CollectionFinisherID)
   179  
   180  	hasNode(t, ge, "checksum1", DatapointNodeType)
   181  	hasOutEdges(t, ge, "checksum1", "execution_query/query1", CollectionFinisherID)
   182  
   183  	hasNode(t, ge, "checksum2", DatapointNodeType)
   184  	hasOutEdges(t, ge, "checksum2", CollectionFinisherID)
   185  
   186  	hasNode(t, ge, "checksum3", DatapointNodeType)
   187  	hasOutEdges(t, ge, "checksum3", CollectionFinisherID)
   188  
   189  	hasNode(t, ge, "checksum4", DatapointNodeType)
   190  	hasOutEdges(t, ge, "checksum4", CollectionFinisherID)
   191  
   192  	hasNode(t, ge, "checksum5", DatapointNodeType)
   193  	hasOutEdges(t, ge, "checksum5", DatapointCollectorID, CollectionFinisherID)
   194  
   195  	hasNode(t, ge, "checksum6", DatapointNodeType)
   196  	hasOutEdges(t, ge, "checksum6", CollectionFinisherID)
   197  }
   198  
   199  func hasNode(t *testing.T, ge *GraphExecutor, nodeID NodeID, nodeType NodeType) {
   200  	t.Helper()
   201  	if assert.Contains(t, ge.nodes, nodeID) {
   202  		assert.Equal(t, nodeType, ge.nodes[nodeID].nodeType)
   203  	}
   204  }
   205  
   206  func hasOutEdges(t *testing.T, ge *GraphExecutor, nodeID NodeID, edges ...NodeID) {
   207  	t.Helper()
   208  	require.Len(t, ge.edges[nodeID], len(edges))
   209  	assert.ElementsMatch(t, ge.edges[nodeID], edges)
   210  }