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