~juju-qa/ubuntu/xenial/juju/2.0-rc2

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/cmd/juju/application/config_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-09-30 14:39:30 UTC
  • mfrom: (1.8.1)
  • Revision ID: nicholas.skaggs@canonical.com-20160930143930-vwwhrefh6ftckccy
import upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2016 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
package application_test
 
4
 
 
5
import (
 
6
        "bytes"
 
7
        "io/ioutil"
 
8
        "os"
 
9
        "strings"
 
10
        "unicode/utf8"
 
11
 
 
12
        "github.com/juju/cmd"
 
13
        jc "github.com/juju/testing/checkers"
 
14
        "github.com/juju/utils"
 
15
        gc "gopkg.in/check.v1"
 
16
        goyaml "gopkg.in/yaml.v2"
 
17
 
 
18
        "github.com/juju/juju/apiserver/common"
 
19
        "github.com/juju/juju/cmd/juju/application"
 
20
        coretesting "github.com/juju/juju/testing"
 
21
)
 
22
 
 
23
type configCommandSuite struct {
 
24
        coretesting.FakeJujuXDGDataHomeSuite
 
25
        dir  string
 
26
        fake *fakeApplicationAPI
 
27
}
 
28
 
 
29
var (
 
30
        _ = gc.Suite(&configCommandSuite{})
 
31
 
 
32
        validSetTestValue   = "a value with spaces\nand newline\nand UTF-8 characters: \U0001F604 / \U0001F44D"
 
33
        invalidSetTestValue = "a value with an invalid UTF-8 sequence: " + string([]byte{0xFF, 0xFF})
 
34
        yamlConfigValue     = "dummy-application:\n  skill-level: 9000\n  username: admin001\n\n"
 
35
)
 
36
 
 
37
var getTests = []struct {
 
38
        application string
 
39
        expected    map[string]interface{}
 
40
}{
 
41
        {
 
42
                "dummy-application",
 
43
                map[string]interface{}{
 
44
                        "application": "dummy-application",
 
45
                        "charm":       "dummy",
 
46
                        "settings": map[string]interface{}{
 
47
                                "title": map[string]interface{}{
 
48
                                        "description": "Specifies title",
 
49
                                        "type":        "string",
 
50
                                        "value":       "Nearly There",
 
51
                                },
 
52
                                "skill-level": map[string]interface{}{
 
53
                                        "description": "Specifies skill-level",
 
54
                                        "value":       100,
 
55
                                        "type":        "int",
 
56
                                },
 
57
                                "username": map[string]interface{}{
 
58
                                        "description": "Specifies username",
 
59
                                        "type":        "string",
 
60
                                        "value":       "admin001",
 
61
                                },
 
62
                                "outlook": map[string]interface{}{
 
63
                                        "description": "Specifies outlook",
 
64
                                        "type":        "string",
 
65
                                        "value":       "true",
 
66
                                },
 
67
                        },
 
68
                },
 
69
        },
 
70
}
 
71
 
 
72
func (s *configCommandSuite) SetUpTest(c *gc.C) {
 
73
        s.FakeJujuXDGDataHomeSuite.SetUpTest(c)
 
74
        s.fake = &fakeApplicationAPI{name: "dummy-application", charmName: "dummy",
 
75
                values: map[string]interface{}{
 
76
                        "title":       "Nearly There",
 
77
                        "skill-level": 100,
 
78
                        "username":    "admin001",
 
79
                        "outlook":     "true",
 
80
                }}
 
81
        s.FakeJujuXDGDataHomeSuite.SetUpTest(c)
 
82
 
 
83
        s.dir = c.MkDir()
 
84
        c.Assert(utf8.ValidString(validSetTestValue), jc.IsTrue)
 
85
        c.Assert(utf8.ValidString(invalidSetTestValue), jc.IsFalse)
 
86
        setupValueFile(c, s.dir, "valid.txt", validSetTestValue)
 
87
        setupValueFile(c, s.dir, "invalid.txt", invalidSetTestValue)
 
88
        setupBigFile(c, s.dir)
 
89
        setupConfigFile(c, s.dir)
 
90
}
 
91
 
 
92
func (s *configCommandSuite) TestGetCommandInit(c *gc.C) {
 
93
        // missing args
 
94
        err := coretesting.InitCommand(application.NewConfigCommandForTest(s.fake), []string{})
 
95
        c.Assert(err, gc.ErrorMatches, "no application name specified")
 
96
}
 
97
 
 
98
func (s *configCommandSuite) TestGetCommandInitWithApplication(c *gc.C) {
 
99
        err := coretesting.InitCommand(application.NewConfigCommandForTest(s.fake), []string{"app"})
 
100
        // everything ok
 
101
        c.Assert(err, jc.ErrorIsNil)
 
102
}
 
