From bd35dbe6ba70c6cb4d7396fe69767c199305abbf Mon Sep 17 00:00:00 2001 From: Rudhar Pratap Singh Date: Mon, 21 Oct 2024 08:55:56 +0530 Subject: [PATCH 1/3] Added test cases for BinaryParser --- go.mod | 5 +- go.sum | 6 ++ parser/binary-parser.go | 18 +++++- parser/binary-parser_test.go | 117 +++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 parser/binary-parser_test.go diff --git a/go.mod b/go.mod index e63f79d..729f102 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/m1gwings/treedrawer v0.3.3-beta github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 github.com/olekukonko/tablewriter v0.0.5 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.9.0 ) require ( @@ -19,8 +19,9 @@ require ( github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/zclconf/go-cty v1.14.1 // indirect golang.org/x/sys v0.6.0 // indirect golang.org/x/text v0.11.0 // indirect - gopkg.in/yaml.v3 v3.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d105ff0..86345da 100644 --- a/go.sum +++ b/go.sum @@ -26,8 +26,12 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA= github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -40,3 +44,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/parser/binary-parser.go b/parser/binary-parser.go index b7c3c92..aae4ae6 100644 --- a/parser/binary-parser.go +++ b/parser/binary-parser.go @@ -9,8 +9,20 @@ import ( tfjson "github.com/hashicorp/terraform-json" ) +type CommandExecutor interface { + CombinedOutput(name string, args ...string) ([]byte, error) +} + +type RealCommandExecutor struct{} + +func (e RealCommandExecutor) CombinedOutput(name string, args ...string) ([]byte, error) { + cmd := exec.Command(name, args...) + return cmd.CombinedOutput() +} + type BinaryParser struct { fileName string + executor CommandExecutor } func (j BinaryParser) Parse() (tfjson.Plan, error) { @@ -18,14 +30,13 @@ func (j BinaryParser) Parse() (tfjson.Plan, error) { if tfoverride, ok := os.LookupEnv("TF_BINARY"); ok { tfbinary = tfoverride } - cmd := exec.Command(tfbinary, "show", "-json", j.fileName) - output, err := cmd.CombinedOutput() + output, err := j.executor.CombinedOutput(tfbinary, "show", "-json", j.fileName) if err != nil { return tfjson.Plan{}, fmt.Errorf( "error when running 'terraform show -json %s': \n%s\n\n%s", j.fileName, output, "Make sure you are running in terraform directory and terraform init is done") } - plan := tfjson.Plan{} + var plan tfjson.Plan err = json.Unmarshal(output, &plan) if err != nil { return tfjson.Plan{}, fmt.Errorf("error when parsing input: %s", err.Error()) @@ -36,5 +47,6 @@ func (j BinaryParser) Parse() (tfjson.Plan, error) { func NewBinaryParser(fileName string) Parser { return BinaryParser{ fileName: fileName, + executor: RealCommandExecutor{}, } } diff --git a/parser/binary-parser_test.go b/parser/binary-parser_test.go new file mode 100644 index 0000000..90c40da --- /dev/null +++ b/parser/binary-parser_test.go @@ -0,0 +1,117 @@ +package parser + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + + tfjson "github.com/hashicorp/terraform-json" +) + +// MockCommandExecutor mocks the command execution. +type MockCommandExecutor struct { + Output []byte + Err error + ActualName string + ActualArgs []string +} + +func (e *MockCommandExecutor) CombinedOutput(name string, args ...string) ([]byte, error) { + e.ActualName = name + e.ActualArgs = args + return e.Output, e.Err +} + +func TestBinaryParser_Parse_Success(t *testing.T) { + // Prepare mock output + mockPlan := tfjson.Plan{ + FormatVersion: "0.1", + TerraformVersion: "1.0.0", + } + output, err := json.Marshal(mockPlan) + if err != nil { + t.Fatalf("Failed to marshal mock plan: %v", err) + } + + // Create parser with mock executor + parser := BinaryParser{ + fileName: "mock-file", + executor: &MockCommandExecutor{ + Output: output, + Err: nil, + }, + } + + // Call Parse + parsedPlan, err := parser.Parse() + + //assertions + assert.NoError(t, err, "Expected no error") + assert.Equal(t, mockPlan, parsedPlan, "Parsed plan does not match expected plan") +} + +func TestBinaryParser_Parse_EmptyOutput(t *testing.T) { + // Simulate the command returning empty output + parser := BinaryParser{ + fileName: "mock-file", + executor: &MockCommandExecutor{ + Output: []byte(""), // Empty output + Err: nil, + }, + } + + // Call Parse + _, err := parser.Parse() + + //assertions + assert.Error(t, err, "Expected error due to empty output") + assert.Contains(t, err.Error(), "error when parsing input", "Unexpected error message") +} +func TestBinaryParser_Parse_MissingRequiredFields(t *testing.T) { + // Prepare mock output with missing required fields + incompleteMockOutput := map[string]interface{}{ + // Removing "format_version" and "terraform_version" + "variables": map[string]interface{}{}, + } + output, err := json.Marshal(incompleteMockOutput) + if err != nil { + t.Fatalf("Failed to marshal incomplete plan: %v", err) + } + + // Create parser with mock executor + parser := BinaryParser{ + fileName: "mock-file", + executor: &MockCommandExecutor{ + Output: output, + Err: nil, + }, + } + + // Call Parse + parsedPlan, err := parser.Parse() + + // Assertions + assert.Error(t, err, "Expected an error due to missing required fields") + assert.Contains(t, err.Error(), "format version is missing", "Unexpected error message") + assert.Equal(t, "", parsedPlan.TerraformVersion, "Expected empty TerraformVersion") +} + +func TestBinaryParser_Parse_InvalidJSONOutput(t *testing.T) { + // Simulate the command returning invalid JSON + invalidJSON := []byte(`{"invalid-json"}`) + parser := BinaryParser{ + fileName: "mock-file", + executor: &MockCommandExecutor{ + Output: invalidJSON, + Err: nil, + }, + } + + // Call Parse + _, err := parser.Parse() + + //assertions + assert.Error(t, err, "Expected error due to invalid JSON output") + assert.Contains(t, err.Error(), "error when parsing input", "Unexpected error message") +} From 1e125eec69103a0b27d9c4bbb73558cfcff39ed9 Mon Sep 17 00:00:00 2001 From: Rudhar Pratap Singh Date: Mon, 21 Oct 2024 17:02:50 +0530 Subject: [PATCH 2/3] Added test cases for BinaryParser --- go.sum | 1 + 1 file changed, 1 insertion(+) diff --git a/go.sum b/go.sum index 86345da..d00aeee 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,7 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA= github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From 08d2803fbdf4dc92c71e9a08869f6286db1d67b2 Mon Sep 17 00:00:00 2001 From: Rudhar Pratap Singh Date: Tue, 22 Oct 2024 16:26:53 +0530 Subject: [PATCH 3/3] Testing --- go.mod | 2 +- go.sum | 34 ++++++-- parser/binary-parser.go | 10 +-- parser/binary-parser_test.go | 113 +++++++++++++++++-------- parser/command_executor.go | 7 ++ parser/mocks/mock_command_interface.go | 54 ++++++++++++ 6 files changed, 168 insertions(+), 52 deletions(-) create mode 100644 parser/command_executor.go create mode 100644 parser/mocks/mock_command_interface.go diff --git a/go.mod b/go.mod index 729f102..c953ab1 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.21 require ( github.com/fatih/color v1.15.0 + github.com/golang/mock v1.6.0 github.com/hashicorp/terraform-json v0.20.0 github.com/m1gwings/treedrawer v0.3.3-beta github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 @@ -19,7 +20,6 @@ require ( github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/objx v0.5.2 // indirect github.com/zclconf/go-cty v1.14.1 // indirect golang.org/x/sys v0.6.0 // indirect golang.org/x/text v0.11.0 // indirect diff --git a/go.sum b/go.sum index d00aeee..a1139fe 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,13 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/terraform-json v0.20.0 h1:cJcvn4gIOTi0SD7pIy+xiofV1zFA3hza+6K+fo52IX8= @@ -25,25 +27,39 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA= github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/parser/binary-parser.go b/parser/binary-parser.go index aae4ae6..c9c1d2b 100644 --- a/parser/binary-parser.go +++ b/parser/binary-parser.go @@ -9,13 +9,9 @@ import ( tfjson "github.com/hashicorp/terraform-json" ) -type CommandExecutor interface { - CombinedOutput(name string, args ...string) ([]byte, error) -} - -type RealCommandExecutor struct{} +type DefaultCommandExecutor struct{} -func (e RealCommandExecutor) CombinedOutput(name string, args ...string) ([]byte, error) { +func (e DefaultCommandExecutor) CombinedOutput(name string, args ...string) ([]byte, error) { cmd := exec.Command(name, args...) return cmd.CombinedOutput() } @@ -47,6 +43,6 @@ func (j BinaryParser) Parse() (tfjson.Plan, error) { func NewBinaryParser(fileName string) Parser { return BinaryParser{ fileName: fileName, - executor: RealCommandExecutor{}, + executor: DefaultCommandExecutor{}, } } diff --git a/parser/binary-parser_test.go b/parser/binary-parser_test.go index 90c40da..ad268f1 100644 --- a/parser/binary-parser_test.go +++ b/parser/binary-parser_test.go @@ -1,9 +1,12 @@ +// binary_parser_test.go package parser import ( "encoding/json" "testing" + "github.com/dineshba/tf-summarize/parser/mocks" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" tfjson "github.com/hashicorp/terraform-json" @@ -24,94 +27,134 @@ func (e *MockCommandExecutor) CombinedOutput(name string, args ...string) ([]byt } func TestBinaryParser_Parse_Success(t *testing.T) { + // Initialize GoMock controller + ctrl := gomock.NewController(t) + defer ctrl.Finish() + // Prepare mock output mockPlan := tfjson.Plan{ FormatVersion: "0.1", TerraformVersion: "1.0.0", } output, err := json.Marshal(mockPlan) - if err != nil { - t.Fatalf("Failed to marshal mock plan: %v", err) - } + assert.NoError(t, err, "Failed to marshal mock plan") + + // Create mock executor + mockExecutor := mocks.NewMockCommandExecutor(ctrl) + tfbinary := "terraform" + + // Set up expected call + mockExecutor. + EXPECT(). + CombinedOutput(tfbinary, "show", "-json", "mock-file"). + Return(output, nil) // Create parser with mock executor parser := BinaryParser{ fileName: "mock-file", - executor: &MockCommandExecutor{ - Output: output, - Err: nil, - }, + executor: mockExecutor, } // Call Parse parsedPlan, err := parser.Parse() - //assertions + // assertions assert.NoError(t, err, "Expected no error") - assert.Equal(t, mockPlan, parsedPlan, "Parsed plan does not match expected plan") + assert.Equal(t, mockPlan, parsedPlan) } - func TestBinaryParser_Parse_EmptyOutput(t *testing.T) { - // Simulate the command returning empty output + // Initialize GoMock controller + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Create mock executor + mockExecutor := mocks.NewMockCommandExecutor(ctrl) + tfbinary := "terraform" + + // Set up expectation CombinedOutput will return empty output + mockExecutor. + EXPECT(). + CombinedOutput(tfbinary, "show", "-json", "mock-file"). + Return([]byte(""), nil) + + // Create parser with mock executor parser := BinaryParser{ fileName: "mock-file", - executor: &MockCommandExecutor{ - Output: []byte(""), // Empty output - Err: nil, - }, + executor: mockExecutor, } // Call Parse _, err := parser.Parse() - //assertions + // assertions assert.Error(t, err, "Expected error due to empty output") - assert.Contains(t, err.Error(), "error when parsing input", "Unexpected error message") + assert.Contains(t, err.Error(), "error when parsing input") } + func TestBinaryParser_Parse_MissingRequiredFields(t *testing.T) { + // Initialize GoMock controller + ctrl := gomock.NewController(t) + defer ctrl.Finish() + // Prepare mock output with missing required fields incompleteMockOutput := map[string]interface{}{ - // Removing "format_version" and "terraform_version" + // format_version and terraform_version are missing "variables": map[string]interface{}{}, } output, err := json.Marshal(incompleteMockOutput) - if err != nil { - t.Fatalf("Failed to marshal incomplete plan: %v", err) - } + assert.NoError(t, err, "Failed to marshal incomplete plan") + + // Create mock executor + mockExecutor := mocks.NewMockCommandExecutor(ctrl) + tfbinary := "terraform" + + // Set up expectation: CombinedOutput should return incomplete output + mockExecutor. + EXPECT(). + CombinedOutput(tfbinary, "show", "-json", "mock-file"). + Return(output, nil) // Create parser with mock executor parser := BinaryParser{ fileName: "mock-file", - executor: &MockCommandExecutor{ - Output: output, - Err: nil, - }, + executor: mockExecutor, } // Call Parse parsedPlan, err := parser.Parse() - // Assertions + // assertions assert.Error(t, err, "Expected an error due to missing required fields") - assert.Contains(t, err.Error(), "format version is missing", "Unexpected error message") - assert.Equal(t, "", parsedPlan.TerraformVersion, "Expected empty TerraformVersion") + assert.Contains(t, err.Error(), "format version is missing") + assert.Equal(t, "", parsedPlan.TerraformVersion) } func TestBinaryParser_Parse_InvalidJSONOutput(t *testing.T) { - // Simulate the command returning invalid JSON + // Initialize GoMock controller + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Prepare mock output with invalid json output invalidJSON := []byte(`{"invalid-json"}`) + mockExecutor := mocks.NewMockCommandExecutor(ctrl) + tfbinary := "terraform" + + // Set up expectation + mockExecutor. + EXPECT(). + CombinedOutput(tfbinary, "show", "-json", "mock-file"). + Return(invalidJSON, nil) + + // Create parser with mock executor parser := BinaryParser{ fileName: "mock-file", - executor: &MockCommandExecutor{ - Output: invalidJSON, - Err: nil, - }, + executor: mockExecutor, } // Call Parse _, err := parser.Parse() - //assertions + // Assertions assert.Error(t, err, "Expected error due to invalid JSON output") - assert.Contains(t, err.Error(), "error when parsing input", "Unexpected error message") + assert.Contains(t, err.Error(), "error when parsing input") } diff --git a/parser/command_executor.go b/parser/command_executor.go new file mode 100644 index 0000000..71f582b --- /dev/null +++ b/parser/command_executor.go @@ -0,0 +1,7 @@ +// command_executor.go +package parser + +// CommandExecutor defines an interface for executing commands. +type CommandExecutor interface { + CombinedOutput(name string, args ...string) ([]byte, error) +} diff --git a/parser/mocks/mock_command_interface.go b/parser/mocks/mock_command_interface.go new file mode 100644 index 0000000..191bf66 --- /dev/null +++ b/parser/mocks/mock_command_interface.go @@ -0,0 +1,54 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./parser/command_executor.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockCommandExecutor is a mock of CommandExecutor interface. +type MockCommandExecutor struct { + ctrl *gomock.Controller + recorder *MockCommandExecutorMockRecorder +} + +// MockCommandExecutorMockRecorder is the mock recorder for MockCommandExecutor. +type MockCommandExecutorMockRecorder struct { + mock *MockCommandExecutor +} + +// NewMockCommandExecutor creates a new mock instance. +func NewMockCommandExecutor(ctrl *gomock.Controller) *MockCommandExecutor { + mock := &MockCommandExecutor{ctrl: ctrl} + mock.recorder = &MockCommandExecutorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCommandExecutor) EXPECT() *MockCommandExecutorMockRecorder { + return m.recorder +} + +// CombinedOutput mocks base method. +func (m *MockCommandExecutor) CombinedOutput(name string, args ...string) ([]byte, error) { + m.ctrl.T.Helper() + varargs := []interface{}{name} + for _, a := range args { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CombinedOutput", varargs...) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CombinedOutput indicates an expected call of CombinedOutput. +func (mr *MockCommandExecutorMockRecorder) CombinedOutput(name interface{}, args ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{name}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CombinedOutput", reflect.TypeOf((*MockCommandExecutor)(nil).CombinedOutput), varargs...) +}