package test import ( "RenewCMS/internal/domain/article" "RenewCMS/internal/domain/audit" "RenewCMS/internal/domain/author" "RenewCMS/internal/domain/contenttype" "RenewCMS/internal/domain/entry" "RenewCMS/internal/domain/image" "RenewCMS/internal/domain/site" "RenewCMS/internal/domain/transaction" "RenewCMS/internal/infrastructure/useCases" "context" "io" "log" "maps" ) type mergeSnapshotter interface { mergeSnapshot() func() } func (m *mockImageRepo) mergeSnapshot() func() { saved, nextID := maps.Clone(m.images), m.nextID return func() { m.images, m.nextID = maps.Clone(saved), nextID } } func (m *mockArticleRepo) mergeSnapshot() func() { saved := maps.Clone(m.articles) return func() { m.articles = maps.Clone(saved) } } func (m *mockSiteRepo) mergeSnapshot() func() { saved := maps.Clone(m.sites) return func() { m.sites = maps.Clone(saved) } } func (m *mockAuthorRepo) mergeSnapshot() func() { saved := maps.Clone(m.authors) return func() { m.authors = maps.Clone(saved) } } func (m *mockEntryRepo) mergeSnapshot() func() { saved := maps.Clone(m.entries) return func() { m.entries = maps.Clone(saved) } } type mockMergeTransactor struct { repos transaction.Repositories failBegin error failCommit error runCalls int committed bool afterCommit func() } var _ transaction.Transactor = &mockMergeTransactor{} func newMergeTransactor(images image.Repository, articles article.Repository, sites site.Repository, authors author.Repository, entries entry.Repository) *mockMergeTransactor { return &mockMergeTransactor{repos: transaction.Repositories{Articles: articles, Sites: sites, Authors: authors, Entries: entries, Images: images}} } func (m *mockMergeTransactor) snapshots() []func() { var restores []func() for _, repo := range []any{m.repos.Articles, m.repos.Sites, m.repos.Authors, m.repos.Entries, m.repos.Images} { if snapshotter, ok := repo.(mergeSnapshotter); ok { restores = append(restores, snapshotter.mergeSnapshot()) } } return restores } func restoreAll(restores []func()) { for _, restore := range restores { restore() } } func (m *mockMergeTransactor) Run(ctx context.Context, work func(transaction.Repositories) error) (err error) { m.runCalls++ m.committed = false if m.failBegin != nil { return m.failBegin } restores := m.snapshots() defer func() { if recovered := recover(); recovered != nil { restoreAll(restores) panic(recovered) } }() if err := work(m.repos); err != nil { restoreAll(restores) return err } if m.failCommit != nil { restoreAll(restores) return m.failCommit } m.committed = true if m.afterCommit != nil { m.afterCommit() } return nil } type mergeFileCall struct { name string committed bool } type mergeRecordingStorage struct { *mockImageStorage transactor *mockMergeTransactor calls []mergeFileCall failFor map[string]error } var _ image.Storage = &mergeRecordingStorage{} func newMergeRecordingStorage(transactor *mockMergeTransactor) *mergeRecordingStorage { return &mergeRecordingStorage{mockImageStorage: newMockImageStorage(), transactor: transactor, failFor: map[string]error{}} } func (s *mergeRecordingStorage) Delete(name string) error { s.calls = append(s.calls, mergeFileCall{name: name, committed: s.transactor.committed}) if err, armed := s.failFor[name]; armed { return err } return s.mockImageStorage.Delete(name) } type mergeRecordingVariantFiles struct { *mockVariantFileRepo transactor *mockMergeTransactor calls []mergeFileCall failSource map[string]error } var _ image.VariantFileRepository = &mergeRecordingVariantFiles{} func newMergeRecordingVariantFiles(transactor *mockMergeTransactor) *mergeRecordingVariantFiles { return &mergeRecordingVariantFiles{mockVariantFileRepo: newMockVariantFileRepo(), transactor: transactor, failSource: map[string]error{}} } func (v *mergeRecordingVariantFiles) ListBySource(sourceName string) ([]image.VariantFile, error) { if err, armed := v.failSource[sourceName]; armed { return nil, err } return v.mockVariantFileRepo.ListBySource(sourceName) } func (v *mergeRecordingVariantFiles) Delete(id uint32) error { v.calls = append(v.calls, mergeFileCall{name: v.rows[id].SourceName, committed: v.transactor.committed}) return v.mockVariantFileRepo.Delete(id) } func newTestMergeMedia(images image.Repository, articles article.Repository, storage image.Storage, variantFiles image.VariantFileRepository, recorder audit.Recorder, sites site.Repository, authors author.Repository, types contenttype.Repository, entries entry.Repository) *useCases.MergeMediaUseCase { return useCases.NewMergeMediaUseCase(images, articles, storage, variantFiles, recorder, types, entries, newMergeTransactor(images, articles, sites, authors, entries), log.New(io.Discard, "", 0)) }