mirror of https://github.com/stashapp/stash.git
593 lines
15 KiB
Go
593 lines
15 KiB
Go
// +build integration
|
|
|
|
package sqlite_test
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stashapp/stash/pkg/models"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestStudioFindByName(t *testing.T) {
|
|
withTxn(func(r models.Repository) error {
|
|
sqb := r.Studio()
|
|
|
|
name := studioNames[studioIdxWithScene] // find a studio by name
|
|
|
|
studio, err := sqb.FindByName(name, false)
|
|
|
|
if err != nil {
|
|
t.Errorf("Error finding studios: %s", err.Error())
|
|
}
|
|
|
|
assert.Equal(t, studioNames[studioIdxWithScene], studio.Name.String)
|
|
|
|
name = studioNames[studioIdxWithDupName] // find a studio by name nocase
|
|
|
|
studio, err = sqb.FindByName(name, true)
|
|
|
|
if err != nil {
|
|
t.Errorf("Error finding studios: %s", err.Error())
|
|
}
|
|
// studioIdxWithDupName and studioIdxWithScene should have similar names ( only diff should be Name vs NaMe)
|
|
//studio.Name should match with studioIdxWithScene since its ID is before studioIdxWithDupName
|
|
assert.Equal(t, studioNames[studioIdxWithScene], studio.Name.String)
|
|
//studio.Name should match with studioIdxWithDupName if the check is not case sensitive
|
|
assert.Equal(t, strings.ToLower(studioNames[studioIdxWithDupName]), strings.ToLower(studio.Name.String))
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func TestStudioQueryForAutoTag(t *testing.T) {
|
|
withTxn(func(r models.Repository) error {
|
|
tqb := r.Studio()
|
|
|
|
name := studioNames[studioIdxWithScene] // find a studio by name
|
|
|
|
studios, err := tqb.QueryForAutoTag([]string{name})
|
|
|
|
if err != nil {
|
|
t.Errorf("Error finding studios: %s", err.Error())
|
|
}
|
|
|
|
assert.Len(t, studios, 2)
|
|
assert.Equal(t, strings.ToLower(studioNames[studioIdxWithScene]), strings.ToLower(studios[0].Name.String))
|
|
assert.Equal(t, strings.ToLower(studioNames[studioIdxWithScene]), strings.ToLower(studios[1].Name.String))
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func TestStudioQueryParent(t *testing.T) {
|
|
withTxn(func(r models.Repository) error {
|
|
sqb := r.Studio()
|
|
studioCriterion := models.MultiCriterionInput{
|
|
Value: []string{
|
|
strconv.Itoa(studioIDs[studioIdxWithChildStudio]),
|
|
},
|
|
Modifier: models.CriterionModifierIncludes,
|
|
}
|
|
|
|
studioFilter := models.StudioFilterType{
|
|
Parents: &studioCriterion,
|
|
}
|
|
|
|
studios, _, err := sqb.Query(&studioFilter, nil)
|
|
if err != nil {
|
|
t.Errorf("Error querying studio: %s", err.Error())
|
|
}
|
|
|
|
assert.Len(t, studios, 1)
|
|
|
|
// ensure id is correct
|
|
assert.Equal(t, sceneIDs[studioIdxWithParentStudio], studios[0].ID)
|
|
|
|
studioCriterion = models.MultiCriterionInput{
|
|
Value: []string{
|
|
strconv.Itoa(studioIDs[studioIdxWithChildStudio]),
|
|
},
|
|
Modifier: models.CriterionModifierExcludes,
|
|
}
|
|
|
|
q := getStudioStringValue(studioIdxWithParentStudio, titleField)
|
|
findFilter := models.FindFilterType{
|
|
Q: &q,
|
|
}
|
|
|
|
studios, _, err = sqb.Query(&studioFilter, &findFilter)
|
|
if err != nil {
|
|
t.Errorf("Error querying studio: %s", err.Error())
|
|
}
|
|
assert.Len(t, studios, 0)
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func TestStudioDestroyParent(t *testing.T) {
|
|
const parentName = "parent"
|
|
const childName = "child"
|
|
|
|
// create parent and child studios
|
|
if err := withTxn(func(r models.Repository) error {
|
|
createdParent, err := createStudio(r.Studio(), parentName, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating parent studio: %s", err.Error())
|
|
}
|
|
|
|
parentID := int64(createdParent.ID)
|
|
createdChild, err := createStudio(r.Studio(), childName, &parentID)
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating child studio: %s", err.Error())
|
|
}
|
|
|
|
sqb := r.Studio()
|
|
|
|
// destroy the parent
|
|
err = sqb.Destroy(createdParent.ID)
|
|
if err != nil {
|
|
return fmt.Errorf("Error destroying parent studio: %s", err.Error())
|
|
}
|
|
|
|
// destroy the child
|
|
err = sqb.Destroy(createdChild.ID)
|
|
if err != nil {
|
|
return fmt.Errorf("Error destroying child studio: %s", err.Error())
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
}
|
|
|
|
func TestStudioFindChildren(t *testing.T) {
|
|
withTxn(func(r models.Repository) error {
|
|
sqb := r.Studio()
|
|
|
|
studios, err := sqb.FindChildren(studioIDs[studioIdxWithChildStudio])
|
|
|
|
if err != nil {
|
|
t.Errorf("error calling FindChildren: %s", err.Error())
|
|
}
|
|
|
|
assert.Len(t, studios, 1)
|
|
assert.Equal(t, studioIDs[studioIdxWithParentStudio], studios[0].ID)
|
|
|
|
studios, err = sqb.FindChildren(0)
|
|
|
|
if err != nil {
|
|
t.Errorf("error calling FindChildren: %s", err.Error())
|
|
}
|
|
|
|
assert.Len(t, studios, 0)
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func TestStudioUpdateClearParent(t *testing.T) {
|
|
const parentName = "clearParent_parent"
|
|
const childName = "clearParent_child"
|
|
|
|
// create parent and child studios
|
|
if err := withTxn(func(r models.Repository) error {
|
|
createdParent, err := createStudio(r.Studio(), parentName, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating parent studio: %s", err.Error())
|
|
}
|
|
|
|
parentID := int64(createdParent.ID)
|
|
createdChild, err := createStudio(r.Studio(), childName, &parentID)
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating child studio: %s", err.Error())
|
|
}
|
|
|
|
sqb := r.Studio()
|
|
|
|
// clear the parent id from the child
|
|
updatePartial := models.StudioPartial{
|
|
ID: createdChild.ID,
|
|
ParentID: &sql.NullInt64{Valid: false},
|
|
}
|
|
|
|
updatedStudio, err := sqb.Update(updatePartial)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("Error updated studio: %s", err.Error())
|
|
}
|
|
|
|
if updatedStudio.ParentID.Valid {
|
|
return errors.New("updated studio has parent ID set")
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
}
|
|
|
|
func TestStudioUpdateStudioImage(t *testing.T) {
|
|
if err := withTxn(func(r models.Repository) error {
|
|
qb := r.Studio()
|
|
|
|
// create performer to test against
|
|
const name = "TestStudioUpdateStudioImage"
|
|
created, err := createStudio(r.Studio(), name, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating studio: %s", err.Error())
|
|
}
|
|
|
|
image := []byte("image")
|
|
err = qb.UpdateImage(created.ID, image)
|
|
if err != nil {
|
|
return fmt.Errorf("Error updating studio image: %s", err.Error())
|
|
}
|
|
|
|
// ensure image set
|
|
storedImage, err := qb.GetImage(created.ID)
|
|
if err != nil {
|
|
return fmt.Errorf("Error getting image: %s", err.Error())
|
|
}
|
|
assert.Equal(t, storedImage, image)
|
|
|
|
// set nil image
|
|
err = qb.UpdateImage(created.ID, nil)
|
|
if err == nil {
|
|
return fmt.Errorf("Expected error setting nil image")
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
}
|
|
|
|
func TestStudioDestroyStudioImage(t *testing.T) {
|
|
if err := withTxn(func(r models.Repository) error {
|
|
qb := r.Studio()
|
|
|
|
// create performer to test against
|
|
const name = "TestStudioDestroyStudioImage"
|
|
created, err := createStudio(r.Studio(), name, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating studio: %s", err.Error())
|
|
}
|
|
|
|
image := []byte("image")
|
|
err = qb.UpdateImage(created.ID, image)
|
|
if err != nil {
|
|
return fmt.Errorf("Error updating studio image: %s", err.Error())
|
|
}
|
|
|
|
err = qb.DestroyImage(created.ID)
|
|
if err != nil {
|
|
return fmt.Errorf("Error destroying studio image: %s", err.Error())
|
|
}
|
|
|
|
// image should be nil
|
|
storedImage, err := qb.GetImage(created.ID)
|
|
if err != nil {
|
|
return fmt.Errorf("Error getting image: %s", err.Error())
|
|
}
|
|
assert.Nil(t, storedImage)
|
|
|
|
return nil
|
|
}); err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
}
|
|
|
|
func TestStudioQuerySceneCount(t *testing.T) {
|
|
const sceneCount = 1
|
|
sceneCountCriterion := models.IntCriterionInput{
|
|
Value: sceneCount,
|
|
Modifier: models.CriterionModifierEquals,
|
|
}
|
|
|
|
verifyStudiosSceneCount(t, sceneCountCriterion)
|
|
|
|
sceneCountCriterion.Modifier = models.CriterionModifierNotEquals
|
|
verifyStudiosSceneCount(t, sceneCountCriterion)
|
|
|
|
sceneCountCriterion.Modifier = models.CriterionModifierGreaterThan
|
|
verifyStudiosSceneCount(t, sceneCountCriterion)
|
|
|
|
sceneCountCriterion.Modifier = models.CriterionModifierLessThan
|
|
verifyStudiosSceneCount(t, sceneCountCriterion)
|
|
}
|
|
|
|
func verifyStudiosSceneCount(t *testing.T, sceneCountCriterion models.IntCriterionInput) {
|
|
withTxn(func(r models.Repository) error {
|
|
sqb := r.Studio()
|
|
studioFilter := models.StudioFilterType{
|
|
SceneCount: &sceneCountCriterion,
|
|
}
|
|
|
|
studios := queryStudio(t, sqb, &studioFilter, nil)
|
|
assert.Greater(t, len(studios), 0)
|
|
|
|
for _, studio := range studios {
|
|
sceneCount, err := r.Scene().CountByStudioID(studio.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
verifyInt(t, sceneCount, sceneCountCriterion)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func TestStudioQueryImageCount(t *testing.T) {
|
|
const imageCount = 1
|
|
imageCountCriterion := models.IntCriterionInput{
|
|
Value: imageCount,
|
|
Modifier: models.CriterionModifierEquals,
|
|
}
|
|
|
|
verifyStudiosImageCount(t, imageCountCriterion)
|
|
|
|
imageCountCriterion.Modifier = models.CriterionModifierNotEquals
|
|
verifyStudiosImageCount(t, imageCountCriterion)
|
|
|
|
imageCountCriterion.Modifier = models.CriterionModifierGreaterThan
|
|
verifyStudiosImageCount(t, imageCountCriterion)
|
|
|
|
imageCountCriterion.Modifier = models.CriterionModifierLessThan
|
|
verifyStudiosImageCount(t, imageCountCriterion)
|
|
}
|
|
|
|
func verifyStudiosImageCount(t *testing.T, imageCountCriterion models.IntCriterionInput) {
|
|
withTxn(func(r models.Repository) error {
|
|
sqb := r.Studio()
|
|
studioFilter := models.StudioFilterType{
|
|
ImageCount: &imageCountCriterion,
|
|
}
|
|
|
|
studios := queryStudio(t, sqb, &studioFilter, nil)
|
|
assert.Greater(t, len(studios), 0)
|
|
|
|
for _, studio := range studios {
|
|
pp := 0
|
|
|
|
_, count, err := r.Image().Query(&models.ImageFilterType{
|
|
Studios: &models.HierarchicalMultiCriterionInput{
|
|
Value: []string{strconv.Itoa(studio.ID)},
|
|
Modifier: models.CriterionModifierIncludes,
|
|
Depth: 0,
|
|
},
|
|
}, &models.FindFilterType{
|
|
PerPage: &pp,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
verifyInt(t, count, imageCountCriterion)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func TestStudioQueryGalleryCount(t *testing.T) {
|
|
const galleryCount = 1
|
|
galleryCountCriterion := models.IntCriterionInput{
|
|
Value: galleryCount,
|
|
Modifier: models.CriterionModifierEquals,
|
|
}
|
|
|
|
verifyStudiosGalleryCount(t, galleryCountCriterion)
|
|
|
|
galleryCountCriterion.Modifier = models.CriterionModifierNotEquals
|
|
verifyStudiosGalleryCount(t, galleryCountCriterion)
|
|
|
|
galleryCountCriterion.Modifier = models.CriterionModifierGreaterThan
|
|
verifyStudiosGalleryCount(t, galleryCountCriterion)
|
|
|
|
galleryCountCriterion.Modifier = models.CriterionModifierLessThan
|
|
verifyStudiosGalleryCount(t, galleryCountCriterion)
|
|
}
|
|
|
|
func verifyStudiosGalleryCount(t *testing.T, galleryCountCriterion models.IntCriterionInput) {
|
|
withTxn(func(r models.Repository) error {
|
|
sqb := r.Studio()
|
|
studioFilter := models.StudioFilterType{
|
|
GalleryCount: &galleryCountCriterion,
|
|
}
|
|
|
|
studios := queryStudio(t, sqb, &studioFilter, nil)
|
|
assert.Greater(t, len(studios), 0)
|
|
|
|
for _, studio := range studios {
|
|
pp := 0
|
|
|
|
_, count, err := r.Gallery().Query(&models.GalleryFilterType{
|
|
Studios: &models.HierarchicalMultiCriterionInput{
|
|
Value: []string{strconv.Itoa(studio.ID)},
|
|
Modifier: models.CriterionModifierIncludes,
|
|
Depth: 0,
|
|
},
|
|
}, &models.FindFilterType{
|
|
PerPage: &pp,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
verifyInt(t, count, galleryCountCriterion)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func TestStudioStashIDs(t *testing.T) {
|
|
if err := withTxn(func(r models.Repository) error {
|
|
qb := r.Studio()
|
|
|
|
// create studio to test against
|
|
const name = "TestStudioStashIDs"
|
|
created, err := createStudio(r.Studio(), name, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating studio: %s", err.Error())
|
|
}
|
|
|
|
testStashIDReaderWriter(t, qb, created.ID)
|
|
return nil
|
|
}); err != nil {
|
|
t.Error(err.Error())
|
|
}
|
|
}
|
|
|
|
func TestStudioQueryURL(t *testing.T) {
|
|
const sceneIdx = 1
|
|
studioURL := getStudioStringValue(sceneIdx, urlField)
|
|
|
|
urlCriterion := models.StringCriterionInput{
|
|
Value: studioURL,
|
|
Modifier: models.CriterionModifierEquals,
|
|
}
|
|
|
|
filter := models.StudioFilterType{
|
|
URL: &urlCriterion,
|
|
}
|
|
|
|
verifyFn := func(g *models.Studio) {
|
|
t.Helper()
|
|
verifyNullString(t, g.URL, urlCriterion)
|
|
}
|
|
|
|
verifyStudioQuery(t, filter, verifyFn)
|
|
|
|
urlCriterion.Modifier = models.CriterionModifierNotEquals
|
|
verifyStudioQuery(t, filter, verifyFn)
|
|
|
|
urlCriterion.Modifier = models.CriterionModifierMatchesRegex
|
|
urlCriterion.Value = "studio_.*1_URL"
|
|
verifyStudioQuery(t, filter, verifyFn)
|
|
|
|
urlCriterion.Modifier = models.CriterionModifierNotMatchesRegex
|
|
verifyStudioQuery(t, filter, verifyFn)
|
|
|
|
urlCriterion.Modifier = models.CriterionModifierIsNull
|
|
urlCriterion.Value = ""
|
|
verifyStudioQuery(t, filter, verifyFn)
|
|
|
|
urlCriterion.Modifier = models.CriterionModifierNotNull
|
|
verifyStudioQuery(t, filter, verifyFn)
|
|
}
|
|
|
|
func TestStudioQueryRating(t *testing.T) {
|
|
const rating = 3
|
|
ratingCriterion := models.IntCriterionInput{
|
|
Value: rating,
|
|
Modifier: models.CriterionModifierEquals,
|
|
}
|
|
|
|
verifyStudiosRating(t, ratingCriterion)
|
|
|
|
ratingCriterion.Modifier = models.CriterionModifierNotEquals
|
|
verifyStudiosRating(t, ratingCriterion)
|
|
|
|
ratingCriterion.Modifier = models.CriterionModifierGreaterThan
|
|
verifyStudiosRating(t, ratingCriterion)
|
|
|
|
ratingCriterion.Modifier = models.CriterionModifierLessThan
|
|
verifyStudiosRating(t, ratingCriterion)
|
|
|
|
ratingCriterion.Modifier = models.CriterionModifierIsNull
|
|
verifyStudiosRating(t, ratingCriterion)
|
|
|
|
ratingCriterion.Modifier = models.CriterionModifierNotNull
|
|
verifyStudiosRating(t, ratingCriterion)
|
|
}
|
|
|
|
func verifyStudioQuery(t *testing.T, filter models.StudioFilterType, verifyFn func(s *models.Studio)) {
|
|
withTxn(func(r models.Repository) error {
|
|
t.Helper()
|
|
sqb := r.Studio()
|
|
|
|
studios := queryStudio(t, sqb, &filter, nil)
|
|
|
|
// assume it should find at least one
|
|
assert.Greater(t, len(studios), 0)
|
|
|
|
for _, studio := range studios {
|
|
verifyFn(studio)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func verifyStudiosRating(t *testing.T, ratingCriterion models.IntCriterionInput) {
|
|
withTxn(func(r models.Repository) error {
|
|
sqb := r.Studio()
|
|
studioFilter := models.StudioFilterType{
|
|
Rating: &ratingCriterion,
|
|
}
|
|
|
|
studios, _, err := sqb.Query(&studioFilter, nil)
|
|
|
|
if err != nil {
|
|
t.Errorf("Error querying studio: %s", err.Error())
|
|
}
|
|
|
|
for _, studio := range studios {
|
|
verifyInt64(t, studio.Rating, ratingCriterion)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func TestStudioQueryIsMissingRating(t *testing.T) {
|
|
withTxn(func(r models.Repository) error {
|
|
sqb := r.Studio()
|
|
isMissing := "rating"
|
|
studioFilter := models.StudioFilterType{
|
|
IsMissing: &isMissing,
|
|
}
|
|
|
|
studios, _, err := sqb.Query(&studioFilter, nil)
|
|
|
|
if err != nil {
|
|
t.Errorf("Error querying studio: %s", err.Error())
|
|
}
|
|
|
|
assert.True(t, len(studios) > 0)
|
|
|
|
for _, studio := range studios {
|
|
assert.True(t, !studio.Rating.Valid)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func queryStudio(t *testing.T, sqb models.StudioReader, studioFilter *models.StudioFilterType, findFilter *models.FindFilterType) []*models.Studio {
|
|
studios, _, err := sqb.Query(studioFilter, findFilter)
|
|
if err != nil {
|
|
t.Errorf("Error querying studio: %s", err.Error())
|
|
}
|
|
|
|
return studios
|
|
}
|
|
|
|
// TODO Create
|
|
// TODO Update
|
|
// TODO Destroy
|
|
// TODO Find
|
|
// TODO FindBySceneID
|
|
// TODO Count
|
|
// TODO All
|
|
// TODO AllSlim
|
|
// TODO Query
|