103
 
 
104
func (s *configCommandSuite) TestGetCommandInitWithKey(c *gc.C) {
 
105
        err := coretesting.InitCommand(application.NewConfigCommandForTest(s.fake), []string{"app", "key"})
 
106
        // everything ok
 
107
        c.Assert(err, jc.ErrorIsNil)
 
108
}
 
109
 
 
110
func (s *configCommandSuite) TestGetCommandInitTooManyArgs(c *gc.C) {
 
111
        err := coretesting.InitCommand(application.NewConfigCommandForTest(s.fake), []string{"app", "key", "another"})
 
112
        c.Assert(err, gc.ErrorMatches, "can only retrieve a single value, or all values")
 
113
}
 
114
 
 
115
func (s *configCommandSuite) TestGetConfig(c *gc.C) {
 
116
        for _, t := range getTests {
 
117
                ctx := coretesting.Context(c)
 
118
                code := cmd.Main(application.NewConfigCommandForTest(s.fake), ctx, []string{t.application})
 
119
                c.Check(code, gc.Equals, 0)
 
120
                c.Assert(ctx.Stderr.(*bytes.Buffer).String(), gc.Equals, "")
 
121
                // round trip via goyaml to avoid being sucked into a quagmire of
 
122
                // map[interface{}]interface{} vs map[string]interface{}. This is
 
123
                // also required if we add json support to this command.
 
124
                buf, err := goyaml.Marshal(t.expected)
 
125
                c.Assert(err, jc.ErrorIsNil)
 
126
                expected := make(map[string]interface{})
 
127
                err = goyaml.Unmarshal(buf, &expected)
 
128
                c.Assert(err, jc.ErrorIsNil)
 
129
 
 
130
                actual := make(map[string]interface{})
 
131
                err = goyaml.Unmarshal(ctx.Stdout.(*bytes.Buffer).Bytes(), &actual)
 
132
                c.Assert(err, jc.ErrorIsNil)
 
133
                c.Assert(actual, gc.DeepEquals, expected)
 
134
        }
 
135
}
 
136
 
 
137
func (s *configCommandSuite) TestGetConfigKey(c *gc.C) {
 
138
        ctx := coretesting.Context(c)
 
139
        code := cmd.Main(application.NewConfigCommandForTest(s.fake), ctx, []string{"dummy-application", "title"})
 
140
        c.Check(code, gc.Equals, 0)
 
141
        c.Assert(ctx.Stderr.(*bytes.Buffer).String(), gc.Equals, "")
 
142
        c.Assert(ctx.Stdout.(*bytes.Buffer).String(), gc.Equals, "Nearly There\n")
 
143
}
 
144
 
 
145
func (s *configCommandSuite) TestGetConfigKeyNotFound(c *gc.C) {
 
146
        ctx := coretesting.Context(c)
 
147
        code := cmd.Main(application.NewConfigCommandForTest(s.fake), ctx, []string{"dummy-application", "invalid"})
 
148
        c.Check(code, gc.Equals, 1)
 
149
        c.Assert(ctx.Stderr.(*bytes.Buffer).String(), gc.Equals, "error: key \"invalid\" not found in \"dummy-application\" application settings.\n")
 
150
        c.Assert(ctx.Stdout.(*bytes.Buffer).String(), gc.Equals, "")
 
151
}
 
152
 
 
153
func (s *configCommandSuite) TestSetCommandInit(c *gc.C) {
 
154
        // missing args
 
155
        err := coretesting.InitCommand(application.NewConfigCommandForTest(s.fake), []string{})
 
156
        c.Assert(err, gc.ErrorMatches, "no application name specified")
 
157
 
 
158
        // missing application name
 
159
        err = coretesting.InitCommand(application.NewConfigCommandForTest(s.fake), []string{"name=foo"})
 
160
        c.Assert(err, gc.ErrorMatches, "no application name specified")
 
161
 
 
162
        // --file path, but no application
 
163
        err = coretesting.InitCommand(application.NewConfigCommandForTest(s.fake), []string{"--file", "testconfig.yaml"})
 
164
        c.Assert(err, gc.ErrorMatches, "no application name specified")
 
165
 
 
166
        // --file and options specified
 
167
        err = coretesting.InitCommand(application.NewConfigCommandForTest(s.fake), []string{"application", "--file", "testconfig.yaml", "bees="})
 
168
        c.Assert(err, gc.ErrorMatches, "cannot specify --file and key=value arguments simultaneously")
 
169
 
 
170
        // --reset and no config name provided
 
171
        err = coretesting.InitCommand(application.NewConfigCommandForTest(s.fake), []string{"application", "--reset"})
 
172
        c.Assert(err, gc.ErrorMatches, "no configuration options specified")
 
173
 
 
174
}
 
