github.com/rainforestapp/rainforest-cli@v2.12.0+incompatible/runner_test.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"reflect"
     8  	"sort"
     9  	"sync"
    10  	"testing"
    11  
    12  	"github.com/rainforestapp/rainforest-cli/rainforest"
    13  	"github.com/urfave/cli"
    14  )
    15  
    16  func TestStringToIntSlice(t *testing.T) {
    17  	var testCases = []struct {
    18  		commaSepList string
    19  		want         []int
    20  	}{
    21  		{
    22  			commaSepList: "123,456,789",
    23  			want:         []int{123, 456, 789},
    24  		},
    25  		{
    26  			commaSepList: "123, 456, 789",
    27  			want:         []int{123, 456, 789},
    28  		},
    29  		{
    30  			commaSepList: "123",
    31  			want:         []int{123},
    32  		},
    33  	}
    34  
    35  	for _, tCase := range testCases {
    36  		got, _ := stringToIntSlice(tCase.commaSepList)
    37  		if !reflect.DeepEqual(tCase.want, got) {
    38  			t.Errorf("stringToIntSlice returned %+v, want %+v", got, tCase.want)
    39  		}
    40  	}
    41  }
    42  
    43  func TestExpandStringSlice(t *testing.T) {
    44  	var testCases = []struct {
    45  		stringSlice []string
    46  		want        []string
    47  	}{
    48  		{
    49  			stringSlice: []string{"foo,bar,baz"},
    50  			want:        []string{"foo", "bar", "baz"},
    51  		},
    52  		{
    53  			stringSlice: []string{"foo", "bar,baz"},
    54  			want:        []string{"foo", "bar", "baz"},
    55  		},
    56  		{
    57  			stringSlice: []string{"foo, bar", "baz"},
    58  			want:        []string{"foo", "bar", "baz"},
    59  		},
    60  		{
    61  			stringSlice: []string{"foo"},
    62  			want:        []string{"foo"},
    63  		},
    64  	}
    65  
    66  	for _, tCase := range testCases {
    67  		got := expandStringSlice(tCase.stringSlice)
    68  		if !reflect.DeepEqual(tCase.want, got) {
    69  			t.Errorf("expandStringSlice returned %+v, want %+v", got, tCase.want)
    70  		}
    71  	}
    72  }
    73  
    74  type fakeRunnerClient struct {
    75  	runStatuses []rainforest.RunStatus
    76  	environment rainforest.Environment
    77  	// runParams captures whatever params were sent
    78  	runParams rainforest.RunParams
    79  	// createdTests captures which tests were created
    80  	createdTests []*rainforest.RFTest
    81  	// got some potential race conditions!
    82  	mu sync.Mutex
    83  	// "inherit" from RFML API
    84  	testRfmlAPI
    85  }
    86  
    87  func (r *fakeRunnerClient) CheckRunStatus(runID int) (*rainforest.RunStatus, error) {
    88  	for _, runStatus := range r.runStatuses {
    89  		if runStatus.ID == runID {
    90  			return &runStatus, nil
    91  		}
    92  	}
    93  
    94  	return nil, fmt.Errorf("Unable to find run status for run ID %v", runID)
    95  }
    96  
    97  func (r *fakeRunnerClient) GetTestIDs() ([]rainforest.TestIDPair, error) {
    98  	pairs := make([]rainforest.TestIDPair, len(r.createdTests))
    99  	for idx, test := range r.createdTests {
   100  		pairs[idx] = rainforest.TestIDPair{ID: test.TestID, RFMLID: test.RFMLID}
   101  	}
   102  
   103  	return pairs, nil
   104  }
   105  
   106  func (r *fakeRunnerClient) CreateTest(t *rainforest.RFTest) error {
   107  	r.mu.Lock()
   108  	defer r.mu.Unlock()
   109  	t.TestID = len(r.createdTests)
   110  	r.createdTests = append(r.createdTests, t)
   111  	return nil
   112  }
   113  
   114  func (r *fakeRunnerClient) UpdateTest(t *rainforest.RFTest) error {
   115  	// meh
   116  	return nil
   117  }
   118  
   119  func (r *fakeRunnerClient) CreateTemporaryEnvironment(s string) (*rainforest.Environment, error) {
   120  	return &r.environment, nil
   121  }
   122  
   123  func (r *fakeRunnerClient) CreateRun(p rainforest.RunParams) (*rainforest.RunStatus, error) {
   124  	r.runParams = p
   125  	return &rainforest.RunStatus{}, nil
   126  }
   127  
   128  func TestGetRunStatus(t *testing.T) {
   129  	runID := 123
   130  	client := new(fakeRunnerClient)
   131  
   132  	// Run is in a final state
   133  	client.runStatuses = []rainforest.RunStatus{
   134  		{
   135  			ID: runID,
   136  			StateDetails: struct {
   137  				Name         string `json:"name"`
   138  				IsFinalState bool   `json:"is_final_state"`
   139  			}{"", true},
   140  		},
   141  	}
   142  
   143  	_, _, done, err := getRunStatus(false, runID, client)
   144  	if err != nil {
   145  		t.Error(err.Error())
   146  	}
   147  	if !done {
   148  		t.Errorf("Expected \"done\" to be true, got %v", done)
   149  	}
   150  
   151  	// Run is failed and failfast is true
   152  	client.runStatuses = []rainforest.RunStatus{
   153  		{
   154  			ID:     runID,
   155  			Result: "failed",
   156  			StateDetails: struct {
   157  				Name         string `json:"name"`
   158  				IsFinalState bool   `json:"is_final_state"`
   159  			}{"", false},
   160  		},
   161  	}
   162  
   163  	_, _, done, err = getRunStatus(true, runID, client)
   164  	if err != nil {
   165  		t.Error(err.Error())
   166  	}
   167  	if !done {
   168  		t.Errorf("Expected \"done\" to be true, got %v", done)
   169  	}
   170  }
   171  
   172  func TestMakeRunParams(t *testing.T) {
   173  	fakeEnvID := 445566
   174  
   175  	var testCases = []struct {
   176  		mappings map[string]interface{}
   177  		args     cli.Args
   178  		expected rainforest.RunParams
   179  	}{
   180  		{
   181  			mappings: make(map[string]interface{}),
   182  			args:     cli.Args{},
   183  			expected: rainforest.RunParams{},
   184  		},
   185  		{
   186  			mappings: map[string]interface{}{
   187  				"folder":         "123",
   188  				"site":           "456",
   189  				"crowd":          "on_premise_crowd",
   190  				"conflict":       "abort",
   191  				"browser":        []string{"chrome", "firefox,safari"},
   192  				"description":    "my awesome description",
   193  				"release":        "1a2b3c",
   194  				"environment-id": "1337",
   195  				"tag":            []string{"tag", "tag2,tag3"},
   196  			},
   197  			args: cli.Args{"12", "34", "56, 78"},
   198  			expected: rainforest.RunParams{
   199  				SmartFolderID: 123,
   200  				SiteID:        456,
   201  				Crowd:         "on_premise_crowd",
   202  				Conflict:      "abort",
   203  				Browsers:      []string{"chrome", "firefox", "safari"},
   204  				Description:   "my awesome description",
   205  				Release:       "1a2b3c",
   206  				EnvironmentID: 1337,
   207  				Tags:          []string{"tag", "tag2", "tag3"},
   208  				Tests:         []int{12, 34, 56, 78},
   209  			},
   210  		},
   211  		{
   212  			mappings: map[string]interface{}{
   213  				"custom-url": "https://www.rainforestqa.com",
   214  			},
   215  			args: cli.Args{},
   216  			expected: rainforest.RunParams{
   217  				EnvironmentID: fakeEnvID,
   218  			},
   219  		},
   220  		{
   221  			mappings: map[string]interface{}{
   222  				"feature": 123,
   223  			},
   224  			args: cli.Args{},
   225  			expected: rainforest.RunParams{
   226  				FeatureID: 123,
   227  			},
   228  		},
   229  		{
   230  			mappings: map[string]interface{}{
   231  				"run-group": 75,
   232  			},
   233  			expected: rainforest.RunParams{
   234  				RunGroupID: 75,
   235  			},
   236  		},
   237  	}
   238  
   239  	for _, testCase := range testCases {
   240  		c := newFakeContext(testCase.mappings, testCase.args)
   241  		r := newRunner()
   242  		fakeEnv := rainforest.Environment{ID: fakeEnvID, Name: "the foo environment"}
   243  		r.client = &fakeRunnerClient{environment: fakeEnv}
   244  		res, err := r.makeRunParams(c, nil)
   245  
   246  		if err != nil {
   247  			t.Errorf("Error trying to create params: %v", err)
   248  		} else if !reflect.DeepEqual(res, testCase.expected) {
   249  			t.Errorf("Incorrect resulting run params.\nActual: %#v\nExpected: %#v", res, testCase.expected)
   250  		}
   251  	}
   252  }
   253  
   254  func TestStartLocalRun(t *testing.T) {
   255  	rfmlDir := setupTestRFMLDir()
   256  	defer os.RemoveAll(rfmlDir)
   257  
   258  	testCases := []struct {
   259  		mappings    map[string]interface{}
   260  		args        cli.Args
   261  		wantError   bool
   262  		wantUpload  []string
   263  		wantExecute []string
   264  	}{
   265  		{
   266  			mappings: map[string]interface{}{
   267  				"f":   true,
   268  				"tag": []string{"foo", "bar"},
   269  				// There's less to stub with bg
   270  				"bg": true,
   271  			},
   272  			args: cli.Args{filepath.Join(rfmlDir, "a"), filepath.Join(rfmlDir, "b/b")},
   273  			// a1 depends on b4 and b4 depends on b5, so b4 and b5 are uploaded
   274  			// even though they're not tagged properly.
   275  			wantUpload: []string{"a1", "a3", "b3", "b4", "b5"},
   276  			// b3 is execute: false so we shouldn't run it
   277  			wantExecute: []string{"a1", "a3"},
   278  		},
   279  		{
   280  			mappings: map[string]interface{}{
   281  				"f":  true,
   282  				"bg": true,
   283  			},
   284  			args:      cli.Args{filepath.Join(rfmlDir, "a")},
   285  			wantError: true,
   286  		},
   287  		{
   288  			mappings: map[string]interface{}{
   289  				"f":       true,
   290  				"feature": 42,
   291  				"bg":      true,
   292  			},
   293  			args:      cli.Args{filepath.Join(rfmlDir, "a"), filepath.Join(rfmlDir, "b/b")},
   294  			wantError: true,
   295  		},
   296  		{
   297  			mappings: map[string]interface{}{
   298  				"f":         true,
   299  				"run-group": 42,
   300  				"bg":        true,
   301  			},
   302  			args:      cli.Args{filepath.Join(rfmlDir, "a"), filepath.Join(rfmlDir, "b/b")},
   303  			wantError: true,
   304  		},
   305  		{
   306  			mappings: map[string]interface{}{
   307  				"f":    true,
   308  				"site": "42",
   309  				"bg":   true,
   310  			},
   311  			args:      cli.Args{filepath.Join(rfmlDir, "a"), filepath.Join(rfmlDir, "b/b")},
   312  			wantError: true,
   313  		},
   314  		{
   315  
   316  			mappings: map[string]interface{}{
   317  				"f":             true,
   318  				"bg":            true,
   319  				"force-execute": []string{filepath.Join(rfmlDir, "b/b/b3.rfml")},
   320  				"exclude":       []string{filepath.Join(rfmlDir, "a/a2.rfml")},
   321  			},
   322  			args: cli.Args{
   323  				filepath.Join(rfmlDir, "a/a2.rfml"),
   324  				filepath.Join(rfmlDir, "a/a3.rfml"),
   325  				filepath.Join(rfmlDir, "b/b/b3.rfml"),
   326  			},
   327  			wantUpload: []string{"a2", "a3", "b3"},
   328  			// We don't want to execute a2 but we override execute:false on b3
   329  			wantExecute: []string{"a3", "b3"},
   330  		},
   331  	}
   332  
   333  	for _, testCase := range testCases {
   334  		c := newFakeContext(testCase.mappings, testCase.args)
   335  		r := newRunner()
   336  		fakeEnv := rainforest.Environment{ID: 123, Name: "the foo environment"}
   337  		client := &fakeRunnerClient{environment: fakeEnv}
   338  		r.client = client
   339  
   340  		err := r.startRun(c)
   341  		if !testCase.wantError && err != nil {
   342  			t.Error("Error starting run:", err)
   343  		} else if testCase.wantError && err == nil {
   344  			t.Errorf("Expected test to fail with args %v but it didn't", testCase.args)
   345  		}
   346  
   347  		var got []string
   348  		for _, t := range client.createdTests {
   349  			got = append(got, t.RFMLID)
   350  		}
   351  		sort.Strings(got)
   352  		want := testCase.wantUpload
   353  		if !reflect.DeepEqual(want, got) {
   354  			t.Errorf("Tests were not uploaded correctly, wanted %v, got %v", want, got)
   355  		}
   356  
   357  		// Check that the right tests were requested for the run
   358  		got = client.runParams.RFMLIDs
   359  		sort.Strings(got)
   360  		want = testCase.wantExecute
   361  		if !reflect.DeepEqual(want, got) {
   362  			t.Errorf("Incorrect tests were requested when starting run, wanted %v, got %v", want, got)
   363  		}
   364  	}
   365  }