github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/common/relationunitswatcher_test.go (about)

     1  // Copyright 2019 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common_test
     5  
     6  import (
     7  	"time"
     8  
     9  	jc "github.com/juju/testing/checkers"
    10  	gc "gopkg.in/check.v1"
    11  	"gopkg.in/tomb.v2"
    12  
    13  	"github.com/juju/juju/apiserver/common"
    14  	"github.com/juju/juju/core/watcher"
    15  	"github.com/juju/juju/rpc/params"
    16  	"github.com/juju/juju/testing"
    17  )
    18  
    19  type relationUnitsWatcherSuite struct{}
    20  
    21  var _ = gc.Suite(&relationUnitsWatcherSuite{})
    22  
    23  func (s *relationUnitsWatcherSuite) TestRelationUnitsWatcherFromState(c *gc.C) {
    24  
    25  	source := &mockRUWatcher{
    26  		changes: make(chan watcher.RelationUnitsChange),
    27  	}
    28  	// Ensure the watcher tomb thinks it's still going.
    29  	source.Tomb.Go(func() error {
    30  		<-source.Tomb.Dying()
    31  		return nil
    32  	})
    33  	w, err := common.RelationUnitsWatcherFromState(source)
    34  	c.Assert(err, jc.ErrorIsNil)
    35  
    36  	c.Assert(source.Err(), gc.Equals, tomb.ErrStillAlive)
    37  	c.Assert(w.Err(), gc.Equals, tomb.ErrStillAlive)
    38  
    39  	event := watcher.RelationUnitsChange{
    40  		Changed: map[string]watcher.UnitSettings{
    41  			"joni/1": {Version: 23},
    42  		},
    43  		AppChanged: map[string]int64{
    44  			"mitchell": 42,
    45  		},
    46  		Departed: []string{"urge", "for", "going"},
    47  	}
    48  	select {
    49  	case source.changes <- event:
    50  	case <-time.After(testing.LongWait):
    51  		c.Fatalf("timed out waiting to send event")
    52  	}
    53  
    54  	// The running loop of the watcher is effectively a 1-event
    55  	// buffer.
    56  
    57  	select {
    58  	case result := <-w.Changes():
    59  		c.Assert(result, gc.DeepEquals, params.RelationUnitsChange{
    60  			Changed: map[string]params.UnitSettings{
    61  				"joni/1": {Version: 23},
    62  			},
    63  			AppChanged: map[string]int64{
    64  				"mitchell": 42,
    65  			},
    66  			Departed: []string{"urge", "for", "going"},
    67  		})
    68  	case <-time.After(testing.LongWait):
    69  		c.Fatalf("timed out waiting for output event")
    70  	}
    71  
    72  	c.Assert(w.Stop(), jc.ErrorIsNil)
    73  	// Ensure that stopping the watcher has stopped the source.
    74  	c.Assert(source.Err(), jc.ErrorIsNil)
    75  
    76  	select {
    77  	case _, ok := <-w.Changes():
    78  		c.Assert(ok, gc.Equals, false)
    79  	default:
    80  		c.Fatalf("didn't close output channel")
    81  	}
    82  }
    83  
    84  func (s *relationUnitsWatcherSuite) TestCanStopWithAPendingSend(c *gc.C) {
    85  	source := &mockRUWatcher{
    86  		changes: make(chan watcher.RelationUnitsChange),
    87  	}
    88  	// Ensure the watcher tomb thinks it's still going.
    89  	source.Tomb.Go(func() error {
    90  		<-source.Tomb.Dying()
    91  		return nil
    92  	})
    93  	w, err := common.RelationUnitsWatcherFromState(source)
    94  	c.Assert(err, jc.ErrorIsNil)
    95  	defer w.Kill()
    96  
    97  	event := watcher.RelationUnitsChange{
    98  		Changed: map[string]watcher.UnitSettings{
    99  			"joni/1": {Version: 23},
   100  		},
   101  	}
   102  	s.send(c, source.changes, event)
   103  
   104  	// Stop without accepting the output event.
   105  	stopped := make(chan error)
   106  	go func() {
   107  		err := w.Stop()
   108  		stopped <- err
   109  	}()
   110  
   111  	select {
   112  	case err := <-stopped:
   113  		c.Assert(err, jc.ErrorIsNil)
   114  	case <-time.After(testing.LongWait):
   115  		c.Fatalf("timed out waiting for watcher to stop with pending send")
   116  	}
   117  }
   118  
   119  func (s *relationUnitsWatcherSuite) TestNilChanged(c *gc.C) {
   120  	source := &mockRUWatcher{
   121  		changes: make(chan watcher.RelationUnitsChange),
   122  	}
   123  	// Ensure the watcher tomb thinks it's still going.
   124  	source.Tomb.Go(func() error {
   125  		<-source.Tomb.Dying()
   126  		return nil
   127  	})
   128  	w, err := common.RelationUnitsWatcherFromState(source)
   129  	c.Assert(err, jc.ErrorIsNil)
   130  
   131  	event := watcher.RelationUnitsChange{
   132  		Departed: []string{"happy", "birthday"},
   133  	}
   134  
   135  	s.send(c, source.changes, event)
   136  	result := s.receive(c, w)
   137  	c.Assert(result, gc.DeepEquals, params.RelationUnitsChange{
   138  		Departed: []string{"happy", "birthday"},
   139  	})
   140  }
   141  
   142  func (s *relationUnitsWatcherSuite) send(c *gc.C, ch chan watcher.RelationUnitsChange, event watcher.RelationUnitsChange) {
   143  	select {
   144  	case ch <- event:
   145  	case <-time.After(testing.LongWait):
   146  		c.Fatalf("timed out waiting to send event")
   147  	}
   148  }
   149  
   150  func (s *relationUnitsWatcherSuite) receive(c *gc.C, w common.RelationUnitsWatcher) params.RelationUnitsChange {
   151  	select {
   152  	case result := <-w.Changes():
   153  		return result
   154  	case <-time.After(testing.LongWait):
   155  		c.Fatalf("timed out waiting for output event")
   156  	}
   157  	// Can't actually happen, but the go compiler can't tell that
   158  	// c.Fatalf panics.
   159  	return params.RelationUnitsChange{}
   160  }
   161  
   162  type mockRUWatcher struct {
   163  	tomb.Tomb
   164  	changes chan watcher.RelationUnitsChange
   165  }
   166  
   167  func (w *mockRUWatcher) Changes() watcher.RelationUnitsChannel {
   168  	return w.changes
   169  }
   170  
   171  func (w *mockRUWatcher) Kill() {
   172  	w.Tomb.Kill(nil)
   173  }
   174  
   175  func (w *mockRUWatcher) Stop() error {
   176  	w.Tomb.Kill(nil)
   177  	return w.Tomb.Wait()
   178  }
   179  
   180  func (w *mockRUWatcher) Err() error {
   181  	return w.Tomb.Err()
   182  }