| package dbengine |
|
|
| import ( |
| "database/sql" |
| _ "embed" |
| "encoding/json" |
| "log" |
| "net" |
| "net/url" |
| "slices" |
| "sort" |
| "strings" |
| "testing" |
|
|
| _ "github.com/lib/pq" |
| "github.com/ory/dockertest" |
| "github.com/ory/dockertest/docker" |
| "github.com/stretchr/testify/assert" |
| "github.com/stretchr/testify/require" |
| "github.com/stretchr/testify/suite" |
| "gorm.io/datatypes" |
| "gorm.io/gorm/logger" |
|
|
| "github.com/metatube-community/metatube-sdk-go/common/parser" |
| "github.com/metatube-community/metatube-sdk-go/database" |
| "github.com/metatube-community/metatube-sdk-go/engine/providerid" |
| "github.com/metatube-community/metatube-sdk-go/model" |
| ) |
|
|
| var ( |
| |
| actorMetadata string |
|
|
| |
| movieMetadata string |
|
|
| |
| movieReviews string |
| ) |
|
|
| type DBEngineTestSuite struct { |
| suite.Suite |
|
|
| typ string |
| dsn string |
| eng DBEngine |
| } |
|
|
| func TestDBEngineTestSuite(t *testing.T) { |
| suites := []struct { |
| typ string |
| dsn string |
| }{ |
| { |
| |
| typ: database.Sqlite, |
| dsn: ":memory:", |
| }, |
| { |
| |
| typ: database.Postgres, |
| dsn: postgresDSN, |
| }, |
| } |
|
|
| for _, s := range suites { |
| t.Run(s.typ, func(t *testing.T) { |
| suite.Run(t, &DBEngineTestSuite{typ: s.typ, dsn: s.dsn}) |
| }) |
| } |
| } |
|
|
| func (s *DBEngineTestSuite) SetupSuite() { |
| db, err := database.Open(&database.Config{ |
| DSN: s.dsn, |
| DisableAutomaticPing: true, |
| LogLevel: logger.Warn, |
| }) |
| s.Require().NoError(err) |
|
|
| s.eng = New(db) |
| err = s.eng.AutoMigrate() |
| s.Require().NoError(err) |
| } |
|
|
| func (s *DBEngineTestSuite) TestVersion() { |
| version, err := s.eng.Version() |
| s.Require().NoError(err) |
| s.Require().NotEmpty(version) |
| } |
|
|
| func (s *DBEngineTestSuite) TestActor() { |
| var jsonData []struct { |
| ID string `json:"id"` |
| Name string `json:"name"` |
| Provider string `json:"provider"` |
| Homepage string `json:"homepage"` |
| } |
| if err := json. |
| NewDecoder(strings.NewReader(actorMetadata)). |
| Decode(&jsonData); err != nil { |
| s.Require().NoError(err) |
| } |
| for _, data := range jsonData { |
| err := s.eng.SaveActorInfo(&model.ActorInfo{ |
| ID: data.ID, |
| Name: data.Name, |
| Provider: data.Provider, |
| Homepage: data.Homepage, |
| }) |
| s.Require().NoError(err) |
| } |
|
|
| s.T().Run("get actor info", func(t *testing.T) { |
| got, err := s.eng.GetActorInfo(providerid.MustParse("AV-LEAGUE:1607")) |
| require.NoError(t, err) |
| require.NotNil(t, got) |
| assert.Equal(t, "上原亜衣", got.Name) |
| }) |
|
|
| s.T().Run("get actor info (case-insensitive)", func(t *testing.T) { |
| got, err := s.eng.GetActorInfo(providerid.MustParse("av-league:1981")) |
| require.NoError(t, err) |
| require.NotNil(t, got) |
| assert.Equal(t, "大場ゆい", got.Name) |
| }) |
|
|
| s.T().Run("search actor by name (no case)", func(t *testing.T) { |
| actors, err := s.eng.SearchActor("hitOmi", ActorSearchOptions{}) |
| require.NoError(t, err) |
| require.Len(t, actors, 1) |
| assert.Equal(t, "7106", actors[0].ID) |
| }) |
|
|
| s.T().Run("search actor by name (match)", func(t *testing.T) { |
| actors, err := s.eng.SearchActor("加藤あやの", ActorSearchOptions{}) |
| require.NoError(t, err) |
| require.Len(t, actors, 1) |
| assert.Equal(t, "2477", actors[0].ID) |
| }) |
|
|
| s.T().Run("search actor by name (fuzz)", func(t *testing.T) { |
| actors, err := s.eng.SearchActor("加藤", ActorSearchOptions{}) |
| require.NoError(t, err) |
| require.Len(t, actors, 2) |
| sort.Slice(actors, func(i, j int) bool { return actors[i].ID < actors[j].ID }) |
| assert.Equal(t, "2477", actors[0].ID) |
| assert.Equal(t, "25352", actors[1].ID) |
| }) |
|
|
| s.T().Run("search actor by name (fuzzer)", func(t *testing.T) { |
| actors, err := s.eng.SearchActor("三", ActorSearchOptions{Threshold: 0.1}) |
| require.NoError(t, err) |
| require.Len(t, actors, 3) |
| }) |
|
|
| s.T().Run("search actor by name (limit)", func(t *testing.T) { |
| actors, err := s.eng.SearchActor("夏", ActorSearchOptions{Threshold: 0.1, Limit: 2}) |
| require.NoError(t, err) |
| require.Len(t, actors, 2) |
| }) |
|
|
| s.T().Run("search actor by name (not found)", func(t *testing.T) { |
| actors, err := s.eng.SearchActor("无名氏", ActorSearchOptions{}) |
| require.NoError(t, err) |
| require.Empty(t, actors) |
| }) |
| } |
|
|
| func (s *DBEngineTestSuite) TestMovie() { |
| var jsonData []struct { |
| ID string `json:"id"` |
| Number string `json:"number"` |
| Title string `json:"title"` |
| Summary string `json:"summary"` |
| Provider string `json:"provider"` |
| Homepage string `json:"homepage"` |
| ThumbURL string `json:"thumb_url"` |
| CoverURL string `json:"cover_url"` |
| } |
| if err := json. |
| NewDecoder(strings.NewReader(movieMetadata)). |
| Decode(&jsonData); err != nil { |
| s.Require().NoError(err) |
| } |
| for _, data := range jsonData { |
| err := s.eng.SaveMovieInfo(&model.MovieInfo{ |
| ID: data.ID, |
| Number: data.Number, |
| Title: data.Title, |
| Summary: data.Summary, |
| Provider: data.Provider, |
| Homepage: data.Homepage, |
| ThumbURL: data.ThumbURL, |
| CoverURL: data.CoverURL, |
| }) |
| s.Require().NoError(err) |
| } |
|
|
| s.T().Run("get movie info", func(t *testing.T) { |
| got, err := s.eng.GetMovieInfo(providerid.MustParse("FANZA:pred00507")) |
| require.NoError(t, err) |
| require.NotNil(t, got) |
| assert.Equal(t, "PRED-507", got.Number) |
| }) |
|
|
| s.T().Run("get movie info (case-insensitive)", func(t *testing.T) { |
| got, err := s.eng.GetMovieInfo(providerid.MustParse("fanZA:h_1133HonB006")) |
| require.NoError(t, err) |
| require.NotNil(t, got) |
| assert.Equal(t, "HONB-006", got.Number) |
| }) |
|
|
| s.T().Run("search movie by id (no case)", func(t *testing.T) { |
| movies, err := s.eng.SearchMovie("juq00588", MovieSearchOptions{}) |
| require.NoError(t, err) |
| require.Len(t, movies, 1) |
| assert.Equal(t, "juq00588", movies[0].ID) |
| }) |
|
|
| s.T().Run("search movie by number (match)", func(t *testing.T) { |
| movies, err := s.eng.SearchMovie("SDMF-033", MovieSearchOptions{}) |
| require.NoError(t, err) |
| require.Len(t, movies, 1) |
| assert.Equal(t, "1sdmf00033", movies[0].ID) |
| }) |
|
|
| s.T().Run("search movie by number (*match*)", func(t *testing.T) { |
| movies, err := s.eng.SearchMovie("HEYZO-0", MovieSearchOptions{}) |
| require.NoError(t, err) |
| assert.GreaterOrEqual(t, len(movies), 12) |
| t.Log(jsonify(movies)) |
| }) |
|
|
| s.T().Run("search movie by number (*match*)", func(t *testing.T) { |
| movies, err := s.eng.SearchMovie("ABP", MovieSearchOptions{}) |
| require.NoError(t, err) |
| assert.GreaterOrEqual(t, len(movies), 6) |
| t.Log(jsonify(movies)) |
| }) |
|
|
| s.T().Run("search movie by title (fuzz)", func(t *testing.T) { |
| movies, err := s.eng.SearchMovie("密着誘惑してくるささやき淫語お姉", MovieSearchOptions{}) |
| require.NoError(t, err) |
| require.Len(t, movies, 1) |
| assert.Equal(t, "ssis00250", movies[0].ID) |
| t.Log(jsonify(movies)) |
| }) |
|
|
| s.T().Run("search movie by title (fuzz&name)", func(t *testing.T) { |
| movies, err := s.eng.SearchMovie("本田岬", MovieSearchOptions{}) |
| require.NoError(t, err) |
| assert.GreaterOrEqual(t, len(movies), 1) |
| t.Log(jsonify(movies)) |
| }) |
|
|
| s.T().Run("search movie by title (fuzzer)", func(t *testing.T) { |
| if s.typ != database.Postgres { |
| t.SkipNow() |
| } |
| |
| movies, err := s.eng.SearchMovie("AMBI-133 エロ配信が担任の先生にバレちゃうなんて!! 高瀬りな", MovieSearchOptions{}) |
| require.NoError(t, err) |
| require.Len(t, movies, 1) |
| assert.Equal(t, "h_237ambi00133", movies[0].ID) |
| t.Log(jsonify(movies)) |
| }) |
| } |
|
|
| func (s *DBEngineTestSuite) TestMovie_Reviews() { |
| var jsonData []struct { |
| ID string `json:"id"` |
| Provider string `json:"provider"` |
| RawReviews json.RawMessage `json:"reviews"` |
| Reviews []struct { |
| Title string `json:"title"` |
| Author string `json:"author"` |
| Comment string `json:"comment"` |
| Score float64 `json:"score"` |
| Date string `json:"date"` |
| } `json:"-"` |
| } |
| if err := json. |
| NewDecoder(strings.NewReader(movieReviews)). |
| Decode(&jsonData); err != nil { |
| s.Require().NoError(err) |
| } |
| for _, data := range jsonData { |
| var reviewJSON string |
| err := json.Unmarshal(data.RawReviews, &reviewJSON) |
| s.Require().NoError(err) |
| err = json.Unmarshal([]byte(reviewJSON), &data.Reviews) |
| s.Require().NoError(err) |
| err = s.eng.SaveMovieReviewInfo(&model.MovieReviewInfo{ |
| ID: data.ID, |
| Provider: data.Provider, |
| Reviews: datatypes.NewJSONSlice( |
| slices.Collect(func(yield func(detail *model.MovieReviewDetail) bool) { |
| for _, review := range data.Reviews { |
| if !yield(&model.MovieReviewDetail{ |
| Title: review.Title, |
| Author: review.Author, |
| Comment: review.Comment, |
| Score: review.Score, |
| Date: parser.ParseDate(review.Date), |
| }) { |
| return |
| } |
| } |
| })), |
| }) |
| s.Require().NoError(err) |
| } |
|
|
| s.T().Run("get review info", func(t *testing.T) { |
| got, err := s.eng.GetMovieReviewInfo(providerid.MustParse("FANZA:ebwh00024")) |
| require.NoError(t, err) |
| require.NotNil(t, got) |
| assert.NotEmpty(t, got.Reviews) |
| }) |
|
|
| s.T().Run("get review info (case-insensitive)", func(t *testing.T) { |
| got, err := s.eng.GetMovieReviewInfo(providerid.MustParse("fanZA:DAsS00465")) |
| require.NoError(t, err) |
| require.NotNil(t, got) |
| assert.NotEmpty(t, got.Reviews) |
| }) |
| } |
|
|
| func jsonify(v interface{}) string { |
| data, _ := json.MarshalIndent(v, "", "\t") |
| return string(data) |
| } |
|
|
| func TestMain(m *testing.M) { |
| pool, err := dockertest.NewPool("") |
| if err != nil { |
| log.Fatalf("Could not construct pool: %s", err) |
| } |
|
|
| if err := pool.Client.Ping(); err != nil { |
| log.Fatalf("Could not connect to Docker: %s", err) |
| } |
|
|
| resource, err := pool.RunWithOptions( |
| &dockertest.RunOptions{ |
| Repository: "postgres", |
| Tag: "15-alpine", |
| Env: []string{ |
| "POSTGRES_DB=" + postgresDB, |
| "POSTGRES_USER=" + postgresUser, |
| "POSTGRES_PASSWORD=" + postgresPass, |
| }, |
| }, |
| func(config *docker.HostConfig) { |
| config.AutoRemove = true |
| config.RestartPolicy = docker.RestartPolicy{Name: "no"} |
| }, |
| ) |
| if err != nil { |
| log.Fatalf("Could not start resource: %s", err) |
| } |
|
|
| postgresDSN = (&url.URL{ |
| Scheme: postgresDriver, |
| User: url.UserPassword(postgresUser, postgresPass), |
| Host: net.JoinHostPort("localhost", resource.GetPort("5432/tcp")), |
| Path: postgresDB, |
| RawQuery: url.Values{"sslmode": []string{"disable"}}.Encode(), |
| }).String() |
|
|
| if err := pool.Retry(func() error { |
| db, err := sql.Open(postgresDriver, postgresDSN) |
| if err != nil { |
| return err |
| } |
| return db.Ping() |
| }); err != nil { |
| log.Fatalf("Could not connect to database: %s", err) |
| } |
|
|
| defer func() { |
| if err := pool.Purge(resource); err != nil { |
| log.Fatalf("Could not purge resource: %s", err) |
| } |
| }() |
|
|
| m.Run() |
| } |
|
|
| var postgresDSN string |
|
|
| const ( |
| postgresDriver = "postgres" |
| postgresDB |
| postgresUser |
| postgresPass |
| ) |
|
|