github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/juju/storage/add_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storage_test 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/juju/cmd" 11 "github.com/juju/errors" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/apiserver/common" 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/cmd/juju/storage" 18 _ "github.com/juju/juju/provider/dummy" 19 "github.com/juju/juju/testing" 20 ) 21 22 type addSuite struct { 23 SubStorageSuite 24 mockAPI *mockAddAPI 25 args []string 26 } 27 28 var _ = gc.Suite(&addSuite{}) 29 30 func (s *addSuite) SetUpTest(c *gc.C) { 31 s.SubStorageSuite.SetUpTest(c) 32 33 s.mockAPI = &mockAddAPI{ 34 addToUnitFunc: func(storages []params.StorageAddParams) ([]params.ErrorResult, error) { 35 result := make([]params.ErrorResult, len(storages)) 36 for i, one := range storages { 37 if strings.HasPrefix(one.StorageName, "err") { 38 result[i].Error = common.ServerError(errors.Errorf("test failure")) 39 } 40 } 41 return result, nil 42 }, 43 } 44 s.args = nil 45 } 46 47 type tstData struct { 48 args []string 49 expectedErr string 50 visibleErr string 51 } 52 53 var errorTsts = []tstData{ 54 { 55 args: nil, 56 expectedErr: "add-storage requires a unit and a storage directive", 57 visibleErr: "add-storage requires a unit and a storage directive", 58 }, 59 { 60 args: []string{"tst/123"}, 61 expectedErr: "add-storage requires a unit and a storage directive", 62 visibleErr: "add-storage requires a unit and a storage directive", 63 }, 64 { 65 args: []string{"tst/123", "data="}, 66 expectedErr: `storage constraints require at least one field to be specified`, 67 visibleErr: `cannot parse constraints for storage "data": storage constraints require at least one field to be specified`, 68 }, 69 { 70 args: []string{"tst/123", "data=-676"}, 71 expectedErr: `count must be greater than zero, got "-676"`, 72 visibleErr: `cannot parse constraints for storage "data": cannot parse count: count must be greater than zero, got "-676"`, 73 }, 74 { 75 args: []string{"tst/123", "data=676", "data=676"}, 76 expectedErr: `storage "data" specified more than once`, 77 visibleErr: `storage "data" specified more than once`, 78 }, 79 } 80 81 func (s *addSuite) TestAddArgs(c *gc.C) { 82 for i, t := range errorTsts { 83 c.Logf("test %d for %q", i, t.args) 84 s.args = t.args 85 s.assertAddErrorOutput(c, t.expectedErr, "", visibleErrorMessage(t.visibleErr)) 86 } 87 } 88 89 func (s *addSuite) TestAddInvalidUnit(c *gc.C) { 90 s.args = []string{"tst-123", "data=676"} 91 92 expectedErr := `unit name "tst-123" not valid` 93 s.assertAddErrorOutput(c, expectedErr, "", visibleErrorMessage(expectedErr)) 94 } 95 96 var validArgs = [][]string{ 97 []string{"tst/123", "data=676"}, 98 []string{"tst/123", "data"}, 99 } 100 101 func (s *addSuite) TestAddSuccess(c *gc.C) { 102 for i, args := range validArgs { 103 c.Logf("test %d for %q", i, args) 104 s.args = args 105 s.assertAddOutput(c, "added \"data\"\n", "") 106 } 107 } 108 109 func (s *addSuite) TestAddOperationAborted(c *gc.C) { 110 s.args = []string{"tst/123", "data=676"} 111 s.mockAPI.addToUnitFunc = func(storages []params.StorageAddParams) ([]params.ErrorResult, error) { 112 return nil, errors.New("aborted") 113 } 114 s.assertAddErrorOutput(c, ".*aborted.*", "", "") 115 } 116 117 func (s *addSuite) TestAddFailure(c *gc.C) { 118 s.args = []string{"tst/123", "err=676"} 119 s.assertAddErrorOutput(c, "cmd: error out silently", "", "failed to add \"err\": test failure\n") 120 } 121 122 func (s *addSuite) TestAddMixOrderPreserved(c *gc.C) { 123 expectedOut := ` 124 added "a" 125 `[1:] 126 expectedErr := ` 127 failed to add "err": test failure 128 `[1:] 129 130 s.args = []string{"tst/123", "a=676", "err=676"} 131 s.assertAddErrorOutput(c, "cmd: error out silently", expectedOut, expectedErr) 132 133 s.args = []string{"tst/123", "err=676", "a=676"} 134 s.assertAddErrorOutput(c, "cmd: error out silently", expectedOut, expectedErr) 135 } 136 137 func (s *addSuite) TestAddAllDistinctErrors(c *gc.C) { 138 expectedOut := ` 139 added "storage0" 140 added "storage1" 141 `[1:] 142 expectedErr := ` 143 failed to add "storage2": storage pool "barf" not found 144 failed to add "storage42": storage "storage42" not found 145 `[1:] 146 147 s.args = []string{"tst/123", "storage0=ebs", "storage2=barf", "storage1=123", "storage42=loop"} 148 s.mockAPI.addToUnitFunc = func(storages []params.StorageAddParams) ([]params.ErrorResult, error) { 149 result := make([]params.ErrorResult, len(storages)) 150 for i, one := range storages { 151 if one.StorageName == "storage2" { 152 result[i].Error = common.ServerError(errors.Errorf(`storage pool "barf" not found`)) 153 } 154 if one.StorageName == "storage42" { 155 result[i].Error = common.ServerError(errors.Errorf(`storage "storage42" not found`)) 156 } 157 } 158 return result, nil 159 } 160 161 s.assertAddErrorOutput(c, "cmd: error out silently", expectedOut, expectedErr) 162 } 163 164 func (s *addSuite) TestAddStorageOnlyDistinctErrors(c *gc.C) { 165 expectedOut := ` 166 added "storage0" 167 `[1:] 168 expectedErr := ` 169 failed to add "storage2": storage "storage2" not found 170 failed to add "storage42": storage "storage42" not found 171 `[1:] 172 173 s.args = []string{"tst/123", "storage0=ebs", "storage2=barf", "storage42=loop"} 174 s.mockAPI.addToUnitFunc = func(storages []params.StorageAddParams) ([]params.ErrorResult, error) { 175 result := make([]params.ErrorResult, len(storages)) 176 for i, one := range storages { 177 if one.StorageName == "storage42" || one.StorageName == "storage2" { 178 result[i].Error = common.ServerError(errors.Errorf(`storage "%v" not found`, one.StorageName)) 179 } 180 } 181 return result, nil 182 } 183 184 s.assertAddErrorOutput(c, "cmd: error out silently", expectedOut, expectedErr) 185 } 186 187 func (s *addSuite) TestAddStorageMixDistinctAndNonDistinctErrors(c *gc.C) { 188 expectedOut := `` 189 expectedErr := ` 190 some unit error 191 storage "storage0" not found 192 `[1:] 193 194 unitErr := `some unit error` 195 s.args = []string{"tst/123", "storage0=ebs", "storage2=barf", "storage42=loop"} 196 s.mockAPI.addToUnitFunc = func(storages []params.StorageAddParams) ([]params.ErrorResult, error) { 197 result := make([]params.ErrorResult, len(storages)) 198 for i, one := range storages { 199 if one.StorageName == "storage42" || one.StorageName == "storage2" { 200 result[i].Error = common.ServerError(errors.New(unitErr)) 201 } else { 202 result[i].Error = common.ServerError(errors.Errorf(`storage "%v" not found`, one.StorageName)) 203 } 204 } 205 return result, nil 206 } 207 208 s.assertAddErrorOutput(c, "cmd: error out silently", expectedOut, expectedErr) 209 } 210 211 func (s *addSuite) TestCollapseUnitErrors(c *gc.C) { 212 expectedErr := `some unit error` 213 214 s.args = []string{"tst/123", "storage0=ebs", "storage2=barf", "storage1=123", "storage42=loop"} 215 s.mockAPI.addToUnitFunc = func(storages []params.StorageAddParams) ([]params.ErrorResult, error) { 216 result := make([]params.ErrorResult, len(storages)) 217 for i, _ := range storages { 218 result[i].Error = common.ServerError(errors.New(expectedErr)) 219 } 220 return result, nil 221 } 222 223 s.assertAddErrorOutput(c, "cmd: error out silently", "", fmt.Sprintf("%v\n", expectedErr)) 224 } 225 226 func (s *addSuite) assertAddOutput(c *gc.C, expectedOut, expectedErr string) { 227 context, err := s.runAdd(c, s.args...) 228 c.Assert(err, jc.ErrorIsNil) 229 230 s.assertExpectedOutput(c, context, expectedOut, expectedErr) 231 } 232 233 func (s *addSuite) assertAddErrorOutput(c *gc.C, expected string, expectedOut, expectedErr string) { 234 context, err := s.runAdd(c, s.args...) 235 c.Assert(errors.Cause(err), gc.ErrorMatches, expected) 236 s.assertExpectedOutput(c, context, expectedOut, expectedErr) 237 } 238 239 func (s *addSuite) assertExpectedOutput(c *gc.C, context *cmd.Context, expectedOut, expectedErr string) { 240 obtainedErr := testing.Stderr(context) 241 c.Assert(obtainedErr, gc.Equals, expectedErr) 242 243 obtainedValid := testing.Stdout(context) 244 c.Assert(obtainedValid, gc.Equals, expectedOut) 245 } 246 247 func (s *addSuite) runAdd(c *gc.C, args ...string) (*cmd.Context, error) { 248 return testing.RunCommand(c, storage.NewAddCommandForTest(s.mockAPI, s.store), args...) 249 } 250 251 func visibleErrorMessage(errMsg string) string { 252 return fmt.Sprintf("error: %v\n", errMsg) 253 } 254 255 type mockAddAPI struct { 256 addToUnitFunc func(storages []params.StorageAddParams) ([]params.ErrorResult, error) 257 } 258 259 func (s mockAddAPI) Close() error { 260 return nil 261 } 262 263 func (s mockAddAPI) AddToUnit(storages []params.StorageAddParams) ([]params.ErrorResult, error) { 264 return s.addToUnitFunc(storages) 265 }