123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762 |
- // Copyright 2021 gorse Project Authors
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package data
- import (
- "context"
- "encoding/json"
- "fmt"
- "reflect"
- "strconv"
- "testing"
- "time"
- "github.com/jaswdr/faker"
- "github.com/juju/errors"
- "github.com/samber/lo"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/suite"
- "google.golang.org/protobuf/proto"
- )
- var (
- positiveFeedbackType = "positiveFeedbackType"
- negativeFeedbackType = "negativeFeedbackType"
- duplicateFeedbackType = "duplicateFeedbackType"
- )
- type baseTestSuite struct {
- suite.Suite
- Database
- }
- func (suite *baseTestSuite) getUsers(ctx context.Context, batchSize int) []User {
- users := make([]User, 0)
- var err error
- var data []User
- cursor := ""
- for {
- cursor, data, err = suite.Database.GetUsers(ctx, cursor, batchSize)
- suite.NoError(err)
- users = append(users, data...)
- if cursor == "" {
- suite.LessOrEqual(len(data), batchSize)
- return users
- } else {
- suite.Equal(batchSize, len(data))
- }
- }
- }
- func (suite *baseTestSuite) getUsersStream(ctx context.Context, batchSize int) []User {
- var users []User
- userChan, errChan := suite.Database.GetUserStream(ctx, batchSize)
- for batchUsers := range userChan {
- users = append(users, batchUsers...)
- }
- suite.NoError(<-errChan)
- return users
- }
- func (suite *baseTestSuite) getItems(ctx context.Context, batchSize int) []Item {
- items := make([]Item, 0)
- var err error
- var data []Item
- cursor := ""
- for {
- cursor, data, err = suite.Database.GetItems(ctx, cursor, batchSize, nil)
- suite.NoError(err)
- items = append(items, data...)
- if cursor == "" {
- suite.LessOrEqual(len(data), batchSize)
- return items
- } else {
- suite.Equal(batchSize, len(data))
- }
- }
- }
- func (suite *baseTestSuite) getItemStream(ctx context.Context, batchSize int) []Item {
- var items []Item
- itemChan, errChan := suite.Database.GetItemStream(ctx, batchSize, nil)
- for batchUsers := range itemChan {
- items = append(items, batchUsers...)
- }
- suite.NoError(<-errChan)
- return items
- }
- func (suite *baseTestSuite) getFeedback(ctx context.Context, batchSize int, beginTime, endTime *time.Time, feedbackTypes ...string) []Feedback {
- feedback := make([]Feedback, 0)
- var err error
- var data []Feedback
- cursor := ""
- for {
- cursor, data, err = suite.Database.GetFeedback(ctx, cursor, batchSize, beginTime, endTime, feedbackTypes...)
- suite.NoError(err)
- feedback = append(feedback, data...)
- if cursor == "" {
- suite.LessOrEqual(len(data), batchSize)
- return feedback
- } else {
- suite.Equal(batchSize, len(data))
- }
- }
- }
- func (suite *baseTestSuite) getFeedbackStream(ctx context.Context, batchSize int, scanOptions ...ScanOption) []Feedback {
- var feedbacks []Feedback
- feedbackChan, errChan := suite.Database.GetFeedbackStream(ctx, batchSize, scanOptions...)
- for batchFeedback := range feedbackChan {
- feedbacks = append(feedbacks, batchFeedback...)
- }
- suite.NoError(<-errChan)
- return feedbacks
- }
- func (suite *baseTestSuite) TearDownSuite() {
- err := suite.Database.Close()
- suite.NoError(err)
- }
- func (suite *baseTestSuite) SetupTest() {
- err := suite.Database.Ping()
- suite.NoError(err)
- err = suite.Database.Purge()
- suite.NoError(err)
- }
- func (suite *baseTestSuite) TearDownTest() {
- err := suite.Database.Purge()
- suite.NoError(err)
- }
- func (suite *baseTestSuite) TestUsers() {
- ctx := context.Background()
- // Insert users
- var insertedUsers []User
- fake := faker.New()
- for i := 9; i >= 0; i-- {
- insertedUsers = append(insertedUsers, User{
- UserId: strconv.Itoa(i),
- Labels: map[string]any{
- "color": fake.Color().ColorName(),
- "company": lo.Map(lo.Range(3), func(_, _ int) any {
- return fake.Genre().Name()
- }),
- },
- Comment: fmt.Sprintf("comment %d", i),
- })
- }
- err := suite.Database.BatchInsertUsers(ctx, insertedUsers)
- suite.NoError(err)
- // Get users
- users := suite.getUsers(ctx, 3)
- suite.Equal(10, len(users))
- for i, user := range users {
- suite.Equal(insertedUsers[9-i], user)
- }
- // Get user stream
- usersFromStream := suite.getUsersStream(ctx, 3)
- suite.ElementsMatch(insertedUsers, usersFromStream)
- // Get this user
- user, err := suite.Database.GetUser(ctx, "0")
- suite.NoError(err)
- suite.Equal("0", user.UserId)
- // Delete this user
- err = suite.Database.DeleteUser(ctx, "0")
- suite.NoError(err)
- _, err = suite.Database.GetUser(ctx, "0")
- suite.True(errors.Is(err, errors.NotFound), err)
- // test override
- err = suite.Database.BatchInsertUsers(ctx, []User{{UserId: "1", Comment: "override"}})
- suite.NoError(err)
- user, err = suite.Database.GetUser(ctx, "1")
- suite.NoError(err)
- suite.Equal("override", user.Comment)
- // test modify
- err = suite.Database.ModifyUser(ctx, "1", UserPatch{Comment: proto.String("modify")})
- suite.NoError(err)
- err = suite.Database.ModifyUser(ctx, "1", UserPatch{Labels: []string{"a", "b", "c"}})
- suite.NoError(err)
- err = suite.Database.ModifyUser(ctx, "1", UserPatch{Subscribe: []string{"d", "e", "f"}})
- suite.NoError(err)
- user, err = suite.Database.GetUser(ctx, "1")
- suite.NoError(err)
- suite.Equal("modify", user.Comment)
- suite.Equal([]any{"a", "b", "c"}, user.Labels)
- suite.Equal([]string{"d", "e", "f"}, user.Subscribe)
- // test insert empty
- err = suite.Database.BatchInsertUsers(ctx, nil)
- suite.NoError(err)
- // insert duplicate users
- err = suite.Database.BatchInsertUsers(ctx, []User{{UserId: "1"}, {UserId: "1"}})
- suite.NoError(err)
- }
- func (suite *baseTestSuite) TestFeedback() {
- ctx := context.Background()
- // users that already exists
- err := suite.Database.BatchInsertUsers(ctx, []User{{"0", []string{"a"}, []string{"x"}, "comment"}})
- suite.NoError(err)
- // items that already exists
- err = suite.Database.BatchInsertItems(ctx, []Item{{ItemId: "0", Labels: []string{"b"}, Timestamp: time.Date(1996, 4, 8, 10, 0, 0, 0, time.UTC)}})
- suite.NoError(err)
- // insert feedbacks
- timestamp := time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC)
- feedback := []Feedback{
- {FeedbackKey{positiveFeedbackType, "0", "8"}, timestamp, "comment"},
- {FeedbackKey{positiveFeedbackType, "1", "6"}, timestamp, "comment"},
- {FeedbackKey{positiveFeedbackType, "2", "4"}, timestamp, "comment"},
- {FeedbackKey{positiveFeedbackType, "3", "2"}, timestamp, "comment"},
- {FeedbackKey{positiveFeedbackType, "4", "0"}, timestamp, "comment"},
- }
- err = suite.Database.BatchInsertFeedback(ctx, feedback, true, true, true)
- suite.NoError(err)
- // other type
- err = suite.Database.BatchInsertFeedback(ctx, []Feedback{{FeedbackKey: FeedbackKey{negativeFeedbackType, "0", "2"}}}, true, true, true)
- suite.NoError(err)
- err = suite.Database.BatchInsertFeedback(ctx, []Feedback{{FeedbackKey: FeedbackKey{negativeFeedbackType, "2", "4"}}}, true, true, true)
- suite.NoError(err)
- // future feedback
- futureFeedback := []Feedback{
- {FeedbackKey{duplicateFeedbackType, "0", "0"}, time.Now().Add(time.Hour), "comment"},
- {FeedbackKey{duplicateFeedbackType, "1", "2"}, time.Now().Add(time.Hour), "comment"},
- {FeedbackKey{duplicateFeedbackType, "2", "4"}, time.Now().Add(time.Hour), "comment"},
- {FeedbackKey{duplicateFeedbackType, "3", "6"}, time.Now().Add(time.Hour), "comment"},
- {FeedbackKey{duplicateFeedbackType, "4", "8"}, time.Now().Add(time.Hour), "comment"},
- }
- err = suite.Database.BatchInsertFeedback(ctx, futureFeedback, true, true, true)
- suite.NoError(err)
- // Get feedback
- ret := suite.getFeedback(ctx, 3, nil, lo.ToPtr(time.Now()), positiveFeedbackType)
- suite.Equal(feedback, ret)
- ret = suite.getFeedback(ctx, 2, nil, lo.ToPtr(time.Now()))
- suite.Equal(len(feedback)+2, len(ret))
- ret = suite.getFeedback(ctx, 2, lo.ToPtr(timestamp.Add(time.Second)), lo.ToPtr(time.Now()))
- suite.Empty(ret)
- // Get feedback stream
- feedbackFromStream := suite.getFeedbackStream(ctx, 3, WithEndTime(time.Now()), WithFeedbackTypes(positiveFeedbackType))
- suite.ElementsMatch(feedback, feedbackFromStream)
- feedbackFromStream = suite.getFeedbackStream(ctx, 3, WithEndTime(time.Now()))
- suite.Equal(len(feedback)+2, len(feedbackFromStream))
- feedbackFromStream = suite.getFeedbackStream(ctx, 3, WithBeginTime(timestamp.Add(time.Second)), WithEndTime(time.Now()))
- suite.Empty(feedbackFromStream)
- feedbackFromStream = suite.getFeedbackStream(ctx, 3, WithBeginUserId("1"), WithEndUserId("3"), WithEndTime(time.Now()), WithFeedbackTypes(positiveFeedbackType))
- suite.Equal(feedback[1:4], feedbackFromStream)
- // Get items
- items := suite.getItems(ctx, 3)
- suite.Equal(5, len(items))
- for i, item := range items {
- suite.Equal(strconv.Itoa(i*2), item.ItemId)
- if item.ItemId != "0" {
- suite.Zero(item.Timestamp)
- suite.Empty(item.Labels)
- suite.Empty(item.Comment)
- }
- }
- // Get users
- users := suite.getUsers(ctx, 2)
- suite.Equal(5, len(users))
- for i, user := range users {
- suite.Equal(strconv.Itoa(i), user.UserId)
- if user.UserId != "0" {
- suite.Empty(user.Labels)
- suite.Empty(user.Subscribe)
- suite.Empty(user.Comment)
- }
- }
- // check users that already exists
- user, err := suite.Database.GetUser(ctx, "0")
- suite.NoError(err)
- suite.Equal(User{"0", []any{"a"}, []string{"x"}, "comment"}, user)
- // check items that already exists
- item, err := suite.Database.GetItem(ctx, "0")
- suite.NoError(err)
- suite.Equal(Item{ItemId: "0", Labels: []any{"b"}, Timestamp: time.Date(1996, 4, 8, 10, 0, 0, 0, time.UTC)}, item)
- // Get typed feedback by user
- ret, err = suite.Database.GetUserFeedback(ctx, "2", lo.ToPtr(time.Now()), positiveFeedbackType)
- suite.NoError(err)
- suite.Equal(1, len(ret))
- suite.Equal("2", ret[0].UserId)
- suite.Equal("4", ret[0].ItemId)
- // Get all feedback by user
- ret, err = suite.Database.GetUserFeedback(ctx, "2", lo.ToPtr(time.Now()))
- suite.NoError(err)
- suite.Equal(2, len(ret))
- // Get typed feedback by item
- ret, err = suite.Database.GetItemFeedback(ctx, "4", positiveFeedbackType)
- suite.NoError(err)
- suite.Equal(1, len(ret))
- suite.Equal("2", ret[0].UserId)
- suite.Equal("4", ret[0].ItemId)
- // Get all feedback by item
- ret, err = suite.Database.GetItemFeedback(ctx, "4")
- suite.NoError(err)
- suite.Equal(2, len(ret))
- // test override
- err = suite.Database.BatchInsertFeedback(ctx, []Feedback{{
- FeedbackKey: FeedbackKey{positiveFeedbackType, "0", "8"},
- Comment: "override",
- }}, true, true, true)
- suite.NoError(err)
- ret, err = suite.Database.GetUserFeedback(ctx, "0", lo.ToPtr(time.Now()), positiveFeedbackType)
- suite.NoError(err)
- suite.Equal(1, len(ret))
- suite.Equal("override", ret[0].Comment)
- // test not overwrite
- err = suite.Database.BatchInsertFeedback(ctx, []Feedback{{
- FeedbackKey: FeedbackKey{positiveFeedbackType, "0", "8"},
- Comment: "not_override",
- }}, true, true, false)
- suite.NoError(err)
- ret, err = suite.Database.GetUserFeedback(ctx, "0", lo.ToPtr(time.Now()), positiveFeedbackType)
- suite.NoError(err)
- suite.Equal(1, len(ret))
- suite.Equal("override", ret[0].Comment)
- // insert no feedback
- err = suite.Database.BatchInsertFeedback(ctx, nil, true, true, true)
- suite.NoError(err)
- // not insert users or items
- err = suite.Database.BatchInsertFeedback(ctx, []Feedback{
- {FeedbackKey: FeedbackKey{"a", "100", "200"}},
- {FeedbackKey: FeedbackKey{"a", "0", "200"}},
- {FeedbackKey: FeedbackKey{"a", "100", "8"}},
- }, false, false, false)
- suite.NoError(err)
- result, err := suite.Database.GetUserItemFeedback(ctx, "100", "200")
- suite.NoError(err)
- suite.Empty(result)
- result, err = suite.Database.GetUserItemFeedback(ctx, "0", "200")
- suite.NoError(err)
- suite.Empty(result)
- result, err = suite.Database.GetUserItemFeedback(ctx, "100", "8")
- suite.NoError(err)
- suite.Empty(result)
- // insert valid feedback and invalid feedback at the same time
- err = suite.Database.BatchInsertFeedback(ctx, []Feedback{
- {FeedbackKey: FeedbackKey{"a", "0", "8"}},
- {FeedbackKey: FeedbackKey{"a", "100", "200"}},
- }, false, false, false)
- suite.NoError(err)
- // insert duplicate feedback
- err = suite.Database.BatchInsertFeedback(ctx, []Feedback{
- {FeedbackKey: FeedbackKey{"a", "0", "0"}},
- {FeedbackKey: FeedbackKey{"a", "0", "0"}},
- }, true, true, true)
- suite.NoError(err)
- }
- func (suite *baseTestSuite) TestItems() {
- ctx := context.Background()
- // Items
- items := []Item{
- {
- ItemId: "0",
- IsHidden: true,
- Categories: []string{"a"},
- Timestamp: time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC),
- Labels: []any{"a"},
- Comment: "comment 0",
- },
- {
- ItemId: "2",
- Categories: []string{"b"},
- Timestamp: time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC),
- Labels: []any{"a"},
- Comment: "comment 2",
- },
- {
- ItemId: "4",
- IsHidden: true,
- Categories: []string{"a"},
- Timestamp: time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC),
- Labels: []any{"a", "b"},
- Comment: "comment 4",
- },
- {
- ItemId: "6",
- Categories: []string{"b"},
- Timestamp: time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC),
- Labels: []any{"b"},
- Comment: "comment 6",
- },
- {
- ItemId: "8",
- IsHidden: true,
- Categories: []string{"a"},
- Timestamp: time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC),
- Labels: []any{"b"},
- Comment: "comment 8",
- },
- }
- // Insert item
- err := suite.Database.BatchInsertItems(ctx, items)
- suite.NoError(err)
- // Get items
- totalItems := suite.getItems(ctx, 3)
- suite.Equal(items, totalItems)
- // Get item stream
- itemsFromStream := suite.getItemStream(ctx, 3)
- suite.ElementsMatch(items, itemsFromStream)
- // Get item
- for _, item := range items {
- ret, err := suite.Database.GetItem(ctx, item.ItemId)
- suite.NoError(err)
- suite.Equal(item, ret)
- }
- // batch get items
- batchItem, err := suite.Database.BatchGetItems(ctx, []string{"2", "6"})
- suite.NoError(err)
- suite.Equal([]Item{items[1], items[3]}, batchItem)
- // Delete item
- err = suite.Database.DeleteItem(ctx, "0")
- suite.NoError(err)
- _, err = suite.Database.GetItem(ctx, "0")
- suite.True(errors.Is(err, errors.NotFound), err)
- // test override
- err = suite.Database.BatchInsertItems(ctx, []Item{{ItemId: "4", IsHidden: false, Categories: []string{"b"}, Labels: []string{"o"}, Comment: "override"}})
- suite.NoError(err)
- item, err := suite.Database.GetItem(ctx, "4")
- suite.NoError(err)
- suite.False(item.IsHidden)
- suite.Equal([]string{"b"}, item.Categories)
- suite.Equal([]any{"o"}, item.Labels)
- suite.Equal("override", item.Comment)
- // test modify
- timestamp := time.Date(2000, 1, 1, 1, 1, 1, 0, time.UTC)
- err = suite.Database.ModifyItem(ctx, "2", ItemPatch{IsHidden: proto.Bool(true)})
- suite.NoError(err)
- err = suite.Database.ModifyItem(ctx, "2", ItemPatch{Categories: []string{"a"}})
- suite.NoError(err)
- err = suite.Database.ModifyItem(ctx, "2", ItemPatch{Comment: proto.String("modify")})
- suite.NoError(err)
- err = suite.Database.ModifyItem(ctx, "2", ItemPatch{Labels: []string{"a", "b", "c"}})
- suite.NoError(err)
- err = suite.Database.ModifyItem(ctx, "2", ItemPatch{Timestamp: ×tamp})
- suite.NoError(err)
- item, err = suite.Database.GetItem(ctx, "2")
- suite.NoError(err)
- suite.True(item.IsHidden)
- suite.Equal([]string{"a"}, item.Categories)
- suite.Equal("modify", item.Comment)
- suite.Equal([]any{"a", "b", "c"}, item.Labels)
- suite.Equal(timestamp, item.Timestamp)
- // test insert empty
- err = suite.Database.BatchInsertItems(ctx, nil)
- suite.NoError(err)
- // test get empty
- items, err = suite.Database.BatchGetItems(ctx, nil)
- suite.NoError(err)
- suite.Empty(items)
- // test insert duplicate items
- err = suite.Database.BatchInsertItems(ctx, []Item{{ItemId: "1"}, {ItemId: "1"}})
- suite.NoError(err)
- }
- func (suite *baseTestSuite) TestDeleteUser() {
- ctx := context.Background()
- // Insert ret
- feedback := []Feedback{
- {FeedbackKey{positiveFeedbackType, "a", "0"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{positiveFeedbackType, "a", "2"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{positiveFeedbackType, "a", "4"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{positiveFeedbackType, "a", "6"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{positiveFeedbackType, "a", "8"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- }
- err := suite.Database.BatchInsertFeedback(ctx, feedback, true, true, true)
- suite.NoError(err)
- // Delete user
- err = suite.Database.DeleteUser(ctx, "a")
- suite.NoError(err)
- _, err = suite.Database.GetUser(ctx, "a")
- suite.NotNil(err, "failed to delete user")
- ret, err := suite.Database.GetUserFeedback(ctx, "a", lo.ToPtr(time.Now()), positiveFeedbackType)
- suite.NoError(err)
- suite.Equal(0, len(ret))
- _, ret, err = suite.Database.GetFeedback(ctx, "", 100, nil, lo.ToPtr(time.Now()), positiveFeedbackType)
- suite.NoError(err)
- suite.Empty(ret)
- }
- func (suite *baseTestSuite) TestDeleteItem() {
- ctx := context.Background()
- // Insert ret
- feedbacks := []Feedback{
- {FeedbackKey{positiveFeedbackType, "0", "b"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{positiveFeedbackType, "1", "b"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{positiveFeedbackType, "2", "b"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{positiveFeedbackType, "3", "b"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{positiveFeedbackType, "4", "b"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- }
- err := suite.Database.BatchInsertFeedback(ctx, feedbacks, true, true, true)
- suite.NoError(err)
- // Delete item
- err = suite.Database.DeleteItem(ctx, "b")
- suite.NoError(err)
- _, err = suite.Database.GetItem(ctx, "b")
- suite.Error(err, "failed to delete item")
- ret, err := suite.Database.GetItemFeedback(ctx, "b", positiveFeedbackType)
- suite.NoError(err)
- suite.Equal(0, len(ret))
- _, ret, err = suite.Database.GetFeedback(ctx, "", 100, nil, lo.ToPtr(time.Now()), positiveFeedbackType)
- suite.NoError(err)
- suite.Empty(ret)
- }
- func (suite *baseTestSuite) TestDeleteFeedback() {
- ctx := context.Background()
- feedbacks := []Feedback{
- {FeedbackKey{"type1", "2", "3"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{"type2", "2", "3"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{"type3", "2", "3"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{"type1", "2", "4"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{"type1", "1", "3"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- }
- err := suite.Database.BatchInsertFeedback(ctx, feedbacks, true, true, true)
- suite.NoError(err)
- // get user-item feedback
- ret, err := suite.Database.GetUserItemFeedback(ctx, "2", "3")
- suite.NoError(err)
- suite.ElementsMatch([]Feedback{feedbacks[0], feedbacks[1], feedbacks[2]}, ret)
- feedbackType2 := "type2"
- ret, err = suite.Database.GetUserItemFeedback(ctx, "2", "3", feedbackType2)
- suite.NoError(err)
- suite.Equal([]Feedback{feedbacks[1]}, ret)
- // delete user-item feedback
- deleteCount, err := suite.Database.DeleteUserItemFeedback(ctx, "2", "3")
- suite.NoError(err)
- suite.Equal(3, deleteCount)
- ret, err = suite.Database.GetUserItemFeedback(ctx, "2", "3")
- suite.NoError(err)
- suite.Empty(ret)
- feedbackType1 := "type1"
- deleteCount, err = suite.Database.DeleteUserItemFeedback(ctx, "1", "3", feedbackType1)
- suite.NoError(err)
- suite.Equal(1, deleteCount)
- ret, err = suite.Database.GetUserItemFeedback(ctx, "1", "3", feedbackType2)
- suite.NoError(err)
- suite.Empty(ret)
- }
- func (suite *baseTestSuite) TestTimeLimit() {
- ctx := context.Background()
- // insert items
- items := []Item{
- {
- ItemId: "0",
- Timestamp: time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC),
- Labels: []any{"a"},
- Comment: "comment 0",
- },
- {
- ItemId: "2",
- Timestamp: time.Date(1997, 3, 15, 0, 0, 0, 0, time.UTC),
- Labels: []any{"a"},
- Comment: "comment 2",
- },
- {
- ItemId: "4",
- Timestamp: time.Date(1998, 3, 15, 0, 0, 0, 0, time.UTC),
- Labels: []any{"a", "b"},
- Comment: "comment 4",
- },
- {
- ItemId: "6",
- Timestamp: time.Date(1999, 3, 15, 0, 0, 0, 0, time.UTC),
- Labels: []any{"b"},
- Comment: "comment 6",
- },
- {
- ItemId: "8",
- Timestamp: time.Date(2000, 3, 15, 0, 0, 0, 0, time.UTC),
- Labels: []any{"b"},
- Comment: "comment 8",
- },
- }
- err := suite.Database.BatchInsertItems(ctx, items)
- suite.NoError(err)
- timeLimit := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC)
- _, ret, err := suite.Database.GetItems(ctx, "", 100, &timeLimit)
- suite.NoError(err)
- suite.Equal([]Item{items[2], items[3], items[4]}, ret)
- // insert feedback
- feedbacks := []Feedback{
- {FeedbackKey{"type1", "2", "3"}, time.Date(1996, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{"type2", "2", "3"}, time.Date(1997, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{"type3", "2", "3"}, time.Date(1998, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{"type1", "2", "4"}, time.Date(1999, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- {FeedbackKey{"type1", "1", "3"}, time.Date(2000, 3, 15, 0, 0, 0, 0, time.UTC), "comment"},
- }
- err = suite.Database.BatchInsertFeedback(ctx, feedbacks, true, true, true)
- suite.NoError(err)
- _, retFeedback, err := suite.Database.GetFeedback(ctx, "", 100, &timeLimit, lo.ToPtr(time.Now()))
- suite.NoError(err)
- suite.Equal([]Feedback{feedbacks[4], feedbacks[3], feedbacks[2]}, retFeedback)
- typeFilter := "type1"
- _, retFeedback, err = suite.Database.GetFeedback(ctx, "", 100, &timeLimit, lo.ToPtr(time.Now()), typeFilter)
- suite.NoError(err)
- suite.Equal([]Feedback{feedbacks[4], feedbacks[3]}, retFeedback)
- }
- func (suite *baseTestSuite) TestTimezone() {
- ctx := context.Background()
- loc, err := time.LoadLocation("Asia/Tokyo")
- suite.NoError(err)
- // insert feedbacks
- err = suite.Database.BatchInsertFeedback(ctx, []Feedback{
- {FeedbackKey: FeedbackKey{"read", "1", "1"}, Timestamp: time.Now().Add(-time.Second).In(loc)},
- {FeedbackKey: FeedbackKey{"read", "1", "2"}, Timestamp: time.Now().Add(-time.Second).In(loc)},
- {FeedbackKey: FeedbackKey{"read", "2", "2"}, Timestamp: time.Now().Add(-time.Second).In(loc)},
- {FeedbackKey: FeedbackKey{"like", "1", "1"}, Timestamp: time.Now().Add(time.Hour).In(loc)},
- {FeedbackKey: FeedbackKey{"like", "1", "2"}, Timestamp: time.Now().Add(time.Hour).In(loc)},
- {FeedbackKey: FeedbackKey{"like", "2", "2"}, Timestamp: time.Now().Add(time.Hour).In(loc)},
- }, true, true, true)
- suite.NoError(err)
- // get feedback stream
- feedback := suite.getFeedback(ctx, 10, nil, lo.ToPtr(time.Now()))
- suite.Equal(3, len(feedback))
- // get feedback
- _, feedback, err = suite.Database.GetFeedback(ctx, "", 10, nil, lo.ToPtr(time.Now()))
- suite.NoError(err)
- suite.Equal(3, len(feedback))
- // get user feedback
- feedback, err = suite.Database.GetUserFeedback(ctx, "1", lo.ToPtr(time.Now()))
- suite.NoError(err)
- suite.Equal(2, len(feedback))
- // get item feedback
- feedback, err = suite.Database.GetItemFeedback(ctx, "2") // no future feedback by default
- suite.NoError(err)
- suite.Equal(2, len(feedback))
- // get user item feedback
- feedback, err = suite.Database.GetUserItemFeedback(ctx, "1", "1") // return future feedback by default
- suite.NoError(err)
- suite.Equal(2, len(feedback))
- // insert items
- now := time.Now().In(loc)
- err = suite.Database.BatchInsertItems(ctx, []Item{{ItemId: "100", Timestamp: now}, {ItemId: "200"}})
- suite.NoError(err)
- err = suite.Database.ModifyItem(ctx, "200", ItemPatch{Timestamp: &now})
- suite.NoError(err)
- switch database := suite.Database.(type) {
- case *SQLDatabase:
- switch suite.Database.(*SQLDatabase).driver {
- case Postgres:
- item, err := suite.Database.GetItem(ctx, "100")
- suite.NoError(err)
- suite.Equal(now.Round(time.Microsecond).In(time.UTC), item.Timestamp)
- item, err = suite.Database.GetItem(ctx, "200")
- suite.NoError(err)
- suite.Equal(now.Round(time.Microsecond).In(time.UTC), item.Timestamp)
- case SQLite:
- item, err := suite.Database.GetItem(ctx, "100")
- suite.NoError(err)
- suite.Equal(now.In(time.UTC), item.Timestamp.In(time.UTC))
- item, err = suite.Database.GetItem(ctx, "200")
- suite.NoError(err)
- suite.Equal(now.In(time.UTC), item.Timestamp.In(time.UTC))
- default:
- suite.T().Skipf("unknown sql database: %v", database.driver)
- }
- case *MongoDB:
- item, err := suite.Database.GetItem(ctx, "100")
- suite.NoError(err)
- suite.Equal(now.Truncate(time.Millisecond).In(time.UTC), item.Timestamp)
- item, err = suite.Database.GetItem(ctx, "200")
- suite.NoError(err)
- suite.Equal(now.Truncate(time.Millisecond).In(time.UTC), item.Timestamp)
- default:
- suite.T().Skipf("unknown database: %v", reflect.TypeOf(suite.Database))
- }
- }
- func (suite *baseTestSuite) TestPurge() {
- ctx := context.Background()
- // insert data
- err := suite.Database.BatchInsertFeedback(ctx, lo.Map(lo.Range(100), func(t int, i int) Feedback {
- return Feedback{FeedbackKey: FeedbackKey{
- FeedbackType: "click",
- UserId: strconv.Itoa(t),
- ItemId: strconv.Itoa(t),
- }}
- }), true, true, true)
- suite.NoError(err)
- _, users, err := suite.Database.GetUsers(ctx, "", 100)
- suite.NoError(err)
- suite.Equal(100, len(users))
- _, items, err := suite.Database.GetItems(ctx, "", 100, nil)
- suite.NoError(err)
- suite.Equal(100, len(items))
- _, feedbacks, err := suite.Database.GetFeedback(ctx, "", 100, nil, lo.ToPtr(time.Now()))
- suite.NoError(err)
- suite.Equal(100, len(feedbacks))
- // purge data
- err = suite.Database.Purge()
- suite.NoError(err)
- _, users, err = suite.Database.GetUsers(ctx, "", 100)
- suite.NoError(err)
- suite.Empty(users)
- _, items, err = suite.Database.GetItems(ctx, "", 100, nil)
- suite.NoError(err)
- suite.Empty(items)
- _, feedbacks, err = suite.Database.GetFeedback(ctx, "", 100, nil, lo.ToPtr(time.Now()))
- suite.NoError(err)
- suite.Empty(feedbacks)
- // purge empty database
- err = suite.Database.Purge()
- suite.NoError(err)
- }
- func TestSortFeedbacks(t *testing.T) {
- feedback := []Feedback{
- {FeedbackKey: FeedbackKey{"star", "1", "1"}, Timestamp: time.Date(2000, 10, 1, 0, 0, 0, 0, time.UTC)},
- {FeedbackKey: FeedbackKey{"like", "1", "1"}, Timestamp: time.Date(2001, 10, 1, 0, 0, 0, 0, time.UTC)},
- {FeedbackKey: FeedbackKey{"read", "1", "1"}, Timestamp: time.Date(2002, 10, 1, 0, 0, 0, 0, time.UTC)},
- }
- SortFeedbacks(feedback)
- assert.Equal(t, []Feedback{
- {FeedbackKey: FeedbackKey{"read", "1", "1"}, Timestamp: time.Date(2002, 10, 1, 0, 0, 0, 0, time.UTC)},
- {FeedbackKey: FeedbackKey{"like", "1", "1"}, Timestamp: time.Date(2001, 10, 1, 0, 0, 0, 0, time.UTC)},
- {FeedbackKey: FeedbackKey{"star", "1", "1"}, Timestamp: time.Date(2000, 10, 1, 0, 0, 0, 0, time.UTC)},
- }, feedback)
- }
- func TestValidateLabels(t *testing.T) {
- assert.NoError(t, ValidateLabels(nil))
- assert.NoError(t, ValidateLabels(json.Number("1")))
- assert.NoError(t, ValidateLabels("label"))
- assert.NoError(t, ValidateLabels([]any{json.Number("1"), json.Number("2"), json.Number("3")}))
- assert.NoError(t, ValidateLabels([]any{"1", "2", "3"}))
- assert.NoError(t, ValidateLabels(map[string]any{"city": json.Number("1"), "tags": []any{json.Number("1"), json.Number("2"), json.Number("3")}}))
- assert.NoError(t, ValidateLabels(map[string]any{"city": "wenzhou", "tags": []any{"1", "2", "3"}}))
- assert.NoError(t, ValidateLabels(map[string]any{"address": map[string]any{"province": json.Number("1"), "city": json.Number("2")}}))
- assert.NoError(t, ValidateLabels(map[string]any{"address": map[string]any{"province": "zhejiang", "city": "wenzhou"}}))
- assert.Error(t, ValidateLabels(map[string]any{"price": 100, "tags": []any{json.Number("1"), "2", "3"}}))
- assert.Error(t, ValidateLabels(map[string]any{"city": "wenzhou", "tags": []any{"1", json.Number("2"), "3"}}))
- assert.Error(t, ValidateLabels(map[string]any{"city": "wenzhou", "tags": []any{"1", "2", json.Number("3")}}))
- }
|