Add tests for recorder
This commit is contained in:
parent
3ae3eefbac
commit
6ed23aa62d
6 changed files with 262 additions and 6 deletions
|
@ -2,6 +2,7 @@ package pcap
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
|
@ -29,6 +30,7 @@ type RecordingOptions struct {
|
|||
}
|
||||
|
||||
type Recorder interface {
|
||||
io.Closer
|
||||
AvailableDevices() (devices []Device, err error)
|
||||
Subscriptions() (subscriptions []Subscription)
|
||||
StartRecording(ctx context.Context, device string, consumer Consumer) (err error)
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package pcap
|
||||
package consumers
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/pcapgo"
|
||||
"gitlab.com/inetmock/inetmock/internal/pcap"
|
||||
"go.uber.org/multierr"
|
||||
)
|
||||
|
||||
|
@ -14,11 +15,11 @@ type writerConsumer struct {
|
|||
packageWriter *pcapgo.Writer
|
||||
}
|
||||
|
||||
func (f *writerConsumer) Init(params CaptureParameters) {
|
||||
func (f *writerConsumer) Init(params pcap.CaptureParameters) {
|
||||
_ = f.packageWriter.WriteFileHeader(65536, params.LinkType)
|
||||
}
|
||||
|
||||
func NewWriterConsumer(name string, writer io.Writer) (consumer Consumer, err error) {
|
||||
func NewWriterConsumer(name string, writer io.Writer) (consumer pcap.Consumer, err error) {
|
||||
consumer = &writerConsumer{
|
||||
name: name,
|
||||
origWriter: writer,
|
31
internal/pcap/consumers/noop_consumer.go
Normal file
31
internal/pcap/consumers/noop_consumer.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package consumers
|
||||
|
||||
import (
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/uuid"
|
||||
"gitlab.com/inetmock/inetmock/internal/pcap"
|
||||
)
|
||||
|
||||
type noopConsumer struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func NewNoOpConsumer() pcap.Consumer {
|
||||
return NewNoOpConsumerWithName(uuid.NewString())
|
||||
}
|
||||
|
||||
func NewNoOpConsumerWithName(name string) pcap.Consumer {
|
||||
return &noopConsumer{
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (n noopConsumer) Name() string {
|
||||
return n.name
|
||||
}
|
||||
|
||||
func (n noopConsumer) Observe(gopacket.Packet) {
|
||||
}
|
||||
|
||||
func (n noopConsumer) Init(pcap.CaptureParameters) {
|
||||
}
|
|
@ -9,6 +9,7 @@ import (
|
|||
"time"
|
||||
|
||||
_ "github.com/google/gopacket/layers"
|
||||
"go.uber.org/multierr"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -37,7 +38,7 @@ type recorder struct {
|
|||
|
||||
func (r recorder) Subscriptions() (subscriptions []Subscription) {
|
||||
r.locker.Lock()
|
||||
r.locker.Unlock()
|
||||
defer r.locker.Unlock()
|
||||
|
||||
for devName, dev := range r.openDevices {
|
||||
sub := Subscription{
|
||||
|
@ -100,7 +101,7 @@ func (r *recorder) StartRecording(ctx context.Context, device string, consumer C
|
|||
|
||||
func (r *recorder) StopRecording(consumerKey string) (err error) {
|
||||
r.locker.Lock()
|
||||
r.locker.Unlock()
|
||||
defer r.locker.Unlock()
|
||||
|
||||
var dev deviceConsumer
|
||||
var known bool
|
||||
|
@ -114,3 +115,13 @@ func (r *recorder) StopRecording(consumerKey string) (err error) {
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
func (r recorder) Close() (err error) {
|
||||
r.locker.Lock()
|
||||
defer r.locker.Unlock()
|
||||
|
||||
for _, consumer := range r.openDevices {
|
||||
err = multierr.Append(err, consumer.Close())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
210
internal/pcap/recorder_test.go
Normal file
210
internal/pcap/recorder_test.go
Normal file
|
@ -0,0 +1,210 @@
|
|||
package pcap_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/internal/pcap"
|
||||
"gitlab.com/inetmock/inetmock/internal/pcap/consumers"
|
||||
)
|
||||
|
||||
func Test_recorder_Subscriptions(t *testing.T) {
|
||||
type subscriptionRequest struct {
|
||||
Name string
|
||||
Device string
|
||||
}
|
||||
type testCase struct {
|
||||
name string
|
||||
requests []subscriptionRequest
|
||||
wantSubscriptions []pcap.Subscription
|
||||
}
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "Emtpy",
|
||||
},
|
||||
{
|
||||
name: "Subscription to loopback",
|
||||
requests: []subscriptionRequest{
|
||||
{
|
||||
Name: "test",
|
||||
Device: "lo",
|
||||
},
|
||||
},
|
||||
wantSubscriptions: []pcap.Subscription{
|
||||
{
|
||||
ConsumerKey: "lo:test",
|
||||
ConsumerName: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple subscriptions to loopback",
|
||||
requests: []subscriptionRequest{
|
||||
{
|
||||
Name: "test",
|
||||
Device: "lo",
|
||||
},
|
||||
{
|
||||
Name: "test2",
|
||||
Device: "lo",
|
||||
},
|
||||
},
|
||||
wantSubscriptions: []pcap.Subscription{
|
||||
{
|
||||
ConsumerKey: "lo:test",
|
||||
ConsumerName: "test",
|
||||
},
|
||||
{
|
||||
ConsumerKey: "lo:test2",
|
||||
ConsumerName: "test2",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
scenario := func(tt testCase) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
r := pcap.NewRecorder()
|
||||
|
||||
t.Cleanup(func() {
|
||||
_ = r.Close()
|
||||
})
|
||||
|
||||
for _, req := range tt.requests {
|
||||
if err := r.StartRecording(context.Background(), req.Device, consumers.NewNoOpConsumerWithName(req.Name)); err != nil {
|
||||
t.Errorf("StartRecording() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if gotSubscriptions := sortSubscriptions(r.Subscriptions()); !reflect.DeepEqual(gotSubscriptions, tt.wantSubscriptions) {
|
||||
t.Errorf("Subscriptions() = %v, want %v", gotSubscriptions, tt.wantSubscriptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, scenario(tt))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_recorder_StartRecordingWithOptions(t *testing.T) {
|
||||
type args struct {
|
||||
device string
|
||||
consumer pcap.Consumer
|
||||
opts pcap.RecordingOptions
|
||||
}
|
||||
type testCase struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
recorderSetup func() (recorder pcap.Recorder, err error)
|
||||
}
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "Listen to lo",
|
||||
recorderSetup: func() (recorder pcap.Recorder, err error) {
|
||||
recorder = pcap.NewRecorder()
|
||||
return
|
||||
},
|
||||
args: args{
|
||||
device: "lo",
|
||||
consumer: consumers.NewNoOpConsumer(),
|
||||
opts: pcap.RecordingOptions{
|
||||
Promiscuous: false,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Listen to lo with existing name",
|
||||
recorderSetup: func() (recorder pcap.Recorder, err error) {
|
||||
recorder = pcap.NewRecorder()
|
||||
err = recorder.StartRecording(context.Background(), "lo", consumers.NewNoOpConsumerWithName("test"))
|
||||
return
|
||||
},
|
||||
args: args{
|
||||
device: "lo",
|
||||
consumer: consumers.NewNoOpConsumerWithName("test"),
|
||||
opts: pcap.RecordingOptions{
|
||||
Promiscuous: false,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
scenario := func(tt testCase) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
var err error
|
||||
var recorder pcap.Recorder
|
||||
|
||||
if recorder, err = tt.recorderSetup(); err != nil {
|
||||
t.Fatalf("recorderSetup() error = %v", err)
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
_ = recorder.Close()
|
||||
})
|
||||
|
||||
if err = recorder.StartRecordingWithOptions(context.Background(), tt.args.device, tt.args.consumer, tt.args.opts); (err != nil) != tt.wantErr {
|
||||
t.Errorf("StartRecordingWithOptions() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, scenario(tt))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_recorder_AvailableDevices(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
mtacher func(got []pcap.Device) error
|
||||
wantErr bool
|
||||
}
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "Expect lo device",
|
||||
mtacher: func(got []pcap.Device) error {
|
||||
if len(got) < 1 {
|
||||
return errors.New("expected at least one interface")
|
||||
}
|
||||
|
||||
foundLoopbackDevice := false
|
||||
for _, d := range got {
|
||||
foundLoopbackDevice = foundLoopbackDevice || d.Name == "lo"
|
||||
}
|
||||
|
||||
if !foundLoopbackDevice {
|
||||
return errors.New("didn't find loopback device")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
re := pcap.NewRecorder()
|
||||
gotDevices, err := re.AvailableDevices()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("AvailableDevices() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if err := tt.mtacher(gotDevices); err != nil {
|
||||
t.Errorf("AvailableDevices() matcher error = %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func sortSubscriptions(subs []pcap.Subscription) []pcap.Subscription {
|
||||
sort.Slice(subs, func(i, j int) bool {
|
||||
return subs[i].ConsumerName < subs[j].ConsumerName
|
||||
})
|
||||
return subs
|
||||
}
|
|
@ -9,6 +9,7 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/internal/pcap"
|
||||
"gitlab.com/inetmock/inetmock/internal/pcap/consumers"
|
||||
"gitlab.com/inetmock/inetmock/pkg/rpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
@ -61,7 +62,7 @@ func (p pcapServer) StartPCAPFileRecording(_ context.Context, req *rpc.RegisterP
|
|||
}
|
||||
|
||||
var consumer pcap.Consumer
|
||||
if consumer, err = pcap.NewWriterConsumer(req.TargetPath, writer); err != nil {
|
||||
if consumer, err = consumers.NewWriterConsumer(req.TargetPath, writer); err != nil {
|
||||
return nil, status.Error(codes.Unknown, err.Error())
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue