diff --git a/internal/engine/common.go b/internal/engine/common.go index e5d14080..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 } diff --git a/internal/engine/scenario.go b/internal/engine/scenario.go index ba51708d..cd05e49f 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,37 @@ 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 +} + +// 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://") { + 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,13 +90,9 @@ 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 := os.ReadFile(path) + source, err := resolveMarkdownSource(path) if err != nil { - panic(err) + return nil, err } // Load environment variables @@ -76,7 +105,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 +198,6 @@ func CreateScenarioFromMarkdown( }, nil } - // Convert a scenario into a shell script func (s *Scenario) ToShellScript() string { var script strings.Builder 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()) + } + }) +}