github.com/jdolitsky/cnab-go@v0.7.1-beta1/action/run_custom_test.go (about)

     1  package action
     2  
     3  import (
     4  	"errors"
     5  	"io/ioutil"
     6  	"testing"
     7  
     8  	"github.com/deislabs/cnab-go/bundle"
     9  	"github.com/deislabs/cnab-go/claim"
    10  	"github.com/deislabs/cnab-go/driver"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  // makes sure RunCustom implements Action interface
    16  var _ Action = &RunCustom{}
    17  
    18  func TestRunCustom(t *testing.T) {
    19  	out := func(op *driver.Operation) error {
    20  		op.Out = ioutil.Discard
    21  		return nil
    22  	}
    23  
    24  	rc := &RunCustom{
    25  		Driver: &mockDriver{
    26  			shouldHandle: true,
    27  			Result: driver.OperationResult{
    28  				Outputs: map[string]string{
    29  					"/tmp/some/path": "SOME CONTENT",
    30  				},
    31  			},
    32  			Error: nil,
    33  		},
    34  		Action: "test",
    35  	}
    36  
    37  	t.Run("happy-path", func(t *testing.T) {
    38  		c := newClaim()
    39  		err := rc.Run(c, mockSet, out)
    40  		assert.NoError(t, err)
    41  		assert.Equal(t, claim.StatusSuccess, c.Result.Status)
    42  		assert.Equal(t, "test", c.Result.Action)
    43  		assert.Equal(t, map[string]interface{}{"some-output": "SOME CONTENT"}, c.Outputs)
    44  	})
    45  
    46  	t.Run("configure operation", func(t *testing.T) {
    47  		c := newClaim()
    48  		d := &mockDriver{
    49  			shouldHandle: true,
    50  			Result: driver.OperationResult{
    51  				Outputs: map[string]string{
    52  					"/tmp/some/path": "SOME CONTENT",
    53  				},
    54  			},
    55  			Error: nil,
    56  		}
    57  		inst := &RunCustom{Driver: d, Action: "test"}
    58  		addFile := func(op *driver.Operation) error {
    59  			op.Files["/tmp/another/path"] = "ANOTHER FILE"
    60  			return nil
    61  		}
    62  		require.NoError(t, inst.Run(c, mockSet, out, addFile))
    63  		assert.Contains(t, d.Operation.Files, "/tmp/another/path")
    64  	})
    65  
    66  	t.Run("error case: configure operation", func(t *testing.T) {
    67  		c := newClaim()
    68  		d := &mockDriver{
    69  			shouldHandle: true,
    70  			Result: driver.OperationResult{
    71  				Outputs: map[string]string{
    72  					"/tmp/some/path": "SOME CONTENT",
    73  				},
    74  			},
    75  			Error: nil,
    76  		}
    77  		inst := &RunCustom{Driver: d, Action: "test"}
    78  		sabotage := func(op *driver.Operation) error {
    79  			return errors.New("oops")
    80  		}
    81  		require.EqualError(t, inst.Run(c, mockSet, out, sabotage), "oops")
    82  	})
    83  
    84  	t.Run("when there are no outputs in the bundle", func(t *testing.T) {
    85  		c := newClaim()
    86  		c.Bundle.Outputs = nil
    87  		rc.Driver = &mockDriver{
    88  			shouldHandle: true,
    89  			Result:       driver.OperationResult{},
    90  			Error:        nil,
    91  		}
    92  		err := rc.Run(c, mockSet, out)
    93  		assert.NoError(t, err)
    94  		assert.NotEqual(t, c.Created, c.Modified, "Claim was not updated with modified timestamp after custom action")
    95  		assert.Equal(t, claim.StatusSuccess, c.Result.Status)
    96  		assert.Equal(t, "test", c.Result.Action)
    97  		assert.Empty(t, c.Outputs)
    98  	})
    99  
   100  	t.Run("error case: driver doesn't handle image", func(t *testing.T) {
   101  		c := newClaim()
   102  		rc.Driver = &mockDriver{
   103  			Error:        errors.New("I always fail"),
   104  			shouldHandle: false,
   105  		}
   106  		err := rc.Run(c, mockSet, out)
   107  		assert.Error(t, err)
   108  		assert.Empty(t, c.Outputs)
   109  	})
   110  
   111  	t.Run("error case: driver returns error", func(t *testing.T) {
   112  		c := newClaim()
   113  		rc.Driver = &mockDriver{
   114  			Result: driver.OperationResult{
   115  				Outputs: map[string]string{
   116  					"/tmp/some/path": "SOME CONTENT",
   117  				},
   118  			},
   119  			Error:        errors.New("I always fail"),
   120  			shouldHandle: true,
   121  		}
   122  		err := rc.Run(c, mockSet, out)
   123  		assert.Error(t, err)
   124  		assert.NotEqual(t, "", c.Result.Message, "Expected error message in claim result message")
   125  		assert.Equal(t, "test", c.Result.Action)
   126  		assert.Equal(t, claim.StatusFailure, c.Result.Status)
   127  		assert.Equal(t, map[string]interface{}{"some-output": "SOME CONTENT"}, c.Outputs)
   128  	})
   129  
   130  	t.Run("error case: driver returns an error but the action does not modify", func(t *testing.T) {
   131  		c := newClaim()
   132  		action := c.Bundle.Actions["test"]
   133  		action.Modifies = false
   134  		c.Bundle.Actions["test"] = action
   135  
   136  		rc.Driver = &mockDriver{
   137  			Result: driver.OperationResult{
   138  				Outputs: map[string]string{
   139  					"/tmp/some/path": "SOME CONTENT",
   140  				},
   141  			},
   142  			Error:        errors.New("I always fail"),
   143  			shouldHandle: true,
   144  		}
   145  		err := rc.Run(c, mockSet, out)
   146  		assert.Error(t, err)
   147  		assert.Empty(t, c.Result, "Expected claim results not to be tracked when the action does not modify")
   148  		assert.Empty(t, c.Outputs, "Expected output results not to be tracked with the action does not modify")
   149  	})
   150  
   151  	t.Run("error case: forbidden custom actions should fail", func(t *testing.T) {
   152  		c := newClaim()
   153  		rc.Action = "install"
   154  		err := rc.Run(c, mockSet, out)
   155  		assert.Error(t, err)
   156  		assert.Empty(t, c.Outputs)
   157  	})
   158  
   159  	t.Run("error case: unknown actions should fail", func(t *testing.T) {
   160  		c := newClaim()
   161  		rc.Action = "test"
   162  		c.Bundle.Actions = map[string]bundle.Action{}
   163  		err := rc.Run(c, mockSet, out)
   164  		assert.Error(t, err, "Unknown action should fail")
   165  		assert.Empty(t, c.Outputs)
   166  	})
   167  }