github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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" 13 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/core/constraints" 16 "github.com/juju/juju/core/instance" 17 "github.com/juju/juju/core/status" 18 "github.com/juju/juju/state" 19 "github.com/juju/juju/state/multiwatcher" 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 WorkloadVersion: "42.47", 82 }, 83 }, 84 json: `["application","change",{"model-uuid": "uuid", "charm-url": "cs:quantal/name","name":"Benji","exposed":true,"life":"dying","owner-tag":"test-owner","workload-version":"42.47","min-units":42,"constraints":{"arch":"armhf", "mem": 1024},"config": {"hello":"goodbye","foo":false},"subordinate":false,"status":{"current":"active", "message":"all good", "version": ""}}]`, 85 }, { 86 about: "UnitInfo Delta", 87 value: multiwatcher.Delta{ 88 Entity: &multiwatcher.UnitInfo{ 89 ModelUUID: "uuid", 90 Name: "Benji", 91 Application: "Shazam", 92 Series: "precise", 93 CharmURL: "cs:~user/precise/wordpress-42", 94 Ports: []multiwatcher.Port{{ 95 Protocol: "http", 96 Number: 80, 97 }}, 98 PortRanges: []multiwatcher.PortRange{{ 99 FromPort: 80, 100 ToPort: 80, 101 Protocol: "http", 102 }}, 103 PublicAddress: "testing.invalid", 104 PrivateAddress: "10.0.0.1", 105 MachineId: "1", 106 WorkloadStatus: multiwatcher.StatusInfo{ 107 Current: status.Active, 108 Message: "all good", 109 }, 110 AgentStatus: multiwatcher.StatusInfo{ 111 Current: status.Idle, 112 }, 113 }, 114 }, 115 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":""}}]`, 116 }, { 117 about: "RelationInfo Delta", 118 value: multiwatcher.Delta{ 119 Entity: &multiwatcher.RelationInfo{ 120 ModelUUID: "uuid", 121 Key: "Benji", 122 Id: 4711, 123 Endpoints: []multiwatcher.Endpoint{ 124 { 125 ApplicationName: "logging", 126 Relation: multiwatcher.CharmRelation{ 127 Name: "logging-directory", 128 Role: "requirer", 129 Interface: "logging", 130 Optional: false, 131 Limit: 1, 132 Scope: "container"}, 133 }, 134 { 135 ApplicationName: "wordpress", 136 Relation: multiwatcher.CharmRelation{ 137 Name: "logging-dir", 138 Role: "provider", 139 Interface: "logging", 140 Optional: false, 141 Limit: 0, 142 Scope: "container"}, 143 }, 144 }, 145 }, 146 }, 147 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"}}]}]`, 148 }, { 149 about: "AnnotationInfo Delta", 150 value: multiwatcher.Delta{ 151 Entity: &multiwatcher.AnnotationInfo{ 152 ModelUUID: "uuid", 153 Tag: "machine-0", 154 Annotations: map[string]string{ 155 "foo": "bar", 156 "arble": "2 4", 157 }, 158 }, 159 }, 160 json: `["annotation","change",{"model-uuid": "uuid", "tag":"machine-0","annotations":{"foo":"bar","arble":"2 4"}}]`, 161 }, { 162 about: "Delta Removed True", 163 value: multiwatcher.Delta{ 164 Removed: true, 165 Entity: &multiwatcher.RelationInfo{ 166 ModelUUID: "uuid", 167 Key: "Benji", 168 }, 169 }, 170 json: `["relation","remove",{"model-uuid": "uuid", "key":"Benji", "id": 0, "endpoints": null}]`, 171 }} 172 173 func (s *MarshalSuite) TestDeltaMarshalJSON(c *gc.C) { 174 for _, t := range marshalTestCases { 175 c.Log(t.about) 176 output, err := t.value.MarshalJSON() 177 c.Check(err, jc.ErrorIsNil) 178 // We check unmarshalled output both to reduce the fragility of the 179 // tests (because ordering in the maps can change) and to verify that 180 // the output is well-formed. 181 var unmarshalledOutput interface{} 182 err = json.Unmarshal(output, &unmarshalledOutput) 183 c.Check(err, jc.ErrorIsNil) 184 var expected interface{} 185 err = json.Unmarshal([]byte(t.json), &expected) 186 c.Check(err, jc.ErrorIsNil) 187 c.Check(unmarshalledOutput, jc.DeepEquals, expected) 188 } 189 } 190 191 func (s *MarshalSuite) TestDeltaUnmarshalJSON(c *gc.C) { 192 for i, t := range marshalTestCases { 193 c.Logf("test %d. %s", i, t.about) 194 var unmarshalled multiwatcher.Delta 195 err := json.Unmarshal([]byte(t.json), &unmarshalled) 196 c.Check(err, jc.ErrorIsNil) 197 c.Check(unmarshalled, jc.DeepEquals, t.value) 198 } 199 } 200 201 func (s *MarshalSuite) TestDeltaMarshalJSONCardinality(c *gc.C) { 202 err := json.Unmarshal([]byte(`[1,2]`), new(multiwatcher.Delta)) 203 c.Check(err, gc.ErrorMatches, "Expected 3 elements in top-level of JSON but got 2") 204 } 205 206 func (s *MarshalSuite) TestDeltaMarshalJSONUnknownOperation(c *gc.C) { 207 err := json.Unmarshal([]byte(`["relation","masticate",{}]`), new(multiwatcher.Delta)) 208 c.Check(err, gc.ErrorMatches, `Unexpected operation "masticate"`) 209 } 210 211 func (s *MarshalSuite) TestDeltaMarshalJSONUnknownEntity(c *gc.C) { 212 err := json.Unmarshal([]byte(`["qwan","change",{}]`), new(multiwatcher.Delta)) 213 c.Check(err, gc.ErrorMatches, `Unexpected entity name "qwan"`) 214 } 215 216 type ErrorResultsSuite struct{} 217 218 var _ = gc.Suite(&ErrorResultsSuite{}) 219 220 func (s *ErrorResultsSuite) TestOneError(c *gc.C) { 221 for i, test := range []struct { 222 results params.ErrorResults 223 errMatch string 224 }{ 225 { 226 errMatch: "expected 1 result, got 0", 227 }, { 228 results: params.ErrorResults{ 229 []params.ErrorResult{{nil}}, 230 }, 231 }, { 232 results: params.ErrorResults{ 233 []params.ErrorResult{{nil}, {nil}}, 234 }, 235 errMatch: "expected 1 result, got 2", 236 }, { 237 results: params.ErrorResults{ 238 []params.ErrorResult{ 239 {¶ms.Error{Message: "test error"}}, 240 }, 241 }, 242 errMatch: "test error", 243 }, 244 } { 245 c.Logf("test %d", i) 246 err := test.results.OneError() 247 if test.errMatch == "" { 248 c.Check(err, jc.ErrorIsNil) 249 } else { 250 c.Check(err, gc.ErrorMatches, test.errMatch) 251 } 252 } 253 } 254 255 func (s *ErrorResultsSuite) TestCombine(c *gc.C) { 256 for i, test := range []struct { 257 msg string 258 results params.ErrorResults 259 errMatch string 260 }{ 261 { 262 msg: "no results, no error", 263 }, { 264 msg: "single nil result", 265 results: params.ErrorResults{ 266 []params.ErrorResult{{nil}}, 267 }, 268 }, { 269 msg: "multiple nil results", 270 results: params.ErrorResults{ 271 []params.ErrorResult{{nil}, {nil}}, 272 }, 273 }, { 274 msg: "one error result", 275 results: params.ErrorResults{ 276 []params.ErrorResult{ 277 {¶ms.Error{Message: "test error"}}, 278 }, 279 }, 280 errMatch: "test error", 281 }, { 282 msg: "mixed error results", 283 results: params.ErrorResults{ 284 []params.ErrorResult{ 285 {¶ms.Error{Message: "test error"}}, 286 {nil}, 287 {¶ms.Error{Message: "second error"}}, 288 }, 289 }, 290 errMatch: "test error\nsecond error", 291 }, 292 } { 293 c.Logf("test %d: %s", i, test.msg) 294 err := test.results.Combine() 295 if test.errMatch == "" { 296 c.Check(err, jc.ErrorIsNil) 297 } else { 298 c.Check(err, gc.ErrorMatches, test.errMatch) 299 } 300 } 301 } 302 303 type importSuite struct{} 304 305 var _ = gc.Suite(&importSuite{}) 306 307 func (*importSuite) TestParamsDoesNotDependOnState(c *gc.C) { 308 imports := testing.FindJujuCoreImports(c, "github.com/juju/juju/apiserver/params") 309 for _, i := range imports { 310 c.Assert(i, gc.Not(gc.Equals), "state") 311 } 312 }