Skip to content

Commit

Permalink
improve tests coverage & ensure subkey naming is consistent
Browse files Browse the repository at this point in the history
Signed-off-by: Dmytro Haidashenko <[email protected]>
  • Loading branch information
dhaidashenko committed Dec 20, 2024
1 parent c062cfd commit dba74ae
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 71 deletions.
6 changes: 3 additions & 3 deletions pkg/solana/logpoller/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type Filter struct {
EventSig []byte
StartingBlock int64
EventIDL string
SubKeyPaths SubKeyPaths
SubkeyPaths SubkeyPaths
Retention time.Duration
MaxLogsKept int64
}
Expand All @@ -26,10 +26,10 @@ type Log struct {
LogIndex int64
BlockHash Hash
BlockNumber int64
BLockTimestamp time.Time
BlockTimestamp time.Time
Address PublicKey
EventSig []byte
SubKeyValues pq.ByteaArray
SubkeyValues pq.ByteaArray
TxHash Signature
Data []byte
CreatedAt time.Time
Expand Down
10 changes: 5 additions & 5 deletions pkg/solana/logpoller/orm.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (o *DSORM) InsertFilter(ctx context.Context, filter Filter) (id int64, err
withEventSig(filter.EventSig).
withStartingBlock(filter.StartingBlock).
withEventIDL(filter.EventIDL).
withSubKeyPaths(filter.SubKeyPaths).
withSubkeyPaths(filter.SubkeyPaths).
toArgs()
if err != nil {
return 0, err
Expand All @@ -56,9 +56,9 @@ func (o *DSORM) InsertFilter(ctx context.Context, filter Filter) (id int64, err
// https://github.com/jmoiron/sqlx/issues/91, https://github.com/jmoiron/sqlx/issues/428
query := `
INSERT INTO solana.log_poller_filters
(chain_id, name, address, event_name, event_sig, starting_block, event_idl, sub_key_paths, retention, max_logs_kept)
VALUES (:chain_id, :name, :address, :event_name, :event_sig, :starting_block, :event_idl, :sub_key_paths, :retention, :max_logs_kept)
ON CONFLICT (solana.f_log_poller_filter_hash(name, chain_id, address, event_sig, sub_key_paths))
(chain_id, name, address, event_name, event_sig, starting_block, event_idl, subkey_paths, retention, max_logs_kept)
VALUES (:chain_id, :name, :address, :event_name, :event_sig, :starting_block, :event_idl, :subkey_paths, :retention, :max_logs_kept)
ON CONFLICT (solana.f_log_poller_filter_hash(name, chain_id, address, event_sig, subkey_paths))
DO UPDATE SET retention=:retention ::::BIGINT, max_logs_kept=:max_logs_kept ::::NUMERIC, starting_block=:starting_block ::::NUMERIC
RETURNING id;`

Expand All @@ -74,7 +74,7 @@ func (o *DSORM) InsertFilter(ctx context.Context, filter Filter) (id int64, err

// GetFilterByID returns filter by ID
func (o *DSORM) GetFilterByID(ctx context.Context, id int64) (Filter, error) {
query := `SELECT id, name, address, event_name, event_sig, starting_block, event_idl, sub_key_paths, retention, max_logs_kept
query := `SELECT id, name, address, event_name, event_sig, starting_block, event_idl, subkey_paths, retention, max_logs_kept
FROM solana.log_poller_filters WHERE id = $1`
var result Filter
err := o.ds.GetContext(ctx, &result, query, id)
Expand Down
146 changes: 94 additions & 52 deletions pkg/solana/logpoller/orm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,57 +30,94 @@ func TestLogPollerFilters(t *testing.T) {
privateKey, err := solana.NewRandomPrivateKey()
require.NoError(t, err)
pubKey := privateKey.PublicKey()
filters := []Filter{
{
Name: "happy path",
Address: PublicKey(pubKey),
EventName: "event",
EventSig: []byte{1, 2, 3},
StartingBlock: 1,
EventIDL: "{}",
SubKeyPaths: SubKeyPaths([][]string{{"a", "b"}, {"c"}}),
Retention: 1000,
MaxLogsKept: 3,
},
{
Name: "empty sub key paths",
Address: PublicKey(pubKey),
EventName: "event",
EventSig: []byte{1, 2, 3},
StartingBlock: 1,
EventIDL: "{}",
SubKeyPaths: SubKeyPaths([][]string{}),
Retention: 1000,
MaxLogsKept: 3,
},
{
Name: "nil sub key paths",
Address: PublicKey(pubKey),
EventName: "event",
EventSig: []byte{1, 2, 3},
StartingBlock: 1,
EventIDL: "{}",
SubKeyPaths: nil,
Retention: 1000,
MaxLogsKept: 3,
},
}
t.Run("Ensure all fields are readable/writable", func(t *testing.T) {
filters := []Filter{
{
Name: "happy path",
Address: PublicKey(pubKey),
EventName: "event",
EventSig: []byte{1, 2, 3},
StartingBlock: 1,
EventIDL: "{}",
SubkeyPaths: SubkeyPaths([][]string{{"a", "b"}, {"c"}}),
Retention: 1000,
MaxLogsKept: 3,
},
{
Name: "empty sub key paths",
Address: PublicKey(pubKey),
EventName: "event",
EventSig: []byte{1, 2, 3},
StartingBlock: 1,
EventIDL: "{}",
SubkeyPaths: SubkeyPaths([][]string{}),
Retention: 1000,
MaxLogsKept: 3,
},
{
Name: "nil sub key paths",
Address: PublicKey(pubKey),
EventName: "event",
EventSig: []byte{1, 2, 3},
StartingBlock: 1,
EventIDL: "{}",
SubkeyPaths: nil,
Retention: 1000,
MaxLogsKept: 3,
},
}

for _, filter := range filters {
t.Run("Save filter: "+filter.Name, func(t *testing.T) {
ctx := tests.Context(t)
id, err := orm.InsertFilter(ctx, filter)
require.NoError(t, err)
filter.ID = id
dbFilter, err := orm.GetFilterByID(ctx, id)
require.NoError(t, err)
require.Equal(t, filter, dbFilter)
for _, filter := range filters {
t.Run("Read/write filter: "+filter.Name, func(t *testing.T) {
ctx := tests.Context(t)
id, err := orm.InsertFilter(ctx, filter)
require.NoError(t, err)
filter.ID = id
dbFilter, err := orm.GetFilterByID(ctx, id)
require.NoError(t, err)
require.Equal(t, filter, dbFilter)

// subsequent insert of the same filter won't produce new db row
secondID, err := orm.InsertFilter(ctx, filter)
require.NoError(t, err)
require.Equal(t, secondID, id)
})
// subsequent insert of the same filter won't produce new db row
secondID, err := orm.InsertFilter(ctx, filter)
require.NoError(t, err)
require.Equal(t, secondID, id)
})
}
})
t.Run("Subsequent insert does not produce a new id", func(t *testing.T) {
filter := newRandomFilter(t)
ctx := tests.Context(t)
firstID, err := orm.InsertFilter(ctx, filter)
require.NoError(t, err)
secondID, err := orm.InsertFilter(ctx, filter)
require.NoError(t, err)
require.Equal(t, firstID, secondID)
})
t.Run("Returns and error if name is not unique", func(t *testing.T) {
filter := newRandomFilter(t)
ctx := tests.Context(t)
_, err = orm.InsertFilter(ctx, filter)
require.NoError(t, err)
filter.EventSig = []byte(uuid.NewString())
_, err = orm.InsertFilter(ctx, filter)
require.EqualError(t, err, `ERROR: duplicate key value violates unique constraint "solana_log_poller_filter_name" (SQLSTATE 23505)`)
})
}

func newRandomFilter(t *testing.T) Filter {
privateKey, err := solana.NewRandomPrivateKey()
require.NoError(t, err)
pubKey := privateKey.PublicKey()
return Filter{
Name: uuid.NewString(),
Address: PublicKey(pubKey),
EventName: "event",
EventSig: []byte{1, 2, 3},
StartingBlock: 1,
EventIDL: "{}",
SubkeyPaths: [][]string{{"a", "b"}, {"c"}},
Retention: 1000,
MaxLogsKept: 3,
}
}

Expand All @@ -105,7 +142,7 @@ func TestLogPollerLogs(t *testing.T) {
EventSig: []byte{1, 2, 3},
StartingBlock: 1,
EventIDL: "{}",
SubKeyPaths: [][]string{{"a", "b"}, {"c"}},
SubkeyPaths: [][]string{{"a", "b"}, {"c"}},
Retention: 1000,
MaxLogsKept: 3,
})
Expand All @@ -119,17 +156,22 @@ func TestLogPollerLogs(t *testing.T) {
LogIndex: 1,
BlockHash: Hash(pubKey),
BlockNumber: 10,
BLockTimestamp: time.Now(),
BlockTimestamp: time.Unix(1731590113, 0),
Address: PublicKey(pubKey),
EventSig: []byte{3, 2, 1},
SubKeyValues: pq.ByteaArray([][]byte{{3, 2, 1}, {1}, {1, 2}, pubKey.Bytes()}),
SubkeyValues: pq.ByteaArray([][]byte{{3, 2, 1}, {1}, {1, 2}, pubKey.Bytes()}),
TxHash: Signature(signature),
Data: data,
}
err = orm.InsertLogs(ctx, []Log{log})
require.NoError(t, err)
// insert of the same Log should not produce two instances
err = orm.InsertLogs(ctx, []Log{log})
require.NoError(t, err)
dbLogs, err := orm.SelectLogs(ctx, 0, 100, log.Address, log.EventSig)
require.NoError(t, err)
require.Len(t, dbLogs, 1)
log.ID = dbLogs[0].ID
log.CreatedAt = dbLogs[0].CreatedAt
require.Equal(t, log, dbLogs[0])
}
12 changes: 6 additions & 6 deletions pkg/solana/logpoller/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ func (q *queryArgs) withEventIDL(eventIDL string) *queryArgs {
return q.withField("event_idl", eventIDL)
}

// withSubKeyPaths sets the SubKeyPaths field in queryArgs.
func (q *queryArgs) withSubKeyPaths(subKeyPaths [][]string) *queryArgs {
return q.withField("sub_key_paths", subKeyPaths)
// withSubkeyPaths sets the SubkeyPaths field in queryArgs.
func (q *queryArgs) withSubkeyPaths(subkeyPaths [][]string) *queryArgs {
return q.withField("subkey_paths", subkeyPaths)
}

// withRetention sets the Retention field in queryArgs.
Expand Down Expand Up @@ -143,9 +143,9 @@ func (q *queryArgs) withBlockTimestamp(blockTimestamp time.Time) *queryArgs {
return q.withField("block_timestamp", blockTimestamp)
}

// withSubKeyValues sets the SubKeyValues field in Log.
func (q *queryArgs) withSubKeyValues(subKeyValues pq.ByteaArray) *queryArgs {
return q.withField("sub_key_values", subKeyValues)
// withSubkeyValues sets the SubkeyValues field in Log.
func (q *queryArgs) withSubkeyValues(subkeyValues pq.ByteaArray) *queryArgs {
return q.withField("subkey_values", subkeyValues)
}

// withTxHash sets the TxHash field in Log.
Expand Down
10 changes: 5 additions & 5 deletions pkg/solana/logpoller/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,21 +68,21 @@ func scanFixedLengthArray(name string, maxLength int, src interface{}, dest []by
return nil
}

type SubKeyPaths [][]string
type SubkeyPaths [][]string

func (k SubKeyPaths) Value() (driver.Value, error) {
func (k SubkeyPaths) Value() (driver.Value, error) {
return json.Marshal([][]string(k))
}

func (p *SubKeyPaths) Scan(src interface{}) error {
func (p *SubkeyPaths) Scan(src interface{}) error {
var bSrc []byte
switch src := src.(type) {
case string:
bSrc = []byte(src)
case []byte:
bSrc = src
default:
return fmt.Errorf("can't scan %T into SubKeyPaths", src)
return fmt.Errorf("can't scan %T into SubkeyPaths", src)
}

if len(bSrc) == 0 || string(bSrc) == "null" {
Expand All @@ -91,7 +91,7 @@ func (p *SubKeyPaths) Scan(src interface{}) error {

err := json.Unmarshal(bSrc, p)
if err != nil {
return fmt.Errorf("failed to scan %v into SubKeyPaths: %w", string(bSrc), err)
return fmt.Errorf("failed to scan %v into SubkeyPaths: %w", string(bSrc), err)
}

return nil
Expand Down

0 comments on commit dba74ae

Please sign in to comment.