corvus-ch/rabbitmq-cli-consumer

View on GitHub
processor/processor_test.go

Summary

Maintainability
A
0 mins
Test Coverage
package processor

import (
    "errors"
    "fmt"
    "io"
    "io/ioutil"
    "os"
    "os/exec"
    "strconv"
    "testing"

    "github.com/bketelsen/logr"
    log "github.com/corvus-ch/logr/buffered"
    "github.com/corvus-ch/rabbitmq-cli-consumer/command"
    "github.com/corvus-ch/rabbitmq-cli-consumer/delivery"
    "github.com/sebdah/goldie"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/mock"
)

var execCommandRunTests = []struct {
    name string
    cmd  *exec.Cmd
    code int
}{
    {
        "success",
        testCommand("echo", false, []string{}...),
        0,
    },
    {
        "error",
        testCommand("error", false, "lorem", "ipsum"),
        1,
    },
    {
        "errorCapture",
        testCommand("error", true, "dolor", "sit"),
        1,
    },
}

func TestProcessor_Run(t *testing.T) {
    for _, test := range execCommandRunTests {
        t.Run(test.name, func(t *testing.T) {
            l := log.New(0)
            p := processor{log: l, cmd: test.cmd}

            assert.Equal(t, p.run(), test.code)
            goldie.Assert(t, t.Name(), l.Buf().Bytes())
        })
    }
}

var properties = delivery.Properties{}
var info = delivery.Info{}

func TestProcessing(t *testing.T) {
    testProcessing(t, "happyPath", func(t *testing.T, a *TestAcknowledger, b *TestBuilder, d *TestDelivery) string {
        cmd := testCommand("exit", true, fmt.Sprintf("%d", 42))

        a.On("Ack", d, 42).Return(nil)
        b.On("GetCommand", properties, info, []byte(t.Name())).Return(cmd, nil)

        return ""
    })
    testProcessing(t, "GetCommandError", func(t *testing.T, a *TestAcknowledger, b *TestBuilder, d *TestDelivery) string {
        var cmd *exec.Cmd

        b.On("GetCommand", properties, info, []byte(t.Name())).Return(cmd, errors.New("invalid json"))
        d.On("Nack", true).Return(nil)

        return "failed to register a consumer: invalid json"
    })
    testProcessing(t, "AckError", func(t *testing.T, a *TestAcknowledger, b *TestBuilder, d *TestDelivery) string {
        cmd := testCommand("exit", true, fmt.Sprintf("%d", 42))

        a.On("Ack", d, 42).Return(errors.New("unexpected exit code 42"))
        b.On("GetCommand", properties, info, []byte(t.Name())).Return(cmd, nil)

        return "failed to aknowledge message: unexpected exit code 42"
    })
}

func testProcessing(t *testing.T, name string, setup func(t *testing.T, a *TestAcknowledger, b *TestBuilder, d *TestDelivery) string) {
    t.Run(name, func(t *testing.T) {
        di := delivery.Info{}
        pr := delivery.Properties{}
        body := []byte(t.Name())

        a := new(TestAcknowledger)
        b := new(TestBuilder)
        d := new(TestDelivery)
        p := New(b, a, log.New(0))

        d.On("Body").Return(body)
        d.On("Properties").Return(pr)
        d.On("Info").Return(di)

        exp := setup(t, a, b, d)
        err := p.Process(d)

        if len(exp) > 0 {
            assert.Equal(t, exp, err.Error())
        }
        a.AssertExpectations(t)
        b.AssertExpectations(t)
        d.AssertExpectations(t)
    })
}

func testCommand(command string, capture bool, args ...string) *exec.Cmd {
    cs := []string{"-test.run=TestHelperProcess", "--", command}
    cs = append(cs, args...)
    cmd := exec.Command(os.Args[0], cs...)
    cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
    if capture {
        cmd.Stdout = ioutil.Discard
        cmd.Stderr = ioutil.Discard
    }

    return cmd
}

func TestHelperProcess(t *testing.T) {
    if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
        return
    }
    defer os.Exit(0)

    args := helperProcessArgs()
    helperProcessAssertArgs(args)

    cmd, args := args[0], args[1:]
    switch cmd {
    case "echo":
        helperProcessCmdEcho(args, 0)

    case "error":
        helperProcessCmdEcho(args, 1)

    case "exit":
        code, err := strconv.Atoi(args[0])
        if err != nil {
            code = 0
        }
        helperProcessCmdEcho(args[1:], code)

    default:
        fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd)
        os.Exit(2)
    }
}

func helperProcessArgs() []string {
    args := os.Args
    for len(args) > 0 {
        if args[0] == "--" {
            args = args[1:]
            break
        }
        args = args[1:]
    }

    return args
}

func helperProcessAssertArgs(args []string) {
    if len(args) == 0 {
        fmt.Fprintln(os.Stderr, "No command")
        os.Exit(2)
    }
}

func helperProcessCmdEcho(args []string, code int) {
    for _, a := range args {
        fmt.Println(a)
    }
    os.Exit(code)
}

type TestBuilder struct {
    command.Builder
    mock.Mock
}

func (b *TestBuilder) SetLogger(l logr.Logger) {
    b.Called(l)
}
func (b *TestBuilder) SetOutputWriter(w io.Writer) {
    b.Called(w)
}
func (b *TestBuilder) SetErrorWriter(lw io.Writer) {
    b.Called(lw)
}
func (b *TestBuilder) SetCaptureOutput(capture bool) {
    b.Called(capture)
}
func (b *TestBuilder) SetCommand(cmd string) {
    b.Called(cmd)
}

func (b *TestBuilder) GetCommand(p delivery.Properties, d delivery.Info, body []byte) (*exec.Cmd, error) {
    argsT := b.Called(p, d, body)

    return argsT.Get(0).(*exec.Cmd), argsT.Error(1)
}

type TestDelivery struct {
    mock.Mock
}

func (t *TestDelivery) Ack() error {
    argstT := t.Called()

    return argstT.Error(0)
}

func (t *TestDelivery) Nack(requeue bool) error {
    argsT := t.Called(requeue)

    return argsT.Error(0)
}

func (t *TestDelivery) Reject(requeue bool) error {
    argsT := t.Called(requeue)

    return argsT.Error(0)
}

func (t *TestDelivery) Body() []byte {
    argsT := t.Called()

    return argsT.Get(0).([]byte)
}

func (t *TestDelivery) Properties() delivery.Properties {
    argsT := t.Called()

    return argsT.Get(0).(delivery.Properties)
}

func (t *TestDelivery) Info() delivery.Info {
    argsT := t.Called()

    return argsT.Get(0).(delivery.Info)
}

type TestAcknowledger struct {
    mock.Mock
}

func (t *TestAcknowledger) Ack(d delivery.Delivery, code int) error {
    argsT := t.Called(d, code)

    return argsT.Error(0)
}