175
 
 
176
func (s *configCommandSuite) TestSetOptionSuccess(c *gc.C) {
 
177
        s.assertSetSuccess(c, s.dir, []string{
 
178
                "username=hello",
 
179
                "outlook=hello@world.tld",
 
180
        }, map[string]interface{}{
 
181
                "username": "hello",
 
182
                "outlook":  "hello@world.tld",
 
183
        })
 
184
        s.assertSetSuccess(c, s.dir, []string{
 
185
                "username=hello=foo",
 
186
        }, map[string]interface{}{
 
187
                "username": "hello=foo",
 
188
                "outlook":  "hello@world.tld",
 
189
        })
 
190
        s.assertSetSuccess(c, s.dir, []string{
 
191
                "username=@valid.txt",
 
192
        }, map[string]interface{}{
 
193
                "username": validSetTestValue,
 
194
                "outlook":  "hello@world.tld",
 
195
        })
 
196
        s.assertSetSuccess(c, s.dir, []string{
 
197
                "username=",
 
198
        }, map[string]interface{}{
 
199
                "username": "",
 
200
                "outlook":  "hello@world.tld",
 
201
        })
 
202
}
 
203
 
 
204
func (s *configCommandSuite) TestSetSameValue(c *gc.C) {
 
205
        s.assertSetSuccess(c, s.dir, []string{
 
206
                "username=hello",
 
207
                "outlook=hello@world.tld",
 
208
        }, map[string]interface{}{
 
209
                "username": "hello",
 
210
                "outlook":  "hello@world.tld",
 
211
        })
 
212
        s.assertSetWarning(c, s.dir, []string{
 
213
                "username=hello",
 
214
        }, "the configuration setting \"username\" already has the value \"hello\"")
 
215
        s.assertSetWarning(c, s.dir, []string{
 
216
                "outlook=hello@world.tld",
 
217
        }, "the configuration setting \"outlook\" already has the value \"hello@world.tld\"")
 
218
 
 
219
}
 
220
 
 
221
func (s *configCommandSuite) TestSetOptionFail(c *gc.C) {
 
222
        s.assertSetFail(c, s.dir, []string{"foo", "bar"},
 
223
                "error: can only retrieve a single value, or all values\n")
 
224
        s.assertSetFail(c, s.dir, []string{"=bar"}, "error: expected \"key=value\", got \"=bar\"\n")
 
225
        s.assertSetFail(c, s.dir, []string{
 
226
                "username=@missing.txt",
 
227
        }, "error: cannot read option from file \"missing.txt\": .* "+utils.NoSuchFileErrRegexp+"\n")
 
228
        s.assertSetFail(c, s.dir, []string{
 
229
                "username=@big.txt",
 
230
        }, "error: size of option file is larger than 5M\n")
 
231
        s.assertSetFail(c, s.dir, []string{
 
232
                "username=@invalid.txt",
 
233
        }, "error: value for option \"username\" contains non-UTF-8 sequences\n")
 
234
}
 
235
 
 
236
func (s *configCommandSuite) TestSetConfig(c *gc.C) {
 
237
        s.assertSetFail(c, s.dir, []string{
 
238
                "--file",
 
239
                "missing.yaml",
 
240
        }, "error.* "+utils.NoSuchFileErrRegexp+"\n")
 
241
 
 
242
        ctx := coretesting.ContextForDir(c, s.dir)
 
243
        code := cmd.Main(application.NewConfigCommandForTest(s.fake), ctx, []string{
 
244
                "dummy-application",
 
245
                "--file",
 
246
                "testconfig.yaml"})
 
247
 
 
248
        c.Check(code, gc.Equals, 0)
 
249
        c.Check(s.fake.config, gc.Equals, yamlConfigValue)
 
250
}
 
251
 
 
252
func (s *configCommandSuite) TestSetFromStdin(c *gc.C) {
 
253
        s.fake = &fakeApplicationAPI{name: "dummy-application"}
 
254
        ctx := coretesting.Context(c)
 
255
        ctx.Stdin = strings.NewReader("settings:\n  username:\n  value: world\n")
 
256
        code := cmd.Main(application.NewConfigCommandForTest(s.fake), ctx, []string{
 
257
                "dummy-application",
 
258
                "--file",
 
259
                "-"})
 
260
 
 
261
        c.Check(code, gc.Equals, 0)
 
262
        c.Check(s.fake.config, jc.DeepEquals, "settings:\n  username:\n  value: world\n")
 
263
}
 
