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 }