github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/client/client_test.go (about)

     1  // Copyright 2017 Google Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package client
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"fmt"
    21  	"os"
    22  	"os/exec"
    23  	"path/filepath"
    24  	"reflect"
    25  	"strconv"
    26  	"strings"
    27  	"sync"
    28  	"sync/atomic"
    29  	"syscall"
    30  	"testing"
    31  	"time"
    32  
    33  	"github.com/google/fleetspeak/fleetspeak/src/client/clienttestutils"
    34  	"github.com/google/fleetspeak/fleetspeak/src/client/config"
    35  	"github.com/google/fleetspeak/fleetspeak/src/client/service"
    36  	"github.com/google/fleetspeak/fleetspeak/src/client/stats"
    37  	"github.com/google/fleetspeak/fleetspeak/src/common"
    38  	"github.com/google/fleetspeak/fleetspeak/src/common/anypbtest"
    39  	"github.com/google/fleetspeak/fleetspeak/src/comtesting"
    40  	"google.golang.org/protobuf/proto"
    41  
    42  	fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak"
    43  )
    44  
    45  var (
    46  	fakeServiceStopCount     = 0
    47  	fakeServiceStartCount    = 0
    48  	fakeServiceCountersMutex = &sync.Mutex{}
    49  )
    50  
    51  // A fakeService implements Service and passes all received messages into a channel.
    52  type fakeService struct {
    53  	c  chan *fspb.Message
    54  	sc service.Context
    55  }
    56  
    57  func (s *fakeService) Start(sc service.Context) error {
    58  	s.sc = sc
    59  	fakeServiceCountersMutex.Lock()
    60  	fakeServiceStartCount++
    61  	fakeServiceCountersMutex.Unlock()
    62  	return nil
    63  }
    64  
    65  func (s *fakeService) ProcessMessage(ctx context.Context, m *fspb.Message) error {
    66  	s.c <- m
    67  	return nil
    68  }
    69  
    70  func (s *fakeService) Stop() error {
    71  	fakeServiceCountersMutex.Lock()
    72  	fakeServiceStopCount++
    73  	fakeServiceCountersMutex.Unlock()
    74  	return nil
    75  }
    76  
    77  func TestMessageDelivery(t *testing.T) {
    78  	msgs := make(chan *fspb.Message)
    79  	fs := fakeService{c: msgs}
    80  
    81  	fakeServiceFactory := func(*fspb.ClientServiceConfig) (service.Service, error) {
    82  		return &fs, nil
    83  	}
    84  
    85  	cl, err := New(
    86  		config.Configuration{
    87  			FixedServices: []*fspb.ClientServiceConfig{{Name: "FakeService", Factory: "FakeService"}},
    88  		},
    89  		Components{
    90  			ServiceFactories: map[string]service.Factory{
    91  				"NOOP":        service.NOOPFactory,
    92  				"FakeService": fakeServiceFactory,
    93  			},
    94  		})
    95  	if err != nil {
    96  		t.Fatalf("Unable to create client: %v", err)
    97  	}
    98  	defer cl.Stop()
    99  
   100  	mid, err := common.RandomMessageID()
   101  	if err != nil {
   102  		t.Fatalf("Unable to create message id: %v", err)
   103  	}
   104  
   105  	if err := cl.ProcessMessage(context.Background(),
   106  		service.AckMessage{
   107  			M: &fspb.Message{
   108  				MessageId: mid.Bytes(),
   109  				Source:    &fspb.Address{ServiceName: "FakeService"},
   110  				Destination: &fspb.Address{
   111  					ClientId:    cl.config.ClientID().Bytes(),
   112  					ServiceName: "FakeService",
   113  				},
   114  			}}); err != nil {
   115  		t.Fatalf("Unable to process message: %v", err)
   116  	}
   117  
   118  	m := <-msgs
   119  
   120  	if !bytes.Equal(m.MessageId, mid.Bytes()) {
   121  		t.Errorf("Got message with id: %v, want: %v", m, mid)
   122  	}
   123  
   124  	// Check that the service.Context provided to fs has a working LocalInfo.
   125  	// TODO: move this to a separate test.
   126  	li := fs.sc.GetLocalInfo()
   127  	if li.ClientID != cl.config.ClientID() {
   128  		t.Errorf("Got LocalInfo.ClientID: %v, want %v", li.ClientID, cl.config.ClientID())
   129  	}
   130  	if !reflect.DeepEqual(li.Services, []string{"FakeService"}) {
   131  		t.Errorf("Got LocalInfo.Services: %v, want %v", li.Services, []string{"FakeService"})
   132  	}
   133  }
   134  
   135  func TestRekey(t *testing.T) {
   136  	cl, err := New(
   137  		config.Configuration{
   138  			FixedServices: []*fspb.ClientServiceConfig{{Name: "FakeService", Factory: "FakeService"}},
   139  		},
   140  		Components{
   141  			ServiceFactories: map[string]service.Factory{
   142  				"NOOP": service.NOOPFactory,
   143  			},
   144  		})
   145  	if err != nil {
   146  		t.Fatalf("Unable to create client: %v", err)
   147  	}
   148  	defer cl.Stop()
   149  
   150  	oid := cl.config.ClientID()
   151  
   152  	mid, err := common.RandomMessageID()
   153  	if err != nil {
   154  		t.Fatalf("Unable to create message id: %v", err)
   155  	}
   156  
   157  	if err := cl.ProcessMessage(context.Background(),
   158  		service.AckMessage{
   159  			M: &fspb.Message{
   160  				MessageId: mid.Bytes(),
   161  				Source:    &fspb.Address{ServiceName: "system"},
   162  				Destination: &fspb.Address{
   163  					ClientId:    oid.Bytes(),
   164  					ServiceName: "system",
   165  				},
   166  				MessageType: "RekeyRequest",
   167  			},
   168  		}); err != nil {
   169  		t.Fatalf("Unable to process message: %v", err)
   170  	}
   171  
   172  	tk := time.NewTicker(200 * time.Millisecond)
   173  	start := time.Now()
   174  	var nid common.ClientID
   175  	defer tk.Stop()
   176  	for range tk.C {
   177  		nid = cl.config.ClientID()
   178  		if nid != oid {
   179  			break
   180  		}
   181  		if time.Since(start) > 20*time.Second {
   182  			t.Errorf("Timed out waiting for id to change.")
   183  			break
   184  		}
   185  	}
   186  }
   187  
   188  func triggerDeath(force bool, t *testing.T) {
   189  	cl, err := New(
   190  		config.Configuration{},
   191  		Components{})
   192  	if err != nil {
   193  		t.Fatalf("Unable to create client: %v", err)
   194  	}
   195  	defer cl.Stop()
   196  
   197  	oid := cl.config.ClientID()
   198  
   199  	mid, err := common.RandomMessageID()
   200  	if err != nil {
   201  		t.Fatalf("Unable to create message id: %v", err)
   202  	}
   203  
   204  	if err := cl.ProcessMessage(context.Background(),
   205  		service.AckMessage{
   206  			M: &fspb.Message{
   207  				MessageId: mid.Bytes(),
   208  				Source:    &fspb.Address{ServiceName: "system"},
   209  				Destination: &fspb.Address{
   210  					ClientId:    oid.Bytes(),
   211  					ServiceName: "system",
   212  				},
   213  				MessageType: "Die",
   214  				Data: anypbtest.New(t, &fspb.DieRequest{
   215  					Force: force,
   216  				}),
   217  			},
   218  		}); err != nil {
   219  		t.Fatalf("Unable to process message: %v", err)
   220  	}
   221  
   222  	tk := time.NewTicker(200 * time.Millisecond)
   223  	start := time.Now()
   224  	defer tk.Stop()
   225  	for range tk.C {
   226  		if time.Since(start) > 20*time.Second {
   227  			t.Errorf("Timed out waiting for the client to die.")
   228  			break
   229  		}
   230  	}
   231  }
   232  
   233  func TestDie(t *testing.T) {
   234  	// Using a workaround from https://talks.golang.org/2014/testing.slide#23 to test os.Exit behavior.
   235  	if os.Getenv("TRIGGER_DEATH") == "1" {
   236  		force, err := strconv.ParseBool(os.Getenv("TRIGGER_DEATH_FORCE"))
   237  		if err != nil {
   238  			t.Fatalf("Can't parse TRIGGER_DEATH_FORCE env variable: %v", err)
   239  		}
   240  		triggerDeath(force, t)
   241  		return
   242  	}
   243  
   244  	for _, force := range []bool{true, false} {
   245  		t.Run(fmt.Sprintf("TestDie[force=%v]", force), func(t *testing.T) {
   246  
   247  			cmd := exec.Command(os.Args[0], "-test.run=TestDie")
   248  			cmd.Env = append(os.Environ(), "TRIGGER_DEATH=1", fmt.Sprintf("TRIGGER_DEATH_FORCE=%v", force))
   249  			err := cmd.Run()
   250  			if e, ok := err.(*exec.ExitError); ok {
   251  				if status, ok := e.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == SuicideExitCode {
   252  					return
   253  				}
   254  			}
   255  			t.Fatalf("Process ran with err %v, want exit status %d", err, SuicideExitCode)
   256  		})
   257  	}
   258  }
   259  
   260  func TestDieDoesNotAck(t *testing.T) {
   261  	cl, err := New(
   262  		config.Configuration{},
   263  		Components{},
   264  	)
   265  	if err != nil {
   266  		t.Fatalf("Unable to create client: %v", err)
   267  	}
   268  
   269  	clientStopped := false
   270  	stopClientOnce := func() {
   271  		if !clientStopped {
   272  			cl.Stop()
   273  			clientStopped = true
   274  		}
   275  	}
   276  
   277  	defer stopClientOnce()
   278  
   279  	oid := cl.config.ClientID()
   280  
   281  	midDie, err := common.RandomMessageID()
   282  	if err != nil {
   283  		t.Fatalf("Unable to create message id: %v", err)
   284  	}
   285  	midFoo, err := common.RandomMessageID()
   286  	if err != nil {
   287  		t.Fatalf("Unable to create message id: %v", err)
   288  	}
   289  
   290  	// Replace system service with a fake service
   291  
   292  	cl.sc.services["system"].service.Stop()
   293  	cl.sc.services["system"].service = &fakeService{c: make(chan *fspb.Message, 5)}
   294  
   295  	// Send a Die message
   296  
   297  	am := service.AckMessage{
   298  		M: &fspb.Message{
   299  			MessageId: midDie.Bytes(),
   300  			Source:    &fspb.Address{ServiceName: "system"},
   301  			Destination: &fspb.Address{
   302  				ClientId:    oid.Bytes(),
   303  				ServiceName: "system"},
   304  			MessageType: "Die",
   305  		},
   306  	}
   307  
   308  	if err := cl.ProcessMessage(context.Background(), am); err != nil {
   309  		t.Fatalf("Unable to process message: %v", err)
   310  	}
   311  
   312  	// Send a Foo message
   313  
   314  	am = service.AckMessage{
   315  		M: &fspb.Message{
   316  			MessageId: midFoo.Bytes(),
   317  			Source:    &fspb.Address{ServiceName: "system"},
   318  			Destination: &fspb.Address{
   319  				ClientId:    oid.Bytes(),
   320  				ServiceName: "system"},
   321  			MessageType: "Foo",
   322  		},
   323  	}
   324  
   325  	if err := cl.ProcessMessage(context.Background(), am); err != nil {
   326  		t.Fatalf("Unable to process message: %v", err)
   327  	}
   328  
   329  	// Stop the client to make sure processing has finished.
   330  
   331  	stopClientOnce()
   332  
   333  	if len(cl.acks) != 1 {
   334  		t.Fatalf("Got %v acks, want 1", len(cl.acks))
   335  	}
   336  }
   337  
   338  func TestRestartService(t *testing.T) {
   339  	fakeServiceCountersMutex.Lock()
   340  	prevStartCount := fakeServiceStartCount
   341  	prevStopCount := fakeServiceStopCount
   342  	fakeServiceCountersMutex.Unlock()
   343  
   344  	msgs := make(chan *fspb.Message)
   345  	fs := fakeService{c: msgs}
   346  
   347  	fakeServiceFactory := func(*fspb.ClientServiceConfig) (service.Service, error) {
   348  		return &fs, nil
   349  	}
   350  
   351  	cl, err := New(
   352  		config.Configuration{
   353  			FixedServices: []*fspb.ClientServiceConfig{{Name: "FakeService", Factory: "FakeService"}},
   354  		},
   355  		Components{
   356  			ServiceFactories: map[string]service.Factory{
   357  				"NOOP":        service.NOOPFactory,
   358  				"FakeService": fakeServiceFactory,
   359  			},
   360  		})
   361  	if err != nil {
   362  		t.Fatalf("Unable to create client: %v", err)
   363  	}
   364  	defer cl.Stop()
   365  
   366  	oid := cl.config.ClientID()
   367  
   368  	mid, err := common.RandomMessageID()
   369  	if err != nil {
   370  		t.Fatalf("Unable to create message id: %v", err)
   371  	}
   372  
   373  	if err := cl.ProcessMessage(context.Background(),
   374  		service.AckMessage{
   375  			M: &fspb.Message{
   376  				MessageId: mid.Bytes(),
   377  				Source:    &fspb.Address{ServiceName: "system"},
   378  				Destination: &fspb.Address{
   379  					ClientId:    oid.Bytes(),
   380  					ServiceName: "system"},
   381  				MessageType: "RestartService",
   382  				Data: anypbtest.New(t, &fspb.RestartServiceRequest{
   383  					Name: "FakeService",
   384  				}),
   385  			},
   386  		}); err != nil {
   387  		t.Fatalf("Unable to process message: %v", err)
   388  	}
   389  
   390  	tk := time.NewTicker(200 * time.Millisecond)
   391  	start := time.Now()
   392  	defer tk.Stop()
   393  	for range tk.C {
   394  		fakeServiceCountersMutex.Lock()
   395  		startDiff := fakeServiceStartCount - prevStartCount
   396  		stopDiff := fakeServiceStopCount - prevStopCount
   397  		fakeServiceCountersMutex.Unlock()
   398  
   399  		// fakeService is started once and then restarted, a restart is a Stop()
   400  		// followed by Start(), meaning that overall 2 starts and 1 stop are expected.
   401  		if startDiff == 2 && stopDiff == 1 {
   402  			break
   403  		}
   404  
   405  		if time.Since(start) > 20*time.Second {
   406  			t.Errorf("Timed out waiting for the server restart.")
   407  			break
   408  		}
   409  	}
   410  }
   411  
   412  func TestMessageValidation(t *testing.T) {
   413  	msgs := make(chan *fspb.Message)
   414  	fs := fakeService{c: msgs}
   415  
   416  	fakeServiceFactory := func(*fspb.ClientServiceConfig) (service.Service, error) {
   417  		return &fs, nil
   418  	}
   419  
   420  	cl, err := New(
   421  		config.Configuration{
   422  			FixedServices: []*fspb.ClientServiceConfig{{Name: "FakeService", Factory: "FakeService"}},
   423  		},
   424  		Components{
   425  			ServiceFactories: map[string]service.Factory{
   426  				"NOOP":        service.NOOPFactory,
   427  				"FakeService": fakeServiceFactory,
   428  			},
   429  		})
   430  	if err != nil {
   431  		t.Fatalf("Unable to create client: %v", err)
   432  	}
   433  
   434  	for _, tc := range []struct {
   435  		m    *fspb.Message
   436  		want string
   437  	}{
   438  		{m: &fspb.Message{},
   439  			want: "destination must have ServiceName",
   440  		},
   441  		{m: &fspb.Message{Destination: &fspb.Address{ServiceName: ""}},
   442  			want: "destination must have ServiceName",
   443  		},
   444  		{m: &fspb.Message{Destination: &fspb.Address{ServiceName: "FakeService", ClientId: []byte("abcdef")}},
   445  			want: "cannot send directly to client",
   446  		},
   447  	} {
   448  		err := cl.ProcessMessage(context.Background(), service.AckMessage{M: tc.m})
   449  		if err == nil || !strings.HasPrefix(err.Error(), tc.want) {
   450  			t.Errorf("ProcessMessage(%v) got [%v] but should give error starting with [%v]", tc.m.String(), err, tc.want)
   451  		}
   452  	}
   453  
   454  	cl.Stop()
   455  }
   456  
   457  func TestServiceValidation(t *testing.T) {
   458  	tmpPath, fin := comtesting.GetTempDir("client_service_validation")
   459  	defer fin()
   460  
   461  	sp := filepath.Join(tmpPath, "services")
   462  	if err := os.Mkdir(sp, 0777); err != nil {
   463  		t.Fatalf("Unable to create services path [%s]: %v", sp, err)
   464  	}
   465  
   466  	msgs := make(chan *fspb.Message, 1)
   467  	fakeServiceFactory := func(*fspb.ClientServiceConfig) (service.Service, error) {
   468  		return &fakeService{c: msgs}, nil
   469  	}
   470  
   471  	// This factory is used for service configs that shouldn't validate - if it does, it
   472  	// causes the test to fail.
   473  	failingServiceFactory := func(cfg *fspb.ClientServiceConfig) (service.Service, error) {
   474  		t.Fatalf("failingServiceFactory called on %s", cfg.Name)
   475  		return nil, fmt.Errorf("failingServiceFactory called")
   476  	}
   477  
   478  	// A service which should work.
   479  	cfg := signServiceConfig(t, &fspb.ClientServiceConfig{
   480  		Name:           "FakeService",
   481  		Factory:        "FakeService",
   482  		RequiredLabels: []*fspb.Label{{ServiceName: "client", Label: "linux"}},
   483  	})
   484  	if err := clienttestutils.WriteSignedServiceConfig(sp, "FakeService.signed", cfg); err != nil {
   485  		t.Fatal(err)
   486  	}
   487  
   488  	// A service requiring the wrong label.
   489  	cfg = signServiceConfig(t, &fspb.ClientServiceConfig{
   490  		Name:           "FailingServiceBadLabel",
   491  		Factory:        "FailingService",
   492  		RequiredLabels: []*fspb.Label{{ServiceName: "client", Label: "windows"}},
   493  	})
   494  	if err := clienttestutils.WriteSignedServiceConfig(sp, "FailingServiceBadLabel.signed", cfg); err != nil {
   495  		t.Fatal(err)
   496  	}
   497  
   498  	ph, err := config.NewFilesystemPersistenceHandler(tmpPath, "")
   499  	if err != nil {
   500  		t.Fatal(err)
   501  	}
   502  
   503  	cl, err := New(
   504  		config.Configuration{
   505  			PersistenceHandler: ph,
   506  			ClientLabels: []*fspb.Label{
   507  				{ServiceName: "client", Label: "TestClient"},
   508  				{ServiceName: "client", Label: "linux"}},
   509  		},
   510  		Components{
   511  			ServiceFactories: map[string]service.Factory{
   512  				"NOOP":           service.NOOPFactory,
   513  				"FakeService":    fakeServiceFactory,
   514  				"FailingService": failingServiceFactory,
   515  			},
   516  		})
   517  	if err != nil {
   518  		t.Fatalf("Unable to create client: %v", err)
   519  	}
   520  	defer cl.Stop()
   521  
   522  	// Check that the good service started by passing a message through it.
   523  	mid, err := common.RandomMessageID()
   524  	if err != nil {
   525  		t.Fatalf("Unable to create message id: %v", err)
   526  	}
   527  	if err := cl.ProcessMessage(context.Background(),
   528  		service.AckMessage{
   529  			M: &fspb.Message{
   530  				MessageId: mid.Bytes(),
   531  				Source:    &fspb.Address{ServiceName: "FakeService"},
   532  				Destination: &fspb.Address{
   533  					ClientId:    cl.config.ClientID().Bytes(),
   534  					ServiceName: "FakeService",
   535  				},
   536  			}}); err != nil {
   537  		t.Fatalf("Unable to process message: %v", err)
   538  	}
   539  
   540  	m := <-msgs
   541  
   542  	if !bytes.Equal(m.MessageId, mid.Bytes()) {
   543  		t.Errorf("Got message with id: %v, want: %v", m, mid)
   544  	}
   545  }
   546  
   547  func TestTextServiceConfig(t *testing.T) {
   548  	tmpPath, fin := comtesting.GetTempDir("TestTextServiceConfig")
   549  	defer fin()
   550  
   551  	tsp := filepath.Join(tmpPath, "textservices")
   552  	if err := os.Mkdir(tsp, 0777); err != nil {
   553  		t.Fatalf("Unable to create services path [%s]: %v", tsp, err)
   554  	}
   555  
   556  	msgs := make(chan *fspb.Message, 1)
   557  	fakeServiceFactory := func(*fspb.ClientServiceConfig) (service.Service, error) {
   558  		return &fakeService{c: msgs}, nil
   559  	}
   560  
   561  	// A text service.
   562  	cfg := &fspb.ClientServiceConfig{
   563  		Name:           "FakeService",
   564  		Factory:        "FakeService",
   565  		RequiredLabels: []*fspb.Label{{ServiceName: "client", Label: "linux"}},
   566  	}
   567  	if err := clienttestutils.WriteServiceConfig(tsp, "FakeService.txt", cfg); err != nil {
   568  		t.Fatal(err)
   569  	}
   570  
   571  	ph, err := config.NewFilesystemPersistenceHandler(tmpPath, "")
   572  	if err != nil {
   573  		t.Fatal(err)
   574  	}
   575  
   576  	cl, err := New(
   577  		config.Configuration{
   578  			PersistenceHandler: ph,
   579  			ClientLabels: []*fspb.Label{
   580  				{ServiceName: "client", Label: "TestClient"},
   581  				{ServiceName: "client", Label: "linux"}},
   582  		},
   583  		Components{
   584  			ServiceFactories: map[string]service.Factory{
   585  				"NOOP":        service.NOOPFactory,
   586  				"FakeService": fakeServiceFactory,
   587  			},
   588  		})
   589  	if err != nil {
   590  		t.Fatalf("Unable to create client: %v", err)
   591  	}
   592  	defer cl.Stop()
   593  
   594  	// Check that the service started by passing a message through it.
   595  	mid, err := common.RandomMessageID()
   596  	if err != nil {
   597  		t.Fatalf("Unable to create message id: %v", err)
   598  	}
   599  	if err := cl.ProcessMessage(context.Background(),
   600  		service.AckMessage{
   601  			M: &fspb.Message{
   602  				MessageId: mid.Bytes(),
   603  				Source:    &fspb.Address{ServiceName: "FakeService"},
   604  				Destination: &fspb.Address{
   605  					ClientId:    cl.config.ClientID().Bytes(),
   606  					ServiceName: "FakeService",
   607  				},
   608  			}}); err != nil {
   609  		t.Fatalf("Unable to process message: %v", err)
   610  	}
   611  
   612  	m := <-msgs
   613  
   614  	if !bytes.Equal(m.MessageId, mid.Bytes()) {
   615  		t.Errorf("Got message with id: %v, want: %v", m, mid)
   616  	}
   617  }
   618  
   619  type clientStats struct {
   620  	stats.NoopCollector
   621  	messages atomic.Int32
   622  }
   623  
   624  func (cs *clientStats) AfterMessageProcessed(msg *fspb.Message, isLocal bool, err error) {
   625  	if msg.GetSource().GetServiceName() == "NOOPService" {
   626  		cs.messages.Add(1)
   627  	}
   628  }
   629  
   630  func TestClientStats(t *testing.T) {
   631  	cs := &clientStats{}
   632  
   633  	cl, err := New(
   634  		config.Configuration{
   635  			FixedServices: []*fspb.ClientServiceConfig{{Name: "NOOPService", Factory: "NOOP"}},
   636  		},
   637  		Components{
   638  			ServiceFactories: map[string]service.Factory{
   639  				"NOOP": service.NOOPFactory,
   640  			},
   641  			Stats: cs,
   642  		})
   643  	if err != nil {
   644  		t.Fatalf("Unable to create client: %v", err)
   645  	}
   646  	defer cl.Stop()
   647  
   648  	mid, err := common.RandomMessageID()
   649  	if err != nil {
   650  		t.Fatalf("Unable to create message id: %v", err)
   651  	}
   652  
   653  	if err := cl.ProcessMessage(context.Background(),
   654  		service.AckMessage{
   655  			M: &fspb.Message{
   656  				MessageId: mid.Bytes(),
   657  				Source:    &fspb.Address{ServiceName: "NOOPService"},
   658  				Destination: &fspb.Address{
   659  					ClientId:    cl.config.ClientID().Bytes(),
   660  					ServiceName: "NOOPService",
   661  				},
   662  			},
   663  		}); err != nil {
   664  		t.Fatalf("Unable to process message: %v", err)
   665  	}
   666  
   667  	messageCount := cs.messages.Load()
   668  	if messageCount != 1 {
   669  		t.Errorf("Unexpected number of messages reported, got: %d, want: 1", messageCount)
   670  	}
   671  }
   672  
   673  func signServiceConfig(t *testing.T, cfg *fspb.ClientServiceConfig) *fspb.SignedClientServiceConfig {
   674  	b, err := proto.Marshal(cfg)
   675  	if err != nil {
   676  		t.Fatalf("Unable to serialize service config: %v", err)
   677  	}
   678  	return &fspb.SignedClientServiceConfig{ServiceConfig: b}
   679  }