mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Signed-off-by: Michael Mayer <michael@photoprism.app> Co-authored-by: Michael Mayer <michael@photoprism.app> Co-authored-by: graciousgrey <theresagresch@gmail.com>
1637 lines
44 KiB
Go
1637 lines
44 KiB
Go
package entity
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/photoprism/photoprism/internal/ai/classify"
|
|
"github.com/photoprism/photoprism/internal/form"
|
|
"github.com/photoprism/photoprism/pkg/media"
|
|
"github.com/photoprism/photoprism/pkg/rnd"
|
|
"github.com/photoprism/photoprism/pkg/time/tz"
|
|
"github.com/photoprism/photoprism/pkg/txt"
|
|
)
|
|
|
|
func TestSavePhotoForm(t *testing.T) {
|
|
t.Run("Ok", func(t *testing.T) {
|
|
f := form.Photo{
|
|
TakenAt: time.Date(2008, 1, 1, 2, 0, 0, 0, time.UTC),
|
|
TakenAtLocal: time.Date(2008, 1, 1, 2, 0, 0, 0, time.UTC),
|
|
TakenSrc: "manual",
|
|
TimeZone: "test",
|
|
PhotoTitle: "Pink beach",
|
|
TitleSrc: SrcManual,
|
|
PhotoFavorite: true,
|
|
PhotoPrivate: true,
|
|
PhotoType: "image",
|
|
PhotoLat: 7.9999,
|
|
PhotoLng: 8.8888,
|
|
PhotoAltitude: 2,
|
|
PhotoIso: 5,
|
|
PhotoFocalLength: 10,
|
|
PhotoFNumber: 3.3,
|
|
PhotoExposure: "exposure",
|
|
CameraID: uint(3),
|
|
CameraSrc: SrcMeta,
|
|
LensID: uint(6),
|
|
CellID: "1234",
|
|
PlaceSrc: SrcManual,
|
|
PlaceID: "765",
|
|
PhotoCountry: "de",
|
|
Details: form.Details{
|
|
PhotoID: uint(1000008),
|
|
Keywords: "test cat dog",
|
|
Subject: "animals",
|
|
Artist: "Bender",
|
|
Notes: "notes",
|
|
Copyright: "copy",
|
|
License: "",
|
|
},
|
|
}
|
|
|
|
m := PhotoFixtures.Get("Photo08")
|
|
|
|
if err := SavePhotoForm(&m, f); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
Db().First(&m)
|
|
|
|
assert.Equal(t, "manual", m.TakenSrc)
|
|
assert.Equal(t, "test", m.TimeZone)
|
|
assert.Equal(t, "Pink beach", m.PhotoTitle)
|
|
assert.Equal(t, "manual", m.TitleSrc)
|
|
assert.Equal(t, true, m.PhotoFavorite)
|
|
assert.Equal(t, true, m.PhotoPrivate)
|
|
assert.Equal(t, "image", m.PhotoType)
|
|
assert.InEpsilon(t, 7.9999, m.PhotoLat, 0.0001)
|
|
assert.InEpsilon(t, 8.8888, m.PhotoLng, 0.0001)
|
|
assert.NotNil(t, m.EditedAt)
|
|
|
|
t.Log(m.GetDetails().Keywords)
|
|
})
|
|
t.Run("BatchDateChangeKeepsTimeZone", func(t *testing.T) {
|
|
photo := PhotoFixtures.Get("Photo09")
|
|
require.Equal(t, "America/Mexico_City", photo.TimeZone)
|
|
|
|
formSnapshot, err := form.NewPhoto(&photo)
|
|
require.NoError(t, err)
|
|
|
|
newDay := photo.PhotoDay + 1
|
|
formSnapshot.PhotoDay = newDay
|
|
formSnapshot.TakenSrc = SrcBatch
|
|
formSnapshot.TimeZone = photo.TimeZone
|
|
formSnapshot.TakenAtLocal = time.Date(
|
|
photo.PhotoYear,
|
|
time.Month(photo.PhotoMonth),
|
|
newDay,
|
|
photo.TakenAtLocal.Hour(),
|
|
photo.TakenAtLocal.Minute(),
|
|
photo.TakenAtLocal.Second(),
|
|
0,
|
|
time.UTC,
|
|
)
|
|
|
|
require.NoError(t, SavePhotoForm(&photo, formSnapshot))
|
|
require.NoError(t, Db().First(&photo, photo.ID).Error)
|
|
|
|
location := tz.Find(photo.TimeZone)
|
|
require.NotNil(t, location)
|
|
expectedUTC := time.Date(
|
|
photo.PhotoYear,
|
|
time.Month(photo.PhotoMonth),
|
|
newDay,
|
|
photo.TakenAtLocal.Hour(),
|
|
photo.TakenAtLocal.Minute(),
|
|
photo.TakenAtLocal.Second(),
|
|
0,
|
|
location,
|
|
).UTC()
|
|
|
|
assert.Equal(t, newDay, photo.PhotoDay)
|
|
assert.Equal(t, "America/Mexico_City", photo.TimeZone)
|
|
assert.Equal(t, SrcBatch, photo.TakenSrc)
|
|
assert.True(t, photo.TakenAt.Equal(expectedUTC))
|
|
})
|
|
}
|
|
|
|
func TestPhoto_LabelKeywords(t *testing.T) {
|
|
t.Run("CollectsSearchableKeywords", func(t *testing.T) {
|
|
photo := Photo{
|
|
Labels: []PhotoLabel{
|
|
{
|
|
LabelSrc: SrcManual,
|
|
Uncertainty: 0,
|
|
Label: &Label{
|
|
LabelName: "Golden Gate",
|
|
LabelCategories: []*Label{
|
|
{LabelName: "Bridge Monuments"},
|
|
{LabelName: "San Francisco"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// Skipped because source is ignored
|
|
LabelSrc: SrcTitle,
|
|
Label: &Label{LabelName: "Title Based"},
|
|
},
|
|
{
|
|
// Skipped because uncertainty >= 100
|
|
LabelSrc: SrcManual,
|
|
Uncertainty: 150,
|
|
Label: &Label{LabelName: "Too Uncertain"},
|
|
},
|
|
{
|
|
// Safeguard: nil label should be ignored
|
|
LabelSrc: SrcManual,
|
|
},
|
|
},
|
|
}
|
|
|
|
expected := append([]string{}, txt.Keywords("Golden Gate")...)
|
|
expected = append(expected, txt.Keywords("Bridge Monuments")...)
|
|
expected = append(expected, txt.Keywords("San Francisco")...)
|
|
|
|
assert.ElementsMatch(t, expected, photo.LabelKeywords())
|
|
})
|
|
t.Run("NilPhoto", func(t *testing.T) {
|
|
var photo *Photo
|
|
assert.Nil(t, photo.LabelKeywords())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_GetUID(t *testing.T) {
|
|
t.Run("ReturnsPhotoUID", func(t *testing.T) {
|
|
uid := rnd.GenerateUID(PhotoUID)
|
|
photo := &Photo{PhotoUID: uid}
|
|
assert.Equal(t, uid, photo.GetUID())
|
|
})
|
|
t.Run("NilPhoto", func(t *testing.T) {
|
|
var photo *Photo
|
|
assert.Equal(t, "<nil>", photo.GetUID())
|
|
})
|
|
}
|
|
|
|
func photoKeywordWords(t *testing.T, photoID uint) []string {
|
|
t.Helper()
|
|
|
|
type row struct {
|
|
Keyword string
|
|
}
|
|
|
|
var rows []row
|
|
|
|
if err := Db().Table(Keyword{}.TableName()).
|
|
Select("keywords.keyword").
|
|
Joins("JOIN photos_keywords pk ON pk.keyword_id = keywords.id AND pk.photo_id = ?", photoID).
|
|
Scan(&rows).Error; err != nil {
|
|
t.Fatalf("failed querying photo keywords: %v", err)
|
|
}
|
|
|
|
words := make([]string, 0, len(rows))
|
|
|
|
for _, r := range rows {
|
|
words = append(words, r.Keyword)
|
|
}
|
|
|
|
return words
|
|
}
|
|
|
|
func TestPhoto_LabelKeywordIndexing(t *testing.T) {
|
|
t.Run("SaveLabels", func(t *testing.T) {
|
|
fixture := PhotoFixtures.Get("Photo56")
|
|
photo := FindPhoto(fixture)
|
|
require.NotNil(t, photo)
|
|
require.Greater(t, len(photo.Labels), 0)
|
|
|
|
require.NoError(t, Db().Where("photo_id = ?", photo.ID).Delete(&PhotoKeyword{}).Error)
|
|
|
|
originalKeywords := photo.GetDetails().Keywords
|
|
|
|
require.NoError(t, photo.SaveLabels())
|
|
|
|
reloaded := FindPhoto(*photo)
|
|
require.NotNil(t, reloaded)
|
|
assert.Equal(t, originalKeywords, reloaded.GetDetails().Keywords)
|
|
|
|
words := photoKeywordWords(t, photo.ID)
|
|
assert.Contains(t, words, "flower")
|
|
assert.Contains(t, words, "cake")
|
|
assert.NotContains(t, words, "cow") // SrcKeyword entries are skipped
|
|
})
|
|
t.Run("Optimize", func(t *testing.T) {
|
|
fixture := PhotoFixtures.Get("Photo57")
|
|
photo := FindPhoto(fixture)
|
|
require.NotNil(t, photo)
|
|
require.Greater(t, len(photo.Labels), 0)
|
|
|
|
require.NoError(t, Db().Where("photo_id = ?", photo.ID).Delete(&PhotoKeyword{}).Error)
|
|
|
|
originalKeywords := photo.GetDetails().Keywords
|
|
|
|
_, _, err := photo.Optimize(false, false, false, false)
|
|
require.NoError(t, err)
|
|
|
|
reloaded := FindPhoto(*photo)
|
|
require.NotNil(t, reloaded)
|
|
assert.Equal(t, originalKeywords, reloaded.GetDetails().Keywords)
|
|
|
|
words := photoKeywordWords(t, photo.ID)
|
|
assert.Contains(t, words, "flower")
|
|
})
|
|
}
|
|
|
|
func TestPhoto_HasUID(t *testing.T) {
|
|
t.Run("True", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo01")
|
|
assert.True(t, m.HasID())
|
|
assert.True(t, m.HasUID())
|
|
})
|
|
t.Run("False", func(t *testing.T) {
|
|
m := Photo{}
|
|
assert.False(t, m.HasID())
|
|
assert.False(t, m.HasUID())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_GetID(t *testing.T) {
|
|
t.Run("Success", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo01")
|
|
assert.Equal(t, uint(1000001), m.GetID())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_MediaType(t *testing.T) {
|
|
t.Run("Image", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("19800101_000002_D640C559")
|
|
assert.Equal(t, media.Image, m.MediaType())
|
|
})
|
|
t.Run("Raw", func(t *testing.T) {
|
|
m := Photo{PhotoType: "raw", TypeSrc: SrcManual}
|
|
assert.Equal(t, media.Raw, m.MediaType())
|
|
m.ResetMediaType(SrcFile)
|
|
assert.Equal(t, media.Raw, m.MediaType())
|
|
assert.Equal(t, SrcManual, m.TypeSrc)
|
|
})
|
|
t.Run("Live", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo27")
|
|
assert.Equal(t, media.Live, m.MediaType())
|
|
assert.Equal(t, SrcFile, m.TypeSrc)
|
|
assert.Equal(t, media.LiveMaxDuration, m.PhotoDuration)
|
|
m.ResetMediaType(SrcFile)
|
|
assert.Equal(t, media.Image, m.MediaType())
|
|
assert.Equal(t, SrcAuto, m.TypeSrc)
|
|
assert.Equal(t, media.LiveMaxDuration, m.PhotoDuration)
|
|
m.ResetDuration()
|
|
assert.Equal(t, time.Duration(0), m.PhotoDuration)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_HasMediaType(t *testing.T) {
|
|
t.Run("Image", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("19800101_000002_D640C559")
|
|
assert.True(t, m.HasMediaType(media.Image))
|
|
assert.True(t, m.HasMediaType(media.Image, media.Video, media.Live))
|
|
assert.False(t, m.HasMediaType(media.Video, media.Live))
|
|
assert.False(t, m.HasMediaType())
|
|
})
|
|
t.Run("Live", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo27")
|
|
assert.True(t, m.HasMediaType(media.Live))
|
|
assert.True(t, m.HasMediaType(media.Image, media.Video, media.Live))
|
|
assert.False(t, m.HasMediaType(media.Image, media.Animated))
|
|
assert.False(t, m.HasMediaType())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_SetMediaType(t *testing.T) {
|
|
t.Run("Image", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("19800101_000002_D640C559")
|
|
assert.Equal(t, media.Image, m.MediaType())
|
|
assert.Equal(t, SrcAuto, m.TypeSrc)
|
|
m.SetMediaType(media.Video, SrcAuto)
|
|
assert.Equal(t, media.Video, m.MediaType())
|
|
assert.Equal(t, SrcAuto, m.TypeSrc)
|
|
m.SetMediaType(media.Live, SrcAuto)
|
|
assert.Equal(t, media.Live, m.MediaType())
|
|
assert.Equal(t, SrcAuto, m.TypeSrc)
|
|
m.SetMediaType(media.Video, SrcAuto)
|
|
assert.Equal(t, media.Live, m.MediaType())
|
|
assert.Equal(t, SrcAuto, m.TypeSrc)
|
|
m.SetMediaType(media.Image, SrcAuto)
|
|
assert.Equal(t, media.Live, m.MediaType())
|
|
assert.Equal(t, SrcAuto, m.TypeSrc)
|
|
m.SetMediaType("", SrcAuto)
|
|
assert.Equal(t, media.Live, m.MediaType())
|
|
assert.Equal(t, SrcAuto, m.TypeSrc)
|
|
m.SetMediaType(media.Video, SrcManual)
|
|
assert.Equal(t, media.Video, m.MediaType())
|
|
assert.Equal(t, SrcManual, m.TypeSrc)
|
|
})
|
|
t.Run("Live", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo27")
|
|
assert.Equal(t, media.Live, m.MediaType())
|
|
m.SetMediaType(media.Image, SrcAuto)
|
|
assert.Equal(t, media.Live, m.MediaType())
|
|
assert.Equal(t, SrcFile, m.TypeSrc)
|
|
m.SetMediaType(media.Image, SrcManual)
|
|
assert.Equal(t, media.Image, m.MediaType())
|
|
assert.Equal(t, SrcManual, m.TypeSrc)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_SaveLabels(t *testing.T) {
|
|
t.Run("NewPhoto", func(t *testing.T) {
|
|
photo := Photo{
|
|
ID: 11111,
|
|
TakenAt: time.Date(2008, 1, 1, 2, 0, 0, 0, time.UTC),
|
|
TakenAtLocal: time.Date(2008, 1, 1, 2, 0, 0, 0, time.UTC),
|
|
TakenSrc: "meta",
|
|
TimeZone: "UTC",
|
|
PhotoTitle: "Black beach",
|
|
TitleSrc: "manual",
|
|
PhotoFavorite: false,
|
|
PhotoPrivate: false,
|
|
PhotoType: "video",
|
|
PhotoLat: 9.9999,
|
|
PhotoLng: 8.8888,
|
|
PhotoAltitude: 2,
|
|
PhotoIso: 5,
|
|
PhotoFocalLength: 10,
|
|
PhotoFNumber: 3.3,
|
|
PhotoExposure: "exposure",
|
|
CameraID: uint(3),
|
|
CameraSrc: "meta",
|
|
LensID: uint(6),
|
|
CellID: "1234",
|
|
PlaceSrc: "geo",
|
|
PlaceID: "765",
|
|
PhotoCountry: "de",
|
|
Keywords: []Keyword{},
|
|
Details: &Details{
|
|
PhotoID: 11111,
|
|
Keywords: "test cat dog",
|
|
Subject: "animals",
|
|
Artist: "Bender",
|
|
Notes: "notes",
|
|
Copyright: "copy",
|
|
License: "",
|
|
},
|
|
}
|
|
|
|
err := photo.SaveLabels()
|
|
|
|
assert.EqualError(t, err, "photo: cannot save to database, id is empty")
|
|
})
|
|
t.Run("ExistingPhoto", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("19800101_000002_D640C559")
|
|
err := m.SaveLabels()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPhoto_ShouldGenerateLabels(t *testing.T) {
|
|
t.Run("NoLabels", func(t *testing.T) {
|
|
p := Photo{}
|
|
assert.True(t, p.ShouldGenerateLabels(false))
|
|
})
|
|
t.Run("Force", func(t *testing.T) {
|
|
p := Photo{Labels: []PhotoLabel{{LabelSrc: string(SrcManual)}}}
|
|
assert.True(t, p.ShouldGenerateLabels(true))
|
|
})
|
|
t.Run("ExistingVisionLabel", func(t *testing.T) {
|
|
p := Photo{Labels: []PhotoLabel{{LabelSrc: string(SrcOllama)}}}
|
|
assert.False(t, p.ShouldGenerateLabels(false))
|
|
})
|
|
t.Run("VisionLabelHighUncertainty", func(t *testing.T) {
|
|
p := Photo{Labels: []PhotoLabel{{LabelSrc: string(SrcOllama), Uncertainty: 100}}}
|
|
assert.True(t, p.ShouldGenerateLabels(false))
|
|
})
|
|
t.Run("CaptionGeneratedLabels", func(t *testing.T) {
|
|
p := Photo{
|
|
Labels: []PhotoLabel{{LabelSrc: string(SrcCaption)}},
|
|
CaptionSrc: SrcOllama,
|
|
}
|
|
assert.False(t, p.ShouldGenerateLabels(false))
|
|
})
|
|
t.Run("ManualLabels", func(t *testing.T) {
|
|
p := Photo{Labels: []PhotoLabel{{LabelSrc: string(SrcManual)}}}
|
|
assert.True(t, p.ShouldGenerateLabels(false))
|
|
})
|
|
t.Run("CaptionManualWithoutVision", func(t *testing.T) {
|
|
p := Photo{
|
|
Labels: []PhotoLabel{{LabelSrc: string(SrcCaption)}},
|
|
CaptionSrc: SrcManual,
|
|
}
|
|
assert.True(t, p.ShouldGenerateLabels(false))
|
|
})
|
|
}
|
|
|
|
func TestPhoto_ShouldGenerateCaption(t *testing.T) {
|
|
ctx := []struct {
|
|
name string
|
|
photo Photo
|
|
source Src
|
|
force bool
|
|
expect bool
|
|
}{
|
|
{
|
|
name: "NoCaptionAutoSource",
|
|
photo: Photo{CaptionSrc: SrcAuto},
|
|
source: SrcOllama,
|
|
expect: true,
|
|
},
|
|
{
|
|
name: "LowerPriority",
|
|
photo: Photo{CaptionSrc: SrcOllama},
|
|
source: SrcImage,
|
|
expect: false,
|
|
},
|
|
{
|
|
name: "HigherPriority",
|
|
photo: Photo{CaptionSrc: SrcImage},
|
|
source: SrcOllama,
|
|
expect: true,
|
|
},
|
|
{
|
|
name: "ForceOverrides",
|
|
photo: Photo{CaptionSrc: SrcImage, PhotoCaption: "existing"},
|
|
source: SrcImage,
|
|
force: true,
|
|
expect: true,
|
|
},
|
|
{
|
|
name: "SamePriorityNoForce",
|
|
photo: Photo{CaptionSrc: SrcOllama, PhotoCaption: "existing"},
|
|
source: SrcOllama,
|
|
expect: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range ctx {
|
|
tc := tc
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := tc.photo.ShouldGenerateCaption(tc.source, tc.force)
|
|
assert.Equal(t, tc.expect, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPhoto_ClassifyLabels(t *testing.T) {
|
|
t.Run("NewPhoto", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo19")
|
|
Db().Set("gorm:auto_preload", true).Model(&m).Related(&m.Labels)
|
|
labels := m.ClassifyLabels()
|
|
assert.Empty(t, labels)
|
|
})
|
|
t.Run("ExistingPhoto", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("19800101_000002_D640C559")
|
|
Db().Set("gorm:auto_preload", true).Model(&m).Related(&m.Labels)
|
|
labels := m.ClassifyLabels()
|
|
assert.LessOrEqual(t, 2, labels.Len())
|
|
})
|
|
t.Run("EmptyLabel", func(t *testing.T) {
|
|
p := Photo{}
|
|
labels := p.ClassifyLabels()
|
|
assert.Empty(t, labels)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_PreloadFiles(t *testing.T) {
|
|
t.Run("Ok", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo01")
|
|
assert.Empty(t, m.Files)
|
|
m.PreloadFiles()
|
|
assert.NotEmpty(t, m.Files)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_PreloadKeywords(t *testing.T) {
|
|
t.Run("Ok", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo01")
|
|
assert.Empty(t, m.Keywords)
|
|
m.PreloadKeywords()
|
|
assert.NotEmpty(t, m.Keywords)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_PreloadAlbums(t *testing.T) {
|
|
t.Run("Ok", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo01")
|
|
assert.Empty(t, m.Albums)
|
|
m.PreloadAlbums()
|
|
assert.NotEmpty(t, m.Albums)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_PreloadMany(t *testing.T) {
|
|
t.Run("Ok", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo01")
|
|
assert.Empty(t, m.Albums)
|
|
assert.Empty(t, m.Files)
|
|
assert.Empty(t, m.Keywords)
|
|
|
|
m.PreloadMany()
|
|
|
|
assert.NotEmpty(t, m.Files)
|
|
assert.NotEmpty(t, m.Albums)
|
|
assert.NotEmpty(t, m.Keywords)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_NoCameraSerial(t *testing.T) {
|
|
t.Run("True", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo04")
|
|
assert.True(t, m.NoCameraSerial())
|
|
})
|
|
t.Run("False", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo05")
|
|
assert.False(t, m.NoCameraSerial())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_GetDetails(t *testing.T) {
|
|
t.Run("True", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("19800101_000002_D640C559")
|
|
result := m.GetDetails()
|
|
|
|
if result == nil {
|
|
t.Fatal("result should never be nil")
|
|
}
|
|
|
|
if result.PhotoID != 1000000 {
|
|
t.Fatal("PhotoID should not be 1000000")
|
|
}
|
|
})
|
|
t.Run("False", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo12")
|
|
result := m.GetDetails()
|
|
|
|
if result == nil {
|
|
t.Fatal("result should never be nil")
|
|
}
|
|
|
|
if result.PhotoID != 1000012 {
|
|
t.Fatal("PhotoID should not be 1000012")
|
|
}
|
|
})
|
|
t.Run("NoID", func(t *testing.T) {
|
|
m := Photo{}
|
|
result := m.GetDetails()
|
|
assert.Equal(t, uint(0x0), result.PhotoID)
|
|
})
|
|
t.Run("NewPhotoWithID", func(t *testing.T) {
|
|
m := Photo{ID: 79550, PhotoUID: "prjwufg1z97rcxff"}
|
|
result := m.GetDetails()
|
|
assert.Equal(t, uint(0x136be), result.PhotoID)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_AddLabels(t *testing.T) {
|
|
resetLabel := func(t *testing.T, photoName, labelName, src string, uncertainty int) {
|
|
t.Helper()
|
|
photo := PhotoFixtures.Get(photoName)
|
|
label := LabelFixtures.Get(labelName)
|
|
assert.NoError(t, UnscopedDb().Model(&PhotoLabel{}).
|
|
Where("photo_id = ? AND label_id = ?", photo.ID, label.ID).
|
|
UpdateColumns(Values{"uncertainty": uncertainty, "label_src": src}).Error)
|
|
}
|
|
|
|
t.Run("Add", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("19800101_000002_D640C559")
|
|
classifyLabels := classify.Labels{{Name: "cactus", Uncertainty: 30, Source: SrcManual, Priority: 5, Categories: []string{"plant"}}}
|
|
len1 := len(m.Labels)
|
|
m.AddLabels(classifyLabels)
|
|
assert.Greater(t, len(m.Labels), len1)
|
|
})
|
|
t.Run("Update", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo15")
|
|
classifyLabels := classify.Labels{{Name: "landscape", Uncertainty: 10, Source: SrcManual, Priority: 5, Categories: []string{"plant"}}}
|
|
assert.Equal(t, 20, m.Labels[0].Uncertainty)
|
|
assert.Equal(t, SrcImage, m.Labels[0].LabelSrc)
|
|
len1 := len(m.Labels)
|
|
m.AddLabels(classifyLabels)
|
|
assert.Equal(t, len(m.Labels), len1)
|
|
assert.Equal(t, 10, m.Labels[0].Uncertainty)
|
|
assert.Equal(t, SrcManual, m.Labels[0].LabelSrc)
|
|
})
|
|
t.Run("OllamaReplacesLowerConfidence", func(t *testing.T) {
|
|
photoName := "Photo15"
|
|
labelName := "landscape"
|
|
resetLabel(t, photoName, labelName, SrcImage, 20)
|
|
|
|
photo := PhotoFixtures.Get(photoName)
|
|
classifyLabels := classify.Labels{{Name: labelName, Uncertainty: 5, Source: SrcOllama}}
|
|
photo.AddLabels(classifyLabels)
|
|
|
|
updated, err := FindPhotoLabel(photo.ID, LabelFixtures.Get(labelName).ID, true)
|
|
if err != nil {
|
|
t.Fatalf("FindPhotoLabel failed: %v", err)
|
|
}
|
|
assert.Equal(t, 5, updated.Uncertainty)
|
|
assert.Equal(t, SrcOllama, updated.LabelSrc)
|
|
})
|
|
t.Run("KeepExistingWhenLessConfident", func(t *testing.T) {
|
|
photoName := "19800101_000002_D640C559"
|
|
labelName := "flower"
|
|
resetLabel(t, photoName, labelName, SrcImage, 20)
|
|
|
|
photo := PhotoFixtures.Get(photoName)
|
|
classifyLabels := classify.Labels{{Name: labelName, Uncertainty: 40, Source: SrcOllama}}
|
|
photo.AddLabels(classifyLabels)
|
|
|
|
updated, err := FindPhotoLabel(photo.ID, LabelFixtures.Get(labelName).ID, true)
|
|
if err != nil {
|
|
t.Fatalf("FindPhotoLabel failed: %v", err)
|
|
}
|
|
assert.Equal(t, 20, updated.Uncertainty)
|
|
assert.Equal(t, SrcImage, updated.LabelSrc)
|
|
})
|
|
t.Run("StoresTopicality", func(t *testing.T) {
|
|
photo := PhotoFixtures.Get("Photo15")
|
|
label := LabelFixtures.Get("landscape")
|
|
|
|
classifyLabels := classify.Labels{{Name: label.LabelSlug, Uncertainty: 15, Source: SrcManual, Topicality: 55}}
|
|
photo.AddLabels(classifyLabels)
|
|
|
|
updated, err := FindPhotoLabel(photo.ID, label.ID, true)
|
|
if err != nil {
|
|
t.Fatalf("FindPhotoLabel failed: %v", err)
|
|
}
|
|
assert.Equal(t, 55, updated.Topicality)
|
|
})
|
|
t.Run("NormalizesProviderSourceCase", func(t *testing.T) {
|
|
photoName := "Photo01"
|
|
labelName := "cow"
|
|
resetLabel(t, photoName, labelName, SrcImage, 20)
|
|
|
|
photo := PhotoFixtures.Get(photoName)
|
|
classifyLabels := classify.Labels{{Name: labelName, Uncertainty: 15, Source: "OlLaMa"}}
|
|
photo.AddLabels(classifyLabels)
|
|
|
|
updated, err := FindPhotoLabel(photo.ID, LabelFixtures.Get(labelName).ID, true)
|
|
if err != nil {
|
|
t.Fatalf("FindPhotoLabel failed: %v", err)
|
|
}
|
|
assert.Equal(t, 15, updated.Uncertainty)
|
|
assert.Equal(t, SrcOllama, updated.LabelSrc)
|
|
})
|
|
t.Run("SkipBlankTitle", func(t *testing.T) {
|
|
photo := PhotoFixtures.Get("Photo15")
|
|
initialLen := len(photo.Labels)
|
|
|
|
var labelCountBefore int
|
|
if err := Db().Model(&Label{}).Where("label_slug = ?", "unknown").Count(&labelCountBefore).Error; err != nil {
|
|
t.Fatalf("count before failed: %v", err)
|
|
}
|
|
|
|
classifyLabels := classify.Labels{{Name: " ", Uncertainty: 30, Source: SrcManual}}
|
|
photo.AddLabels(classifyLabels)
|
|
|
|
assert.Equal(t, initialLen, len(photo.Labels))
|
|
|
|
var labelCountAfter int
|
|
if err := Db().Model(&Label{}).Where("label_slug = ?", "unknown").Count(&labelCountAfter).Error; err != nil {
|
|
t.Fatalf("count after failed: %v", err)
|
|
}
|
|
assert.Equal(t, labelCountBefore, labelCountAfter)
|
|
})
|
|
t.Run("SkipZeroProbability", func(t *testing.T) {
|
|
photo := PhotoFixtures.Get("Photo15")
|
|
initialLen := len(photo.Labels)
|
|
|
|
labelSlug := "zero-probability"
|
|
var labelCountBefore int
|
|
if err := Db().Model(&Label{}).Where("label_slug = ?", labelSlug).Count(&labelCountBefore).Error; err != nil {
|
|
t.Fatalf("count before failed: %v", err)
|
|
}
|
|
|
|
classifyLabels := classify.Labels{{Name: "Zero Probability", Uncertainty: 100, Source: SrcManual}}
|
|
photo.AddLabels(classifyLabels)
|
|
|
|
assert.Equal(t, initialLen, len(photo.Labels))
|
|
|
|
var labelCountAfter int
|
|
if err := Db().Model(&Label{}).Where("label_slug = ?", labelSlug).Count(&labelCountAfter).Error; err != nil {
|
|
t.Fatalf("count after failed: %v", err)
|
|
}
|
|
assert.Equal(t, labelCountBefore, labelCountAfter)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_Delete(t *testing.T) {
|
|
t.Run("NotPermanent", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo16")
|
|
files, err := m.Delete(false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assert.Len(t, files, 1)
|
|
})
|
|
t.Run("Permanent", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo16")
|
|
files, err := m.Delete(true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assert.Len(t, files, 1)
|
|
})
|
|
t.Run("NoID", func(t *testing.T) {
|
|
m := Photo{}
|
|
_, err := m.Delete(true)
|
|
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestPhotos_UIDs(t *testing.T) {
|
|
t.Run("Ok", func(t *testing.T) {
|
|
uid1 := rnd.GenerateUID(PhotoUID)
|
|
uid2 := rnd.GenerateUID(PhotoUID)
|
|
photo1 := &Photo{PhotoUID: uid1}
|
|
photo2 := &Photo{PhotoUID: uid2}
|
|
photos := Photos{photo1, photo2}
|
|
assert.Equal(t, []string{uid1, uid2}, photos.UIDs())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_String(t *testing.T) {
|
|
generatedUID := rnd.GenerateUID(PhotoUID)
|
|
testcases := []struct {
|
|
name string
|
|
photo *Photo
|
|
want string
|
|
checkFmt bool
|
|
}{
|
|
{
|
|
name: "Nil",
|
|
photo: nil,
|
|
want: "Photo<nil>",
|
|
checkFmt: true,
|
|
},
|
|
{
|
|
name: "PhotoNameWithPath",
|
|
photo: &Photo{PhotoPath: "albums/test", PhotoName: "my photo.jpg"},
|
|
want: "'albums/test/my photo.jpg'",
|
|
checkFmt: true,
|
|
},
|
|
{
|
|
name: "PhotoNameOnly",
|
|
photo: &Photo{PhotoName: "photo.jpg"},
|
|
want: "photo.jpg",
|
|
},
|
|
{
|
|
name: "OriginalName",
|
|
photo: &Photo{OriginalName: "orig name.dng"},
|
|
want: "'orig name.dng'",
|
|
},
|
|
{
|
|
name: "UID",
|
|
photo: &Photo{PhotoUID: generatedUID},
|
|
want: fmt.Sprintf("uid %s", generatedUID),
|
|
},
|
|
{
|
|
name: "ID",
|
|
photo: &Photo{ID: 42},
|
|
want: "id 42",
|
|
},
|
|
{
|
|
name: "Fallback",
|
|
photo: &Photo{},
|
|
want: "*Photo",
|
|
checkFmt: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if tc.photo == nil {
|
|
var p *Photo
|
|
assert.Equal(t, tc.want, p.String())
|
|
if tc.checkFmt {
|
|
assert.Equal(t, tc.want, fmt.Sprintf("%s", p))
|
|
}
|
|
return
|
|
}
|
|
|
|
assert.Equal(t, tc.want, tc.photo.String())
|
|
if tc.checkFmt {
|
|
assert.Equal(t, tc.want, fmt.Sprintf("%s", tc.photo))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPhoto_Create(t *testing.T) {
|
|
t.Run("Ok", func(t *testing.T) {
|
|
photo := Photo{PhotoUID: rnd.GenerateUID(PhotoUID), PhotoName: "Holiday", OriginalName: "holidayOriginal2"}
|
|
err := photo.Create()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPhoto_Save(t *testing.T) {
|
|
t.Run("Ok", func(t *testing.T) {
|
|
photo := Photo{PhotoUID: rnd.GenerateUID(PhotoUID), PhotoName: "Holiday", OriginalName: "holidayOriginal2"}
|
|
err := photo.Save()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
t.Run("Error", func(t *testing.T) {
|
|
photo := Photo{PhotoUID: "ps6sg6be2lvl0yh0"}
|
|
assert.Error(t, photo.Save())
|
|
})
|
|
}
|
|
|
|
func TestFindPhoto(t *testing.T) {
|
|
t.Run("Save", func(t *testing.T) {
|
|
photo := Photo{PhotoUID: "pt9atdre2lvl0yhx", PhotoName: "Holiday", OriginalName: "holidayOriginal2"}
|
|
|
|
if err := photo.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.NotNil(t, FindPhoto(photo))
|
|
})
|
|
t.Run("Found", func(t *testing.T) {
|
|
photo := Photo{PhotoUID: "ps6sg6be2lvl0yh0"}
|
|
assert.NotNil(t, photo.Find())
|
|
assert.NotNil(t, FindPhoto(photo))
|
|
})
|
|
t.Run("EmptyStruct", func(t *testing.T) {
|
|
photo := Photo{}
|
|
assert.Nil(t, FindPhoto(photo))
|
|
assert.Nil(t, photo.Find())
|
|
})
|
|
t.Run("InvalidID", func(t *testing.T) {
|
|
photo := Photo{ID: 647487}
|
|
assert.Nil(t, FindPhoto(photo))
|
|
assert.Nil(t, photo.Find())
|
|
})
|
|
t.Run("InvalidUID", func(t *testing.T) {
|
|
photo := Photo{PhotoUID: "ps6sg6be2lvl0iuj"}
|
|
assert.Nil(t, FindPhoto(photo))
|
|
assert.Nil(t, photo.Find())
|
|
})
|
|
t.Run("FindByID", func(t *testing.T) {
|
|
photo := Photo{ID: 1000001}
|
|
assert.NotNil(t, FindPhoto(photo))
|
|
})
|
|
}
|
|
|
|
func TestPhoto_RemoveKeyword(t *testing.T) {
|
|
t.Run("Ok", func(t *testing.T) {
|
|
keyword := Keyword{Keyword: "snake"}
|
|
keyword2 := Keyword{Keyword: "otter"}
|
|
keywords := []Keyword{keyword, keyword2}
|
|
photo := &Photo{Keywords: keywords}
|
|
err := photo.Save()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assert.Equal(t, 2, len(photo.Keywords))
|
|
err = photo.RemoveKeyword("otter")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assert.Equal(t, 2, len(photo.Keywords))
|
|
})
|
|
}
|
|
|
|
func TestPhoto_UpdateLabels(t *testing.T) {
|
|
t.Run("Success", func(t *testing.T) {
|
|
labelNative := Label{LabelName: "Native", LabelSlug: "native"}
|
|
var deletedTime = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
labelWindow := Label{LabelName: "Window", LabelSlug: "window", DeletedAt: &deletedTime}
|
|
|
|
err := labelWindow.Save()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = labelNative.Save()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
details := &Details{
|
|
Subject: "native",
|
|
SubjectSrc: SrcMeta,
|
|
Keywords: "cow, flower, snake, otter",
|
|
KeywordsSrc: SrcMeta,
|
|
}
|
|
photo := Photo{ID: 134567, PhotoTitle: "Cat in the House", Details: details}
|
|
|
|
err = photo.Save()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
p := FindPhoto(photo)
|
|
|
|
assert.Equal(t, 0, len(p.Labels))
|
|
|
|
err = p.UpdateLabels()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
p = FindPhoto(*p)
|
|
|
|
assert.Equal(t, 25, len(p.Details.Keywords))
|
|
assert.Equal(t, 3, len(p.Labels))
|
|
})
|
|
}
|
|
|
|
func TestPhoto_SubjectNames(t *testing.T) {
|
|
t.Run("Photo09", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo09")
|
|
|
|
if names := m.SubjectNames(); len(names) > 0 {
|
|
t.Errorf("no name expected: %#v", names)
|
|
}
|
|
})
|
|
t.Run("Photo10", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo10")
|
|
|
|
if names := m.SubjectNames(); len(names) == 1 {
|
|
assert.Equal(t, "Actor A", names[0])
|
|
} else {
|
|
t.Logf("unstable subject list: %#v", names)
|
|
}
|
|
})
|
|
t.Run("Photo04", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo04")
|
|
|
|
if names := m.SubjectNames(); len(names) != 2 {
|
|
t.Errorf("two names expected: %#v", names)
|
|
} else {
|
|
assert.Equal(t, []string{"Corn McCornface", "Jens Mander"}, names)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPhoto_UpdateSubjectLabels(t *testing.T) {
|
|
labelEgg := Label{LabelName: "Egg", LabelSlug: "egg"}
|
|
var deletedTime = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
labelBird := Label{LabelName: "Bird", LabelSlug: "bird", DeletedAt: &deletedTime}
|
|
|
|
if err := labelBird.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := labelEgg.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
t.Run(`Success`, func(t *testing.T) {
|
|
details := &Details{Subject: "cow, egg, bird", SubjectSrc: SrcMeta}
|
|
photo := Photo{ID: 334567, TitleSrc: SrcName, Details: details}
|
|
|
|
if err := photo.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
p := FindPhoto(photo)
|
|
|
|
assert.Equal(t, 0, len(p.Labels))
|
|
|
|
if err := p.UpdateSubjectLabels(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
p = FindPhoto(*p)
|
|
|
|
assert.Equal(t, "cow, egg, bird", p.Details.Subject)
|
|
assert.Equal(t, 2, len(p.Labels))
|
|
})
|
|
t.Run("EmptySubject", func(t *testing.T) {
|
|
details := &Details{Subject: "", SubjectSrc: SrcMeta}
|
|
photo := Photo{ID: 334568, TitleSrc: SrcName, Details: details}
|
|
|
|
if err := photo.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
p := FindPhoto(photo)
|
|
|
|
assert.Equal(t, 0, len(p.Labels))
|
|
|
|
if err := p.UpdateSubjectLabels(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
p = FindPhoto(*p)
|
|
|
|
assert.Equal(t, "", p.Details.Subject)
|
|
assert.Equal(t, 0, len(p.Labels))
|
|
})
|
|
}
|
|
|
|
func TestPhoto_UpdateKeywordLabels(t *testing.T) {
|
|
labelOtter := Label{LabelName: "Otter", LabelSlug: "otter"}
|
|
var deletedTime = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
labelSnake := Label{LabelName: "Snake", LabelSlug: "snake", DeletedAt: &deletedTime}
|
|
|
|
if err := labelSnake.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := labelOtter.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
t.Run("Success", func(t *testing.T) {
|
|
details := &Details{Keywords: "cow, flower, snake, otter", KeywordsSrc: SrcAuto}
|
|
photo := Photo{ID: 434567, Details: details}
|
|
|
|
if err := photo.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
p := FindPhoto(photo)
|
|
|
|
assert.Equal(t, 0, len(p.Labels))
|
|
|
|
if err := p.UpdateKeywordLabels(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
p = FindPhoto(*p)
|
|
|
|
assert.Equal(t, "cow, flower, snake, otter", p.Details.Keywords)
|
|
assert.Equal(t, 3, len(p.Labels))
|
|
})
|
|
t.Run("EmptyKeywords", func(t *testing.T) {
|
|
details := &Details{Keywords: "", KeywordsSrc: SrcAuto}
|
|
photo := Photo{ID: 434568, Details: details}
|
|
|
|
if err := photo.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
p := FindPhoto(photo)
|
|
|
|
assert.Equal(t, 0, len(p.Labels))
|
|
|
|
if err := p.UpdateKeywordLabels(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
p = FindPhoto(*p)
|
|
|
|
assert.Equal(t, "", p.Details.Keywords)
|
|
assert.Equal(t, 0, len(p.Labels))
|
|
})
|
|
}
|
|
|
|
func TestPhoto_LocationLoaded(t *testing.T) {
|
|
t.Run("Photo", func(t *testing.T) {
|
|
photo := Photo{PhotoUID: rnd.GenerateUID(PhotoUID), PhotoName: "Holiday", OriginalName: "holidayOriginal2"}
|
|
assert.False(t, photo.LocationLoaded())
|
|
})
|
|
t.Run("PhotoWithCell", func(t *testing.T) {
|
|
location := &Cell{Place: nil}
|
|
photo := Photo{PhotoName: "Holiday", Cell: location}
|
|
assert.False(t, photo.LocationLoaded())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_LoadLocation(t *testing.T) {
|
|
t.Run("Ok", func(t *testing.T) {
|
|
photo := PhotoFixtures.Get("Photo03")
|
|
if err := photo.LoadLocation(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
t.Run("UnknownLocation", func(t *testing.T) {
|
|
location := &Cell{Place: nil}
|
|
photo := Photo{PhotoName: "Holiday", Cell: location}
|
|
assert.Error(t, photo.LoadLocation())
|
|
})
|
|
t.Run("KnownLocation", func(t *testing.T) {
|
|
location := CellFixtures.Pointer("mexico")
|
|
photo := Photo{PhotoName: "Holiday", Cell: location}
|
|
assert.Error(t, photo.LoadLocation())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_PlaceLoaded(t *testing.T) {
|
|
t.Run("False", func(t *testing.T) {
|
|
photo := Photo{PhotoUID: rnd.GenerateUID(PhotoUID), PhotoName: "Holiday", OriginalName: "holidayOriginal2"}
|
|
assert.False(t, photo.PlaceLoaded())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_LoadPlace(t *testing.T) {
|
|
t.Run("Ok", func(t *testing.T) {
|
|
photo := PhotoFixtures.Get("Photo03")
|
|
err := photo.LoadPlace()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
})
|
|
t.Run("UnknownLocation", func(t *testing.T) {
|
|
location := &Cell{Place: nil}
|
|
photo := Photo{PhotoName: "Holiday", Cell: location}
|
|
assert.Error(t, photo.LoadPlace())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_AllFilesMissing(t *testing.T) {
|
|
t.Run("True", func(t *testing.T) {
|
|
photo := Photo{ID: 6969866}
|
|
assert.True(t, photo.AllFilesMissing())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_Updates(t *testing.T) {
|
|
t.Run("Ok", func(t *testing.T) {
|
|
photo := Photo{PhotoCaption: "bcss", PhotoName: "InitialName"}
|
|
|
|
if err := photo.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.Equal(t, "InitialName", photo.PhotoName)
|
|
assert.Equal(t, "bcss", photo.PhotoCaption)
|
|
|
|
if err := photo.Updates(Photo{PhotoName: "UpdatedName", PhotoCaption: "UpdatedDesc"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.Equal(t, "UpdatedName", photo.PhotoName)
|
|
assert.Equal(t, "UpdatedDesc", photo.PhotoCaption)
|
|
|
|
})
|
|
}
|
|
|
|
func TestPhoto_SetFavorite(t *testing.T) {
|
|
t.Run("SetTrue", func(t *testing.T) {
|
|
photo := Photo{PhotoFavorite: true}
|
|
|
|
if err := photo.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := photo.SetFavorite(false); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.Equal(t, false, photo.PhotoFavorite)
|
|
})
|
|
t.Run("SetFalse", func(t *testing.T) {
|
|
photo := Photo{PhotoFavorite: false}
|
|
|
|
if err := photo.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := photo.SetFavorite(true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.Equal(t, true, photo.PhotoFavorite)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_SetStack(t *testing.T) {
|
|
t.Run("Ignore", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo27")
|
|
assert.Equal(t, IsStackable, m.PhotoStack)
|
|
m.SetStack(IsStackable)
|
|
assert.Equal(t, IsStackable, m.PhotoStack)
|
|
})
|
|
t.Run("Update", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo27")
|
|
assert.Equal(t, IsStackable, m.PhotoStack)
|
|
m.SetStack(IsUnstacked)
|
|
assert.Equal(t, IsUnstacked, m.PhotoStack)
|
|
m.SetStack(IsStackable)
|
|
assert.Equal(t, IsStackable, m.PhotoStack)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_Approve(t *testing.T) {
|
|
t.Run("Quality4", func(t *testing.T) {
|
|
photo := Photo{PhotoQuality: 4}
|
|
|
|
if err := photo.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := photo.Approve(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.Equal(t, 4, photo.PhotoQuality)
|
|
})
|
|
t.Run("Quality1", func(t *testing.T) {
|
|
photo := Photo{PhotoQuality: 1}
|
|
|
|
if err := photo.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.False(t, photo.Approved())
|
|
|
|
if err := photo.Approve(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.Equal(t, 3, photo.PhotoQuality)
|
|
assert.True(t, photo.Approved())
|
|
})
|
|
t.Run("NoID", func(t *testing.T) {
|
|
photo := Photo{PhotoUID: ""}
|
|
|
|
assert.False(t, photo.Approved())
|
|
|
|
assert.Error(t, photo.Approve())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_Links(t *testing.T) {
|
|
t.Run("OneResult", func(t *testing.T) {
|
|
photo := Photo{PhotoUID: "ps6sg6b1wowuy3c3"}
|
|
links := photo.Links()
|
|
assert.Equal(t, "7jxf3jfn2k", links[0].LinkToken)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_SetPrimary(t *testing.T) {
|
|
t.Run("NoChange", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("19800101_000002_D640C559")
|
|
|
|
f1, err := m.PrimaryFile()
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := m.SetPrimary(""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
f2, err := m.PrimaryFile()
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.Equal(t, f1, f2)
|
|
})
|
|
t.Run("ChangePrimary", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo06")
|
|
|
|
f1, err := m.PrimaryFile()
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.NotEqual(t, f1.FileUID, "fs6sg6bqhhinlplo")
|
|
|
|
if err := m.SetPrimary("fs6sg6bqhhinlplo"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
f2, err := m.PrimaryFile()
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.Equal(t, f2.FileUID, "fs6sg6bqhhinlplo")
|
|
|
|
if err2 := m.SetPrimary("fs6sg6bqhhinlplp"); err2 != nil {
|
|
t.Fatal(err2)
|
|
}
|
|
|
|
f3, err3 := m.PrimaryFile()
|
|
|
|
if err3 != nil {
|
|
t.Fatal(err3)
|
|
}
|
|
|
|
assert.Equal(t, f3.FileUID, "fs6sg6bqhhinlplp")
|
|
})
|
|
t.Run("PhotoUIDEmpty", func(t *testing.T) {
|
|
m := Photo{}
|
|
|
|
err := m.SetPrimary("")
|
|
assert.Error(t, err)
|
|
})
|
|
t.Run("NoPreviewImage", func(t *testing.T) {
|
|
m := Photo{PhotoUID: rnd.GenerateUID(PhotoUID)}
|
|
|
|
err := m.SetPrimary("")
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestMapKey(t *testing.T) {
|
|
assert.Equal(t, "ogh006/abc236", MapKey(time.Date(2016, 11, 11, 9, 7, 18, 0, time.UTC), "abc236"))
|
|
}
|
|
|
|
func TestNewPhoto(t *testing.T) {
|
|
t.Run("Stackable", func(t *testing.T) {
|
|
m := NewPhoto(true)
|
|
assert.Equal(t, IsStackable, m.PhotoStack)
|
|
assert.Equal(t, tz.Local, m.TimeZone)
|
|
})
|
|
t.Run("NotStackable", func(t *testing.T) {
|
|
m := NewPhoto(false)
|
|
assert.Equal(t, IsUnstacked, m.PhotoStack)
|
|
assert.Equal(t, tz.Local, m.TimeZone)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_FirstOrCreate(t *testing.T) {
|
|
t.Run("ExistingPhoto", func(t *testing.T) {
|
|
initialUID := "567454"
|
|
photo := Photo{PhotoUID: initialUID, PhotoName: "Light", OriginalName: "lightBlub.jpg"}
|
|
|
|
if err := photo.Save(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.NotNil(t, FindPhoto(photo))
|
|
assert.Nil(t, FindPhoto(Photo{PhotoUID: initialUID}))
|
|
|
|
created := photo.FirstOrCreate()
|
|
|
|
assert.NotNil(t, created)
|
|
assert.Equal(t, photo.ID, created.ID)
|
|
assert.Equal(t, photo.PhotoUID, created.PhotoUID)
|
|
})
|
|
t.Run("NewPhoto", func(t *testing.T) {
|
|
initialUID := "567459"
|
|
photo := Photo{PhotoUID: initialUID, PhotoName: "Light2", OriginalName: "lightBlub2.jpg"}
|
|
|
|
assert.Nil(t, FindPhoto(photo))
|
|
|
|
if created := photo.FirstOrCreate(); created == nil {
|
|
t.Fatal("created must not be nil")
|
|
} else {
|
|
assert.Truef(t, created.ID > 0, "%d should be > 0", created.ID)
|
|
assert.Equal(t, photo.PhotoUID, created.PhotoUID)
|
|
assert.Nil(t, FindPhoto(Photo{PhotoUID: initialUID}))
|
|
assert.NotNil(t, FindPhoto(photo))
|
|
assert.NotNil(t, FindPhoto(*created))
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPhoto_UnknownCamera(t *testing.T) {
|
|
t.Run("True", func(t *testing.T) {
|
|
photo := Photo{}
|
|
assert.True(t, photo.UnknownCamera())
|
|
})
|
|
t.Run("False", func(t *testing.T) {
|
|
photo := Photo{CameraID: 100000}
|
|
assert.False(t, photo.UnknownCamera())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_UnknownLens(t *testing.T) {
|
|
t.Run("True", func(t *testing.T) {
|
|
photo := Photo{}
|
|
assert.True(t, photo.UnknownLens())
|
|
})
|
|
t.Run("False", func(t *testing.T) {
|
|
photo := Photo{LensID: 100000}
|
|
assert.False(t, photo.UnknownLens())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_UpdateDateFields(t *testing.T) {
|
|
t.Run("YearTooSmall", func(t *testing.T) {
|
|
photo := &Photo{TakenAt: time.Date(900, 11, 11, 9, 7, 18, 0, time.UTC)}
|
|
photo.UpdateDateFields()
|
|
assert.Equal(t, time.Date(900, 11, 11, 9, 7, 18, 0, time.UTC), photo.TakenAt)
|
|
assert.Empty(t, photo.TakenAtLocal)
|
|
})
|
|
t.Run("SetToUnknown", func(t *testing.T) {
|
|
photo := &Photo{TakenAt: time.Date(1900, 11, 11, 9, 7, 18, 0, time.UTC), TakenSrc: SrcAuto, CreatedAt: time.Date(1900, 11, 11, 5, 7, 18, 0, time.UTC)}
|
|
photo.UpdateDateFields()
|
|
assert.Equal(t, UnknownYear, photo.PhotoYear)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_SetCamera(t *testing.T) {
|
|
t.Run("CameraNil", func(t *testing.T) {
|
|
photo := &Photo{}
|
|
photo.SetCamera(nil, SrcAuto)
|
|
assert.Empty(t, photo.Camera)
|
|
})
|
|
t.Run("CameraUnknown", func(t *testing.T) {
|
|
photo := &Photo{}
|
|
camera := &Camera{CameraSlug: ""}
|
|
photo.SetCamera(camera, SrcAuto)
|
|
assert.Empty(t, photo.Camera)
|
|
})
|
|
t.Run("DoNotOverwriteManualChanges", func(t *testing.T) {
|
|
cameraOld := &Camera{CameraSlug: "OldCamera", ID: 10000000111}
|
|
photo := &Photo{CameraSrc: SrcManual, Camera: cameraOld, CameraID: 10000000111}
|
|
assert.Equal(t, "OldCamera", photo.Camera.CameraSlug)
|
|
assert.Equal(t, SrcManual, photo.CameraSrc)
|
|
assert.False(t, photo.UnknownCamera())
|
|
camera := &Camera{CameraSlug: "NewCamera"}
|
|
photo.SetCamera(camera, SrcAuto)
|
|
assert.Equal(t, "OldCamera", photo.Camera.CameraSlug)
|
|
})
|
|
t.Run("SetNewCamera", func(t *testing.T) {
|
|
cameraOld := &Camera{CameraSlug: "OldCamera", ID: 10000000111}
|
|
photo := &Photo{CameraSrc: SrcAuto, Camera: cameraOld, CameraID: 10000000111}
|
|
assert.Equal(t, "OldCamera", photo.Camera.CameraSlug)
|
|
camera := &Camera{CameraSlug: "NewCamera"}
|
|
photo.SetCamera(camera, SrcMeta)
|
|
assert.Equal(t, "NewCamera", photo.Camera.CameraSlug)
|
|
})
|
|
t.Run("Scanner", func(t *testing.T) {
|
|
cameraOld := &Camera{CameraSlug: "OldCamera", ID: 10000000111}
|
|
photo := &Photo{CameraSrc: SrcAuto, Camera: cameraOld, CameraID: 10000000111}
|
|
assert.Equal(t, "OldCamera", photo.Camera.CameraSlug)
|
|
assert.False(t, photo.PhotoScan)
|
|
camera := &Camera{CameraSlug: "MSscanner"}
|
|
photo.SetCamera(camera, SrcMeta)
|
|
assert.Equal(t, "MSscanner", photo.Camera.CameraSlug)
|
|
assert.True(t, photo.PhotoScan)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_SetLens(t *testing.T) {
|
|
t.Run("LensNil", func(t *testing.T) {
|
|
photo := &Photo{}
|
|
photo.SetLens(nil, SrcAuto)
|
|
assert.Empty(t, photo.Lens)
|
|
})
|
|
t.Run("LensUnknown", func(t *testing.T) {
|
|
photo := &Photo{}
|
|
lens := &Lens{LensSlug: ""}
|
|
photo.SetLens(lens, SrcAuto)
|
|
assert.Empty(t, photo.Lens)
|
|
})
|
|
t.Run("DoNotOverwriteManualChanges", func(t *testing.T) {
|
|
lensOld := &Lens{LensSlug: "OldLens", ID: 10000000111}
|
|
photo := &Photo{CameraSrc: SrcManual, Lens: lensOld, LensID: 10000000111}
|
|
assert.Equal(t, "OldLens", photo.Lens.LensSlug)
|
|
lens := &Lens{LensSlug: "NewLens"}
|
|
photo.SetLens(lens, SrcAuto)
|
|
assert.Equal(t, "OldLens", photo.Lens.LensSlug)
|
|
})
|
|
t.Run("SetNewLens", func(t *testing.T) {
|
|
lensOld := &Lens{LensSlug: "OldLens", ID: 10000000111}
|
|
photo := &Photo{CameraSrc: SrcAuto, Lens: lensOld, LensID: 10000000111}
|
|
assert.Equal(t, "OldLens", photo.Lens.LensSlug)
|
|
lens := &Lens{LensSlug: "NewLens"}
|
|
photo.SetLens(lens, SrcMeta)
|
|
assert.Equal(t, "NewLens", photo.Lens.LensSlug)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_SetExposure(t *testing.T) {
|
|
t.Run("Priority", func(t *testing.T) {
|
|
photo := &Photo{PhotoFocalLength: 5, PhotoFNumber: 3, PhotoIso: 300, PhotoExposure: "45", CameraSrc: SrcMeta}
|
|
photo.SetExposure(8, 9, 500, "66", SrcManual)
|
|
assert.Equal(t, 8, photo.PhotoFocalLength)
|
|
assert.Equal(t, float32(9), photo.PhotoFNumber)
|
|
assert.Equal(t, 500, photo.PhotoIso)
|
|
assert.Equal(t, "66", photo.PhotoExposure)
|
|
})
|
|
t.Run("NoPriority", func(t *testing.T) {
|
|
photo := &Photo{PhotoFocalLength: 5, PhotoFNumber: 3, PhotoIso: 300, PhotoExposure: "45", CameraSrc: SrcManual}
|
|
photo.SetExposure(8, 9, 500, "66", SrcMeta)
|
|
assert.Equal(t, 5, photo.PhotoFocalLength)
|
|
assert.Equal(t, float32(3), photo.PhotoFNumber)
|
|
assert.Equal(t, 300, photo.PhotoIso)
|
|
assert.Equal(t, "45", photo.PhotoExposure)
|
|
})
|
|
t.Run("ValidRange", func(t *testing.T) {
|
|
photo := &Photo{}
|
|
photo.SetExposure(256000, 256000, 256000, "256000", SrcManual)
|
|
assert.Equal(t, 0, photo.PhotoFocalLength)
|
|
assert.Equal(t, float32(0), photo.PhotoFNumber)
|
|
assert.Equal(t, 0, photo.PhotoIso)
|
|
assert.Equal(t, "256000", photo.PhotoExposure)
|
|
photo.SetExposure(1, 1, 1, "1", SrcManual)
|
|
assert.Equal(t, 1, photo.PhotoFocalLength)
|
|
assert.Equal(t, float32(1), photo.PhotoFNumber)
|
|
assert.Equal(t, 1, photo.PhotoIso)
|
|
assert.Equal(t, "1", photo.PhotoExposure)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_AllFiles(t *testing.T) {
|
|
t.Run("PhotoWithFiles", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo01")
|
|
files := m.AllFiles()
|
|
assert.Equal(t, 2, len(files))
|
|
})
|
|
t.Run("PhotoWithoutFiles", func(t *testing.T) {
|
|
m := &Photo{ID: 100000023456}
|
|
files := m.AllFiles()
|
|
assert.Equal(t, 0, len(files))
|
|
})
|
|
}
|
|
|
|
func TestPhoto_ArchiveRestore(t *testing.T) {
|
|
t.Run("NotYetArchived", func(t *testing.T) {
|
|
m := &Photo{ID: 10000, PhotoUID: "prjwufg1z97rcxff", PhotoTitle: "HappyLilly"}
|
|
assert.Empty(t, m.DeletedAt)
|
|
err := m.Archive()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assert.NotEmpty(t, m.DeletedAt)
|
|
err = m.Restore()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assert.Empty(t, m.DeletedAt)
|
|
})
|
|
t.Run("AlreadyArchived", func(t *testing.T) {
|
|
var deletedTime = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
m := &Photo{ID: 10000, PhotoUID: "prjwufg1z97rcxff", PhotoTitle: "HappyLilly", DeletedAt: &deletedTime}
|
|
assert.NotEmpty(t, m.DeletedAt)
|
|
err := m.Archive()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assert.NotEmpty(t, m.DeletedAt)
|
|
err = m.Restore()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assert.Empty(t, m.DeletedAt)
|
|
})
|
|
t.Run("NoID", func(t *testing.T) {
|
|
m := &Photo{PhotoTitle: "HappyLilly"}
|
|
err := m.Archive()
|
|
assert.Error(t, err)
|
|
err = m.Restore()
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_SetCameraSerial(t *testing.T) {
|
|
m := &Photo{}
|
|
assert.Empty(t, m.CameraSerial)
|
|
m.SetCameraSerial("abcCamera")
|
|
assert.Equal(t, "abcCamera", m.CameraSerial)
|
|
}
|
|
|
|
func TestPhoto_MapKey(t *testing.T) {
|
|
m := &Photo{TakenAt: time.Date(2016, 11, 11, 9, 7, 18, 0, time.UTC), CellID: "abc236"}
|
|
assert.Equal(t, "ogh006/abc236", m.MapKey())
|
|
}
|
|
|
|
func TestPhoto_FaceCount(t *testing.T) {
|
|
t.Run("Photo04", func(t *testing.T) {
|
|
m := PhotoFixtures.Get("Photo04")
|
|
assert.Equal(t, 3, m.FaceCount())
|
|
})
|
|
}
|
|
|
|
func TestPhoto_Indexed(t *testing.T) {
|
|
t.Run("Success", func(t *testing.T) {
|
|
photo := Photo{}
|
|
assert.True(t, photo.IsNewlyIndexed())
|
|
photo.Indexed()
|
|
assert.False(t, photo.IsNewlyIndexed())
|
|
assert.IsType(t, &time.Time{}, photo.IndexedAt)
|
|
})
|
|
}
|
|
|
|
func TestPhoto_IsNewlyIndexed(t *testing.T) {
|
|
t.Run("ChangeStatus", func(t *testing.T) {
|
|
photo := Photo{IndexedAt: nil}
|
|
assert.True(t, photo.IsNewlyIndexed())
|
|
photo.Indexed()
|
|
assert.False(t, photo.IsNewlyIndexed())
|
|
})
|
|
t.Run("ZeroTimestamp", func(t *testing.T) {
|
|
zero := time.Time{}
|
|
photo := Photo{IndexedAt: &zero}
|
|
assert.True(t, photo.IsNewlyIndexed())
|
|
})
|
|
t.Run("HasIndexedAt", func(t *testing.T) {
|
|
photo := Photo{IndexedAt: TimeStamp()}
|
|
assert.False(t, photo.IsNewlyIndexed())
|
|
})
|
|
t.Run("HasDeletedAt", func(t *testing.T) {
|
|
photo := Photo{IndexedAt: nil, DeletedAt: TimeStamp()}
|
|
assert.False(t, photo.IsNewlyIndexed())
|
|
})
|
|
}
|