Skip to content

Commit

Permalink
Merge pull request #96 from facundomedica/facu/add-tsbefore-after
Browse files Browse the repository at this point in the history
feat: re-add GetTimestampAfter and GetTimestampBefore
  • Loading branch information
themandalore authored Mar 18, 2024
2 parents 54d3cfe + da876e8 commit b98c9c9
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 0 deletions.
38 changes: 38 additions & 0 deletions x/oracle/keeper/aggregate.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,41 @@ func (k Keeper) GetCurrentValueForQueryId(ctx context.Context, queryId []byte) *

return mostRecent
}

func (k Keeper) GetTimestampBefore(ctx sdk.Context, queryId []byte, timestamp time.Time) (time.Time, error) {
rng := collections.NewPrefixedPairRange[[]byte, int64](queryId).EndInclusive(timestamp.Unix()).Descending()
var mostRecent int64
err := k.Aggregates.Walk(ctx, rng, func(key collections.Pair[[]byte, int64], value types.Aggregate) (stop bool, err error) {
mostRecent = key.K2()
return true, nil
})

if err != nil {
panic(err)
}

if mostRecent == 0 {
return time.Time{}, fmt.Errorf("no data before timestamp %v available for query id %s", timestamp, hex.EncodeToString(queryId))
}

return time.Unix(mostRecent, 0), nil
}

func (k Keeper) GetTimestampAfter(ctx sdk.Context, queryId []byte, timestamp time.Time) (time.Time, error) {
rng := collections.NewPrefixedPairRange[[]byte, int64](queryId).StartInclusive(timestamp.Unix())
var mostRecent int64
err := k.Aggregates.Walk(ctx, rng, func(key collections.Pair[[]byte, int64], value types.Aggregate) (stop bool, err error) {
mostRecent = key.K2()
return true, nil
})

if err != nil {
panic(err)
}

if mostRecent == 0 {
return time.Time{}, fmt.Errorf("no data before timestamp %v available for query id %s", timestamp, hex.EncodeToString(queryId))
}

return time.Unix(mostRecent, 0), nil
}
165 changes: 165 additions & 0 deletions x/oracle/keeper/aggregate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package keeper_test

import (
"testing"
"time"

"cosmossdk.io/collections"
"github.com/tellor-io/layer/x/oracle/types"
)

func (s *KeeperTestSuite) TestFindTimestampBefore() {
testCases := []struct {
name string
timestamps []time.Time
target time.Time
expectedTs time.Time
}{
{
name: "Empty slice",
timestamps: []time.Time{},
target: time.Unix(100, 0),
expectedTs: time.Time{},
},
{
name: "Single timestamp before target",
timestamps: []time.Time{time.Unix(50, 0)},
target: time.Unix(100, 0),
expectedTs: time.Unix(50, 0),
},
{
name: "Single timestamp after target",
timestamps: []time.Time{time.Unix(150, 0)},
target: time.Unix(100, 0),
expectedTs: time.Time{},
},
{
name: "Multiple timestamps, target present",
timestamps: []time.Time{time.Unix(50, 0), time.Unix(100, 0), time.Unix(150, 0)},
target: time.Unix(100, 0),
expectedTs: time.Unix(100, 0),
},
{
name: "Multiple timestamps, target not present",
timestamps: []time.Time{time.Unix(50, 0), time.Unix(70, 0), time.Unix(90, 0), time.Unix(110, 0), time.Unix(130, 0)},
target: time.Unix(100, 0),
expectedTs: time.Unix(90, 0),
},
{
name: "Multiple timestamps, target before all",
timestamps: []time.Time{time.Unix(200, 0), time.Unix(300, 0), time.Unix(400, 0)},
target: time.Unix(100, 0),
expectedTs: time.Time{},
},
{
name: "Multiple timestamps, target after all",
timestamps: []time.Time{time.Unix(10, 0), time.Unix(20, 0), time.Unix(40, 0)},
target: time.Unix(100, 0),
expectedTs: time.Unix(40, 0),
},
}

for _, tc := range testCases {
s.T().Run(tc.name, func(t *testing.T) {
s.SetupTest()
queryId := []byte("test")
for _, v := range tc.timestamps {
err := s.oracleKeeper.Aggregates.Set(
s.ctx,
collections.Join(queryId, v.Unix()),
types.Aggregate{},
)
s.Require().NoError(err)
}

ts, err := s.oracleKeeper.GetTimestampBefore(s.ctx, queryId, tc.target)
if ts.IsZero() {
s.Require().Error(err)
} else {
s.Require().NoError(err)
}

if ts != tc.expectedTs {
t.Errorf("Test '%s' failed: expected %v, got %v", tc.name, tc.expectedTs, ts)
}
})
}
}

func (s *KeeperTestSuite) TestFindTimestampAfter() {
testCases := []struct {
name string
timestamps []time.Time
target time.Time
expectedTs time.Time
}{
{
name: "Empty slice",
timestamps: []time.Time{},
target: time.Unix(100, 0),
expectedTs: time.Time{},
},
{
name: "Single timestamp after target",
timestamps: []time.Time{time.Unix(50, 0)},
target: time.Unix(25, 0),
expectedTs: time.Unix(50, 0),
},
{
name: "Single timestamp before target",
timestamps: []time.Time{time.Unix(150, 0)},
target: time.Unix(200, 0),
expectedTs: time.Time{},
},
{
name: "Multiple timestamps, target present",
timestamps: []time.Time{time.Unix(50, 0), time.Unix(100, 0), time.Unix(150, 0)},
target: time.Unix(100, 0),
expectedTs: time.Unix(100, 0),
},
{
name: "Multiple timestamps, target not present",
timestamps: []time.Time{time.Unix(50, 0), time.Unix(70, 0), time.Unix(90, 0), time.Unix(110, 0), time.Unix(130, 0)},
target: time.Unix(100, 0),
expectedTs: time.Unix(110, 0),
},
{
name: "Multiple timestamps, target before all",
timestamps: []time.Time{time.Unix(200, 0), time.Unix(300, 0), time.Unix(400, 0)},
target: time.Unix(100, 0),
expectedTs: time.Unix(200, 0),
},
{
name: "Multiple timestamps, target after all",
timestamps: []time.Time{time.Unix(10, 0), time.Unix(20, 0), time.Unix(40, 0)},
target: time.Unix(100, 0),
expectedTs: time.Time{},
},
}

for _, tc := range testCases {
s.T().Run(tc.name, func(t *testing.T) {
s.SetupTest()
queryId := []byte("test")
for _, v := range tc.timestamps {
err := s.oracleKeeper.Aggregates.Set(
s.ctx,
collections.Join(queryId, v.Unix()),
types.Aggregate{},
)
s.Require().NoError(err)
}

ts, err := s.oracleKeeper.GetTimestampAfter(s.ctx, queryId, tc.target)
if ts.IsZero() {
s.Require().Error(err)
} else {
s.Require().NoError(err)
}

if ts != tc.expectedTs {
t.Errorf("Test '%s' failed: expected %v, got %v", tc.name, tc.expectedTs, ts)
}
})
}
}

0 comments on commit b98c9c9

Please sign in to comment.