github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/interact/pollster_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package interact
     5  
     6  import (
     7  	"bytes"
     8  	"io"
     9  	"os"
    10  	"strings"
    11  
    12  	"github.com/juju/errors"
    13  	"github.com/juju/jsonschema"
    14  	"github.com/juju/testing"
    15  	jc "github.com/juju/testing/checkers"
    16  	gc "gopkg.in/check.v1"
    17  )
    18  
    19  type PollsterSuite struct {
    20  	testing.IsolationSuite
    21  }
    22  
    23  var _ = gc.Suite(PollsterSuite{})
    24  
    25  func (p PollsterSuite) TearDownTest(c *gc.C) {
    26  	p.IsolationSuite.TearDownTest(c)
    27  	os.Unsetenv("SCHEMA_VAR")
    28  	os.Unsetenv("SCHEMA_VAR_TWO")
    29  }
    30  
    31  func (PollsterSuite) TestSelect(c *gc.C) {
    32  	r := strings.NewReader("macintosh")
    33  	w := &bytes.Buffer{}
    34  	p := New(r, w, w)
    35  	s, err := p.Select(List{
    36  		Singular: "apple",
    37  		Plural:   "apples",
    38  		Options:  []string{"macintosh", "granny smith"},
    39  	})
    40  	c.Assert(err, jc.ErrorIsNil)
    41  	c.Assert(s, gc.Equals, "macintosh")
    42  
    43  	// Note: please only check the full output here, so that we don't have to
    44  	// edit a million tests if we make minor tweaks to the output.
    45  	c.Assert(w.String(), gc.Equals, `
    46  Apples
    47    macintosh
    48    granny smith
    49  
    50  Select apple: 
    51  `[1:])
    52  }
    53  
    54  func (PollsterSuite) TestSelectDefault(c *gc.C) {
    55  	r := strings.NewReader("\n")
    56  	w := &bytes.Buffer{}
    57  	p := New(r, w, w)
    58  	s, err := p.Select(List{
    59  		Singular: "apple",
    60  		Plural:   "apples",
    61  		Options:  []string{"macintosh", "granny smith"},
    62  		Default:  "macintosh",
    63  	})
    64  	c.Assert(err, jc.ErrorIsNil)
    65  	c.Assert(s, gc.Equals, "macintosh")
    66  	c.Assert(w.String(), jc.Contains, `Select apple [macintosh]: `)
    67  }
    68  
    69  func (PollsterSuite) TestSelectDefaultIfOnlyOption(c *gc.C) {
    70  	r := strings.NewReader("\n")
    71  	w := &bytes.Buffer{}
    72  	p := New(r, w, w)
    73  	s, err := p.Select(List{
    74  		Singular: "apple",
    75  		Plural:   "apples",
    76  		Options:  []string{"macintosh"},
    77  		Default:  "macintosh",
    78  	})
    79  	c.Assert(err, jc.ErrorIsNil)
    80  	c.Assert(s, gc.Equals, "macintosh")
    81  	c.Assert(w.String(), jc.Contains, `Select apple [macintosh]: `)
    82  }
    83  
    84  func (PollsterSuite) TestSelectIncorrect(c *gc.C) {
    85  	r := strings.NewReader("mac\nmacintosh")
    86  	w := &bytes.Buffer{}
    87  	p := New(r, w, w)
    88  	s, err := p.Select(List{
    89  		Singular: "apple",
    90  		Plural:   "apples",
    91  		Options:  []string{"macintosh", "granny smith"},
    92  	})
    93  	c.Assert(err, jc.ErrorIsNil)
    94  	c.Assert(s, gc.Equals, "macintosh")
    95  
    96  	c.Assert(squash(w.String()), jc.Contains, `Invalid apple: "mac"Select apple:`)
    97  }
    98  
    99  // squash removes all newlines from the given string so our tests can be more
   100  // resilient in the face of minor tweaks to spacing.
   101  func squash(s string) string {
   102  	return strings.Replace(s, "\n", "", -1)
   103  }
   104  
   105  func (PollsterSuite) TestSelectNoMultiple(c *gc.C) {
   106  	r := strings.NewReader("macintosh,granny smith\ngranny smith")
   107  	w := &bytes.Buffer{}
   108  	p := New(r, w, w)
   109  	s, err := p.Select(List{
   110  		Singular: "apple",
   111  		Plural:   "apples",
   112  		Options:  []string{"macintosh", "granny smith"},
   113  	})
   114  	c.Assert(err, jc.ErrorIsNil)
   115  	c.Assert(s, gc.Equals, "granny smith")
   116  	c.Assert(w.String(), jc.Contains, `Invalid apple: "macintosh,granny smith"`)
   117  }
   118  
   119  func (PollsterSuite) TestMultiSelectSingle(c *gc.C) {
   120  	r := strings.NewReader("macintosh")
   121  	w := &bytes.Buffer{}
   122  	p := New(r, w, w)
   123  	vals, err := p.MultiSelect(MultiList{
   124  		Singular: "apple",
   125  		Plural:   "apples",
   126  		Options:  []string{"macintosh", "granny smith", "gala"},
   127  	})
   128  	c.Assert(err, jc.ErrorIsNil)
   129  	c.Assert(vals, jc.SameContents, []string{"macintosh"})
   130  }
   131  
   132  func (PollsterSuite) TestMultiSelectMany(c *gc.C) {
   133  	// note there's a couple spaces in the middle here that we're stripping out.
   134  	r := strings.NewReader("macintosh,  granny smith")
   135  	w := &bytes.Buffer{}
   136  	p := New(r, w, w)
   137  	vals, err := p.MultiSelect(MultiList{
   138  		Singular: "apple",
   139  		Plural:   "apples",
   140  		Options:  []string{"macintosh", "granny smith", "gala"},
   141  	})
   142  	c.Assert(err, jc.ErrorIsNil)
   143  	c.Assert(vals, jc.SameContents, []string{"macintosh", "granny smith"})
   144  }
   145  
   146  func (PollsterSuite) TestMultiSelectDefault(c *gc.C) {
   147  	r := strings.NewReader("\n")
   148  	w := &bytes.Buffer{}
   149  	p := New(r, w, w)
   150  	vals, err := p.MultiSelect(MultiList{
   151  		Singular: "apple",
   152  		Plural:   "apples",
   153  		Options:  []string{"macintosh", "granny smith", "gala"},
   154  		Default:  []string{"gala", "granny smith"},
   155  	})
   156  	c.Assert(err, jc.ErrorIsNil)
   157  	c.Assert(vals, jc.SameContents, []string{"gala", "granny smith"})
   158  }
   159  
   160  func (PollsterSuite) TestMultiSelectDefaultIfOnlyOne(c *gc.C) {
   161  	r := strings.NewReader("\n")
   162  	w := &bytes.Buffer{}
   163  	p := New(r, w, w)
   164  	vals, err := p.MultiSelect(MultiList{
   165  		Singular: "apple",
   166  		Plural:   "apples",
   167  		Options:  []string{"macintosh"},
   168  		Default:  []string{"macintosh"},
   169  	})
   170  	c.Assert(err, jc.ErrorIsNil)
   171  	c.Assert(vals, jc.SameContents, []string{"macintosh"})
   172  	c.Assert(w.String(), gc.Equals, "Apples\n  macintosh\n\n")
   173  }
   174  
   175  func (PollsterSuite) TestMultiSelectWithMultipleDefaults(c *gc.C) {
   176  	r := strings.NewReader("\n")
   177  	w := &bytes.Buffer{}
   178  	p := New(r, w, w)
   179  	vals, err := p.MultiSelect(MultiList{
   180  		Singular: "apple",
   181  		Plural:   "apples",
   182  		Options:  []string{"macintosh", "gala"},
   183  		Default:  []string{"macintosh", "gala"},
   184  	})
   185  	c.Assert(err, jc.ErrorIsNil)
   186  	c.Assert(vals, jc.SameContents, []string{"macintosh", "gala"})
   187  	c.Assert(w.String(), jc.Contains, "Select one or more apples separated by commas [macintosh, gala]: \n")
   188  }
   189  
   190  func (PollsterSuite) TestMultiSelectOneError(c *gc.C) {
   191  	r := strings.NewReader("mac\nmacintosh")
   192  	w := &bytes.Buffer{}
   193  	p := New(r, w, w)
   194  	vals, err := p.MultiSelect(MultiList{
   195  		Singular: "apple",
   196  		Plural:   "apples",
   197  		Options:  []string{"macintosh", "granny smith", "gala"},
   198  	})
   199  	c.Assert(err, jc.ErrorIsNil)
   200  	c.Assert(vals, jc.SameContents, []string{"macintosh"})
   201  	c.Assert(w.String(), jc.Contains, `Invalid apple: "mac"`)
   202  }
   203  
   204  func (PollsterSuite) TestMultiSelectManyErrors(c *gc.C) {
   205  	r := strings.NewReader("mac,  smith\nmacintosh")
   206  	w := &bytes.Buffer{}
   207  	p := New(r, w, w)
   208  	vals, err := p.MultiSelect(MultiList{
   209  		Singular: "apple",
   210  		Plural:   "apples",
   211  		Options:  []string{"macintosh", "granny smith", "gala"},
   212  	})
   213  	c.Assert(err, jc.ErrorIsNil)
   214  	c.Assert(vals, jc.SameContents, []string{"macintosh"})
   215  	c.Assert(w.String(), jc.Contains, `Invalid apples: "mac", "smith"`)
   216  }
   217  
   218  func (PollsterSuite) TestEnter(c *gc.C) {
   219  	r := strings.NewReader("Bill Smith")
   220  	w := &bytes.Buffer{}
   221  	p := New(r, w, w)
   222  	a, err := p.Enter("your name")
   223  	c.Assert(err, jc.ErrorIsNil)
   224  	c.Assert(a, gc.Equals, "Bill Smith")
   225  	c.Assert(w.String(), gc.Equals, "Enter your name: \n")
   226  }
   227  
   228  func (PollsterSuite) TestEnterEmpty(c *gc.C) {
   229  	r := strings.NewReader("\nBill")
   230  	w := &bytes.Buffer{}
   231  	p := New(r, w, w)
   232  	a, err := p.Enter("your name")
   233  	c.Assert(err, jc.ErrorIsNil)
   234  	c.Assert(a, gc.Equals, "Bill")
   235  	// We should re-query without any error on empty input.
   236  	c.Assert(squash(w.String()), jc.Contains, "Enter your name: Enter your name: ")
   237  }
   238  
   239  func (PollsterSuite) TestEnterVerify(c *gc.C) {
   240  	r := strings.NewReader("Bill Smith")
   241  	w := &bytes.Buffer{}
   242  	p := New(r, w, w)
   243  	verify := func(s string) (ok bool, errmsg string, err error) {
   244  		if s == "Bill Smith" {
   245  			return true, "", nil
   246  		}
   247  		return false, "not bill!", nil
   248  	}
   249  	a, err := p.EnterVerify("your name", verify)
   250  	c.Assert(err, jc.ErrorIsNil)
   251  	c.Assert(a, gc.Equals, "Bill Smith")
   252  	c.Assert(w.String(), gc.Equals, "Enter your name: \n")
   253  }
   254  
   255  func (PollsterSuite) TestEnterVerifyBad(c *gc.C) {
   256  	r := strings.NewReader("Will Smithy\nBill Smith")
   257  	w := &bytes.Buffer{}
   258  	p := New(r, w, w)
   259  	verify := func(s string) (ok bool, errmsg string, err error) {
   260  		if s == "Bill Smith" {
   261  			return true, "", nil
   262  		}
   263  		return false, "not bill!", nil
   264  	}
   265  	a, err := p.EnterVerify("your name", verify)
   266  	c.Assert(err, jc.ErrorIsNil)
   267  	c.Assert(a, gc.Equals, "Bill Smith")
   268  	c.Assert(squash(w.String()), gc.Equals, "Enter your name: not bill!Enter your name: ")
   269  }
   270  
   271  func (PollsterSuite) TestEnterDefaultNonEmpty(c *gc.C) {
   272  	r := strings.NewReader("Bill Smith")
   273  	w := &bytes.Buffer{}
   274  	p := New(r, w, w)
   275  	a, err := p.EnterDefault("your name", "John")
   276  	c.Assert(err, jc.ErrorIsNil)
   277  	c.Assert(a, gc.Equals, "Bill Smith")
   278  	c.Assert(w.String(), gc.Equals, "Enter your name [John]: \n")
   279  }
   280  
   281  func (PollsterSuite) TestEnterDefaultEmpty(c *gc.C) {
   282  	r := strings.NewReader("\n")
   283  	w := &bytes.Buffer{}
   284  	p := New(r, w, w)
   285  	a, err := p.EnterDefault("your name", "John")
   286  	c.Assert(err, jc.ErrorIsNil)
   287  	c.Assert(a, gc.Equals, "John")
   288  	// We should re-query without any error on empty input.
   289  	c.Assert(squash(w.String()), jc.Contains, "Enter your name [John]: ")
   290  }
   291  
   292  func (PollsterSuite) TestEnterVerifyDefaultEmpty(c *gc.C) {
   293  	r := strings.NewReader("\n")
   294  	w := &bytes.Buffer{}
   295  	p := New(r, w, w)
   296  	// note that the verification does not accept empty string, but the default
   297  	// should still work
   298  	verify := func(s string) (ok bool, errmsg string, err error) {
   299  		if s == "Bill Smith" {
   300  			return true, "", nil
   301  		}
   302  		return false, "not bill!", nil
   303  	}
   304  	a, err := p.EnterVerifyDefault("your name", verify, "John")
   305  	c.Assert(err, jc.ErrorIsNil)
   306  	c.Assert(a, gc.Equals, "John")
   307  	// We should re-query without any error on empty input.
   308  	c.Assert(squash(w.String()), jc.Contains, "Enter your name [John]: ")
   309  }
   310  
   311  func (PollsterSuite) TestYNDefaultFalse(c *gc.C) {
   312  	r := strings.NewReader("Y")
   313  	w := &bytes.Buffer{}
   314  	p := New(r, w, w)
   315  	a, err := p.YN("Should this test pass", false)
   316  	c.Assert(err, jc.ErrorIsNil)
   317  	c.Assert(a, jc.IsTrue)
   318  	c.Assert(w.String(), gc.Equals, "Should this test pass? (y/N): \n")
   319  }
   320  
   321  func (PollsterSuite) TestYNDefaultTrue(c *gc.C) {
   322  	r := strings.NewReader("Y")
   323  	w := &bytes.Buffer{}
   324  	p := New(r, w, w)
   325  	a, err := p.YN("Should this test pass", true)
   326  	c.Assert(err, jc.ErrorIsNil)
   327  	c.Assert(a, jc.IsTrue)
   328  	c.Assert(w.String(), gc.Equals, "Should this test pass? (Y/n): \n")
   329  }
   330  
   331  func (PollsterSuite) TestYNTable(c *gc.C) {
   332  	tests := []struct {
   333  		In       string
   334  		Def, Res bool
   335  	}{
   336  		0:  {In: "Y", Def: false, Res: true},
   337  		1:  {In: "y", Def: false, Res: true},
   338  		2:  {In: "yes", Def: false, Res: true},
   339  		3:  {In: "YES", Def: false, Res: true},
   340  		4:  {In: "N", Def: true, Res: false},
   341  		5:  {In: "n", Def: true, Res: false},
   342  		6:  {In: "no", Def: true, Res: false},
   343  		7:  {In: "NO", Def: true, Res: false},
   344  		8:  {In: "Y", Def: true, Res: true},
   345  		9:  {In: "y", Def: true, Res: true},
   346  		10: {In: "yes", Def: true, Res: true},
   347  		11: {In: "YES", Def: true, Res: true},
   348  		12: {In: "N", Def: false, Res: false},
   349  		13: {In: "n", Def: false, Res: false},
   350  		14: {In: "no", Def: false, Res: false},
   351  		15: {In: "NO", Def: false, Res: false},
   352  	}
   353  	for i, test := range tests {
   354  		c.Logf("test %d", i)
   355  		r := strings.NewReader(test.In)
   356  		w := &bytes.Buffer{}
   357  		p := New(r, w, w)
   358  		a, err := p.YN("doesn't matter", test.Def)
   359  		c.Assert(err, jc.ErrorIsNil)
   360  		c.Assert(a, gc.Equals, test.Res)
   361  	}
   362  }
   363  
   364  func (PollsterSuite) TestYNInvalid(c *gc.C) {
   365  	r := strings.NewReader("wat\nY")
   366  	w := &bytes.Buffer{}
   367  	p := New(r, w, w)
   368  	a, err := p.YN("Should this test pass", false)
   369  	c.Assert(err, jc.ErrorIsNil)
   370  	c.Assert(a, jc.IsTrue)
   371  	c.Assert(w.String(), jc.Contains, `Invalid entry: "wat", please choose y or n`)
   372  }
   373  
   374  func (PollsterSuite) TestQueryStringSchema(c *gc.C) {
   375  	schema := &jsonschema.Schema{
   376  		Singular: "region",
   377  		Type:     []jsonschema.Type{jsonschema.StringType},
   378  	}
   379  	r := strings.NewReader("wat")
   380  	w := &bytes.Buffer{}
   381  	p := New(r, w, w)
   382  	v, err := p.QuerySchema(schema)
   383  	c.Assert(err, jc.ErrorIsNil)
   384  	s, ok := v.(string)
   385  	c.Check(ok, jc.IsTrue)
   386  	c.Check(s, gc.Equals, "wat")
   387  	c.Assert(w.String(), jc.Contains, "Enter region:")
   388  }
   389  
   390  func (PollsterSuite) TestQueryStringSchemaWithDefault(c *gc.C) {
   391  	schema := &jsonschema.Schema{
   392  		Singular: "region",
   393  		Type:     []jsonschema.Type{jsonschema.StringType},
   394  		Default:  "foo",
   395  	}
   396  	r := strings.NewReader("\n")
   397  	w := &bytes.Buffer{}
   398  	p := New(r, w, w)
   399  	v, err := p.QuerySchema(schema)
   400  	c.Assert(err, jc.ErrorIsNil)
   401  	s, ok := v.(string)
   402  	c.Check(ok, jc.IsTrue)
   403  	c.Check(s, gc.Equals, "foo")
   404  	c.Assert(w.String(), jc.Contains, "Enter region [foo]:")
   405  }
   406  
   407  func (PollsterSuite) TestQueryStringSchemaWithUnusedDefault(c *gc.C) {
   408  	schema := &jsonschema.Schema{
   409  		Singular: "region",
   410  		Type:     []jsonschema.Type{jsonschema.StringType},
   411  		Default:  "foo",
   412  	}
   413  	r := strings.NewReader("bar\n")
   414  	w := &bytes.Buffer{}
   415  	p := New(r, w, w)
   416  	v, err := p.QuerySchema(schema)
   417  	c.Assert(err, jc.ErrorIsNil)
   418  	s, ok := v.(string)
   419  	c.Check(ok, jc.IsTrue)
   420  	c.Check(s, gc.Equals, "bar")
   421  	c.Assert(w.String(), jc.Contains, "Enter region [foo]:")
   422  }
   423  
   424  func (PollsterSuite) TestQueryStringSchemaWithPromptDefault(c *gc.C) {
   425  	schema := &jsonschema.Schema{
   426  		Singular:      "region",
   427  		Type:          []jsonschema.Type{jsonschema.StringType},
   428  		Default:       "foo",
   429  		PromptDefault: "not foo",
   430  	}
   431  	r := strings.NewReader("\n")
   432  	w := &bytes.Buffer{}
   433  	p := New(r, w, w)
   434  	v, err := p.QuerySchema(schema)
   435  	c.Assert(err, jc.ErrorIsNil)
   436  	s, ok := v.(string)
   437  	c.Check(ok, jc.IsTrue)
   438  	c.Check(s, gc.Equals, "foo")
   439  	c.Check(w.String(), jc.Contains, "Enter region [not foo]:")
   440  }
   441  
   442  func (PollsterSuite) TestQueryStringSchemaWithDefaultEnvVar(c *gc.C) {
   443  	schema := &jsonschema.Schema{
   444  		Singular: "region",
   445  		Type:     []jsonschema.Type{jsonschema.StringType},
   446  		Default:  "",
   447  		EnvVars:  []string{"SCHEMA_VAR"},
   448  	}
   449  	os.Setenv("SCHEMA_VAR", "value from env var")
   450  	r := strings.NewReader("\n")
   451  	w := &bytes.Buffer{}
   452  	p := New(r, w, w)
   453  	v, err := p.QuerySchema(schema)
   454  	c.Assert(err, jc.ErrorIsNil)
   455  	s, ok := v.(string)
   456  	c.Check(ok, jc.IsTrue)
   457  	c.Check(s, gc.Equals, "value from env var")
   458  	c.Assert(w.String(), jc.Contains, "Enter region [value from env var]:")
   459  }
   460  
   461  func (PollsterSuite) TestQueryStringSchemaWithDefaultEnvVarOverride(c *gc.C) {
   462  	schema := &jsonschema.Schema{
   463  		Singular: "region",
   464  		Type:     []jsonschema.Type{jsonschema.StringType},
   465  		Default:  "",
   466  		EnvVars:  []string{"SCHEMA_VAR"},
   467  	}
   468  	os.Setenv("SCHEMA_VAR", "value from env var")
   469  	r := strings.NewReader("use me\n")
   470  	w := &bytes.Buffer{}
   471  	p := New(r, w, w)
   472  	v, err := p.QuerySchema(schema)
   473  	c.Assert(err, jc.ErrorIsNil)
   474  	s, ok := v.(string)
   475  	c.Check(ok, jc.IsTrue)
   476  	c.Check(s, gc.Equals, "use me")
   477  	c.Assert(w.String(), jc.Contains, "Enter region [value from env var]:")
   478  }
   479  
   480  func (PollsterSuite) TestQueryStringSchemaWithDefaultTwoEnvVar(c *gc.C) {
   481  	schema := &jsonschema.Schema{
   482  		Singular: "region",
   483  		Type:     []jsonschema.Type{jsonschema.StringType},
   484  		Default:  "",
   485  		EnvVars:  []string{"SCHEMA_VAR", "SCHEMA_VAR_TWO"},
   486  	}
   487  	os.Setenv("SCHEMA_VAR_TWO", "value from second")
   488  	r := strings.NewReader("\n")
   489  	w := &bytes.Buffer{}
   490  	p := New(r, w, w)
   491  	v, err := p.QuerySchema(schema)
   492  	c.Assert(err, jc.ErrorIsNil)
   493  	s, ok := v.(string)
   494  	c.Check(ok, jc.IsTrue)
   495  	c.Check(s, gc.Equals, "value from second")
   496  	c.Assert(w.String(), jc.Contains, "Enter region [value from second]:")
   497  }
   498  
   499  func (PollsterSuite) TestQueryURISchema(c *gc.C) {
   500  	schema := &jsonschema.Schema{
   501  		Singular: "region",
   502  		Type:     []jsonschema.Type{jsonschema.StringType},
   503  		Format:   jsonschema.FormatURI,
   504  	}
   505  	// invalid escape sequence
   506  	r := strings.NewReader("https://&%5abc")
   507  	w := &bytes.Buffer{}
   508  	p := New(r, w, w)
   509  	_, err := p.QuerySchema(schema)
   510  	c.Check(errors.Cause(err), gc.Equals, io.EOF)
   511  	c.Assert(w.String(), gc.Equals, `
   512  Enter region: Invalid URI: "https://&%5abc"
   513  
   514  Enter region: 
   515  `[1:])
   516  }
   517  
   518  func (PollsterSuite) TestQueryArraySchema(c *gc.C) {
   519  	schema := &jsonschema.Schema{
   520  		Singular: "number",
   521  		Plural:   "numbers",
   522  		Type:     []jsonschema.Type{jsonschema.ArrayType},
   523  		Items: &jsonschema.ItemSpec{
   524  			Schemas: []*jsonschema.Schema{{
   525  				Type: []jsonschema.Type{jsonschema.StringType},
   526  				Enum: []interface{}{
   527  					"one",
   528  					"two",
   529  					"three",
   530  				},
   531  			}},
   532  		},
   533  	}
   534  	r := strings.NewReader("one, three")
   535  	w := &bytes.Buffer{}
   536  	p := New(r, w, w)
   537  	v, err := p.QuerySchema(schema)
   538  	c.Assert(err, jc.ErrorIsNil)
   539  	c.Check(w.String(), gc.Equals, `
   540  Numbers
   541    one
   542    two
   543    three
   544  
   545  Select one or more numbers separated by commas: 
   546  `[1:])
   547  	s, ok := v.([]string)
   548  	c.Check(ok, jc.IsTrue)
   549  	c.Check(s, jc.SameContents, []string{"one", "three"})
   550  }
   551  
   552  func (PollsterSuite) TestQueryArraySchemaDefault(c *gc.C) {
   553  	schema := &jsonschema.Schema{
   554  		Singular: "number",
   555  		Plural:   "numbers",
   556  		Type:     []jsonschema.Type{jsonschema.ArrayType},
   557  		Default:  "two",
   558  		Items: &jsonschema.ItemSpec{
   559  			Schemas: []*jsonschema.Schema{{
   560  				Type: []jsonschema.Type{jsonschema.StringType},
   561  				Enum: []interface{}{
   562  					"one",
   563  					"two",
   564  					"three",
   565  				},
   566  			}},
   567  		},
   568  	}
   569  	r := strings.NewReader("\n")
   570  	w := &bytes.Buffer{}
   571  	p := New(r, w, w)
   572  	v, err := p.QuerySchema(schema)
   573  	c.Assert(err, jc.ErrorIsNil)
   574  	c.Check(w.String(), gc.Equals, `
   575  Numbers
   576    one
   577    two
   578    three
   579  
   580  Select one or more numbers separated by commas [two]: 
   581  `[1:])
   582  	s, ok := v.([]string)
   583  	c.Check(ok, jc.IsTrue)
   584  	c.Check(s, jc.SameContents, []string{"two"})
   585  }
   586  
   587  func (PollsterSuite) TestQueryArraySchemaNotDefault(c *gc.C) {
   588  	schema := &jsonschema.Schema{
   589  		Singular: "number",
   590  		Plural:   "numbers",
   591  		Type:     []jsonschema.Type{jsonschema.ArrayType},
   592  		Default:  "two",
   593  		Items: &jsonschema.ItemSpec{
   594  			Schemas: []*jsonschema.Schema{{
   595  				Type: []jsonschema.Type{jsonschema.StringType},
   596  				Enum: []interface{}{
   597  					"one",
   598  					"two",
   599  					"three",
   600  				},
   601  			}},
   602  		},
   603  	}
   604  	r := strings.NewReader("three")
   605  	w := &bytes.Buffer{}
   606  	p := New(r, w, w)
   607  	v, err := p.QuerySchema(schema)
   608  	c.Assert(err, jc.ErrorIsNil)
   609  	c.Check(w.String(), gc.Equals, `
   610  Numbers
   611    one
   612    two
   613    three
   614  
   615  Select one or more numbers separated by commas [two]: 
   616  `[1:])
   617  	s, ok := v.([]string)
   618  	c.Check(ok, jc.IsTrue)
   619  	c.Check(s, jc.SameContents, []string{"three"})
   620  }
   621  
   622  func (PollsterSuite) TestQueryEnum(c *gc.C) {
   623  	schema := &jsonschema.Schema{
   624  		Singular: "number",
   625  		Plural:   "numbers",
   626  		Type:     []jsonschema.Type{jsonschema.IntegerType},
   627  		Enum: []interface{}{
   628  			1,
   629  			2,
   630  			3,
   631  		},
   632  	}
   633  	r := strings.NewReader("2")
   634  	w := &bytes.Buffer{}
   635  	p := New(r, w, w)
   636  	v, err := p.QuerySchema(schema)
   637  	c.Assert(err, jc.ErrorIsNil)
   638  	c.Check(w.String(), gc.Equals, `
   639  Numbers
   640    1
   641    2
   642    3
   643  
   644  Select number: 
   645  `[1:])
   646  	i, ok := v.(int)
   647  	c.Check(ok, jc.IsTrue)
   648  	c.Check(i, gc.Equals, 2)
   649  }
   650  
   651  func (PollsterSuite) TestQueryObjectSchema(c *gc.C) {
   652  	schema := &jsonschema.Schema{
   653  		Type: []jsonschema.Type{jsonschema.ObjectType},
   654  		Properties: map[string]*jsonschema.Schema{
   655  			"numbers": {
   656  				Singular: "number",
   657  				Plural:   "numbers",
   658  				Type:     []jsonschema.Type{jsonschema.ArrayType},
   659  				Items: &jsonschema.ItemSpec{
   660  					Schemas: []*jsonschema.Schema{{
   661  						Type: []jsonschema.Type{jsonschema.StringType},
   662  						Enum: []interface{}{
   663  							"one",
   664  							"two",
   665  							"three",
   666  						},
   667  					}},
   668  				},
   669  			},
   670  			"name": {
   671  				Type:     []jsonschema.Type{jsonschema.StringType},
   672  				Singular: "the name",
   673  			},
   674  		},
   675  	}
   676  	// queries should be alphabetical without an order specified, so name then
   677  	// number.
   678  	r := strings.NewReader("Bill\ntwo, three")
   679  	w := &bytes.Buffer{}
   680  	p := New(r, w, w)
   681  	v, err := p.QuerySchema(schema)
   682  	c.Assert(err, jc.ErrorIsNil)
   683  	c.Check(v, jc.DeepEquals, map[string]interface{}{
   684  		"name":    "Bill",
   685  		"numbers": []string{"two", "three"},
   686  	})
   687  }
   688  
   689  func (PollsterSuite) TestQueryObjectSchemaOrder(c *gc.C) {
   690  	schema := &jsonschema.Schema{
   691  		Type: []jsonschema.Type{jsonschema.ObjectType},
   692  		// Order should match up with order of input in strings.NewReader below.
   693  		Order: []string{"numbers", "name"},
   694  		Properties: map[string]*jsonschema.Schema{
   695  			"numbers": {
   696  				Singular: "number",
   697  				Plural:   "numbers",
   698  				Type:     []jsonschema.Type{jsonschema.ArrayType},
   699  				Items: &jsonschema.ItemSpec{
   700  					Schemas: []*jsonschema.Schema{{
   701  						Type: []jsonschema.Type{jsonschema.StringType},
   702  						Enum: []interface{}{
   703  							"one",
   704  							"two",
   705  							"three",
   706  						},
   707  					}},
   708  				},
   709  			},
   710  			"name": {
   711  				Type:     []jsonschema.Type{jsonschema.StringType},
   712  				Singular: "the name",
   713  			},
   714  		},
   715  	}
   716  	// queries should be ordered by order, so number then name.
   717  	r := strings.NewReader("two, three\nBill")
   718  	w := &bytes.Buffer{}
   719  	p := New(r, w, w)
   720  	v, err := p.QuerySchema(schema)
   721  	c.Assert(err, jc.ErrorIsNil)
   722  	c.Check(v, jc.DeepEquals, map[string]interface{}{
   723  		"name":    "Bill",
   724  		"numbers": []string{"two", "three"},
   725  	})
   726  }
   727  
   728  func (PollsterSuite) TestQueryObjectSchemaAdditional(c *gc.C) {
   729  	schema := &jsonschema.Schema{
   730  		Type:     []jsonschema.Type{jsonschema.ObjectType},
   731  		Singular: "region",
   732  		Plural:   "regions",
   733  		AdditionalProperties: &jsonschema.Schema{
   734  			Type: []jsonschema.Type{jsonschema.ObjectType},
   735  			Properties: map[string]*jsonschema.Schema{
   736  				"loc": {
   737  					Singular: "location",
   738  					Type:     []jsonschema.Type{jsonschema.StringType},
   739  				},
   740  			},
   741  		},
   742  	}
   743  	r := strings.NewReader(`
   744  one
   745  east
   746  y
   747  two
   748  west
   749  n
   750  `[1:])
   751  	w := &bytes.Buffer{}
   752  	p := New(r, w, w)
   753  	v, err := p.QuerySchema(schema)
   754  	c.Assert(err, jc.ErrorIsNil)
   755  	c.Check(v, jc.DeepEquals, map[string]interface{}{
   756  		"one": map[string]interface{}{"loc": "east"},
   757  		"two": map[string]interface{}{"loc": "west"},
   758  	})
   759  	c.Check(w.String(), gc.Equals, `
   760  Enter region name: 
   761  Enter location: 
   762  Enter another region? (y/N): 
   763  Enter region name: 
   764  Enter location: 
   765  Enter another region? (y/N): 
   766  `[1:])
   767  }
   768  
   769  func (PollsterSuite) TestQueryObjectSchemaAdditionalEmpty(c *gc.C) {
   770  	schema := &jsonschema.Schema{
   771  		Type:     []jsonschema.Type{jsonschema.ObjectType},
   772  		Singular: "region",
   773  		Plural:   "regions",
   774  		AdditionalProperties: &jsonschema.Schema{
   775  			Type: []jsonschema.Type{jsonschema.ObjectType},
   776  		},
   777  	}
   778  	r := strings.NewReader(`
   779  one
   780  y
   781  two
   782  n
   783  `[1:])
   784  	w := &bytes.Buffer{}
   785  	p := New(r, w, w)
   786  	v, err := p.QuerySchema(schema)
   787  	c.Assert(err, jc.ErrorIsNil)
   788  	c.Check(v, jc.DeepEquals, map[string]interface{}{
   789  		"one": map[string]interface{}{},
   790  		"two": map[string]interface{}{},
   791  	})
   792  	c.Check(w.String(), gc.Equals, `
   793  Enter region name: 
   794  Enter another region? (y/N): 
   795  Enter region name: 
   796  Enter another region? (y/N): 
   797  `[1:])
   798  }
   799  
   800  func (PollsterSuite) TestQueryObjectSchemaWithOutDefault(c *gc.C) {
   801  	schema := &jsonschema.Schema{
   802  		Type:  []jsonschema.Type{jsonschema.ObjectType},
   803  		Order: []string{"name", "nested", "bar"},
   804  		Properties: map[string]*jsonschema.Schema{
   805  			"nested": {
   806  				Singular: "nested",
   807  				Type:     []jsonschema.Type{jsonschema.ObjectType},
   808  				AdditionalProperties: &jsonschema.Schema{
   809  					Type:          []jsonschema.Type{jsonschema.ObjectType},
   810  					Required:      []string{"name"},
   811  					MaxProperties: jsonschema.Int(1),
   812  					Properties: map[string]*jsonschema.Schema{
   813  						"name": {
   814  							Singular:      "the name",
   815  							Type:          []jsonschema.Type{jsonschema.StringType},
   816  							Default:       "",
   817  							PromptDefault: "use name",
   818  						},
   819  					},
   820  				},
   821  			},
   822  			"bar": {
   823  				Singular: "nested",
   824  				Type:     []jsonschema.Type{jsonschema.ObjectType},
   825  				Default:  "",
   826  				AdditionalProperties: &jsonschema.Schema{
   827  					Type:          []jsonschema.Type{jsonschema.ObjectType},
   828  					Required:      []string{"name"},
   829  					MaxProperties: jsonschema.Int(1),
   830  					Properties: map[string]*jsonschema.Schema{
   831  						"name": {
   832  							Singular:      "the name",
   833  							Type:          []jsonschema.Type{jsonschema.StringType},
   834  							Default:       "",
   835  							PromptDefault: "use name",
   836  						},
   837  					},
   838  				},
   839  			},
   840  			"name": {
   841  				Type:     []jsonschema.Type{jsonschema.StringType},
   842  				Singular: "the name",
   843  			},
   844  		},
   845  	}
   846  	// queries should be alphabetical without an order specified, so name then
   847  	// number.
   848  	r := strings.NewReader("Bill\n\nnamespace\n\n\nfoo\nbaz\n\n\n")
   849  	w := &bytes.Buffer{}
   850  	p := New(r, w, w)
   851  	v, err := p.QuerySchema(schema)
   852  	c.Assert(err, jc.ErrorIsNil)
   853  	c.Check(v, jc.DeepEquals, map[string]interface{}{
   854  		"name": "Bill",
   855  		"nested": map[string]interface{}{
   856  			"namespace": map[string]interface{}{
   857  				"name": "",
   858  			},
   859  		},
   860  		"bar": map[string]interface{}{
   861  			"foo": map[string]interface{}{
   862  				"name": "baz",
   863  			},
   864  		},
   865  	})
   866  }
   867  
   868  func (PollsterSuite) TestQueryObjectSchemaWithDefault(c *gc.C) {
   869  	schema := &jsonschema.Schema{
   870  		Type:  []jsonschema.Type{jsonschema.ObjectType},
   871  		Order: []string{"name", "nested"},
   872  		Properties: map[string]*jsonschema.Schema{
   873  			"nested": {
   874  				Singular: "nested",
   875  				Default:  "default",
   876  				Type:     []jsonschema.Type{jsonschema.ObjectType},
   877  				AdditionalProperties: &jsonschema.Schema{
   878  					Type:          []jsonschema.Type{jsonschema.ObjectType},
   879  					Required:      []string{"name"},
   880  					MaxProperties: jsonschema.Int(1),
   881  					Properties: map[string]*jsonschema.Schema{
   882  						"name": {
   883  							Singular:      "the name",
   884  							Type:          []jsonschema.Type{jsonschema.StringType},
   885  							Default:       "",
   886  							PromptDefault: "use name",
   887  						},
   888  					},
   889  				},
   890  			},
   891  			"name": {
   892  				Type:     []jsonschema.Type{jsonschema.StringType},
   893  				Singular: "the name",
   894  			},
   895  		},
   896  	}
   897  	// queries should be alphabetical without an order specified, so name then
   898  	// number.
   899  	r := strings.NewReader("Bill\n\n\n\n")
   900  	w := &bytes.Buffer{}
   901  	p := New(r, w, w)
   902  	v, err := p.QuerySchema(schema)
   903  	c.Assert(err, jc.ErrorIsNil)
   904  	c.Check(v, jc.DeepEquals, map[string]interface{}{
   905  		"name": "Bill",
   906  		"nested": map[string]interface{}{
   907  			"default": map[string]interface{}{
   908  				"name": "",
   909  			},
   910  		},
   911  	})
   912  }
   913  
   914  func (PollsterSuite) TestQueryObjectSchemaWithDefaultEnvVars(c *gc.C) {
   915  	schema := &jsonschema.Schema{
   916  		Type:  []jsonschema.Type{jsonschema.ObjectType},
   917  		Order: []string{"name", "nested"},
   918  		Properties: map[string]*jsonschema.Schema{
   919  			"nested": {
   920  				Singular:      "nested",
   921  				Default:       "default",
   922  				PromptDefault: "use default value",
   923  				EnvVars:       []string{"TEST_ENV_VAR_NESTED"},
   924  				Type:          []jsonschema.Type{jsonschema.ObjectType},
   925  				AdditionalProperties: &jsonschema.Schema{
   926  					Type:          []jsonschema.Type{jsonschema.ObjectType},
   927  					Required:      []string{"name"},
   928  					MaxProperties: jsonschema.Int(1),
   929  					Properties: map[string]*jsonschema.Schema{
   930  						"name": {
   931  							Singular:      "the name",
   932  							Type:          []jsonschema.Type{jsonschema.StringType},
   933  							Default:       "",
   934  							PromptDefault: "use name",
   935  						},
   936  					},
   937  				},
   938  			},
   939  			"name": {
   940  				Type:     []jsonschema.Type{jsonschema.StringType},
   941  				Singular: "the name",
   942  			},
   943  		},
   944  	}
   945  	// queries should be alphabetical without an order specified, so name then
   946  	// number.
   947  	os.Setenv("TEST_ENV_VAR_NESTED", "baz")
   948  	defer os.Unsetenv("TEST_ENV_VAR_NESTED")
   949  
   950  	r := strings.NewReader("Bill\n\n\n\n")
   951  	w := &bytes.Buffer{}
   952  	p := New(r, w, w)
   953  	v, err := p.QuerySchema(schema)
   954  	c.Assert(err, jc.ErrorIsNil)
   955  	c.Check(v, jc.DeepEquals, map[string]interface{}{
   956  		"name": "Bill",
   957  		"nested": map[string]interface{}{
   958  			"baz": map[string]interface{}{
   959  				"name": "",
   960  			},
   961  		},
   962  	})
   963  }
   964  
   965  func (PollsterSuite) TestQueryObjectSchemaEnvVarsWithOutDefault(c *gc.C) {
   966  	schema := &jsonschema.Schema{
   967  		Type:  []jsonschema.Type{jsonschema.ObjectType},
   968  		Order: []string{"name", "nested"},
   969  		Properties: map[string]*jsonschema.Schema{
   970  			"nested": {
   971  				Singular: "nested",
   972  				EnvVars:  []string{"TEST_ENV_VAR_NESTED"},
   973  				Type:     []jsonschema.Type{jsonschema.ObjectType},
   974  				AdditionalProperties: &jsonschema.Schema{
   975  					Type:          []jsonschema.Type{jsonschema.ObjectType},
   976  					Required:      []string{"name"},
   977  					MaxProperties: jsonschema.Int(1),
   978  					Properties: map[string]*jsonschema.Schema{
   979  						"name": {
   980  							Singular:      "the name",
   981  							Type:          []jsonschema.Type{jsonschema.StringType},
   982  							Default:       "",
   983  							PromptDefault: "use name",
   984  						},
   985  					},
   986  				},
   987  			},
   988  			"name": {
   989  				Type:     []jsonschema.Type{jsonschema.StringType},
   990  				Singular: "the name",
   991  			},
   992  		},
   993  	}
   994  	// queries should be alphabetical without an order specified, so name then
   995  	// number.
   996  	os.Setenv("TEST_ENV_VAR_NESTED", "baz")
   997  	defer os.Unsetenv("TEST_ENV_VAR_NESTED")
   998  
   999  	r := strings.NewReader("Bill\nbaz\n\n\n")
  1000  	w := &bytes.Buffer{}
  1001  	p := New(r, w, w)
  1002  	v, err := p.QuerySchema(schema)
  1003  	c.Assert(err, jc.ErrorIsNil)
  1004  	c.Check(v, jc.DeepEquals, map[string]interface{}{
  1005  		"name": "Bill",
  1006  		"nested": map[string]interface{}{
  1007  			"baz": map[string]interface{}{
  1008  				"name": "",
  1009  			},
  1010  		},
  1011  	})
  1012  }