Merge pull request #1005 from MichaelMure/cli-testing

Michael Muré created

commands: add a helper to generate testing regex for CLI output

Change summary

commands/bug/bug_test.go       | 82 +++++++++++++++++++++++++----------
commands/cmdtest/regex.go      | 62 +++++++++++++++++++++++++++
commands/cmdtest/regex_test.go | 25 ++++++++++
3 files changed, 146 insertions(+), 23 deletions(-)

Detailed changes

commands/bug/bug_test.go 🔗

@@ -1,13 +1,12 @@
 package bugcmd
 
 import (
-	"encoding/json"
 	"testing"
 
 	"github.com/stretchr/testify/require"
 
 	"github.com/MichaelMure/git-bug/commands/bug/testenv"
-	"github.com/MichaelMure/git-bug/commands/cmdjson"
+	. "github.com/MichaelMure/git-bug/commands/cmdtest"
 )
 
 func Test_repairQuery(t *testing.T) {
@@ -47,24 +46,69 @@ func Test_repairQuery(t *testing.T) {
 }
 
 func TestBug_Format(t *testing.T) {
-	const expOrgMode = `^#+TODO: OPEN | CLOSED
-[*] OPEN   [0-9a-f]{7} \[\d\d\d\d-\d\d-\d\d [[:alpha:]]{3} \d\d:\d\d\] John Doe: this is a bug title ::
-[*]{2} Last Edited: \[\d\d\d\d-\d\d-\d\d [[:alpha:]]{3} \d\d:\d\d\]
-[*]{2} Actors:
-: [0-9a-f]{7} John Doe
-[*]{2} Participants:
-: [0-9a-f]{7} John Doe
-$`
+	const expOrgMode = `#+TODO: OPEN | CLOSED
+* OPEN   ` + ExpHumanId + ` [` + ExpOrgModeDate + `] John Doe: this is a bug title ::
+** Last Edited: [` + ExpOrgModeDate + `]
+** Actors:
+: ` + ExpHumanId + ` John Doe
+** Participants:
+: ` + ExpHumanId + ` John Doe
+`
+
+	const expJson = `[
+    {
+        "id": "` + ExpId + `",
+        "human_id": "` + ExpHumanId + `",
+        "create_time": {
+            "timestamp": ` + ExpTimestamp + `,
+            "time": "` + ExpISO8601 + `",
+            "lamport": 2
+        },
+        "edit_time": {
+            "timestamp": ` + ExpTimestamp + `,
+            "time": "` + ExpISO8601 + `",
+            "lamport": 2
+        },
+        "status": "open",
+        "labels": null,
+        "title": "this is a bug title",
+        "actors": [
+            {
+                "id": "` + ExpId + `",
+                "human_id": "` + ExpHumanId + `",
+                "name": "John Doe",
+                "login": ""
+            }
+        ],
+        "participants": [
+            {
+                "id": "` + ExpId + `",
+                "human_id": "` + ExpHumanId + `",
+                "name": "John Doe",
+                "login": ""
+            }
+        ],
+        "author": {
+            "id": "` + ExpId + `",
+            "human_id": "` + ExpHumanId + `",
+            "name": "John Doe",
+            "login": ""
+        },
+        "comments": 1,
+        "metadata": {}
+    }
+]
+`
 
 	cases := []struct {
 		format string
 		exp    string
 	}{
-		{"default", "^[0-9a-f]{7}\topen\tthis is a bug title                      John Doe        \n$"},
-		{"plain", "^[0-9a-f]{7}\topen\tthis is a bug title\n$"},
-		{"id", "^[0-9a-f]{64}\n$"},
+		{"default", ExpHumanId + "\topen\tthis is a bug title                      John Doe        \n"},
+		{"plain", ExpHumanId + "\topen\tthis is a bug title\n"},
+		{"id", ExpId + "\n"},
 		{"org-mode", expOrgMode},
-		{"json", ".*"},
+		{"json", expJson},
 	}
 
 	for _, testcase := range cases {
@@ -79,15 +123,7 @@ $`
 			}
 
 			require.NoError(t, runBug(env, opts, []string{}))
-
-			switch testcase.format {
-			case "json":
-				var bugs []cmdjson.BugExcerpt
-				require.NoError(t, json.Unmarshal(env.Out.Bytes(), &bugs))
-				require.Len(t, bugs, 1)
-			default:
-				require.Regexp(t, testcase.exp, env.Out.String())
-			}
+			require.Regexp(t, MakeExpectedRegex(testcase.exp), env.Out.String())
 		})
 	}
 }

commands/cmdtest/regex.go 🔗

@@ -0,0 +1,62 @@
+package cmdtest
+
+import (
+	"regexp"
+	"strings"
+)
+
+const ExpId = "\x07id\x07"
+const ExpHumanId = "\x07human-id\x07"
+const ExpTimestamp = "\x07timestamp\x07"
+const ExpISO8601 = "\x07iso8601\x07"
+
+const ExpOrgModeDate = "\x07org-mode-date\x07"
+
+// MakeExpectedRegex transform a raw string of an expected output into a regex suitable for testing.
+// Some markers like ExpId are available to substitute the appropriate regex for element that can vary randomly.
+func MakeExpectedRegex(input string) string {
+	var substitutes = map[string]string{
+		ExpId:          `[0-9a-f]{64}`,
+		ExpHumanId:     `[0-9a-f]{7}`,
+		ExpTimestamp:   `[0-9]{7,10}`,
+		ExpISO8601:     `\d{4}(-\d\d(-\d\d(T\d\d:\d\d(:\d\d)?(\.\d+)?(([+-]\d\d:\d\d)|Z)?)?)?)?`,
+		ExpOrgModeDate: `\d\d\d\d-\d\d-\d\d [[:alpha:]]{3} \d\d:\d\d`,
+	}
+
+	escaped := []rune(regexp.QuoteMeta(input))
+
+	var result strings.Builder
+	var inSubstitute bool
+	var substitute strings.Builder
+
+	result.WriteString("^")
+
+	for i := 0; i < len(escaped); i++ {
+		r := escaped[i]
+		if !inSubstitute && r == '\x07' {
+			substitute.Reset()
+			substitute.WriteRune(r)
+			inSubstitute = true
+			continue
+		}
+		if inSubstitute && r == '\x07' {
+			substitute.WriteRune(r)
+			sub, ok := substitutes[substitute.String()]
+			if !ok {
+				panic("unknown substitute: " + substitute.String())
+			}
+			result.WriteString(sub)
+			inSubstitute = false
+			continue
+		}
+		if inSubstitute {
+			substitute.WriteRune(r)
+		} else {
+			result.WriteRune(r)
+		}
+	}
+
+	result.WriteString("$")
+
+	return result.String()
+}

commands/cmdtest/regex_test.go 🔗

@@ -0,0 +1,25 @@
+package cmdtest
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestMakeExpectedRegex(t *testing.T) {
+	cases := []struct {
+		sub  string
+		text string
+	}{
+		{ExpId, "d96dc877077a571414168c946eb013035888715b561e75682cfae9ef785e3227"},
+		{ExpHumanId, "d96dc87"},
+		{ExpTimestamp, "1674368486"},
+		{ExpISO8601, "2023-01-22T07:21:26+01:00"},
+	}
+
+	for _, tc := range cases {
+		t.Run(tc.sub, func(t *testing.T) {
+			require.Regexp(t, MakeExpectedRegex(tc.text), tc.text)
+		})
+	}
+}