Skip to content

Commit

Permalink
tests: improve coverage for adwatchd and cmdhandler (#1017)
Browse files Browse the repository at this point in the history
UDENG-2764
UDENG-2765
  • Loading branch information
GabrielNagy authored Jun 12, 2024
2 parents a306af0 + 85cd0e7 commit 6610fb3
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 5 deletions.
8 changes: 8 additions & 0 deletions cmd/adsysd/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ func TestAppUsageError(t *testing.T) {
require.True(t, isUsageError, "Usage error is reported as such")
}

func TestAppUsageErrorReportsSuggestions(t *testing.T) {
a := client.New()
a.SetArgs("hel")

err := a.Run()
require.ErrorContains(t, err, "Did you mean this?\n\thelp", "Run should return usage with suggestions")
}

func TestAppCanQuitWhenExecute(t *testing.T) {
t.Parallel()

Expand Down
16 changes: 13 additions & 3 deletions cmd/adwatchd/commands/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ type App struct {

// options are the configurable functional options of the application.
type options struct {
name string
name string
tuiCtx context.Context
}
type option func(*options)

Expand All @@ -48,11 +49,20 @@ func WithServiceName(name string) func(o *options) {
}
}

// WithTUIContext allows passing a context to the TUI. Shouldn't be in general
// general necessary apart for integration tests.
func WithTUIContext(ctx context.Context) func(o *options) {
return func(o *options) {
o.tuiCtx = ctx
}
}

// New registers commands and return a new App.
func New(opts ...option) *App {
// Set default options.
args := options{
name: watchdconfig.CmdName,
name: watchdconfig.CmdName,
tuiCtx: context.Background(),
}

// Apply given options.
Expand Down Expand Up @@ -164,7 +174,7 @@ func New(opts ...option) *App {
}

configFileSet := a.rootCmd.Flags().Lookup("config").Changed
if err := watchdtui.Start(a.viper.ConfigFileUsed(), prevConfigFile, !configFileSet); err != nil {
if err := watchdtui.Start(a.options.tuiCtx, a.viper.ConfigFileUsed(), prevConfigFile, !configFileSet); err != nil {
return err
}

Expand Down
8 changes: 8 additions & 0 deletions cmd/adwatchd/integration_tests/adwatchd_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,3 +277,11 @@ func TestServiceConfigFlagUsage(t *testing.T) {
})
}
}

func TestServiceWithNoNameFails(t *testing.T) {
app := commands.New(commands.WithServiceName(""))
changeAppArgs(t, app, "", "service", "status")

err := app.Run()
require.Error(t, err, "Service with empty name should fail to create")
}
33 changes: 33 additions & 0 deletions cmd/adwatchd/integration_tests/adwatchd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"os"
"path/filepath"
"runtime"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -41,6 +42,38 @@ func TestMain(m *testing.M) {
m.Run()
}

func TestNoArgumentsStartsTUI(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
app := commands.New(commands.WithTUIContext(ctx), commands.WithServiceName("adwatchd-test-tui"))

changeAppArgs(t, app, "")

// capture stdout
r, w, err := os.Pipe()
require.NoError(t, err, "Setup: pipe shouldn't fail")
orig := os.Stdout
os.Stdout = w

go func() { _ = app.Run() }()

app.WaitReady()
cancel()

// restore and collect
os.Stdout = orig
w.Close()
var out bytes.Buffer
_, errCopy := io.Copy(&out, r)
require.NoError(t, errCopy, "Setup: couldn't copy stdout to buffer")

// Cannot reliably assert on anything if we don't have a TTY...
if _, err := os.Open("/dev/tty"); err != nil && runtime.GOOS != "windows" {
return
}

require.Contains(t, out.String(), "Ubuntu AD Watch Daemon Installer")
}

func generateConfig(t *testing.T, verbosity int, dirs ...string) string {
t.Helper()

Expand Down
83 changes: 83 additions & 0 deletions internal/cmdhandler/cmdhandler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package cmdhandler_test

import (
"testing"

"github.com/spf13/cobra"
"github.com/stretchr/testify/require"
"github.com/ubuntu/adsys/internal/cmdhandler"
)

func TestNoCmd(t *testing.T) {
t.Parallel()

err := cmdhandler.NoCmd(nil, nil)
require.NoError(t, err, "NoCmd should return no error")
}

func TestZeroOrNArgs(t *testing.T) {
t.Parallel()

tests := map[string]struct {
argCount int
args []string

wantErr bool
}{
"Zero args": {},
"Exactly N args": {argCount: 1, args: []string{"arg1"}},

"Error with less than N args": {argCount: 2, args: []string{"arg1"}, wantErr: true},
"Error with more than N args": {argCount: 2, args: []string{"arg1", "arg2", "arg3"}, wantErr: true},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{}
err := cmdhandler.ZeroOrNArgs(tc.argCount)(cmd, tc.args)
if tc.wantErr {
require.Error(t, err, "ZeroOrNArgs should return an error")
return
}
require.NoError(t, err, "ZeroOrNArgs should return no error")
})
}
}

func TestNoValidArgs(t *testing.T) {
t.Parallel()

cmd := &cobra.Command{}
args, directive := cmdhandler.NoValidArgs(cmd, []string{"arg1", "arg2"}, "")

require.Empty(t, args, "NoValidArgs should return no args")
require.Equal(t, directive, cobra.ShellCompDirectiveNoFileComp, "NoValidArgs should return NoFileComp directive")
}

func TestRegisterAlias(t *testing.T) {
t.Parallel()

parent := &cobra.Command{
Use: "parent",
}
child := &cobra.Command{
Use: "child",
Long: "This is the aliased command",
}

cmdhandler.RegisterAlias(child, parent)

// Find the alias command in the parent's commands
var alias *cobra.Command
for _, cmd := range parent.Commands() {
if cmd.Use == "child" {
alias = cmd
break
}
}

require.NotNil(t, alias, "Alias command not found")
require.Equal(t, `This is the aliased command (Alias of "child")`, alias.Long, "Alias long description is incorrect")
require.NotSame(t, child, alias, "Alias command should be a copy, but it points to the same command")
}
4 changes: 2 additions & 2 deletions internal/watchdtui/watchdtui.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,8 +533,8 @@ func (m model) View() string {
}

// Start starts the interactive TUI.
func Start(configFile string, prevConfigFile string, isDefaultConfig bool) error {
p := tea.NewProgram(initialModel(configFile, prevConfigFile, isDefaultConfig))
func Start(ctx context.Context, configFile string, prevConfigFile string, isDefaultConfig bool) error {
p := tea.NewProgram(initialModel(configFile, prevConfigFile, isDefaultConfig), tea.WithContext(ctx))
if _, err := p.Run(); err != nil {
return err
}
Expand Down

0 comments on commit 6610fb3

Please sign in to comment.