package adapter_test import ( "cmp" "context" "fmt" "slices" "testing" "time" "gitea.dwysokinski.me/twhelp/core/internal/domain" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func testTribeSnapshotRepository(t *testing.T, newRepos func(t *testing.T) repositories) { t.Helper() ctx := context.Background() t.Run("Create", func(t *testing.T) { t.Parallel() const dateFormat = "2006-01-02" repos := newRepos(t) assertCreated := func(t *testing.T, params []domain.CreateTribeSnapshotParams) { t.Helper() require.NotEmpty(t, params) res, err := repos.tribeSnapshot.List(ctx, domain.NewListTribeSnapshotsParams()) tribeSnapshots := res.TribeSnapshots() require.NoError(t, err) for i, p := range params { date := p.Date().Format(dateFormat) idx := slices.IndexFunc(tribeSnapshots, func(ts domain.TribeSnapshot) bool { return ts.ServerKey() == p.ServerKey() && ts.TribeID() == p.TribeID() && ts.Date().Format(dateFormat) == date }) require.GreaterOrEqualf(t, idx, 0, "params[%d] not found", i) tribeSnapshot := tribeSnapshots[idx] assert.Equalf(t, p.TribeID(), tribeSnapshot.TribeID(), "params[%d]", i) assert.Equalf(t, p.ServerKey(), tribeSnapshot.ServerKey(), "params[%d]", i) assert.Equalf(t, p.NumMembers(), tribeSnapshot.NumMembers(), "params[%d]", i) assert.Equalf(t, p.NumVillages(), tribeSnapshot.NumVillages(), "params[%d]", i) assert.Equalf(t, p.Points(), tribeSnapshot.Points(), "params[%d]", i) assert.Equalf(t, p.AllPoints(), tribeSnapshot.AllPoints(), "params[%d]", i) assert.Equalf(t, p.Rank(), tribeSnapshot.Rank(), "params[%d]", i) assert.Equalf(t, p.OD(), tribeSnapshot.OD(), "params[%d]", i) assert.InDeltaf(t, p.Dominance(), tribeSnapshot.Dominance(), 0.001, "params[%d]", i) assert.Equalf(t, date, tribeSnapshot.Date().Format(dateFormat), "params[%d]", i) assert.WithinDurationf(t, time.Now(), tribeSnapshot.CreatedAt(), time.Minute, "params[%d]", i) } } assertNoDuplicates := func(t *testing.T, params []domain.CreateTribeSnapshotParams) { t.Helper() require.NotEmpty(t, params) res, err := repos.tribeSnapshot.List(ctx, domain.NewListTribeSnapshotsParams()) require.NoError(t, err) tribeSnapshots := res.TribeSnapshots() m := make(map[string][]int) for _, p := range params { key := fmt.Sprintf("%s-%d-%s", p.ServerKey(), p.TribeID(), p.Date().Format(dateFormat)) for i, ts := range tribeSnapshots { if ts.ServerKey() == p.ServerKey() && ts.TribeID() == p.TribeID() && ts.Date().Equal(p.Date()) { m[key] = append(m[key], i) } } } for key, indexes := range m { assert.Len(t, indexes, 1, key) } } t.Run("OK", func(t *testing.T) { t.Parallel() listTribesParams := domain.NewListTribesParams() require.NoError(t, listTribesParams.SetDeleted(domain.NullBool{ V: false, Valid: true, })) res, err := repos.tribe.List(ctx, listTribesParams) require.NoError(t, err) tribes := res.Tribes() require.NotEmpty(t, tribes) date := time.Now() createParams, err := domain.NewCreateTribeSnapshotParams(tribes, date) require.NoError(t, err) require.NoError(t, repos.tribeSnapshot.Create(ctx, createParams...)) assertCreated(t, createParams) require.NoError(t, repos.tribeSnapshot.Create(ctx, createParams...)) assertNoDuplicates(t, createParams) }) t.Run("OK: len(params) == 0", func(t *testing.T) { t.Parallel() require.NoError(t, repos.tribeSnapshot.Create(ctx)) }) }) t.Run("List & ListWithRelations", func(t *testing.T) { t.Parallel() repos := newRepos(t) tests := []struct { name string params func(t *testing.T) domain.ListTribeSnapshotsParams assertResult func(t *testing.T, params domain.ListTribeSnapshotsParams, res domain.ListTribeSnapshotsResult) assertError func(t *testing.T, err error) }{ { name: "OK: default params", params: func(t *testing.T) domain.ListTribeSnapshotsParams { t.Helper() return domain.NewListTribeSnapshotsParams() }, assertResult: func( t *testing.T, _ domain.ListTribeSnapshotsParams, res domain.ListTribeSnapshotsResult, ) { t.Helper() tribeSnapshots := res.TribeSnapshots() assert.NotEmpty(t, tribeSnapshots) assert.True(t, slices.IsSortedFunc(tribeSnapshots, func(a, b domain.TribeSnapshot) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), a.Date().Compare(b.Date()), cmp.Compare(a.ID(), b.ID()), ) })) assert.False(t, res.Self().IsZero()) assert.True(t, res.Next().IsZero()) }, }, { name: "OK: sort=[serverKey DESC, date DESC, id DESC]", params: func(t *testing.T) domain.ListTribeSnapshotsParams { t.Helper() params := domain.NewListTribeSnapshotsParams() require.NoError(t, params.SetSort([]domain.TribeSnapshotSort{ domain.TribeSnapshotSortServerKeyDESC, domain.TribeSnapshotSortDateDESC, domain.TribeSnapshotSortIDDESC, })) return params }, assertResult: func( t *testing.T, _ domain.ListTribeSnapshotsParams, res domain.ListTribeSnapshotsResult, ) { t.Helper() tribeSnapshots := res.TribeSnapshots() assert.NotEmpty(t, tribeSnapshots) assert.True(t, slices.IsSortedFunc(tribeSnapshots, func(a, b domain.TribeSnapshot) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), a.Date().Compare(b.Date()), cmp.Compare(a.ID(), b.ID()), ) * -1 })) }, }, { name: "OK: sort=[id ASC]", params: func(t *testing.T) domain.ListTribeSnapshotsParams { t.Helper() params := domain.NewListTribeSnapshotsParams() require.NoError(t, params.SetSort([]domain.TribeSnapshotSort{ domain.TribeSnapshotSortIDASC, })) return params }, assertResult: func( t *testing.T, _ domain.ListTribeSnapshotsParams, res domain.ListTribeSnapshotsResult, ) { t.Helper() tribeSnapshots := res.TribeSnapshots() assert.NotEmpty(t, tribeSnapshots) assert.True(t, slices.IsSortedFunc(tribeSnapshots, func(a, b domain.TribeSnapshot) int { return cmp.Compare(a.ID(), b.ID()) })) }, }, { name: "OK: sort=[id DESC]", params: func(t *testing.T) domain.ListTribeSnapshotsParams { t.Helper() params := domain.NewListTribeSnapshotsParams() require.NoError(t, params.SetSort([]domain.TribeSnapshotSort{ domain.TribeSnapshotSortIDDESC, })) return params }, assertResult: func( t *testing.T, _ domain.ListTribeSnapshotsParams, res domain.ListTribeSnapshotsResult, ) { t.Helper() tribeSnapshots := res.TribeSnapshots() assert.NotEmpty(t, tribeSnapshots) assert.True(t, slices.IsSortedFunc(tribeSnapshots, func(a, b domain.TribeSnapshot) int { return cmp.Compare(a.ID(), b.ID()) * -1 })) }, }, { name: "OK: serverKeys", params: func(t *testing.T) domain.ListTribeSnapshotsParams { t.Helper() params := domain.NewListTribeSnapshotsParams() res, err := repos.tribeSnapshot.List(ctx, params) require.NoError(t, err) require.NotEmpty(t, res.TribeSnapshots()) randTribeSnapshot := res.TribeSnapshots()[0] require.NoError(t, params.SetServerKeys([]string{randTribeSnapshot.ServerKey()})) return params }, assertResult: func( t *testing.T, params domain.ListTribeSnapshotsParams, res domain.ListTribeSnapshotsResult, ) { t.Helper() serverKeys := params.ServerKeys() tribeSnapshots := res.TribeSnapshots() assert.NotZero(t, tribeSnapshots) for _, ts := range tribeSnapshots { assert.True(t, slices.Contains(serverKeys, ts.ServerKey())) } }, }, { name: "OK: tribeIDs serverKeys", params: func(t *testing.T) domain.ListTribeSnapshotsParams { t.Helper() params := domain.NewListTribeSnapshotsParams() res, err := repos.tribeSnapshot.List(ctx, params) require.NoError(t, err) require.NotEmpty(t, res.TribeSnapshots()) randTribeSnapshot := res.TribeSnapshots()[0] require.NoError(t, params.SetServerKeys([]string{randTribeSnapshot.ServerKey()})) require.NoError(t, params.SetTribeIDs([]int{randTribeSnapshot.TribeID()})) return params }, assertResult: func( t *testing.T, params domain.ListTribeSnapshotsParams, res domain.ListTribeSnapshotsResult, ) { t.Helper() serverKeys := params.ServerKeys() tribeIDs := params.TribeIDs() tribeSnapshots := res.TribeSnapshots() assert.NotZero(t, tribeSnapshots) for _, ts := range tribeSnapshots { assert.True(t, slices.Contains(serverKeys, ts.ServerKey())) assert.True(t, slices.Contains(tribeIDs, ts.TribeID())) } }, }, { name: "OK: cursor serverKeys sort=[id ASC]", params: func(t *testing.T) domain.ListTribeSnapshotsParams { t.Helper() params := domain.NewListTribeSnapshotsParams() res, err := repos.tribeSnapshot.List(ctx, params) require.NoError(t, err) require.Greater(t, len(res.TribeSnapshots()), 2) require.NoError(t, params.SetSort([]domain.TribeSnapshotSort{domain.TribeSnapshotSortIDASC})) require.NoError(t, params.SetServerKeys([]string{res.TribeSnapshots()[1].ServerKey()})) cursor, err := res.TribeSnapshots()[1].ToCursor() require.NoError(t, err) require.NoError(t, params.SetCursor(cursor)) return params }, assertResult: func(t *testing.T, params domain.ListTribeSnapshotsParams, res domain.ListTribeSnapshotsResult) { t.Helper() serverKeys := params.ServerKeys() tribeSnapshots := res.TribeSnapshots() assert.NotEmpty(t, tribeSnapshots) for _, ts := range tribeSnapshots { assert.GreaterOrEqual(t, ts.ID(), params.Cursor().ID()) assert.True(t, slices.Contains(serverKeys, ts.ServerKey())) } assert.True(t, slices.IsSortedFunc(tribeSnapshots, func(a, b domain.TribeSnapshot) int { return cmp.Compare(a.ID(), b.ID()) })) }, }, { name: "OK: cursor sort=[serverKey ASC, id ASC]", params: func(t *testing.T) domain.ListTribeSnapshotsParams { t.Helper() params := domain.NewListTribeSnapshotsParams() require.NoError(t, params.SetSort([]domain.TribeSnapshotSort{ domain.TribeSnapshotSortServerKeyASC, domain.TribeSnapshotSortIDASC, })) res, err := repos.tribeSnapshot.List(ctx, params) require.NoError(t, err) require.Greater(t, len(res.TribeSnapshots()), 2) cursor, err := res.TribeSnapshots()[1].ToCursor() require.NoError(t, err) require.NoError(t, params.SetCursor(cursor)) return params }, assertResult: func(t *testing.T, params domain.ListTribeSnapshotsParams, res domain.ListTribeSnapshotsResult) { t.Helper() tribeSnapshots := res.TribeSnapshots() assert.NotEmpty(t, tribeSnapshots) assert.True(t, slices.IsSortedFunc(tribeSnapshots, func(a, b domain.TribeSnapshot) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), cmp.Compare(a.ID(), b.ID()), ) })) assert.GreaterOrEqual(t, tribeSnapshots[0].ID(), params.Cursor().ID()) for _, ts := range tribeSnapshots { assert.GreaterOrEqual(t, ts.ServerKey(), params.Cursor().ServerKey()) } }, }, { name: "OK: cursor sort=[serverKey DESC, id DESC]", params: func(t *testing.T) domain.ListTribeSnapshotsParams { t.Helper() params := domain.NewListTribeSnapshotsParams() require.NoError(t, params.SetSort([]domain.TribeSnapshotSort{ domain.TribeSnapshotSortServerKeyDESC, domain.TribeSnapshotSortIDDESC, })) res, err := repos.tribeSnapshot.List(ctx, params) require.NoError(t, err) require.Greater(t, len(res.TribeSnapshots()), 2) cursor, err := res.TribeSnapshots()[1].ToCursor() require.NoError(t, err) require.NoError(t, params.SetCursor(cursor)) return params }, assertResult: func(t *testing.T, params domain.ListTribeSnapshotsParams, res domain.ListTribeSnapshotsResult) { t.Helper() tribeSnapshots := res.TribeSnapshots() assert.NotEmpty(t, tribeSnapshots) assert.True(t, slices.IsSortedFunc(tribeSnapshots, func(a, b domain.TribeSnapshot) int { return cmp.Or( cmp.Compare(a.ServerKey(), b.ServerKey()), cmp.Compare(a.ID(), b.ID()), ) * -1 })) assert.LessOrEqual(t, tribeSnapshots[0].ID(), params.Cursor().ID()) for _, ts := range tribeSnapshots { assert.LessOrEqual(t, ts.ServerKey(), params.Cursor().ServerKey()) } }, }, { name: "OK: limit=2", params: func(t *testing.T) domain.ListTribeSnapshotsParams { t.Helper() params := domain.NewListTribeSnapshotsParams() require.NoError(t, params.SetLimit(2)) return params }, assertResult: func( t *testing.T, params domain.ListTribeSnapshotsParams, res domain.ListTribeSnapshotsResult, ) { t.Helper() assert.Len(t, res.TribeSnapshots(), params.Limit()) assert.False(t, res.Self().IsZero()) assert.False(t, res.Next().IsZero()) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() assertError := tt.assertError if assertError == nil { assertError = func(t *testing.T, err error) { t.Helper() require.NoError(t, err) } } params := tt.params(t) res, err := repos.tribeSnapshot.List(ctx, params) assertError(t, err) tt.assertResult(t, params, res) resWithRelations, err := repos.tribeSnapshot.ListWithRelations(ctx, params) assertError(t, err) require.Len(t, resWithRelations.TribeSnapshots(), len(res.TribeSnapshots())) for i, ts := range resWithRelations.TribeSnapshots() { assert.Equal(t, res.TribeSnapshots()[i], ts.TribeSnapshot()) assert.Equal(t, ts.TribeSnapshot().TribeID(), ts.Tribe().ID()) } }) } }) t.Run("Delete", func(t *testing.T) { t.Parallel() t.Run("OK", func(t *testing.T) { t.Parallel() repos := newRepos(t) params := domain.NewListTribeSnapshotsParams() require.NoError(t, params.SetSort([]domain.TribeSnapshotSort{ domain.TribeSnapshotSortServerKeyASC, domain.TribeSnapshotSortDateASC, domain.TribeSnapshotSortIDASC, })) res, err := repos.tribeSnapshot.List(ctx, params) require.NoError(t, err) require.NotEmpty(t, res.TribeSnapshots()) randSnapshot := res.TribeSnapshots()[0] require.NoError(t, repos.tribeSnapshot.Delete(ctx, randSnapshot.ServerKey(), randSnapshot.Date())) require.NoError(t, params.SetServerKeys([]string{randSnapshot.ServerKey()})) res, err = repos.tribeSnapshot.List(ctx, params) require.NoError(t, err) assert.NotEmpty(t, res.TribeSnapshots()) for _, ts := range res.TribeSnapshots() { assert.True(t, ts.Date().After(randSnapshot.Date())) } }) }) }