Finishes server and adds tests that include mutual tls.

This commit is contained in:
Mariano Uvalle 2021-08-11 18:29:56 -05:00
parent 1ca6706ff3
commit bf7eecd29b
4 changed files with 242 additions and 7 deletions

View file

@ -0,0 +1,200 @@
package server
import (
"context"
"io/ioutil"
"net"
"testing"
api "github.com/AYM1607/proglog/api/v1"
"github.com/AYM1607/proglog/internal/config"
"github.com/AYM1607/proglog/internal/log"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/status"
)
func TestServer(t *testing.T) {
for scenario, fn := range map[string]func(
t *testing.T,
client api.LogClient,
config *Config,
){
"produce/consume a message to/from the log succeeds": testProduceConsume,
"produce/consume stream succeeds": testProduceConsumeStream,
"consume past a log boundary fails": testConsumePastBoundary,
} {
t.Run(scenario, func(t *testing.T) {
client, config, teardown := setupTest(t, nil)
defer teardown()
fn(t, client, config)
})
}
}
func setupTest(t *testing.T, fn func(*Config)) (
client api.LogClient,
cfg *Config,
teardown func(),
) {
t.Helper()
l, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
// Client config.
clientTLSConfig, err := config.SetupTLSConfig(config.TLSConfig{
CertFile: config.ClientCertFile,
KeyFile: config.ClientKeyFile,
CAFile: config.CAFile,
})
require.NoError(t, err)
clientCreds := credentials.NewTLS(clientTLSConfig)
cc, err := grpc.Dial(
l.Addr().String(),
grpc.WithTransportCredentials(clientCreds),
)
require.NoError(t, err)
client = api.NewLogClient(cc)
// Server config.
serverTLSConfig, err := config.SetupTLSConfig(config.TLSConfig{
CertFile: config.ServerCertFile,
KeyFile: config.ServerKeyFile,
CAFile: config.CAFile,
ServerAddress: l.Addr().String(),
Server: true,
})
require.NoError(t, err)
serverCreds := credentials.NewTLS(serverTLSConfig)
dir, err := ioutil.TempDir("", "server-test")
require.NoError(t, err)
clog, err := log.NewLog(dir, log.Config{})
require.NoError(t, err)
cfg = &Config{
CommitLog: clog,
}
if fn != nil {
fn(cfg)
}
server, err := NewGRPCServer(cfg, grpc.Creds(serverCreds))
require.NoError(t, err)
go func() {
server.Serve(l)
}()
return client, cfg, func() {
server.Stop()
cc.Close()
l.Close()
clog.Remove()
}
}
func testProduceConsume(t *testing.T, client api.LogClient, config *Config) {
ctx := context.Background()
want := &api.Record{
Value: []byte("hello world"),
}
produce, err := client.Produce(
ctx,
&api.ProduceRequest{
Record: want,
},
)
require.NoError(t, err)
consume, err := client.Consume(ctx, &api.ConsumeRequest{
Offset: produce.Offset,
})
require.NoError(t, err)
require.Equal(t, produce.Offset, consume.Record.Offset)
require.Equal(t, want.Value, consume.Record.Value)
}
func testConsumePastBoundary(
t *testing.T,
client api.LogClient,
config *Config,
) {
ctx := context.Background()
produce, err := client.Produce(ctx, &api.ProduceRequest{
Record: &api.Record{
Value: []byte("hello world"),
},
})
require.NoError(t, err)
consume, err := client.Consume(ctx, &api.ConsumeRequest{
Offset: produce.Offset + 1,
})
require.Nil(t, consume, "consume should be nil")
got := status.Code(err)
want := status.Code(api.ErrOffsetOutOfRange{}.GRPCStatus().Err())
require.Equal(t, want, got)
}
func testProduceConsumeStream(
t *testing.T,
client api.LogClient,
config *Config,
) {
ctx := context.Background()
records := []*api.Record{{
Value: []byte("first message"),
Offset: 0,
}, {
Value: []byte("second message"),
Offset: 1,
}}
// Test Produce Stream.
// The code from the book adds an extra scope. Is it really needed?
{
stream, err := client.ProduceStream(ctx)
require.NoError(t, err)
// The log is empty so the slice index for reach record is also their offset.
for offset, record := range records {
err = stream.Send(&api.ProduceRequest{
Record: record,
})
require.NoError(t, err)
res, err := stream.Recv()
require.NoError(t, err)
require.Equal(t, uint64(offset), res.Offset)
}
}
// Test Consume stream.
// The code from the book adds an extra scope. Is it really needed?
{
stream, err := client.ConsumeStream(
ctx,
&api.ConsumeRequest{Offset: 0},
)
require.NoError(t, err)
for _, record := range records {
res, err := stream.Recv()
require.NoError(t, err)
// A record literal must be used otherwise the comparison fails.
require.Equal(t, &api.Record{
Value: record.Value,
Offset: record.Offset,
}, res.Record)
}
}
}