get.porter.sh/porter@v1.3.0/pkg/storage/grpc_test.go (about)

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"testing"
     7  	"time"
     8  
     9  	"get.porter.sh/porter/pkg/portercontext"
    10  	"get.porter.sh/porter/pkg/storage/plugins"
    11  	"get.porter.sh/porter/pkg/storage/plugins/proto"
    12  	"get.porter.sh/porter/pkg/storage/plugins/testplugin"
    13  	"get.porter.sh/porter/pkg/storage/pluginstore"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  	"go.mongodb.org/mongo-driver/bson"
    17  	"google.golang.org/grpc"
    18  	"google.golang.org/grpc/credentials/insecure"
    19  )
    20  
    21  func TestRoundTripDataOverGRPC(t *testing.T) {
    22  	// Just check that we can round trip data through our storage grpc service
    23  	c := portercontext.NewTestContext(t)
    24  	store := testplugin.NewTestStoragePlugin(c)
    25  	ctx := context.Background()
    26  
    27  	server := pluginstore.NewServer(c.Context, store)
    28  	addr := "localhost:"
    29  	lis, err := net.Listen("tcp", addr)
    30  	require.NoError(t, err)
    31  	grpcServer := grpc.NewServer()
    32  	proto.RegisterStorageProtocolServer(grpcServer, server)
    33  	errs := make(chan error, 1)
    34  	go func() {
    35  		errs <- grpcServer.Serve(lis)
    36  	}()
    37  	defer func() {
    38  		grpcServer.Stop()
    39  		if err := <-errs; err != nil {
    40  			require.NoError(t, err)
    41  		}
    42  	}()
    43  
    44  	conn, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
    45  	require.NoError(t, err)
    46  	defer conn.Close()
    47  	client := pluginstore.NewClient(proto.NewStorageProtocolClient(conn))
    48  
    49  	// Add an index to support filtering
    50  	const collection = "things"
    51  	err = client.EnsureIndex(ctx, plugins.EnsureIndexOptions{Indices: []plugins.Index{
    52  		{Collection: collection, Keys: bson.D{{Key: "namespace", Value: 1}, {Key: "name", Value: 1}}},
    53  	}})
    54  	require.NoError(t, err)
    55  
    56  	thing1 := bson.M{"name": "Thing1", "namespace": "dev"}
    57  	err = client.Insert(ctx, plugins.InsertOptions{
    58  		Collection: collection,
    59  		Documents: []bson.M{
    60  			thing1,
    61  			{"name": "Thing2", "namespace": "staging"},
    62  		},
    63  	})
    64  	require.NoError(t, err)
    65  
    66  	results, err := client.Find(ctx, plugins.FindOptions{
    67  		Collection: collection,
    68  		Filter:     bson.M{"namespace": "dev"},
    69  		Select:     bson.D{{Key: "name", Value: 1}, {Key: "namespace", Value: 1}, {Key: "_id", Value: 0}},
    70  	})
    71  	require.NoError(t, err)
    72  	require.Len(t, results, 1)
    73  
    74  	var gotThing1 bson.M
    75  	require.NoError(t, bson.Unmarshal(results[0], &gotThing1))
    76  	assert.Equal(t, thing1, gotThing1)
    77  
    78  	opts := plugins.EnsureIndexOptions{
    79  		Indices: []plugins.Index{
    80  			// query most recent outputs by run (porter installation run show, when we list outputs)
    81  			{Collection: CollectionOutputs, Keys: bson.D{{Key: "namespace", Value: 1}, {Key: "installation", Value: 1}, {Key: "-resultId", Value: 1}}},
    82  			// query outputs by result (list)
    83  			{Collection: CollectionOutputs, Keys: bson.D{{Key: "resultId", Value: 1}, {Key: "name", Value: 1}}, Unique: true},
    84  			// query most recent outputs by name for an installation
    85  			{Collection: CollectionOutputs, Keys: bson.D{{Key: "namespace", Value: 1}, {Key: "installation", Value: 1}, {Key: "name", Value: 1}, {Key: "-resultId", Value: 1}}},
    86  		},
    87  	}
    88  
    89  	err = client.EnsureIndex(ctx, opts)
    90  	require.NoError(t, err)
    91  
    92  	err = client.Insert(ctx, plugins.InsertOptions{
    93  		Collection: CollectionOutputs,
    94  		Documents: []bson.M{{"namespace": "dev", "installation": "test", "name": "thing1", "resultId": "111"},
    95  			{"namespace": "dev", "installation": "test", "name": "thing2", "resultId": "111"},
    96  			{"namespace": "dev", "installation": "test", "name": "thing2", "resultId": "222"},
    97  		},
    98  	})
    99  	require.NoError(t, err)
   100  
   101  	// Add a small delay to ensure all documents are properly indexed
   102  	time.Sleep(200 * time.Millisecond)
   103  
   104  	aggregateResults, err := client.Aggregate(ctx, plugins.AggregateOptions{
   105  		Collection: CollectionOutputs,
   106  		Pipeline: []bson.D{
   107  			// List outputs by installation
   108  			{{Key: "$match", Value: bson.M{
   109  				"namespace":    "dev",
   110  				"installation": "test",
   111  			}}},
   112  			// Reverse sort them (newest on top)
   113  			{{Key: "$sort", Value: bson.D{
   114  				{Key: "namespace", Value: 1},
   115  				{Key: "installation", Value: 1},
   116  				{Key: "name", Value: 1},
   117  				{Key: "resultId", Value: -1},
   118  			}}},
   119  			// Group them by output name and select the last value for each output
   120  			{{Key: "$group", Value: bson.D{
   121  				{Key: "_id", Value: "$name"},
   122  				{Key: "lastOutput", Value: bson.M{"$first": "$$ROOT"}},
   123  			}}},
   124  		},
   125  	})
   126  	require.NoError(t, err)
   127  	require.Len(t, aggregateResults, 2)
   128  	// Find the thing2 output and verify it has the most recent resultId
   129  	for _, result := range aggregateResults {
   130  		if result.Lookup("_id").String() == "thing2" {
   131  			require.Contains(t, result.Lookup("lastOutput").String(), "222")
   132  			break
   133  		}
   134  	}
   135  }