github.com/replicatedhq/ship@v0.55.0/pkg/lifecycle/render/render_test.go (about)

     1  package render
     2  
     3  import (
     4  	"context"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/go-kit/kit/log"
    13  	"github.com/golang/mock/gomock"
    14  	"github.com/mitchellh/cli"
    15  	"github.com/replicatedhq/ship/pkg/api"
    16  	"github.com/replicatedhq/ship/pkg/lifecycle/daemon/daemontypes"
    17  	"github.com/replicatedhq/ship/pkg/lifecycle/render/planner"
    18  	_ "github.com/replicatedhq/ship/pkg/lifecycle/render/test-cases"
    19  	"github.com/replicatedhq/ship/pkg/state"
    20  	mockconfig "github.com/replicatedhq/ship/pkg/test-mocks/config"
    21  	mockdaemon "github.com/replicatedhq/ship/pkg/test-mocks/daemon"
    22  	mockplanner "github.com/replicatedhq/ship/pkg/test-mocks/planner"
    23  	"github.com/replicatedhq/ship/pkg/test-mocks/ui"
    24  	"github.com/replicatedhq/ship/pkg/testing/logger"
    25  	"github.com/spf13/afero"
    26  	"github.com/spf13/viper"
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/require"
    29  	yaml "gopkg.in/yaml.v3"
    30  )
    31  
    32  type testcase struct {
    33  	Name        string
    34  	Metadata    api.ReleaseMetadata
    35  	Spec        api.Spec
    36  	ViperConfig map[string]interface{} `yaml:"viper_config"`
    37  	Responses   map[string]string
    38  	Expect      map[string]string
    39  }
    40  
    41  func TestRender(t *testing.T) {
    42  	ctx := context.Background()
    43  
    44  	tests := loadTestCases(t, filepath.Join("test-cases", "render-inline.yaml"))
    45  
    46  	for _, test := range tests {
    47  		t.Run(test.Name, func(t *testing.T) {
    48  			mc := gomock.NewController(t)
    49  
    50  			mockUI := ui.NewMockUi(mc)
    51  			p := mockplanner.NewMockPlanner(mc)
    52  			configResolver := mockconfig.NewMockResolver(mc)
    53  			mockFS := afero.Afero{Fs: afero.NewMemMapFs()}
    54  			mockDaemon := mockdaemon.NewMockDaemon(mc)
    55  
    56  			renderer := &headlessrenderer{
    57  				Logger: log.NewNopLogger(),
    58  				Now:    time.Now,
    59  			}
    60  			renderer.Fs = mockFS
    61  			renderer.UI = mockUI
    62  			renderer.ConfigResolver = configResolver
    63  			renderer.Planner = p
    64  
    65  			// this has to be the singleton state manager because ship will not always
    66  			// re-use renderer.StateManager
    67  			state, err := state.GetManager(renderer.Logger, mockFS, viper.New())
    68  			assert.NoError(t, err)
    69  
    70  			renderer.StateManager = state
    71  
    72  			prog := mockDaemon.EXPECT().SetProgress(ProgressRead)
    73  			prog = mockDaemon.EXPECT().SetProgress(ProgressRender).After(prog)
    74  			prog = mockDaemon.EXPECT().SetStepName(ctx, daemontypes.StepNameConfirm).After(prog)
    75  			mockDaemon.EXPECT().ClearProgress().After(prog)
    76  
    77  			renderer.StatusReceiver = mockDaemon
    78  
    79  			release := &api.Release{Spec: test.Spec}
    80  
    81  			func() {
    82  				defer mc.Finish()
    83  
    84  				configResolver.EXPECT().
    85  					ResolveConfig(ctx, release, gomock.Any()).
    86  					Return(test.ViperConfig, nil)
    87  
    88  				p.EXPECT().
    89  					Build("installer", test.Spec.Assets.V1, test.Spec.Config.V1, gomock.Any(), test.ViperConfig).
    90  					Return(planner.Plan{}, []string{}, nil)
    91  
    92  				p.EXPECT().
    93  					Execute(ctx, planner.Plan{}).
    94  					Return(nil)
    95  
    96  				err := renderer.Execute(ctx, release, &api.Render{})
    97  				assert.NoError(t, err)
    98  			}()
    99  		})
   100  	}
   101  }
   102  
   103  func TestBacksUpExisting(t *testing.T) {
   104  	tests := []struct {
   105  		name     string
   106  		target   string
   107  		existing []string
   108  		expect   []string
   109  	}{
   110  		{
   111  			name:     "empty",
   112  			target:   "/tmp/installer",
   113  			existing: []string{},
   114  			expect:   []string{},
   115  		},
   116  		{
   117  			name:   "backs up file",
   118  			target: "/tmp/installer",
   119  			existing: []string{
   120  				"/tmp/installer",
   121  			},
   122  			expect: []string{
   123  				"/tmp/installer.bak",
   124  			},
   125  		},
   126  	}
   127  
   128  	for _, test := range tests {
   129  		t.Run(test.name, func(t *testing.T) {
   130  			req := require.New(t)
   131  			mockFS := afero.Afero{Fs: afero.NewMemMapFs()}
   132  			r := headlessrenderer{
   133  				Logger: &logger.TestLogger{T: t},
   134  				Fs:     mockFS,
   135  				Now: func() time.Time {
   136  					return time.Unix(12345, 0)
   137  				},
   138  				UI: &cli.MockUi{},
   139  			}
   140  
   141  			for _, filename := range test.existing {
   142  				err := mockFS.WriteFile(filename, []byte("not a directory but thats okay"), 0755)
   143  				req.NoError(err)
   144  			}
   145  
   146  			err := r.backupIfPresent(test.target)
   147  			req.NoError(err)
   148  
   149  			debugFs := &strings.Builder{}
   150  			err = r.Fs.Walk("/", func(path string, info os.FileInfo, err error) error {
   151  				debugFs.WriteString(path)
   152  				debugFs.WriteString("\n")
   153  				return nil
   154  			})
   155  			req.NoError(err)
   156  
   157  			for _, filename := range test.expect {
   158  				exists, err := mockFS.Exists(filename)
   159  				req.NoError(err)
   160  				req.True(exists, "expected file %s to exist, fs had %s", filename, debugFs)
   161  			}
   162  
   163  		})
   164  	}
   165  }
   166  
   167  func loadTestCases(t *testing.T, path string) []testcase {
   168  	tests := make([]testcase, 1)
   169  	contents, err := ioutil.ReadFile(path)
   170  	assert.NoError(t, err)
   171  	err = yaml.Unmarshal(contents, &tests)
   172  	assert.NoError(t, err)
   173  	return tests
   174  }