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 }