Skip to content

Commit

Permalink
fix(scenarios): Fix the regex that locates variables to override (#202)
Browse files Browse the repository at this point in the history
This PR updates the regex used to find exported variables in a scenario
that have more complex declarations (I.E. variables that get values from
sub-shells, variables that are defined on the same line as another,
etc). This PR fixes #199 and tests have been added to ensure that this
issue doesn't arise again.
  • Loading branch information
vmarcella authored Jun 13, 2024
1 parent 2f1f50d commit a3f6cb8
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 3 deletions.
7 changes: 4 additions & 3 deletions internal/engine/scenario.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func downloadScenarioMarkdown(url string) ([]byte, error) {
return body, nil
}

// Given either a local or remote path to a markdown file, resolve the path to
// Given either a local or remote path to a markdown file, resolve the path to
// the markdown file and return the contents of the file.
func resolveMarkdownSource(path string) ([]byte, error) {
if strings.HasPrefix(path, "https://") || strings.HasPrefix(path, "http://") {
Expand Down Expand Up @@ -128,8 +128,9 @@ func CreateScenarioFromMarkdown(

varsToExport := lib.CopyMap(environmentVariableOverrides)
for key, value := range environmentVariableOverrides {
environmentVariables[key] = value
logging.GlobalLogger.Debugf("Attempting to override %s with %s", key, value)
exportRegex := regexp.MustCompile(fmt.Sprintf(`export %s=["']?([a-z-A-Z0-9_]+)["']?`, key))
exportRegex := regexp.MustCompile(fmt.Sprintf(`(?m)export %s\s*=\s*(.*?)(;|&&|$)`, key))

for index, codeBlock := range codeBlocks {
matches := exportRegex.FindAllStringSubmatch(codeBlock.Content, -1)
Expand All @@ -150,7 +151,7 @@ func CreateScenarioFromMarkdown(
oldValue := match[1]

// Replace the old export with the new export statement
newLine := strings.Replace(oldLine, oldValue, value, 1)
newLine := strings.Replace(oldLine, oldValue, value+" ", 1)
logging.GlobalLogger.Debugf("Replacing '%s' with '%s'", oldLine, newLine)

// Update the code block with the new export statement
Expand Down
98 changes: 98 additions & 0 deletions internal/engine/scenario_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"os"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

// Mock HTTP server for testing downloading markdown from URL
Expand Down Expand Up @@ -76,3 +78,99 @@ func TestResolveMarkdownSource(t *testing.T) {
}
})
}

func TestVariableOverrides(t *testing.T) {
// Test overriding environment variables
t.Run("Override a standard variable declaration", func(t *testing.T) {
scenario, err := CreateScenarioFromMarkdown(
"../../scenarios/testing/variables.md",
[]string{"bash"},
map[string]string{
"MY_VAR": "my_value",
},
)

assert.NoError(t, err)
assert.Equal(t, "my_value", scenario.Environment["MY_VAR"])
assert.Contains(t, scenario.Steps[0].CodeBlocks[0].Content, "export MY_VAR=my_value")
})

t.Run(
"Override a variable that is declared on the same line as another variable, separated by &&",
func(t *testing.T) {
scenario, err := CreateScenarioFromMarkdown(
"../../scenarios/testing/variables.md",
[]string{"bash"},
map[string]string{
"NEXT_VAR": "next_value",
},
)

assert.NoError(t, err)
assert.Equal(t, "next_value", scenario.Environment["NEXT_VAR"])
assert.Contains(
t,
scenario.Steps[1].CodeBlocks[0].Content,
`export NEXT_VAR=next_value && export OTHER_VAR="Hello, World!"`,
)
},
)

t.Run(
"Override a variable that is declared on the same line as another variable, separated by ;",
func(t *testing.T) {
scenario, err := CreateScenarioFromMarkdown(
"../../scenarios/testing/variables.md",
[]string{"bash"},
map[string]string{
"THIS_VAR": "this_value",
"THAT_VAR": "that_value",
},
)

assert.NoError(t, err)
assert.Equal(t, "this_value", scenario.Environment["THIS_VAR"])
assert.Equal(t, "that_value", scenario.Environment["THAT_VAR"])
assert.Contains(
t,
scenario.Steps[2].CodeBlocks[0].Content,
`export THIS_VAR=this_value ; export THAT_VAR=that_value`,
)
})

t.Run("Override a variable that has a subshell command as it's value", func(t *testing.T) {
scenario, err := CreateScenarioFromMarkdown(
"../../scenarios/testing/variables.md",
[]string{"bash"},
map[string]string{
"SUBSHELL_VARIABLE": "subshell_value",
},
)

assert.NoError(t, err)
assert.Equal(t, "subshell_value", scenario.Environment["SUBSHELL_VARIABLE"])
assert.Contains(
t,
scenario.Steps[3].CodeBlocks[0].Content,
`export SUBSHELL_VARIABLE=subshell_value`,
)
})

t.Run("Override a variable that references another variable", func(t *testing.T) {
scenario, err := CreateScenarioFromMarkdown(
"../../scenarios/testing/variables.md",
[]string{"bash"},
map[string]string{
"VAR2": "var2_value",
},
)

assert.NoError(t, err)
assert.Equal(t, "var2_value", scenario.Environment["VAR2"])
assert.Contains(
t,
scenario.Steps[4].CodeBlocks[0].Content,
`export VAR2=var2_value`,
)
})
}
37 changes: 37 additions & 0 deletions scenarios/testing/variables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Variable declaration and usage

## Simple declaration

```bash
export MY_VAR="Hello, World!"
echo $MY_VAR
```

## Double variable declaration

```bash
export NEXT_VAR="Hello" && export OTHER_VAR="Hello, World!"
echo $NEXT_VAR
```

## Double declaration with semicolon

```bash
export THIS_VAR="Hello"; export THAT_VAR="Hello, World!"
echo $OTHER_VAR
```

## Declaration with subshell value

```bash
export SUBSHELL_VARIABLE=$(echo "Hello, World!")
echo $SUBSHELL_VARIABLE
```

## Declaration with other variable in value

```bash
export VAR1="Hello"
export VAR2="$VAR1, World!"
echo $VAR2
```

0 comments on commit a3f6cb8

Please sign in to comment.