github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/action/showoutput_test.go (about)

     1  // Copyright 2014-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package action_test
     5  
     6  import (
     7  	"bytes"
     8  	"errors"
     9  	"strings"
    10  	"time"
    11  
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/apiserver/common"
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/cmd/juju/action"
    17  	"github.com/juju/juju/testing"
    18  )
    19  
    20  type ShowOutputSuite struct {
    21  	BaseActionSuite
    22  }
    23  
    24  var _ = gc.Suite(&ShowOutputSuite{})
    25  
    26  func (s *ShowOutputSuite) SetUpTest(c *gc.C) {
    27  	s.BaseActionSuite.SetUpTest(c)
    28  }
    29  
    30  func (s *ShowOutputSuite) TestInit(c *gc.C) {
    31  	tests := []struct {
    32  		should      string
    33  		args        []string
    34  		expectError string
    35  	}{{
    36  		should:      "fail with missing arg",
    37  		args:        []string{},
    38  		expectError: "no action ID specified",
    39  	}, {
    40  		should:      "fail with multiple args",
    41  		args:        []string{"12345", "54321"},
    42  		expectError: `unrecognized args: \["54321"\]`,
    43  	}}
    44  
    45  	for i, t := range tests {
    46  		for _, modelFlag := range s.modelFlags {
    47  			c.Logf("test %d: it should %s: juju show-action-output %s", i,
    48  				t.should, strings.Join(t.args, " "))
    49  			cmd, _ := action.NewShowOutputCommandForTest(s.store)
    50  			args := append([]string{modelFlag, "admin"}, t.args...)
    51  			err := testing.InitCommand(cmd, args)
    52  			if t.expectError != "" {
    53  				c.Check(err, gc.ErrorMatches, t.expectError)
    54  			}
    55  		}
    56  	}
    57  }
    58  
    59  func (s *ShowOutputSuite) TestRun(c *gc.C) {
    60  	tests := []struct {
    61  		should            string
    62  		withClientWait    string
    63  		withClientQueryID string
    64  		withAPIDelay      time.Duration
    65  		withAPITimeout    time.Duration
    66  		withTags          params.FindTagsResults
    67  		withAPIResponse   []params.ActionResult
    68  		withAPIError      string
    69  		expectedErr       string
    70  		expectedOutput    string
    71  	}{{
    72  		should:         "handle wait-time formatting errors",
    73  		withClientWait: "not-a-duration-at-all",
    74  		expectedErr:    "time: invalid duration not-a-duration-at-all",
    75  	}, {
    76  		should:            "timeout if result never comes",
    77  		withClientWait:    "3s",
    78  		withAPIDelay:      6 * time.Second,
    79  		withAPITimeout:    10 * time.Second,
    80  		withClientQueryID: validActionId,
    81  		withTags:          tagsForIdPrefix(validActionId, validActionTagString),
    82  		withAPIResponse:   []params.ActionResult{{}},
    83  		expectedOutput: `
    84  status: pending
    85  timing:
    86    enqueued: 2015-02-14 08:13:00 +0000 UTC
    87    started: 2015-02-14 08:15:00 +0000 UTC
    88  `[1:],
    89  	}, {
    90  		should:            "pass api error through properly",
    91  		withClientQueryID: validActionId,
    92  		withAPITimeout:    10 * time.Second,
    93  		withTags:          tagsForIdPrefix(validActionId, validActionTagString),
    94  		withAPIError:      "api call error",
    95  		expectedErr:       "api call error",
    96  	}, {
    97  		should:            "fail with no tag matches",
    98  		withClientQueryID: validActionId,
    99  		withAPITimeout:    10 * time.Second,
   100  		withTags:          tagsForIdPrefix(validActionId),
   101  		expectedErr:       `actions for identifier "` + validActionId + `" not found`,
   102  	}, {
   103  		should:            "fail with no results",
   104  		withClientQueryID: validActionId,
   105  		withAPITimeout:    10 * time.Second,
   106  		withTags:          tagsForIdPrefix(validActionId, validActionTagString),
   107  		withAPIResponse:   []params.ActionResult{},
   108  		expectedErr:       "no results for action " + validActionId,
   109  	}, {
   110  		should:            "error correctly with multiple results",
   111  		withClientQueryID: validActionId,
   112  		withAPITimeout:    10 * time.Second,
   113  		withTags:          tagsForIdPrefix(validActionId, validActionTagString),
   114  		withAPIResponse:   []params.ActionResult{{}, {}},
   115  		expectedErr:       "too many results for action " + validActionId,
   116  	}, {
   117  		should:            "pass through an error from the API server",
   118  		withClientQueryID: validActionId,
   119  		withAPITimeout:    10 * time.Second,
   120  		withTags:          tagsForIdPrefix(validActionId, validActionTagString),
   121  		withAPIResponse: []params.ActionResult{{
   122  			Error: common.ServerError(errors.New("an apiserver error")),
   123  		}},
   124  		expectedErr: "an apiserver error",
   125  	}, {
   126  		should:            "only return once status is no longer running or pending",
   127  		withAPIDelay:      2 * time.Second,
   128  		withClientWait:    "6s",
   129  		withClientQueryID: validActionId,
   130  		withAPITimeout:    4 * time.Second,
   131  		withTags:          tagsForIdPrefix(validActionId, validActionTagString),
   132  		withAPIResponse: []params.ActionResult{{
   133  			Status: "running",
   134  			Output: map[string]interface{}{
   135  				"foo": map[string]interface{}{
   136  					"bar": "baz",
   137  				},
   138  			},
   139  			Enqueued: time.Date(2015, time.February, 14, 8, 13, 0, 0, time.UTC),
   140  			Started:  time.Date(2015, time.February, 14, 8, 15, 0, 0, time.UTC),
   141  		}},
   142  		expectedErr: "test timed out before wait time",
   143  	}, {
   144  		should:            "pretty-print action output",
   145  		withClientQueryID: validActionId,
   146  		withAPITimeout:    10 * time.Second,
   147  		withTags:          tagsForIdPrefix(validActionId, validActionTagString),
   148  		withAPIResponse: []params.ActionResult{{
   149  			Status:  "complete",
   150  			Message: "oh dear",
   151  			Output: map[string]interface{}{
   152  				"foo": map[string]interface{}{
   153  					"bar": "baz",
   154  				},
   155  			},
   156  			Enqueued:  time.Date(2015, time.February, 14, 8, 13, 0, 0, time.UTC),
   157  			Started:   time.Date(2015, time.February, 14, 8, 15, 0, 0, time.UTC),
   158  			Completed: time.Date(2015, time.February, 14, 8, 15, 30, 0, time.UTC),
   159  		}},
   160  		expectedOutput: `
   161  message: oh dear
   162  results:
   163    foo:
   164      bar: baz
   165  status: complete
   166  timing:
   167    completed: 2015-02-14 08:15:30 +0000 UTC
   168    enqueued: 2015-02-14 08:13:00 +0000 UTC
   169    started: 2015-02-14 08:15:00 +0000 UTC
   170  `[1:],
   171  	}, {
   172  		should:            "pretty-print action output with no completed time",
   173  		withClientQueryID: validActionId,
   174  		withAPITimeout:    10 * time.Second,
   175  		withTags:          tagsForIdPrefix(validActionId, validActionTagString),
   176  		withAPIResponse: []params.ActionResult{{
   177  			Status: "pending",
   178  			Output: map[string]interface{}{
   179  				"foo": map[string]interface{}{
   180  					"bar": "baz",
   181  				},
   182  			},
   183  			Enqueued: time.Date(2015, time.February, 14, 8, 13, 0, 0, time.UTC),
   184  			Started:  time.Date(2015, time.February, 14, 8, 15, 0, 0, time.UTC),
   185  		}},
   186  		expectedOutput: `
   187  results:
   188    foo:
   189      bar: baz
   190  status: pending
   191  timing:
   192    enqueued: 2015-02-14 08:13:00 +0000 UTC
   193    started: 2015-02-14 08:15:00 +0000 UTC
   194  `[1:],
   195  	}, {
   196  		should:            "pretty-print action output with no enqueued time",
   197  		withClientQueryID: validActionId,
   198  		withAPITimeout:    10 * time.Second,
   199  		withTags:          tagsForIdPrefix(validActionId, validActionTagString),
   200  		withAPIResponse: []params.ActionResult{{
   201  			Status: "pending",
   202  			Output: map[string]interface{}{
   203  				"foo": map[string]interface{}{
   204  					"bar": "baz",
   205  				},
   206  			},
   207  			Completed: time.Date(2015, time.February, 14, 8, 15, 30, 0, time.UTC),
   208  			Started:   time.Date(2015, time.February, 14, 8, 15, 0, 0, time.UTC),
   209  		}},
   210  		expectedOutput: `
   211  results:
   212    foo:
   213      bar: baz
   214  status: pending
   215  timing:
   216    completed: 2015-02-14 08:15:30 +0000 UTC
   217    started: 2015-02-14 08:15:00 +0000 UTC
   218  `[1:],
   219  	}, {
   220  		should:            "pretty-print action output with no started time",
   221  		withClientQueryID: validActionId,
   222  		withAPITimeout:    10 * time.Second,
   223  		withTags:          tagsForIdPrefix(validActionId, validActionTagString),
   224  		withAPIResponse: []params.ActionResult{{
   225  			Status: "pending",
   226  			Output: map[string]interface{}{
   227  				"foo": map[string]interface{}{
   228  					"bar": "baz",
   229  				},
   230  			},
   231  			Enqueued:  time.Date(2015, time.February, 14, 8, 13, 0, 0, time.UTC),
   232  			Completed: time.Date(2015, time.February, 14, 8, 15, 30, 0, time.UTC),
   233  		}},
   234  		expectedOutput: `
   235  results:
   236    foo:
   237      bar: baz
   238  status: pending
   239  timing:
   240    completed: 2015-02-14 08:15:30 +0000 UTC
   241    enqueued: 2015-02-14 08:13:00 +0000 UTC
   242  `[1:],
   243  	}, {
   244  		should:            "set an appropriate timer and wait, get a result",
   245  		withClientQueryID: validActionId,
   246  		withAPITimeout:    10 * time.Second,
   247  		withClientWait:    "4s",
   248  		withAPIDelay:      2 * time.Second,
   249  		withTags:          tagsForIdPrefix(validActionId, validActionTagString),
   250  		withAPIResponse: []params.ActionResult{{
   251  			Status: "completed",
   252  			Output: map[string]interface{}{
   253  				"foo": map[string]interface{}{
   254  					"bar": "baz",
   255  				},
   256  			},
   257  			Enqueued:  time.Date(2015, time.February, 14, 8, 13, 0, 0, time.UTC),
   258  			Completed: time.Date(2015, time.February, 14, 8, 15, 30, 0, time.UTC),
   259  		}},
   260  		expectedOutput: `
   261  results:
   262    foo:
   263      bar: baz
   264  status: completed
   265  timing:
   266    completed: 2015-02-14 08:15:30 +0000 UTC
   267    enqueued: 2015-02-14 08:13:00 +0000 UTC
   268  `[1:],
   269  	}}
   270  
   271  	for i, t := range tests {
   272  		for _, modelFlag := range s.modelFlags {
   273  			c.Logf("test %d (model flag %v): should %s", i, modelFlag, t.should)
   274  			testRunHelper(
   275  				c, s,
   276  				makeFakeClient(
   277  					t.withAPIDelay,
   278  					t.withAPITimeout,
   279  					t.withTags,
   280  					t.withAPIResponse,
   281  					params.ActionsByNames{},
   282  					t.withAPIError),
   283  				t.expectedErr,
   284  				t.expectedOutput,
   285  				t.withClientWait,
   286  				t.withClientQueryID,
   287  				modelFlag,
   288  			)
   289  		}
   290  	}
   291  }
   292  
   293  func testRunHelper(c *gc.C, s *ShowOutputSuite, client *fakeAPIClient, expectedErr, expectedOutput, wait, query, modelFlag string) {
   294  	unpatch := s.BaseActionSuite.patchAPIClient(client)
   295  	defer unpatch()
   296  	args := append([]string{modelFlag, "admin"}, query)
   297  	if wait != "" {
   298  		args = append(args, "--wait", wait)
   299  	}
   300  	cmd, _ := action.NewShowOutputCommandForTest(s.store)
   301  	ctx, err := testing.RunCommand(c, cmd, args...)
   302  	if expectedErr != "" {
   303  		c.Check(err, gc.ErrorMatches, expectedErr)
   304  	} else {
   305  		c.Assert(err, gc.IsNil)
   306  		c.Check(ctx.Stdout.(*bytes.Buffer).String(), gc.Equals, expectedOutput)
   307  	}
   308  }
   309  
   310  func makeFakeClient(
   311  	delay, timeout time.Duration,
   312  	tags params.FindTagsResults,
   313  	response []params.ActionResult,
   314  	actionsByNames params.ActionsByNames,
   315  	errStr string,
   316  ) *fakeAPIClient {
   317  	client := &fakeAPIClient{
   318  		delay:            time.NewTimer(delay),
   319  		timeout:          time.NewTimer(timeout),
   320  		actionTagMatches: tags,
   321  		actionResults:    response,
   322  		actionsByNames:   actionsByNames,
   323  	}
   324  	if errStr != "" {
   325  		client.apiErr = errors.New(errStr)
   326  	}
   327  	return client
   328  }