diff --git a/internal/app/action/action.go b/internal/app/action/action.go
index c3e97ae..edb738f 100644
--- a/internal/app/action/action.go
+++ b/internal/app/action/action.go
@@ -345,6 +345,9 @@ func (a *Action) renderResultsText(w http.ResponseWriter, valuesStr []string) er
}
func (a *Action) renderResultsTable(w http.ResponseWriter, valuesMap []map[string]any) error {
+ if len(valuesMap) == 0 {
+ return nil
+ }
firstRow := valuesMap[0]
keys := make([]string, 0, len(firstRow))
for k := range firstRow {
diff --git a/internal/app/tests/appaction_test.go b/internal/app/tests/appaction_test.go
index bac6d6f..423bb6e 100644
--- a/internal/app/tests/appaction_test.go
+++ b/internal/app/tests/appaction_test.go
@@ -2,7 +2,9 @@ package app_test
import (
"net/http/httptest"
+ "net/url"
"path"
+ "strings"
"testing"
"github.com/claceio/clace/internal/app"
@@ -95,7 +97,7 @@ func TestParamErrors(t *testing.T) {
def handler(dry_run, args):
return ace.result(status="done", values=["a", "b"], param_errors={"param1": "param1error", "param3": "param3error"})
-app = ace.app("testApp",
+app = ace.app("testApp",
actions=[ace.action("testAction", "/", handler)])
`,
@@ -365,3 +367,237 @@ app = ace.app("testApp",
`, response.Body.String())
}
+
+func TestParamPost(t *testing.T) {
+ logger := testutil.TestLogger()
+ fileData := map[string]string{
+ "app.star": `
+def handler(dry_run, args):
+ return ace.result(status="done", values=[{"c1": args.param1, "c2": args.param2, "c3": args.param3}])
+
+app = ace.app("testApp",
+ actions=[ace.action("testAction", "/", handler, report=ace.TABLE)])
+
+ `,
+ "params.star": `param("param1", description="param1 description", type=STRING, default="myvalue")
+param("param2", description="param2 description", type=BOOLEAN, default=False)
+param("param3", description="param3 description", type=INT, default=10)`,
+ }
+ a, _, err := CreateTestApp(logger, fileData)
+ if err != nil {
+ t.Fatalf("Error %s", err)
+ }
+
+ request := httptest.NewRequest("GET", "/test/", nil)
+ response := httptest.NewRecorder()
+ a.ServeHTTP(response, request)
+
+ testutil.AssertEqualsInt(t, "code", 200, response.Code)
+ testutil.AssertStringContains(t, response.Body.String(), "
testAction")
+ testutil.AssertStringContains(t, response.Body.String(), `id="param_param1"`)
+
+ values := url.Values{
+ "param1": {"abc"},
+ "param2": {"true"},
+ "param3": {"20"},
+ }
+
+ request = httptest.NewRequest("POST", "/test", strings.NewReader(values.Encode()))
+ request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+
+ response = httptest.NewRecorder()
+ a.ServeHTTP(response, request)
+ testutil.AssertEqualsInt(t, "code", 200, response.Code)
+ testutil.AssertStringMatch(t, "match response", `
+
+ done
+
+
+
+
Report
+
+
+
+
+ c1 |
+ c2 |
+ c3 |
+
+
+
+
+ abc |
+ true |
+ 20 |
+
+
+
+
`, response.Body.String())
+}
+
+func TestCustomReport(t *testing.T) {
+ logger := testutil.TestLogger()
+ fileData := map[string]string{
+ "app.star": `
+def handler(dry_run, args):
+ return ace.result(status="done", values=[{"a": 1, "b": "abc"}])
+
+app = ace.app("testApp",
+ actions=[ace.action("testAction", "/", handler, report="custom")])
+
+ `,
+ "params.star": `param("param1", description="param1 description", type=STRING, default="myvalue")`,
+ "myfile.go.html": `{{block "custom" .}} customdata {{end}}`,
+ }
+ a, _, err := CreateTestApp(logger, fileData)
+ if err != nil {
+ t.Fatalf("Error %s", err)
+ }
+
+ request := httptest.NewRequest("GET", "/test/", nil)
+ response := httptest.NewRecorder()
+ a.ServeHTTP(response, request)
+
+ testutil.AssertEqualsInt(t, "code", 200, response.Code)
+ testutil.AssertStringContains(t, response.Body.String(), "testAction")
+ testutil.AssertStringContains(t, response.Body.String(), `id="param_param1"`)
+
+ request = httptest.NewRequest("POST", "/test", nil)
+ response = httptest.NewRecorder()
+ a.ServeHTTP(response, request)
+ testutil.AssertEqualsInt(t, "code", 200, response.Code)
+ testutil.AssertStringMatch(t, "match response", `
+ done
+
+ customdata
`, response.Body.String())
+
+ // Unset the template
+ fileData["myfile.go.html"] = ``
+ a, _, err = CreateTestApp(logger, fileData)
+ if err != nil {
+ t.Fatalf("Error %s", err)
+ }
+ request = httptest.NewRequest("GET", "/test/", nil)
+ response = httptest.NewRecorder()
+ a.ServeHTTP(response, request)
+
+ testutil.AssertEqualsInt(t, "code", 200, response.Code)
+ testutil.AssertStringContains(t, response.Body.String(), "testAction")
+ testutil.AssertStringContains(t, response.Body.String(), `id="param_param1"`)
+
+ request = httptest.NewRequest("POST", "/test", nil)
+ response = httptest.NewRecorder()
+ a.ServeHTTP(response, request)
+ testutil.AssertEqualsInt(t, "code", 200, response.Code)
+ testutil.AssertStringMatch(t, "match response", ` done
+
html/template: "custom" is undefined`, response.Body.String())
+}
+
+func TestActionError(t *testing.T) {
+ logger := testutil.TestLogger()
+ fileData := map[string]string{
+ "app.star": `
+def handler(dry_run, args):
+ if args.param1 == "error":
+ return "errormessage"
+ 10/args.param3
+ return ace.result(status="done", values=[{"c1": args.param1, "c2": args.param2, "c3": args.param3}])
+
+app = ace.app("testApp",
+ actions=[ace.action("testAction", "/", handler, report=ace.TABLE)])
+
+ `,
+ "params.star": `param("param1", description="param1 description", type=STRING, default="myvalue")
+param("param2", description="param2 description", type=BOOLEAN, default=False)
+param("param3", description="param3 description", type=INT, default=10)`,
+ }
+ a, _, err := CreateTestApp(logger, fileData)
+ if err != nil {
+ t.Fatalf("Error %s", err)
+ }
+
+ request := httptest.NewRequest("GET", "/test/", nil)
+ response := httptest.NewRecorder()
+ a.ServeHTTP(response, request)
+
+ testutil.AssertEqualsInt(t, "code", 200, response.Code)
+ testutil.AssertStringContains(t, response.Body.String(), "testAction")
+ testutil.AssertStringContains(t, response.Body.String(), `id="param_param1"`)
+
+ values := url.Values{
+ "param1": {"error"},
+ "param2": {"true"},
+ "param3": {"20"},
+ }
+
+ request = httptest.NewRequest("POST", "/test", strings.NewReader(values.Encode()))
+ request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+
+ response = httptest.NewRecorder()
+ a.ServeHTTP(response, request)
+ testutil.AssertEqualsInt(t, "code", 200, response.Code)
+ testutil.AssertStringMatch(t, "match response", `
+ "errormessage"
+
`, response.Body.String())
+
+ values = url.Values{
+ "param1": {"p1val"},
+ "param2": {"true"},
+ "param3": {"0"},
+ }
+
+ request = httptest.NewRequest("POST", "/test", strings.NewReader(values.Encode()))
+ request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+
+ response = httptest.NewRecorder()
+ a.ServeHTTP(response, request)
+ testutil.AssertEqualsInt(t, "code", 500, response.Code)
+ testutil.AssertStringMatch(t, "response", `floating-point division by zero`, response.Body.String())
+
+ values = url.Values{
+ "param1": {"p1val"},
+ "param2": {"true"},
+ "param3": {"50"},
+ }
+
+ request = httptest.NewRequest("POST", "/test", strings.NewReader(values.Encode()))
+ request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+
+ response = httptest.NewRecorder()
+ a.ServeHTTP(response, request)
+ testutil.AssertEqualsInt(t, "code", 200, response.Code)
+ testutil.AssertStringMatch(t, "response", `
+ done
+
+
+
+
Report
+
+
+
+
+
+ c1 |
+
+ c2 |
+
+ c3 |
+
+
+
+
+
+
+
+ p1val |
+
+ true |
+
+ 50 |
+
+
+
+
+
+
`, response.Body.String())
+}