Skip to content
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

Mocked Pipelined with expected error does not match Redis' behavior #78

Open
amayausky opened this issue Sep 18, 2023 · 1 comment
Open

Comments

@amayausky
Copy link

I'm attempting to test a Pipelined function that experiences a failure partway through execution of a set of commands. With Redis (and go-redis) the behavior seen is all commands are run, and each one returns a response. So if an error occurs partway through it does not stop execution of the other errors while returning its error with the response array. With redismock I'm seeing the execution stop after the first error.

Here are some sample tests:

func Test_RedisPipelined(t *testing.T) {
	c := getRedisClient(t) // returns a redis.NewClient
	defer c.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
	defer cancel()

	// prep keys
	if c.Set(ctx, "foo", "bar", 0).Err() != nil {
		t.Fatal("failed to set foo key")
	}
	if c.Set(ctx, "bar", "baz", 0).Err() != nil {
		t.Fatal("failed to set bar key")
	}

	cmds, err := c.Pipelined(ctx, func(p redis.Pipeliner) error {
		p.Get(ctx, "foo") // should exist
		p.Get(ctx, "baz") // should not exist
		p.Get(ctx, "bar") // should exist
		return nil
	})

	// go-redis returns the first error found in the chain as this error
	// other errors can also be returned
	if err != nil {
		if err != redis.Nil {
			t.Fatalf("expected error to be %v, got %v", redis.Nil, err)
		}
	} else {
		t.Fatal("expected error returned")
	}

	if len(cmds) != 3 {
		t.Fatalf("expected %v cmds, got %v", 3, len(cmds))
	}
	if cmds[0].Err() != nil {
		t.Fatalf("first command returned error: %v", cmds[0].Err())
	}
	if cmds[1].Err() == nil {
		t.Fatal("second command returned nil error")
	}
	if cmds[2].Err() != nil {
		t.Fatalf("third command returned error: %v", cmds[2].Err())
	}
}

func Test_MockedPipelined(t *testing.T) {
	db, mock := redismock.NewClientMock()

	mock.ExpectGet("foo").SetVal("foo")
	mock.ExpectGet("baz").RedisNil()
	mock.ExpectGet("bar").SetVal("bar")

	ctx := context.Background()
	db.Pipelined(ctx, func(p redis.Pipeliner) error {
		p.Get(ctx, "foo")
		p.Get(ctx, "baz")
		p.Get(ctx, "bar")
		return nil
	})

	if err := mock.ExpectationsWereMet(); err != nil {
		t.Error(err)
	}
}

The Test_RedisPipelined test which uses an actual Redis server succeeds, while the mock one fails with:

--- FAIL: Test_MockedPipelined (0.00s)
    main_test.go:133: there is a remaining expectation which was not matched: [get bar]
FAIL
@leatral
Copy link

leatral commented Nov 20, 2023

I meet this error too.

While use redis/v9/Client's Pipeline().exec(), it finally call pipelineReadCmds() in redis/v9/osscluster.go. In this method it only skip remain command while meet non redis err, but ProcessPipelineHook() in redismock/v9/mock.go don't have this judgement. Which caused this inconsistence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants