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