github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/uniter/runner/jujuc/relation-set_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Copyright 2014 Cloudbase Solutions SRL 3 // Licensed under the AGPLv3, see LICENCE file for details. 4 5 package jujuc_test 6 7 import ( 8 "bytes" 9 "fmt" 10 "io/ioutil" 11 "path/filepath" 12 13 "github.com/juju/cmd" 14 "github.com/juju/cmd/cmdtesting" 15 jc "github.com/juju/testing/checkers" 16 gc "gopkg.in/check.v1" 17 18 "github.com/juju/juju/worker/uniter/runner/jujuc" 19 "github.com/juju/juju/worker/uniter/runner/jujuc/jujuctesting" 20 ) 21 22 type RelationSetSuite struct { 23 relationSuite 24 } 25 26 var _ = gc.Suite(&RelationSetSuite{}) 27 28 var helpTests = []struct { 29 relid int 30 expect string 31 }{{-1, ""}, {0, "peer0:0"}} 32 33 func (s *RelationSetSuite) TestHelp(c *gc.C) { 34 for i, t := range helpTests { 35 c.Logf("test %d", i) 36 hctx, _ := s.newHookContext(t.relid, "") 37 com, err := jujuc.NewCommand(hctx, cmdString("relation-set")) 38 c.Assert(err, jc.ErrorIsNil) 39 ctx := cmdtesting.Context(c) 40 code := cmd.Main(jujuc.NewJujucCommandWrappedForTest(com), ctx, []string{"--help"}) 41 c.Assert(code, gc.Equals, 0) 42 c.Assert(bufferString(ctx.Stdout), gc.Equals, fmt.Sprintf(` 43 Usage: relation-set [options] key=value [key=value ...] 44 45 Summary: 46 set relation settings 47 48 Options: 49 --file (= ) 50 file containing key-value pairs 51 --format (= "") 52 deprecated format flag 53 -r, --relation (= %s) 54 specify a relation by id 55 56 Details: 57 "relation-set" writes the local unit's settings for some relation. 58 If no relation is specified then the current relation is used. The 59 setting values are not inspected and are stored as strings. Setting 60 an empty string causes the setting to be removed. Duplicate settings 61 are not allowed. 62 63 The --file option should be used when one or more key-value pairs are 64 too long to fit within the command length limit of the shell or 65 operating system. The file will contain a YAML map containing the 66 settings. Settings in the file will be overridden by any duplicate 67 key-value arguments. A value of "-" for the filename means <stdin>. 68 `[1:], t.expect)) 69 c.Assert(bufferString(ctx.Stderr), gc.Equals, "") 70 } 71 } 72 73 type relationSetInitTest struct { 74 summary string 75 ctxrelid int 76 args []string 77 content string 78 err string 79 relid int 80 settings map[string]string 81 } 82 83 func (t relationSetInitTest) log(c *gc.C, i int) { 84 var summary string 85 if t.summary != "" { 86 summary = " - " + t.summary 87 } 88 c.Logf("test %d%s", i, summary) 89 } 90 91 func (t relationSetInitTest) filename() (string, int) { 92 for i, arg := range t.args { 93 next := i + 1 94 if arg == "--file" && next < len(t.args) { 95 return t.args[next], next 96 } 97 } 98 return "", -1 99 } 100 101 func (t relationSetInitTest) init(c *gc.C, s *RelationSetSuite) (cmd.Command, []string, *cmd.Context) { 102 args := make([]string, len(t.args)) 103 copy(args, t.args) 104 105 hctx, _ := s.newHookContext(t.ctxrelid, "") 106 com, err := jujuc.NewCommand(hctx, cmdString("relation-set")) 107 c.Assert(err, jc.ErrorIsNil) 108 109 ctx := cmdtesting.Context(c) 110 111 // Adjust the args and context for the filename. 112 filename, i := t.filename() 113 if filename == "-" { 114 ctx.Stdin = bytes.NewBufferString(t.content) 115 } else if filename != "" { 116 filename = filepath.Join(c.MkDir(), filename) 117 args[i] = filename 118 err := ioutil.WriteFile(filename, []byte(t.content), 0644) 119 c.Assert(err, jc.ErrorIsNil) 120 } 121 122 return com, args, ctx 123 } 124 125 func (t relationSetInitTest) check(c *gc.C, com cmd.Command, err error) { 126 if t.err == "" { 127 if !c.Check(err, jc.ErrorIsNil) { 128 return 129 } 130 131 rset := com.(*jujuc.RelationSetCommand) 132 c.Check(rset.RelationId, gc.Equals, t.relid) 133 134 settings := t.settings 135 if settings == nil { 136 settings = map[string]string{} 137 } 138 c.Check(rset.Settings, jc.DeepEquals, settings) 139 } else { 140 c.Logf("%#v", com.(*jujuc.RelationSetCommand).Settings) 141 c.Check(err, gc.ErrorMatches, t.err) 142 } 143 } 144 145 var relationSetInitTests = []relationSetInitTest{ 146 { 147 // compatibility: 0 args is valid. 148 }, { 149 ctxrelid: -1, 150 err: `no relation id specified`, 151 }, { 152 ctxrelid: -1, 153 args: []string{"-r", "one"}, 154 err: `invalid value "one" for option -r: invalid relation id`, 155 }, { 156 ctxrelid: 1, 157 args: []string{"-r", "one"}, 158 err: `invalid value "one" for option -r: invalid relation id`, 159 }, { 160 ctxrelid: -1, 161 args: []string{"-r", "ignored:one"}, 162 err: `invalid value "ignored:one" for option -r: invalid relation id`, 163 }, { 164 ctxrelid: 1, 165 args: []string{"-r", "ignored:one"}, 166 err: `invalid value "ignored:one" for option -r: invalid relation id`, 167 }, { 168 ctxrelid: -1, 169 args: []string{"-r", "2"}, 170 err: `invalid value "2" for option -r: relation not found`, 171 }, { 172 ctxrelid: 1, 173 args: []string{"-r", "ignored:2"}, 174 err: `invalid value "ignored:2" for option -r: relation not found`, 175 }, { 176 ctxrelid: -1, 177 err: `no relation id specified`, 178 }, { 179 ctxrelid: 1, 180 args: []string{"-r", "ignored:0"}, 181 relid: 0, 182 }, { 183 ctxrelid: 1, 184 args: []string{"-r", "0"}, 185 relid: 0, 186 }, { 187 ctxrelid: -1, 188 args: []string{"-r", "1"}, 189 relid: 1, 190 }, { 191 ctxrelid: 0, 192 args: []string{"-r", "1"}, 193 relid: 1, 194 }, { 195 ctxrelid: 1, 196 args: []string{"haha"}, 197 err: `expected "key=value", got "haha"`, 198 }, { 199 ctxrelid: 1, 200 args: []string{"=haha"}, 201 err: `expected "key=value", got "=haha"`, 202 }, { 203 ctxrelid: 1, 204 args: []string{"foo="}, 205 relid: 1, 206 settings: map[string]string{"foo": ""}, 207 }, { 208 ctxrelid: 1, 209 args: []string{"foo='"}, 210 relid: 1, 211 settings: map[string]string{"foo": "'"}, 212 }, { 213 ctxrelid: 1, 214 args: []string{"foo=bar"}, 215 relid: 1, 216 settings: map[string]string{"foo": "bar"}, 217 }, { 218 ctxrelid: 1, 219 args: []string{"foo=bar=baz=qux"}, 220 relid: 1, 221 settings: map[string]string{"foo": "bar=baz=qux"}, 222 }, { 223 ctxrelid: 1, 224 args: []string{"foo=foo: bar"}, 225 relid: 1, 226 settings: map[string]string{"foo": "foo: bar"}, 227 }, { 228 ctxrelid: 0, 229 args: []string{"-r", "1", "foo=bar"}, 230 relid: 1, 231 settings: map[string]string{"foo": "bar"}, 232 }, { 233 ctxrelid: 1, 234 args: []string{"foo=123", "bar=true", "baz=4.5", "qux="}, 235 relid: 1, 236 settings: map[string]string{"foo": "123", "bar": "true", "baz": "4.5", "qux": ""}, 237 }, { 238 summary: "file with a valid setting", 239 args: []string{"--file", "spam"}, 240 content: "{foo: bar}", 241 settings: map[string]string{"foo": "bar"}, 242 }, { 243 summary: "file with multiple settings on a line", 244 args: []string{"--file", "spam"}, 245 content: "{foo: bar, spam: eggs}", 246 settings: map[string]string{"foo": "bar", "spam": "eggs"}, 247 }, { 248 summary: "file with multiple lines", 249 args: []string{"--file", "spam"}, 250 content: "{\n foo: bar,\n spam: eggs\n}", 251 settings: map[string]string{"foo": "bar", "spam": "eggs"}, 252 }, { 253 summary: "an empty file", 254 args: []string{"--file", "spam"}, 255 content: "", 256 settings: map[string]string{}, 257 }, { 258 summary: "an empty map", 259 args: []string{"--file", "spam"}, 260 content: "{}", 261 settings: map[string]string{}, 262 }, { 263 summary: "accidental same format as command-line", 264 args: []string{"--file", "spam"}, 265 content: "foo=bar ham=eggs good=bad", 266 err: "yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `foo=bar...` into map.*", 267 }, { 268 summary: "scalar instead of map", 269 args: []string{"--file", "spam"}, 270 content: "haha", 271 err: "yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `haha` into map.*", 272 }, { 273 summary: "sequence instead of map", 274 args: []string{"--file", "spam"}, 275 content: "[haha]", 276 err: "yaml: unmarshal errors:\n line 1: cannot unmarshal !!seq into map.*", 277 }, { 278 summary: "multiple maps", 279 args: []string{"--file", "spam"}, 280 content: "{a: b}\n{c: d}", 281 settings: map[string]string{"a": "b"}, 282 }, { 283 summary: "value with a space", 284 args: []string{"--file", "spam"}, 285 content: "{foo: 'bar baz'}", 286 settings: map[string]string{"foo": "bar baz"}, 287 }, { 288 summary: "value with an equal sign", 289 args: []string{"--file", "spam"}, 290 content: "{foo: foo=bar, base64: YmFzZTY0IGV4YW1wbGU=}", 291 settings: map[string]string{"foo": "foo=bar", "base64": "YmFzZTY0IGV4YW1wbGU="}, 292 }, { 293 summary: "values with brackets", 294 args: []string{"--file", "spam"}, 295 content: "{foo: '[x]', bar: '{y}'}", 296 settings: map[string]string{"foo": "[x]", "bar": "{y}"}, 297 }, { 298 summary: "a messy file", 299 args: []string{"--file", "spam"}, 300 content: "\n { \n # a comment \n\n \nfoo: bar, \nham: eggs,\n\n good: bad,\nup: down, left: right\n}\n", 301 settings: map[string]string{"foo": "bar", "ham": "eggs", "good": "bad", "up": "down", "left": "right"}, 302 }, { 303 summary: "file + settings", 304 args: []string{"--file", "spam", "foo=bar"}, 305 content: "{ham: eggs}", 306 settings: map[string]string{"ham": "eggs", "foo": "bar"}, 307 }, { 308 summary: "file overridden by settings", 309 args: []string{"--file", "spam", "foo=bar"}, 310 content: "{foo: baz}", 311 settings: map[string]string{"foo": "bar"}, 312 }, { 313 summary: "read from stdin", 314 args: []string{"--file", "-"}, 315 content: "{foo: bar}", 316 settings: map[string]string{"foo": "bar"}, 317 }, 318 } 319 320 func (s *RelationSetSuite) TestInit(c *gc.C) { 321 for i, t := range relationSetInitTests { 322 t.log(c, i) 323 com, args, ctx := t.init(c, s) 324 325 err := cmdtesting.InitCommand(com, args) 326 if err == nil { 327 err = jujuc.HandleSettingsFile(com.(*jujuc.RelationSetCommand), ctx) 328 } 329 t.check(c, com, err) 330 } 331 } 332 333 // Tests start with a relation with the settings {"base": "value"} 334 var relationSetRunTests = []struct { 335 change map[string]string 336 expect jujuctesting.Settings 337 }{ 338 { 339 map[string]string{"base": ""}, 340 jujuctesting.Settings{}, 341 }, { 342 map[string]string{"foo": "bar"}, 343 jujuctesting.Settings{"base": "value", "foo": "bar"}, 344 }, { 345 map[string]string{"base": "changed"}, 346 jujuctesting.Settings{"base": "changed"}, 347 }, 348 } 349 350 func (s *RelationSetSuite) TestRun(c *gc.C) { 351 hctx, info := s.newHookContext(0, "") 352 for i, t := range relationSetRunTests { 353 c.Logf("test %d", i) 354 355 pristine := jujuctesting.Settings{"pristine": "untouched"} 356 info.rels[0].Units["u/0"] = pristine 357 basic := jujuctesting.Settings{"base": "value"} 358 info.rels[1].Units["u/0"] = basic 359 360 // Run the command. 361 com, err := jujuc.NewCommand(hctx, cmdString("relation-set")) 362 c.Assert(err, jc.ErrorIsNil) 363 rset := com.(*jujuc.RelationSetCommand) 364 rset.RelationId = 1 365 rset.Settings = t.change 366 ctx := cmdtesting.Context(c) 367 err = com.Run(ctx) 368 c.Assert(err, jc.ErrorIsNil) 369 370 // Check changes. 371 c.Assert(info.rels[0].Units["u/0"], gc.DeepEquals, pristine) 372 c.Assert(info.rels[1].Units["u/0"], gc.DeepEquals, t.expect) 373 } 374 } 375 376 func (s *RelationSetSuite) TestRunDeprecationWarning(c *gc.C) { 377 hctx, _ := s.newHookContext(0, "") 378 com, _ := jujuc.NewCommand(hctx, cmdString("relation-set")) 379 com = jujuc.NewJujucCommandWrappedForTest(com) 380 // The rel= is needed to make this a valid command. 381 ctx, err := cmdtesting.RunCommand(c, com, "--format", "foo", "rel=") 382 383 c.Assert(err, jc.ErrorIsNil) 384 c.Assert(cmdtesting.Stdout(ctx), gc.Equals, "") 385 c.Assert(cmdtesting.Stderr(ctx), gc.Equals, "--format flag deprecated for command \"relation-set\"") 386 }