Skip to content

Commit d61d282

Browse files
authored
Merge pull request #69 from LumeraProtocol/setupSNTests
implement tests for supernode processing
2 parents 2610569 + 98191c4 commit d61d282

File tree

21 files changed

+1347
-36
lines changed

21 files changed

+1347
-36
lines changed

pkg/storage/rqstore/store.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const createRQSymbolsDir string = `
1818
PRIMARY KEY (txid)
1919
);`
2020

21+
//go:generate mockgen -destination=rq_mock.go -package=rqstore -source=store.go
2122
type Store interface {
2223
DeleteSymbolsByTxID(txid string) error
2324
StoreSymbolDirectory(txid, dir string) error

supernode/cmd/keys_add_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package cmd_test
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/LumeraProtocol/supernode/pkg/keyring"
8+
"github.com/spf13/cobra"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestKeysAddCmd_RunE(t *testing.T) {
13+
cmd := getTestKeysAddCmd(t)
14+
15+
tests := []struct {
16+
name string
17+
args []string
18+
wantErr bool
19+
}{
20+
{"no_name_arg", []string{}, false},
21+
{"with_name_arg", []string{"testkey"}, false},
22+
}
23+
24+
for _, tt := range tests {
25+
t.Run(tt.name, func(t *testing.T) {
26+
buf := new(bytes.Buffer)
27+
cmd.SetOut(buf)
28+
cmd.SetArgs(tt.args)
29+
30+
err := cmd.Execute()
31+
if (err != nil) != tt.wantErr {
32+
t.Errorf("unexpected error = %v, wantErr %v", err, tt.wantErr)
33+
}
34+
})
35+
}
36+
}
37+
38+
// getTestKeysAddCmd initializes and returns a test instance of the cobra.Command for keys add.
39+
func getTestKeysAddCmd(t *testing.T) *cobra.Command {
40+
return &cobra.Command{
41+
Use: "add",
42+
RunE: func(cmd *cobra.Command, args []string) error {
43+
name := "testkey"
44+
if len(args) > 0 {
45+
name = args[0]
46+
}
47+
kr, err := keyring.InitKeyring("test", "/tmp")
48+
require.NoError(t, err)
49+
_, _, err = keyring.CreateNewAccount(kr, name)
50+
return err
51+
},
52+
}
53+
}

supernode/cmd/service_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"errors"
6+
"testing"
7+
"time"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
// mockService implements the service interface
13+
type mockService struct {
14+
name string
15+
runFunc func(ctx context.Context) error
16+
}
17+
18+
func (m *mockService) Run(ctx context.Context) error {
19+
return m.runFunc(ctx)
20+
}
21+
22+
func TestRunServices_AllSuccessful(t *testing.T) {
23+
s1 := &mockService{name: "s1", runFunc: func(ctx context.Context) error {
24+
return nil
25+
}}
26+
s2 := &mockService{name: "s2", runFunc: func(ctx context.Context) error {
27+
return nil
28+
}}
29+
30+
err := RunServices(context.Background(), s1, s2)
31+
assert.NoError(t, err)
32+
}
33+
34+
func TestRunServices_OneFails(t *testing.T) {
35+
s1 := &mockService{name: "s1", runFunc: func(ctx context.Context) error {
36+
return errors.New("s1 failed")
37+
}}
38+
s2 := &mockService{name: "s2", runFunc: func(ctx context.Context) error {
39+
return nil
40+
}}
41+
42+
err := RunServices(context.Background(), s1, s2)
43+
assert.Error(t, err)
44+
assert.Equal(t, "s1 failed", err.Error())
45+
}
46+
47+
func TestRunServices_MultipleFail(t *testing.T) {
48+
s1 := &mockService{name: "s1", runFunc: func(ctx context.Context) error {
49+
return errors.New("s1 failed")
50+
}}
51+
s2 := &mockService{name: "s2", runFunc: func(ctx context.Context) error {
52+
return errors.New("s2 failed")
53+
}}
54+
55+
err := RunServices(context.Background(), s1, s2)
56+
assert.Error(t, err)
57+
assert.Contains(t, err.Error(), "failed") // may not be deterministic which one returns
58+
}
59+
60+
func TestRunServices_WithCancellation(t *testing.T) {
61+
ctx, cancel := context.WithCancel(context.Background())
62+
63+
s1 := &mockService{name: "s1", runFunc: func(ctx context.Context) error {
64+
select {
65+
case <-ctx.Done():
66+
return ctx.Err()
67+
case <-time.After(2 * time.Second):
68+
return nil
69+
}
70+
}}
71+
s2 := &mockService{name: "s2", runFunc: func(ctx context.Context) error {
72+
cancel() // cancel context early
73+
return nil
74+
}}
75+
76+
err := RunServices(ctx, s1, s2)
77+
assert.Error(t, err)
78+
assert.Equal(t, context.Canceled, err)
79+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package cascade
2+
3+
import (
4+
"context"
5+
"io"
6+
7+
pb "github.com/LumeraProtocol/supernode/gen/supernode/action/cascade"
8+
"google.golang.org/grpc/metadata"
9+
)
10+
11+
// mockStream simulates pb.CascadeService_RegisterServer
12+
type mockStream struct {
13+
ctx context.Context
14+
request []*pb.RegisterRequest
15+
sent []*pb.RegisterResponse
16+
pos int
17+
}
18+
19+
func (m *mockStream) Context() context.Context {
20+
return m.ctx
21+
}
22+
23+
func (m *mockStream) Send(resp *pb.RegisterResponse) error {
24+
m.sent = append(m.sent, resp)
25+
return nil
26+
}
27+
28+
func (m *mockStream) Recv() (*pb.RegisterRequest, error) {
29+
if m.pos >= len(m.request) {
30+
return nil, io.EOF
31+
}
32+
req := m.request[m.pos]
33+
m.pos++
34+
return req, nil
35+
}
36+
37+
func (m *mockStream) SetHeader(md metadata.MD) error { return nil }
38+
func (m *mockStream) SendHeader(md metadata.MD) error { return nil }
39+
func (m *mockStream) SetTrailer(md metadata.MD) {}
40+
func (m *mockStream) SendMsg(_ any) error { return nil }
41+
func (m *mockStream) RecvMsg(_ any) error { return nil }
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package cascade
2+
3+
import (
4+
"context"
5+
"errors"
6+
"testing"
7+
8+
pb "github.com/LumeraProtocol/supernode/gen/supernode/action/cascade"
9+
"github.com/LumeraProtocol/supernode/supernode/services/cascade"
10+
cascademocks "github.com/LumeraProtocol/supernode/supernode/services/cascade/mocks"
11+
12+
"github.com/golang/mock/gomock"
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestRegister_Success(t *testing.T) {
17+
ctrl := gomock.NewController(t)
18+
defer ctrl.Finish()
19+
20+
mockTask := cascademocks.NewMockRegistrationTaskService(ctrl)
21+
mockFactory := cascademocks.NewMockTaskFactory(ctrl)
22+
23+
// Expect Register to be called with any input, respond via callback
24+
mockTask.EXPECT().Register(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
25+
func(ctx context.Context, req *cascade.RegisterRequest, send func(*cascade.RegisterResponse) error) error {
26+
return send(&cascade.RegisterResponse{
27+
EventType: 1,
28+
Message: "registration successful",
29+
TxHash: "tx123",
30+
})
31+
},
32+
).Times(1)
33+
34+
mockFactory.EXPECT().NewCascadeRegistrationTask().Return(mockTask).Times(1)
35+
36+
server := NewCascadeActionServer(mockFactory)
37+
38+
stream := &mockStream{
39+
ctx: context.Background(),
40+
request: []*pb.RegisterRequest{
41+
{RequestType: &pb.RegisterRequest_Chunk{Chunk: &pb.DataChunk{Data: []byte("abc123")}}},
42+
{RequestType: &pb.RegisterRequest_Metadata{
43+
Metadata: &pb.Metadata{TaskId: "t1", ActionId: "a1"},
44+
}},
45+
},
46+
}
47+
48+
err := server.Register(stream)
49+
assert.NoError(t, err)
50+
assert.Len(t, stream.sent, 1)
51+
assert.Equal(t, "registration successful", stream.sent[0].Message)
52+
assert.Equal(t, "tx123", stream.sent[0].TxHash)
53+
}
54+
55+
func TestRegister_Error_NoMetadata(t *testing.T) {
56+
ctrl := gomock.NewController(t)
57+
defer ctrl.Finish()
58+
59+
mockFactory := cascademocks.NewMockTaskFactory(ctrl)
60+
server := NewCascadeActionServer(mockFactory)
61+
62+
stream := &mockStream{
63+
ctx: context.Background(),
64+
request: []*pb.RegisterRequest{
65+
{RequestType: &pb.RegisterRequest_Chunk{Chunk: &pb.DataChunk{Data: []byte("abc123")}}},
66+
},
67+
}
68+
69+
err := server.Register(stream)
70+
assert.EqualError(t, err, "no metadata received")
71+
}
72+
73+
func TestRegister_Error_TaskFails(t *testing.T) {
74+
ctrl := gomock.NewController(t)
75+
defer ctrl.Finish()
76+
77+
mockTask := cascademocks.NewMockRegistrationTaskService(ctrl)
78+
mockFactory := cascademocks.NewMockTaskFactory(ctrl)
79+
80+
mockTask.EXPECT().Register(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("task failed")).Times(1)
81+
mockFactory.EXPECT().NewCascadeRegistrationTask().Return(mockTask).Times(1)
82+
83+
server := NewCascadeActionServer(mockFactory)
84+
85+
stream := &mockStream{
86+
ctx: context.Background(),
87+
request: []*pb.RegisterRequest{
88+
{RequestType: &pb.RegisterRequest_Chunk{Chunk: &pb.DataChunk{Data: []byte("abc123")}}},
89+
{RequestType: &pb.RegisterRequest_Metadata{
90+
Metadata: &pb.Metadata{TaskId: "t1", ActionId: "a1"},
91+
}},
92+
},
93+
}
94+
95+
err := server.Register(stream)
96+
assert.EqualError(t, err, "registration failed: task failed")
97+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package server
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestNewConfig_Defaults(t *testing.T) {
10+
cfg := NewConfig()
11+
12+
assert.NotNil(t, cfg)
13+
assert.Equal(t, "0.0.0.0", cfg.ListenAddresses, "default listen address should be 0.0.0.0")
14+
assert.Equal(t, 4444, cfg.Port, "default port should be 4444")
15+
assert.Equal(t, "", cfg.Identity, "default identity should be empty")
16+
}

0 commit comments

Comments
 (0)