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  					{&params.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  					{&params.Error{Message: "test error"}},
   278  				},
   279  			},
   280  			errMatch: "test error",
   281  		}, {
   282  			msg: "mixed error results",
   283  			results: params.ErrorResults{
   284  				[]params.ErrorResult{
   285  					{&params.Error{Message: "test error"}},
   286  					{nil},
   287  					{&params.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  }