From b706844e079420a18303303ccfbeaf917ff6e697 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Thu, 28 Mar 2024 11:55:08 -0700 Subject: [PATCH 1/4] [add] support for remote url execution. --- internal/engine/common.go | 2 ++ internal/engine/scenario.go | 46 +++++++++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/internal/engine/common.go b/internal/engine/common.go index e5d14080..2dd87c67 100644 --- a/internal/engine/common.go +++ b/internal/engine/common.go @@ -69,3 +69,5 @@ func compareCommandOutputs( return nil } + + diff --git a/internal/engine/scenario.go b/internal/engine/scenario.go index ba51708d..8df4540f 100644 --- a/internal/engine/scenario.go +++ b/internal/engine/scenario.go @@ -2,6 +2,8 @@ package engine import ( "fmt" + "io" + "net/http" "os" "path/filepath" "regexp" @@ -32,7 +34,7 @@ type Scenario struct { // This organizes the codeblocks into steps that can be executed linearly. func groupCodeBlocksIntoSteps(blocks []parsers.CodeBlock) []Step { var groupedSteps []Step - var headerIndex = make(map[string]int) + headerIndex := make(map[string]int) for _, block := range blocks { if index, ok := headerIndex[block.Header]; ok { @@ -49,6 +51,35 @@ func groupCodeBlocksIntoSteps(blocks []parsers.CodeBlock) []Step { return groupedSteps } +// Download the scenario markdown over http +func downloadScenarioMarkdown(url string) ([]byte, error) { + resp, err := http.Get(url) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return body, nil +} + +func resolveMarkdownSource(path string) ([]byte, error) { + if strings.HasPrefix(path, "https://") { + return downloadScenarioMarkdown(path) + } + + if !fs.FileExists(path) { + return nil, fmt.Errorf("markdown file '%s' does not exist", path) + } + + return os.ReadFile(path) +} + // Creates a scenario object from a given markdown file. languagesToExecute is // used to filter out code blocks that should not be parsed out of the markdown // file. @@ -57,14 +88,11 @@ func CreateScenarioFromMarkdown( languagesToExecute []string, environmentVariableOverrides map[string]string, ) (*Scenario, error) { - if !fs.FileExists(path) { - return nil, fmt.Errorf("markdown file '%s' does not exist", path) - } + source, err := resolveMarkdownSource(path) - source, err := os.ReadFile(path) - if err != nil { - panic(err) - } + if err != nil { + return nil, err + } // Load environment variables markdownINI := strings.TrimSuffix(path, filepath.Ext(path)) + ".ini" @@ -76,7 +104,6 @@ func CreateScenarioFromMarkdown( } else { logging.GlobalLogger.Infof("INI file '%s' exists, loading...", markdownINI) environmentVariables, err = parsers.ParseINIFile(markdownINI) - if err != nil { return nil, err } @@ -170,7 +197,6 @@ func CreateScenarioFromMarkdown( }, nil } - // Convert a scenario into a shell script func (s *Scenario) ToShellScript() string { var script strings.Builder From 69091ac88eb77337a0a68b310e08ec0be4e1af07 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Thu, 28 Mar 2024 12:06:39 -0700 Subject: [PATCH 2/4] [add] unit tests for resolving markdown paths/files. --- internal/engine/scenario.go | 9 ++-- internal/engine/scenario_test.go | 78 ++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 internal/engine/scenario_test.go diff --git a/internal/engine/scenario.go b/internal/engine/scenario.go index 8df4540f..0824c359 100644 --- a/internal/engine/scenario.go +++ b/internal/engine/scenario.go @@ -69,7 +69,7 @@ func downloadScenarioMarkdown(url string) ([]byte, error) { } func resolveMarkdownSource(path string) ([]byte, error) { - if strings.HasPrefix(path, "https://") { + if strings.HasPrefix(path, "https://") || strings.HasPrefix(path, "http://"){ return downloadScenarioMarkdown(path) } @@ -89,10 +89,9 @@ func CreateScenarioFromMarkdown( environmentVariableOverrides map[string]string, ) (*Scenario, error) { source, err := resolveMarkdownSource(path) - - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } // Load environment variables markdownINI := strings.TrimSuffix(path, filepath.Ext(path)) + ".ini" diff --git a/internal/engine/scenario_test.go b/internal/engine/scenario_test.go new file mode 100644 index 00000000..510b240a --- /dev/null +++ b/internal/engine/scenario_test.go @@ -0,0 +1,78 @@ +package engine + +import ( + "fmt" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" +) + +// Mock HTTP server for testing downloading markdown from URL +func mockHTTPServer(content string) *httptest.Server { + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, content) + })) +} + +func TestResolveMarkdownSource(t *testing.T) { + // Test downloading from URL + t.Run("Download markdown from URL", func(t *testing.T) { + content := "Test content from URL" + mockServer := mockHTTPServer(content) + defer mockServer.Close() + + url := mockServer.URL + result, err := resolveMarkdownSource(url) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + expected := []byte(content) + if string(result) != string(expected) { + t.Errorf("Expected content to be %q, got %q", expected, result) + } + }) + + // Test reading from local file + t.Run("Read from a local file", func(t *testing.T) { + content := "Test content from local file" + temporaryFile, err := os.CreateTemp("", "example") + if err != nil { + t.Fatalf("Error creating temporary file: %v", err) + } + defer os.Remove(temporaryFile.Name()) + + if _, err := temporaryFile.Write([]byte(content)); err != nil { + t.Fatalf("Error writing to temporary file: %v", err) + } + if err := temporaryFile.Close(); err != nil { + t.Fatalf("Error closing temporary file: %v", err) + } + + path := temporaryFile.Name() + result, err := resolveMarkdownSource(path) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + expected := []byte(content) + if string(result) != string(expected) { + t.Errorf("Expected content to be %q, got %q", expected, result) + } + }) + + // Test non-existing file + t.Run("Non-existing file", func(t *testing.T) { + nonExistingPath := "non_existing_file.md" + _, err := resolveMarkdownSource(nonExistingPath) + if err == nil { + t.Error("Expected error for non-existing file, but got nil") + } + expectedErrorMsg := fmt.Sprintf("markdown file '%s' does not exist", nonExistingPath) + if !strings.Contains(err.Error(), expectedErrorMsg) { + t.Errorf("Expected error message to contain %q, got %q", expectedErrorMsg, err.Error()) + } + }) +} From 5cf2488e129c10ecfd506111e71f74d166f95c52 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Thu, 28 Mar 2024 12:11:52 -0700 Subject: [PATCH 3/4] [add] documentation. --- internal/engine/scenario.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/engine/scenario.go b/internal/engine/scenario.go index 0824c359..cd05e49f 100644 --- a/internal/engine/scenario.go +++ b/internal/engine/scenario.go @@ -68,8 +68,10 @@ func downloadScenarioMarkdown(url string) ([]byte, error) { return body, nil } +// 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://"){ + if strings.HasPrefix(path, "https://") || strings.HasPrefix(path, "http://") { return downloadScenarioMarkdown(path) } From 5c288a4c9026503bc3f4d6ed2276b86655cec084 Mon Sep 17 00:00:00 2001 From: vmarcella Date: Thu, 28 Mar 2024 12:12:50 -0700 Subject: [PATCH 4/4] [remove] spacing. --- internal/engine/common.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/engine/common.go b/internal/engine/common.go index 2dd87c67..b072c44b 100644 --- a/internal/engine/common.go +++ b/internal/engine/common.go @@ -38,7 +38,6 @@ func compareCommandOutputs( actualOutput, ) results, err := lib.CompareJsonStrings(actualOutput, expectedOutput, expectedSimilarity) - if err != nil { return err } @@ -69,5 +68,3 @@ func compareCommandOutputs( return nil } - -