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

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"log"
     8  	"os"
     9  	"path/filepath"
    10  	"reflect"
    11  	"sort"
    12  	"strconv"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/rainforestapp/rainforest-cli/rainforest"
    17  	"github.com/urfave/cli"
    18  )
    19  
    20  func TestNewRFMLTest(t *testing.T) {
    21  	context := new(fakeContext)
    22  	testDefaultSpecFolder := "testing/" + defaultSpecFolder
    23  
    24  	/*
    25  	   Declare reusable variables
    26  	*/
    27  	var contents []byte
    28  	var rfmlText string
    29  	var err error
    30  	var expectedRFMLPath string
    31  	var file *os.File
    32  
    33  	/*
    34  	   Helper functions
    35  	*/
    36  	testExpectation := func(filePath string, title string) {
    37  		_, err = os.Stat(filePath)
    38  		if os.IsNotExist(err) {
    39  			t.Errorf("Expected RFML test does not exist: %v", filePath)
    40  			return
    41  		}
    42  
    43  		contents, err = ioutil.ReadFile(filePath)
    44  		if err != nil {
    45  			t.Error(err.Error())
    46  			return
    47  		}
    48  
    49  		rfmlText = string(contents)
    50  
    51  		if !strings.Contains(rfmlText, title) {
    52  			t.Errorf("Expected title \"%v\" to appear in RFML test", title)
    53  		}
    54  	}
    55  
    56  	/*
    57  	   No flags or args and spec folder doesn't exist
    58  	*/
    59  	context.mappings = map[string]interface{}{
    60  		"test-folder": testDefaultSpecFolder,
    61  	}
    62  
    63  	err = newRFMLTest(context)
    64  	if err != nil {
    65  		t.Error(err.Error())
    66  	}
    67  
    68  	expectedRFMLPath = filepath.Join(testDefaultSpecFolder, "Unnamed Test.rfml")
    69  	testExpectation(expectedRFMLPath, "Unnamed Test")
    70  	err = os.RemoveAll("./testing")
    71  	if err != nil {
    72  		t.Fatal(err.Error())
    73  	}
    74  
    75  	/*
    76  	   No flags or args and spec folder does exist
    77  	*/
    78  	err = os.MkdirAll(testDefaultSpecFolder, os.ModePerm)
    79  	if err != nil {
    80  		t.Fatal(err.Error())
    81  	}
    82  
    83  	err = newRFMLTest(context)
    84  	if err != nil {
    85  		t.Error(err.Error())
    86  	}
    87  
    88  	testExpectation(expectedRFMLPath, "Unnamed Test")
    89  	err = os.RemoveAll("./testing")
    90  	if err != nil {
    91  		t.Fatal(err.Error())
    92  	}
    93  
    94  	/*
    95  	   Test folder given
    96  	*/
    97  	specFolder := "./my_specs"
    98  	err = os.MkdirAll(specFolder, os.ModePerm)
    99  	if err != nil {
   100  		t.Fatal(err)
   101  	}
   102  
   103  	context.mappings = map[string]interface{}{
   104  		"test-folder": specFolder,
   105  	}
   106  
   107  	err = newRFMLTest(context)
   108  	if err != nil {
   109  		err = os.RemoveAll(specFolder)
   110  		if err != nil {
   111  			t.Fatal(err.Error())
   112  		}
   113  		t.Fatal(err)
   114  	}
   115  
   116  	expectedRFMLPath = filepath.Join(specFolder, "Unnamed Test.rfml")
   117  	testExpectation(expectedRFMLPath, "Unnamed Test")
   118  	err = os.RemoveAll(specFolder)
   119  	if err != nil {
   120  		t.Fatal(err.Error())
   121  	}
   122  
   123  	/*
   124  	   Filename argument given
   125  	*/
   126  	context.mappings = map[string]interface{}{
   127  		"test-folder": testDefaultSpecFolder,
   128  	}
   129  
   130  	context.args = []string{"my_file_name.rfml"}
   131  
   132  	err = newRFMLTest(context)
   133  	if err != nil {
   134  		t.Fatal(err.Error())
   135  	}
   136  
   137  	expectedRFMLPath = filepath.Join(testDefaultSpecFolder, "my_file_name.rfml")
   138  	testExpectation(expectedRFMLPath, "my_file_name")
   139  	err = os.RemoveAll("./testing")
   140  	if err != nil {
   141  		t.Fatal(err.Error())
   142  	}
   143  
   144  	/*
   145  	   Title argument given
   146  	*/
   147  	context.args = []string{"my_test_title"}
   148  
   149  	err = newRFMLTest(context)
   150  	if err != nil {
   151  		t.Fatal(err.Error())
   152  	}
   153  
   154  	expectedRFMLPath = filepath.Join(testDefaultSpecFolder, "my_test_title.rfml")
   155  	testExpectation(expectedRFMLPath, "my_test_title")
   156  	err = os.RemoveAll("./testing")
   157  	if err != nil {
   158  		t.Fatal(err.Error())
   159  	}
   160  
   161  	/*
   162  	   Test folder flag is actually a file
   163  	*/
   164  	dummyFolder := "./dummy"
   165  	err = os.MkdirAll(dummyFolder, os.ModePerm)
   166  	if err != nil {
   167  		t.Fatal(err)
   168  	}
   169  
   170  	dummyFilePath := filepath.Join(dummyFolder, "dummy_file")
   171  	file, err = os.Create(dummyFilePath)
   172  	if err != nil {
   173  		err = os.RemoveAll(dummyFolder)
   174  		if err != nil {
   175  			t.Fatal(err.Error())
   176  		}
   177  		t.Fatal(err)
   178  	}
   179  
   180  	file.Close()
   181  
   182  	context.args = []string{}
   183  	context.mappings = map[string]interface{}{
   184  		"test-folder": dummyFilePath,
   185  	}
   186  
   187  	err = newRFMLTest(context)
   188  	if err == nil {
   189  		t.Error("Expecting an error, got nil")
   190  	}
   191  
   192  	os.RemoveAll(dummyFolder)
   193  
   194  	/*
   195  	   RFML file already exists
   196  	*/
   197  	context.mappings = map[string]interface{}{
   198  		"test-folder": testDefaultSpecFolder,
   199  	}
   200  
   201  	err = os.MkdirAll(testDefaultSpecFolder, os.ModePerm)
   202  	if err != nil {
   203  		t.Fatal(err.Error())
   204  	}
   205  
   206  	existingRFMLPath := filepath.Join(testDefaultSpecFolder, "Unnamed Test.rfml")
   207  	file, err = os.Create(existingRFMLPath)
   208  	file.Close()
   209  	if err != nil {
   210  		t.Fatal(err.Error())
   211  	}
   212  
   213  	err = newRFMLTest(context)
   214  	if err != nil {
   215  		err = os.RemoveAll("./testing")
   216  		if err != nil {
   217  			t.Fatal(err.Error())
   218  		}
   219  		t.Fatal(err.Error())
   220  	}
   221  
   222  	expectedRFMLPath = filepath.Join(testDefaultSpecFolder, "Unnamed Test (1).rfml")
   223  	_, err = os.Stat(expectedRFMLPath)
   224  	if err != nil {
   225  		t.Error(err.Error())
   226  	}
   227  
   228  	err = os.RemoveAll("./testing")
   229  	if err != nil {
   230  		t.Fatal(err.Error())
   231  	}
   232  }
   233  
   234  type testRfmlAPI struct {
   235  	testIDs          []rainforest.TestIDPair
   236  	tests            []rainforest.RFTest
   237  	handleUpdateTest func(*rainforest.RFTest)
   238  }
   239  
   240  func (t *testRfmlAPI) GetTestIDs() ([]rainforest.TestIDPair, error) {
   241  	return t.testIDs, nil
   242  }
   243  
   244  func (t *testRfmlAPI) GetTest(testID int) (*rainforest.RFTest, error) {
   245  	for _, test := range t.tests {
   246  		if test.TestID == testID {
   247  			return &test, nil
   248  		}
   249  	}
   250  	return nil, errors.New("Test ID not found")
   251  }
   252  
   253  func (t *testRfmlAPI) GetTests(*rainforest.RFTestFilters) ([]rainforest.RFTest, error) {
   254  	return t.tests, nil
   255  }
   256  
   257  func (t *testRfmlAPI) ClientToken() string {
   258  	return "abc123"
   259  }
   260  
   261  func (t *testRfmlAPI) CreateTest(_ *rainforest.RFTest) error {
   262  	// implement when needed
   263  	return errStub
   264  }
   265  
   266  func (t *testRfmlAPI) UpdateTest(test *rainforest.RFTest) error {
   267  	t.handleUpdateTest(test)
   268  	return nil
   269  }
   270  
   271  func (t *testRfmlAPI) ParseEmbeddedFiles(_ *rainforest.RFTest) error {
   272  	// implement when needed
   273  	return errStub
   274  }
   275  
   276  func createTestFolder(testFolderPath string) error {
   277  	absTestFolderPath, err := filepath.Abs(testFolderPath)
   278  	if err != nil {
   279  		return err
   280  	}
   281  
   282  	dirStat, err := os.Stat(absTestFolderPath)
   283  	if os.IsNotExist(err) {
   284  		os.MkdirAll(absTestFolderPath, os.ModePerm)
   285  	} else if err != nil {
   286  		return err
   287  	} else if !dirStat.IsDir() {
   288  		return fmt.Errorf("Test folder path is not a directory: %v", absTestFolderPath)
   289  	}
   290  
   291  	return nil
   292  }
   293  
   294  func cleanUpTestFolder(testFolderPath string) error {
   295  	_, err := os.Stat(testFolderPath)
   296  
   297  	if err != nil && os.IsNotExist(err) {
   298  		return err
   299  	}
   300  
   301  	err = os.RemoveAll(testFolderPath)
   302  	if err != nil {
   303  		return err
   304  	}
   305  
   306  	return nil
   307  }
   308  
   309  func TestUploadRFML(t *testing.T) {
   310  	context := new(fakeContext)
   311  	testAPI := new(testRfmlAPI)
   312  	testDefaultSpecFolder := "testing/" + defaultSpecFolder
   313  
   314  	defer func() {
   315  		err := cleanUpTestFolder("testing")
   316  		if err != nil {
   317  			t.Fatal(err.Error())
   318  		}
   319  	}()
   320  
   321  	context.mappings = map[string]interface{}{
   322  		"test-folder": testDefaultSpecFolder,
   323  	}
   324  
   325  	testID := 666
   326  	rfmlID := "unique_rfml_id"
   327  	title := "a very descriptive title"
   328  	var featureID rainforest.FeatureIDInt = 777
   329  
   330  	err := createTestFolder(testDefaultSpecFolder)
   331  	if err != nil {
   332  		t.Fatal(err.Error())
   333  	}
   334  
   335  	testPath := filepath.Join(testDefaultSpecFolder, "valid_test.rfml")
   336  
   337  	testAPI.testIDs = []rainforest.TestIDPair{{ID: testID, RFMLID: rfmlID}}
   338  
   339  	// basic test
   340  	testAPI.handleUpdateTest = func(rfTest *rainforest.RFTest) {
   341  		testCases := []struct {
   342  			fieldName string
   343  			expected  interface{}
   344  			got       interface{}
   345  		}{
   346  			{"test ID", testID, rfTest.TestID},
   347  			{"RFML ID", rfmlID, rfTest.RFMLID},
   348  			{"title", title, rfTest.Title},
   349  			{"feature ID", featureID, rfTest.FeatureID},
   350  			{"disabled state", "enabled", rfTest.State},
   351  		}
   352  
   353  		for _, testCase := range testCases {
   354  			if testCase.got != testCase.expected {
   355  				t.Errorf("Incorrect value for %v. Expected %v, Got %v", testCase.fieldName, testCase.expected, testCase.got)
   356  			}
   357  		}
   358  	}
   359  
   360  	testContents := fmt.Sprintf(`#! %v
   361  # title: %v
   362  # feature_id: %v
   363  `, rfmlID, title, featureID)
   364  
   365  	err = ioutil.WriteFile(testPath, []byte(testContents), os.ModePerm)
   366  	if err != nil {
   367  		t.Fatal(err.Error())
   368  	}
   369  
   370  	err = uploadRFML(context, testAPI)
   371  	if err != nil {
   372  		t.Fatal(err.Error())
   373  	}
   374  
   375  	// state is specified
   376  	testAPI.handleUpdateTest = func(rfTest *rainforest.RFTest) {
   377  		if rfTest.State != "disabled" {
   378  			t.Errorf("Incorrect value for state. Expected \"disabled\", Got %v", rfTest.State)
   379  		}
   380  	}
   381  
   382  	testContents = fmt.Sprintf(`#! %v
   383  # title: %v
   384  # state: disabled
   385  `, rfmlID, title)
   386  
   387  	err = ioutil.WriteFile(testPath, []byte(testContents), os.ModePerm)
   388  	if err != nil {
   389  		t.Fatal(err.Error())
   390  	}
   391  
   392  	err = uploadRFML(context, testAPI)
   393  	if err != nil {
   394  		t.Fatal(err.Error())
   395  	}
   396  }
   397  
   398  func TestDeleteRFML(t *testing.T) {
   399  	// Test error in parsing file
   400  	dir, err := ioutil.TempDir("", "")
   401  	if err != nil {
   402  		t.Fatal(err.Error())
   403  	}
   404  	defer os.RemoveAll(dir)
   405  
   406  	rfmlFilePath := filepath.Join(dir, "testing.rfml")
   407  	fileContents := `#! testing
   408  # title: hello
   409  # site_id: a_string
   410  `
   411  	err = ioutil.WriteFile(rfmlFilePath, []byte(fileContents), 0666)
   412  	if err != nil {
   413  		t.Fatal(err.Error())
   414  	}
   415  
   416  	dummyMappings := map[string]interface{}{}
   417  	args := cli.Args{rfmlFilePath}
   418  	ctx := newFakeContext(dummyMappings, args)
   419  
   420  	err = deleteRFML(ctx)
   421  	if err == nil {
   422  		t.Fatal("Expected parse error but received no error.")
   423  	}
   424  
   425  	if _, ok := err.(*cli.ExitError); !ok {
   426  		t.Fatalf("Unexpected error type: %v", reflect.TypeOf(err))
   427  	}
   428  
   429  	errMsg := err.Error()
   430  	if !strings.Contains(errMsg, rfmlFilePath) {
   431  		t.Errorf("Expected error to contain file path \"%v\". Got:\n%v", rfmlFilePath, errMsg)
   432  	}
   433  }
   434  
   435  func TestDownloadRFML(t *testing.T) {
   436  	context := new(fakeContext)
   437  	testAPI := new(testRfmlAPI)
   438  	testDefaultSpecFolder := "testing/" + defaultSpecFolder
   439  
   440  	defer func() {
   441  		err := cleanUpTestFolder("testing")
   442  		if err != nil {
   443  			t.Fatal(err.Error())
   444  		}
   445  	}()
   446  
   447  	testID := 112233
   448  	rfmlID := "rfml_test_id"
   449  	title := "My Test Title"
   450  	featureID := 665544
   451  
   452  	rfTest := rainforest.RFTest{
   453  		TestID:    testID,
   454  		RFMLID:    rfmlID,
   455  		Title:     title,
   456  		FeatureID: rainforest.FeatureIDInt(featureID),
   457  		State:     "enabled",
   458  	}
   459  
   460  	testAPI.testIDs = []rainforest.TestIDPair{{ID: testID, RFMLID: rfmlID}}
   461  	testAPI.tests = []rainforest.RFTest{rfTest}
   462  
   463  	paddedTestID := fmt.Sprintf("%010d", testID)
   464  	sanitizedTitle := "my_test_title"
   465  	expectedFileName := fmt.Sprintf("%v_%v.rfml", paddedTestID, sanitizedTitle)
   466  	expectedRFMLPath := filepath.Join(testDefaultSpecFolder, expectedFileName)
   467  
   468  	context.mappings = map[string]interface{}{
   469  		"test-folder": testDefaultSpecFolder,
   470  	}
   471  
   472  	// basic test
   473  	err := downloadRFML(context, testAPI)
   474  	if err != nil {
   475  		t.Fatal(err.Error())
   476  	}
   477  
   478  	fileInfo, err := os.Stat(expectedRFMLPath)
   479  	if os.IsNotExist(err) {
   480  		t.Fatalf("Expected RFML test does not exist: %v", expectedRFMLPath)
   481  	}
   482  
   483  	if fileInfo.Name() != expectedFileName {
   484  		t.Errorf("Expected RFML file path %v, got %v", expectedRFMLPath, fileInfo.Name())
   485  	} else if err != nil {
   486  		t.Fatalf(err.Error())
   487  	}
   488  
   489  	var contents []byte
   490  	contents, err = ioutil.ReadFile(expectedRFMLPath)
   491  	if err != nil {
   492  		t.Fatalf(err.Error())
   493  	}
   494  
   495  	rfmlText := string(contents)
   496  
   497  	if !strings.Contains(rfmlText, title) {
   498  		t.Errorf("Expected title \"%v\" to appear in RFML test", title)
   499  	}
   500  
   501  	if !strings.Contains(rfmlText, rfmlID) {
   502  		t.Errorf("Expected RFML ID \"%v\" to appear in RFML test", rfmlID)
   503  	}
   504  
   505  	if !strings.Contains(rfmlText, strconv.Itoa(featureID)) {
   506  		t.Errorf("Expected Feature ID \"%v\" to appear in RFML test", featureID)
   507  	}
   508  
   509  	if strings.Contains(rfmlText, "state") {
   510  		t.Errorf("Did not expect state field in RFML test. Got %v", rfmlText)
   511  	}
   512  
   513  	// Test is disabled
   514  	rfTest.State = "disabled"
   515  	testAPI.tests = []rainforest.RFTest{rfTest}
   516  
   517  	err = downloadRFML(context, testAPI)
   518  	if err != nil {
   519  		t.Fatal(err.Error())
   520  	}
   521  
   522  	fileInfo, err = os.Stat(expectedRFMLPath)
   523  	if os.IsNotExist(err) {
   524  		t.Fatalf("Expected RFML test does not exist: %v", expectedRFMLPath)
   525  	}
   526  
   527  	contents, err = ioutil.ReadFile(expectedRFMLPath)
   528  	if err != nil {
   529  		t.Fatalf(err.Error())
   530  	}
   531  	rfmlText = string(contents)
   532  
   533  	if !strings.Contains(rfmlText, "# state: disabled") {
   534  		t.Errorf("Expected RFML test state to read disabled. Output: %v", rfmlText)
   535  	}
   536  }
   537  
   538  func TestSanitizeTestTitle(t *testing.T) {
   539  	// Test that it replaces non-alphanumeric characters with underscores
   540  	illegalTitle := `Foo\123|*&bar `
   541  	sanitizedTitle := sanitizeTestTitle(illegalTitle)
   542  	expectedSanitizedTitle := "foo_123_bar"
   543  
   544  	if sanitizedTitle != expectedSanitizedTitle {
   545  		t.Errorf("Expected sanitized title to be %v, got %v", expectedSanitizedTitle, sanitizedTitle)
   546  	}
   547  
   548  	// Test that it truncates strings with more than 30 characters
   549  	longTitle := strings.Repeat("abc", 11) // 33 characters
   550  	sanitizedTitle = sanitizeTestTitle(longTitle)
   551  	expectedSanitizedTitle = strings.Repeat("abc", 10) // 30 characters
   552  
   553  	if sanitizedTitle != expectedSanitizedTitle {
   554  		t.Errorf("Expected sanitized title to be %v, got %v", expectedSanitizedTitle, sanitizedTitle)
   555  	}
   556  }
   557  
   558  func TestValidateEmbedded(t *testing.T) {
   559  	t1 := rainforest.RFTest{
   560  		TestID:   1,
   561  		RFMLID:   "1",
   562  		RFMLPath: "./1.rfml",
   563  	}
   564  	t2 := rainforest.RFTest{
   565  		TestID:   2,
   566  		RFMLID:   "2",
   567  		RFMLPath: "./2.rfml",
   568  		Steps: []interface{}{
   569  			rainforest.RFEmbeddedTest{
   570  				RFMLID:   "1",
   571  				Redirect: true,
   572  			},
   573  		},
   574  	}
   575  
   576  	testAPI := new(testRfmlAPI)
   577  	testAPI.testIDs = []rainforest.TestIDPair{
   578  		{ID: t1.TestID, RFMLID: t1.RFMLID},
   579  		{ID: t2.TestID, RFMLID: t2.RFMLID},
   580  	}
   581  
   582  	tests := []*rainforest.RFTest{&t2}
   583  	var err error
   584  
   585  	// With API access, the validation should be fine
   586  	err = validateRFMLFiles(tests, false, testAPI)
   587  	if err != nil {
   588  		t.Error("Non-local validation failed:", err)
   589  	}
   590  
   591  	// With local-only and all embedded tests available, the validation should
   592  	// be fine
   593  	allTests := append(tests, &t1)
   594  	err = validateRFMLFiles(allTests, true, testAPI)
   595  	if err != nil {
   596  		t.Error("Local-only validation with all tests failed:", err)
   597  	}
   598  
   599  	// With local-only and embedded tests not available locally, the validation
   600  	// should fail
   601  	err = validateRFMLFiles(tests, true, testAPI)
   602  	if err == nil {
   603  		t.Error("Local-only validation should have failed but didn't")
   604  	} else if err != errValidation {
   605  		t.Error("Unexpected error for local-only validation:", err)
   606  	}
   607  }
   608  
   609  func TestReadRFMLFiles(t *testing.T) {
   610  	dir := setupTestRFMLDir()
   611  	defer os.RemoveAll(dir)
   612  
   613  	var testCases = []struct {
   614  		files     []string
   615  		wantFiles []string
   616  		wantError bool
   617  	}{
   618  		{
   619  			files:     []string{"a/a1.rfml"},
   620  			wantFiles: []string{"a/a1.rfml"},
   621  		},
   622  		{
   623  			files:     []string{"a"},
   624  			wantFiles: []string{"a/a1.rfml", "a/a2.rfml", "a/a3.rfml"},
   625  		},
   626  		{
   627  			files:     []string{"a", "a/a1.rfml", "b/b1.rfml"},
   628  			wantFiles: []string{"a/a1.rfml", "a/a2.rfml", "a/a3.rfml", "b/b1.rfml"},
   629  		},
   630  		{
   631  			files: []string{""},
   632  			wantFiles: []string{
   633  				"a/a1.rfml",
   634  				"a/a2.rfml",
   635  				"a/a3.rfml",
   636  				"b/b1.rfml",
   637  				"b/a/b2.rfml",
   638  				"b/b/b3.rfml",
   639  				"b/b/b4.rfml",
   640  				"b/b/b5.rfml",
   641  				"standalone.rfml",
   642  			},
   643  		},
   644  		{
   645  			files:     []string{"c"},
   646  			wantError: true,
   647  		},
   648  		{
   649  			// We want to just ignore bogus files, rather than error, so that
   650  			// shell expansions like foo/ab* still work
   651  			files:     []string{"a/a1.rfml", "a/bogus.rf"},
   652  			wantFiles: []string{"a/a1.rfml"},
   653  		},
   654  	}
   655  
   656  	for _, tc := range testCases {
   657  		fullFiles := []string{}
   658  		for _, f := range tc.files {
   659  			fullFiles = append(fullFiles, filepath.Join(dir, f))
   660  		}
   661  
   662  		pTests, err := readRFMLFiles(fullFiles)
   663  		if err != nil && !tc.wantError {
   664  			t.Error(err)
   665  			continue
   666  		} else if err == nil && tc.wantError {
   667  			t.Errorf("Expected error when reading %v", tc.files)
   668  			continue
   669  		}
   670  
   671  		gotFiles := []string{}
   672  		for _, p := range pTests {
   673  			gotFiles = append(gotFiles, p.RFMLPath)
   674  		}
   675  		sort.Strings(gotFiles)
   676  
   677  		wantFiles := []string{}
   678  		for _, f := range tc.wantFiles {
   679  			wantFiles = append(wantFiles, filepath.Join(dir, f))
   680  		}
   681  		sort.Strings(wantFiles)
   682  
   683  		if !reflect.DeepEqual(wantFiles, gotFiles) {
   684  			t.Errorf("Unexpected files returned (want: %v, got: %v)", wantFiles, gotFiles)
   685  		}
   686  	}
   687  }
   688  
   689  func TestReadRFMLFile(t *testing.T) {
   690  	// Test error in parsing file
   691  	dir, err := ioutil.TempDir("", "")
   692  	if err != nil {
   693  		t.Fatal(err.Error())
   694  	}
   695  	defer os.RemoveAll(dir)
   696  
   697  	rfmlFilePath := filepath.Join(dir, "testing.rfml")
   698  	fileContents := `#! testing
   699  # title: hello
   700  # site_id: a_string
   701  `
   702  	err = ioutil.WriteFile(rfmlFilePath, []byte(fileContents), 0666)
   703  	if err != nil {
   704  		t.Fatal(err.Error())
   705  	}
   706  
   707  	_, err = readRFMLFile(rfmlFilePath)
   708  	if err == nil {
   709  		t.Fatal("Expected parse error but received no error.")
   710  	}
   711  
   712  	errMsg := err.Error()
   713  	if !strings.Contains(errMsg, rfmlFilePath) {
   714  		t.Errorf("Expected error to contain file path \"%v\". Got:\n%v", rfmlFilePath, errMsg)
   715  	}
   716  }
   717  
   718  func setupTestRFMLDir() string {
   719  	var rfmlTests = []struct {
   720  		path    string
   721  		content *rainforest.RFTest
   722  	}{
   723  		{
   724  			path: "a/a1.rfml",
   725  			content: &rainforest.RFTest{
   726  				RFMLID:  "a1",
   727  				Tags:    []string{"foo", "baz"},
   728  				Execute: true,
   729  				Steps: []interface{}{
   730  					rainforest.RFEmbeddedTest{
   731  						RFMLID: "b4",
   732  					},
   733  				},
   734  			},
   735  		},
   736  		{
   737  			path: "a/a2.rfml",
   738  			content: &rainforest.RFTest{
   739  				RFMLID:  "a2",
   740  				Execute: true,
   741  			},
   742  		},
   743  		{
   744  			path: "a/a3.rfml",
   745  			content: &rainforest.RFTest{
   746  				RFMLID:  "a3",
   747  				Tags:    []string{"bar"},
   748  				Execute: true,
   749  			},
   750  		},
   751  		{
   752  			path: "b/b1.rfml",
   753  			content: &rainforest.RFTest{
   754  				RFMLID:  "b1",
   755  				Execute: true,
   756  			},
   757  		},
   758  		{
   759  			path: "b/a/b2.rfml",
   760  			content: &rainforest.RFTest{
   761  				RFMLID:  "b2",
   762  				Execute: true,
   763  			},
   764  		},
   765  		{
   766  			path: "b/b/b3.rfml",
   767  			content: &rainforest.RFTest{
   768  				RFMLID:  "b3",
   769  				Tags:    []string{"foo"},
   770  				Execute: false,
   771  			},
   772  		},
   773  		{
   774  			path: "b/b/b4.rfml",
   775  			content: &rainforest.RFTest{
   776  				RFMLID:  "b4",
   777  				Tags:    []string{},
   778  				Execute: true,
   779  				Steps: []interface{}{
   780  					rainforest.RFEmbeddedTest{
   781  						RFMLID: "b5",
   782  					},
   783  				},
   784  			},
   785  		},
   786  		{
   787  			path: "b/b/b5.rfml",
   788  			content: &rainforest.RFTest{
   789  				RFMLID:  "b5",
   790  				Execute: true,
   791  			},
   792  		},
   793  		{
   794  			path: "standalone.rfml",
   795  			content: &rainforest.RFTest{
   796  				RFMLID:  "standalone",
   797  				Execute: true,
   798  			},
   799  		},
   800  		{
   801  			path: "a/bogus.rf",
   802  			content: &rainforest.RFTest{
   803  				RFMLID:  "bogus",
   804  				Execute: true,
   805  			},
   806  		},
   807  	}
   808  
   809  	dir, err := ioutil.TempDir("", "")
   810  	if err != nil {
   811  		log.Fatal(err)
   812  	}
   813  
   814  	for _, subdir := range []string{"a", "b/a", "b/b"} {
   815  		err := os.MkdirAll(filepath.Join(dir, subdir), os.ModePerm)
   816  		if err != nil {
   817  			log.Fatal(err)
   818  		}
   819  	}
   820  
   821  	for i, test := range rfmlTests {
   822  		test.content.TestID = i
   823  		test.content.Title = test.content.RFMLID
   824  		p := filepath.Join(dir, test.path)
   825  		test.content.RFMLPath = p
   826  		f, err := os.Create(p)
   827  		if err != nil {
   828  			log.Fatal(err)
   829  		}
   830  		w := rainforest.NewRFMLWriter(f)
   831  		err = w.WriteRFMLTest(test.content)
   832  		if err != nil {
   833  			log.Fatal(err)
   834  		}
   835  	}
   836  
   837  	return dir
   838  }