github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/params/params_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package params_test 5 6 import ( 7 "encoding/json" 8 stdtesting "testing" 9 10 jc "github.com/juju/testing/checkers" 11 gc "gopkg.in/check.v1" 12 "gopkg.in/juju/charm.v6-unstable" 13 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/constraints" 16 "github.com/juju/juju/instance" 17 "github.com/juju/juju/state" 18 "github.com/juju/juju/state/multiwatcher" 19 "github.com/juju/juju/status" 20 "github.com/juju/juju/testing" 21 ) 22 23 // TestPackage integrates the tests into gotest. 24 func TestPackage(t *stdtesting.T) { 25 gc.TestingT(t) 26 } 27 28 type MarshalSuite struct{} 29 30 var _ = gc.Suite(&MarshalSuite{}) 31 32 var marshalTestCases = []struct { 33 about string 34 // Value holds a real Go struct. 35 value multiwatcher.Delta 36 // JSON document. 37 json string 38 }{{ 39 about: "MachineInfo Delta", 40 value: multiwatcher.Delta{ 41 Entity: &multiwatcher.MachineInfo{ 42 ModelUUID: "uuid", 43 Id: "Benji", 44 InstanceId: "Shazam", 45 AgentStatus: multiwatcher.StatusInfo{ 46 Current: status.Error, 47 Message: "foo", 48 }, 49 InstanceStatus: multiwatcher.StatusInfo{ 50 Current: status.Pending, 51 }, 52 Life: multiwatcher.Life("alive"), 53 Series: "trusty", 54 SupportedContainers: []instance.ContainerType{instance.LXD}, 55 Jobs: []multiwatcher.MachineJob{state.JobManageModel.ToParams()}, 56 Addresses: []multiwatcher.Address{}, 57 HardwareCharacteristics: &instance.HardwareCharacteristics{}, 58 }, 59 }, 60 json: `["machine","change",{"model-uuid":"uuid","id":"Benji","instance-id":"Shazam","agent-status":{"current":"error","message":"foo","version":""},"instance-status":{"current":"pending","message":"","version":""},"life":"alive","series":"trusty","supported-containers":["lxd"],"supported-containers-known":false,"hardware-characteristics":{},"jobs":["JobManageModel"],"addresses":[],"has-vote":false,"wants-vote":false}]`, 61 }, { 62 about: "ApplicationInfo Delta", 63 value: multiwatcher.Delta{ 64 Entity: &multiwatcher.ApplicationInfo{ 65 ModelUUID: "uuid", 66 Name: "Benji", 67 Exposed: true, 68 CharmURL: "cs:quantal/name", 69 Life: multiwatcher.Life("dying"), 70 OwnerTag: "test-owner", 71 MinUnits: 42, 72 Constraints: constraints.MustParse("arch=armhf mem=1024M"), 73 Config: charm.Settings{ 74 "hello": "goodbye", 75 "foo": false, 76 }, 77 Status: multiwatcher.StatusInfo{ 78 Current: status.Active, 79 Message: "all good", 80 }, 81 }, 82 }, 83 json: `["application","change",{"model-uuid": "uuid", "charm-url": "cs:quantal/name","name":"Benji","exposed":true,"life":"dying","owner-tag":"test-owner","min-units":42,"constraints":{"arch":"armhf", "mem": 1024},"config": {"hello":"goodbye","foo":false},"subordinate":false,"status":{"current":"active", "message":"all good", "version": ""}}]`, 84 }, { 85 about: "UnitInfo Delta", 86 value: multiwatcher.Delta{ 87 Entity: &multiwatcher.UnitInfo{ 88 ModelUUID: "uuid", 89 Name: "Benji", 90 Application: "Shazam", 91 Series: "precise", 92 CharmURL: "cs:~user/precise/wordpress-42", 93 Ports: []multiwatcher.Port{{ 94 Protocol: "http", 95 Number: 80, 96 }}, 97 PortRanges: []multiwatcher.PortRange{{ 98 FromPort: 80, 99 ToPort: 80, 100 Protocol: "http", 101 }}, 102 PublicAddress: "testing.invalid", 103 PrivateAddress: "10.0.0.1", 104 MachineId: "1", 105 WorkloadStatus: multiwatcher.StatusInfo{ 106 Current: status.Active, 107 Message: "all good", 108 }, 109 AgentStatus: multiwatcher.StatusInfo{ 110 Current: status.Idle, 111 }, 112 }, 113 }, 114 json: `["unit","change",{"model-uuid":"uuid","name":"Benji","application":"Shazam","series":"precise","charm-url":"cs:~user/precise/wordpress-42","public-address":"testing.invalid","private-address":"10.0.0.1","machine-id":"1","ports":[{"protocol":"http","number":80}],"port-ranges":[{"from-port":80,"to-port":80,"protocol":"http"}],"subordinate":false,"workload-status":{"current":"active","message":"all good","version":""},"agent-status":{"current":"idle","message":"","version":""}}]`, 115 }, { 116 about: "RelationInfo Delta", 117 value: multiwatcher.Delta{ 118 Entity: &multiwatcher.RelationInfo{ 119 ModelUUID: "uuid", 120 Key: "Benji", 121 Id: 4711, 122 Endpoints: []multiwatcher.Endpoint{ 123 { 124 ApplicationName: "logging", 125 Relation: multiwatcher.CharmRelation{ 126 Name: "logging-directory", 127 Role: "requirer", 128 Interface: "logging", 129 Optional: false, 130 Limit: 1, 131 Scope: "container"}, 132 }, 133 { 134 ApplicationName: "wordpress", 135 Relation: multiwatcher.CharmRelation{ 136 Name: "logging-dir", 137 Role: "provider", 138 Interface: "logging", 139 Optional: false, 140 Limit: 0, 141 Scope: "container"}, 142 }, 143 }, 144 }, 145 }, 146 json: `["relation","change",{"model-uuid": "uuid", "key":"Benji", "id": 4711, "endpoints": [{"application-name":"logging", "relation":{"name":"logging-directory", "role":"requirer", "interface":"logging", "optional":false, "limit":1, "scope":"container"}}, {"application-name":"wordpress", "relation":{"name":"logging-dir", "role":"provider", "interface":"logging", "optional":false, "limit":0, "scope":"container"}}]}]`, 147 }, { 148 about: "AnnotationInfo Delta", 149 value: multiwatcher.Delta{ 150 Entity: &multiwatcher.AnnotationInfo{ 151 ModelUUID: "uuid", 152 Tag: "machine-0", 153 Annotations: map[string]string{ 154 "foo": "bar", 155 "arble": "2 4", 156 }, 157 }, 158 }, 159 json: `["annotation","change",{"model-uuid": "uuid", "tag":"machine-0","annotations":{"foo":"bar","arble":"2 4"}}]`, 160 }, { 161 about: "Delta Removed True", 162 value: multiwatcher.Delta{ 163 Removed: true, 164 Entity: &multiwatcher.RelationInfo{ 165 ModelUUID: "uuid", 166 Key: "Benji", 167 }, 168 }, 169 json: `["relation","remove",{"model-uuid": "uuid", "key":"Benji", "id": 0, "endpoints": null}]`, 170 }} 171 172 func (s *MarshalSuite) TestDeltaMarshalJSON(c *gc.C) { 173 for _, t := range marshalTestCases { 174 c.Log(t.about) 175 output, err := t.value.MarshalJSON() 176 c.Check(err, jc.ErrorIsNil) 177 // We check unmarshalled output both to reduce the fragility of the 178 // tests (because ordering in the maps can change) and to verify that 179 // the output is well-formed. 180 var unmarshalledOutput interface{} 181 err = json.Unmarshal(output, &unmarshalledOutput) 182 c.Check(err, jc.ErrorIsNil) 183 var expected interface{} 184 err = json.Unmarshal([]byte(t.json), &expected) 185 c.Check(err, jc.ErrorIsNil) 186 c.Check(unmarshalledOutput, jc.DeepEquals, expected) 187 } 188 } 189 190 func (s *MarshalSuite) TestDeltaUnmarshalJSON(c *gc.C) { 191 for i, t := range marshalTestCases { 192 c.Logf("test %d. %s", i, t.about) 193 var unmarshalled multiwatcher.Delta 194 err := json.Unmarshal([]byte(t.json), &unmarshalled) 195 c.Check(err, jc.ErrorIsNil) 196 c.Check(unmarshalled, jc.DeepEquals, t.value) 197 } 198 } 199 200 func (s *MarshalSuite) TestDeltaMarshalJSONCardinality(c *gc.C) { 201 err := json.Unmarshal([]byte(`[1,2]`), new(multiwatcher.Delta)) 202 c.Check(err, gc.ErrorMatches, "Expected 3 elements in top-level of JSON but got 2") 203 } 204 205 func (s *MarshalSuite) TestDeltaMarshalJSONUnknownOperation(c *gc.C) { 206 err := json.Unmarshal([]byte(`["relation","masticate",{}]`), new(multiwatcher.Delta)) 207 c.Check(err, gc.ErrorMatches, `Unexpected operation "masticate"`) 208 } 209 210 func (s *MarshalSuite) TestDeltaMarshalJSONUnknownEntity(c *gc.C) { 211 err := json.Unmarshal([]byte(`["qwan","change",{}]`), new(multiwatcher.Delta)) 212 c.Check(err, gc.ErrorMatches, `Unexpected entity name "qwan"`) 213 } 214 215 type ErrorResultsSuite struct{} 216 217 var _ = gc.Suite(&ErrorResultsSuite{}) 218 219 func (s *ErrorResultsSuite) TestOneError(c *gc.C) { 220 for i, test := range []struct { 221 results params.ErrorResults 222 errMatch string 223 }{ 224 { 225 errMatch: "expected 1 result, got 0", 226 }, { 227 results: params.ErrorResults{ 228 []params.ErrorResult{{nil}}, 229 }, 230 }, { 231 results: params.ErrorResults{ 232 []params.ErrorResult{{nil}, {nil}}, 233 }, 234 errMatch: "expected 1 result, got 2", 235 }, { 236 results: params.ErrorResults{ 237 []params.ErrorResult{ 238 {¶ms.Error{Message: "test error"}}, 239 }, 240 }, 241 errMatch: "test error", 242 }, 243 } { 244 c.Logf("test %d", i) 245 err := test.results.OneError() 246 if test.errMatch == "" { 247 c.Check(err, jc.ErrorIsNil) 248 } else { 249 c.Check(err, gc.ErrorMatches, test.errMatch) 250 } 251 } 252 } 253 254 func (s *ErrorResultsSuite) TestCombine(c *gc.C) { 255 for i, test := range []struct { 256 msg string 257 results params.ErrorResults 258 errMatch string 259 }{ 260 { 261 msg: "no results, no error", 262 }, { 263 msg: "single nil result", 264 results: params.ErrorResults{ 265 []params.ErrorResult{{nil}}, 266 }, 267 }, { 268 msg: "multiple nil results", 269 results: params.ErrorResults{ 270 []params.ErrorResult{{nil}, {nil}}, 271 }, 272 }, { 273 msg: "one error result", 274 results: params.ErrorResults{ 275 []params.ErrorResult{ 276 {¶ms.Error{Message: "test error"}}, 277 }, 278 }, 279 errMatch: "test error", 280 }, { 281 msg: "mixed error results", 282 results: params.ErrorResults{ 283 []params.ErrorResult{ 284 {¶ms.Error{Message: "test error"}}, 285 {nil}, 286 {¶ms.Error{Message: "second error"}}, 287 }, 288 }, 289 errMatch: "test error\nsecond error", 290 }, 291 } { 292 c.Logf("test %d: %s", i, test.msg) 293 err := test.results.Combine() 294 if test.errMatch == "" { 295 c.Check(err, jc.ErrorIsNil) 296 } else { 297 c.Check(err, gc.ErrorMatches, test.errMatch) 298 } 299 } 300 } 301 302 type importSuite struct{} 303 304 var _ = gc.Suite(&importSuite{}) 305 306 func (*importSuite) TestParamsDoesNotDependOnState(c *gc.C) { 307 imports := testing.FindJujuCoreImports(c, "github.com/juju/juju/apiserver/params") 308 for _, i := range imports { 309 c.Assert(i, gc.Not(gc.Equals), "state") 310 } 311 }