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