diff --git a/bun/bunpaginate/pagination_column.go b/bun/bunpaginate/pagination_column.go index a168b1dd..52ab21ba 100644 --- a/bun/bunpaginate/pagination_column.go +++ b/bun/bunpaginate/pagination_column.go @@ -112,18 +112,32 @@ func findPaginationFieldPath(v any, paginationColumn string) []reflect.StructFie typeOfT := reflect.TypeOf(v) for i := 0; i < typeOfT.NumField(); i++ { field := typeOfT.Field(i) - switch field.Type.Kind() { + fieldType := field.Type + + // If the field is a pointer, we unreference it to target the concrete type + // For example: + // type Object struct { + // *AnotherObject + // } + for { + if field.Type.Kind() == reflect.Ptr { + fieldType = field.Type.Elem() + } + break + } + + switch fieldType.Kind() { case reflect.Struct: - if field.Type.AssignableTo(reflect.TypeOf(time.Time{})) || - field.Type.AssignableTo(reflect.TypeOf(&time.Time{})) || - field.Type.AssignableTo(reflect.TypeOf(libtime.Time{})) || - field.Type.AssignableTo(reflect.TypeOf(&libtime.Time{})) { + if fieldType.AssignableTo(reflect.TypeOf(time.Time{})) || + fieldType.AssignableTo(reflect.TypeOf(libtime.Time{})) || + fieldType.AssignableTo(reflect.TypeOf(big.Int{})) || + fieldType.AssignableTo(reflect.TypeOf(BigInt{})) { if fields := checkTag(field, paginationColumn); len(fields) > 0 { return fields } } else { - fields := findPaginationFieldPath(reflect.New(field.Type).Elem().Interface(), paginationColumn) + fields := findPaginationFieldPath(reflect.New(fieldType).Elem().Interface(), paginationColumn) if len(fields) > 0 { return fields } @@ -163,8 +177,12 @@ func findPaginationField(v any, fields ...reflect.StructField) *big.Int { return big.NewInt(rawPaginationID.UTC().UnixMicro()) case *BigInt: return (*big.Int)(rawPaginationID) + case BigInt: + return (*big.Int)(&rawPaginationID) case *big.Int: return rawPaginationID + case big.Int: + return &rawPaginationID case int64: return big.NewInt(rawPaginationID) case int: diff --git a/bun/bunpaginate/pagination_column_test.go b/bun/bunpaginate/pagination_column_test.go index 1a5ee2df..c4c2bbc5 100644 --- a/bun/bunpaginate/pagination_column_test.go +++ b/bun/bunpaginate/pagination_column_test.go @@ -9,7 +9,7 @@ import ( "github.com/uptrace/bun" "github.com/formancehq/go-libs/bun/bunconnect" - bunpaginate2 "github.com/formancehq/go-libs/bun/bunpaginate" + "github.com/formancehq/go-libs/bun/bunpaginate" "github.com/formancehq/go-libs/logging" "github.com/stretchr/testify/require" @@ -38,14 +38,14 @@ func TestColumnPagination(t *testing.T) { require.NoError(t, err) type model struct { - ID *bunpaginate2.BigInt `bun:"id,type:numeric"` - Pair bool `bun:"pair"` + ID *bunpaginate.BigInt `bun:"id,type:numeric"` + Pair bool `bun:"pair"` } models := make([]model, 0) for i := 0; i < 100; i++ { models = append(models, model{ - ID: (*bunpaginate2.BigInt)(big.NewInt(int64(i))), + ID: (*bunpaginate.BigInt)(big.NewInt(int64(i))), Pair: i%2 == 0, }) } @@ -57,67 +57,67 @@ func TestColumnPagination(t *testing.T) { type testCase struct { name string - query bunpaginate2.ColumnPaginatedQuery[bool] - expectedNext *bunpaginate2.ColumnPaginatedQuery[bool] - expectedPrevious *bunpaginate2.ColumnPaginatedQuery[bool] + query bunpaginate.ColumnPaginatedQuery[bool] + expectedNext *bunpaginate.ColumnPaginatedQuery[bool] + expectedPrevious *bunpaginate.ColumnPaginatedQuery[bool] expectedNumberOfItems int64 } testCases := []testCase{ { name: "asc first page", - query: bunpaginate2.ColumnPaginatedQuery[bool]{ + query: bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", - Order: bunpaginate2.OrderAsc, + Order: bunpaginate.OrderAsc, }, - expectedNext: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedNext: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", PaginationID: big.NewInt(int64(10)), - Order: bunpaginate2.OrderAsc, + Order: bunpaginate.OrderAsc, Bottom: big.NewInt(int64(0)), }, expectedNumberOfItems: 10, }, { name: "asc second page using next cursor", - query: bunpaginate2.ColumnPaginatedQuery[bool]{ + query: bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", PaginationID: big.NewInt(int64(10)), - Order: bunpaginate2.OrderAsc, + Order: bunpaginate.OrderAsc, Bottom: big.NewInt(int64(0)), }, - expectedPrevious: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedPrevious: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", - Order: bunpaginate2.OrderAsc, + Order: bunpaginate.OrderAsc, Bottom: big.NewInt(int64(0)), PaginationID: big.NewInt(int64(10)), Reverse: true, }, - expectedNext: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedNext: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", PaginationID: big.NewInt(int64(20)), - Order: bunpaginate2.OrderAsc, + Order: bunpaginate.OrderAsc, Bottom: big.NewInt(int64(0)), }, expectedNumberOfItems: 10, }, { name: "asc last page using next cursor", - query: bunpaginate2.ColumnPaginatedQuery[bool]{ + query: bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", PaginationID: big.NewInt(int64(90)), - Order: bunpaginate2.OrderAsc, + Order: bunpaginate.OrderAsc, Bottom: big.NewInt(int64(0)), }, - expectedPrevious: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedPrevious: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", - Order: bunpaginate2.OrderAsc, + Order: bunpaginate.OrderAsc, PaginationID: big.NewInt(int64(90)), Bottom: big.NewInt(int64(0)), Reverse: true, @@ -126,116 +126,116 @@ func TestColumnPagination(t *testing.T) { }, { name: "desc first page", - query: bunpaginate2.ColumnPaginatedQuery[bool]{ + query: bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", - Order: bunpaginate2.OrderDesc, + Order: bunpaginate.OrderDesc, }, - expectedNext: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedNext: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Bottom: big.NewInt(int64(99)), Column: "id", PaginationID: big.NewInt(int64(89)), - Order: bunpaginate2.OrderDesc, + Order: bunpaginate.OrderDesc, }, expectedNumberOfItems: 10, }, { name: "desc second page using next cursor", - query: bunpaginate2.ColumnPaginatedQuery[bool]{ + query: bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Bottom: big.NewInt(int64(99)), Column: "id", PaginationID: big.NewInt(int64(89)), - Order: bunpaginate2.OrderDesc, + Order: bunpaginate.OrderDesc, }, - expectedPrevious: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedPrevious: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Bottom: big.NewInt(int64(99)), Column: "id", PaginationID: big.NewInt(int64(89)), - Order: bunpaginate2.OrderDesc, + Order: bunpaginate.OrderDesc, Reverse: true, }, - expectedNext: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedNext: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Bottom: big.NewInt(int64(99)), Column: "id", PaginationID: big.NewInt(int64(79)), - Order: bunpaginate2.OrderDesc, + Order: bunpaginate.OrderDesc, }, expectedNumberOfItems: 10, }, { name: "desc last page using next cursor", - query: bunpaginate2.ColumnPaginatedQuery[bool]{ + query: bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Bottom: big.NewInt(int64(99)), Column: "id", PaginationID: big.NewInt(int64(9)), - Order: bunpaginate2.OrderDesc, + Order: bunpaginate.OrderDesc, }, - expectedPrevious: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedPrevious: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Bottom: big.NewInt(int64(99)), Column: "id", PaginationID: big.NewInt(int64(9)), - Order: bunpaginate2.OrderDesc, + Order: bunpaginate.OrderDesc, Reverse: true, }, expectedNumberOfItems: 10, }, { name: "asc first page using previous cursor", - query: bunpaginate2.ColumnPaginatedQuery[bool]{ + query: bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Bottom: big.NewInt(int64(0)), Column: "id", PaginationID: big.NewInt(int64(10)), - Order: bunpaginate2.OrderAsc, + Order: bunpaginate.OrderAsc, Reverse: true, }, - expectedNext: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedNext: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Bottom: big.NewInt(int64(0)), Column: "id", PaginationID: big.NewInt(int64(10)), - Order: bunpaginate2.OrderAsc, + Order: bunpaginate.OrderAsc, }, expectedNumberOfItems: 10, }, { name: "desc first page using previous cursor", - query: bunpaginate2.ColumnPaginatedQuery[bool]{ + query: bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Bottom: big.NewInt(int64(99)), Column: "id", PaginationID: big.NewInt(int64(89)), - Order: bunpaginate2.OrderDesc, + Order: bunpaginate.OrderDesc, Reverse: true, }, - expectedNext: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedNext: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Bottom: big.NewInt(int64(99)), Column: "id", PaginationID: big.NewInt(int64(89)), - Order: bunpaginate2.OrderDesc, + Order: bunpaginate.OrderDesc, }, expectedNumberOfItems: 10, }, { name: "asc first page with filter", - query: bunpaginate2.ColumnPaginatedQuery[bool]{ + query: bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", - Order: bunpaginate2.OrderAsc, + Order: bunpaginate.OrderAsc, Options: true, }, - expectedNext: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedNext: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", PaginationID: big.NewInt(int64(20)), - Order: bunpaginate2.OrderAsc, + Order: bunpaginate.OrderAsc, Options: true, Bottom: big.NewInt(int64(0)), }, @@ -243,27 +243,27 @@ func TestColumnPagination(t *testing.T) { }, { name: "asc second page with filter", - query: bunpaginate2.ColumnPaginatedQuery[bool]{ + query: bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", PaginationID: big.NewInt(int64(20)), - Order: bunpaginate2.OrderAsc, + Order: bunpaginate.OrderAsc, Options: true, Bottom: big.NewInt(int64(0)), }, - expectedNext: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedNext: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", PaginationID: big.NewInt(int64(40)), - Order: bunpaginate2.OrderAsc, + Order: bunpaginate.OrderAsc, Options: true, Bottom: big.NewInt(int64(0)), }, - expectedPrevious: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedPrevious: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", PaginationID: big.NewInt(int64(20)), - Order: bunpaginate2.OrderAsc, + Order: bunpaginate.OrderAsc, Options: true, Bottom: big.NewInt(int64(0)), Reverse: true, @@ -272,17 +272,17 @@ func TestColumnPagination(t *testing.T) { }, { name: "desc first page with filter", - query: bunpaginate2.ColumnPaginatedQuery[bool]{ + query: bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", - Order: bunpaginate2.OrderDesc, + Order: bunpaginate.OrderDesc, Options: true, }, - expectedNext: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedNext: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", PaginationID: big.NewInt(int64(78)), - Order: bunpaginate2.OrderDesc, + Order: bunpaginate.OrderDesc, Options: true, Bottom: big.NewInt(int64(98)), }, @@ -290,27 +290,27 @@ func TestColumnPagination(t *testing.T) { }, { name: "desc second page with filter", - query: bunpaginate2.ColumnPaginatedQuery[bool]{ + query: bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", PaginationID: big.NewInt(int64(78)), - Order: bunpaginate2.OrderDesc, + Order: bunpaginate.OrderDesc, Options: true, Bottom: big.NewInt(int64(98)), }, - expectedNext: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedNext: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", PaginationID: big.NewInt(int64(58)), - Order: bunpaginate2.OrderDesc, + Order: bunpaginate.OrderDesc, Options: true, Bottom: big.NewInt(int64(98)), }, - expectedPrevious: &bunpaginate2.ColumnPaginatedQuery[bool]{ + expectedPrevious: &bunpaginate.ColumnPaginatedQuery[bool]{ PageSize: 10, Column: "id", PaginationID: big.NewInt(int64(78)), - Order: bunpaginate2.OrderDesc, + Order: bunpaginate.OrderDesc, Options: true, Bottom: big.NewInt(int64(98)), Reverse: true, @@ -327,7 +327,7 @@ func TestColumnPagination(t *testing.T) { if tc.query.Options { query = query.Where("pair = ?", true) } - cursor, err := bunpaginate2.UsingColumn[bool, model](context.Background(), query, tc.query) + cursor, err := bunpaginate.UsingColumn[bool, model](context.Background(), query, tc.query) require.NoError(t, err) if tc.expectedNext == nil { @@ -335,8 +335,8 @@ func TestColumnPagination(t *testing.T) { } else { require.NotEmpty(t, cursor.Next) - q := bunpaginate2.ColumnPaginatedQuery[bool]{} - require.NoError(t, bunpaginate2.UnmarshalCursor(cursor.Next, &q)) + q := bunpaginate.ColumnPaginatedQuery[bool]{} + require.NoError(t, bunpaginate.UnmarshalCursor(cursor.Next, &q)) require.EqualValues(t, *tc.expectedNext, q) } @@ -345,8 +345,8 @@ func TestColumnPagination(t *testing.T) { } else { require.NotEmpty(t, cursor.Previous) - q := bunpaginate2.ColumnPaginatedQuery[bool]{} - require.NoError(t, bunpaginate2.UnmarshalCursor(cursor.Previous, &q)) + q := bunpaginate.ColumnPaginatedQuery[bool]{} + require.NoError(t, bunpaginate.UnmarshalCursor(cursor.Previous, &q)) require.EqualValues(t, *tc.expectedPrevious, q) } }) diff --git a/testing/migrations/testing.go b/testing/migrations/testing.go new file mode 100644 index 00000000..7fe5fd44 --- /dev/null +++ b/testing/migrations/testing.go @@ -0,0 +1,65 @@ +package migrations + +import ( + "context" + "testing" + + "github.com/formancehq/go-libs/logging" + "github.com/formancehq/go-libs/migrations" + "github.com/stretchr/testify/require" + "github.com/uptrace/bun" +) + +type HookFn func(ctx context.Context, t *testing.T, db bun.IDB) + +type Hook struct { + Before HookFn + After HookFn +} + +type MigrationTest struct { + migrator *migrations.Migrator + hooks map[int][]Hook + db bun.IDB + t *testing.T +} + +func (mt *MigrationTest) Run() { + ctx := logging.TestingContext() + i := 0 + for { + for _, hook := range mt.hooks[i] { + if hook.Before != nil { + hook.Before(ctx, mt.t, mt.db) + } + } + + more, err := mt.migrator.UpByOne(ctx, mt.db) + require.NoError(mt.t, err) + + for _, hook := range mt.hooks[i] { + if hook.After != nil { + hook.After(ctx, mt.t, mt.db) + } + } + + i++ + + if !more { + break + } + } +} + +func (mt *MigrationTest) Append(i int, hook Hook) { + mt.hooks[i] = append(mt.hooks[i], hook) +} + +func NewMigrationTest(t *testing.T, migrator *migrations.Migrator, db bun.IDB) *MigrationTest { + return &MigrationTest{ + migrator: migrator, + hooks: map[int][]Hook{}, + t: t, + db: db, + } +} diff --git a/testing/platform/pgtesting/postgres.go b/testing/platform/pgtesting/postgres.go index 01c4746a..5cee0c1e 100644 --- a/testing/platform/pgtesting/postgres.go +++ b/testing/platform/pgtesting/postgres.go @@ -229,6 +229,7 @@ func CreatePostgresServer(t T, pool *docker.Pool, opts ...Option) *PostgresServe "-c", "shared_preload_libraries=auto_explain,pg_stat_statements", "-c", "log_lock_waits=on", "-c", "log_min_messages=info", + "-c", "max_connections=100", }, }, CheckFn: func(ctx context.Context, resource *dockertest.Resource) error {