Finishes server and adds tests that include mutual tls.
This commit is contained in:
parent
1ca6706ff3
commit
bf7eecd29b
4 changed files with 242 additions and 7 deletions
200
internal/server/server_test.go
Normal file
200
internal/server/server_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue