package proto import ( "bytes" "errors" "io" "testing" "github.com/edgelesssys/constellation/coordinator/pubapi/pubproto" "github.com/stretchr/testify/assert" "google.golang.org/grpc" ) // dummyActivateAsCoordinatorClient is a dummy and panics if Recv() is called. type dummyActivateAsCoordinatorClient struct { grpc.ClientStream } func (c dummyActivateAsCoordinatorClient) Recv() (*pubproto.ActivateAsCoordinatorResponse, error) { panic("i'm a dummy, Recv() not implemented") } // dummyActivateAsCoordinatorClient is a dummy and panics if Recv() is called. type dummyActivateAdditionalNodesClient struct { grpc.ClientStream } func (c dummyActivateAdditionalNodesClient) Recv() (*pubproto.ActivateAdditionalNodesResponse, error) { panic("i'm a dummy, Recv() not implemented") } // stubActivationAsCoordinatorClient recives responses from an predefined // response stream iterator or a stub error. type stubActivationAsCoordinatorClient struct { grpc.ClientStream stream *stubActivateAsCoordinatorResponseIter recvErr error } func (c stubActivationAsCoordinatorClient) Recv() (*pubproto.ActivateAsCoordinatorResponse, error) { if c.recvErr != nil { return nil, c.recvErr } return c.stream.Next() } // stubActivateAsCoordinatorResponseIter is an iterator over a slice of // ActivateAsCoordinatorResponses. It returns the messages in the order // they occur in the slice and returns an io.EOF error when no response // is left. type stubActivateAsCoordinatorResponseIter struct { msgs []*pubproto.ActivateAsCoordinatorResponse } // Next returns the next message from the message slice or an io.EOF error // if the message slice is empty. func (q *stubActivateAsCoordinatorResponseIter) Next() (*pubproto.ActivateAsCoordinatorResponse, error) { if len(q.msgs) == 0 { return nil, io.EOF } msg := q.msgs[0] q.msgs = q.msgs[1:] return msg, nil } func TestNextLog(t *testing.T) { testClientVpnIp := "192.0.2.1" testCoordinatorVpnKey := []byte("32bytesWireGuardKeyForTheTesting") testCoordinatorVpnKey64 := []byte("MzJieXRlc1dpcmVHdWFyZEtleUZvclRoZVRlc3Rpbmc=") testKubeconfig := []byte("apiVersion:v1 kind:Config...") testConfigResp := &pubproto.ActivateAsCoordinatorResponse{ Content: &pubproto.ActivateAsCoordinatorResponse_AdminConfig{ AdminConfig: &pubproto.AdminConfig{ AdminVpnIp: testClientVpnIp, CoordinatorVpnPubKey: testCoordinatorVpnKey, Kubeconfig: testKubeconfig, }, }, } testLogMessage := "some log message" testLogResp := &pubproto.ActivateAsCoordinatorResponse{ Content: &pubproto.ActivateAsCoordinatorResponse_Log{ Log: &pubproto.Log{ Message: testLogMessage, }, }, } someErr := errors.New("failed") testCases := map[string]struct { msgs []*pubproto.ActivateAsCoordinatorResponse wantLogLen int wantState bool recvErr error wantErr bool }{ "some logs": { msgs: []*pubproto.ActivateAsCoordinatorResponse{testLogResp, testLogResp, testLogResp}, wantLogLen: 3, }, "only admin config": { msgs: []*pubproto.ActivateAsCoordinatorResponse{testConfigResp}, wantState: true, }, "logs and configs": { msgs: []*pubproto.ActivateAsCoordinatorResponse{testLogResp, testConfigResp, testLogResp, testConfigResp}, wantLogLen: 2, wantState: true, }, "no response": { msgs: []*pubproto.ActivateAsCoordinatorResponse{}, wantLogLen: 0, }, "recv fail": { recvErr: someErr, wantErr: true, }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) respClient := stubActivationAsCoordinatorClient{ stream: &stubActivateAsCoordinatorResponseIter{ msgs: tc.msgs, }, recvErr: tc.recvErr, } client := NewActivationRespClient(respClient) var logs []string var err error for err == nil { var log string log, err = client.NextLog() if err == nil { logs = append(logs, log) } } assert.Error(err) if tc.wantErr { assert.NotErrorIs(err, io.EOF) return } assert.ErrorIs(err, io.EOF) assert.Len(logs, tc.wantLogLen) if tc.wantState { ip, err := client.GetClientVpnIp() assert.NoError(err) assert.Equal(testClientVpnIp, ip) config, err := client.GetKubeconfig() assert.NoError(err) assert.Equal(string(testKubeconfig), config) key, err := client.GetCoordinatorVpnKey() assert.NoError(err) assert.Equal(string(testCoordinatorVpnKey64), key) } }) } } func TestPrintLogStream(t *testing.T) { assert := assert.New(t) // // 10 logs a 10 byte // var msgs []*pubproto.ActivateAsCoordinatorResponse for i := 0; i < 10; i++ { msgs = append(msgs, &pubproto.ActivateAsCoordinatorResponse{ Content: &pubproto.ActivateAsCoordinatorResponse_Log{ Log: &pubproto.Log{ Message: "10BytesLog", }, }, }) } respClient := stubActivationAsCoordinatorClient{ stream: &stubActivateAsCoordinatorResponseIter{ msgs: msgs, }, } client := NewActivationRespClient(respClient) out := &bytes.Buffer{} assert.NoError(client.WriteLogStream(out)) assert.Equal(out.Len(), 10*11) // 10 messages * (len(message) + 1 newline) // // Check error handling. // someErr := errors.New("failed") respClient = stubActivationAsCoordinatorClient{ recvErr: someErr, } client = NewActivationRespClient(respClient) assert.Error(client.WriteLogStream(&bytes.Buffer{})) } func TestGetKubeconfig(t *testing.T) { assert := assert.New(t) client := NewActivationRespClient(dummyActivateAsCoordinatorClient{}) _, err := client.GetKubeconfig() assert.Error(err) client.kubeconfig = "apiVersion:v1 kind:Config..." config, err := client.GetKubeconfig() assert.NoError(err) assert.Equal("apiVersion:v1 kind:Config...", config) } func TestGetCoordinatorVpnKey(t *testing.T) { assert := assert.New(t) client := NewActivationRespClient(dummyActivateAsCoordinatorClient{}) _, err := client.GetCoordinatorVpnKey() assert.Error(err) client.coordinatorVpnKey = "32bytesWireGuardKeyForTheTesting" key, err := client.GetCoordinatorVpnKey() assert.NoError(err) assert.Equal("32bytesWireGuardKeyForTheTesting", key) } func TestGetClientVpnIp(t *testing.T) { assert := assert.New(t) client := NewActivationRespClient(dummyActivateAsCoordinatorClient{}) _, err := client.GetClientVpnIp() assert.Error(err) client.clientVpnIp = "192.0.2.1" ip, err := client.GetClientVpnIp() assert.NoError(err) assert.Equal("192.0.2.1", ip) }