264
 
 
265
func (s *configCommandSuite) TestResetConfigToDefault(c *gc.C) {
 
266
        s.fake = &fakeApplicationAPI{name: "dummy-application", values: map[string]interface{}{
 
267
                "username": "hello",
 
268
        }}
 
269
        s.assertSetSuccess(c, s.dir, []string{
 
270
                "--reset",
 
271
                "username",
 
272
        }, make(map[string]interface{}))
 
273
}
 
274
 
 
275
func (s *configCommandSuite) TestBlockSetConfig(c *gc.C) {
 
276
        // Block operation
 
277
        s.fake.err = common.OperationBlockedError("TestBlockSetConfig")
 
278
        ctx := coretesting.ContextForDir(c, s.dir)
 
279
        code := cmd.Main(application.NewConfigCommandForTest(s.fake), ctx, []string{
 
280
                "dummy-application",
 
281
                "--file",
 
282
                "testconfig.yaml"})
 
283
        c.Check(code, gc.Equals, 1)
 
284
        // msg is logged
 
285
        stripped := strings.Replace(c.GetTestLog(), "\n", "", -1)
 
286
        c.Check(stripped, gc.Matches, ".*TestBlockSetConfig.*")
 
287
}
 
288
 
 
289
// assertSetSuccess sets configuration options and checks the expected settings.
 
290
func (s *configCommandSuite) assertSetSuccess(c *gc.C, dir string, args []string, expect map[string]interface{}) {
 
291
        ctx := coretesting.ContextForDir(c, dir)
 
292
        code := cmd.Main(application.NewConfigCommandForTest(s.fake), ctx, append([]string{"dummy-application"}, args...))
 
293
        c.Assert(code, gc.Equals, 0)
 
294
}
 
295
 
 
296
// assertSetFail sets configuration options and checks the expected error.
 
297
func (s *configCommandSuite) assertSetFail(c *gc.C, dir string, args []string, err string) {
 
298
        ctx := coretesting.ContextForDir(c, dir)
 
299
        code := cmd.Main(application.NewConfigCommandForTest(s.fake), ctx, append([]string{"dummy-application"}, args...))
 
300
        c.Check(code, gc.Not(gc.Equals), 0)
 
301
        c.Assert(ctx.Stderr.(*bytes.Buffer).String(), gc.Matches, err)
 
302
}
 
303
 
 
304
func (s *configCommandSuite) assertSetWarning(c *gc.C, dir string, args []string, w string) {
 
305
        ctx := coretesting.ContextForDir(c, dir)
 
306
        code := cmd.Main(application.NewConfigCommandForTest(s.fake), ctx, append([]string{"dummy-application"}, args...))
 
307
        c.Check(code, gc.Equals, 0)
 
308
 
 
309
        c.Assert(strings.Replace(c.GetTestLog(), "\n", " ", -1), gc.Matches, ".*WARNING.*"+w+".*")
 
310
}
 
311
 
 
312
// setupValueFile creates a file containing one value for testing
 
313
// set with name=@filename.
 
314
func setupValueFile(c *gc.C, dir, filename, value string) string {
 
315
        ctx := coretesting.ContextForDir(c, dir)
 
316
        path := ctx.AbsPath(filename)
 
317
        content := []byte(value)
 
318
        err := ioutil.WriteFile(path, content, 0666)
 
319
        c.Assert(err, jc.ErrorIsNil)
 
320
        return path
 
321
}
 
322
 
 
323
// setupBigFile creates a too big file for testing
 
324
// set with name=@filename.
 
325
func setupBigFile(c *gc.C, dir string) string {
 
326
        ctx := coretesting.ContextForDir(c, dir)
 
327
        path := ctx.AbsPath("big.txt")
 
328
        file, err := os.Create(path)
 
329
        c.Assert(err, jc.ErrorIsNil)
 
330
        defer file.Close()
 
331
        chunk := make([]byte, 1024)
 
332
        for i := 0; i < cap(chunk); i++ {
 
333
                chunk[i] = byte(i % 256)
 
334
        }
 
335
        for i := 0; i < 6000; i++ {
 
336
                _, err = file.Write(chunk)
 
337
                c.Assert(err, jc.ErrorIsNil)
 
338
        }
 
339
        return path
 
340
}
 
341
 
 
342
// setupConfigFile creates a configuration file for testing set
 
343
// with the --file argument specifying a configuration file.
 
344
func setupConfigFile(c *gc.C, dir string) string {
 
345
        ctx := coretesting.ContextForDir(c, dir)
 
346
        path := ctx.AbsPath("testconfig.yaml")
 
347
        content := []byte(yamlConfigValue)
 
348
        err := ioutil.WriteFile(path, content, 0666)
 
349
        c.Assert(err, jc.ErrorIsNil)
 
350
        return path
 
351
}