get.porter.sh/porter@v1.3.0/tests/grpc/installation_test.go (about) 1 package grpc 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "strconv" 8 "testing" 9 10 iGRPC "get.porter.sh/porter/gen/proto/go/porterapis/installation/v1alpha1" 11 pGRPC "get.porter.sh/porter/gen/proto/go/porterapis/porter/v1alpha1" 12 "get.porter.sh/porter/pkg/cnab" 13 "get.porter.sh/porter/pkg/porter" 14 "get.porter.sh/porter/pkg/storage" 15 "get.porter.sh/porter/tests" 16 "github.com/cnabio/cnab-go/bundle" 17 "github.com/cnabio/cnab-go/bundle/definition" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 "github.com/tidwall/gjson" 21 "google.golang.org/grpc" 22 "google.golang.org/grpc/credentials/insecure" 23 "google.golang.org/protobuf/encoding/protojson" 24 ) 25 26 type testOutputOpt struct { 27 name string 28 value string 29 bundleOutput bundle.Output 30 } 31 type testInstallationOpts struct { 32 bundleDefs *map[string]*definition.Schema 33 outputs *[]testOutputOpt 34 } 35 36 // TODO: add opts structure for different installation options 37 func newTestInstallation(t *testing.T, namespace, name string, grpcSvr *TestPorterGRPCServer, opts testInstallationOpts) storage.Installation { 38 //Bundle Definition with required porter-state 39 bd := definition.Definitions{ 40 "porter-state": &definition.Schema{ 41 Type: "string", 42 Comment: "porter-internal", 43 }, 44 } 45 for name, schema := range *opts.bundleDefs { 46 bd[name] = schema 47 } 48 //Bundle Output with required porter-state 49 bo := map[string]bundle.Output{ 50 "porter-state": { 51 Definition: "porter-state", 52 Path: "/cnab/app/outputs/porter-state.tgz", 53 }, 54 } 55 for _, out := range *opts.outputs { 56 bo[out.name] = out.bundleOutput 57 } 58 b := bundle.Bundle{ 59 Definitions: bd, 60 Outputs: bo, 61 } 62 extB := cnab.NewBundle(b) 63 storeInst := grpcSvr.TestPorter.TestInstallations.CreateInstallation(storage.NewInstallation(namespace, name), grpcSvr.TestPorter.TestInstallations.SetMutableInstallationValues, func(i *storage.Installation) { 64 i.Status.BundleVersion = "v0.1.0" 65 i.Status.ResultStatus = cnab.StatusSucceeded 66 i.Bundle.Repository = "test-bundle" 67 i.Bundle.Version = "v0.1.0" 68 }) 69 c := grpcSvr.TestPorter.TestInstallations.CreateRun(storeInst.NewRun(cnab.ActionInstall, cnab.ExtendedBundle{}), func(sRun *storage.Run) { 70 sRun.Bundle = b 71 sRun.ParameterOverrides.Parameters = grpcSvr.TestPorter.SanitizeParameters(sRun.ParameterOverrides.Parameters, sRun.ID, extB) 72 }) 73 sRes := grpcSvr.TestPorter.TestInstallations.CreateResult(c.NewResult(cnab.StatusSucceeded)) 74 for _, out := range *opts.outputs { 75 grpcSvr.TestPorter.CreateOutput(sRes.NewOutput(out.name, []byte(out.value)), extB) 76 } 77 return storeInst 78 } 79 80 func TestInstall_installationMessage(t *testing.T) { 81 writeOnly := true 82 basicInstOpts := testInstallationOpts{ 83 bundleDefs: &map[string]*definition.Schema{ 84 "foo": {Type: "string", WriteOnly: &writeOnly}, 85 "bar": {Type: "string", WriteOnly: &writeOnly}, 86 }, 87 outputs: &[]testOutputOpt{ 88 { 89 name: "foo", 90 value: "foo-data", 91 bundleOutput: bundle.Output{Definition: "foo", Path: "/path/to/foo"}, 92 }, 93 { 94 name: "bar", 95 value: "bar-data", 96 bundleOutput: bundle.Output{Definition: "bar", Path: "/path/to/bar"}, 97 }, 98 }, 99 } 100 tests := []struct { 101 testName string 102 instName string 103 instNamespace string "" 104 instOpts testInstallationOpts 105 }{ 106 { 107 testName: "basic installation", 108 instName: "test", 109 instOpts: basicInstOpts, 110 }, 111 { 112 testName: "another installation", 113 instName: "another-test", 114 instOpts: basicInstOpts, 115 }, 116 } 117 for _, test := range tests { 118 t.Run(test.testName, func(t *testing.T) { 119 //Server setup 120 grpcSvr, err := NewTestGRPCServer(t) 121 require.NoError(t, err) 122 server := grpcSvr.ListenAndServe() 123 defer server.Stop() 124 125 //Client setup 126 ctx := context.TODO() 127 client, err := grpc.NewClient("passthrough://bufnet", grpc.WithContextDialer(bufDialer), grpc.WithTransportCredentials(insecure.NewCredentials())) 128 require.NoError(t, err) 129 defer client.Close() 130 instClient := pGRPC.NewPorterClient(client) 131 132 inst := newTestInstallation(t, test.instNamespace, test.instName, grpcSvr, test.instOpts) 133 134 //Call ListInstallations 135 resp, err := instClient.ListInstallations(ctx, &iGRPC.ListInstallationsRequest{}) 136 require.NoError(t, err) 137 assert.Len(t, resp.Installation, 1) 138 // Validation 139 validateInstallations(t, inst, resp.GetInstallation()[0]) 140 141 //Call ListInstallationLatestOutputRequest 142 req := &iGRPC.ListInstallationLatestOutputRequest{Name: test.instName, Namespace: &test.instNamespace} 143 oresp, err := instClient.ListInstallationLatestOutputs(ctx, req) 144 require.NoError(t, err) 145 assert.Len(t, oresp.GetOutputs(), len(*test.instOpts.outputs)) 146 147 oOpts := &porter.OutputListOptions{} 148 oOpts.Name = test.instName 149 oOpts.Namespace = test.instNamespace 150 oOpts.Format = "json" 151 dvs, err := grpcSvr.TestPorter.ListBundleOutputs(ctx, oOpts) 152 require.NoError(t, err) 153 154 //Validation 155 validateOutputs(t, dvs, oresp) 156 }) 157 } 158 } 159 160 func validateInstallations(t *testing.T, expected storage.Installation, actual *iGRPC.Installation) { 161 assert.Equal(t, actual.Name, expected.Name) 162 bExpInst, err := json.Marshal(porter.NewDisplayInstallation(expected)) 163 require.NoError(t, err) 164 bExpInst, err = tests.GRPCDisplayInstallationExpectedJSON(bExpInst) 165 require.NoError(t, err) 166 pjm := protojson.MarshalOptions{EmitUnpopulated: true} 167 bActInst, err := pjm.Marshal(actual) 168 require.NoError(t, err) 169 var pJson bytes.Buffer 170 require.NoError(t, json.Indent(&pJson, bActInst, "", " ")) 171 assert.JSONEq(t, string(bExpInst), string(bActInst)) 172 } 173 174 func validateOutputs(t *testing.T, dvs porter.DisplayValues, actual *iGRPC.ListInstallationLatestOutputResponse) { 175 //Get expected json 176 bExpOuts, err := json.MarshalIndent(dvs, "", " ") 177 require.NoError(t, err) 178 pjm := protojson.MarshalOptions{EmitUnpopulated: true, Multiline: true, Indent: " "} 179 //Get actual json response 180 for i, gPV := range actual.GetOutputs() { 181 bActOut, err := pjm.Marshal(gPV) 182 require.NoError(t, err) 183 //TODO: make this not dependent on order 184 bExpOut := gjson.GetBytes(bExpOuts, strconv.Itoa(i)).String() 185 assert.JSONEq(t, bExpOut, string(bActOut)) 186 } 187 }