github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/client/statushistory_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package client_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/errors"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  	"gopkg.in/juju/names.v2"
    13  
    14  	"github.com/juju/juju/apiserver/client"
    15  	"github.com/juju/juju/apiserver/params"
    16  	apiservertesting "github.com/juju/juju/apiserver/testing"
    17  	"github.com/juju/juju/status"
    18  	"github.com/juju/juju/testing"
    19  )
    20  
    21  var _ = gc.Suite(&statusHistoryTestSuite{})
    22  
    23  type statusHistoryTestSuite struct {
    24  	testing.BaseSuite
    25  	st  *mockState
    26  	api *client.Client
    27  }
    28  
    29  func (s *statusHistoryTestSuite) SetUpTest(c *gc.C) {
    30  	s.st = &mockState{}
    31  	tag := names.NewUserTag("admin")
    32  	authorizer := &apiservertesting.FakeAuthorizer{Tag: tag}
    33  	var err error
    34  	s.api, err = client.NewClient(
    35  		s.st,
    36  		nil, // modelconfig API
    37  		nil, // resources
    38  		authorizer,
    39  		nil, // statusSetter
    40  		nil, // toolsFinder
    41  		nil, // newEnviron
    42  		nil, // blockChecker
    43  	)
    44  	c.Assert(err, jc.ErrorIsNil)
    45  }
    46  
    47  func statusInfoWithDates(si []status.StatusInfo) []status.StatusInfo {
    48  	// Add timestamps to input status info records.
    49  	// Timestamps will be in descending order so that we can
    50  	// check that sorting has occurred and the output should
    51  	// be in ascending order.
    52  	result := make([]status.StatusInfo, len(si))
    53  	for i, s := range si {
    54  		t := time.Unix(int64(1000-i), 0)
    55  		s.Since = &t
    56  		result[i] = s
    57  	}
    58  	return result
    59  }
    60  
    61  func reverseStatusInfo(si []status.StatusInfo) []status.StatusInfo {
    62  	result := make([]status.StatusInfo, len(si))
    63  	for i, s := range si {
    64  		result[len(si)-i-1] = s
    65  	}
    66  	return result
    67  }
    68  
    69  func checkStatusInfo(c *gc.C, obtained []params.DetailedStatus, expected []status.StatusInfo) {
    70  	c.Assert(len(obtained), gc.Equals, len(expected))
    71  	lastTimestamp := int64(0)
    72  	for i, obtainedInfo := range obtained {
    73  		c.Logf("Checking status %q with info %q", obtainedInfo.Status, obtainedInfo.Info)
    74  		thisTimeStamp := obtainedInfo.Since.Unix()
    75  		c.Assert(thisTimeStamp >= lastTimestamp, jc.IsTrue)
    76  		lastTimestamp = thisTimeStamp
    77  		obtainedInfo.Since = nil
    78  		c.Assert(obtainedInfo.Status, gc.Equals, expected[i].Status.String())
    79  		c.Assert(obtainedInfo.Info, gc.Equals, expected[i].Message)
    80  	}
    81  }
    82  
    83  func (s *statusHistoryTestSuite) TestSizeRequired(c *gc.C) {
    84  	r := s.api.StatusHistory(params.StatusHistoryRequests{
    85  		Requests: []params.StatusHistoryRequest{{
    86  			Tag:    "unit-unit-1",
    87  			Kind:   status.KindUnit.String(),
    88  			Filter: params.StatusHistoryFilter{Size: 0},
    89  		}}})
    90  	c.Assert(r.Results, gc.HasLen, 1)
    91  	c.Assert(r.Results[0].Error.Message, gc.Equals, "cannot validate status history filter: empty struct not valid")
    92  }
    93  
    94  func (s *statusHistoryTestSuite) TestNoConflictingFilters(c *gc.C) {
    95  	now := time.Now()
    96  	r := s.api.StatusHistory(params.StatusHistoryRequests{
    97  		Requests: []params.StatusHistoryRequest{{
    98  			Tag:    "unit-unit-1",
    99  			Kind:   status.KindUnit.String(),
   100  			Filter: params.StatusHistoryFilter{Size: 1, Date: &now},
   101  		}}})
   102  	c.Assert(r.Results, gc.HasLen, 1)
   103  	c.Assert(r.Results[0].Error.Message, gc.Equals, "cannot validate status history filter: Size and Date together not valid")
   104  
   105  	yesterday := time.Hour * 24
   106  	r = s.api.StatusHistory(params.StatusHistoryRequests{
   107  		Requests: []params.StatusHistoryRequest{{
   108  			Tag:    "unit-unit-1",
   109  			Kind:   status.KindUnit.String(),
   110  			Filter: params.StatusHistoryFilter{Size: 1, Delta: &yesterday},
   111  		}}})
   112  	c.Assert(r.Results, gc.HasLen, 1)
   113  	c.Assert(r.Results[0].Error.Message, gc.Equals, "cannot validate status history filter: Size and Delta together not valid")
   114  
   115  	r = s.api.StatusHistory(params.StatusHistoryRequests{
   116  		Requests: []params.StatusHistoryRequest{{
   117  			Tag:    "unit-unit-1",
   118  			Kind:   status.KindUnit.String(),
   119  			Filter: params.StatusHistoryFilter{Date: &now, Delta: &yesterday},
   120  		}}})
   121  	c.Assert(r.Results, gc.HasLen, 1)
   122  	c.Assert(r.Results[0].Error.Message, gc.Equals, "cannot validate status history filter: Date and Delta together not valid")
   123  }
   124  
   125  func (s *statusHistoryTestSuite) TestStatusHistoryUnitOnly(c *gc.C) {
   126  	s.st.unitHistory = statusInfoWithDates([]status.StatusInfo{
   127  		{
   128  			Status:  status.Maintenance,
   129  			Message: "working",
   130  		},
   131  		{
   132  			Status:  status.Active,
   133  			Message: "running",
   134  		},
   135  	})
   136  	s.st.agentHistory = statusInfoWithDates([]status.StatusInfo{
   137  		{
   138  			Status: status.Idle,
   139  		},
   140  	})
   141  	h := s.api.StatusHistory(params.StatusHistoryRequests{
   142  		Requests: []params.StatusHistoryRequest{{
   143  			Tag:    "unit-unit-0",
   144  			Kind:   status.KindWorkload.String(),
   145  			Filter: params.StatusHistoryFilter{Size: 10},
   146  		}}})
   147  	c.Assert(h.Results, gc.HasLen, 1)
   148  	c.Assert(h.Results[0].Error, gc.IsNil)
   149  	checkStatusInfo(c, h.Results[0].History.Statuses, reverseStatusInfo(s.st.unitHistory))
   150  }
   151  
   152  func (s *statusHistoryTestSuite) TestStatusHistoryAgentOnly(c *gc.C) {
   153  	s.st.unitHistory = statusInfoWithDates([]status.StatusInfo{
   154  		{
   155  			Status:  status.Maintenance,
   156  			Message: "working",
   157  		},
   158  		{
   159  			Status:  status.Active,
   160  			Message: "running",
   161  		},
   162  	})
   163  	s.st.agentHistory = statusInfoWithDates([]status.StatusInfo{
   164  		{
   165  			Status: status.Executing,
   166  		},
   167  		{
   168  			Status: status.Idle,
   169  		},
   170  	})
   171  	h := s.api.StatusHistory(params.StatusHistoryRequests{
   172  		Requests: []params.StatusHistoryRequest{{
   173  			Tag:    "unit-unit-0",
   174  			Kind:   status.KindUnitAgent.String(),
   175  			Filter: params.StatusHistoryFilter{Size: 10},
   176  		}}})
   177  	c.Assert(h.Results, gc.HasLen, 1)
   178  	c.Assert(h.Results[0].Error, gc.IsNil)
   179  	checkStatusInfo(c, h.Results[0].History.Statuses, reverseStatusInfo(s.st.agentHistory))
   180  }
   181  
   182  func (s *statusHistoryTestSuite) TestStatusHistoryCombined(c *gc.C) {
   183  	s.st.unitHistory = statusInfoWithDates([]status.StatusInfo{
   184  		{
   185  			Status:  status.Maintenance,
   186  			Message: "working",
   187  		},
   188  		{
   189  			Status:  status.Active,
   190  			Message: "running",
   191  		},
   192  		{
   193  			Status:  status.Blocked,
   194  			Message: "waiting",
   195  		},
   196  	})
   197  	s.st.agentHistory = statusInfoWithDates([]status.StatusInfo{
   198  		{
   199  			Status: status.Executing,
   200  		},
   201  		{
   202  			Status: status.Idle,
   203  		},
   204  	})
   205  	h := s.api.StatusHistory(params.StatusHistoryRequests{
   206  		Requests: []params.StatusHistoryRequest{{
   207  			Tag:    "unit-unit-0",
   208  			Kind:   status.KindUnit.String(),
   209  			Filter: params.StatusHistoryFilter{Size: 3},
   210  		}}})
   211  	c.Assert(h.Results, gc.HasLen, 1)
   212  	c.Assert(h.Results[0].Error, gc.IsNil)
   213  	expected := []status.StatusInfo{
   214  		s.st.agentHistory[1],
   215  		s.st.unitHistory[0],
   216  		s.st.agentHistory[0],
   217  	}
   218  	checkStatusInfo(c, h.Results[0].History.Statuses, expected)
   219  }
   220  
   221  type mockState struct {
   222  	client.Backend
   223  	unitHistory  []status.StatusInfo
   224  	agentHistory []status.StatusInfo
   225  }
   226  
   227  func (m *mockState) ModelUUID() string {
   228  	return "uuid"
   229  }
   230  
   231  func (m *mockState) ModelTag() names.ModelTag {
   232  	return names.NewModelTag("deadbeef-0bad-400d-8000-4b1d0d06f00d")
   233  }
   234  
   235  func (m *mockState) Unit(name string) (client.Unit, error) {
   236  	if name != "unit/0" {
   237  		return nil, errors.NotFoundf("%v", name)
   238  	}
   239  	return &mockUnit{
   240  		status: m.unitHistory,
   241  		agent:  &mockUnitAgent{m.agentHistory},
   242  	}, nil
   243  }
   244  
   245  type mockUnit struct {
   246  	status statuses
   247  	agent  *mockUnitAgent
   248  	client.Unit
   249  }
   250  
   251  func (m *mockUnit) StatusHistory(filter status.StatusHistoryFilter) ([]status.StatusInfo, error) {
   252  	return m.status.StatusHistory(filter)
   253  }
   254  
   255  func (m *mockUnit) AgentHistory() status.StatusHistoryGetter {
   256  	return m.agent
   257  }
   258  
   259  type mockUnitAgent struct {
   260  	statuses
   261  }
   262  
   263  type statuses []status.StatusInfo
   264  
   265  func (s statuses) StatusHistory(filter status.StatusHistoryFilter) ([]status.StatusInfo, error) {
   266  	if filter.Size > len(s) {
   267  		filter.Size = len(s)
   268  	}
   269  	return s[:filter.Size], nil
   270  }