-
Notifications
You must be signed in to change notification settings - Fork 35
CLI refactoring #168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
CLI refactoring #168
Changes from all commits
ce220d1
b29b7e5
d9b8a5c
60b5d9c
6430df7
d7fbcd3
e92c3f0
437e74d
528d58e
97ef073
54c06c4
234fc0d
a0e5432
895e4a2
bf504c4
7747504
49541c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| /.cache/ | ||
| /.claude/ | ||
| /yts/testdata/ | ||
| /go-yaml |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -30,7 +30,6 @@ MAKES-CLEAN := $(CLI-BINARY) | |||||
| MAKES-REALCLEAN := $(dir $(YTS-DIR)) | ||||||
|
|
||||||
| # Setup and include go.mk and shell.mk: | ||||||
| GO-FILES := $(shell find -not \( -path ./.cache -prune \) -name '*.go' | sort) | ||||||
| GO-CMDS-SKIP := test fmt vet | ||||||
| ifndef GO-VERSION-NEEDED | ||||||
| GO-NO-DEP-GO := true | ||||||
|
|
@@ -58,19 +57,25 @@ SHELL-NAME := makes go-yaml | |||||
| include $(MAKES)/clean.mk | ||||||
| include $(MAKES)/shell.mk | ||||||
|
|
||||||
| MAKES-CLEAN := $(dir $(YTS-DIR)) $(GOLANGCI-LINT) | ||||||
| MAKES-CLEAN += $(GOLANGCI-LINT) | ||||||
|
|
||||||
| MAKE := $(MAKE) --no-print-directory | ||||||
|
|
||||||
| v ?= | ||||||
| count ?= 1 | ||||||
|
|
||||||
|
|
||||||
| # Test rules: | ||||||
| test: $(GO-DEPS) | ||||||
| go test$(if $v, -v) -vet=off ./... | ||||||
| check: | ||||||
| $(MAKE) fmt | ||||||
| $(MAKE) tidy | ||||||
| $(MAKE) lint | ||||||
| $(MAKE) test | ||||||
|
|
||||||
| test-data: $(YTS-DIR) | ||||||
| test: test-unit test-cli test-yts-all | ||||||
|
|
||||||
| test-all: test test-yts-all | ||||||
| test-unit: $(GO-DEPS) | ||||||
| go test$(if $v, -v) | ||||||
|
|
||||||
| test-yts: $(GO-DEPS) $(YTS-DIR) | ||||||
| go test$(if $v, -v) ./yts -count=$(count) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
@@ -83,12 +88,14 @@ test-yts-fail: $(GO-DEPS) $(YTS-DIR) | |||||
| @echo 'Testing yaml-test-suite failures' | ||||||
| @RUNFAILING=1 bash -c "$$yts_pass_fail" | ||||||
|
|
||||||
| test-cli: $(GO-DEPS) cli | ||||||
| go test$(if $v, -v) ./cmd/go-yaml -count=$(count) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| get-test-data: $(YTS-DIR) | ||||||
|
|
||||||
| # Install golangci-lint for GitHub Actions: | ||||||
| golangci-lint-install: $(GOLANGCI-LINT) | ||||||
|
|
||||||
| fmt: $(GOLANGCI-LINT-VERSIONED) | ||||||
| $< fmt ./... | ||||||
|
|
||||||
|
Comment on lines
-89
to
-91
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no reason to remove make fmt |
||||||
| lint: $(GOLANGCI-LINT-VERSIONED) | ||||||
| $< run ./... | ||||||
|
|
||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,193 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "os" | ||
| "os/exec" | ||
| "path/filepath" | ||
| "strings" | ||
| "testing" | ||
|
|
||
| "go.yaml.in/yaml/v4" | ||
| ) | ||
|
|
||
| // TestCase represents a single test case from a test file | ||
| type TestCase struct { | ||
| Name string `yaml:"name"` | ||
| Text string `yaml:"text"` | ||
| Token string `yaml:"token,omitempty"` | ||
| TOKEN string `yaml:"TOKEN,omitempty"` | ||
| Event string `yaml:"event,omitempty"` | ||
| EVENT string `yaml:"EVENT,omitempty"` | ||
| Node string `yaml:"node,omitempty"` | ||
| NODE string `yaml:"NODE,omitempty"` | ||
| Yaml string `yaml:"yaml,omitempty"` | ||
| YAML string `yaml:"YAML,omitempty"` | ||
| Json string `yaml:"json,omitempty"` | ||
| JSON string `yaml:"JSON,omitempty"` | ||
| } | ||
|
|
||
| // TestSuite is a sequence of test cases | ||
| type TestSuite []TestCase | ||
|
|
||
| // flagMapping maps test file field names to CLI flags | ||
| var flagMapping = map[string]string{ | ||
| "token": "-t", | ||
| "TOKEN": "-T", | ||
| "event": "-e", | ||
| "EVENT": "-E", | ||
| "node": "-n", | ||
| "NODE": "-N", | ||
| "yaml": "-y", | ||
| "YAML": "-Y", | ||
| "json": "-j", | ||
| "JSON": "-J", | ||
| } | ||
|
|
||
| func TestCLI(t *testing.T) { | ||
| // Find all test files in testdata/ | ||
| testFiles, err := filepath.Glob("testdata/*.yaml") | ||
| if err != nil { | ||
| t.Fatalf("Failed to find test files: %v", err) | ||
| } | ||
|
|
||
| if len(testFiles) == 0 { | ||
| t.Skip("No test files found in testdata/") | ||
| } | ||
|
|
||
| // Build the CLI binary if it doesn't exist | ||
| binaryPath := "../../go-yaml" | ||
| if _, err := os.Stat(binaryPath); os.IsNotExist(err) { | ||
| t.Logf("Building go-yaml binary...") | ||
| cmd := exec.Command("go", "build", "-o", binaryPath, ".") | ||
| if output, err := cmd.CombinedOutput(); err != nil { | ||
| t.Fatalf("Failed to build go-yaml: %v\n%s", err, output) | ||
| } | ||
| } | ||
|
Comment on lines
+58
to
+66
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not logic to build it in Go. go run can be used Also, code should be written in a way go test could be enough to emulate everything The usual and idiomatic way in Go is to use os.ExecPipe and to play with it to pass data to Stdin and read result of Stdout Using exec and string.Buffer is not the way to run and test go code. It would be of the binary launched was a blackbox binary
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for @SVilgelm |
||
|
|
||
| // Process each test file | ||
| for _, testFile := range testFiles { | ||
| testFileName := filepath.Base(testFile) | ||
| t.Run(testFileName, func(t *testing.T) { | ||
| runTestFile(t, testFile, binaryPath) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func runTestFile(t *testing.T, testFile, binaryPath string) { | ||
| // Read and parse the test file | ||
| data, err := os.ReadFile(testFile) | ||
| if err != nil { | ||
| t.Fatalf("Failed to read test file %s: %v", testFile, err) | ||
| } | ||
|
|
||
| var suite TestSuite | ||
| if err := yaml.Unmarshal(data, &suite); err != nil { | ||
| t.Fatalf("Failed to parse test file %s: %v", testFile, err) | ||
| } | ||
|
|
||
| // Run each test case | ||
| for _, testCase := range suite { | ||
| t.Run(testCase.Name, func(t *testing.T) { | ||
| runTestCase(t, testCase, binaryPath) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func runTestCase(t *testing.T, tc TestCase, binaryPath string) { | ||
| // Test each output format that has an expected value | ||
| tests := []struct { | ||
| field string | ||
| flag string | ||
| expected string | ||
| }{ | ||
| {"token", flagMapping["token"], tc.Token}, | ||
| {"TOKEN", flagMapping["TOKEN"], tc.TOKEN}, | ||
| {"event", flagMapping["event"], tc.Event}, | ||
| {"EVENT", flagMapping["EVENT"], tc.EVENT}, | ||
| {"node", flagMapping["node"], tc.Node}, | ||
| {"NODE", flagMapping["NODE"], tc.NODE}, | ||
| {"yaml", flagMapping["yaml"], tc.Yaml}, | ||
| {"YAML", flagMapping["YAML"], tc.YAML}, | ||
| {"json", flagMapping["json"], tc.Json}, | ||
| {"JSON", flagMapping["JSON"], tc.JSON}, | ||
| } | ||
|
|
||
| for _, test := range tests { | ||
| if test.expected == "" { | ||
| continue // Skip if no expected output for this format | ||
| } | ||
|
|
||
| t.Run(test.field, func(t *testing.T) { | ||
| // Run the CLI command | ||
| cmd := exec.Command(binaryPath, test.flag) | ||
| cmd.Stdin = strings.NewReader(tc.Text) | ||
|
|
||
| var stdout, stderr bytes.Buffer | ||
| cmd.Stdout = &stdout | ||
| cmd.Stderr = &stderr | ||
|
|
||
| if err := cmd.Run(); err != nil { | ||
| t.Fatalf("Command failed: %v\nStderr: %s", err, stderr.String()) | ||
| } | ||
|
|
||
| // Normalize output for comparison | ||
| actual := normalizeOutput(stdout.String()) | ||
| expected := normalizeOutput(test.expected) | ||
|
|
||
| if actual != expected { | ||
| t.Errorf("Output mismatch for flag %s\nExpected:\n%s\n\nActual:\n%s\n\nDiff:\n%s", | ||
| test.flag, expected, actual, diff(expected, actual)) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // normalizeOutput trims whitespace and ensures consistent line endings | ||
| func normalizeOutput(s string) string { | ||
| s = strings.TrimSpace(s) | ||
| s = strings.ReplaceAll(s, "\r\n", "\n") | ||
| return s | ||
| } | ||
|
|
||
| // diff provides a simple diff output for debugging | ||
| func diff(expected, actual string) string { | ||
| expLines := strings.Split(expected, "\n") | ||
| actLines := strings.Split(actual, "\n") | ||
|
|
||
| maxLines := len(expLines) | ||
| if len(actLines) > maxLines { | ||
| maxLines = len(actLines) | ||
| } | ||
|
|
||
| var result strings.Builder | ||
| for i := 0; i < maxLines; i++ { | ||
| expLine := "" | ||
| actLine := "" | ||
|
|
||
| if i < len(expLines) { | ||
| expLine = expLines[i] | ||
| } | ||
| if i < len(actLines) { | ||
| actLine = actLines[i] | ||
| } | ||
|
|
||
| if expLine != actLine { | ||
| result.WriteString("Line ") | ||
| result.WriteString(strings.Repeat(" ", len(strings.TrimSpace(expLine))+1)) | ||
| result.WriteString("\n") | ||
| if expLine != "" { | ||
| result.WriteString("- ") | ||
| result.WriteString(expLine) | ||
| result.WriteString("\n") | ||
| } | ||
| if actLine != "" { | ||
| result.WriteString("+ ") | ||
| result.WriteString(actLine) | ||
| result.WriteString("\n") | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return result.String() | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are removing the vet=off we just added 😁 also the ./...