gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/hookstate/ctlcmd/ctlcmd_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package ctlcmd_test 21 22 import ( 23 "fmt" 24 "strings" 25 "testing" 26 27 "github.com/jessevdk/go-flags" 28 . "gopkg.in/check.v1" 29 30 "github.com/snapcore/snapd/overlord/hookstate" 31 "github.com/snapcore/snapd/overlord/hookstate/ctlcmd" 32 "github.com/snapcore/snapd/overlord/hookstate/hooktest" 33 "github.com/snapcore/snapd/overlord/state" 34 "github.com/snapcore/snapd/snap" 35 "github.com/snapcore/snapd/testutil" 36 ) 37 38 func Test(t *testing.T) { TestingT(t) } 39 40 type ctlcmdSuite struct { 41 mockContext *hookstate.Context 42 } 43 44 var _ = Suite(&ctlcmdSuite{}) 45 46 func (s *ctlcmdSuite) SetUpTest(c *C) { 47 handler := hooktest.NewMockHandler() 48 49 state := state.New(nil) 50 state.Lock() 51 defer state.Unlock() 52 53 task := state.NewTask("test-task", "my test task") 54 setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "test-hook"} 55 56 var err error 57 s.mockContext, err = hookstate.NewContext(task, task.State(), setup, handler, "") 58 c.Assert(err, IsNil) 59 } 60 61 func (s *ctlcmdSuite) TestNonExistingCommand(c *C) { 62 stdout, stderr, err := ctlcmd.Run(s.mockContext, []string{"foo"}, 0) 63 c.Check(string(stdout), Equals, "") 64 c.Check(string(stderr), Equals, "") 65 c.Check(err, ErrorMatches, ".*[Uu]nknown command.*") 66 } 67 68 func (s *ctlcmdSuite) TestCommandOutput(c *C) { 69 mockCommand := ctlcmd.AddMockCommand("mock") 70 defer ctlcmd.RemoveCommand("mock") 71 72 mockCommand.FakeStdout = "test stdout" 73 mockCommand.FakeStderr = "test stderr" 74 75 stdout, stderr, err := ctlcmd.Run(s.mockContext, []string{"mock", "foo"}, 0) 76 c.Check(err, IsNil) 77 c.Check(string(stdout), Equals, "test stdout") 78 c.Check(string(stderr), Equals, "test stderr") 79 c.Check(mockCommand.Args, DeepEquals, []string{"foo"}) 80 } 81 82 func taskKinds(tasks []*state.Task) []string { 83 kinds := make([]string, len(tasks)) 84 for i, task := range tasks { 85 k := task.Kind() 86 if k == "run-hook" { 87 var hooksup hookstate.HookSetup 88 if err := task.Get("hook-setup", &hooksup); err != nil { 89 panic(err) 90 } 91 k = fmt.Sprintf("%s[%s]", k, hooksup.Hook) 92 } 93 kinds[i] = k 94 } 95 return kinds 96 } 97 98 func (s *ctlcmdSuite) TestHiddenCommand(c *C) { 99 ctlcmd.AddHiddenMockCommand("mock-hidden") 100 ctlcmd.AddMockCommand("mock-shown") 101 defer ctlcmd.RemoveCommand("mock-hidden") 102 defer ctlcmd.RemoveCommand("mock-shown") 103 104 _, _, err := ctlcmd.Run(s.mockContext, []string{"--help"}, 0) 105 // help message output is returned as *flags.Error with 106 // Type as flags.ErrHelp 107 c.Assert(err, FitsTypeOf, &flags.Error{}) 108 c.Check(err.(*flags.Error).Type, Equals, flags.ErrHelp) 109 // snapctl is mentioned (not snapd) 110 c.Check(err.Error(), testutil.Contains, "snapctl") 111 // mock-shown is in the help message 112 c.Check(err.Error(), testutil.Contains, " mock-shown\n") 113 // mock-hidden is not in the help message 114 c.Check(err.Error(), Not(testutil.Contains), " mock-hidden\n") 115 } 116 117 func (s *ctlcmdSuite) TestRootRequiredCommandFailure(c *C) { 118 _, _, err := ctlcmd.Run(s.mockContext, []string{"start"}, 1000) 119 120 c.Check(err, FitsTypeOf, &ctlcmd.ForbiddenCommandError{}) 121 c.Check(err.Error(), Equals, `cannot use "start" with uid 1000, try with sudo`) 122 } 123 124 func (s *ctlcmdSuite) TestRunNoArgsFailure(c *C) { 125 _, _, err := ctlcmd.Run(s.mockContext, []string{}, 0) 126 c.Check(err, NotNil) 127 } 128 129 func (s *ctlcmdSuite) TestRunOnlyHelp(c *C) { 130 _, _, err := ctlcmd.Run(s.mockContext, []string{"-h"}, 1000) 131 c.Check(err, NotNil) 132 c.Assert(strings.HasPrefix(err.Error(), "Usage:"), Equals, true) 133 134 _, _, err = ctlcmd.Run(s.mockContext, []string{"--help"}, 1000) 135 c.Check(err, NotNil) 136 c.Assert(strings.HasPrefix(err.Error(), "Usage:"), Equals, true) 137 } 138 139 func (s *ctlcmdSuite) TestRunHelpAtAnyPosition(c *C) { 140 _, _, err := ctlcmd.Run(s.mockContext, []string{"start", "a", "-h"}, 1000) 141 c.Check(err, NotNil) 142 c.Assert(strings.HasPrefix(err.Error(), "Usage:"), Equals, true) 143 144 _, _, err = ctlcmd.Run(s.mockContext, []string{"start", "a", "b", "--help"}, 1000) 145 c.Check(err, NotNil) 146 c.Assert(strings.HasPrefix(err.Error(), "Usage:"), Equals, true) 147 } 148 149 func (s *ctlcmdSuite) TestRunNonRootAllowedCommandWithAllowedCmdAsArg(c *C) { 150 // this test protects us against a future refactor introducing a bug that allows 151 // a root-only command to run without root if an arg is in the nonRootAllowed list 152 _, _, err := ctlcmd.Run(s.mockContext, []string{"set", "get", "a"}, 1000) 153 c.Check(err, FitsTypeOf, &ctlcmd.ForbiddenCommandError{}) 154 c.Check(err.Error(), Equals, `cannot use "set" with uid 1000, try with sudo`) 155 }