refactor(tests): move remaining fake doubles to mockery
- commit
- 2064b93dc06e80f6bda2fb20f2d73cf664cd585d
- author
- brice · · 2026-06-23 15:22 UTC
- committer
- brice · · 2026-06-23 20:01 UTC
- parent
- a605e6b
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
29 files · +3146 -519
M .mockery.yaml +63 -0
| @@ -2,6 +2,47 @@ template: testify | |||
| 2 | 2 | template-data: | |
| 3 | 3 | unroll-variadic: true | |
| 4 | 4 | packages: | |
| 5 | + | github.com/bnema/dumber/internal/bootstrap: | |
| 6 | + | config: | |
| 7 | + | dir: '{{.InterfaceDir}}/mocks' | |
| 8 | + | filename: '{{ .InterfaceFile | base }}' | |
| 9 | + | pkgname: mocks | |
| 10 | + | interfaces: | |
| 11 | + | configChangeManager: | |
| 12 | + | config: | |
| 13 | + | structname: MockConfigChangeManager | |
| 14 | + | github.com/bnema/dumber/internal/cli/cmd: | |
| 15 | + | config: | |
| 16 | + | dir: '{{.InterfaceDir}}/mocks' | |
| 17 | + | filename: '{{ .InterfaceFile | base }}' | |
| 18 | + | pkgname: mocks | |
| 19 | + | interfaces: | |
| 20 | + | sessionManager: | |
| 21 | + | config: | |
| 22 | + | structname: MockSessionManager | |
| 23 | + | github.com/bnema/dumber/internal/infrastructure/clipboard: | |
| 24 | + | config: | |
| 25 | + | dir: '{{.InterfaceDir}}/mocks' | |
| 26 | + | filename: '{{ .InterfaceFile | base }}' | |
| 27 | + | pkgname: mocks | |
| 28 | + | interfaces: | |
| 29 | + | toolkitClipboard: | |
| 30 | + | config: | |
| 31 | + | structname: MockToolkitClipboard | |
| 32 | + | github.com/bnema/dumber/internal/infrastructure/systemviewsbridge: | |
| 33 | + | config: | |
| 34 | + | dir: '{{.InterfaceDir}}/mocks' | |
| 35 | + | filename: '{{ .InterfaceFile | base }}' | |
| 36 | + | pkgname: mocks | |
| 37 | + | interfaces: | |
| 38 | + | Transport: {} | |
| 39 | + | github.com/bnema/purego-pipewire/pipewire: | |
| 40 | + | config: | |
| 41 | + | dir: internal/infrastructure/audio/pipewire/mocks | |
| 42 | + | filename: '{{ .InterfaceFile | base }}' | |
| 43 | + | pkgname: mocks | |
| 44 | + | interfaces: | |
| 45 | + | Player: {} | |
| 5 | 46 | github.com/bnema/dumber/internal/application/port: | |
| 6 | 47 | config: | |
| 7 | 48 | dir: '{{.InterfaceDir}}/mocks' | |
| @@ -58,6 +99,12 @@ packages: | |||
| 58 | 99 | DesktopIntegration: {} | |
| 59 | 100 | DownloadEventHandler: {} | |
| 60 | 101 | DownloadPreparer: {} | |
| 102 | + | FaviconBlobStore: {} | |
| 103 | + | FaviconFetcher: {} | |
| 104 | + | FaviconImageConverter: {} | |
| 105 | + | FaviconInvalidators: {} | |
| 106 | + | FaviconRefreshScheduler: {} | |
| 107 | + | FaviconRepository: {} | |
| 61 | 108 | FaviconService: {} | |
| 62 | 109 | FileSystem: {} | |
| 63 | 110 | FilterManager: {} | |
| @@ -134,3 +181,19 @@ packages: | |||
| 134 | 181 | SpinnerWidget: {} | |
| 135 | 182 | Widget: {} | |
| 136 | 183 | WidgetFactory: {} | |
| 184 | + | github.com/bnema/dumber/internal/ui/coordinator: | |
| 185 | + | config: | |
| 186 | + | dir: '{{.InterfaceDir}}/mocks' | |
| 187 | + | filename: '{{ .InterfaceFile | base }}' | |
| 188 | + | pkgname: mocks | |
| 189 | + | interfaces: | |
| 190 | + | OmniboxProvider: {} | |
| 191 | + | github.com/bnema/dumber/internal/ui/dialog: | |
| 192 | + | config: | |
| 193 | + | dir: '{{.InterfaceDir}}/mocks' | |
| 194 | + | filename: '{{ .InterfaceFile | base }}' | |
| 195 | + | pkgname: mocks | |
| 196 | + | interfaces: | |
| 197 | + | permissionPopup: | |
| 198 | + | config: | |
| 199 | + | structname: MockPermissionPopup | |
M cmd/systemviews/bootstrap_test.go +45 -45
| @@ -17,8 +17,8 @@ import ( | |||
| 17 | 17 | func TestNewBridgeApp_WiresHistoryService(t *testing.T) { | |
| 18 | 18 | t.Parallel() | |
| 19 | 19 | ||
| 20 | - | bridge := &fakeBridgeService{historyEntries: []*entity.HistoryEntry{{Title: "Example", URL: "https://example.com"}}} | |
| 21 | - | app := newBridgeApp(&fakeDOM{}, "dumb://history", bridge) | |
| 20 | + | bridge := &bridgeServiceRecorder{historyEntries: []*entity.HistoryEntry{{Title: "Example", URL: "https://example.com"}}} | |
| 21 | + | app := newBridgeApp(&recordingDOM{}, "dumb://history", bridge) | |
| 22 | 22 | ||
| 23 | 23 | require.NoError(t, app.LoadInitial(context.Background())) | |
| 24 | 24 | assert.True(t, bridge.calledHistory.Load()) | |
| @@ -30,7 +30,7 @@ func TestNewBridgeApp_WiresHistoryService(t *testing.T) { | |||
| 30 | 30 | func TestNewBridgeApp_UsesCurrentConfigForNonConfigRoutes(t *testing.T) { | |
| 31 | 31 | t.Parallel() | |
| 32 | 32 | ||
| 33 | - | bridge := &fakeBridgeService{ | |
| 33 | + | bridge := &bridgeServiceRecorder{ | |
| 34 | 34 | historyEntries: []*entity.HistoryEntry{{Title: "Example", URL: "https://example.com"}}, | |
| 35 | 35 | currentConfig: dto.SystemviewConfigPayload{ | |
| 36 | 36 | Appearance: dto.WebUIAppearanceConfig{ | |
| @@ -47,7 +47,7 @@ func TestNewBridgeApp_UsesCurrentConfigForNonConfigRoutes(t *testing.T) { | |||
| 47 | 47 | }, | |
| 48 | 48 | }, | |
| 49 | 49 | } | |
| 50 | - | dom := &fakeDOM{mounts: make(chan string, 4)} | |
| 50 | + | dom := &recordingDOM{mounts: make(chan string, 4)} | |
| 51 | 51 | app := newBridgeApp(dom, "dumb://history", bridge) | |
| 52 | 52 | ||
| 53 | 53 | require.NoError(t, app.Run()) | |
| @@ -62,12 +62,12 @@ func TestNewBridgeApp_UsesCurrentConfigForNonConfigRoutes(t *testing.T) { | |||
| 62 | 62 | func TestNewBridgeApp_WiresFavoritesService(t *testing.T) { | |
| 63 | 63 | t.Parallel() | |
| 64 | 64 | ||
| 65 | - | bridge := &fakeBridgeService{ | |
| 65 | + | bridge := &bridgeServiceRecorder{ | |
| 66 | 66 | favorites: []*entity.Favorite{{Title: "Example", URL: "https://example.com"}}, | |
| 67 | 67 | folders: []*entity.Folder{{Name: "Read Later"}}, | |
| 68 | 68 | tags: []*entity.Tag{{Name: "Go"}}, | |
| 69 | 69 | } | |
| 70 | - | app := newBridgeApp(&fakeDOM{}, "dumb://favorites", bridge) | |
| 70 | + | app := newBridgeApp(&recordingDOM{}, "dumb://favorites", bridge) | |
| 71 | 71 | ||
| 72 | 72 | require.NoError(t, app.LoadInitial(context.Background())) | |
| 73 | 73 | assert.False(t, bridge.calledHistory.Load()) | |
| @@ -79,11 +79,11 @@ func TestNewBridgeApp_WiresFavoritesService(t *testing.T) { | |||
| 79 | 79 | func TestNewBridgeApp_WiresConfigService(t *testing.T) { | |
| 80 | 80 | t.Parallel() | |
| 81 | 81 | ||
| 82 | - | bridge := &fakeBridgeService{ | |
| 82 | + | bridge := &bridgeServiceRecorder{ | |
| 83 | 83 | currentConfig: dto.SystemviewConfigPayload{EngineType: "webkit"}, | |
| 84 | 84 | keybindings: port.KeybindingsConfig{Groups: []port.KeybindingGroup{{DisplayName: "Default"}}}, | |
| 85 | 85 | } | |
| 86 | - | app := newBridgeApp(&fakeDOM{}, "dumb://config", bridge) | |
| 86 | + | app := newBridgeApp(&recordingDOM{}, "dumb://config", bridge) | |
| 87 | 87 | ||
| 88 | 88 | require.NoError(t, app.LoadInitial(context.Background())) | |
| 89 | 89 | assert.False(t, bridge.calledHistory.Load()) | |
| @@ -92,12 +92,12 @@ func TestNewBridgeApp_WiresConfigService(t *testing.T) { | |||
| 92 | 92 | assert.True(t, bridge.calledKeybindings.Load()) | |
| 93 | 93 | } | |
| 94 | 94 | ||
| 95 | - | type fakeDOM struct { | |
| 95 | + | type recordingDOM struct { | |
| 96 | 96 | html string | |
| 97 | 97 | mounts chan string | |
| 98 | 98 | } | |
| 99 | 99 | ||
| 100 | - | func (f *fakeDOM) Mount(html string) error { | |
| 100 | + | func (f *recordingDOM) Mount(html string) error { | |
| 101 | 101 | f.html = html | |
| 102 | 102 | if f.mounts != nil { | |
| 103 | 103 | select { | |
| @@ -131,9 +131,9 @@ func receiveMountContaining(t *testing.T, mounts <-chan string, values ...string | |||
| 131 | 131 | } | |
| 132 | 132 | } | |
| 133 | 133 | ||
| 134 | - | // Handwritten fake intentionally tracks state across history, favorites, config, | |
| 134 | + | // Recording fixture intentionally tracks state across history, favorites, config, | |
| 135 | 135 | // and keybindings boundaries for the composite bootstrap assertions. | |
| 136 | - | type fakeBridgeService struct { | |
| 136 | + | type bridgeServiceRecorder struct { | |
| 137 | 137 | calledHistory atomic.Bool | |
| 138 | 138 | calledFavorites atomic.Bool | |
| 139 | 139 | calledConfig atomic.Bool | |
| @@ -147,112 +147,112 @@ type fakeBridgeService struct { | |||
| 147 | 147 | keybindings port.KeybindingsConfig | |
| 148 | 148 | } | |
| 149 | 149 | ||
| 150 | - | func (f *fakeBridgeService) Timeline(context.Context, int, int) ([]*entity.HistoryEntry, error) { | |
| 150 | + | func (f *bridgeServiceRecorder) Timeline(context.Context, int, int) ([]*entity.HistoryEntry, error) { | |
| 151 | 151 | f.calledHistory.Store(true) | |
| 152 | 152 | return f.historyEntries, nil | |
| 153 | 153 | } | |
| 154 | 154 | ||
| 155 | - | func (f *fakeBridgeService) TimelineByDomain(context.Context, string, int, int) ([]*entity.HistoryEntry, error) { | |
| 155 | + | func (f *bridgeServiceRecorder) TimelineByDomain(context.Context, string, int, int) ([]*entity.HistoryEntry, error) { | |
| 156 | 156 | f.calledHistory.Store(true) | |
| 157 | 157 | return f.historyEntries, nil | |
| 158 | 158 | } | |
| 159 | 159 | ||
| 160 | - | func (f *fakeBridgeService) TimelineWindow(_ context.Context, before time.Time, _ string) (*entity.HistoryWindow, error) { | |
| 160 | + | func (f *bridgeServiceRecorder) TimelineWindow(_ context.Context, before time.Time, _ string) (*entity.HistoryWindow, error) { | |
| 161 | 161 | f.calledHistory.Store(true) | |
| 162 | 162 | return &entity.HistoryWindow{Entries: f.historyEntries, Before: before, After: before.Add(-24 * time.Hour)}, nil | |
| 163 | 163 | } | |
| 164 | 164 | ||
| 165 | - | func (*fakeBridgeService) Search(context.Context, string, int) ([]*entity.HistoryEntry, error) { | |
| 165 | + | func (*bridgeServiceRecorder) Search(context.Context, string, int) ([]*entity.HistoryEntry, error) { | |
| 166 | 166 | return nil, nil | |
| 167 | 167 | } | |
| 168 | 168 | ||
| 169 | - | func (*fakeBridgeService) DeleteEntry(context.Context, int64) error { return nil } | |
| 169 | + | func (*bridgeServiceRecorder) DeleteEntry(context.Context, int64) error { return nil } | |
| 170 | 170 | ||
| 171 | - | func (*fakeBridgeService) DeleteRange(context.Context, string) error { return nil } | |
| 171 | + | func (*bridgeServiceRecorder) DeleteRange(context.Context, string) error { return nil } | |
| 172 | 172 | ||
| 173 | - | func (*fakeBridgeService) Stats(context.Context) (*entity.HistoryStats, error) { | |
| 173 | + | func (*bridgeServiceRecorder) Stats(context.Context) (*entity.HistoryStats, error) { | |
| 174 | 174 | return nil, nil | |
| 175 | 175 | } | |
| 176 | 176 | ||
| 177 | - | func (*fakeBridgeService) Analytics(context.Context) (*entity.HistoryAnalytics, error) { | |
| 177 | + | func (*bridgeServiceRecorder) Analytics(context.Context) (*entity.HistoryAnalytics, error) { | |
| 178 | 178 | return nil, nil | |
| 179 | 179 | } | |
| 180 | 180 | ||
| 181 | - | func (*fakeBridgeService) DomainStats(context.Context, int) ([]*entity.DomainStat, error) { | |
| 181 | + | func (*bridgeServiceRecorder) DomainStats(context.Context, int) ([]*entity.DomainStat, error) { | |
| 182 | 182 | return nil, nil | |
| 183 | 183 | } | |
| 184 | 184 | ||
| 185 | - | func (*fakeBridgeService) DeleteDomain(context.Context, string) error { return nil } | |
| 185 | + | func (*bridgeServiceRecorder) DeleteDomain(context.Context, string) error { return nil } | |
| 186 | 186 | ||
| 187 | - | func (f *fakeBridgeService) List(context.Context) ([]*entity.Favorite, error) { | |
| 187 | + | func (f *bridgeServiceRecorder) List(context.Context) ([]*entity.Favorite, error) { | |
| 188 | 188 | f.calledFavorites.Store(true) | |
| 189 | 189 | return f.favorites, nil | |
| 190 | 190 | } | |
| 191 | 191 | ||
| 192 | - | func (*fakeBridgeService) CreateFavorite(context.Context, dto.FavoriteCreateInput) (*entity.Favorite, error) { | |
| 192 | + | func (*bridgeServiceRecorder) CreateFavorite(context.Context, dto.FavoriteCreateInput) (*entity.Favorite, error) { | |
| 193 | 193 | return nil, nil | |
| 194 | 194 | } | |
| 195 | 195 | ||
| 196 | - | func (*fakeBridgeService) UpdateFavorite(context.Context, dto.FavoriteUpdateInput) (*entity.Favorite, error) { | |
| 196 | + | func (*bridgeServiceRecorder) UpdateFavorite(context.Context, dto.FavoriteUpdateInput) (*entity.Favorite, error) { | |
| 197 | 197 | return nil, nil | |
| 198 | 198 | } | |
| 199 | 199 | ||
| 200 | - | func (*fakeBridgeService) DeleteFavorite(context.Context, int64) error { return nil } | |
| 200 | + | func (*bridgeServiceRecorder) DeleteFavorite(context.Context, int64) error { return nil } | |
| 201 | 201 | ||
| 202 | - | func (f *fakeBridgeService) ListFolders(context.Context) ([]*entity.Folder, error) { | |
| 202 | + | func (f *bridgeServiceRecorder) ListFolders(context.Context) ([]*entity.Folder, error) { | |
| 203 | 203 | return f.folders, nil | |
| 204 | 204 | } | |
| 205 | 205 | ||
| 206 | - | func (f *fakeBridgeService) ListTags(context.Context) ([]*entity.Tag, error) { | |
| 206 | + | func (f *bridgeServiceRecorder) ListTags(context.Context) ([]*entity.Tag, error) { | |
| 207 | 207 | return f.tags, nil | |
| 208 | 208 | } | |
| 209 | 209 | ||
| 210 | - | func (*fakeBridgeService) SetShortcut(context.Context, int64, *int) error { return nil } | |
| 210 | + | func (*bridgeServiceRecorder) SetShortcut(context.Context, int64, *int) error { return nil } | |
| 211 | 211 | ||
| 212 | - | func (*fakeBridgeService) SetFolder(context.Context, int64, *int64) error { return nil } | |
| 212 | + | func (*bridgeServiceRecorder) SetFolder(context.Context, int64, *int64) error { return nil } | |
| 213 | 213 | ||
| 214 | - | func (*fakeBridgeService) CreateFolder(context.Context, string, string, *int64) (*entity.Folder, error) { | |
| 214 | + | func (*bridgeServiceRecorder) CreateFolder(context.Context, string, string, *int64) (*entity.Folder, error) { | |
| 215 | 215 | return nil, nil | |
| 216 | 216 | } | |
| 217 | 217 | ||
| 218 | - | func (*fakeBridgeService) UpdateFolder(context.Context, int64, string, string) error { return nil } | |
| 218 | + | func (*bridgeServiceRecorder) UpdateFolder(context.Context, int64, string, string) error { return nil } | |
| 219 | 219 | ||
| 220 | - | func (*fakeBridgeService) DeleteFolder(context.Context, int64) error { return nil } | |
| 220 | + | func (*bridgeServiceRecorder) DeleteFolder(context.Context, int64) error { return nil } | |
| 221 | 221 | ||
| 222 | - | func (*fakeBridgeService) CreateTag(context.Context, string, string) (*entity.Tag, error) { | |
| 222 | + | func (*bridgeServiceRecorder) CreateTag(context.Context, string, string) (*entity.Tag, error) { | |
| 223 | 223 | return nil, nil | |
| 224 | 224 | } | |
| 225 | 225 | ||
| 226 | - | func (*fakeBridgeService) UpdateTag(context.Context, int64, string, string) error { return nil } | |
| 226 | + | func (*bridgeServiceRecorder) UpdateTag(context.Context, int64, string, string) error { return nil } | |
| 227 | 227 | ||
| 228 | - | func (*fakeBridgeService) DeleteTag(context.Context, int64) error { return nil } | |
| 228 | + | func (*bridgeServiceRecorder) DeleteTag(context.Context, int64) error { return nil } | |
| 229 | 229 | ||
| 230 | - | func (*fakeBridgeService) AssignTag(context.Context, int64, int64) error { return nil } | |
| 230 | + | func (*bridgeServiceRecorder) AssignTag(context.Context, int64, int64) error { return nil } | |
| 231 | 231 | ||
| 232 | - | func (*fakeBridgeService) RemoveTag(context.Context, int64, int64) error { return nil } | |
| 232 | + | func (*bridgeServiceRecorder) RemoveTag(context.Context, int64, int64) error { return nil } | |
| 233 | 233 | ||
| 234 | - | func (f *fakeBridgeService) Current(context.Context) (dto.SystemviewConfigPayload, error) { | |
| 234 | + | func (f *bridgeServiceRecorder) Current(context.Context) (dto.SystemviewConfigPayload, error) { | |
| 235 | 235 | f.calledConfig.Store(true) | |
| 236 | 236 | return f.currentConfig, nil | |
| 237 | 237 | } | |
| 238 | 238 | ||
| 239 | - | func (*fakeBridgeService) Default(context.Context) (dto.SystemviewConfigPayload, error) { | |
| 239 | + | func (*bridgeServiceRecorder) Default(context.Context) (dto.SystemviewConfigPayload, error) { | |
| 240 | 240 | return dto.SystemviewConfigPayload{}, nil | |
| 241 | 241 | } | |
| 242 | 242 | ||
| 243 | - | func (*fakeBridgeService) Save(context.Context, dto.WebUIConfig) error { return nil } | |
| 243 | + | func (*bridgeServiceRecorder) Save(context.Context, dto.WebUIConfig) error { return nil } | |
| 244 | 244 | ||
| 245 | - | func (f *fakeBridgeService) GetKeybindings(context.Context) (port.KeybindingsConfig, error) { | |
| 245 | + | func (f *bridgeServiceRecorder) GetKeybindings(context.Context) (port.KeybindingsConfig, error) { | |
| 246 | 246 | f.calledKeybindings.Store(true) | |
| 247 | 247 | return f.keybindings, nil | |
| 248 | 248 | } | |
| 249 | 249 | ||
| 250 | - | func (*fakeBridgeService) SetKeybinding(context.Context, port.SetKeybindingRequest) (port.SetKeybindingResponse, error) { | |
| 250 | + | func (*bridgeServiceRecorder) SetKeybinding(context.Context, port.SetKeybindingRequest) (port.SetKeybindingResponse, error) { | |
| 251 | 251 | return port.SetKeybindingResponse{}, nil | |
| 252 | 252 | } | |
| 253 | 253 | ||
| 254 | - | func (*fakeBridgeService) ResetKeybinding(context.Context, port.ResetKeybindingRequest) error { | |
| 254 | + | func (*bridgeServiceRecorder) ResetKeybinding(context.Context, port.ResetKeybindingRequest) error { | |
| 255 | 255 | return nil | |
| 256 | 256 | } | |
| 257 | 257 | ||
| 258 | - | func (*fakeBridgeService) ResetAllKeybindings(context.Context) error { return nil } | |
| 258 | + | func (*bridgeServiceRecorder) ResetAllKeybindings(context.Context) error { return nil } | |
A internal/application/port/mocks/favicon_blob_store.go +525 -0
| @@ -0,0 +1,525 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | "github.com/bnema/dumber/internal/domain/favicon" | |
| 11 | + | mock "github.com/stretchr/testify/mock" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | // NewMockFaviconBlobStore creates a new instance of MockFaviconBlobStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 15 | + | // The first argument is typically a *testing.T value. | |
| 16 | + | func NewMockFaviconBlobStore(t interface { | |
| 17 | + | mock.TestingT | |
| 18 | + | Cleanup(func()) | |
| 19 | + | }) *MockFaviconBlobStore { | |
| 20 | + | mock := &MockFaviconBlobStore{} | |
| 21 | + | mock.Mock.Test(t) | |
| 22 | + | ||
| 23 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 24 | + | ||
| 25 | + | return mock | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | // MockFaviconBlobStore is an autogenerated mock type for the FaviconBlobStore type | |
| 29 | + | type MockFaviconBlobStore struct { | |
| 30 | + | mock.Mock | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | type MockFaviconBlobStore_Expecter struct { | |
| 34 | + | mock *mock.Mock | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | func (_m *MockFaviconBlobStore) EXPECT() *MockFaviconBlobStore_Expecter { | |
| 38 | + | return &MockFaviconBlobStore_Expecter{mock: &_m.Mock} | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | // ReadOriginal provides a mock function for the type MockFaviconBlobStore | |
| 42 | + | func (_mock *MockFaviconBlobStore) ReadOriginal(ctx context.Context, key favicon.Key) ([]byte, string, error) { | |
| 43 | + | ret := _mock.Called(ctx, key) | |
| 44 | + | ||
| 45 | + | if len(ret) == 0 { | |
| 46 | + | panic("no return value specified for ReadOriginal") | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | var r0 []byte | |
| 50 | + | var r1 string | |
| 51 | + | var r2 error | |
| 52 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key) ([]byte, string, error)); ok { | |
| 53 | + | return returnFunc(ctx, key) | |
| 54 | + | } | |
| 55 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key) []byte); ok { | |
| 56 | + | r0 = returnFunc(ctx, key) | |
| 57 | + | } else { | |
| 58 | + | if ret.Get(0) != nil { | |
| 59 | + | r0 = ret.Get(0).([]byte) | |
| 60 | + | } | |
| 61 | + | } | |
| 62 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, favicon.Key) string); ok { | |
| 63 | + | r1 = returnFunc(ctx, key) | |
| 64 | + | } else { | |
| 65 | + | r1 = ret.Get(1).(string) | |
| 66 | + | } | |
| 67 | + | if returnFunc, ok := ret.Get(2).(func(context.Context, favicon.Key) error); ok { | |
| 68 | + | r2 = returnFunc(ctx, key) | |
| 69 | + | } else { | |
| 70 | + | r2 = ret.Error(2) | |
| 71 | + | } | |
| 72 | + | return r0, r1, r2 | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | // MockFaviconBlobStore_ReadOriginal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReadOriginal' | |
| 76 | + | type MockFaviconBlobStore_ReadOriginal_Call struct { | |
| 77 | + | *mock.Call | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | // ReadOriginal is a helper method to define mock.On call | |
| 81 | + | // - ctx context.Context | |
| 82 | + | // - key favicon.Key | |
| 83 | + | func (_e *MockFaviconBlobStore_Expecter) ReadOriginal(ctx any, key any) *MockFaviconBlobStore_ReadOriginal_Call { | |
| 84 | + | return &MockFaviconBlobStore_ReadOriginal_Call{Call: _e.mock.On("ReadOriginal", ctx, key)} | |
| 85 | + | } | |
| 86 | + | ||
| 87 | + | func (_c *MockFaviconBlobStore_ReadOriginal_Call) Run(run func(ctx context.Context, key favicon.Key)) *MockFaviconBlobStore_ReadOriginal_Call { | |
| 88 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 89 | + | var arg0 context.Context | |
| 90 | + | if args[0] != nil { | |
| 91 | + | arg0 = args[0].(context.Context) | |
| 92 | + | } | |
| 93 | + | var arg1 favicon.Key | |
| 94 | + | if args[1] != nil { | |
| 95 | + | arg1 = args[1].(favicon.Key) | |
| 96 | + | } | |
| 97 | + | run( | |
| 98 | + | arg0, | |
| 99 | + | arg1, | |
| 100 | + | ) | |
| 101 | + | }) | |
| 102 | + | return _c | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | func (_c *MockFaviconBlobStore_ReadOriginal_Call) Return(bytes []byte, s string, err error) *MockFaviconBlobStore_ReadOriginal_Call { | |
| 106 | + | _c.Call.Return(bytes, s, err) | |
| 107 | + | return _c | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | func (_c *MockFaviconBlobStore_ReadOriginal_Call) RunAndReturn(run func(ctx context.Context, key favicon.Key) ([]byte, string, error)) *MockFaviconBlobStore_ReadOriginal_Call { | |
| 111 | + | _c.Call.Return(run) | |
| 112 | + | return _c | |
| 113 | + | } | |
| 114 | + | ||
| 115 | + | // ReadPNG provides a mock function for the type MockFaviconBlobStore | |
| 116 | + | func (_mock *MockFaviconBlobStore) ReadPNG(ctx context.Context, key favicon.Key) ([]byte, string, error) { | |
| 117 | + | ret := _mock.Called(ctx, key) | |
| 118 | + | ||
| 119 | + | if len(ret) == 0 { | |
| 120 | + | panic("no return value specified for ReadPNG") | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | var r0 []byte | |
| 124 | + | var r1 string | |
| 125 | + | var r2 error | |
| 126 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key) ([]byte, string, error)); ok { | |
| 127 | + | return returnFunc(ctx, key) | |
| 128 | + | } | |
| 129 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key) []byte); ok { | |
| 130 | + | r0 = returnFunc(ctx, key) | |
| 131 | + | } else { | |
| 132 | + | if ret.Get(0) != nil { | |
| 133 | + | r0 = ret.Get(0).([]byte) | |
| 134 | + | } | |
| 135 | + | } | |
| 136 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, favicon.Key) string); ok { | |
| 137 | + | r1 = returnFunc(ctx, key) | |
| 138 | + | } else { | |
| 139 | + | r1 = ret.Get(1).(string) | |
| 140 | + | } | |
| 141 | + | if returnFunc, ok := ret.Get(2).(func(context.Context, favicon.Key) error); ok { | |
| 142 | + | r2 = returnFunc(ctx, key) | |
| 143 | + | } else { | |
| 144 | + | r2 = ret.Error(2) | |
| 145 | + | } | |
| 146 | + | return r0, r1, r2 | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | // MockFaviconBlobStore_ReadPNG_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReadPNG' | |
| 150 | + | type MockFaviconBlobStore_ReadPNG_Call struct { | |
| 151 | + | *mock.Call | |
| 152 | + | } | |
| 153 | + | ||
| 154 | + | // ReadPNG is a helper method to define mock.On call | |
| 155 | + | // - ctx context.Context | |
| 156 | + | // - key favicon.Key | |
| 157 | + | func (_e *MockFaviconBlobStore_Expecter) ReadPNG(ctx any, key any) *MockFaviconBlobStore_ReadPNG_Call { | |
| 158 | + | return &MockFaviconBlobStore_ReadPNG_Call{Call: _e.mock.On("ReadPNG", ctx, key)} | |
| 159 | + | } | |
| 160 | + | ||
| 161 | + | func (_c *MockFaviconBlobStore_ReadPNG_Call) Run(run func(ctx context.Context, key favicon.Key)) *MockFaviconBlobStore_ReadPNG_Call { | |
| 162 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 163 | + | var arg0 context.Context | |
| 164 | + | if args[0] != nil { | |
| 165 | + | arg0 = args[0].(context.Context) | |
| 166 | + | } | |
| 167 | + | var arg1 favicon.Key | |
| 168 | + | if args[1] != nil { | |
| 169 | + | arg1 = args[1].(favicon.Key) | |
| 170 | + | } | |
| 171 | + | run( | |
| 172 | + | arg0, | |
| 173 | + | arg1, | |
| 174 | + | ) | |
| 175 | + | }) | |
| 176 | + | return _c | |
| 177 | + | } | |
| 178 | + | ||
| 179 | + | func (_c *MockFaviconBlobStore_ReadPNG_Call) Return(bytes []byte, s string, err error) *MockFaviconBlobStore_ReadPNG_Call { | |
| 180 | + | _c.Call.Return(bytes, s, err) | |
| 181 | + | return _c | |
| 182 | + | } | |
| 183 | + | ||
| 184 | + | func (_c *MockFaviconBlobStore_ReadPNG_Call) RunAndReturn(run func(ctx context.Context, key favicon.Key) ([]byte, string, error)) *MockFaviconBlobStore_ReadPNG_Call { | |
| 185 | + | _c.Call.Return(run) | |
| 186 | + | return _c | |
| 187 | + | } | |
| 188 | + | ||
| 189 | + | // ReadSizedPNG provides a mock function for the type MockFaviconBlobStore | |
| 190 | + | func (_mock *MockFaviconBlobStore) ReadSizedPNG(ctx context.Context, key favicon.Key, size int) ([]byte, string, error) { | |
| 191 | + | ret := _mock.Called(ctx, key, size) | |
| 192 | + | ||
| 193 | + | if len(ret) == 0 { | |
| 194 | + | panic("no return value specified for ReadSizedPNG") | |
| 195 | + | } | |
| 196 | + | ||
| 197 | + | var r0 []byte | |
| 198 | + | var r1 string | |
| 199 | + | var r2 error | |
| 200 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key, int) ([]byte, string, error)); ok { | |
| 201 | + | return returnFunc(ctx, key, size) | |
| 202 | + | } | |
| 203 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key, int) []byte); ok { | |
| 204 | + | r0 = returnFunc(ctx, key, size) | |
| 205 | + | } else { | |
| 206 | + | if ret.Get(0) != nil { | |
| 207 | + | r0 = ret.Get(0).([]byte) | |
| 208 | + | } | |
| 209 | + | } | |
| 210 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, favicon.Key, int) string); ok { | |
| 211 | + | r1 = returnFunc(ctx, key, size) | |
| 212 | + | } else { | |
| 213 | + | r1 = ret.Get(1).(string) | |
| 214 | + | } | |
| 215 | + | if returnFunc, ok := ret.Get(2).(func(context.Context, favicon.Key, int) error); ok { | |
| 216 | + | r2 = returnFunc(ctx, key, size) | |
| 217 | + | } else { | |
| 218 | + | r2 = ret.Error(2) | |
| 219 | + | } | |
| 220 | + | return r0, r1, r2 | |
| 221 | + | } | |
| 222 | + | ||
| 223 | + | // MockFaviconBlobStore_ReadSizedPNG_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReadSizedPNG' | |
| 224 | + | type MockFaviconBlobStore_ReadSizedPNG_Call struct { | |
| 225 | + | *mock.Call | |
| 226 | + | } | |
| 227 | + | ||
| 228 | + | // ReadSizedPNG is a helper method to define mock.On call | |
| 229 | + | // - ctx context.Context | |
| 230 | + | // - key favicon.Key | |
| 231 | + | // - size int | |
| 232 | + | func (_e *MockFaviconBlobStore_Expecter) ReadSizedPNG(ctx any, key any, size any) *MockFaviconBlobStore_ReadSizedPNG_Call { | |
| 233 | + | return &MockFaviconBlobStore_ReadSizedPNG_Call{Call: _e.mock.On("ReadSizedPNG", ctx, key, size)} | |
| 234 | + | } | |
| 235 | + | ||
| 236 | + | func (_c *MockFaviconBlobStore_ReadSizedPNG_Call) Run(run func(ctx context.Context, key favicon.Key, size int)) *MockFaviconBlobStore_ReadSizedPNG_Call { | |
| 237 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 238 | + | var arg0 context.Context | |
| 239 | + | if args[0] != nil { | |
| 240 | + | arg0 = args[0].(context.Context) | |
| 241 | + | } | |
| 242 | + | var arg1 favicon.Key | |
| 243 | + | if args[1] != nil { | |
| 244 | + | arg1 = args[1].(favicon.Key) | |
| 245 | + | } | |
| 246 | + | var arg2 int | |
| 247 | + | if args[2] != nil { | |
| 248 | + | arg2 = args[2].(int) | |
| 249 | + | } | |
| 250 | + | run( | |
| 251 | + | arg0, | |
| 252 | + | arg1, | |
| 253 | + | arg2, | |
| 254 | + | ) | |
| 255 | + | }) | |
| 256 | + | return _c | |
| 257 | + | } | |
| 258 | + | ||
| 259 | + | func (_c *MockFaviconBlobStore_ReadSizedPNG_Call) Return(bytes []byte, s string, err error) *MockFaviconBlobStore_ReadSizedPNG_Call { | |
| 260 | + | _c.Call.Return(bytes, s, err) | |
| 261 | + | return _c | |
| 262 | + | } | |
| 263 | + | ||
| 264 | + | func (_c *MockFaviconBlobStore_ReadSizedPNG_Call) RunAndReturn(run func(ctx context.Context, key favicon.Key, size int) ([]byte, string, error)) *MockFaviconBlobStore_ReadSizedPNG_Call { | |
| 265 | + | _c.Call.Return(run) | |
| 266 | + | return _c | |
| 267 | + | } | |
| 268 | + | ||
| 269 | + | // RemoveDerived provides a mock function for the type MockFaviconBlobStore | |
| 270 | + | func (_mock *MockFaviconBlobStore) RemoveDerived(ctx context.Context, key favicon.Key) error { | |
| 271 | + | ret := _mock.Called(ctx, key) | |
| 272 | + | ||
| 273 | + | if len(ret) == 0 { | |
| 274 | + | panic("no return value specified for RemoveDerived") | |
| 275 | + | } | |
| 276 | + | ||
| 277 | + | var r0 error | |
| 278 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key) error); ok { | |
| 279 | + | r0 = returnFunc(ctx, key) | |
| 280 | + | } else { | |
| 281 | + | r0 = ret.Error(0) | |
| 282 | + | } | |
| 283 | + | return r0 | |
| 284 | + | } | |
| 285 | + | ||
| 286 | + | // MockFaviconBlobStore_RemoveDerived_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemoveDerived' | |
| 287 | + | type MockFaviconBlobStore_RemoveDerived_Call struct { | |
| 288 | + | *mock.Call | |
| 289 | + | } | |
| 290 | + | ||
| 291 | + | // RemoveDerived is a helper method to define mock.On call | |
| 292 | + | // - ctx context.Context | |
| 293 | + | // - key favicon.Key | |
| 294 | + | func (_e *MockFaviconBlobStore_Expecter) RemoveDerived(ctx any, key any) *MockFaviconBlobStore_RemoveDerived_Call { | |
| 295 | + | return &MockFaviconBlobStore_RemoveDerived_Call{Call: _e.mock.On("RemoveDerived", ctx, key)} | |
| 296 | + | } | |
| 297 | + | ||
| 298 | + | func (_c *MockFaviconBlobStore_RemoveDerived_Call) Run(run func(ctx context.Context, key favicon.Key)) *MockFaviconBlobStore_RemoveDerived_Call { | |
| 299 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 300 | + | var arg0 context.Context | |
| 301 | + | if args[0] != nil { | |
| 302 | + | arg0 = args[0].(context.Context) | |
| 303 | + | } | |
| 304 | + | var arg1 favicon.Key | |
| 305 | + | if args[1] != nil { | |
| 306 | + | arg1 = args[1].(favicon.Key) | |
| 307 | + | } | |
| 308 | + | run( | |
| 309 | + | arg0, | |
| 310 | + | arg1, | |
| 311 | + | ) | |
| 312 | + | }) | |
| 313 | + | return _c | |
| 314 | + | } | |
| 315 | + | ||
| 316 | + | func (_c *MockFaviconBlobStore_RemoveDerived_Call) Return(err error) *MockFaviconBlobStore_RemoveDerived_Call { | |
| 317 | + | _c.Call.Return(err) | |
| 318 | + | return _c | |
| 319 | + | } | |
| 320 | + | ||
| 321 | + | func (_c *MockFaviconBlobStore_RemoveDerived_Call) RunAndReturn(run func(ctx context.Context, key favicon.Key) error) *MockFaviconBlobStore_RemoveDerived_Call { | |
| 322 | + | _c.Call.Return(run) | |
| 323 | + | return _c | |
| 324 | + | } | |
| 325 | + | ||
| 326 | + | // WriteOriginal provides a mock function for the type MockFaviconBlobStore | |
| 327 | + | func (_mock *MockFaviconBlobStore) WriteOriginal(ctx context.Context, key favicon.Key, data []byte, contentType string) error { | |
| 328 | + | ret := _mock.Called(ctx, key, data, contentType) | |
| 329 | + | ||
| 330 | + | if len(ret) == 0 { | |
| 331 | + | panic("no return value specified for WriteOriginal") | |
| 332 | + | } | |
| 333 | + | ||
| 334 | + | var r0 error | |
| 335 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key, []byte, string) error); ok { | |
| 336 | + | r0 = returnFunc(ctx, key, data, contentType) | |
| 337 | + | } else { | |
| 338 | + | r0 = ret.Error(0) | |
| 339 | + | } | |
| 340 | + | return r0 | |
| 341 | + | } | |
| 342 | + | ||
| 343 | + | // MockFaviconBlobStore_WriteOriginal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteOriginal' | |
| 344 | + | type MockFaviconBlobStore_WriteOriginal_Call struct { | |
| 345 | + | *mock.Call | |
| 346 | + | } | |
| 347 | + | ||
| 348 | + | // WriteOriginal is a helper method to define mock.On call | |
| 349 | + | // - ctx context.Context | |
| 350 | + | // - key favicon.Key | |
| 351 | + | // - data []byte | |
| 352 | + | // - contentType string | |
| 353 | + | func (_e *MockFaviconBlobStore_Expecter) WriteOriginal(ctx any, key any, data any, contentType any) *MockFaviconBlobStore_WriteOriginal_Call { | |
| 354 | + | return &MockFaviconBlobStore_WriteOriginal_Call{Call: _e.mock.On("WriteOriginal", ctx, key, data, contentType)} | |
| 355 | + | } | |
| 356 | + | ||
| 357 | + | func (_c *MockFaviconBlobStore_WriteOriginal_Call) Run(run func(ctx context.Context, key favicon.Key, data []byte, contentType string)) *MockFaviconBlobStore_WriteOriginal_Call { | |
| 358 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 359 | + | var arg0 context.Context | |
| 360 | + | if args[0] != nil { | |
| 361 | + | arg0 = args[0].(context.Context) | |
| 362 | + | } | |
| 363 | + | var arg1 favicon.Key | |
| 364 | + | if args[1] != nil { | |
| 365 | + | arg1 = args[1].(favicon.Key) | |
| 366 | + | } | |
| 367 | + | var arg2 []byte | |
| 368 | + | if args[2] != nil { | |
| 369 | + | arg2 = args[2].([]byte) | |
| 370 | + | } | |
| 371 | + | var arg3 string | |
| 372 | + | if args[3] != nil { | |
| 373 | + | arg3 = args[3].(string) | |
| 374 | + | } | |
| 375 | + | run( | |
| 376 | + | arg0, | |
| 377 | + | arg1, | |
| 378 | + | arg2, | |
| 379 | + | arg3, | |
| 380 | + | ) | |
| 381 | + | }) | |
| 382 | + | return _c | |
| 383 | + | } | |
| 384 | + | ||
| 385 | + | func (_c *MockFaviconBlobStore_WriteOriginal_Call) Return(err error) *MockFaviconBlobStore_WriteOriginal_Call { | |
| 386 | + | _c.Call.Return(err) | |
| 387 | + | return _c | |
| 388 | + | } | |
| 389 | + | ||
| 390 | + | func (_c *MockFaviconBlobStore_WriteOriginal_Call) RunAndReturn(run func(ctx context.Context, key favicon.Key, data []byte, contentType string) error) *MockFaviconBlobStore_WriteOriginal_Call { | |
| 391 | + | _c.Call.Return(run) | |
| 392 | + | return _c | |
| 393 | + | } | |
| 394 | + | ||
| 395 | + | // WritePNG provides a mock function for the type MockFaviconBlobStore | |
| 396 | + | func (_mock *MockFaviconBlobStore) WritePNG(ctx context.Context, key favicon.Key, data []byte) error { | |
| 397 | + | ret := _mock.Called(ctx, key, data) | |
| 398 | + | ||
| 399 | + | if len(ret) == 0 { | |
| 400 | + | panic("no return value specified for WritePNG") | |
| 401 | + | } | |
| 402 | + | ||
| 403 | + | var r0 error | |
| 404 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key, []byte) error); ok { | |
| 405 | + | r0 = returnFunc(ctx, key, data) | |
| 406 | + | } else { | |
| 407 | + | r0 = ret.Error(0) | |
| 408 | + | } | |
| 409 | + | return r0 | |
| 410 | + | } | |
| 411 | + | ||
| 412 | + | // MockFaviconBlobStore_WritePNG_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WritePNG' | |
| 413 | + | type MockFaviconBlobStore_WritePNG_Call struct { | |
| 414 | + | *mock.Call | |
| 415 | + | } | |
| 416 | + | ||
| 417 | + | // WritePNG is a helper method to define mock.On call | |
| 418 | + | // - ctx context.Context | |
| 419 | + | // - key favicon.Key | |
| 420 | + | // - data []byte | |
| 421 | + | func (_e *MockFaviconBlobStore_Expecter) WritePNG(ctx any, key any, data any) *MockFaviconBlobStore_WritePNG_Call { | |
| 422 | + | return &MockFaviconBlobStore_WritePNG_Call{Call: _e.mock.On("WritePNG", ctx, key, data)} | |
| 423 | + | } | |
| 424 | + | ||
| 425 | + | func (_c *MockFaviconBlobStore_WritePNG_Call) Run(run func(ctx context.Context, key favicon.Key, data []byte)) *MockFaviconBlobStore_WritePNG_Call { | |
| 426 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 427 | + | var arg0 context.Context | |
| 428 | + | if args[0] != nil { | |
| 429 | + | arg0 = args[0].(context.Context) | |
| 430 | + | } | |
| 431 | + | var arg1 favicon.Key | |
| 432 | + | if args[1] != nil { | |
| 433 | + | arg1 = args[1].(favicon.Key) | |
| 434 | + | } | |
| 435 | + | var arg2 []byte | |
| 436 | + | if args[2] != nil { | |
| 437 | + | arg2 = args[2].([]byte) | |
| 438 | + | } | |
| 439 | + | run( | |
| 440 | + | arg0, | |
| 441 | + | arg1, | |
| 442 | + | arg2, | |
| 443 | + | ) | |
| 444 | + | }) | |
| 445 | + | return _c | |
| 446 | + | } | |
| 447 | + | ||
| 448 | + | func (_c *MockFaviconBlobStore_WritePNG_Call) Return(err error) *MockFaviconBlobStore_WritePNG_Call { | |
| 449 | + | _c.Call.Return(err) | |
| 450 | + | return _c | |
| 451 | + | } | |
| 452 | + | ||
| 453 | + | func (_c *MockFaviconBlobStore_WritePNG_Call) RunAndReturn(run func(ctx context.Context, key favicon.Key, data []byte) error) *MockFaviconBlobStore_WritePNG_Call { | |
| 454 | + | _c.Call.Return(run) | |
| 455 | + | return _c | |
| 456 | + | } | |
| 457 | + | ||
| 458 | + | // WriteSizedPNG provides a mock function for the type MockFaviconBlobStore | |
| 459 | + | func (_mock *MockFaviconBlobStore) WriteSizedPNG(ctx context.Context, key favicon.Key, size int, data []byte) error { | |
| 460 | + | ret := _mock.Called(ctx, key, size, data) | |
| 461 | + | ||
| 462 | + | if len(ret) == 0 { | |
| 463 | + | panic("no return value specified for WriteSizedPNG") | |
| 464 | + | } | |
| 465 | + | ||
| 466 | + | var r0 error | |
| 467 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key, int, []byte) error); ok { | |
| 468 | + | r0 = returnFunc(ctx, key, size, data) | |
| 469 | + | } else { | |
| 470 | + | r0 = ret.Error(0) | |
| 471 | + | } | |
| 472 | + | return r0 | |
| 473 | + | } | |
| 474 | + | ||
| 475 | + | // MockFaviconBlobStore_WriteSizedPNG_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteSizedPNG' | |
| 476 | + | type MockFaviconBlobStore_WriteSizedPNG_Call struct { | |
| 477 | + | *mock.Call | |
| 478 | + | } | |
| 479 | + | ||
| 480 | + | // WriteSizedPNG is a helper method to define mock.On call | |
| 481 | + | // - ctx context.Context | |
| 482 | + | // - key favicon.Key | |
| 483 | + | // - size int | |
| 484 | + | // - data []byte | |
| 485 | + | func (_e *MockFaviconBlobStore_Expecter) WriteSizedPNG(ctx any, key any, size any, data any) *MockFaviconBlobStore_WriteSizedPNG_Call { | |
| 486 | + | return &MockFaviconBlobStore_WriteSizedPNG_Call{Call: _e.mock.On("WriteSizedPNG", ctx, key, size, data)} | |
| 487 | + | } | |
| 488 | + | ||
| 489 | + | func (_c *MockFaviconBlobStore_WriteSizedPNG_Call) Run(run func(ctx context.Context, key favicon.Key, size int, data []byte)) *MockFaviconBlobStore_WriteSizedPNG_Call { | |
| 490 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 491 | + | var arg0 context.Context | |
| 492 | + | if args[0] != nil { | |
| 493 | + | arg0 = args[0].(context.Context) | |
| 494 | + | } | |
| 495 | + | var arg1 favicon.Key | |
| 496 | + | if args[1] != nil { | |
| 497 | + | arg1 = args[1].(favicon.Key) | |
| 498 | + | } | |
| 499 | + | var arg2 int | |
| 500 | + | if args[2] != nil { | |
| 501 | + | arg2 = args[2].(int) | |
| 502 | + | } | |
| 503 | + | var arg3 []byte | |
| 504 | + | if args[3] != nil { | |
| 505 | + | arg3 = args[3].([]byte) | |
| 506 | + | } | |
| 507 | + | run( | |
| 508 | + | arg0, | |
| 509 | + | arg1, | |
| 510 | + | arg2, | |
| 511 | + | arg3, | |
| 512 | + | ) | |
| 513 | + | }) | |
| 514 | + | return _c | |
| 515 | + | } | |
| 516 | + | ||
| 517 | + | func (_c *MockFaviconBlobStore_WriteSizedPNG_Call) Return(err error) *MockFaviconBlobStore_WriteSizedPNG_Call { | |
| 518 | + | _c.Call.Return(err) | |
| 519 | + | return _c | |
| 520 | + | } | |
| 521 | + | ||
| 522 | + | func (_c *MockFaviconBlobStore_WriteSizedPNG_Call) RunAndReturn(run func(ctx context.Context, key favicon.Key, size int, data []byte) error) *MockFaviconBlobStore_WriteSizedPNG_Call { | |
| 523 | + | _c.Call.Return(run) | |
| 524 | + | return _c | |
| 525 | + | } | |
A internal/application/port/mocks/favicon_converter.go +119 -0
| @@ -0,0 +1,119 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | "github.com/bnema/dumber/internal/application/port" | |
| 11 | + | mock "github.com/stretchr/testify/mock" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | // NewMockFaviconImageConverter creates a new instance of MockFaviconImageConverter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 15 | + | // The first argument is typically a *testing.T value. | |
| 16 | + | func NewMockFaviconImageConverter(t interface { | |
| 17 | + | mock.TestingT | |
| 18 | + | Cleanup(func()) | |
| 19 | + | }) *MockFaviconImageConverter { | |
| 20 | + | mock := &MockFaviconImageConverter{} | |
| 21 | + | mock.Mock.Test(t) | |
| 22 | + | ||
| 23 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 24 | + | ||
| 25 | + | return mock | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | // MockFaviconImageConverter is an autogenerated mock type for the FaviconImageConverter type | |
| 29 | + | type MockFaviconImageConverter struct { | |
| 30 | + | mock.Mock | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | type MockFaviconImageConverter_Expecter struct { | |
| 34 | + | mock *mock.Mock | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | func (_m *MockFaviconImageConverter) EXPECT() *MockFaviconImageConverter_Expecter { | |
| 38 | + | return &MockFaviconImageConverter_Expecter{mock: &_m.Mock} | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | // Convert provides a mock function for the type MockFaviconImageConverter | |
| 42 | + | func (_mock *MockFaviconImageConverter) Convert(ctx context.Context, original []byte, contentType string, sizes []int) (*port.ConvertedFavicon, error) { | |
| 43 | + | ret := _mock.Called(ctx, original, contentType, sizes) | |
| 44 | + | ||
| 45 | + | if len(ret) == 0 { | |
| 46 | + | panic("no return value specified for Convert") | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | var r0 *port.ConvertedFavicon | |
| 50 | + | var r1 error | |
| 51 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, []byte, string, []int) (*port.ConvertedFavicon, error)); ok { | |
| 52 | + | return returnFunc(ctx, original, contentType, sizes) | |
| 53 | + | } | |
| 54 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, []byte, string, []int) *port.ConvertedFavicon); ok { | |
| 55 | + | r0 = returnFunc(ctx, original, contentType, sizes) | |
| 56 | + | } else { | |
| 57 | + | if ret.Get(0) != nil { | |
| 58 | + | r0 = ret.Get(0).(*port.ConvertedFavicon) | |
| 59 | + | } | |
| 60 | + | } | |
| 61 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, []byte, string, []int) error); ok { | |
| 62 | + | r1 = returnFunc(ctx, original, contentType, sizes) | |
| 63 | + | } else { | |
| 64 | + | r1 = ret.Error(1) | |
| 65 | + | } | |
| 66 | + | return r0, r1 | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | // MockFaviconImageConverter_Convert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Convert' | |
| 70 | + | type MockFaviconImageConverter_Convert_Call struct { | |
| 71 | + | *mock.Call | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | // Convert is a helper method to define mock.On call | |
| 75 | + | // - ctx context.Context | |
| 76 | + | // - original []byte | |
| 77 | + | // - contentType string | |
| 78 | + | // - sizes []int | |
| 79 | + | func (_e *MockFaviconImageConverter_Expecter) Convert(ctx any, original any, contentType any, sizes any) *MockFaviconImageConverter_Convert_Call { | |
| 80 | + | return &MockFaviconImageConverter_Convert_Call{Call: _e.mock.On("Convert", ctx, original, contentType, sizes)} | |
| 81 | + | } | |
| 82 | + | ||
| 83 | + | func (_c *MockFaviconImageConverter_Convert_Call) Run(run func(ctx context.Context, original []byte, contentType string, sizes []int)) *MockFaviconImageConverter_Convert_Call { | |
| 84 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 85 | + | var arg0 context.Context | |
| 86 | + | if args[0] != nil { | |
| 87 | + | arg0 = args[0].(context.Context) | |
| 88 | + | } | |
| 89 | + | var arg1 []byte | |
| 90 | + | if args[1] != nil { | |
| 91 | + | arg1 = args[1].([]byte) | |
| 92 | + | } | |
| 93 | + | var arg2 string | |
| 94 | + | if args[2] != nil { | |
| 95 | + | arg2 = args[2].(string) | |
| 96 | + | } | |
| 97 | + | var arg3 []int | |
| 98 | + | if args[3] != nil { | |
| 99 | + | arg3 = args[3].([]int) | |
| 100 | + | } | |
| 101 | + | run( | |
| 102 | + | arg0, | |
| 103 | + | arg1, | |
| 104 | + | arg2, | |
| 105 | + | arg3, | |
| 106 | + | ) | |
| 107 | + | }) | |
| 108 | + | return _c | |
| 109 | + | } | |
| 110 | + | ||
| 111 | + | func (_c *MockFaviconImageConverter_Convert_Call) Return(convertedFavicon *port.ConvertedFavicon, err error) *MockFaviconImageConverter_Convert_Call { | |
| 112 | + | _c.Call.Return(convertedFavicon, err) | |
| 113 | + | return _c | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | func (_c *MockFaviconImageConverter_Convert_Call) RunAndReturn(run func(ctx context.Context, original []byte, contentType string, sizes []int) (*port.ConvertedFavicon, error)) *MockFaviconImageConverter_Convert_Call { | |
| 117 | + | _c.Call.Return(run) | |
| 118 | + | return _c | |
| 119 | + | } | |
A internal/application/port/mocks/favicon_fetcher.go +107 -0
| @@ -0,0 +1,107 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | "github.com/bnema/dumber/internal/application/port" | |
| 11 | + | mock "github.com/stretchr/testify/mock" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | // NewMockFaviconFetcher creates a new instance of MockFaviconFetcher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 15 | + | // The first argument is typically a *testing.T value. | |
| 16 | + | func NewMockFaviconFetcher(t interface { | |
| 17 | + | mock.TestingT | |
| 18 | + | Cleanup(func()) | |
| 19 | + | }) *MockFaviconFetcher { | |
| 20 | + | mock := &MockFaviconFetcher{} | |
| 21 | + | mock.Mock.Test(t) | |
| 22 | + | ||
| 23 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 24 | + | ||
| 25 | + | return mock | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | // MockFaviconFetcher is an autogenerated mock type for the FaviconFetcher type | |
| 29 | + | type MockFaviconFetcher struct { | |
| 30 | + | mock.Mock | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | type MockFaviconFetcher_Expecter struct { | |
| 34 | + | mock *mock.Mock | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | func (_m *MockFaviconFetcher) EXPECT() *MockFaviconFetcher_Expecter { | |
| 38 | + | return &MockFaviconFetcher_Expecter{mock: &_m.Mock} | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | // Fetch provides a mock function for the type MockFaviconFetcher | |
| 42 | + | func (_mock *MockFaviconFetcher) Fetch(ctx context.Context, request port.FaviconFetchRequest) (*port.FaviconFetchedIcon, error) { | |
| 43 | + | ret := _mock.Called(ctx, request) | |
| 44 | + | ||
| 45 | + | if len(ret) == 0 { | |
| 46 | + | panic("no return value specified for Fetch") | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | var r0 *port.FaviconFetchedIcon | |
| 50 | + | var r1 error | |
| 51 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, port.FaviconFetchRequest) (*port.FaviconFetchedIcon, error)); ok { | |
| 52 | + | return returnFunc(ctx, request) | |
| 53 | + | } | |
| 54 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, port.FaviconFetchRequest) *port.FaviconFetchedIcon); ok { | |
| 55 | + | r0 = returnFunc(ctx, request) | |
| 56 | + | } else { | |
| 57 | + | if ret.Get(0) != nil { | |
| 58 | + | r0 = ret.Get(0).(*port.FaviconFetchedIcon) | |
| 59 | + | } | |
| 60 | + | } | |
| 61 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, port.FaviconFetchRequest) error); ok { | |
| 62 | + | r1 = returnFunc(ctx, request) | |
| 63 | + | } else { | |
| 64 | + | r1 = ret.Error(1) | |
| 65 | + | } | |
| 66 | + | return r0, r1 | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | // MockFaviconFetcher_Fetch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Fetch' | |
| 70 | + | type MockFaviconFetcher_Fetch_Call struct { | |
| 71 | + | *mock.Call | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | // Fetch is a helper method to define mock.On call | |
| 75 | + | // - ctx context.Context | |
| 76 | + | // - request port.FaviconFetchRequest | |
| 77 | + | func (_e *MockFaviconFetcher_Expecter) Fetch(ctx any, request any) *MockFaviconFetcher_Fetch_Call { | |
| 78 | + | return &MockFaviconFetcher_Fetch_Call{Call: _e.mock.On("Fetch", ctx, request)} | |
| 79 | + | } | |
| 80 | + | ||
| 81 | + | func (_c *MockFaviconFetcher_Fetch_Call) Run(run func(ctx context.Context, request port.FaviconFetchRequest)) *MockFaviconFetcher_Fetch_Call { | |
| 82 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 83 | + | var arg0 context.Context | |
| 84 | + | if args[0] != nil { | |
| 85 | + | arg0 = args[0].(context.Context) | |
| 86 | + | } | |
| 87 | + | var arg1 port.FaviconFetchRequest | |
| 88 | + | if args[1] != nil { | |
| 89 | + | arg1 = args[1].(port.FaviconFetchRequest) | |
| 90 | + | } | |
| 91 | + | run( | |
| 92 | + | arg0, | |
| 93 | + | arg1, | |
| 94 | + | ) | |
| 95 | + | }) | |
| 96 | + | return _c | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | func (_c *MockFaviconFetcher_Fetch_Call) Return(faviconFetchedIcon *port.FaviconFetchedIcon, err error) *MockFaviconFetcher_Fetch_Call { | |
| 100 | + | _c.Call.Return(faviconFetchedIcon, err) | |
| 101 | + | return _c | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | func (_c *MockFaviconFetcher_Fetch_Call) RunAndReturn(run func(ctx context.Context, request port.FaviconFetchRequest) (*port.FaviconFetchedIcon, error)) *MockFaviconFetcher_Fetch_Call { | |
| 105 | + | _c.Call.Return(run) | |
| 106 | + | return _c | |
| 107 | + | } | |
A internal/application/port/mocks/favicon_invalidator.go +96 -0
| @@ -0,0 +1,96 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | "github.com/bnema/dumber/internal/domain/favicon" | |
| 11 | + | mock "github.com/stretchr/testify/mock" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | // NewMockFaviconInvalidators creates a new instance of MockFaviconInvalidators. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 15 | + | // The first argument is typically a *testing.T value. | |
| 16 | + | func NewMockFaviconInvalidators(t interface { | |
| 17 | + | mock.TestingT | |
| 18 | + | Cleanup(func()) | |
| 19 | + | }) *MockFaviconInvalidators { | |
| 20 | + | mock := &MockFaviconInvalidators{} | |
| 21 | + | mock.Mock.Test(t) | |
| 22 | + | ||
| 23 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 24 | + | ||
| 25 | + | return mock | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | // MockFaviconInvalidators is an autogenerated mock type for the FaviconInvalidators type | |
| 29 | + | type MockFaviconInvalidators struct { | |
| 30 | + | mock.Mock | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | type MockFaviconInvalidators_Expecter struct { | |
| 34 | + | mock *mock.Mock | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | func (_m *MockFaviconInvalidators) EXPECT() *MockFaviconInvalidators_Expecter { | |
| 38 | + | return &MockFaviconInvalidators_Expecter{mock: &_m.Mock} | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | // InvalidateAll provides a mock function for the type MockFaviconInvalidators | |
| 42 | + | func (_mock *MockFaviconInvalidators) InvalidateAll(ctx context.Context, key favicon.Key) error { | |
| 43 | + | ret := _mock.Called(ctx, key) | |
| 44 | + | ||
| 45 | + | if len(ret) == 0 { | |
| 46 | + | panic("no return value specified for InvalidateAll") | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | var r0 error | |
| 50 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key) error); ok { | |
| 51 | + | r0 = returnFunc(ctx, key) | |
| 52 | + | } else { | |
| 53 | + | r0 = ret.Error(0) | |
| 54 | + | } | |
| 55 | + | return r0 | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | // MockFaviconInvalidators_InvalidateAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InvalidateAll' | |
| 59 | + | type MockFaviconInvalidators_InvalidateAll_Call struct { | |
| 60 | + | *mock.Call | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | // InvalidateAll is a helper method to define mock.On call | |
| 64 | + | // - ctx context.Context | |
| 65 | + | // - key favicon.Key | |
| 66 | + | func (_e *MockFaviconInvalidators_Expecter) InvalidateAll(ctx any, key any) *MockFaviconInvalidators_InvalidateAll_Call { | |
| 67 | + | return &MockFaviconInvalidators_InvalidateAll_Call{Call: _e.mock.On("InvalidateAll", ctx, key)} | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | func (_c *MockFaviconInvalidators_InvalidateAll_Call) Run(run func(ctx context.Context, key favicon.Key)) *MockFaviconInvalidators_InvalidateAll_Call { | |
| 71 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 72 | + | var arg0 context.Context | |
| 73 | + | if args[0] != nil { | |
| 74 | + | arg0 = args[0].(context.Context) | |
| 75 | + | } | |
| 76 | + | var arg1 favicon.Key | |
| 77 | + | if args[1] != nil { | |
| 78 | + | arg1 = args[1].(favicon.Key) | |
| 79 | + | } | |
| 80 | + | run( | |
| 81 | + | arg0, | |
| 82 | + | arg1, | |
| 83 | + | ) | |
| 84 | + | }) | |
| 85 | + | return _c | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | func (_c *MockFaviconInvalidators_InvalidateAll_Call) Return(err error) *MockFaviconInvalidators_InvalidateAll_Call { | |
| 89 | + | _c.Call.Return(err) | |
| 90 | + | return _c | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | func (_c *MockFaviconInvalidators_InvalidateAll_Call) RunAndReturn(run func(ctx context.Context, key favicon.Key) error) *MockFaviconInvalidators_InvalidateAll_Call { | |
| 94 | + | _c.Call.Return(run) | |
| 95 | + | return _c | |
| 96 | + | } | |
A internal/application/port/mocks/favicon_repository.go +359 -0
| @@ -0,0 +1,359 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | "time" | |
| 10 | + | ||
| 11 | + | "github.com/bnema/dumber/internal/domain/favicon" | |
| 12 | + | mock "github.com/stretchr/testify/mock" | |
| 13 | + | ) | |
| 14 | + | ||
| 15 | + | // NewMockFaviconRepository creates a new instance of MockFaviconRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 16 | + | // The first argument is typically a *testing.T value. | |
| 17 | + | func NewMockFaviconRepository(t interface { | |
| 18 | + | mock.TestingT | |
| 19 | + | Cleanup(func()) | |
| 20 | + | }) *MockFaviconRepository { | |
| 21 | + | mock := &MockFaviconRepository{} | |
| 22 | + | mock.Mock.Test(t) | |
| 23 | + | ||
| 24 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 25 | + | ||
| 26 | + | return mock | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | // MockFaviconRepository is an autogenerated mock type for the FaviconRepository type | |
| 30 | + | type MockFaviconRepository struct { | |
| 31 | + | mock.Mock | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | type MockFaviconRepository_Expecter struct { | |
| 35 | + | mock *mock.Mock | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | func (_m *MockFaviconRepository) EXPECT() *MockFaviconRepository_Expecter { | |
| 39 | + | return &MockFaviconRepository_Expecter{mock: &_m.Mock} | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | // Delete provides a mock function for the type MockFaviconRepository | |
| 43 | + | func (_mock *MockFaviconRepository) Delete(ctx context.Context, key favicon.Key) error { | |
| 44 | + | ret := _mock.Called(ctx, key) | |
| 45 | + | ||
| 46 | + | if len(ret) == 0 { | |
| 47 | + | panic("no return value specified for Delete") | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | var r0 error | |
| 51 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key) error); ok { | |
| 52 | + | r0 = returnFunc(ctx, key) | |
| 53 | + | } else { | |
| 54 | + | r0 = ret.Error(0) | |
| 55 | + | } | |
| 56 | + | return r0 | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | // MockFaviconRepository_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' | |
| 60 | + | type MockFaviconRepository_Delete_Call struct { | |
| 61 | + | *mock.Call | |
| 62 | + | } | |
| 63 | + | ||
| 64 | + | // Delete is a helper method to define mock.On call | |
| 65 | + | // - ctx context.Context | |
| 66 | + | // - key favicon.Key | |
| 67 | + | func (_e *MockFaviconRepository_Expecter) Delete(ctx any, key any) *MockFaviconRepository_Delete_Call { | |
| 68 | + | return &MockFaviconRepository_Delete_Call{Call: _e.mock.On("Delete", ctx, key)} | |
| 69 | + | } | |
| 70 | + | ||
| 71 | + | func (_c *MockFaviconRepository_Delete_Call) Run(run func(ctx context.Context, key favicon.Key)) *MockFaviconRepository_Delete_Call { | |
| 72 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 73 | + | var arg0 context.Context | |
| 74 | + | if args[0] != nil { | |
| 75 | + | arg0 = args[0].(context.Context) | |
| 76 | + | } | |
| 77 | + | var arg1 favicon.Key | |
| 78 | + | if args[1] != nil { | |
| 79 | + | arg1 = args[1].(favicon.Key) | |
| 80 | + | } | |
| 81 | + | run( | |
| 82 | + | arg0, | |
| 83 | + | arg1, | |
| 84 | + | ) | |
| 85 | + | }) | |
| 86 | + | return _c | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | func (_c *MockFaviconRepository_Delete_Call) Return(err error) *MockFaviconRepository_Delete_Call { | |
| 90 | + | _c.Call.Return(err) | |
| 91 | + | return _c | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | func (_c *MockFaviconRepository_Delete_Call) RunAndReturn(run func(ctx context.Context, key favicon.Key) error) *MockFaviconRepository_Delete_Call { | |
| 95 | + | _c.Call.Return(run) | |
| 96 | + | return _c | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | // FindFirst provides a mock function for the type MockFaviconRepository | |
| 100 | + | func (_mock *MockFaviconRepository) FindFirst(ctx context.Context, keys []favicon.Key) (*favicon.Metadata, error) { | |
| 101 | + | ret := _mock.Called(ctx, keys) | |
| 102 | + | ||
| 103 | + | if len(ret) == 0 { | |
| 104 | + | panic("no return value specified for FindFirst") | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | var r0 *favicon.Metadata | |
| 108 | + | var r1 error | |
| 109 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, []favicon.Key) (*favicon.Metadata, error)); ok { | |
| 110 | + | return returnFunc(ctx, keys) | |
| 111 | + | } | |
| 112 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, []favicon.Key) *favicon.Metadata); ok { | |
| 113 | + | r0 = returnFunc(ctx, keys) | |
| 114 | + | } else { | |
| 115 | + | if ret.Get(0) != nil { | |
| 116 | + | r0 = ret.Get(0).(*favicon.Metadata) | |
| 117 | + | } | |
| 118 | + | } | |
| 119 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, []favicon.Key) error); ok { | |
| 120 | + | r1 = returnFunc(ctx, keys) | |
| 121 | + | } else { | |
| 122 | + | r1 = ret.Error(1) | |
| 123 | + | } | |
| 124 | + | return r0, r1 | |
| 125 | + | } | |
| 126 | + | ||
| 127 | + | // MockFaviconRepository_FindFirst_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindFirst' | |
| 128 | + | type MockFaviconRepository_FindFirst_Call struct { | |
| 129 | + | *mock.Call | |
| 130 | + | } | |
| 131 | + | ||
| 132 | + | // FindFirst is a helper method to define mock.On call | |
| 133 | + | // - ctx context.Context | |
| 134 | + | // - keys []favicon.Key | |
| 135 | + | func (_e *MockFaviconRepository_Expecter) FindFirst(ctx any, keys any) *MockFaviconRepository_FindFirst_Call { | |
| 136 | + | return &MockFaviconRepository_FindFirst_Call{Call: _e.mock.On("FindFirst", ctx, keys)} | |
| 137 | + | } | |
| 138 | + | ||
| 139 | + | func (_c *MockFaviconRepository_FindFirst_Call) Run(run func(ctx context.Context, keys []favicon.Key)) *MockFaviconRepository_FindFirst_Call { | |
| 140 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 141 | + | var arg0 context.Context | |
| 142 | + | if args[0] != nil { | |
| 143 | + | arg0 = args[0].(context.Context) | |
| 144 | + | } | |
| 145 | + | var arg1 []favicon.Key | |
| 146 | + | if args[1] != nil { | |
| 147 | + | arg1 = args[1].([]favicon.Key) | |
| 148 | + | } | |
| 149 | + | run( | |
| 150 | + | arg0, | |
| 151 | + | arg1, | |
| 152 | + | ) | |
| 153 | + | }) | |
| 154 | + | return _c | |
| 155 | + | } | |
| 156 | + | ||
| 157 | + | func (_c *MockFaviconRepository_FindFirst_Call) Return(metadata *favicon.Metadata, err error) *MockFaviconRepository_FindFirst_Call { | |
| 158 | + | _c.Call.Return(metadata, err) | |
| 159 | + | return _c | |
| 160 | + | } | |
| 161 | + | ||
| 162 | + | func (_c *MockFaviconRepository_FindFirst_Call) RunAndReturn(run func(ctx context.Context, keys []favicon.Key) (*favicon.Metadata, error)) *MockFaviconRepository_FindFirst_Call { | |
| 163 | + | _c.Call.Return(run) | |
| 164 | + | return _c | |
| 165 | + | } | |
| 166 | + | ||
| 167 | + | // Get provides a mock function for the type MockFaviconRepository | |
| 168 | + | func (_mock *MockFaviconRepository) Get(ctx context.Context, key favicon.Key) (*favicon.Metadata, error) { | |
| 169 | + | ret := _mock.Called(ctx, key) | |
| 170 | + | ||
| 171 | + | if len(ret) == 0 { | |
| 172 | + | panic("no return value specified for Get") | |
| 173 | + | } | |
| 174 | + | ||
| 175 | + | var r0 *favicon.Metadata | |
| 176 | + | var r1 error | |
| 177 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key) (*favicon.Metadata, error)); ok { | |
| 178 | + | return returnFunc(ctx, key) | |
| 179 | + | } | |
| 180 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key) *favicon.Metadata); ok { | |
| 181 | + | r0 = returnFunc(ctx, key) | |
| 182 | + | } else { | |
| 183 | + | if ret.Get(0) != nil { | |
| 184 | + | r0 = ret.Get(0).(*favicon.Metadata) | |
| 185 | + | } | |
| 186 | + | } | |
| 187 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, favicon.Key) error); ok { | |
| 188 | + | r1 = returnFunc(ctx, key) | |
| 189 | + | } else { | |
| 190 | + | r1 = ret.Error(1) | |
| 191 | + | } | |
| 192 | + | return r0, r1 | |
| 193 | + | } | |
| 194 | + | ||
| 195 | + | // MockFaviconRepository_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' | |
| 196 | + | type MockFaviconRepository_Get_Call struct { | |
| 197 | + | *mock.Call | |
| 198 | + | } | |
| 199 | + | ||
| 200 | + | // Get is a helper method to define mock.On call | |
| 201 | + | // - ctx context.Context | |
| 202 | + | // - key favicon.Key | |
| 203 | + | func (_e *MockFaviconRepository_Expecter) Get(ctx any, key any) *MockFaviconRepository_Get_Call { | |
| 204 | + | return &MockFaviconRepository_Get_Call{Call: _e.mock.On("Get", ctx, key)} | |
| 205 | + | } | |
| 206 | + | ||
| 207 | + | func (_c *MockFaviconRepository_Get_Call) Run(run func(ctx context.Context, key favicon.Key)) *MockFaviconRepository_Get_Call { | |
| 208 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 209 | + | var arg0 context.Context | |
| 210 | + | if args[0] != nil { | |
| 211 | + | arg0 = args[0].(context.Context) | |
| 212 | + | } | |
| 213 | + | var arg1 favicon.Key | |
| 214 | + | if args[1] != nil { | |
| 215 | + | arg1 = args[1].(favicon.Key) | |
| 216 | + | } | |
| 217 | + | run( | |
| 218 | + | arg0, | |
| 219 | + | arg1, | |
| 220 | + | ) | |
| 221 | + | }) | |
| 222 | + | return _c | |
| 223 | + | } | |
| 224 | + | ||
| 225 | + | func (_c *MockFaviconRepository_Get_Call) Return(metadata *favicon.Metadata, err error) *MockFaviconRepository_Get_Call { | |
| 226 | + | _c.Call.Return(metadata, err) | |
| 227 | + | return _c | |
| 228 | + | } | |
| 229 | + | ||
| 230 | + | func (_c *MockFaviconRepository_Get_Call) RunAndReturn(run func(ctx context.Context, key favicon.Key) (*favicon.Metadata, error)) *MockFaviconRepository_Get_Call { | |
| 231 | + | _c.Call.Return(run) | |
| 232 | + | return _c | |
| 233 | + | } | |
| 234 | + | ||
| 235 | + | // UpdateLastChecked provides a mock function for the type MockFaviconRepository | |
| 236 | + | func (_mock *MockFaviconRepository) UpdateLastChecked(ctx context.Context, key favicon.Key, contentHash string, checkedAt time.Time) error { | |
| 237 | + | ret := _mock.Called(ctx, key, contentHash, checkedAt) | |
| 238 | + | ||
| 239 | + | if len(ret) == 0 { | |
| 240 | + | panic("no return value specified for UpdateLastChecked") | |
| 241 | + | } | |
| 242 | + | ||
| 243 | + | var r0 error | |
| 244 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key, string, time.Time) error); ok { | |
| 245 | + | r0 = returnFunc(ctx, key, contentHash, checkedAt) | |
| 246 | + | } else { | |
| 247 | + | r0 = ret.Error(0) | |
| 248 | + | } | |
| 249 | + | return r0 | |
| 250 | + | } | |
| 251 | + | ||
| 252 | + | // MockFaviconRepository_UpdateLastChecked_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateLastChecked' | |
| 253 | + | type MockFaviconRepository_UpdateLastChecked_Call struct { | |
| 254 | + | *mock.Call | |
| 255 | + | } | |
| 256 | + | ||
| 257 | + | // UpdateLastChecked is a helper method to define mock.On call | |
| 258 | + | // - ctx context.Context | |
| 259 | + | // - key favicon.Key | |
| 260 | + | // - contentHash string | |
| 261 | + | // - checkedAt time.Time | |
| 262 | + | func (_e *MockFaviconRepository_Expecter) UpdateLastChecked(ctx any, key any, contentHash any, checkedAt any) *MockFaviconRepository_UpdateLastChecked_Call { | |
| 263 | + | return &MockFaviconRepository_UpdateLastChecked_Call{Call: _e.mock.On("UpdateLastChecked", ctx, key, contentHash, checkedAt)} | |
| 264 | + | } | |
| 265 | + | ||
| 266 | + | func (_c *MockFaviconRepository_UpdateLastChecked_Call) Run(run func(ctx context.Context, key favicon.Key, contentHash string, checkedAt time.Time)) *MockFaviconRepository_UpdateLastChecked_Call { | |
| 267 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 268 | + | var arg0 context.Context | |
| 269 | + | if args[0] != nil { | |
| 270 | + | arg0 = args[0].(context.Context) | |
| 271 | + | } | |
| 272 | + | var arg1 favicon.Key | |
| 273 | + | if args[1] != nil { | |
| 274 | + | arg1 = args[1].(favicon.Key) | |
| 275 | + | } | |
| 276 | + | var arg2 string | |
| 277 | + | if args[2] != nil { | |
| 278 | + | arg2 = args[2].(string) | |
| 279 | + | } | |
| 280 | + | var arg3 time.Time | |
| 281 | + | if args[3] != nil { | |
| 282 | + | arg3 = args[3].(time.Time) | |
| 283 | + | } | |
| 284 | + | run( | |
| 285 | + | arg0, | |
| 286 | + | arg1, | |
| 287 | + | arg2, | |
| 288 | + | arg3, | |
| 289 | + | ) | |
| 290 | + | }) | |
| 291 | + | return _c | |
| 292 | + | } | |
| 293 | + | ||
| 294 | + | func (_c *MockFaviconRepository_UpdateLastChecked_Call) Return(err error) *MockFaviconRepository_UpdateLastChecked_Call { | |
| 295 | + | _c.Call.Return(err) | |
| 296 | + | return _c | |
| 297 | + | } | |
| 298 | + | ||
| 299 | + | func (_c *MockFaviconRepository_UpdateLastChecked_Call) RunAndReturn(run func(ctx context.Context, key favicon.Key, contentHash string, checkedAt time.Time) error) *MockFaviconRepository_UpdateLastChecked_Call { | |
| 300 | + | _c.Call.Return(run) | |
| 301 | + | return _c | |
| 302 | + | } | |
| 303 | + | ||
| 304 | + | // Upsert provides a mock function for the type MockFaviconRepository | |
| 305 | + | func (_mock *MockFaviconRepository) Upsert(ctx context.Context, meta favicon.Metadata) error { | |
| 306 | + | ret := _mock.Called(ctx, meta) | |
| 307 | + | ||
| 308 | + | if len(ret) == 0 { | |
| 309 | + | panic("no return value specified for Upsert") | |
| 310 | + | } | |
| 311 | + | ||
| 312 | + | var r0 error | |
| 313 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Metadata) error); ok { | |
| 314 | + | r0 = returnFunc(ctx, meta) | |
| 315 | + | } else { | |
| 316 | + | r0 = ret.Error(0) | |
| 317 | + | } | |
| 318 | + | return r0 | |
| 319 | + | } | |
| 320 | + | ||
| 321 | + | // MockFaviconRepository_Upsert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Upsert' | |
| 322 | + | type MockFaviconRepository_Upsert_Call struct { | |
| 323 | + | *mock.Call | |
| 324 | + | } | |
| 325 | + | ||
| 326 | + | // Upsert is a helper method to define mock.On call | |
| 327 | + | // - ctx context.Context | |
| 328 | + | // - meta favicon.Metadata | |
| 329 | + | func (_e *MockFaviconRepository_Expecter) Upsert(ctx any, meta any) *MockFaviconRepository_Upsert_Call { | |
| 330 | + | return &MockFaviconRepository_Upsert_Call{Call: _e.mock.On("Upsert", ctx, meta)} | |
| 331 | + | } | |
| 332 | + | ||
| 333 | + | func (_c *MockFaviconRepository_Upsert_Call) Run(run func(ctx context.Context, meta favicon.Metadata)) *MockFaviconRepository_Upsert_Call { | |
| 334 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 335 | + | var arg0 context.Context | |
| 336 | + | if args[0] != nil { | |
| 337 | + | arg0 = args[0].(context.Context) | |
| 338 | + | } | |
| 339 | + | var arg1 favicon.Metadata | |
| 340 | + | if args[1] != nil { | |
| 341 | + | arg1 = args[1].(favicon.Metadata) | |
| 342 | + | } | |
| 343 | + | run( | |
| 344 | + | arg0, | |
| 345 | + | arg1, | |
| 346 | + | ) | |
| 347 | + | }) | |
| 348 | + | return _c | |
| 349 | + | } | |
| 350 | + | ||
| 351 | + | func (_c *MockFaviconRepository_Upsert_Call) Return(err error) *MockFaviconRepository_Upsert_Call { | |
| 352 | + | _c.Call.Return(err) | |
| 353 | + | return _c | |
| 354 | + | } | |
| 355 | + | ||
| 356 | + | func (_c *MockFaviconRepository_Upsert_Call) RunAndReturn(run func(ctx context.Context, meta favicon.Metadata) error) *MockFaviconRepository_Upsert_Call { | |
| 357 | + | _c.Call.Return(run) | |
| 358 | + | return _c | |
| 359 | + | } | |
A internal/application/port/mocks/favicon_scheduler.go +102 -0
| @@ -0,0 +1,102 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | "github.com/bnema/dumber/internal/domain/favicon" | |
| 11 | + | mock "github.com/stretchr/testify/mock" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | // NewMockFaviconRefreshScheduler creates a new instance of MockFaviconRefreshScheduler. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 15 | + | // The first argument is typically a *testing.T value. | |
| 16 | + | func NewMockFaviconRefreshScheduler(t interface { | |
| 17 | + | mock.TestingT | |
| 18 | + | Cleanup(func()) | |
| 19 | + | }) *MockFaviconRefreshScheduler { | |
| 20 | + | mock := &MockFaviconRefreshScheduler{} | |
| 21 | + | mock.Mock.Test(t) | |
| 22 | + | ||
| 23 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 24 | + | ||
| 25 | + | return mock | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | // MockFaviconRefreshScheduler is an autogenerated mock type for the FaviconRefreshScheduler type | |
| 29 | + | type MockFaviconRefreshScheduler struct { | |
| 30 | + | mock.Mock | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | type MockFaviconRefreshScheduler_Expecter struct { | |
| 34 | + | mock *mock.Mock | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | func (_m *MockFaviconRefreshScheduler) EXPECT() *MockFaviconRefreshScheduler_Expecter { | |
| 38 | + | return &MockFaviconRefreshScheduler_Expecter{mock: &_m.Mock} | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | // Schedule provides a mock function for the type MockFaviconRefreshScheduler | |
| 42 | + | func (_mock *MockFaviconRefreshScheduler) Schedule(ctx context.Context, key favicon.Key, work func(context.Context)) bool { | |
| 43 | + | ret := _mock.Called(ctx, key, work) | |
| 44 | + | ||
| 45 | + | if len(ret) == 0 { | |
| 46 | + | panic("no return value specified for Schedule") | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | var r0 bool | |
| 50 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, favicon.Key, func(context.Context)) bool); ok { | |
| 51 | + | r0 = returnFunc(ctx, key, work) | |
| 52 | + | } else { | |
| 53 | + | r0 = ret.Get(0).(bool) | |
| 54 | + | } | |
| 55 | + | return r0 | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | // MockFaviconRefreshScheduler_Schedule_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Schedule' | |
| 59 | + | type MockFaviconRefreshScheduler_Schedule_Call struct { | |
| 60 | + | *mock.Call | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | // Schedule is a helper method to define mock.On call | |
| 64 | + | // - ctx context.Context | |
| 65 | + | // - key favicon.Key | |
| 66 | + | // - work func(context.Context) | |
| 67 | + | func (_e *MockFaviconRefreshScheduler_Expecter) Schedule(ctx any, key any, work any) *MockFaviconRefreshScheduler_Schedule_Call { | |
| 68 | + | return &MockFaviconRefreshScheduler_Schedule_Call{Call: _e.mock.On("Schedule", ctx, key, work)} | |
| 69 | + | } | |
| 70 | + | ||
| 71 | + | func (_c *MockFaviconRefreshScheduler_Schedule_Call) Run(run func(ctx context.Context, key favicon.Key, work func(context.Context))) *MockFaviconRefreshScheduler_Schedule_Call { | |
| 72 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 73 | + | var arg0 context.Context | |
| 74 | + | if args[0] != nil { | |
| 75 | + | arg0 = args[0].(context.Context) | |
| 76 | + | } | |
| 77 | + | var arg1 favicon.Key | |
| 78 | + | if args[1] != nil { | |
| 79 | + | arg1 = args[1].(favicon.Key) | |
| 80 | + | } | |
| 81 | + | var arg2 func(context.Context) | |
| 82 | + | if args[2] != nil { | |
| 83 | + | arg2 = args[2].(func(context.Context)) | |
| 84 | + | } | |
| 85 | + | run( | |
| 86 | + | arg0, | |
| 87 | + | arg1, | |
| 88 | + | arg2, | |
| 89 | + | ) | |
| 90 | + | }) | |
| 91 | + | return _c | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | func (_c *MockFaviconRefreshScheduler_Schedule_Call) Return(b bool) *MockFaviconRefreshScheduler_Schedule_Call { | |
| 95 | + | _c.Call.Return(b) | |
| 96 | + | return _c | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | func (_c *MockFaviconRefreshScheduler_Schedule_Call) RunAndReturn(run func(ctx context.Context, key favicon.Key, work func(context.Context)) bool) *MockFaviconRefreshScheduler_Schedule_Call { | |
| 100 | + | _c.Call.Return(run) | |
| 101 | + | return _c | |
| 102 | + | } | |
M internal/application/usecase/favicon_test.go +175 -79
| @@ -7,11 +7,13 @@ import ( | |||
| 7 | 7 | "time" | |
| 8 | 8 | ||
| 9 | 9 | appport "github.com/bnema/dumber/internal/application/port" | |
| 10 | + | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | |
| 10 | 11 | "github.com/bnema/dumber/internal/domain/favicon" | |
| 12 | + | "github.com/stretchr/testify/mock" | |
| 11 | 13 | ) | |
| 12 | 14 | ||
| 13 | 15 | func TestFaviconResolveExactHit(t *testing.T) { | |
| 14 | - | fx := newFaviconFixture() | |
| 16 | + | fx := newFaviconFixture(t) | |
| 15 | 17 | fx.seed("docs.example.com", []byte("png32"), false) | |
| 16 | 18 | got, err := fx.uc.Resolve(context.Background(), "https://docs.example.com/a", 32, ResolveOptions{Purpose: ResolvePurposeUI}) | |
| 17 | 19 | if err != nil { | |
| @@ -23,7 +25,7 @@ func TestFaviconResolveExactHit(t *testing.T) { | |||
| 23 | 25 | } | |
| 24 | 26 | ||
| 25 | 27 | func TestFaviconResolveParentFallback(t *testing.T) { | |
| 26 | - | fx := newFaviconFixture() | |
| 28 | + | fx := newFaviconFixture(t) | |
| 27 | 29 | fx.seed("example.com", []byte("parent"), false) | |
| 28 | 30 | got, err := fx.uc.Resolve(context.Background(), "https://docs.example.com/a", 32, ResolveOptions{Purpose: ResolvePurposeUI}) | |
| 29 | 31 | if err != nil { | |
| @@ -35,7 +37,7 @@ func TestFaviconResolveParentFallback(t *testing.T) { | |||
| 35 | 37 | } | |
| 36 | 38 | ||
| 37 | 39 | func TestFaviconResolvePrefersParentPathFallbackBeforeDomain(t *testing.T) { | |
| 38 | - | fx := newFaviconFixture() | |
| 40 | + | fx := newFaviconFixture(t) | |
| 39 | 41 | fx.seed("github.com", []byte("github-default"), false) | |
| 40 | 42 | fx.seed("github.com/bnema/gordon", []byte("repo-default"), false) | |
| 41 | 43 | ||
| @@ -49,7 +51,7 @@ func TestFaviconResolvePrefersParentPathFallbackBeforeDomain(t *testing.T) { | |||
| 49 | 51 | } | |
| 50 | 52 | ||
| 51 | 53 | func TestFaviconResolvePathSpecificDoesNotLeakToParentOrSiblings(t *testing.T) { | |
| 52 | - | fx := newFaviconFixture() | |
| 54 | + | fx := newFaviconFixture(t) | |
| 53 | 55 | fx.seed("github.com", []byte("github-default"), false) | |
| 54 | 56 | ||
| 55 | 57 | if _, err := fx.uc.Observe( | |
| @@ -91,7 +93,7 @@ func TestFaviconResolvePathSpecificDoesNotLeakToParentOrSiblings(t *testing.T) { | |||
| 91 | 93 | } | |
| 92 | 94 | ||
| 93 | 95 | func TestFaviconResolveRepairsOrphanCanonicalBlobMetadata(t *testing.T) { | |
| 94 | - | fx := newFaviconFixture() | |
| 96 | + | fx := newFaviconFixture(t) | |
| 95 | 97 | fx.blobs.png["example.com"] = []byte("png") | |
| 96 | 98 | fx.blobs.sized["example.com"] = map[int][]byte{SystemviewIconSize: []byte("png32")} | |
| 97 | 99 | ||
| @@ -112,7 +114,7 @@ func TestFaviconResolveRepairsOrphanCanonicalBlobMetadata(t *testing.T) { | |||
| 112 | 114 | } | |
| 113 | 115 | ||
| 114 | 116 | func TestFaviconResolveRepairOrphanWithoutConverterReturnsMiss(t *testing.T) { | |
| 115 | - | fx := newFaviconFixture() | |
| 117 | + | fx := newFaviconFixture(t) | |
| 116 | 118 | fx.uc.converter = nil | |
| 117 | 119 | fx.blobs.original["example.com"] = []byte("original") | |
| 118 | 120 | fx.blobs.contentTypes["example.com"] = "image/png" | |
| @@ -124,7 +126,7 @@ func TestFaviconResolveRepairOrphanWithoutConverterReturnsMiss(t *testing.T) { | |||
| 124 | 126 | } | |
| 125 | 127 | ||
| 126 | 128 | func TestFaviconResolveStaleSchedulesDedupedBackgroundRefresh(t *testing.T) { | |
| 127 | - | fx := newFaviconFixture() | |
| 129 | + | fx := newFaviconFixture(t) | |
| 128 | 130 | fx.seed("example.com", []byte("old"), true) | |
| 129 | 131 | for i := 0; i < 2; i++ { | |
| 130 | 132 | got, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{ScheduleBackgroundRefresh: true}) | |
| @@ -144,7 +146,7 @@ func TestFaviconResolveStaleSchedulesDedupedBackgroundRefresh(t *testing.T) { | |||
| 144 | 146 | } | |
| 145 | 147 | ||
| 146 | 148 | func TestFaviconResolveStaleExactRefreshIgnoresFreshParent(t *testing.T) { | |
| 147 | - | fx := newFaviconFixture() | |
| 149 | + | fx := newFaviconFixture(t) | |
| 148 | 150 | fx.seed("docs.example.com", []byte("old-exact"), true) | |
| 149 | 151 | fx.seed("example.com", []byte("fresh-parent"), false) | |
| 150 | 152 | got, err := fx.uc.Resolve( | |
| @@ -166,7 +168,7 @@ func TestFaviconResolveStaleExactRefreshIgnoresFreshParent(t *testing.T) { | |||
| 166 | 168 | } | |
| 167 | 169 | ||
| 168 | 170 | func TestFaviconObserveSameHashUpdatesMetadataOnly(t *testing.T) { | |
| 169 | - | fx := newFaviconFixture() | |
| 171 | + | fx := newFaviconFixture(t) | |
| 170 | 172 | fx.seedOriginal([]byte("same")) | |
| 171 | 173 | if _, err := fx.uc.Observe(context.Background(), "https://example.com", "https://cdn.test/icon.png", []byte("same"), favicon.SourceEngine, "image/png"); err != nil { | |
| 172 | 174 | t.Fatal(err) | |
| @@ -180,7 +182,7 @@ func TestFaviconObserveSameHashUpdatesMetadataOnly(t *testing.T) { | |||
| 180 | 182 | } | |
| 181 | 183 | ||
| 182 | 184 | func TestFaviconObserveChangedHashRegeneratesAndInvalidates(t *testing.T) { | |
| 183 | - | fx := newFaviconFixture() | |
| 185 | + | fx := newFaviconFixture(t) | |
| 184 | 186 | fx.seedOriginal([]byte("old")) | |
| 185 | 187 | if _, err := fx.uc.Observe(context.Background(), "https://example.com", "https://cdn.test/icon.png", []byte("new"), favicon.SourceEngine, "image/png"); err != nil { | |
| 186 | 188 | t.Fatal(err) | |
| @@ -197,7 +199,7 @@ func TestFaviconObserveChangedHashRegeneratesAndInvalidates(t *testing.T) { | |||
| 197 | 199 | } | |
| 198 | 200 | ||
| 199 | 201 | func TestFaviconObserveWithoutConverterStoresOriginalOnly(t *testing.T) { | |
| 200 | - | fx := newFaviconFixture() | |
| 202 | + | fx := newFaviconFixture(t) | |
| 201 | 203 | fx.uc.converter = nil | |
| 202 | 204 | if _, err := fx.uc.Observe(context.Background(), "https://example.com", "https://example.com/icon.png", []byte("raw"), favicon.SourceEngine, "image/png"); err != nil { | |
| 203 | 205 | t.Fatal(err) | |
| @@ -211,7 +213,7 @@ func TestFaviconObserveWithoutConverterStoresOriginalOnly(t *testing.T) { | |||
| 211 | 213 | } | |
| 212 | 214 | ||
| 213 | 215 | func TestFaviconResolveMissingFetchesOnlyWhenBlockingAllowed(t *testing.T) { | |
| 214 | - | fx := newFaviconFixture() | |
| 216 | + | fx := newFaviconFixture(t) | |
| 215 | 217 | if _, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{}); !errors.Is(err, ErrFaviconMiss) { | |
| 216 | 218 | t.Fatalf("default resolve err=%v", err) | |
| 217 | 219 | } | |
| @@ -228,7 +230,7 @@ func TestFaviconResolveMissingFetchesOnlyWhenBlockingAllowed(t *testing.T) { | |||
| 228 | 230 | } | |
| 229 | 231 | ||
| 230 | 232 | func TestFaviconRefreshIfStaleUsesParentFallbackBeforeFetching(t *testing.T) { | |
| 231 | - | fx := newFaviconFixture() | |
| 233 | + | fx := newFaviconFixture(t) | |
| 232 | 234 | fx.seed("example.com", []byte("parent"), false) | |
| 233 | 235 | if err := fx.uc.RefreshIfStale(context.Background(), "https://docs.example.com/page"); err != nil { | |
| 234 | 236 | t.Fatal(err) | |
| @@ -239,7 +241,7 @@ func TestFaviconRefreshIfStaleUsesParentFallbackBeforeFetching(t *testing.T) { | |||
| 239 | 241 | } | |
| 240 | 242 | ||
| 241 | 243 | func TestFaviconUseCasePropagatesRepositoryErrors(t *testing.T) { | |
| 242 | - | fx := newFaviconFixture() | |
| 244 | + | fx := newFaviconFixture(t) | |
| 243 | 245 | fx.repo.err = errors.New("database unavailable") | |
| 244 | 246 | if _, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{}); !errors.Is(err, fx.repo.err) { | |
| 245 | 247 | t.Fatalf("Resolve err=%v, want %v", err, fx.repo.err) | |
| @@ -253,7 +255,7 @@ func TestFaviconUseCasePropagatesRepositoryErrors(t *testing.T) { | |||
| 253 | 255 | } | |
| 254 | 256 | ||
| 255 | 257 | func TestFaviconResolvePropagatesBlobErrors(t *testing.T) { | |
| 256 | - | fx := newFaviconFixture() | |
| 258 | + | fx := newFaviconFixture(t) | |
| 257 | 259 | fx.seed("example.com", []byte("cached"), false) | |
| 258 | 260 | fx.blobs.err = errors.New("disk unavailable") | |
| 259 | 261 | if _, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{}); !errors.Is(err, fx.blobs.err) { | |
| @@ -262,7 +264,7 @@ func TestFaviconResolvePropagatesBlobErrors(t *testing.T) { | |||
| 262 | 264 | } | |
| 263 | 265 | ||
| 264 | 266 | func TestFaviconResolveBlockingRefreshesWhenMetadataFreshButBlobMissing(t *testing.T) { | |
| 265 | - | fx := newFaviconFixture() | |
| 267 | + | fx := newFaviconFixture(t) | |
| 266 | 268 | fx.repo.byKey["example.com"] = &favicon.Metadata{Key: "example.com", PageURL: "https://example.com", ContentHash: favicon.Hash([]byte("old")), LastCheckedAt: fx.now} | |
| 267 | 269 | got, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{AllowBlockingRefresh: true}) | |
| 268 | 270 | if err != nil { | |
| @@ -274,7 +276,7 @@ func TestFaviconResolveBlockingRefreshesWhenMetadataFreshButBlobMissing(t *testi | |||
| 274 | 276 | } | |
| 275 | 277 | ||
| 276 | 278 | func TestFaviconResolveBlockingPropagatesFetchErrors(t *testing.T) { | |
| 277 | - | fx := newFaviconFixture() | |
| 279 | + | fx := newFaviconFixture(t) | |
| 278 | 280 | fx.fetcher.err = errors.New("network unavailable") | |
| 279 | 281 | if _, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{AllowBlockingRefresh: true}); !errors.Is(err, fx.fetcher.err) { | |
| 280 | 282 | t.Fatalf("Resolve err=%v, want %v", err, fx.fetcher.err) | |
| @@ -282,7 +284,7 @@ func TestFaviconResolveBlockingPropagatesFetchErrors(t *testing.T) { | |||
| 282 | 284 | } | |
| 283 | 285 | ||
| 284 | 286 | func TestFaviconRefreshFromIconURLsUsesPageURLKey(t *testing.T) { | |
| 285 | - | fx := newFaviconFixture() | |
| 287 | + | fx := newFaviconFixture(t) | |
| 286 | 288 | if err := fx.uc.RefreshFromIconURLs(context.Background(), "https://docs.example.com/page", []string{"https://cdn.example.net/icon.png"}); err != nil { | |
| 287 | 289 | t.Fatal(err) | |
| 288 | 290 | } | |
| @@ -295,8 +297,8 @@ func TestFaviconRefreshFromIconURLsUsesPageURLKey(t *testing.T) { | |||
| 295 | 297 | } | |
| 296 | 298 | ||
| 297 | 299 | func TestFaviconRefreshStoresDuckDuckGoFallbackUnderHostKey(t *testing.T) { | |
| 298 | - | fx := newFaviconFixture() | |
| 299 | - | fx.fetcher.responses = map[string]fakeFetchedIcon{ | |
| 300 | + | fx := newFaviconFixture(t) | |
| 301 | + | fx.fetcher.responses = map[string]faviconFetchResponse{ | |
| 300 | 302 | "https://example.com/favicon.ico": { | |
| 301 | 303 | bytes: []byte("host-fallback"), | |
| 302 | 304 | contentType: "image/png", | |
| @@ -316,8 +318,8 @@ func TestFaviconRefreshStoresDuckDuckGoFallbackUnderHostKey(t *testing.T) { | |||
| 316 | 318 | } | |
| 317 | 319 | ||
| 318 | 320 | func TestFaviconRefreshRejectsResolvedKeyOutsideCandidateHierarchy(t *testing.T) { | |
| 319 | - | fx := newFaviconFixture() | |
| 320 | - | fx.fetcher.responses = map[string]fakeFetchedIcon{ | |
| 321 | + | fx := newFaviconFixture(t) | |
| 322 | + | fx.fetcher.responses = map[string]faviconFetchResponse{ | |
| 321 | 323 | "https://example.com/favicon.ico": { | |
| 322 | 324 | bytes: []byte("bad-fallback"), | |
| 323 | 325 | contentType: "image/png", | |
| @@ -335,8 +337,8 @@ func TestFaviconRefreshRejectsResolvedKeyOutsideCandidateHierarchy(t *testing.T) | |||
| 335 | 337 | } | |
| 336 | 338 | ||
| 337 | 339 | func TestFaviconRefreshRejectsFetcherPageURLOutsideRequestedHierarchy(t *testing.T) { | |
| 338 | - | fx := newFaviconFixture() | |
| 339 | - | fx.fetcher.responses = map[string]fakeFetchedIcon{ | |
| 340 | + | fx := newFaviconFixture(t) | |
| 341 | + | fx.fetcher.responses = map[string]faviconFetchResponse{ | |
| 340 | 342 | "https://example.com/favicon.ico": { | |
| 341 | 343 | bytes: []byte("bad-page-url"), | |
| 342 | 344 | contentType: "image/png", | |
| @@ -355,8 +357,8 @@ func TestFaviconRefreshRejectsFetcherPageURLOutsideRequestedHierarchy(t *testing | |||
| 355 | 357 | } | |
| 356 | 358 | ||
| 357 | 359 | func TestFaviconRefreshFromIconURLsSkipsUnsupportedCandidates(t *testing.T) { | |
| 358 | - | fx := newFaviconFixture() | |
| 359 | - | fx.fetcher.responses = map[string]fakeFetchedIcon{ | |
| 360 | + | fx := newFaviconFixture(t) | |
| 361 | + | fx.fetcher.responses = map[string]faviconFetchResponse{ | |
| 360 | 362 | "https://cdn.example.net/icon.svg": {bytes: []byte("svg"), contentType: "image/svg+xml"}, | |
| 361 | 363 | "https://cdn.example.net/icon.png": {bytes: []byte("png"), contentType: "image/png"}, | |
| 362 | 364 | } | |
| @@ -385,7 +387,7 @@ func TestFaviconRefreshFromIconURLsSkipsUnsupportedCandidates(t *testing.T) { | |||
| 385 | 387 | } | |
| 386 | 388 | ||
| 387 | 389 | func TestFaviconObserveConversionErrorDoesNotMutateExistingAssets(t *testing.T) { | |
| 388 | - | fx := newFaviconFixture() | |
| 390 | + | fx := newFaviconFixture(t) | |
| 389 | 391 | fx.seedOriginal([]byte("old")) | |
| 390 | 392 | fx.blobs.png["example.com"] = []byte("old-png") | |
| 391 | 393 | fx.blobs.sized["example.com"] = map[int][]byte{32: []byte("old-32")} | |
| @@ -403,7 +405,7 @@ func TestFaviconObserveConversionErrorDoesNotMutateExistingAssets(t *testing.T) | |||
| 403 | 405 | } | |
| 404 | 406 | ||
| 405 | 407 | func TestFaviconEnsureSizedPropagatesReadSizedErrors(t *testing.T) { | |
| 406 | - | fx := newFaviconFixture() | |
| 408 | + | fx := newFaviconFixture(t) | |
| 407 | 409 | fx.seedOriginal([]byte("old")) | |
| 408 | 410 | fx.blobs.readSizedErr = errors.New("sized store unavailable") | |
| 409 | 411 | if err := fx.uc.EnsureSized(context.Background(), "example.com", 32); !errors.Is(err, fx.blobs.readSizedErr) { | |
| @@ -415,12 +417,12 @@ func TestFaviconEnsureSizedPropagatesReadSizedErrors(t *testing.T) { | |||
| 415 | 417 | } | |
| 416 | 418 | ||
| 417 | 419 | type faviconFixture struct { | |
| 418 | - | repo *fakeFaviconRepo | |
| 419 | - | blobs *fakeBlobStore | |
| 420 | - | converter *fakeConverter | |
| 421 | - | scheduler *fakeScheduler | |
| 422 | - | invalidators *fakeInvalidators | |
| 423 | - | fetcher *fakeFetcher | |
| 420 | + | repo *faviconRepoState | |
| 421 | + | blobs *faviconBlobStoreState | |
| 422 | + | converter *faviconConverterState | |
| 423 | + | scheduler *faviconSchedulerState | |
| 424 | + | invalidators *faviconInvalidatorsState | |
| 425 | + | fetcher *faviconFetcherState | |
| 424 | 426 | uc *FaviconUseCase | |
| 425 | 427 | now time.Time | |
| 426 | 428 | } | |
| @@ -429,29 +431,98 @@ type faviconTestContextKey string | |||
| 429 | 431 | ||
| 430 | 432 | const faviconFixtureBackgroundKey faviconTestContextKey = "bg" | |
| 431 | 433 | ||
| 432 | - | func newFaviconFixture() *faviconFixture { | |
| 434 | + | func newFaviconFixture(t *testing.T) *faviconFixture { | |
| 435 | + | t.Helper() | |
| 436 | + | ||
| 433 | 437 | now := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) | |
| 434 | 438 | fx := &faviconFixture{ | |
| 435 | - | repo: newFakeRepo(), | |
| 436 | - | blobs: newFakeBlobStore(), | |
| 437 | - | converter: &fakeConverter{}, | |
| 438 | - | scheduler: &fakeScheduler{seen: map[favicon.Key]bool{}}, | |
| 439 | - | invalidators: &fakeInvalidators{}, | |
| 440 | - | fetcher: &fakeFetcher{}, | |
| 439 | + | repo: newFaviconRepoState(), | |
| 440 | + | blobs: newFaviconBlobStoreState(), | |
| 441 | + | converter: &faviconConverterState{}, | |
| 442 | + | scheduler: &faviconSchedulerState{seen: map[favicon.Key]bool{}}, | |
| 443 | + | invalidators: &faviconInvalidatorsState{}, | |
| 444 | + | fetcher: &faviconFetcherState{}, | |
| 441 | 445 | now: now, | |
| 442 | 446 | } | |
| 447 | + | ||
| 448 | + | repo := mockFaviconRepository(t, fx.repo) | |
| 449 | + | blobs := mockFaviconBlobStore(t, fx.blobs) | |
| 450 | + | converter := mockFaviconConverter(t, fx.converter) | |
| 451 | + | scheduler := mockFaviconScheduler(t, fx.scheduler) | |
| 452 | + | invalidators := mockFaviconInvalidators(t, fx.invalidators) | |
| 453 | + | fetcher := mockFaviconFetcher(t, fx.fetcher) | |
| 454 | + | ||
| 443 | 455 | fx.uc = NewFaviconUseCase(FaviconDeps{ | |
| 444 | - | Repository: fx.repo, | |
| 445 | - | BlobStore: fx.blobs, | |
| 446 | - | Converter: fx.converter, | |
| 447 | - | Scheduler: fx.scheduler, | |
| 448 | - | Invalidators: fx.invalidators, | |
| 449 | - | Fetcher: fx.fetcher, | |
| 456 | + | Repository: repo, | |
| 457 | + | BlobStore: blobs, | |
| 458 | + | Converter: converter, | |
| 459 | + | Scheduler: scheduler, | |
| 460 | + | Invalidators: invalidators, | |
| 461 | + | Fetcher: fetcher, | |
| 450 | 462 | Now: func() time.Time { return fx.now }, | |
| 451 | 463 | Background: context.WithValue(context.Background(), faviconFixtureBackgroundKey, true), | |
| 452 | 464 | }) | |
| 453 | 465 | return fx | |
| 454 | 466 | } | |
| 467 | + | ||
| 468 | + | func mockFaviconRepository(t *testing.T, state *faviconRepoState) *portmocks.MockFaviconRepository { | |
| 469 | + | t.Helper() | |
| 470 | + | ||
| 471 | + | repo := portmocks.NewMockFaviconRepository(t) | |
| 472 | + | repo.EXPECT().Get(mock.Anything, mock.Anything).RunAndReturn(state.get).Maybe() | |
| 473 | + | repo.EXPECT().FindFirst(mock.Anything, mock.Anything).RunAndReturn(state.findFirst).Maybe() | |
| 474 | + | repo.EXPECT().Upsert(mock.Anything, mock.Anything).RunAndReturn(state.upsert).Maybe() | |
| 475 | + | repo.EXPECT().UpdateLastChecked(mock.Anything, mock.Anything, mock.Anything, mock.Anything).RunAndReturn(state.updateLastChecked).Maybe() | |
| 476 | + | repo.EXPECT().Delete(mock.Anything, mock.Anything).RunAndReturn(state.delete).Maybe() | |
| 477 | + | return repo | |
| 478 | + | } | |
| 479 | + | ||
| 480 | + | func mockFaviconBlobStore(t *testing.T, state *faviconBlobStoreState) *portmocks.MockFaviconBlobStore { | |
| 481 | + | t.Helper() | |
| 482 | + | ||
| 483 | + | blobs := portmocks.NewMockFaviconBlobStore(t) | |
| 484 | + | blobs.EXPECT().ReadOriginal(mock.Anything, mock.Anything).RunAndReturn(state.readOriginal).Maybe() | |
| 485 | + | blobs.EXPECT().WriteOriginal(mock.Anything, mock.Anything, mock.Anything, mock.Anything).RunAndReturn(state.writeOriginal).Maybe() | |
| 486 | + | blobs.EXPECT().ReadPNG(mock.Anything, mock.Anything).RunAndReturn(state.readPNG).Maybe() | |
| 487 | + | blobs.EXPECT().WritePNG(mock.Anything, mock.Anything, mock.Anything).RunAndReturn(state.writePNG).Maybe() | |
| 488 | + | blobs.EXPECT().ReadSizedPNG(mock.Anything, mock.Anything, mock.Anything).RunAndReturn(state.readSizedPNG).Maybe() | |
| 489 | + | blobs.EXPECT().WriteSizedPNG(mock.Anything, mock.Anything, mock.Anything, mock.Anything).RunAndReturn(state.writeSizedPNG).Maybe() | |
| 490 | + | blobs.EXPECT().RemoveDerived(mock.Anything, mock.Anything).RunAndReturn(state.removeDerivedForKey).Maybe() | |
| 491 | + | return blobs | |
| 492 | + | } | |
| 493 | + | ||
| 494 | + | func mockFaviconConverter(t *testing.T, state *faviconConverterState) *portmocks.MockFaviconImageConverter { | |
| 495 | + | t.Helper() | |
| 496 | + | ||
| 497 | + | converter := portmocks.NewMockFaviconImageConverter(t) | |
| 498 | + | converter.EXPECT().Convert(mock.Anything, mock.Anything, mock.Anything, mock.Anything).RunAndReturn(state.convert).Maybe() | |
| 499 | + | return converter | |
| 500 | + | } | |
| 501 | + | ||
| 502 | + | func mockFaviconScheduler(t *testing.T, state *faviconSchedulerState) *portmocks.MockFaviconRefreshScheduler { | |
| 503 | + | t.Helper() | |
| 504 | + | ||
| 505 | + | scheduler := portmocks.NewMockFaviconRefreshScheduler(t) | |
| 506 | + | scheduler.EXPECT().Schedule(mock.Anything, mock.Anything, mock.Anything).RunAndReturn(state.schedule).Maybe() | |
| 507 | + | return scheduler | |
| 508 | + | } | |
| 509 | + | ||
| 510 | + | func mockFaviconInvalidators(t *testing.T, state *faviconInvalidatorsState) *portmocks.MockFaviconInvalidators { | |
| 511 | + | t.Helper() | |
| 512 | + | ||
| 513 | + | invalidators := portmocks.NewMockFaviconInvalidators(t) | |
| 514 | + | invalidators.EXPECT().InvalidateAll(mock.Anything, mock.Anything).RunAndReturn(state.invalidateAll).Maybe() | |
| 515 | + | return invalidators | |
| 516 | + | } | |
| 517 | + | ||
| 518 | + | func mockFaviconFetcher(t *testing.T, state *faviconFetcherState) *portmocks.MockFaviconFetcher { | |
| 519 | + | t.Helper() | |
| 520 | + | ||
| 521 | + | fetcher := portmocks.NewMockFaviconFetcher(t) | |
| 522 | + | fetcher.EXPECT().Fetch(mock.Anything, mock.Anything).RunAndReturn(state.fetch).Maybe() | |
| 523 | + | return fetcher | |
| 524 | + | } | |
| 525 | + | ||
| 455 | 526 | func (f *faviconFixture) seed(key string, sized []byte, stale bool) { | |
| 456 | 527 | checked := f.now | |
| 457 | 528 | if stale { | |
| @@ -460,6 +531,7 @@ func (f *faviconFixture) seed(key string, sized []byte, stale bool) { | |||
| 460 | 531 | f.repo.byKey[favicon.Key(key)] = &favicon.Metadata{Key: favicon.Key(key), PageURL: "https://" + key, ContentHash: favicon.Hash([]byte("orig")), LastCheckedAt: checked} | |
| 461 | 532 | f.blobs.sized[favicon.Key(key)] = map[int][]byte{32: sized} | |
| 462 | 533 | } | |
| 534 | + | ||
| 463 | 535 | func (f *faviconFixture) seedOriginal(b []byte) { | |
| 464 | 536 | const contentType = "image/png" | |
| 465 | 537 | key := favicon.Key("example.com") | |
| @@ -474,15 +546,16 @@ func (f *faviconFixture) seedOriginal(b []byte) { | |||
| 474 | 546 | f.blobs.contentTypes[key] = contentType | |
| 475 | 547 | } | |
| 476 | 548 | ||
| 477 | - | type fakeFaviconRepo struct { | |
| 549 | + | type faviconRepoState struct { | |
| 478 | 550 | byKey map[favicon.Key]*favicon.Metadata | |
| 479 | 551 | err error | |
| 480 | 552 | } | |
| 481 | 553 | ||
| 482 | - | func newFakeRepo() *fakeFaviconRepo { | |
| 483 | - | return &fakeFaviconRepo{byKey: map[favicon.Key]*favicon.Metadata{}} | |
| 554 | + | func newFaviconRepoState() *faviconRepoState { | |
| 555 | + | return &faviconRepoState{byKey: map[favicon.Key]*favicon.Metadata{}} | |
| 484 | 556 | } | |
| 485 | - | func (r *fakeFaviconRepo) Get(_ context.Context, key favicon.Key) (*favicon.Metadata, error) { | |
| 557 | + | ||
| 558 | + | func (r *faviconRepoState) get(_ context.Context, key favicon.Key) (*favicon.Metadata, error) { | |
| 486 | 559 | if r.err != nil { | |
| 487 | 560 | return nil, r.err | |
| 488 | 561 | } | |
| @@ -492,18 +565,20 @@ func (r *fakeFaviconRepo) Get(_ context.Context, key favicon.Key) (*favicon.Meta | |||
| 492 | 565 | } | |
| 493 | 566 | return nil, nil | |
| 494 | 567 | } | |
| 495 | - | func (r *fakeFaviconRepo) FindFirst(ctx context.Context, keys []favicon.Key) (*favicon.Metadata, error) { | |
| 568 | + | ||
| 569 | + | func (r *faviconRepoState) findFirst(ctx context.Context, keys []favicon.Key) (*favicon.Metadata, error) { | |
| 496 | 570 | if r.err != nil { | |
| 497 | 571 | return nil, r.err | |
| 498 | 572 | } | |
| 499 | 573 | for _, k := range keys { | |
| 500 | - | if m, _ := r.Get(ctx, k); m != nil { | |
| 574 | + | if m, _ := r.get(ctx, k); m != nil { | |
| 501 | 575 | return m, nil | |
| 502 | 576 | } | |
| 503 | 577 | } | |
| 504 | 578 | return nil, nil | |
| 505 | 579 | } | |
| 506 | - | func (r *fakeFaviconRepo) Upsert(_ context.Context, meta favicon.Metadata) error { | |
| 580 | + | ||
| 581 | + | func (r *faviconRepoState) upsert(_ context.Context, meta favicon.Metadata) error { | |
| 507 | 582 | if r.err != nil { | |
| 508 | 583 | return r.err | |
| 509 | 584 | } | |
| @@ -511,7 +586,8 @@ func (r *fakeFaviconRepo) Upsert(_ context.Context, meta favicon.Metadata) error | |||
| 511 | 586 | r.byKey[meta.Key] = &cp | |
| 512 | 587 | return nil | |
| 513 | 588 | } | |
| 514 | - | func (r *fakeFaviconRepo) UpdateLastChecked(_ context.Context, key favicon.Key, hash string, checkedAt time.Time) error { | |
| 589 | + | ||
| 590 | + | func (r *faviconRepoState) updateLastChecked(_ context.Context, key favicon.Key, hash string, checkedAt time.Time) error { | |
| 515 | 591 | if r.err != nil { | |
| 516 | 592 | return r.err | |
| 517 | 593 | } | |
| @@ -519,7 +595,8 @@ func (r *fakeFaviconRepo) UpdateLastChecked(_ context.Context, key favicon.Key, | |||
| 519 | 595 | r.byKey[key].LastCheckedAt = checkedAt | |
| 520 | 596 | return nil | |
| 521 | 597 | } | |
| 522 | - | func (r *fakeFaviconRepo) Delete(_ context.Context, key favicon.Key) error { | |
| 598 | + | ||
| 599 | + | func (r *faviconRepoState) delete(_ context.Context, key favicon.Key) error { | |
| 523 | 600 | if r.err != nil { | |
| 524 | 601 | return r.err | |
| 525 | 602 | } | |
| @@ -527,7 +604,7 @@ func (r *fakeFaviconRepo) Delete(_ context.Context, key favicon.Key) error { | |||
| 527 | 604 | return nil | |
| 528 | 605 | } | |
| 529 | 606 | ||
| 530 | - | type fakeBlobStore struct { | |
| 607 | + | type faviconBlobStoreState struct { | |
| 531 | 608 | original, png map[favicon.Key][]byte | |
| 532 | 609 | sized map[favicon.Key]map[int][]byte | |
| 533 | 610 | contentTypes map[favicon.Key]string | |
| @@ -536,10 +613,16 @@ type fakeBlobStore struct { | |||
| 536 | 613 | readSizedErr error | |
| 537 | 614 | } | |
| 538 | 615 | ||
| 539 | - | func newFakeBlobStore() *fakeBlobStore { | |
| 540 | - | return &fakeBlobStore{original: map[favicon.Key][]byte{}, png: map[favicon.Key][]byte{}, sized: map[favicon.Key]map[int][]byte{}, contentTypes: map[favicon.Key]string{}} | |
| 616 | + | func newFaviconBlobStoreState() *faviconBlobStoreState { | |
| 617 | + | return &faviconBlobStoreState{ | |
| 618 | + | original: map[favicon.Key][]byte{}, | |
| 619 | + | png: map[favicon.Key][]byte{}, | |
| 620 | + | sized: map[favicon.Key]map[int][]byte{}, | |
| 621 | + | contentTypes: map[favicon.Key]string{}, | |
| 622 | + | } | |
| 541 | 623 | } | |
| 542 | - | func (b *fakeBlobStore) ReadOriginal(_ context.Context, key favicon.Key) ([]byte, string, error) { | |
| 624 | + | ||
| 625 | + | func (b *faviconBlobStoreState) readOriginal(_ context.Context, key favicon.Key) ([]byte, string, error) { | |
| 543 | 626 | if b.err != nil { | |
| 544 | 627 | return nil, "", b.err | |
| 545 | 628 | } | |
| @@ -549,7 +632,8 @@ func (b *fakeBlobStore) ReadOriginal(_ context.Context, key favicon.Key) ([]byte | |||
| 549 | 632 | } | |
| 550 | 633 | return v, b.contentTypes[key], nil | |
| 551 | 634 | } | |
| 552 | - | func (b *fakeBlobStore) WriteOriginal(_ context.Context, key favicon.Key, data []byte, ct string) error { | |
| 635 | + | ||
| 636 | + | func (b *faviconBlobStoreState) writeOriginal(_ context.Context, key favicon.Key, data []byte, ct string) error { | |
| 553 | 637 | if b.err != nil { | |
| 554 | 638 | return b.err | |
| 555 | 639 | } | |
| @@ -558,7 +642,8 @@ func (b *fakeBlobStore) WriteOriginal(_ context.Context, key favicon.Key, data [ | |||
| 558 | 642 | b.contentTypes[key] = ct | |
| 559 | 643 | return nil | |
| 560 | 644 | } | |
| 561 | - | func (b *fakeBlobStore) ReadPNG(_ context.Context, key favicon.Key) ([]byte, string, error) { | |
| 645 | + | ||
| 646 | + | func (b *faviconBlobStoreState) readPNG(_ context.Context, key favicon.Key) ([]byte, string, error) { | |
| 562 | 647 | if b.err != nil { | |
| 563 | 648 | return nil, "", b.err | |
| 564 | 649 | } | |
| @@ -568,14 +653,16 @@ func (b *fakeBlobStore) ReadPNG(_ context.Context, key favicon.Key) ([]byte, str | |||
| 568 | 653 | } | |
| 569 | 654 | return v, "image/png", nil | |
| 570 | 655 | } | |
| 571 | - | func (b *fakeBlobStore) WritePNG(_ context.Context, key favicon.Key, data []byte) error { | |
| 656 | + | ||
| 657 | + | func (b *faviconBlobStoreState) writePNG(_ context.Context, key favicon.Key, data []byte) error { | |
| 572 | 658 | if b.err != nil { | |
| 573 | 659 | return b.err | |
| 574 | 660 | } | |
| 575 | 661 | b.png[key] = data | |
| 576 | 662 | return nil | |
| 577 | 663 | } | |
| 578 | - | func (b *fakeBlobStore) ReadSizedPNG(_ context.Context, key favicon.Key, size int) ([]byte, string, error) { | |
| 664 | + | ||
| 665 | + | func (b *faviconBlobStoreState) readSizedPNG(_ context.Context, key favicon.Key, size int) ([]byte, string, error) { | |
| 579 | 666 | if b.err != nil { | |
| 580 | 667 | return nil, "", b.err | |
| 581 | 668 | } | |
| @@ -587,7 +674,8 @@ func (b *fakeBlobStore) ReadSizedPNG(_ context.Context, key favicon.Key, size in | |||
| 587 | 674 | } | |
| 588 | 675 | return b.sized[key][size], "image/png", nil | |
| 589 | 676 | } | |
| 590 | - | func (b *fakeBlobStore) WriteSizedPNG(_ context.Context, key favicon.Key, size int, data []byte) error { | |
| 677 | + | ||
| 678 | + | func (b *faviconBlobStoreState) writeSizedPNG(_ context.Context, key favicon.Key, size int, data []byte) error { | |
| 591 | 679 | if b.err != nil { | |
| 592 | 680 | return b.err | |
| 593 | 681 | } | |
| @@ -597,7 +685,8 @@ func (b *fakeBlobStore) WriteSizedPNG(_ context.Context, key favicon.Key, size i | |||
| 597 | 685 | b.sized[key][size] = data | |
| 598 | 686 | return nil | |
| 599 | 687 | } | |
| 600 | - | func (b *fakeBlobStore) RemoveDerived(_ context.Context, key favicon.Key) error { | |
| 688 | + | ||
| 689 | + | func (b *faviconBlobStoreState) removeDerivedForKey(_ context.Context, key favicon.Key) error { | |
| 601 | 690 | if b.err != nil { | |
| 602 | 691 | return b.err | |
| 603 | 692 | } | |
| @@ -607,13 +696,13 @@ func (b *fakeBlobStore) RemoveDerived(_ context.Context, key favicon.Key) error | |||
| 607 | 696 | return nil | |
| 608 | 697 | } | |
| 609 | 698 | ||
| 610 | - | type fakeConverter struct { | |
| 699 | + | type faviconConverterState struct { | |
| 611 | 700 | calls int | |
| 612 | 701 | err error | |
| 613 | 702 | errByContentType map[string]error | |
| 614 | 703 | } | |
| 615 | 704 | ||
| 616 | - | func (c *fakeConverter) Convert(_ context.Context, original []byte, contentType string, sizes []int) (*appport.ConvertedFavicon, error) { | |
| 705 | + | func (c *faviconConverterState) convert(_ context.Context, original []byte, contentType string, sizes []int) (*appport.ConvertedFavicon, error) { | |
| 617 | 706 | c.calls++ | |
| 618 | 707 | if c.err != nil { | |
| 619 | 708 | return nil, c.err | |
| @@ -628,14 +717,14 @@ func (c *fakeConverter) Convert(_ context.Context, original []byte, contentType | |||
| 628 | 717 | return out, nil | |
| 629 | 718 | } | |
| 630 | 719 | ||
| 631 | - | type fakeScheduler struct { | |
| 720 | + | type faviconSchedulerState struct { | |
| 632 | 721 | seen map[favicon.Key]bool | |
| 633 | 722 | works map[favicon.Key]func(context.Context) | |
| 634 | 723 | schedules int | |
| 635 | 724 | usedCallerContext bool | |
| 636 | 725 | } | |
| 637 | 726 | ||
| 638 | - | func (s *fakeScheduler) Schedule(ctx context.Context, key favicon.Key, work func(context.Context)) bool { | |
| 727 | + | func (s *faviconSchedulerState) schedule(ctx context.Context, key favicon.Key, work func(context.Context)) bool { | |
| 639 | 728 | if ctx.Value(faviconFixtureBackgroundKey) != true { | |
| 640 | 729 | s.usedCallerContext = true | |
| 641 | 730 | } | |
| @@ -651,33 +740,33 @@ func (s *fakeScheduler) Schedule(ctx context.Context, key favicon.Key, work func | |||
| 651 | 740 | return true | |
| 652 | 741 | } | |
| 653 | 742 | ||
| 654 | - | func (s *fakeScheduler) run(key favicon.Key) { | |
| 743 | + | func (s *faviconSchedulerState) run(key favicon.Key) { | |
| 655 | 744 | if work := s.works[key]; work != nil { | |
| 656 | 745 | work(context.Background()) | |
| 657 | 746 | } | |
| 658 | 747 | } | |
| 659 | 748 | ||
| 660 | - | type fakeInvalidators struct{ calls int } | |
| 749 | + | type faviconInvalidatorsState struct{ calls int } | |
| 661 | 750 | ||
| 662 | - | func (i *fakeInvalidators) InvalidateAll(_ context.Context, _ favicon.Key) error { | |
| 751 | + | func (i *faviconInvalidatorsState) invalidateAll(_ context.Context, _ favicon.Key) error { | |
| 663 | 752 | i.calls++ | |
| 664 | 753 | return nil | |
| 665 | 754 | } | |
| 666 | 755 | ||
| 667 | - | type fakeFetchedIcon struct { | |
| 756 | + | type faviconFetchResponse struct { | |
| 668 | 757 | bytes []byte | |
| 669 | 758 | contentType string | |
| 670 | 759 | pageURL string | |
| 671 | 760 | resolvedKey favicon.Key | |
| 672 | 761 | } | |
| 673 | 762 | ||
| 674 | - | type fakeFetcher struct { | |
| 763 | + | type faviconFetcherState struct { | |
| 675 | 764 | calls int | |
| 676 | 765 | err error | |
| 677 | - | responses map[string]fakeFetchedIcon | |
| 766 | + | responses map[string]faviconFetchResponse | |
| 678 | 767 | } | |
| 679 | 768 | ||
| 680 | - | func (f *fakeFetcher) Fetch(_ context.Context, req appport.FaviconFetchRequest) (*appport.FaviconFetchedIcon, error) { | |
| 769 | + | func (f *faviconFetcherState) fetch(_ context.Context, req appport.FaviconFetchRequest) (*appport.FaviconFetchedIcon, error) { | |
| 681 | 770 | f.calls++ | |
| 682 | 771 | if f.err != nil { | |
| 683 | 772 | return nil, f.err | |
| @@ -688,11 +777,18 @@ func (f *fakeFetcher) Fetch(_ context.Context, req appport.FaviconFetchRequest) | |||
| 688 | 777 | } | |
| 689 | 778 | response := f.responses[iconURL] | |
| 690 | 779 | if response.bytes == nil { | |
| 691 | - | response = fakeFetchedIcon{bytes: []byte("fetched"), contentType: "image/png"} | |
| 780 | + | response = faviconFetchResponse{bytes: []byte("fetched"), contentType: "image/png"} | |
| 692 | 781 | } | |
| 693 | 782 | pageURL := req.PageURL | |
| 694 | 783 | if response.pageURL != "" { | |
| 695 | 784 | pageURL = response.pageURL | |
| 696 | 785 | } | |
| 697 | - | return &appport.FaviconFetchedIcon{PageURL: pageURL, IconURL: iconURL, ResolvedKey: response.resolvedKey, Bytes: response.bytes, Source: favicon.SourceDuckDuckGo, ContentType: response.contentType}, nil | |
| 786 | + | return &appport.FaviconFetchedIcon{ | |
| 787 | + | PageURL: pageURL, | |
| 788 | + | IconURL: iconURL, | |
| 789 | + | ResolvedKey: response.resolvedKey, | |
| 790 | + | Bytes: response.bytes, | |
| 791 | + | Source: favicon.SourceDuckDuckGo, | |
| 792 | + | ContentType: response.contentType, | |
| 793 | + | }, nil | |
| 698 | 794 | } | |
A internal/bootstrap/mocks/runtime_config.go +167 -0
| @@ -0,0 +1,167 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "github.com/bnema/dumber/internal/infrastructure/config" | |
| 9 | + | mock "github.com/stretchr/testify/mock" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | // NewMockConfigChangeManager creates a new instance of MockConfigChangeManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 13 | + | // The first argument is typically a *testing.T value. | |
| 14 | + | func NewMockConfigChangeManager(t interface { | |
| 15 | + | mock.TestingT | |
| 16 | + | Cleanup(func()) | |
| 17 | + | }) *MockConfigChangeManager { | |
| 18 | + | mock := &MockConfigChangeManager{} | |
| 19 | + | mock.Mock.Test(t) | |
| 20 | + | ||
| 21 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 22 | + | ||
| 23 | + | return mock | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | // MockConfigChangeManager is an autogenerated mock type for the configChangeManager type | |
| 27 | + | type MockConfigChangeManager struct { | |
| 28 | + | mock.Mock | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | type MockConfigChangeManager_Expecter struct { | |
| 32 | + | mock *mock.Mock | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | func (_m *MockConfigChangeManager) EXPECT() *MockConfigChangeManager_Expecter { | |
| 36 | + | return &MockConfigChangeManager_Expecter{mock: &_m.Mock} | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | // Get provides a mock function for the type MockConfigChangeManager | |
| 40 | + | func (_mock *MockConfigChangeManager) Get() *config.Config { | |
| 41 | + | ret := _mock.Called() | |
| 42 | + | ||
| 43 | + | if len(ret) == 0 { | |
| 44 | + | panic("no return value specified for Get") | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | var r0 *config.Config | |
| 48 | + | if returnFunc, ok := ret.Get(0).(func() *config.Config); ok { | |
| 49 | + | r0 = returnFunc() | |
| 50 | + | } else { | |
| 51 | + | if ret.Get(0) != nil { | |
| 52 | + | r0 = ret.Get(0).(*config.Config) | |
| 53 | + | } | |
| 54 | + | } | |
| 55 | + | return r0 | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | // MockConfigChangeManager_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' | |
| 59 | + | type MockConfigChangeManager_Get_Call struct { | |
| 60 | + | *mock.Call | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | // Get is a helper method to define mock.On call | |
| 64 | + | func (_e *MockConfigChangeManager_Expecter) Get() *MockConfigChangeManager_Get_Call { | |
| 65 | + | return &MockConfigChangeManager_Get_Call{Call: _e.mock.On("Get")} | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | func (_c *MockConfigChangeManager_Get_Call) Run(run func()) *MockConfigChangeManager_Get_Call { | |
| 69 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 70 | + | run() | |
| 71 | + | }) | |
| 72 | + | return _c | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | func (_c *MockConfigChangeManager_Get_Call) Return(config1 *config.Config) *MockConfigChangeManager_Get_Call { | |
| 76 | + | _c.Call.Return(config1) | |
| 77 | + | return _c | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | func (_c *MockConfigChangeManager_Get_Call) RunAndReturn(run func() *config.Config) *MockConfigChangeManager_Get_Call { | |
| 81 | + | _c.Call.Return(run) | |
| 82 | + | return _c | |
| 83 | + | } | |
| 84 | + | ||
| 85 | + | // OnConfigChange provides a mock function for the type MockConfigChangeManager | |
| 86 | + | func (_mock *MockConfigChangeManager) OnConfigChange(fn func(*config.Config)) { | |
| 87 | + | _mock.Called(fn) | |
| 88 | + | return | |
| 89 | + | } | |
| 90 | + | ||
| 91 | + | // MockConfigChangeManager_OnConfigChange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnConfigChange' | |
| 92 | + | type MockConfigChangeManager_OnConfigChange_Call struct { | |
| 93 | + | *mock.Call | |
| 94 | + | } | |
| 95 | + | ||
| 96 | + | // OnConfigChange is a helper method to define mock.On call | |
| 97 | + | // - fn func(*config.Config) | |
| 98 | + | func (_e *MockConfigChangeManager_Expecter) OnConfigChange(fn any) *MockConfigChangeManager_OnConfigChange_Call { | |
| 99 | + | return &MockConfigChangeManager_OnConfigChange_Call{Call: _e.mock.On("OnConfigChange", fn)} | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | func (_c *MockConfigChangeManager_OnConfigChange_Call) Run(run func(fn func(*config.Config))) *MockConfigChangeManager_OnConfigChange_Call { | |
| 103 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 104 | + | var arg0 func(*config.Config) | |
| 105 | + | if args[0] != nil { | |
| 106 | + | arg0 = args[0].(func(*config.Config)) | |
| 107 | + | } | |
| 108 | + | run( | |
| 109 | + | arg0, | |
| 110 | + | ) | |
| 111 | + | }) | |
| 112 | + | return _c | |
| 113 | + | } | |
| 114 | + | ||
| 115 | + | func (_c *MockConfigChangeManager_OnConfigChange_Call) Return() *MockConfigChangeManager_OnConfigChange_Call { | |
| 116 | + | _c.Call.Return() | |
| 117 | + | return _c | |
| 118 | + | } | |
| 119 | + | ||
| 120 | + | func (_c *MockConfigChangeManager_OnConfigChange_Call) RunAndReturn(run func(fn func(*config.Config))) *MockConfigChangeManager_OnConfigChange_Call { | |
| 121 | + | _c.Run(run) | |
| 122 | + | return _c | |
| 123 | + | } | |
| 124 | + | ||
| 125 | + | // Watch provides a mock function for the type MockConfigChangeManager | |
| 126 | + | func (_mock *MockConfigChangeManager) Watch() error { | |
| 127 | + | ret := _mock.Called() | |
| 128 | + | ||
| 129 | + | if len(ret) == 0 { | |
| 130 | + | panic("no return value specified for Watch") | |
| 131 | + | } | |
| 132 | + | ||
| 133 | + | var r0 error | |
| 134 | + | if returnFunc, ok := ret.Get(0).(func() error); ok { | |
| 135 | + | r0 = returnFunc() | |
| 136 | + | } else { | |
| 137 | + | r0 = ret.Error(0) | |
| 138 | + | } | |
| 139 | + | return r0 | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | // MockConfigChangeManager_Watch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Watch' | |
| 143 | + | type MockConfigChangeManager_Watch_Call struct { | |
| 144 | + | *mock.Call | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | // Watch is a helper method to define mock.On call | |
| 148 | + | func (_e *MockConfigChangeManager_Expecter) Watch() *MockConfigChangeManager_Watch_Call { | |
| 149 | + | return &MockConfigChangeManager_Watch_Call{Call: _e.mock.On("Watch")} | |
| 150 | + | } | |
| 151 | + | ||
| 152 | + | func (_c *MockConfigChangeManager_Watch_Call) Run(run func()) *MockConfigChangeManager_Watch_Call { | |
| 153 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 154 | + | run() | |
| 155 | + | }) | |
| 156 | + | return _c | |
| 157 | + | } | |
| 158 | + | ||
| 159 | + | func (_c *MockConfigChangeManager_Watch_Call) Return(err error) *MockConfigChangeManager_Watch_Call { | |
| 160 | + | _c.Call.Return(err) | |
| 161 | + | return _c | |
| 162 | + | } | |
| 163 | + | ||
| 164 | + | func (_c *MockConfigChangeManager_Watch_Call) RunAndReturn(run func() error) *MockConfigChangeManager_Watch_Call { | |
| 165 | + | _c.Call.Return(run) | |
| 166 | + | return _c | |
| 167 | + | } | |
M internal/bootstrap/runtime_config_test.go +38 -26
| @@ -5,32 +5,44 @@ import ( | |||
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | 7 | "github.com/bnema/dumber/internal/application/port" | |
| 8 | + | bootstrapmocks "github.com/bnema/dumber/internal/bootstrap/mocks" | |
| 8 | 9 | "github.com/bnema/dumber/internal/domain/entity" | |
| 9 | 10 | "github.com/bnema/dumber/internal/infrastructure/config" | |
| 11 | + | "github.com/stretchr/testify/mock" | |
| 10 | 12 | ) | |
| 11 | 13 | ||
| 12 | - | type fakeConfigManager struct { | |
| 14 | + | type configManagerState struct { | |
| 13 | 15 | current *config.Config | |
| 14 | 16 | watchCalls int | |
| 15 | 17 | callbacks []func(*config.Config) | |
| 16 | 18 | } | |
| 17 | 19 | ||
| 18 | - | func (f *fakeConfigManager) Watch() error { | |
| 19 | - | f.watchCalls++ | |
| 20 | + | func newMockConfigManager(t *testing.T, state *configManagerState) *bootstrapmocks.MockConfigChangeManager { | |
| 21 | + | t.Helper() | |
| 22 | + | ||
| 23 | + | manager := bootstrapmocks.NewMockConfigChangeManager(t) | |
| 24 | + | manager.EXPECT().Get().RunAndReturn(state.get).Maybe() | |
| 25 | + | manager.EXPECT().Watch().RunAndReturn(state.watch).Maybe() | |
| 26 | + | manager.EXPECT().OnConfigChange(mock.Anything).RunAndReturn(state.onConfigChange).Maybe() | |
| 27 | + | return manager | |
| 28 | + | } | |
| 29 | + | ||
| 30 | + | func (s *configManagerState) watch() error { | |
| 31 | + | s.watchCalls++ | |
| 20 | 32 | return nil | |
| 21 | 33 | } | |
| 22 | 34 | ||
| 23 | - | func (f *fakeConfigManager) OnConfigChange(callback func(*config.Config)) { | |
| 24 | - | f.callbacks = append(f.callbacks, callback) | |
| 35 | + | func (s *configManagerState) onConfigChange(callback func(*config.Config)) { | |
| 36 | + | s.callbacks = append(s.callbacks, callback) | |
| 25 | 37 | } | |
| 26 | 38 | ||
| 27 | - | func (f *fakeConfigManager) Get() *config.Config { | |
| 28 | - | return f.current | |
| 39 | + | func (s *configManagerState) get() *config.Config { | |
| 40 | + | return s.current | |
| 29 | 41 | } | |
| 30 | 42 | ||
| 31 | - | func (f *fakeConfigManager) emit(next *config.Config) { | |
| 32 | - | f.current = next | |
| 33 | - | for _, callback := range f.callbacks { | |
| 43 | + | func (s *configManagerState) emit(next *config.Config) { | |
| 44 | + | s.current = next | |
| 45 | + | for _, callback := range s.callbacks { | |
| 34 | 46 | callback(next) | |
| 35 | 47 | } | |
| 36 | 48 | } | |
| @@ -71,8 +83,8 @@ func TestEngineSettingsPayloadFromConfigMapsRuntimeFields(t *testing.T) { | |||
| 71 | 83 | func TestRuntimeConfigProviderUpdatesSnapshotBeforeCallback(t *testing.T) { | |
| 72 | 84 | initial := config.DefaultConfig() | |
| 73 | 85 | initial.DefaultUIScale = 1 | |
| 74 | - | manager := &fakeConfigManager{} | |
| 75 | - | provider := NewRuntimeConfigProvider(initial, manager) | |
| 86 | + | manager := &configManagerState{} | |
| 87 | + | provider := NewRuntimeConfigProvider(initial, newMockConfigManager(t, manager)) | |
| 76 | 88 | ||
| 77 | 89 | var seen port.RuntimeConfigSnapshot | |
| 78 | 90 | provider.OnChange(func(snapshot port.RuntimeConfigSnapshot) { | |
| @@ -94,8 +106,8 @@ func TestRuntimeConfigProviderUpdatesSnapshotBeforeCallback(t *testing.T) { | |||
| 94 | 106 | func TestRuntimeConfigProviderCurrentReflectsManagerGetWithoutSubscription(t *testing.T) { | |
| 95 | 107 | initial := config.DefaultConfig() | |
| 96 | 108 | initial.DefaultUIScale = 1 | |
| 97 | - | manager := &fakeConfigManager{current: initial} | |
| 98 | - | provider := NewRuntimeConfigProvider(initial, manager) | |
| 109 | + | manager := &configManagerState{current: initial} | |
| 110 | + | provider := NewRuntimeConfigProvider(initial, newMockConfigManager(t, manager)) | |
| 99 | 111 | ||
| 100 | 112 | next := config.DefaultConfig() | |
| 101 | 113 | next.DefaultUIScale = 1.8 | |
| @@ -109,8 +121,8 @@ func TestRuntimeConfigProviderCurrentReflectsManagerGetWithoutSubscription(t *te | |||
| 109 | 121 | func TestRuntimeConfigProviderOnChangeCallbackCanCallCurrent(t *testing.T) { | |
| 110 | 122 | initial := config.DefaultConfig() | |
| 111 | 123 | initial.DefaultUIScale = 1 | |
| 112 | - | manager := &fakeConfigManager{current: initial} | |
| 113 | - | provider := NewRuntimeConfigProvider(initial, manager) | |
| 124 | + | manager := &configManagerState{current: initial} | |
| 125 | + | provider := NewRuntimeConfigProvider(initial, newMockConfigManager(t, manager)) | |
| 114 | 126 | ||
| 115 | 127 | completed := false | |
| 116 | 128 | var seen port.RuntimeConfigSnapshot | |
| @@ -132,8 +144,8 @@ func TestRuntimeConfigProviderOnChangeCallbackCanCallCurrent(t *testing.T) { | |||
| 132 | 144 | } | |
| 133 | 145 | ||
| 134 | 146 | func TestRuntimeConfigProviderOnChangeNilCallbackDoesNotRegister(t *testing.T) { | |
| 135 | - | manager := &fakeConfigManager{current: config.DefaultConfig()} | |
| 136 | - | provider := NewRuntimeConfigProvider(config.DefaultConfig(), manager) | |
| 147 | + | manager := &configManagerState{current: config.DefaultConfig()} | |
| 148 | + | provider := NewRuntimeConfigProvider(config.DefaultConfig(), newMockConfigManager(t, manager)) | |
| 137 | 149 | ||
| 138 | 150 | provider.OnChange(nil) | |
| 139 | 151 | ||
| @@ -143,8 +155,8 @@ func TestRuntimeConfigProviderOnChangeNilCallbackDoesNotRegister(t *testing.T) { | |||
| 143 | 155 | } | |
| 144 | 156 | ||
| 145 | 157 | func TestRuntimeConfigProviderWatchDelegatesToManager(t *testing.T) { | |
| 146 | - | manager := &fakeConfigManager{} | |
| 147 | - | provider := NewRuntimeConfigProvider(config.DefaultConfig(), manager) | |
| 158 | + | manager := &configManagerState{} | |
| 159 | + | provider := NewRuntimeConfigProvider(config.DefaultConfig(), newMockConfigManager(t, manager)) | |
| 148 | 160 | ||
| 149 | 161 | if err := provider.Watch(); err != nil { | |
| 150 | 162 | t.Fatalf("Watch returned error: %v", err) | |
| @@ -159,7 +171,7 @@ func TestRuntimeConfigProviderCurrentReturnsMapClone(t *testing.T) { | |||
| 159 | 171 | cfg.SearchShortcuts = map[string]config.SearchShortcut{ | |
| 160 | 172 | "gh": {URL: "https://github.com/search?q=%s", Description: "GitHub"}, | |
| 161 | 173 | } | |
| 162 | - | provider := NewRuntimeConfigProvider(cfg, &fakeConfigManager{current: cfg}) | |
| 174 | + | provider := NewRuntimeConfigProvider(cfg, newMockConfigManager(t, &configManagerState{current: cfg})) | |
| 163 | 175 | ||
| 164 | 176 | first := provider.Current() | |
| 165 | 177 | first.UI.SearchShortcuts["gh"] = port.RuntimeSearchShortcut{URL: "mutated"} | |
| @@ -172,7 +184,7 @@ func TestRuntimeConfigProviderCurrentReturnsMapClone(t *testing.T) { | |||
| 172 | 184 | ||
| 173 | 185 | func TestRuntimeConfigProviderCurrentReturnsNestedConfigClone(t *testing.T) { | |
| 174 | 186 | cfg := runtimeConfigWithNestedMutableFields() | |
| 175 | - | provider := NewRuntimeConfigProvider(cfg, &fakeConfigManager{current: cfg}) | |
| 187 | + | provider := NewRuntimeConfigProvider(cfg, newMockConfigManager(t, &configManagerState{current: cfg})) | |
| 176 | 188 | ||
| 177 | 189 | first := provider.Current() | |
| 178 | 190 | mutateNestedRuntimeConfigSnapshot(first) | |
| @@ -182,8 +194,8 @@ func TestRuntimeConfigProviderCurrentReturnsNestedConfigClone(t *testing.T) { | |||
| 182 | 194 | } | |
| 183 | 195 | ||
| 184 | 196 | func TestRuntimeConfigProviderOnChangePassesNestedConfigClone(t *testing.T) { | |
| 185 | - | manager := &fakeConfigManager{} | |
| 186 | - | provider := NewRuntimeConfigProvider(config.DefaultConfig(), manager) | |
| 197 | + | manager := &configManagerState{} | |
| 198 | + | provider := NewRuntimeConfigProvider(config.DefaultConfig(), newMockConfigManager(t, manager)) | |
| 187 | 199 | ||
| 188 | 200 | provider.OnChange(func(snapshot port.RuntimeConfigSnapshot) { | |
| 189 | 201 | mutateNestedRuntimeConfigSnapshot(snapshot) | |
| @@ -246,7 +258,7 @@ func mutateNestedRuntimeConfigSnapshot(snapshot port.RuntimeConfigSnapshot) { | |||
| 246 | 258 | snapshot.UI.Session.SessionMode.Actions["added-session"] = entity.ActionBinding{Keys: []string{"added"}} | |
| 247 | 259 | } | |
| 248 | 260 | ||
| 249 | - | func mutateActionBinding(actions map[string]entity.ActionBinding, action string, key string) { | |
| 261 | + | func mutateActionBinding(actions map[string]entity.ActionBinding, action, key string) { | |
| 250 | 262 | binding := actions[action] | |
| 251 | 263 | binding.Keys[0] = key | |
| 252 | 264 | actions[action] = binding | |
| @@ -279,7 +291,7 @@ func assertNestedRuntimeConfigSnapshotUnchanged(t *testing.T, snapshot port.Runt | |||
| 279 | 291 | assertMapEntryAbsent(t, snapshot.UI.Session.SessionMode.Actions, "added-session") | |
| 280 | 292 | } | |
| 281 | 293 | ||
| 282 | - | func assertActionBinding(t *testing.T, actions map[string]entity.ActionBinding, action string, wantKey string) { | |
| 294 | + | func assertActionBinding(t *testing.T, actions map[string]entity.ActionBinding, action, wantKey string) { | |
| 283 | 295 | t.Helper() | |
| 284 | 296 | ||
| 285 | 297 | binding, ok := actions[action] | |
M internal/cli/cmd/logs_test.go +17 -15
| @@ -8,23 +8,13 @@ import ( | |||
| 8 | 8 | "testing" | |
| 9 | 9 | "time" | |
| 10 | 10 | ||
| 11 | + | cmdmocks "github.com/bnema/dumber/internal/cli/cmd/mocks" | |
| 11 | 12 | "github.com/bnema/dumber/internal/domain/entity" | |
| 12 | 13 | "github.com/bnema/dumber/internal/logging" | |
| 14 | + | "github.com/stretchr/testify/mock" | |
| 13 | 15 | "github.com/stretchr/testify/require" | |
| 14 | 16 | ) | |
| 15 | 17 | ||
| 16 | - | type fakeSessionMgr struct { | |
| 17 | - | sessions []*entity.Session | |
| 18 | - | err error | |
| 19 | - | } | |
| 20 | - | ||
| 21 | - | func (f *fakeSessionMgr) GetRecentSessions(_ context.Context, _ int) ([]*entity.Session, error) { | |
| 22 | - | if f.err != nil { | |
| 23 | - | return nil, f.err | |
| 24 | - | } | |
| 25 | - | return f.sessions, nil | |
| 26 | - | } | |
| 27 | - | ||
| 28 | 18 | func TestGetSessionsMerged_DBAndFS(t *testing.T) { | |
| 29 | 19 | logDir := t.TempDir() | |
| 30 | 20 | ||
| @@ -34,7 +24,11 @@ func TestGetSessionsMerged_DBAndFS(t *testing.T) { | |||
| 34 | 24 | require.NoError(t, os.WriteFile(filepath.Join(logDir, logging.SessionFilename(string(s1.ID))), []byte("hi\n"), 0o600)) | |
| 35 | 25 | require.NoError(t, os.WriteFile(filepath.Join(logDir, logging.SessionFilename("legacy_only")), []byte("legacy\n"), 0o600)) | |
| 36 | 26 | ||
| 37 | - | mgr := &fakeSessionMgr{sessions: []*entity.Session{s1, s2}} | |
| 27 | + | mgr := cmdmocks.NewMockSessionManager(t) | |
| 28 | + | mgr.EXPECT(). | |
| 29 | + | GetRecentSessions(mock.Anything, recentSessionsLimit). | |
| 30 | + | Return([]*entity.Session{s1, s2}, nil). | |
| 31 | + | Once() | |
| 38 | 32 | ||
| 39 | 33 | merged, err := getSessionsMerged(context.Background(), mgr, logDir) | |
| 40 | 34 | require.NoError(t, err) | |
| @@ -54,7 +48,11 @@ func TestGetSessionsMerged_MissingTableFallsBack(t *testing.T) { | |||
| 54 | 48 | logDir := t.TempDir() | |
| 55 | 49 | require.NoError(t, os.WriteFile(filepath.Join(logDir, logging.SessionFilename("legacy_only")), []byte("legacy\n"), 0o600)) | |
| 56 | 50 | ||
| 57 | - | mgr := &fakeSessionMgr{err: fmt.Errorf("no such table: sessions")} | |
| 51 | + | mgr := cmdmocks.NewMockSessionManager(t) | |
| 52 | + | mgr.EXPECT(). | |
| 53 | + | GetRecentSessions(mock.Anything, recentSessionsLimit). | |
| 54 | + | Return(nil, fmt.Errorf("no such table: sessions")). | |
| 55 | + | Once() | |
| 58 | 56 | merged, err := getSessionsMerged(context.Background(), mgr, logDir) | |
| 59 | 57 | require.NoError(t, err) | |
| 60 | 58 | require.Len(t, merged, 1) | |
| @@ -65,7 +63,11 @@ func TestFindSession_DBShortIDMatch(t *testing.T) { | |||
| 65 | 63 | logDir := t.TempDir() | |
| 66 | 64 | ||
| 67 | 65 | s1 := &entity.Session{ID: entity.SessionID("20251217_205106_a7b3"), Type: entity.SessionTypeBrowser, StartedAt: time.Date(2025, 12, 17, 20, 51, 6, 0, time.UTC)} | |
| 68 | - | mgr := &fakeSessionMgr{sessions: []*entity.Session{s1}} | |
| 66 | + | mgr := cmdmocks.NewMockSessionManager(t) | |
| 67 | + | mgr.EXPECT(). | |
| 68 | + | GetRecentSessions(mock.Anything, recentSessionsLimit). | |
| 69 | + | Return([]*entity.Session{s1}, nil). | |
| 70 | + | Once() | |
| 69 | 71 | ||
| 70 | 72 | info, err := findSession(context.Background(), mgr, logDir, "a7b3") | |
| 71 | 73 | require.NoError(t, err) | |
A internal/cli/cmd/mocks/logs.go +107 -0
| @@ -0,0 +1,107 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | "github.com/bnema/dumber/internal/domain/entity" | |
| 11 | + | mock "github.com/stretchr/testify/mock" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | // NewMockSessionManager creates a new instance of MockSessionManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 15 | + | // The first argument is typically a *testing.T value. | |
| 16 | + | func NewMockSessionManager(t interface { | |
| 17 | + | mock.TestingT | |
| 18 | + | Cleanup(func()) | |
| 19 | + | }) *MockSessionManager { | |
| 20 | + | mock := &MockSessionManager{} | |
| 21 | + | mock.Mock.Test(t) | |
| 22 | + | ||
| 23 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 24 | + | ||
| 25 | + | return mock | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | // MockSessionManager is an autogenerated mock type for the sessionManager type | |
| 29 | + | type MockSessionManager struct { | |
| 30 | + | mock.Mock | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | type MockSessionManager_Expecter struct { | |
| 34 | + | mock *mock.Mock | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | func (_m *MockSessionManager) EXPECT() *MockSessionManager_Expecter { | |
| 38 | + | return &MockSessionManager_Expecter{mock: &_m.Mock} | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | // GetRecentSessions provides a mock function for the type MockSessionManager | |
| 42 | + | func (_mock *MockSessionManager) GetRecentSessions(ctx context.Context, limit int) ([]*entity.Session, error) { | |
| 43 | + | ret := _mock.Called(ctx, limit) | |
| 44 | + | ||
| 45 | + | if len(ret) == 0 { | |
| 46 | + | panic("no return value specified for GetRecentSessions") | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | var r0 []*entity.Session | |
| 50 | + | var r1 error | |
| 51 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, int) ([]*entity.Session, error)); ok { | |
| 52 | + | return returnFunc(ctx, limit) | |
| 53 | + | } | |
| 54 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, int) []*entity.Session); ok { | |
| 55 | + | r0 = returnFunc(ctx, limit) | |
| 56 | + | } else { | |
| 57 | + | if ret.Get(0) != nil { | |
| 58 | + | r0 = ret.Get(0).([]*entity.Session) | |
| 59 | + | } | |
| 60 | + | } | |
| 61 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, int) error); ok { | |
| 62 | + | r1 = returnFunc(ctx, limit) | |
| 63 | + | } else { | |
| 64 | + | r1 = ret.Error(1) | |
| 65 | + | } | |
| 66 | + | return r0, r1 | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | // MockSessionManager_GetRecentSessions_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRecentSessions' | |
| 70 | + | type MockSessionManager_GetRecentSessions_Call struct { | |
| 71 | + | *mock.Call | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | // GetRecentSessions is a helper method to define mock.On call | |
| 75 | + | // - ctx context.Context | |
| 76 | + | // - limit int | |
| 77 | + | func (_e *MockSessionManager_Expecter) GetRecentSessions(ctx any, limit any) *MockSessionManager_GetRecentSessions_Call { | |
| 78 | + | return &MockSessionManager_GetRecentSessions_Call{Call: _e.mock.On("GetRecentSessions", ctx, limit)} | |
| 79 | + | } | |
| 80 | + | ||
| 81 | + | func (_c *MockSessionManager_GetRecentSessions_Call) Run(run func(ctx context.Context, limit int)) *MockSessionManager_GetRecentSessions_Call { | |
| 82 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 83 | + | var arg0 context.Context | |
| 84 | + | if args[0] != nil { | |
| 85 | + | arg0 = args[0].(context.Context) | |
| 86 | + | } | |
| 87 | + | var arg1 int | |
| 88 | + | if args[1] != nil { | |
| 89 | + | arg1 = args[1].(int) | |
| 90 | + | } | |
| 91 | + | run( | |
| 92 | + | arg0, | |
| 93 | + | arg1, | |
| 94 | + | ) | |
| 95 | + | }) | |
| 96 | + | return _c | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | func (_c *MockSessionManager_GetRecentSessions_Call) Return(sessions []*entity.Session, err error) *MockSessionManager_GetRecentSessions_Call { | |
| 100 | + | _c.Call.Return(sessions, err) | |
| 101 | + | return _c | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | func (_c *MockSessionManager_GetRecentSessions_Call) RunAndReturn(run func(ctx context.Context, limit int) ([]*entity.Session, error)) *MockSessionManager_GetRecentSessions_Call { | |
| 105 | + | _c.Call.Return(run) | |
| 106 | + | return _c | |
| 107 | + | } | |
A internal/infrastructure/audio/pipewire/mocks/player.go +257 -0
| @@ -0,0 +1,257 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "github.com/bnema/purego-pipewire/pipewire" | |
| 9 | + | mock "github.com/stretchr/testify/mock" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | // NewMockPlayer creates a new instance of MockPlayer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 13 | + | // The first argument is typically a *testing.T value. | |
| 14 | + | func NewMockPlayer(t interface { | |
| 15 | + | mock.TestingT | |
| 16 | + | Cleanup(func()) | |
| 17 | + | }) *MockPlayer { | |
| 18 | + | mock := &MockPlayer{} | |
| 19 | + | mock.Mock.Test(t) | |
| 20 | + | ||
| 21 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 22 | + | ||
| 23 | + | return mock | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | // MockPlayer is an autogenerated mock type for the Player type | |
| 27 | + | type MockPlayer struct { | |
| 28 | + | mock.Mock | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | type MockPlayer_Expecter struct { | |
| 32 | + | mock *mock.Mock | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | func (_m *MockPlayer) EXPECT() *MockPlayer_Expecter { | |
| 36 | + | return &MockPlayer_Expecter{mock: &_m.Mock} | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | // Close provides a mock function for the type MockPlayer | |
| 40 | + | func (_mock *MockPlayer) Close() error { | |
| 41 | + | ret := _mock.Called() | |
| 42 | + | ||
| 43 | + | if len(ret) == 0 { | |
| 44 | + | panic("no return value specified for Close") | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | var r0 error | |
| 48 | + | if returnFunc, ok := ret.Get(0).(func() error); ok { | |
| 49 | + | r0 = returnFunc() | |
| 50 | + | } else { | |
| 51 | + | r0 = ret.Error(0) | |
| 52 | + | } | |
| 53 | + | return r0 | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | // MockPlayer_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' | |
| 57 | + | type MockPlayer_Close_Call struct { | |
| 58 | + | *mock.Call | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | // Close is a helper method to define mock.On call | |
| 62 | + | func (_e *MockPlayer_Expecter) Close() *MockPlayer_Close_Call { | |
| 63 | + | return &MockPlayer_Close_Call{Call: _e.mock.On("Close")} | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | func (_c *MockPlayer_Close_Call) Run(run func()) *MockPlayer_Close_Call { | |
| 67 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 68 | + | run() | |
| 69 | + | }) | |
| 70 | + | return _c | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | func (_c *MockPlayer_Close_Call) Return(err error) *MockPlayer_Close_Call { | |
| 74 | + | _c.Call.Return(err) | |
| 75 | + | return _c | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | func (_c *MockPlayer_Close_Call) RunAndReturn(run func() error) *MockPlayer_Close_Call { | |
| 79 | + | _c.Call.Return(run) | |
| 80 | + | return _c | |
| 81 | + | } | |
| 82 | + | ||
| 83 | + | // Pause provides a mock function for the type MockPlayer | |
| 84 | + | func (_mock *MockPlayer) Pause() error { | |
| 85 | + | ret := _mock.Called() | |
| 86 | + | ||
| 87 | + | if len(ret) == 0 { | |
| 88 | + | panic("no return value specified for Pause") | |
| 89 | + | } | |
| 90 | + | ||
| 91 | + | var r0 error | |
| 92 | + | if returnFunc, ok := ret.Get(0).(func() error); ok { | |
| 93 | + | r0 = returnFunc() | |
| 94 | + | } else { | |
| 95 | + | r0 = ret.Error(0) | |
| 96 | + | } | |
| 97 | + | return r0 | |
| 98 | + | } | |
| 99 | + | ||
| 100 | + | // MockPlayer_Pause_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Pause' | |
| 101 | + | type MockPlayer_Pause_Call struct { | |
| 102 | + | *mock.Call | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | // Pause is a helper method to define mock.On call | |
| 106 | + | func (_e *MockPlayer_Expecter) Pause() *MockPlayer_Pause_Call { | |
| 107 | + | return &MockPlayer_Pause_Call{Call: _e.mock.On("Pause")} | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | func (_c *MockPlayer_Pause_Call) Run(run func()) *MockPlayer_Pause_Call { | |
| 111 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 112 | + | run() | |
| 113 | + | }) | |
| 114 | + | return _c | |
| 115 | + | } | |
| 116 | + | ||
| 117 | + | func (_c *MockPlayer_Pause_Call) Return(err error) *MockPlayer_Pause_Call { | |
| 118 | + | _c.Call.Return(err) | |
| 119 | + | return _c | |
| 120 | + | } | |
| 121 | + | ||
| 122 | + | func (_c *MockPlayer_Pause_Call) RunAndReturn(run func() error) *MockPlayer_Pause_Call { | |
| 123 | + | _c.Call.Return(run) | |
| 124 | + | return _c | |
| 125 | + | } | |
| 126 | + | ||
| 127 | + | // Start provides a mock function for the type MockPlayer | |
| 128 | + | func (_mock *MockPlayer) Start() error { | |
| 129 | + | ret := _mock.Called() | |
| 130 | + | ||
| 131 | + | if len(ret) == 0 { | |
| 132 | + | panic("no return value specified for Start") | |
| 133 | + | } | |
| 134 | + | ||
| 135 | + | var r0 error | |
| 136 | + | if returnFunc, ok := ret.Get(0).(func() error); ok { | |
| 137 | + | r0 = returnFunc() | |
| 138 | + | } else { | |
| 139 | + | r0 = ret.Error(0) | |
| 140 | + | } | |
| 141 | + | return r0 | |
| 142 | + | } | |
| 143 | + | ||
| 144 | + | // MockPlayer_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' | |
| 145 | + | type MockPlayer_Start_Call struct { | |
| 146 | + | *mock.Call | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | // Start is a helper method to define mock.On call | |
| 150 | + | func (_e *MockPlayer_Expecter) Start() *MockPlayer_Start_Call { | |
| 151 | + | return &MockPlayer_Start_Call{Call: _e.mock.On("Start")} | |
| 152 | + | } | |
| 153 | + | ||
| 154 | + | func (_c *MockPlayer_Start_Call) Run(run func()) *MockPlayer_Start_Call { | |
| 155 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 156 | + | run() | |
| 157 | + | }) | |
| 158 | + | return _c | |
| 159 | + | } | |
| 160 | + | ||
| 161 | + | func (_c *MockPlayer_Start_Call) Return(err error) *MockPlayer_Start_Call { | |
| 162 | + | _c.Call.Return(err) | |
| 163 | + | return _c | |
| 164 | + | } | |
| 165 | + | ||
| 166 | + | func (_c *MockPlayer_Start_Call) RunAndReturn(run func() error) *MockPlayer_Start_Call { | |
| 167 | + | _c.Call.Return(run) | |
| 168 | + | return _c | |
| 169 | + | } | |
| 170 | + | ||
| 171 | + | // State provides a mock function for the type MockPlayer | |
| 172 | + | func (_mock *MockPlayer) State() pipewire.PlayerState { | |
| 173 | + | ret := _mock.Called() | |
| 174 | + | ||
| 175 | + | if len(ret) == 0 { | |
| 176 | + | panic("no return value specified for State") | |
| 177 | + | } | |
| 178 | + | ||
| 179 | + | var r0 pipewire.PlayerState | |
| 180 | + | if returnFunc, ok := ret.Get(0).(func() pipewire.PlayerState); ok { | |
| 181 | + | r0 = returnFunc() | |
| 182 | + | } else { | |
| 183 | + | r0 = ret.Get(0).(pipewire.PlayerState) | |
| 184 | + | } | |
| 185 | + | return r0 | |
| 186 | + | } | |
| 187 | + | ||
| 188 | + | // MockPlayer_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State' | |
| 189 | + | type MockPlayer_State_Call struct { | |
| 190 | + | *mock.Call | |
| 191 | + | } | |
| 192 | + | ||
| 193 | + | // State is a helper method to define mock.On call | |
| 194 | + | func (_e *MockPlayer_Expecter) State() *MockPlayer_State_Call { | |
| 195 | + | return &MockPlayer_State_Call{Call: _e.mock.On("State")} | |
| 196 | + | } | |
| 197 | + | ||
| 198 | + | func (_c *MockPlayer_State_Call) Run(run func()) *MockPlayer_State_Call { | |
| 199 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 200 | + | run() | |
| 201 | + | }) | |
| 202 | + | return _c | |
| 203 | + | } | |
| 204 | + | ||
| 205 | + | func (_c *MockPlayer_State_Call) Return(playerState pipewire.PlayerState) *MockPlayer_State_Call { | |
| 206 | + | _c.Call.Return(playerState) | |
| 207 | + | return _c | |
| 208 | + | } | |
| 209 | + | ||
| 210 | + | func (_c *MockPlayer_State_Call) RunAndReturn(run func() pipewire.PlayerState) *MockPlayer_State_Call { | |
| 211 | + | _c.Call.Return(run) | |
| 212 | + | return _c | |
| 213 | + | } | |
| 214 | + | ||
| 215 | + | // Stop provides a mock function for the type MockPlayer | |
| 216 | + | func (_mock *MockPlayer) Stop() error { | |
| 217 | + | ret := _mock.Called() | |
| 218 | + | ||
| 219 | + | if len(ret) == 0 { | |
| 220 | + | panic("no return value specified for Stop") | |
| 221 | + | } | |
| 222 | + | ||
| 223 | + | var r0 error | |
| 224 | + | if returnFunc, ok := ret.Get(0).(func() error); ok { | |
| 225 | + | r0 = returnFunc() | |
| 226 | + | } else { | |
| 227 | + | r0 = ret.Error(0) | |
| 228 | + | } | |
| 229 | + | return r0 | |
| 230 | + | } | |
| 231 | + | ||
| 232 | + | // MockPlayer_Stop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Stop' | |
| 233 | + | type MockPlayer_Stop_Call struct { | |
| 234 | + | *mock.Call | |
| 235 | + | } | |
| 236 | + | ||
| 237 | + | // Stop is a helper method to define mock.On call | |
| 238 | + | func (_e *MockPlayer_Expecter) Stop() *MockPlayer_Stop_Call { | |
| 239 | + | return &MockPlayer_Stop_Call{Call: _e.mock.On("Stop")} | |
| 240 | + | } | |
| 241 | + | ||
| 242 | + | func (_c *MockPlayer_Stop_Call) Run(run func()) *MockPlayer_Stop_Call { | |
| 243 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 244 | + | run() | |
| 245 | + | }) | |
| 246 | + | return _c | |
| 247 | + | } | |
| 248 | + | ||
| 249 | + | func (_c *MockPlayer_Stop_Call) Return(err error) *MockPlayer_Stop_Call { | |
| 250 | + | _c.Call.Return(err) | |
| 251 | + | return _c | |
| 252 | + | } | |
| 253 | + | ||
| 254 | + | func (_c *MockPlayer_Stop_Call) RunAndReturn(run func() error) *MockPlayer_Stop_Call { | |
| 255 | + | _c.Call.Return(run) | |
| 256 | + | return _c | |
| 257 | + | } | |
M internal/infrastructure/audio/pipewire/output_test.go +36 -21
| @@ -8,15 +8,15 @@ import ( | |||
| 8 | 8 | "testing" | |
| 9 | 9 | ||
| 10 | 10 | "github.com/bnema/dumber/internal/application/port" | |
| 11 | + | pipewiremocks "github.com/bnema/dumber/internal/infrastructure/audio/pipewire/mocks" | |
| 11 | 12 | pwpipewire "github.com/bnema/purego-pipewire/pipewire" | |
| 12 | 13 | "github.com/stretchr/testify/assert" | |
| 13 | 14 | "github.com/stretchr/testify/require" | |
| 14 | 15 | ) | |
| 15 | 16 | ||
| 16 | - | // --- Test doubles --- | |
| 17 | + | type playerRecorder struct { | |
| 18 | + | *pipewiremocks.MockPlayer | |
| 17 | 19 | ||
| 18 | - | // fakePlayer is a test double for purego-pipewire Player. | |
| 19 | - | type fakePlayer struct { | |
| 20 | 20 | started bool | |
| 21 | 21 | paused bool | |
| 22 | 22 | stopped bool | |
| @@ -28,7 +28,19 @@ type fakePlayer struct { | |||
| 28 | 28 | closeErr error | |
| 29 | 29 | } | |
| 30 | 30 | ||
| 31 | - | func (p *fakePlayer) Start() error { | |
| 31 | + | func newPlayerRecorder(t *testing.T) *playerRecorder { | |
| 32 | + | t.Helper() | |
| 33 | + | ||
| 34 | + | recorder := &playerRecorder{MockPlayer: pipewiremocks.NewMockPlayer(t)} | |
| 35 | + | recorder.EXPECT().Start().RunAndReturn(recorder.start).Maybe() | |
| 36 | + | recorder.EXPECT().Pause().RunAndReturn(recorder.pause).Maybe() | |
| 37 | + | recorder.EXPECT().Stop().RunAndReturn(recorder.stop).Maybe() | |
| 38 | + | recorder.EXPECT().Close().RunAndReturn(recorder.close).Maybe() | |
| 39 | + | recorder.EXPECT().State().RunAndReturn(recorder.currentState).Maybe() | |
| 40 | + | return recorder | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | func (p *playerRecorder) start() error { | |
| 32 | 44 | if p.startErr != nil { | |
| 33 | 45 | return p.startErr | |
| 34 | 46 | } | |
| @@ -37,13 +49,13 @@ func (p *fakePlayer) Start() error { | |||
| 37 | 49 | return nil | |
| 38 | 50 | } | |
| 39 | 51 | ||
| 40 | - | func (p *fakePlayer) Pause() error { | |
| 52 | + | func (p *playerRecorder) pause() error { | |
| 41 | 53 | p.paused = true | |
| 42 | 54 | p.state = pwpipewire.PlayerStatePaused | |
| 43 | 55 | return nil | |
| 44 | 56 | } | |
| 45 | 57 | ||
| 46 | - | func (p *fakePlayer) Stop() error { | |
| 58 | + | func (p *playerRecorder) stop() error { | |
| 47 | 59 | if p.stopErr != nil { | |
| 48 | 60 | return p.stopErr | |
| 49 | 61 | } | |
| @@ -52,7 +64,7 @@ func (p *fakePlayer) Stop() error { | |||
| 52 | 64 | return nil | |
| 53 | 65 | } | |
| 54 | 66 | ||
| 55 | - | func (p *fakePlayer) Close() error { | |
| 67 | + | func (p *playerRecorder) close() error { | |
| 56 | 68 | if p.closeErr != nil { | |
| 57 | 69 | return p.closeErr | |
| 58 | 70 | } | |
| @@ -61,7 +73,7 @@ func (p *fakePlayer) Close() error { | |||
| 61 | 73 | return nil | |
| 62 | 74 | } | |
| 63 | 75 | ||
| 64 | - | func (p *fakePlayer) State() pwpipewire.PlayerState { | |
| 76 | + | func (p *playerRecorder) currentState() pwpipewire.PlayerState { | |
| 65 | 77 | return p.state | |
| 66 | 78 | } | |
| 67 | 79 | ||
| @@ -69,7 +81,7 @@ func (p *fakePlayer) State() pwpipewire.PlayerState { | |||
| 69 | 81 | ||
| 70 | 82 | func TestFactory_NewStream_ValidFormat_CreatesStream(t *testing.T) { | |
| 71 | 83 | var capturedConfig pwpipewire.PlayerConfig | |
| 72 | - | player := &fakePlayer{} | |
| 84 | + | player := newPlayerRecorder(t) | |
| 73 | 85 | ||
| 74 | 86 | factory := &Factory{ | |
| 75 | 87 | createPlayer: func(config pwpipewire.PlayerConfig, _ pwpipewire.PlayerCallbacks) (pwpipewire.Player, error) { | |
| @@ -175,7 +187,8 @@ func TestFactory_NewStream_PlayerCreationFails_ReturnsError(t *testing.T) { | |||
| 175 | 187 | } | |
| 176 | 188 | ||
| 177 | 189 | func TestFactory_NewStream_PlayerStartFails_ClosesAndReturnsError(t *testing.T) { | |
| 178 | - | player := &fakePlayer{startErr: errors.New("start failed")} | |
| 190 | + | player := newPlayerRecorder(t) | |
| 191 | + | player.startErr = errors.New("start failed") | |
| 179 | 192 | factory := &Factory{ | |
| 180 | 193 | createPlayer: func(_ pwpipewire.PlayerConfig, _ pwpipewire.PlayerCallbacks) (pwpipewire.Player, error) { | |
| 181 | 194 | return player, nil | |
| @@ -224,7 +237,7 @@ func TestFactory_NewStream_MultipleFormats_Accepted(t *testing.T) { | |||
| 224 | 237 | assert.Equal(t, fmt.SampleRate, config.SampleRate) | |
| 225 | 238 | assert.Equal(t, fmt.ChannelCount, config.Channels) | |
| 226 | 239 | assert.Equal(t, fmt.FramesPerBuffer, config.FramesPerBuffer) | |
| 227 | - | return &fakePlayer{}, nil | |
| 240 | + | return newPlayerRecorder(t), nil | |
| 228 | 241 | }, | |
| 229 | 242 | } | |
| 230 | 243 | ||
| @@ -244,7 +257,7 @@ func TestFactory_ImplementsPortInterface(_ *testing.T) { | |||
| 244 | 257 | // --- Stream adapter tests --- | |
| 245 | 258 | ||
| 246 | 259 | func TestPlayerStreamAdapter_Write_SendsToBuffer(t *testing.T) { | |
| 247 | - | player := &fakePlayer{} | |
| 260 | + | player := newPlayerRecorder(t) | |
| 248 | 261 | adapter := &playerStreamAdapter{ | |
| 249 | 262 | player: player, | |
| 250 | 263 | buf: make(chan [][]float32, 4), | |
| @@ -265,7 +278,7 @@ func TestPlayerStreamAdapter_Write_SendsToBuffer(t *testing.T) { | |||
| 265 | 278 | } | |
| 266 | 279 | ||
| 267 | 280 | func TestPlayerStreamAdapter_Write_AfterClose_ReturnsError(t *testing.T) { | |
| 268 | - | player := &fakePlayer{} | |
| 281 | + | player := newPlayerRecorder(t) | |
| 269 | 282 | adapter := &playerStreamAdapter{ | |
| 270 | 283 | player: player, | |
| 271 | 284 | buf: make(chan [][]float32, 4), | |
| @@ -279,7 +292,7 @@ func TestPlayerStreamAdapter_Write_AfterClose_ReturnsError(t *testing.T) { | |||
| 279 | 292 | } | |
| 280 | 293 | ||
| 281 | 294 | func TestPlayerStreamAdapter_Close_StopsAndClosesPlayer(t *testing.T) { | |
| 282 | - | player := &fakePlayer{} | |
| 295 | + | player := newPlayerRecorder(t) | |
| 283 | 296 | adapter := &playerStreamAdapter{ | |
| 284 | 297 | player: player, | |
| 285 | 298 | buf: make(chan [][]float32, 4), | |
| @@ -294,7 +307,7 @@ func TestPlayerStreamAdapter_Close_StopsAndClosesPlayer(t *testing.T) { | |||
| 294 | 307 | } | |
| 295 | 308 | ||
| 296 | 309 | func TestPlayerStreamAdapter_Close_IsIdempotent(t *testing.T) { | |
| 297 | - | player := &fakePlayer{} | |
| 310 | + | player := newPlayerRecorder(t) | |
| 298 | 311 | adapter := &playerStreamAdapter{ | |
| 299 | 312 | player: player, | |
| 300 | 313 | buf: make(chan [][]float32, 4), | |
| @@ -306,7 +319,8 @@ func TestPlayerStreamAdapter_Close_IsIdempotent(t *testing.T) { | |||
| 306 | 319 | } | |
| 307 | 320 | ||
| 308 | 321 | func TestPlayerStreamAdapter_Close_PropagatesStopError(t *testing.T) { | |
| 309 | - | player := &fakePlayer{stopErr: errors.New("stop error")} | |
| 322 | + | player := newPlayerRecorder(t) | |
| 323 | + | player.stopErr = errors.New("stop error") | |
| 310 | 324 | adapter := &playerStreamAdapter{ | |
| 311 | 325 | player: player, | |
| 312 | 326 | buf: make(chan [][]float32, 4), | |
| @@ -319,7 +333,8 @@ func TestPlayerStreamAdapter_Close_PropagatesStopError(t *testing.T) { | |||
| 319 | 333 | } | |
| 320 | 334 | ||
| 321 | 335 | func TestPlayerStreamAdapter_Close_PropagatesCloseError(t *testing.T) { | |
| 322 | - | player := &fakePlayer{closeErr: errors.New("close error")} | |
| 336 | + | player := newPlayerRecorder(t) | |
| 337 | + | player.closeErr = errors.New("close error") | |
| 323 | 338 | adapter := &playerStreamAdapter{ | |
| 324 | 339 | player: player, | |
| 325 | 340 | buf: make(chan [][]float32, 4), | |
| @@ -427,7 +442,7 @@ func TestCopyToPCM_EmptySamples_ReturnsZero(t *testing.T) { | |||
| 427 | 442 | ||
| 428 | 443 | func TestPlayerStreamAdapter_Write_DropsWhenBufferFull(t *testing.T) { | |
| 429 | 444 | adapter := &playerStreamAdapter{ | |
| 430 | - | player: &fakePlayer{}, | |
| 445 | + | player: newPlayerRecorder(t), | |
| 431 | 446 | buf: make(chan [][]float32, 4), | |
| 432 | 447 | closed: make(chan struct{}), | |
| 433 | 448 | } | |
| @@ -450,7 +465,7 @@ func TestPlayerStreamAdapter_Write_DropsWhenBufferFull(t *testing.T) { | |||
| 450 | 465 | ||
| 451 | 466 | func TestPlayerStreamAdapter_Write_DropsLogOnlyOnFirstAndPowerOfTwo(t *testing.T) { | |
| 452 | 467 | adapter := &playerStreamAdapter{ | |
| 453 | - | player: &fakePlayer{}, | |
| 468 | + | player: newPlayerRecorder(t), | |
| 454 | 469 | buf: make(chan [][]float32, 1), // tiny buffer to force drops | |
| 455 | 470 | closed: make(chan struct{}), | |
| 456 | 471 | } | |
| @@ -470,7 +485,7 @@ func TestPlayerStreamAdapter_Write_DropsLogOnlyOnFirstAndPowerOfTwo(t *testing.T | |||
| 470 | 485 | // --- Concurrent Write / Close tests --- | |
| 471 | 486 | ||
| 472 | 487 | func TestPlayerStreamAdapter_ConcurrentWriteClose_NoRace(t *testing.T) { | |
| 473 | - | player := &fakePlayer{} | |
| 488 | + | player := newPlayerRecorder(t) | |
| 474 | 489 | adapter := &playerStreamAdapter{ | |
| 475 | 490 | player: player, | |
| 476 | 491 | buf: make(chan [][]float32, 4), | |
| @@ -507,7 +522,7 @@ func TestNewFactory_Succeeds(t *testing.T) { | |||
| 507 | 522 | ||
| 508 | 523 | func TestStream_WriteFlowsThroughToFill(t *testing.T) { | |
| 509 | 524 | var capturedCallbacks pwpipewire.PlayerCallbacks | |
| 510 | - | player := &fakePlayer{} | |
| 525 | + | player := newPlayerRecorder(t) | |
| 511 | 526 | ||
| 512 | 527 | factory := &Factory{ | |
| 513 | 528 | createPlayer: func(_ pwpipewire.PlayerConfig, callbacks pwpipewire.PlayerCallbacks) (pwpipewire.Player, error) { | |
M internal/infrastructure/cef/popup_opener_bridge_test.go +1 -1
| @@ -66,6 +66,6 @@ func TestHandlePopupOpenerPostMessage_UsesPopupCommittedURIForSourceMetadata(t * | |||
| 66 | 66 | DataKind: "json", | |
| 67 | 67 | TargetOrigin: "https://app.example", | |
| 68 | 68 | SourceOrigin: "https://evil.example", | |
| 69 | - | SourceHref: "https://evil.example/fake", | |
| 69 | + | SourceHref: "https://evil.example/decoy", | |
| 70 | 70 | }) | |
| 71 | 71 | } | |
M internal/infrastructure/clipboard/clipboard_test.go +55 -37
| @@ -9,10 +9,14 @@ import ( | |||
| 9 | 9 | "testing" | |
| 10 | 10 | ||
| 11 | 11 | "github.com/bnema/dumber/internal/domain/entity" | |
| 12 | + | clipboardmocks "github.com/bnema/dumber/internal/infrastructure/clipboard/mocks" | |
| 13 | + | "github.com/stretchr/testify/mock" | |
| 12 | 14 | "github.com/stretchr/testify/require" | |
| 13 | 15 | ) | |
| 14 | 16 | ||
| 15 | - | type fakeToolkitClipboard struct { | |
| 17 | + | type toolkitClipboardRecorder struct { | |
| 18 | + | *clipboardmocks.MockToolkitClipboard | |
| 19 | + | ||
| 16 | 20 | writeTextCalls int | |
| 17 | 21 | readTextCalls int | |
| 18 | 22 | writeImageCalls int | |
| @@ -23,67 +27,81 @@ type fakeToolkitClipboard struct { | |||
| 23 | 27 | writeImageErr error | |
| 24 | 28 | } | |
| 25 | 29 | ||
| 26 | - | func (f *fakeToolkitClipboard) WriteText(_ context.Context, text string) error { | |
| 27 | - | f.writeTextCalls++ | |
| 28 | - | f.text = text | |
| 29 | - | return f.writeTextErr | |
| 30 | + | func newToolkitClipboardRecorder(t *testing.T) *toolkitClipboardRecorder { | |
| 31 | + | t.Helper() | |
| 32 | + | ||
| 33 | + | recorder := &toolkitClipboardRecorder{MockToolkitClipboard: clipboardmocks.NewMockToolkitClipboard(t)} | |
| 34 | + | recorder.EXPECT().WriteText(mock.Anything, mock.Anything).RunAndReturn(recorder.writeText).Maybe() | |
| 35 | + | recorder.EXPECT().ReadText(mock.Anything).RunAndReturn(recorder.readText).Maybe() | |
| 36 | + | recorder.EXPECT().WriteImage(mock.Anything, mock.Anything).RunAndReturn(recorder.writeImage).Maybe() | |
| 37 | + | return recorder | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | func (r *toolkitClipboardRecorder) writeText(_ context.Context, text string) error { | |
| 41 | + | r.writeTextCalls++ | |
| 42 | + | r.text = text | |
| 43 | + | return r.writeTextErr | |
| 30 | 44 | } | |
| 31 | 45 | ||
| 32 | - | func (f *fakeToolkitClipboard) ReadText(_ context.Context) (string, error) { | |
| 33 | - | f.readTextCalls++ | |
| 34 | - | return f.text, f.readTextErr | |
| 46 | + | func (r *toolkitClipboardRecorder) readText(_ context.Context) (string, error) { | |
| 47 | + | r.readTextCalls++ | |
| 48 | + | return r.text, r.readTextErr | |
| 35 | 49 | } | |
| 36 | 50 | ||
| 37 | - | func (f *fakeToolkitClipboard) WriteImage(_ context.Context, image entity.ImageData) error { | |
| 38 | - | f.writeImageCalls++ | |
| 39 | - | f.image = image | |
| 40 | - | return f.writeImageErr | |
| 51 | + | func (r *toolkitClipboardRecorder) writeImage(_ context.Context, image entity.ImageData) error { | |
| 52 | + | r.writeImageCalls++ | |
| 53 | + | r.image = image | |
| 54 | + | return r.writeImageErr | |
| 41 | 55 | } | |
| 42 | 56 | ||
| 43 | 57 | func TestAdapter_ReadTextFallsBackToToolkitClipboardWhenNoSystemTool(t *testing.T) { | |
| 44 | - | fake := &fakeToolkitClipboard{text: "toolkit text"} | |
| 45 | - | adapter := &Adapter{toolkit: fake} | |
| 58 | + | toolkit := newToolkitClipboardRecorder(t) | |
| 59 | + | toolkit.text = "toolkit text" | |
| 60 | + | adapter := &Adapter{toolkit: toolkit} | |
| 46 | 61 | ||
| 47 | 62 | text, err := adapter.ReadText(context.Background()) | |
| 48 | 63 | ||
| 49 | 64 | require.NoError(t, err) | |
| 50 | 65 | require.Equal(t, "toolkit text", text) | |
| 51 | - | require.Equal(t, 1, fake.readTextCalls) | |
| 66 | + | require.Equal(t, 1, toolkit.readTextCalls) | |
| 52 | 67 | } | |
| 53 | 68 | ||
| 54 | 69 | func TestAdapter_HasTextReturnsTrueWhenToolkitFallbackHasText(t *testing.T) { | |
| 55 | - | fake := &fakeToolkitClipboard{text: "toolkit text"} | |
| 56 | - | adapter := &Adapter{toolkit: fake} | |
| 70 | + | toolkit := newToolkitClipboardRecorder(t) | |
| 71 | + | toolkit.text = "toolkit text" | |
| 72 | + | adapter := &Adapter{toolkit: toolkit} | |
| 57 | 73 | ||
| 58 | 74 | hasText, err := adapter.HasText(context.Background()) | |
| 59 | 75 | ||
| 60 | 76 | require.NoError(t, err) | |
| 61 | 77 | require.True(t, hasText) | |
| 62 | - | require.Equal(t, 1, fake.readTextCalls) | |
| 78 | + | require.Equal(t, 1, toolkit.readTextCalls) | |
| 63 | 79 | } | |
| 64 | 80 | ||
| 65 | 81 | func TestAdapter_WriteTextReturnsToolkitErrorWhenNoSystemTool(t *testing.T) { | |
| 66 | 82 | sentinel := errors.New("toolkit text failed") | |
| 67 | - | fake := &fakeToolkitClipboard{writeTextErr: sentinel} | |
| 68 | - | adapter := &Adapter{toolkit: fake} | |
| 83 | + | toolkit := newToolkitClipboardRecorder(t) | |
| 84 | + | toolkit.writeTextErr = sentinel | |
| 85 | + | adapter := &Adapter{toolkit: toolkit} | |
| 69 | 86 | ||
| 70 | 87 | err := adapter.WriteText(context.Background(), "hello") | |
| 71 | 88 | ||
| 72 | 89 | require.Equal(t, sentinel, err) | |
| 73 | - | require.Equal(t, 1, fake.writeTextCalls) | |
| 74 | - | require.Equal(t, "hello", fake.text) | |
| 90 | + | require.Equal(t, 1, toolkit.writeTextCalls) | |
| 91 | + | require.Equal(t, "hello", toolkit.text) | |
| 75 | 92 | } | |
| 76 | 93 | ||
| 77 | 94 | func TestAdapter_WriteImageReturnsToolkitErrorWhenNoSystemTool(t *testing.T) { | |
| 78 | 95 | sentinel := errors.New("toolkit image failed") | |
| 79 | - | fake := &fakeToolkitClipboard{writeImageErr: sentinel} | |
| 80 | - | adapter := &Adapter{toolkit: fake} | |
| 96 | + | toolkit := newToolkitClipboardRecorder(t) | |
| 97 | + | toolkit.writeImageErr = sentinel | |
| 98 | + | adapter := &Adapter{toolkit: toolkit} | |
| 81 | 99 | ||
| 82 | 100 | err := adapter.WriteImage(context.Background(), entity.ImageData{Bytes: []byte{1}, MimeType: "image/png"}) | |
| 83 | 101 | ||
| 84 | 102 | require.Equal(t, sentinel, err) | |
| 85 | - | require.Equal(t, 1, fake.writeImageCalls) | |
| 86 | - | require.Equal(t, entity.ImageData{Bytes: []byte{1}, MimeType: "image/png"}, fake.image) | |
| 103 | + | require.Equal(t, 1, toolkit.writeImageCalls) | |
| 104 | + | require.Equal(t, entity.ImageData{Bytes: []byte{1}, MimeType: "image/png"}, toolkit.image) | |
| 87 | 105 | } | |
| 88 | 106 | ||
| 89 | 107 | func TestNew_FallsBackToToolkitClipboardWhenSystemToolsUnavailable(t *testing.T) { | |
| @@ -94,9 +112,9 @@ func TestNew_FallsBackToToolkitClipboardWhenSystemToolsUnavailable(t *testing.T) | |||
| 94 | 112 | lookPath = oldLookPath | |
| 95 | 113 | }) | |
| 96 | 114 | ||
| 97 | - | fake := &fakeToolkitClipboard{} | |
| 115 | + | toolkit := newToolkitClipboardRecorder(t) | |
| 98 | 116 | newToolkitClipboard = func() toolkitClipboard { | |
| 99 | - | return fake | |
| 117 | + | return toolkit | |
| 100 | 118 | } | |
| 101 | 119 | lookPath = func(_ string) (string, error) { | |
| 102 | 120 | return "", errors.New("missing") | |
| @@ -107,8 +125,8 @@ func TestNew_FallsBackToToolkitClipboardWhenSystemToolsUnavailable(t *testing.T) | |||
| 107 | 125 | ||
| 108 | 126 | adapter := New().(*Adapter) | |
| 109 | 127 | require.NoError(t, adapter.WriteImage(context.Background(), entity.ImageData{Bytes: []byte{1, 2, 3}})) | |
| 110 | - | require.Equal(t, 1, fake.writeImageCalls) | |
| 111 | - | require.Equal(t, []byte{1, 2, 3}, fake.image.Bytes) | |
| 128 | + | require.Equal(t, 1, toolkit.writeImageCalls) | |
| 129 | + | require.Equal(t, []byte{1, 2, 3}, toolkit.image.Bytes) | |
| 112 | 130 | } | |
| 113 | 131 | ||
| 114 | 132 | func TestAdapter_WriteImagePrefersToolkitClipboardWhenAvailable(t *testing.T) { | |
| @@ -123,14 +141,14 @@ func TestAdapter_WriteImagePrefersToolkitClipboardWhenAvailable(t *testing.T) { | |||
| 123 | 141 | script := "#!/bin/sh\ncat > \"" + stdinPath + "\"\n" | |
| 124 | 142 | require.NoError(t, os.WriteFile(scriptPath, []byte(script), 0o755)) | |
| 125 | 143 | ||
| 126 | - | fake := &fakeToolkitClipboard{} | |
| 127 | - | newToolkitClipboard = func() toolkitClipboard { return fake } | |
| 144 | + | toolkit := newToolkitClipboardRecorder(t) | |
| 145 | + | newToolkitClipboard = func() toolkitClipboard { return toolkit } | |
| 128 | 146 | adapter := &Adapter{copyCmd: scriptPath} | |
| 129 | 147 | image := entity.ImageData{Bytes: []byte{1, 2, 3}, MimeType: "image/png"} | |
| 130 | 148 | ||
| 131 | 149 | require.NoError(t, adapter.WriteImage(context.Background(), image)) | |
| 132 | - | require.Equal(t, 1, fake.writeImageCalls) | |
| 133 | - | require.Equal(t, image, fake.image) | |
| 150 | + | require.Equal(t, 1, toolkit.writeImageCalls) | |
| 151 | + | require.Equal(t, image, toolkit.image) | |
| 134 | 152 | ||
| 135 | 153 | _, err := os.Stat(stdinPath) | |
| 136 | 154 | require.Error(t, err) | |
| @@ -193,14 +211,14 @@ func TestAdapter_WriteImageRejectsEmptyBytes(t *testing.T) { | |||
| 193 | 211 | lookPath = oldLookPath | |
| 194 | 212 | }) | |
| 195 | 213 | ||
| 196 | - | fake := &fakeToolkitClipboard{} | |
| 197 | - | newToolkitClipboard = func() toolkitClipboard { return fake } | |
| 214 | + | toolkit := newToolkitClipboardRecorder(t) | |
| 215 | + | newToolkitClipboard = func() toolkitClipboard { return toolkit } | |
| 198 | 216 | lookPath = func(_ string) (string, error) { return "/usr/bin/clipboard", nil } | |
| 199 | 217 | ||
| 200 | 218 | err := New().(*Adapter).WriteImage(context.Background(), entity.ImageData{}) | |
| 201 | 219 | require.Error(t, err) | |
| 202 | 220 | require.Contains(t, err.Error(), "empty image data") | |
| 203 | - | require.Zero(t, fake.writeImageCalls) | |
| 221 | + | require.Zero(t, toolkit.writeImageCalls) | |
| 204 | 222 | } | |
| 205 | 223 | ||
| 206 | 224 | func TestAdapter_WriteImageWithCommandUsesImageMimeType(t *testing.T) { | |
A internal/infrastructure/clipboard/mocks/clipboard.go +213 -0
| @@ -0,0 +1,213 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | "github.com/bnema/dumber/internal/domain/entity" | |
| 11 | + | mock "github.com/stretchr/testify/mock" | |
| 12 | + | ) | |
| 13 | + | ||
| 14 | + | // NewMockToolkitClipboard creates a new instance of MockToolkitClipboard. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 15 | + | // The first argument is typically a *testing.T value. | |
| 16 | + | func NewMockToolkitClipboard(t interface { | |
| 17 | + | mock.TestingT | |
| 18 | + | Cleanup(func()) | |
| 19 | + | }) *MockToolkitClipboard { | |
| 20 | + | mock := &MockToolkitClipboard{} | |
| 21 | + | mock.Mock.Test(t) | |
| 22 | + | ||
| 23 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 24 | + | ||
| 25 | + | return mock | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | // MockToolkitClipboard is an autogenerated mock type for the toolkitClipboard type | |
| 29 | + | type MockToolkitClipboard struct { | |
| 30 | + | mock.Mock | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | type MockToolkitClipboard_Expecter struct { | |
| 34 | + | mock *mock.Mock | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | func (_m *MockToolkitClipboard) EXPECT() *MockToolkitClipboard_Expecter { | |
| 38 | + | return &MockToolkitClipboard_Expecter{mock: &_m.Mock} | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | // ReadText provides a mock function for the type MockToolkitClipboard | |
| 42 | + | func (_mock *MockToolkitClipboard) ReadText(ctx context.Context) (string, error) { | |
| 43 | + | ret := _mock.Called(ctx) | |
| 44 | + | ||
| 45 | + | if len(ret) == 0 { | |
| 46 | + | panic("no return value specified for ReadText") | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | var r0 string | |
| 50 | + | var r1 error | |
| 51 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) (string, error)); ok { | |
| 52 | + | return returnFunc(ctx) | |
| 53 | + | } | |
| 54 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) string); ok { | |
| 55 | + | r0 = returnFunc(ctx) | |
| 56 | + | } else { | |
| 57 | + | r0 = ret.Get(0).(string) | |
| 58 | + | } | |
| 59 | + | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | |
| 60 | + | r1 = returnFunc(ctx) | |
| 61 | + | } else { | |
| 62 | + | r1 = ret.Error(1) | |
| 63 | + | } | |
| 64 | + | return r0, r1 | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | // MockToolkitClipboard_ReadText_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReadText' | |
| 68 | + | type MockToolkitClipboard_ReadText_Call struct { | |
| 69 | + | *mock.Call | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | // ReadText is a helper method to define mock.On call | |
| 73 | + | // - ctx context.Context | |
| 74 | + | func (_e *MockToolkitClipboard_Expecter) ReadText(ctx any) *MockToolkitClipboard_ReadText_Call { | |
| 75 | + | return &MockToolkitClipboard_ReadText_Call{Call: _e.mock.On("ReadText", ctx)} | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | func (_c *MockToolkitClipboard_ReadText_Call) Run(run func(ctx context.Context)) *MockToolkitClipboard_ReadText_Call { | |
| 79 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 80 | + | var arg0 context.Context | |
| 81 | + | if args[0] != nil { | |
| 82 | + | arg0 = args[0].(context.Context) | |
| 83 | + | } | |
| 84 | + | run( | |
| 85 | + | arg0, | |
| 86 | + | ) | |
| 87 | + | }) | |
| 88 | + | return _c | |
| 89 | + | } | |
| 90 | + | ||
| 91 | + | func (_c *MockToolkitClipboard_ReadText_Call) Return(s string, err error) *MockToolkitClipboard_ReadText_Call { | |
| 92 | + | _c.Call.Return(s, err) | |
| 93 | + | return _c | |
| 94 | + | } | |
| 95 | + | ||
| 96 | + | func (_c *MockToolkitClipboard_ReadText_Call) RunAndReturn(run func(ctx context.Context) (string, error)) *MockToolkitClipboard_ReadText_Call { | |
| 97 | + | _c.Call.Return(run) | |
| 98 | + | return _c | |
| 99 | + | } | |
| 100 | + | ||
| 101 | + | // WriteImage provides a mock function for the type MockToolkitClipboard | |
| 102 | + | func (_mock *MockToolkitClipboard) WriteImage(ctx context.Context, image entity.ImageData) error { | |
| 103 | + | ret := _mock.Called(ctx, image) | |
| 104 | + | ||
| 105 | + | if len(ret) == 0 { | |
| 106 | + | panic("no return value specified for WriteImage") | |
| 107 | + | } | |
| 108 | + | ||
| 109 | + | var r0 error | |
| 110 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, entity.ImageData) error); ok { | |
| 111 | + | r0 = returnFunc(ctx, image) | |
| 112 | + | } else { | |
| 113 | + | r0 = ret.Error(0) | |
| 114 | + | } | |
| 115 | + | return r0 | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | // MockToolkitClipboard_WriteImage_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteImage' | |
| 119 | + | type MockToolkitClipboard_WriteImage_Call struct { | |
| 120 | + | *mock.Call | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | // WriteImage is a helper method to define mock.On call | |
| 124 | + | // - ctx context.Context | |
| 125 | + | // - image entity.ImageData | |
| 126 | + | func (_e *MockToolkitClipboard_Expecter) WriteImage(ctx any, image any) *MockToolkitClipboard_WriteImage_Call { | |
| 127 | + | return &MockToolkitClipboard_WriteImage_Call{Call: _e.mock.On("WriteImage", ctx, image)} | |
| 128 | + | } | |
| 129 | + | ||
| 130 | + | func (_c *MockToolkitClipboard_WriteImage_Call) Run(run func(ctx context.Context, image entity.ImageData)) *MockToolkitClipboard_WriteImage_Call { | |
| 131 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 132 | + | var arg0 context.Context | |
| 133 | + | if args[0] != nil { | |
| 134 | + | arg0 = args[0].(context.Context) | |
| 135 | + | } | |
| 136 | + | var arg1 entity.ImageData | |
| 137 | + | if args[1] != nil { | |
| 138 | + | arg1 = args[1].(entity.ImageData) | |
| 139 | + | } | |
| 140 | + | run( | |
| 141 | + | arg0, | |
| 142 | + | arg1, | |
| 143 | + | ) | |
| 144 | + | }) | |
| 145 | + | return _c | |
| 146 | + | } | |
| 147 | + | ||
| 148 | + | func (_c *MockToolkitClipboard_WriteImage_Call) Return(err error) *MockToolkitClipboard_WriteImage_Call { | |
| 149 | + | _c.Call.Return(err) | |
| 150 | + | return _c | |
| 151 | + | } | |
| 152 | + | ||
| 153 | + | func (_c *MockToolkitClipboard_WriteImage_Call) RunAndReturn(run func(ctx context.Context, image entity.ImageData) error) *MockToolkitClipboard_WriteImage_Call { | |
| 154 | + | _c.Call.Return(run) | |
| 155 | + | return _c | |
| 156 | + | } | |
| 157 | + | ||
| 158 | + | // WriteText provides a mock function for the type MockToolkitClipboard | |
| 159 | + | func (_mock *MockToolkitClipboard) WriteText(ctx context.Context, text string) error { | |
| 160 | + | ret := _mock.Called(ctx, text) | |
| 161 | + | ||
| 162 | + | if len(ret) == 0 { | |
| 163 | + | panic("no return value specified for WriteText") | |
| 164 | + | } | |
| 165 | + | ||
| 166 | + | var r0 error | |
| 167 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string) error); ok { | |
| 168 | + | r0 = returnFunc(ctx, text) | |
| 169 | + | } else { | |
| 170 | + | r0 = ret.Error(0) | |
| 171 | + | } | |
| 172 | + | return r0 | |
| 173 | + | } | |
| 174 | + | ||
| 175 | + | // MockToolkitClipboard_WriteText_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WriteText' | |
| 176 | + | type MockToolkitClipboard_WriteText_Call struct { | |
| 177 | + | *mock.Call | |
| 178 | + | } | |
| 179 | + | ||
| 180 | + | // WriteText is a helper method to define mock.On call | |
| 181 | + | // - ctx context.Context | |
| 182 | + | // - text string | |
| 183 | + | func (_e *MockToolkitClipboard_Expecter) WriteText(ctx any, text any) *MockToolkitClipboard_WriteText_Call { | |
| 184 | + | return &MockToolkitClipboard_WriteText_Call{Call: _e.mock.On("WriteText", ctx, text)} | |
| 185 | + | } | |
| 186 | + | ||
| 187 | + | func (_c *MockToolkitClipboard_WriteText_Call) Run(run func(ctx context.Context, text string)) *MockToolkitClipboard_WriteText_Call { | |
| 188 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 189 | + | var arg0 context.Context | |
| 190 | + | if args[0] != nil { | |
| 191 | + | arg0 = args[0].(context.Context) | |
| 192 | + | } | |
| 193 | + | var arg1 string | |
| 194 | + | if args[1] != nil { | |
| 195 | + | arg1 = args[1].(string) | |
| 196 | + | } | |
| 197 | + | run( | |
| 198 | + | arg0, | |
| 199 | + | arg1, | |
| 200 | + | ) | |
| 201 | + | }) | |
| 202 | + | return _c | |
| 203 | + | } | |
| 204 | + | ||
| 205 | + | func (_c *MockToolkitClipboard_WriteText_Call) Return(err error) *MockToolkitClipboard_WriteText_Call { | |
| 206 | + | _c.Call.Return(err) | |
| 207 | + | return _c | |
| 208 | + | } | |
| 209 | + | ||
| 210 | + | func (_c *MockToolkitClipboard_WriteText_Call) RunAndReturn(run func(ctx context.Context, text string) error) *MockToolkitClipboard_WriteText_Call { | |
| 211 | + | _c.Call.Return(run) | |
| 212 | + | return _c | |
| 213 | + | } | |
M internal/infrastructure/systemviewsbridge/client_test.go +46 -29
| @@ -9,6 +9,8 @@ import ( | |||
| 9 | 9 | ||
| 10 | 10 | "github.com/bnema/dumber/internal/application/dto" | |
| 11 | 11 | "github.com/bnema/dumber/internal/application/port" | |
| 12 | + | systemviewsbridgemocks "github.com/bnema/dumber/internal/infrastructure/systemviewsbridge/mocks" | |
| 13 | + | "github.com/stretchr/testify/mock" | |
| 12 | 14 | ) | |
| 13 | 15 | ||
| 14 | 16 | func TestBuildMessageEnvelope(t *testing.T) { | |
| @@ -41,8 +43,8 @@ func TestBuildMessageEnvelope(t *testing.T) { | |||
| 41 | 43 | func TestClientSendPrefersNativeTransport(t *testing.T) { | |
| 42 | 44 | t.Parallel() | |
| 43 | 45 | ||
| 44 | - | native := &fakeTransport{available: true, response: []byte(`{"transport":"native"}`)} | |
| 45 | - | fetch := &fakeTransport{available: true, response: []byte(`{"transport":"fetch"}`)} | |
| 46 | + | native := newTransportRecorder(t, true, []byte(`{"transport":"native"}`)) | |
| 47 | + | fetch := newTransportRecorder(t, true, []byte(`{"transport":"fetch"}`)) | |
| 46 | 48 | ||
| 47 | 49 | client := NewClient(native, fetch) | |
| 48 | 50 | got, err := client.Send(context.Background(), "favorite_list", struct { | |
| @@ -75,8 +77,8 @@ func TestClientSendPrefersNativeTransport(t *testing.T) { | |||
| 75 | 77 | func TestClientSendFallsBackToFetchTransport(t *testing.T) { | |
| 76 | 78 | t.Parallel() | |
| 77 | 79 | ||
| 78 | - | native := &fakeTransport{available: false, response: []byte(`{"transport":"native"}`)} | |
| 79 | - | fetch := &fakeTransport{available: true, response: []byte(`{"transport":"fetch"}`)} | |
| 80 | + | native := newTransportRecorder(t, false, []byte(`{"transport":"native"}`)) | |
| 81 | + | fetch := newTransportRecorder(t, true, []byte(`{"transport":"fetch"}`)) | |
| 80 | 82 | ||
| 81 | 83 | client := NewClient(native, fetch) | |
| 82 | 84 | got, err := client.Send(context.Background(), "favorite_list", struct { | |
| @@ -101,7 +103,7 @@ func TestClientSendFallsBackToFetchTransport(t *testing.T) { | |||
| 101 | 103 | func TestClientTimelineDecodesEntries(t *testing.T) { | |
| 102 | 104 | t.Parallel() | |
| 103 | 105 | ||
| 104 | - | native := &fakeTransport{available: true, response: []byte(`{"requestId":"req-9","success":true,"data":[{"id":1,"url":"https://example.com","title":"Example"}]}`)} | |
| 106 | + | native := newTransportRecorder(t, true, []byte(`{"requestId":"req-9","success":true,"data":[{"id":1,"url":"https://example.com","title":"Example"}]}`)) | |
| 105 | 107 | client := NewClient(native, nil) | |
| 106 | 108 | ||
| 107 | 109 | entries, err := client.Timeline(context.Background(), 25, 0) | |
| @@ -130,7 +132,7 @@ func TestClientTimelineDecodesEntries(t *testing.T) { | |||
| 130 | 132 | func TestClientTimelineWindowOmitsZeroCursor(t *testing.T) { | |
| 131 | 133 | t.Parallel() | |
| 132 | 134 | ||
| 133 | - | native := &fakeTransport{available: true, response: []byte(`{"requestId":"req-window","success":true}`)} | |
| 135 | + | native := newTransportRecorder(t, true, []byte(`{"requestId":"req-window","success":true}`)) | |
| 134 | 136 | client := NewClient(native, nil) | |
| 135 | 137 | ||
| 136 | 138 | _, err := client.TimelineWindow(context.Background(), time.Time{}, "") | |
| @@ -157,7 +159,7 @@ func TestClientTimelineWindowOmitsZeroCursor(t *testing.T) { | |||
| 157 | 159 | func TestClientDeleteRangeRejectsEmptyRange(t *testing.T) { | |
| 158 | 160 | t.Parallel() | |
| 159 | 161 | ||
| 160 | - | transport := &fakeTransport{available: true, response: []byte(`{"requestId":"req-range-empty","success":true}`)} | |
| 162 | + | transport := newTransportRecorder(t, true, []byte(`{"requestId":"req-range-empty","success":true}`)) | |
| 161 | 163 | client := NewClient(transport, nil) | |
| 162 | 164 | ||
| 163 | 165 | err := client.DeleteRange(context.Background(), " ") | |
| @@ -172,7 +174,7 @@ func TestClientDeleteRangeRejectsEmptyRange(t *testing.T) { | |||
| 172 | 174 | func TestClientDeleteRangeSendsRange(t *testing.T) { | |
| 173 | 175 | t.Parallel() | |
| 174 | 176 | ||
| 175 | - | native := &fakeTransport{available: true, response: []byte(`{"requestId":"req-10","success":true}`)} | |
| 177 | + | native := newTransportRecorder(t, true, []byte(`{"requestId":"req-10","success":true}`)) | |
| 176 | 178 | client := NewClient(native, nil) | |
| 177 | 179 | ||
| 178 | 180 | if err := client.DeleteRange(context.Background(), "week"); err != nil { | |
| @@ -191,7 +193,7 @@ func TestClientDeleteRangeSendsRange(t *testing.T) { | |||
| 191 | 193 | func TestClientListDecodesFavorites(t *testing.T) { | |
| 192 | 194 | t.Parallel() | |
| 193 | 195 | ||
| 194 | - | native := &fakeTransport{available: true, response: []byte(`{"requestId":"req-11","success":true,"data":[{"id":1,"url":"https://example.com","title":"Example"}]}`)} | |
| 196 | + | native := newTransportRecorder(t, true, []byte(`{"requestId":"req-11","success":true,"data":[{"id":1,"url":"https://example.com","title":"Example"}]}`)) | |
| 195 | 197 | client := NewClient(native, nil) | |
| 196 | 198 | ||
| 197 | 199 | favorites, err := client.List(context.Background()) | |
| @@ -217,7 +219,7 @@ func TestClientListDecodesFavorites(t *testing.T) { | |||
| 217 | 219 | func TestClientListFoldersDecodesFolders(t *testing.T) { | |
| 218 | 220 | t.Parallel() | |
| 219 | 221 | ||
| 220 | - | native := &fakeTransport{available: true, response: []byte(`{"requestId":"req-12","success":true,"data":[{"id":9,"name":"Read Later"}]}`)} | |
| 222 | + | native := newTransportRecorder(t, true, []byte(`{"requestId":"req-12","success":true,"data":[{"id":9,"name":"Read Later"}]}`)) | |
| 221 | 223 | client := NewClient(native, nil) | |
| 222 | 224 | ||
| 223 | 225 | folders, err := client.ListFolders(context.Background()) | |
| @@ -243,7 +245,7 @@ func TestClientListFoldersDecodesFolders(t *testing.T) { | |||
| 243 | 245 | func TestClientListTagsDecodesTags(t *testing.T) { | |
| 244 | 246 | t.Parallel() | |
| 245 | 247 | ||
| 246 | - | native := &fakeTransport{available: true, response: []byte(`{"requestId":"req-13","success":true,"data":[{"id":5,"name":"Go","color":"#00ff00"}]}`)} | |
| 248 | + | native := newTransportRecorder(t, true, []byte(`{"requestId":"req-13","success":true,"data":[{"id":5,"name":"Go","color":"#00ff00"}]}`)) | |
| 247 | 249 | client := NewClient(native, nil) | |
| 248 | 250 | ||
| 249 | 251 | tags, err := client.ListTags(context.Background()) | |
| @@ -271,7 +273,8 @@ func TestClientCurrentAndDefaultDecodeConfigPayload(t *testing.T) { | |||
| 271 | 273 | ||
| 272 | 274 | resp := []byte(`{"requestId":"req-14","success":true,"data":{"engine_type":"webkit","default_search_engine":"DuckDuckGo"}}`) | |
| 273 | 275 | ||
| 274 | - | currentClient := NewClient(&fakeTransport{available: true, response: resp}, nil) | |
| 276 | + | currentTransport := newTransportRecorder(t, true, resp) | |
| 277 | + | currentClient := NewClient(currentTransport, nil) | |
| 275 | 278 | current, err := currentClient.Current(context.Background()) | |
| 276 | 279 | if err != nil { | |
| 277 | 280 | t.Fatalf("Current() error = %v", err) | |
| @@ -281,14 +284,15 @@ func TestClientCurrentAndDefaultDecodeConfigPayload(t *testing.T) { | |||
| 281 | 284 | } | |
| 282 | 285 | ||
| 283 | 286 | var msg port.WebUIMessage | |
| 284 | - | if unmarshalErr := json.Unmarshal(currentClient.native.(*fakeTransport).last, &msg); unmarshalErr != nil { | |
| 287 | + | if unmarshalErr := json.Unmarshal(currentTransport.last, &msg); unmarshalErr != nil { | |
| 285 | 288 | t.Fatalf("unmarshal sent envelope: %v", unmarshalErr) | |
| 286 | 289 | } | |
| 287 | 290 | if msg.Type != "/api/config" { | |
| 288 | 291 | t.Fatalf("sent type = %q, want %q", msg.Type, "/api/config") | |
| 289 | 292 | } | |
| 290 | 293 | ||
| 291 | - | defaultClient := NewClient(&fakeTransport{available: true, response: resp}, nil) | |
| 294 | + | defaultTransport := newTransportRecorder(t, true, resp) | |
| 295 | + | defaultClient := NewClient(defaultTransport, nil) | |
| 292 | 296 | defaultCfg, err := defaultClient.Default(context.Background()) | |
| 293 | 297 | if err != nil { | |
| 294 | 298 | t.Fatalf("Default() error = %v", err) | |
| @@ -297,7 +301,7 @@ func TestClientCurrentAndDefaultDecodeConfigPayload(t *testing.T) { | |||
| 297 | 301 | t.Fatalf("Default() = %+v", defaultCfg) | |
| 298 | 302 | } | |
| 299 | 303 | ||
| 300 | - | if err := json.Unmarshal(defaultClient.native.(*fakeTransport).last, &msg); err != nil { | |
| 304 | + | if err := json.Unmarshal(defaultTransport.last, &msg); err != nil { | |
| 301 | 305 | t.Fatalf("unmarshal sent envelope: %v", err) | |
| 302 | 306 | } | |
| 303 | 307 | if msg.Type != "/api/config/default" { | |
| @@ -372,7 +376,7 @@ func TestClientConfigActionsUseExpectedMessageTypes(t *testing.T) { | |||
| 372 | 376 | t.Run(tt.name, func(t *testing.T) { | |
| 373 | 377 | t.Parallel() | |
| 374 | 378 | ||
| 375 | - | transport := &fakeTransport{available: true, response: []byte(`{"requestId":"req-15","success":true,"data":{"status":"success","conflicts":[]}}`)} | |
| 379 | + | transport := newTransportRecorder(t, true, []byte(`{"requestId":"req-15","success":true,"data":{"status":"success","conflicts":[]}}`)) | |
| 376 | 380 | if tt.name == "save config" || tt.name == "reset keybinding" || tt.name == "reset all keybindings" { | |
| 377 | 381 | transport.response = []byte(`{"requestId":"req-15","success":true}`) | |
| 378 | 382 | } | |
| @@ -418,7 +422,7 @@ func TestClientCreateFolderIncludesParentID(t *testing.T) { | |||
| 418 | 422 | t.Parallel() | |
| 419 | 423 | ||
| 420 | 424 | parentID := int64(42) | |
| 421 | - | transport := &fakeTransport{available: true, response: []byte(`{"requestId":"req-16","success":true,"data":{"id":9,"name":"Nested","icon":"📁","parent_id":42}}`)} | |
| 425 | + | transport := newTransportRecorder(t, true, []byte(`{"requestId":"req-16","success":true,"data":{"id":9,"name":"Nested","icon":"📁","parent_id":42}}`)) | |
| 422 | 426 | client := NewClient(transport, nil) | |
| 423 | 427 | ||
| 424 | 428 | folder, err := client.CreateFolder(context.Background(), "Nested", " 📁 ", &parentID) | |
| @@ -472,7 +476,7 @@ func TestClientCreateFolderIncludesParentID(t *testing.T) { | |||
| 472 | 476 | func TestClientDomainStatsRejectsNegativeLimit(t *testing.T) { | |
| 473 | 477 | t.Parallel() | |
| 474 | 478 | ||
| 475 | - | transport := &fakeTransport{available: true, response: []byte(`{"requestId":"req-17","success":true,"data":[]}`)} | |
| 479 | + | transport := newTransportRecorder(t, true, []byte(`{"requestId":"req-17","success":true,"data":[]}`)) | |
| 476 | 480 | client := NewClient(transport, nil) | |
| 477 | 481 | ||
| 478 | 482 | _, err := client.DomainStats(context.Background(), -1) | |
| @@ -487,7 +491,7 @@ func TestClientDomainStatsRejectsNegativeLimit(t *testing.T) { | |||
| 487 | 491 | func TestClientDeleteDomainRejectsEmptyDomain(t *testing.T) { | |
| 488 | 492 | t.Parallel() | |
| 489 | 493 | ||
| 490 | - | transport := &fakeTransport{available: true, response: []byte(`{"requestId":"req-domain-empty","success":true}`)} | |
| 494 | + | transport := newTransportRecorder(t, true, []byte(`{"requestId":"req-domain-empty","success":true}`)) | |
| 491 | 495 | client := NewClient(transport, nil) | |
| 492 | 496 | ||
| 493 | 497 | err := client.DeleteDomain(context.Background(), " ") | |
| @@ -502,7 +506,7 @@ func TestClientDeleteDomainRejectsEmptyDomain(t *testing.T) { | |||
| 502 | 506 | func TestClientUpdateFolderTrimsIcon(t *testing.T) { | |
| 503 | 507 | t.Parallel() | |
| 504 | 508 | ||
| 505 | - | transport := &fakeTransport{available: true, response: []byte(`{"requestId":"req-18","success":true}`)} | |
| 509 | + | transport := newTransportRecorder(t, true, []byte(`{"requestId":"req-18","success":true}`)) | |
| 506 | 510 | client := NewClient(transport, nil) | |
| 507 | 511 | ||
| 508 | 512 | if err := client.UpdateFolder(context.Background(), 9, "Read", " 📚 "); err != nil { | |
| @@ -530,7 +534,7 @@ func TestClientUpdateFolderTrimsIcon(t *testing.T) { | |||
| 530 | 534 | func TestClientUpdateFolderOmitsBlankIcon(t *testing.T) { | |
| 531 | 535 | t.Parallel() | |
| 532 | 536 | ||
| 533 | - | transport := &fakeTransport{available: true, response: []byte(`{"requestId":"req-19","success":true}`)} | |
| 537 | + | transport := newTransportRecorder(t, true, []byte(`{"requestId":"req-19","success":true}`)) | |
| 534 | 538 | client := NewClient(transport, nil) | |
| 535 | 539 | ||
| 536 | 540 | if err := client.UpdateFolder(context.Background(), 9, "Read", " "); err != nil { | |
| @@ -555,7 +559,7 @@ func TestClientUpdateFolderOmitsBlankIcon(t *testing.T) { | |||
| 555 | 559 | func TestClientDeleteEntryRejectsInvalidID(t *testing.T) { | |
| 556 | 560 | t.Parallel() | |
| 557 | 561 | ||
| 558 | - | transport := &fakeTransport{available: true, response: []byte(`{"requestId":"req-20","success":true}`)} | |
| 562 | + | transport := newTransportRecorder(t, true, []byte(`{"requestId":"req-20","success":true}`)) | |
| 559 | 563 | client := NewClient(transport, nil) | |
| 560 | 564 | ||
| 561 | 565 | err := client.DeleteEntry(context.Background(), 0) | |
| @@ -570,7 +574,7 @@ func TestClientDeleteEntryRejectsInvalidID(t *testing.T) { | |||
| 570 | 574 | func TestClientTagActionsTrimFields(t *testing.T) { | |
| 571 | 575 | t.Parallel() | |
| 572 | 576 | ||
| 573 | - | createTransport := &fakeTransport{available: true, response: []byte(`{"requestId":"req-21","success":true,"data":{"id":5,"name":"Go","color":"#00add8"}}`)} | |
| 577 | + | createTransport := newTransportRecorder(t, true, []byte(`{"requestId":"req-21","success":true,"data":{"id":5,"name":"Go","color":"#00add8"}}`)) | |
| 574 | 578 | client := NewClient(createTransport, nil) | |
| 575 | 579 | if _, err := client.CreateTag(context.Background(), " Go ", " #00add8 "); err != nil { | |
| 576 | 580 | t.Fatalf("CreateTag() error = %v", err) | |
| @@ -590,7 +594,7 @@ func TestClientTagActionsTrimFields(t *testing.T) { | |||
| 590 | 594 | t.Fatalf("CreateTag payload = %#v", createPayload) | |
| 591 | 595 | } | |
| 592 | 596 | ||
| 593 | - | updateTransport := &fakeTransport{available: true, response: []byte(`{"requestId":"req-22","success":true}`)} | |
| 597 | + | updateTransport := newTransportRecorder(t, true, []byte(`{"requestId":"req-22","success":true}`)) | |
| 594 | 598 | client = NewClient(updateTransport, nil) | |
| 595 | 599 | if err := client.UpdateTag(context.Background(), 5, " Go ", " "); err != nil { | |
| 596 | 600 | t.Fatalf("UpdateTag() error = %v", err) | |
| @@ -610,16 +614,29 @@ func TestClientTagActionsTrimFields(t *testing.T) { | |||
| 610 | 614 | } | |
| 611 | 615 | } | |
| 612 | 616 | ||
| 613 | - | type fakeTransport struct { | |
| 617 | + | type transportRecorder struct { | |
| 618 | + | *systemviewsbridgemocks.MockTransport | |
| 619 | + | ||
| 614 | 620 | available bool | |
| 615 | 621 | called bool | |
| 616 | 622 | last []byte | |
| 617 | 623 | response []byte | |
| 618 | 624 | } | |
| 619 | 625 | ||
| 620 | - | func (t *fakeTransport) Available() bool { return t.available } | |
| 626 | + | func newTransportRecorder(t *testing.T, available bool, response []byte) *transportRecorder { | |
| 627 | + | t.Helper() | |
| 628 | + | ||
| 629 | + | recorder := &transportRecorder{ | |
| 630 | + | MockTransport: systemviewsbridgemocks.NewMockTransport(t), | |
| 631 | + | available: available, | |
| 632 | + | response: response, | |
| 633 | + | } | |
| 634 | + | recorder.EXPECT().Available().RunAndReturn(func() bool { return recorder.available }).Maybe() | |
| 635 | + | recorder.EXPECT().Send(mock.Anything, mock.Anything).RunAndReturn(recorder.send).Maybe() | |
| 636 | + | return recorder | |
| 637 | + | } | |
| 621 | 638 | ||
| 622 | - | func (t *fakeTransport) Send(_ context.Context, body []byte) ([]byte, error) { | |
| 639 | + | func (t *transportRecorder) send(_ context.Context, body []byte) ([]byte, error) { | |
| 623 | 640 | t.called = true | |
| 624 | 641 | t.last = append(t.last[:0], body...) | |
| 625 | 642 | return t.response, nil | |
| @@ -641,7 +658,7 @@ func (t *blockingTransport) Send(ctx context.Context, _ []byte) ([]byte, error) | |||
| 641 | 658 | func TestClientRejectsInvalidFavoriteMutationIDs(t *testing.T) { | |
| 642 | 659 | t.Parallel() | |
| 643 | 660 | ||
| 644 | - | transport := &fakeTransport{available: true, response: []byte(`{"requestId":"req","success":true}`)} | |
| 661 | + | transport := newTransportRecorder(t, true, []byte(`{"requestId":"req","success":true}`)) | |
| 645 | 662 | client := NewClient(transport, nil) | |
| 646 | 663 | ||
| 647 | 664 | checks := []struct { | |
| @@ -679,7 +696,7 @@ func TestClientRejectsInvalidFavoriteMutationIDs(t *testing.T) { | |||
| 679 | 696 | func TestClientRejectsNegativeHistoryLimits(t *testing.T) { | |
| 680 | 697 | t.Parallel() | |
| 681 | 698 | ||
| 682 | - | client := NewClient(&fakeTransport{available: true}, nil) | |
| 699 | + | client := NewClient(newTransportRecorder(t, true, nil), nil) | |
| 683 | 700 | ||
| 684 | 701 | if _, err := client.Timeline(context.Background(), -1, 0); err == nil { | |
| 685 | 702 | t.Fatal("Timeline() error = nil, want error") | |
A internal/infrastructure/systemviewsbridge/mocks/client.go +150 -0
| @@ -0,0 +1,150 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | mock "github.com/stretchr/testify/mock" | |
| 11 | + | ) | |
| 12 | + | ||
| 13 | + | // NewMockTransport creates a new instance of MockTransport. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 14 | + | // The first argument is typically a *testing.T value. | |
| 15 | + | func NewMockTransport(t interface { | |
| 16 | + | mock.TestingT | |
| 17 | + | Cleanup(func()) | |
| 18 | + | }) *MockTransport { | |
| 19 | + | mock := &MockTransport{} | |
| 20 | + | mock.Mock.Test(t) | |
| 21 | + | ||
| 22 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 23 | + | ||
| 24 | + | return mock | |
| 25 | + | } | |
| 26 | + | ||
| 27 | + | // MockTransport is an autogenerated mock type for the Transport type | |
| 28 | + | type MockTransport struct { | |
| 29 | + | mock.Mock | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | type MockTransport_Expecter struct { | |
| 33 | + | mock *mock.Mock | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | func (_m *MockTransport) EXPECT() *MockTransport_Expecter { | |
| 37 | + | return &MockTransport_Expecter{mock: &_m.Mock} | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | // Available provides a mock function for the type MockTransport | |
| 41 | + | func (_mock *MockTransport) Available() bool { | |
| 42 | + | ret := _mock.Called() | |
| 43 | + | ||
| 44 | + | if len(ret) == 0 { | |
| 45 | + | panic("no return value specified for Available") | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | var r0 bool | |
| 49 | + | if returnFunc, ok := ret.Get(0).(func() bool); ok { | |
| 50 | + | r0 = returnFunc() | |
| 51 | + | } else { | |
| 52 | + | r0 = ret.Get(0).(bool) | |
| 53 | + | } | |
| 54 | + | return r0 | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | // MockTransport_Available_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Available' | |
| 58 | + | type MockTransport_Available_Call struct { | |
| 59 | + | *mock.Call | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | // Available is a helper method to define mock.On call | |
| 63 | + | func (_e *MockTransport_Expecter) Available() *MockTransport_Available_Call { | |
| 64 | + | return &MockTransport_Available_Call{Call: _e.mock.On("Available")} | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | func (_c *MockTransport_Available_Call) Run(run func()) *MockTransport_Available_Call { | |
| 68 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 69 | + | run() | |
| 70 | + | }) | |
| 71 | + | return _c | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | func (_c *MockTransport_Available_Call) Return(b bool) *MockTransport_Available_Call { | |
| 75 | + | _c.Call.Return(b) | |
| 76 | + | return _c | |
| 77 | + | } | |
| 78 | + | ||
| 79 | + | func (_c *MockTransport_Available_Call) RunAndReturn(run func() bool) *MockTransport_Available_Call { | |
| 80 | + | _c.Call.Return(run) | |
| 81 | + | return _c | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | // Send provides a mock function for the type MockTransport | |
| 85 | + | func (_mock *MockTransport) Send(ctx context.Context, body []byte) ([]byte, error) { | |
| 86 | + | ret := _mock.Called(ctx, body) | |
| 87 | + | ||
| 88 | + | if len(ret) == 0 { | |
| 89 | + | panic("no return value specified for Send") | |
| 90 | + | } | |
| 91 | + | ||
| 92 | + | var r0 []byte | |
| 93 | + | var r1 error | |
| 94 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, []byte) ([]byte, error)); ok { | |
| 95 | + | return returnFunc(ctx, body) | |
| 96 | + | } | |
| 97 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, []byte) []byte); ok { | |
| 98 | + | r0 = returnFunc(ctx, body) | |
| 99 | + | } else { | |
| 100 | + | if ret.Get(0) != nil { | |
| 101 | + | r0 = ret.Get(0).([]byte) | |
| 102 | + | } | |
| 103 | + | } | |
| 104 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, []byte) error); ok { | |
| 105 | + | r1 = returnFunc(ctx, body) | |
| 106 | + | } else { | |
| 107 | + | r1 = ret.Error(1) | |
| 108 | + | } | |
| 109 | + | return r0, r1 | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | // MockTransport_Send_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Send' | |
| 113 | + | type MockTransport_Send_Call struct { | |
| 114 | + | *mock.Call | |
| 115 | + | } | |
| 116 | + | ||
| 117 | + | // Send is a helper method to define mock.On call | |
| 118 | + | // - ctx context.Context | |
| 119 | + | // - body []byte | |
| 120 | + | func (_e *MockTransport_Expecter) Send(ctx any, body any) *MockTransport_Send_Call { | |
| 121 | + | return &MockTransport_Send_Call{Call: _e.mock.On("Send", ctx, body)} | |
| 122 | + | } | |
| 123 | + | ||
| 124 | + | func (_c *MockTransport_Send_Call) Run(run func(ctx context.Context, body []byte)) *MockTransport_Send_Call { | |
| 125 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 126 | + | var arg0 context.Context | |
| 127 | + | if args[0] != nil { | |
| 128 | + | arg0 = args[0].(context.Context) | |
| 129 | + | } | |
| 130 | + | var arg1 []byte | |
| 131 | + | if args[1] != nil { | |
| 132 | + | arg1 = args[1].([]byte) | |
| 133 | + | } | |
| 134 | + | run( | |
| 135 | + | arg0, | |
| 136 | + | arg1, | |
| 137 | + | ) | |
| 138 | + | }) | |
| 139 | + | return _c | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | func (_c *MockTransport_Send_Call) Return(bytes []byte, err error) *MockTransport_Send_Call { | |
| 143 | + | _c.Call.Return(bytes, err) | |
| 144 | + | return _c | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | func (_c *MockTransport_Send_Call) RunAndReturn(run func(ctx context.Context, body []byte) ([]byte, error)) *MockTransport_Send_Call { | |
| 148 | + | _c.Call.Return(run) | |
| 149 | + | return _c | |
| 150 | + | } | |
M internal/infrastructure/updater/applier_test.go +1 -1
| @@ -122,7 +122,7 @@ func TestApplier_StageUpdate(t *testing.T) { | |||
| 122 | 122 | applier := NewApplier(tmpDir) | |
| 123 | 123 | ctx := context.Background() | |
| 124 | 124 | ||
| 125 | - | // Create a fake binary to stage | |
| 125 | + | // Create a dummy binary to stage | |
| 126 | 126 | srcBinary := filepath.Join(tmpDir, "new-binary") | |
| 127 | 127 | binaryContent := []byte("#!/bin/sh\necho 'new version'\n") | |
| 128 | 128 | require.NoError(t, os.WriteFile(srcBinary, binaryContent, 0o755)) | |
M internal/ui/app_browser_launch_test.go +71 -71
| @@ -2441,9 +2441,9 @@ func TestApp_ActiveWorkspaceUsesLastFocusedWindowTabsNotGlobalActiveTab(t *testi | |||
| 2441 | 2441 | } | |
| 2442 | 2442 | } | |
| 2443 | 2443 | ||
| 2444 | - | // fakeRecordingWebView is a complete port.WebView that records which navigation | |
| 2444 | + | // recordingWebView is a complete port.WebView that records which navigation | |
| 2445 | 2445 | // methods were called and with what arguments. It does not use reflect or unsafe. | |
| 2446 | - | type fakeRecordingWebView struct { | |
| 2446 | + | type recordingWebView struct { | |
| 2447 | 2447 | id port.WebViewID | |
| 2448 | 2448 | ||
| 2449 | 2449 | loadURICalled bool | |
| @@ -2461,79 +2461,79 @@ type fakeRecordingWebView struct { | |||
| 2461 | 2461 | destroyCalls int | |
| 2462 | 2462 | } | |
| 2463 | 2463 | ||
| 2464 | - | func (f *fakeRecordingWebView) ID() port.WebViewID { return f.id } | |
| 2464 | + | func (f *recordingWebView) ID() port.WebViewID { return f.id } | |
| 2465 | 2465 | ||
| 2466 | - | func (f *fakeRecordingWebView) LoadURI(_ context.Context, uri string) error { | |
| 2466 | + | func (f *recordingWebView) LoadURI(_ context.Context, uri string) error { | |
| 2467 | 2467 | f.loadURICalled = true | |
| 2468 | 2468 | f.loadURILastURI = uri | |
| 2469 | 2469 | return nil | |
| 2470 | 2470 | } | |
| 2471 | 2471 | ||
| 2472 | - | func (f *fakeRecordingWebView) LoadHTML(_ context.Context, _, _ string) error { | |
| 2472 | + | func (f *recordingWebView) LoadHTML(_ context.Context, _, _ string) error { | |
| 2473 | 2473 | f.loadHTMLCalls++ | |
| 2474 | 2474 | return nil | |
| 2475 | 2475 | } | |
| 2476 | 2476 | ||
| 2477 | - | func (f *fakeRecordingWebView) Reload(_ context.Context) error { | |
| 2477 | + | func (f *recordingWebView) Reload(_ context.Context) error { | |
| 2478 | 2478 | f.reloadCalls++ | |
| 2479 | 2479 | return nil | |
| 2480 | 2480 | } | |
| 2481 | 2481 | ||
| 2482 | - | func (f *fakeRecordingWebView) ReloadBypassCache(_ context.Context) error { | |
| 2482 | + | func (f *recordingWebView) ReloadBypassCache(_ context.Context) error { | |
| 2483 | 2483 | f.reloadBypassCacheCalls++ | |
| 2484 | 2484 | return nil | |
| 2485 | 2485 | } | |
| 2486 | 2486 | ||
| 2487 | - | func (f *fakeRecordingWebView) Stop(_ context.Context) error { | |
| 2487 | + | func (f *recordingWebView) Stop(_ context.Context) error { | |
| 2488 | 2488 | f.stopCalls++ | |
| 2489 | 2489 | return nil | |
| 2490 | 2490 | } | |
| 2491 | 2491 | ||
| 2492 | - | func (f *fakeRecordingWebView) GoBack(_ context.Context) error { | |
| 2492 | + | func (f *recordingWebView) GoBack(_ context.Context) error { | |
| 2493 | 2493 | f.goBackCalls++ | |
| 2494 | 2494 | return nil | |
| 2495 | 2495 | } | |
| 2496 | 2496 | ||
| 2497 | - | func (f *fakeRecordingWebView) GoForward(_ context.Context) error { | |
| 2497 | + | func (f *recordingWebView) GoForward(_ context.Context) error { | |
| 2498 | 2498 | f.goForwardCalls++ | |
| 2499 | 2499 | return nil | |
| 2500 | 2500 | } | |
| 2501 | 2501 | ||
| 2502 | - | func (f *fakeRecordingWebView) State() port.WebViewState { return port.WebViewState{} } | |
| 2502 | + | func (f *recordingWebView) State() port.WebViewState { return port.WebViewState{} } | |
| 2503 | 2503 | ||
| 2504 | - | func (f *fakeRecordingWebView) URI() string { return f.loadURILastURI } | |
| 2505 | - | func (f *fakeRecordingWebView) Title() string { return "" } | |
| 2504 | + | func (f *recordingWebView) URI() string { return f.loadURILastURI } | |
| 2505 | + | func (f *recordingWebView) Title() string { return "" } | |
| 2506 | 2506 | ||
| 2507 | - | func (f *fakeRecordingWebView) IsLoading() bool { return false } | |
| 2508 | - | func (f *fakeRecordingWebView) EstimatedProgress() float64 { return 0 } | |
| 2509 | - | func (f *fakeRecordingWebView) CanGoBack() bool { return false } | |
| 2510 | - | func (f *fakeRecordingWebView) CanGoForward() bool { return false } | |
| 2507 | + | func (f *recordingWebView) IsLoading() bool { return false } | |
| 2508 | + | func (f *recordingWebView) EstimatedProgress() float64 { return 0 } | |
| 2509 | + | func (f *recordingWebView) CanGoBack() bool { return false } | |
| 2510 | + | func (f *recordingWebView) CanGoForward() bool { return false } | |
| 2511 | 2511 | ||
| 2512 | - | func (f *fakeRecordingWebView) SetZoomLevel(_ context.Context, level float64) error { | |
| 2512 | + | func (f *recordingWebView) SetZoomLevel(_ context.Context, level float64) error { | |
| 2513 | 2513 | f.setZoomLevelCalls++ | |
| 2514 | 2514 | f.setZoomLevelLastLevel = level | |
| 2515 | 2515 | return nil | |
| 2516 | 2516 | } | |
| 2517 | 2517 | ||
| 2518 | - | func (f *fakeRecordingWebView) OpenDevTools() { f.openDevToolsCalls++ } | |
| 2519 | - | func (f *fakeRecordingWebView) PrintPage() { f.printPageCalls++ } | |
| 2518 | + | func (f *recordingWebView) OpenDevTools() { f.openDevToolsCalls++ } | |
| 2519 | + | func (f *recordingWebView) PrintPage() { f.printPageCalls++ } | |
| 2520 | 2520 | ||
| 2521 | - | func (f *fakeRecordingWebView) GetZoomLevel() float64 { return 1.0 } | |
| 2522 | - | func (f *fakeRecordingWebView) GetFindController() port.FindController { return nil } | |
| 2523 | - | func (f *fakeRecordingWebView) SetCallbacks(_ *port.WebViewCallbacks) {} | |
| 2524 | - | func (f *fakeRecordingWebView) RunJavaScript(_ context.Context, _ string) {} | |
| 2525 | - | func (f *fakeRecordingWebView) SetBackgroundColor(_, _, _, _ float64) {} | |
| 2526 | - | func (f *fakeRecordingWebView) ResetBackgroundToDefault() {} | |
| 2527 | - | func (f *fakeRecordingWebView) Favicon() port.Texture { return nil } | |
| 2528 | - | func (f *fakeRecordingWebView) Generation() uint64 { return 0 } | |
| 2529 | - | func (f *fakeRecordingWebView) IsFullscreen() bool { return false } | |
| 2530 | - | func (f *fakeRecordingWebView) IsPlayingAudio() bool { return false } | |
| 2531 | - | func (f *fakeRecordingWebView) IsDestroyed() bool { return f.destroyCalls > 0 } | |
| 2532 | - | func (f *fakeRecordingWebView) Destroy() { f.destroyCalls++ } | |
| 2521 | + | func (f *recordingWebView) GetZoomLevel() float64 { return 1.0 } | |
| 2522 | + | func (f *recordingWebView) GetFindController() port.FindController { return nil } | |
| 2523 | + | func (f *recordingWebView) SetCallbacks(_ *port.WebViewCallbacks) {} | |
| 2524 | + | func (f *recordingWebView) RunJavaScript(_ context.Context, _ string) {} | |
| 2525 | + | func (f *recordingWebView) SetBackgroundColor(_, _, _, _ float64) {} | |
| 2526 | + | func (f *recordingWebView) ResetBackgroundToDefault() {} | |
| 2527 | + | func (f *recordingWebView) Favicon() port.Texture { return nil } | |
| 2528 | + | func (f *recordingWebView) Generation() uint64 { return 0 } | |
| 2529 | + | func (f *recordingWebView) IsFullscreen() bool { return false } | |
| 2530 | + | func (f *recordingWebView) IsPlayingAudio() bool { return false } | |
| 2531 | + | func (f *recordingWebView) IsDestroyed() bool { return f.destroyCalls > 0 } | |
| 2532 | + | func (f *recordingWebView) Destroy() { f.destroyCalls++ } | |
| 2533 | 2533 | ||
| 2534 | 2534 | func TestApp_AttachPopupToTabDestroysPopupWhenWorkspaceViewMissing(t *testing.T) { | |
| 2535 | 2535 | ctx := context.Background() | |
| 2536 | - | popupWV := &fakeRecordingWebView{id: 1} | |
| 2536 | + | popupWV := &recordingWebView{id: 1} | |
| 2537 | 2537 | app := &App{workspaceViews: map[entity.TabID]*component.WorkspaceView{}} | |
| 2538 | 2538 | ||
| 2539 | 2539 | app.attachPopupToTab(ctx, entity.TabID("missing-tab"), entity.NewPane(entity.PaneID("popup-pane")), popupWV) | |
| @@ -2545,7 +2545,7 @@ func TestApp_AttachPopupToTabDestroysPopupWhenWorkspaceViewMissing(t *testing.T) | |||
| 2545 | 2545 | ||
| 2546 | 2546 | func TestApp_AttachPopupToTabDestroysPopupWhenPaneNil(t *testing.T) { | |
| 2547 | 2547 | ctx := context.Background() | |
| 2548 | - | popupWV := &fakeRecordingWebView{id: 1} | |
| 2548 | + | popupWV := &recordingWebView{id: 1} | |
| 2549 | 2549 | tabID := entity.TabID("tab-1") | |
| 2550 | 2550 | app := &App{workspaceViews: map[entity.TabID]*component.WorkspaceView{tabID: &component.WorkspaceView{}}} | |
| 2551 | 2551 | ||
| @@ -2568,7 +2568,7 @@ func TestApp_AttachPopupToTabSkipsRegistrationWhenPaneViewMissing(t *testing.T) | |||
| 2568 | 2568 | }, | |
| 2569 | 2569 | } | |
| 2570 | 2570 | ||
| 2571 | - | popupWV := &fakeRecordingWebView{id: 1} | |
| 2571 | + | popupWV := &recordingWebView{id: 1} | |
| 2572 | 2572 | app.attachPopupToTab(ctx, tabID, pane, popupWV) | |
| 2573 | 2573 | ||
| 2574 | 2574 | if got := contentCoord.GetWebView(pane.ID); got != nil { | |
| @@ -2608,7 +2608,7 @@ func TestApp_AttachPopupToTabReleasesRegistrationWhenWrapFails(t *testing.T) { | |||
| 2608 | 2608 | tabID: wsView, | |
| 2609 | 2609 | }, | |
| 2610 | 2610 | } | |
| 2611 | - | popupWV := &fakeRecordingWebView{id: 1} | |
| 2611 | + | popupWV := &recordingWebView{id: 1} | |
| 2612 | 2612 | ||
| 2613 | 2613 | app.attachPopupToTab(ctx, tabID, pane, popupWV) | |
| 2614 | 2614 | ||
| @@ -2637,15 +2637,15 @@ func TestApp_BrowserWindowWebViewActionsIgnoreStaleFocusedWindow(t *testing.T) { | |||
| 2637 | 2637 | secondTabs.SetActive(tab2.ID) | |
| 2638 | 2638 | second := &browserWindow{id: "window-2", tabs: secondTabs} | |
| 2639 | 2639 | ||
| 2640 | - | // Create fake recording webviews. | |
| 2641 | - | fakeWv1 := &fakeRecordingWebView{id: 1} | |
| 2642 | - | fakeWv2 := &fakeRecordingWebView{id: 2} | |
| 2640 | + | // Create recording webviews. | |
| 2641 | + | recordingWv1 := &recordingWebView{id: 1} | |
| 2642 | + | recordingWv2 := &recordingWebView{id: 2} | |
| 2643 | 2643 | ||
| 2644 | 2644 | // Create content coordinator with initialized internal maps to avoid | |
| 2645 | 2645 | // nil-map panics in SetNavigationOrigin during NavigateWebView calls. | |
| 2646 | 2646 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | |
| 2647 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv1) | |
| 2648 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), fakeWv2) | |
| 2647 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv1) | |
| 2648 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), recordingWv2) | |
| 2649 | 2649 | ||
| 2650 | 2650 | // Create NavigationCoordinator (no navigateUC needed for direct webview calls). | |
| 2651 | 2651 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | |
| @@ -2684,11 +2684,11 @@ func TestApp_BrowserWindowWebViewActionsIgnoreStaleFocusedWindow(t *testing.T) { | |||
| 2684 | 2684 | t.Fatalf("goForwardBrowserWindow returned error: %v", err) | |
| 2685 | 2685 | } | |
| 2686 | 2686 | ||
| 2687 | - | assertRecordingWebViewIdle(t, "first", fakeWv1) | |
| 2688 | - | assertRecordingWebViewActions(t, "second", fakeWv2, "https://google.com") | |
| 2687 | + | assertRecordingWebViewIdle(t, "first", recordingWv1) | |
| 2688 | + | assertRecordingWebViewActions(t, "second", recordingWv2, "https://google.com") | |
| 2689 | 2689 | } | |
| 2690 | 2690 | ||
| 2691 | - | func assertRecordingWebViewIdle(t *testing.T, name string, wv *fakeRecordingWebView) { | |
| 2691 | + | func assertRecordingWebViewIdle(t *testing.T, name string, wv *recordingWebView) { | |
| 2692 | 2692 | t.Helper() | |
| 2693 | 2693 | if wv.loadURICalled || wv.loadURILastURI != "" { | |
| 2694 | 2694 | t.Errorf("%s webview LoadURI was called, expected zero calls", name) | |
| @@ -2710,7 +2710,7 @@ func assertRecordingWebViewIdle(t *testing.T, name string, wv *fakeRecordingWebV | |||
| 2710 | 2710 | } | |
| 2711 | 2711 | } | |
| 2712 | 2712 | ||
| 2713 | - | func assertRecordingWebViewActions(t *testing.T, name string, wv *fakeRecordingWebView, wantURI string) { | |
| 2713 | + | func assertRecordingWebViewActions(t *testing.T, name string, wv *recordingWebView, wantURI string) { | |
| 2714 | 2714 | t.Helper() | |
| 2715 | 2715 | if !wv.loadURICalled { | |
| 2716 | 2716 | t.Errorf("%s webview LoadURI was not called", name) | |
| @@ -2735,7 +2735,7 @@ func assertRecordingWebViewActions(t *testing.T, name string, wv *fakeRecordingW | |||
| 2735 | 2735 | } | |
| 2736 | 2736 | } | |
| 2737 | 2737 | ||
| 2738 | - | func assertRecordingWebViewBrowserActionsIdle(t *testing.T, name string, wv *fakeRecordingWebView) { | |
| 2738 | + | func assertRecordingWebViewBrowserActionsIdle(t *testing.T, name string, wv *recordingWebView) { | |
| 2739 | 2739 | t.Helper() | |
| 2740 | 2740 | if wv.reloadCalls > 0 { | |
| 2741 | 2741 | t.Errorf("%s webview Reload calls = %d, want 0", name, wv.reloadCalls) | |
| @@ -2763,7 +2763,7 @@ func assertRecordingWebViewBrowserActionsIdle(t *testing.T, name string, wv *fak | |||
| 2763 | 2763 | } | |
| 2764 | 2764 | } | |
| 2765 | 2765 | ||
| 2766 | - | func assertRecordingWebViewBrowserActionsCalledOnce(t *testing.T, name string, wv *fakeRecordingWebView) { | |
| 2766 | + | func assertRecordingWebViewBrowserActionsCalledOnce(t *testing.T, name string, wv *recordingWebView) { | |
| 2767 | 2767 | t.Helper() | |
| 2768 | 2768 | if wv.reloadCalls != 1 { | |
| 2769 | 2769 | t.Errorf("%s webview Reload calls = %d, want 1", name, wv.reloadCalls) | |
| @@ -2837,14 +2837,14 @@ func TestApp_DispatchBrowserWindowActionUsesSourceWindow(t *testing.T) { | |||
| 2837 | 2837 | secondTabs.SetActive(tab2.ID) | |
| 2838 | 2838 | second := &browserWindow{id: "window-2", tabs: secondTabs} | |
| 2839 | 2839 | ||
| 2840 | - | // Create fake recording webviews. | |
| 2841 | - | fakeWv1 := &fakeRecordingWebView{id: 1, loadURILastURI: "https://first.example"} | |
| 2842 | - | fakeWv2 := &fakeRecordingWebView{id: 2, loadURILastURI: "https://second.example"} | |
| 2840 | + | // Create recording webviews. | |
| 2841 | + | recordingWv1 := &recordingWebView{id: 1, loadURILastURI: "https://first.example"} | |
| 2842 | + | recordingWv2 := &recordingWebView{id: 2, loadURILastURI: "https://second.example"} | |
| 2843 | 2843 | ||
| 2844 | - | // Register fake webviews in content coordinator. | |
| 2844 | + | // Register recording webviews in content coordinator. | |
| 2845 | 2845 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | |
| 2846 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv1) | |
| 2847 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), fakeWv2) | |
| 2846 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv1) | |
| 2847 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), recordingWv2) | |
| 2848 | 2848 | ||
| 2849 | 2849 | // Create navCoord (no navigateUC needed for direct webview calls). | |
| 2850 | 2850 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | |
| @@ -2880,11 +2880,11 @@ func TestApp_DispatchBrowserWindowActionUsesSourceWindow(t *testing.T) { | |||
| 2880 | 2880 | } | |
| 2881 | 2881 | } | |
| 2882 | 2882 | ||
| 2883 | - | // Assert first fake webview receives zero calls (stale focus is ignored). | |
| 2884 | - | assertRecordingWebViewBrowserActionsIdle(t, "first", fakeWv1) | |
| 2883 | + | // Assert first recording webview receives zero calls (stale focus is ignored). | |
| 2884 | + | assertRecordingWebViewBrowserActionsIdle(t, "first", recordingWv1) | |
| 2885 | 2885 | ||
| 2886 | - | // Assert second fake webview receives exactly one call per action. | |
| 2887 | - | assertRecordingWebViewBrowserActionsCalledOnce(t, "second", fakeWv2) | |
| 2886 | + | // Assert second recording webview receives exactly one call per action. | |
| 2887 | + | assertRecordingWebViewBrowserActionsCalledOnce(t, "second", recordingWv2) | |
| 2888 | 2888 | } | |
| 2889 | 2889 | ||
| 2890 | 2890 | func TestApp_DispatchBrowserWindowActionZoomInSupportsFileURLs(t *testing.T) { | |
| @@ -2896,9 +2896,9 @@ func TestApp_DispatchBrowserWindowActionZoomInSupportsFileURLs(t *testing.T) { | |||
| 2896 | 2896 | tabs.SetActive(tab.ID) | |
| 2897 | 2897 | bw := &browserWindow{id: "window-1", tabs: tabs} | |
| 2898 | 2898 | ||
| 2899 | - | fakeWv := &fakeRecordingWebView{id: 1, loadURILastURI: "file:///tmp/demo.html"} | |
| 2899 | + | recordingWv := &recordingWebView{id: 1, loadURILastURI: "file:///tmp/demo.html"} | |
| 2900 | 2900 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | |
| 2901 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv) | |
| 2901 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv) | |
| 2902 | 2902 | ||
| 2903 | 2903 | app := &App{ | |
| 2904 | 2904 | browserWindows: map[string]*browserWindow{bw.id: bw}, | |
| @@ -2916,11 +2916,11 @@ func TestApp_DispatchBrowserWindowActionZoomInSupportsFileURLs(t *testing.T) { | |||
| 2916 | 2916 | if err := app.dispatchBrowserWindowAction(ctx, bw, input.ActionZoomIn); err != nil { | |
| 2917 | 2917 | t.Fatalf("dispatchBrowserWindowAction returned error: %v", err) | |
| 2918 | 2918 | } | |
| 2919 | - | if fakeWv.setZoomLevelCalls != 1 { | |
| 2920 | - | t.Fatalf("webview SetZoomLevel calls = %d, want 1", fakeWv.setZoomLevelCalls) | |
| 2919 | + | if recordingWv.setZoomLevelCalls != 1 { | |
| 2920 | + | t.Fatalf("webview SetZoomLevel calls = %d, want 1", recordingWv.setZoomLevelCalls) | |
| 2921 | 2921 | } | |
| 2922 | - | if diff := math.Abs(fakeWv.setZoomLevelLastLevel - 1.1); diff > 0.0001 { | |
| 2923 | - | t.Fatalf("webview SetZoomLevel level = %f, want 1.1", fakeWv.setZoomLevelLastLevel) | |
| 2922 | + | if diff := math.Abs(recordingWv.setZoomLevelLastLevel - 1.1); diff > 0.0001 { | |
| 2923 | + | t.Fatalf("webview SetZoomLevel level = %f, want 1.1", recordingWv.setZoomLevelLastLevel) | |
| 2924 | 2924 | } | |
| 2925 | 2925 | } | |
| 2926 | 2926 | ||
| @@ -3161,11 +3161,11 @@ func TestApp_WorkspaceOmniboxNavigateUsesOwnerWindow(t *testing.T) { | |||
| 3161 | 3161 | first := &browserWindow{id: "window-1", tabs: firstTabs} | |
| 3162 | 3162 | second := &browserWindow{id: "window-2", tabs: secondTabs} | |
| 3163 | 3163 | ||
| 3164 | - | fakeWv1 := &fakeRecordingWebView{id: 1} | |
| 3165 | - | fakeWv2 := &fakeRecordingWebView{id: 2} | |
| 3164 | + | recordingWv1 := &recordingWebView{id: 1} | |
| 3165 | + | recordingWv2 := &recordingWebView{id: 2} | |
| 3166 | 3166 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | |
| 3167 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv1) | |
| 3168 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), fakeWv2) | |
| 3167 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv1) | |
| 3168 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), recordingWv2) | |
| 3169 | 3169 | ||
| 3170 | 3170 | app := &App{ | |
| 3171 | 3171 | deps: &Dependencies{Config: &config.Config{}}, | |
| @@ -3189,11 +3189,11 @@ func TestApp_WorkspaceOmniboxNavigateUsesOwnerWindow(t *testing.T) { | |||
| 3189 | 3189 | t.Fatalf("omnibox navigate callback returned error: %v", err) | |
| 3190 | 3190 | } | |
| 3191 | 3191 | ||
| 3192 | - | if fakeWv1.loadURICalled { | |
| 3193 | - | t.Errorf("first window webview navigated to %q, want no navigation", fakeWv1.loadURILastURI) | |
| 3192 | + | if recordingWv1.loadURICalled { | |
| 3193 | + | t.Errorf("first window webview navigated to %q, want no navigation", recordingWv1.loadURILastURI) | |
| 3194 | 3194 | } | |
| 3195 | - | if !fakeWv2.loadURICalled || fakeWv2.loadURILastURI != "https://example.com" { | |
| 3196 | - | t.Errorf("second window webview navigation = called %v uri %q, want https://example.com", fakeWv2.loadURICalled, fakeWv2.loadURILastURI) | |
| 3195 | + | if !recordingWv2.loadURICalled || recordingWv2.loadURILastURI != "https://example.com" { | |
| 3196 | + | t.Errorf("second window webview navigation = called %v uri %q, want https://example.com", recordingWv2.loadURICalled, recordingWv2.loadURILastURI) | |
| 3197 | 3197 | } | |
| 3198 | 3198 | } | |
| 3199 | 3199 | ||
M internal/ui/browser_window_history_sidebar_test.go +38 -38
| @@ -41,13 +41,13 @@ func TestHistorySidebarConfig_OnNavigateNavigatesActivePaneAndKeepsSidebar(t *te | |||
| 41 | 41 | secondTabs.SetActive(tab2.ID) | |
| 42 | 42 | second := &browserWindow{id: "window-2", tabs: secondTabs} | |
| 43 | 43 | ||
| 44 | - | // Create fake webviews, one per pane. | |
| 45 | - | fakeWv1 := &fakeRecordingWebView{id: 1} | |
| 46 | - | fakeWv2 := &fakeRecordingWebView{id: 2} | |
| 44 | + | // Create recording webviews, one per pane. | |
| 45 | + | recordingWv1 := &recordingWebView{id: 1} | |
| 46 | + | recordingWv2 := &recordingWebView{id: 2} | |
| 47 | 47 | ||
| 48 | 48 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | |
| 49 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv1) | |
| 50 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), fakeWv2) | |
| 49 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv1) | |
| 50 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), recordingWv2) | |
| 51 | 51 | ||
| 52 | 52 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | |
| 53 | 53 | ||
| @@ -75,12 +75,12 @@ func TestHistorySidebarConfig_OnNavigateNavigatesActivePaneAndKeepsSidebar(t *te | |||
| 75 | 75 | require.NoError(t, err, "OnNavigate should succeed") | |
| 76 | 76 | ||
| 77 | 77 | // The second window's webview must have received the navigation. | |
| 78 | - | assert.True(t, fakeWv2.loadURICalled, "second window webview should receive navigation") | |
| 79 | - | assert.Equal(t, navigateURL, fakeWv2.loadURILastURI) | |
| 78 | + | assert.True(t, recordingWv2.loadURICalled, "second window webview should receive navigation") | |
| 79 | + | assert.Equal(t, navigateURL, recordingWv2.loadURILastURI) | |
| 80 | 80 | assert.True(t, second.sidebarVisible, "default history activation should keep the sidebar visible") | |
| 81 | 81 | ||
| 82 | 82 | // The first window's webview must NOT have been touched (stale-focus guard). | |
| 83 | - | assert.False(t, fakeWv1.loadURICalled, "first window webview must not receive navigation from second window callback") | |
| 83 | + | assert.False(t, recordingWv1.loadURICalled, "first window webview must not receive navigation from second window callback") | |
| 84 | 84 | } | |
| 85 | 85 | ||
| 86 | 86 | // TestHistorySidebarConfig_OnNavigateKeepOpenNavigatesWithoutClosing verifies | |
| @@ -103,8 +103,8 @@ func TestHistorySidebarConfig_OnNavigateKeepOpenNavigatesWithoutClosing(t *testi | |||
| 103 | 103 | } | |
| 104 | 104 | ||
| 105 | 105 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | |
| 106 | - | fakeWv := &fakeRecordingWebView{id: 1} | |
| 107 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv) | |
| 106 | + | recordingWv := &recordingWebView{id: 1} | |
| 107 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv) | |
| 108 | 108 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | |
| 109 | 109 | ||
| 110 | 110 | app := &App{ | |
| @@ -122,8 +122,8 @@ func TestHistorySidebarConfig_OnNavigateKeepOpenNavigatesWithoutClosing(t *testi | |||
| 122 | 122 | navigateURL := "https://keep-open.com" | |
| 123 | 123 | err := cfg.OnNavigateKeepOpen(ctx, navigateURL) | |
| 124 | 124 | require.NoError(t, err) | |
| 125 | - | assert.True(t, fakeWv.loadURICalled) | |
| 126 | - | assert.Equal(t, navigateURL, fakeWv.loadURILastURI, "Ctrl+Enter navigation should go to the URL") | |
| 125 | + | assert.True(t, recordingWv.loadURICalled) | |
| 126 | + | assert.Equal(t, navigateURL, recordingWv.loadURILastURI, "Ctrl+Enter navigation should go to the URL") | |
| 127 | 127 | assert.True(t, bw.sidebarVisible, "Ctrl+Enter navigation should keep the sidebar visible") | |
| 128 | 128 | } | |
| 129 | 129 | ||
| @@ -148,12 +148,12 @@ func TestHistorySidebar_OwnershipOnMultiWindowNavigation(t *testing.T) { | |||
| 148 | 148 | secondTabs.SetActive(tab2.ID) | |
| 149 | 149 | second := &browserWindow{id: "window-2", tabs: secondTabs} | |
| 150 | 150 | ||
| 151 | - | fakeWv1 := &fakeRecordingWebView{id: 1} | |
| 152 | - | fakeWv2 := &fakeRecordingWebView{id: 2} | |
| 151 | + | recordingWv1 := &recordingWebView{id: 1} | |
| 152 | + | recordingWv2 := &recordingWebView{id: 2} | |
| 153 | 153 | ||
| 154 | 154 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | |
| 155 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv1) | |
| 156 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), fakeWv2) | |
| 155 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv1) | |
| 156 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), recordingWv2) | |
| 157 | 157 | ||
| 158 | 158 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | |
| 159 | 159 | ||
| @@ -177,11 +177,11 @@ func TestHistorySidebar_OwnershipOnMultiWindowNavigation(t *testing.T) { | |||
| 177 | 177 | require.NoError(t, err) | |
| 178 | 178 | ||
| 179 | 179 | // Second window's webview must receive the navigation. | |
| 180 | - | assert.True(t, fakeWv2.loadURICalled, "second window webview should receive navigation") | |
| 181 | - | assert.Equal(t, "https://second-window.com", fakeWv2.loadURILastURI) | |
| 180 | + | assert.True(t, recordingWv2.loadURICalled, "second window webview should receive navigation") | |
| 181 | + | assert.Equal(t, "https://second-window.com", recordingWv2.loadURILastURI) | |
| 182 | 182 | ||
| 183 | 183 | // First window's webview must NOT have been touched. | |
| 184 | - | assert.False(t, fakeWv1.loadURICalled, "first window webview should NOT receive navigation when second was targeted") | |
| 184 | + | assert.False(t, recordingWv1.loadURICalled, "first window webview should NOT receive navigation when second was targeted") | |
| 185 | 185 | } | |
| 186 | 186 | ||
| 187 | 187 | // ============================================================================= | |
| @@ -509,9 +509,9 @@ func TestApp_HistorySidebarConfig_NavigateCallbackNavigates(t *testing.T) { | |||
| 509 | 509 | sidebarVisible: true, | |
| 510 | 510 | } | |
| 511 | 511 | ||
| 512 | - | fakeWv := &fakeRecordingWebView{id: 1} | |
| 512 | + | recordingWv := &recordingWebView{id: 1} | |
| 513 | 513 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | |
| 514 | - | contentCoord.RegisterPopupWebView(paneID, fakeWv) | |
| 514 | + | contentCoord.RegisterPopupWebView(paneID, recordingWv) | |
| 515 | 515 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | |
| 516 | 516 | ||
| 517 | 517 | app := &App{ | |
| @@ -535,8 +535,8 @@ func TestApp_HistorySidebarConfig_NavigateCallbackNavigates(t *testing.T) { | |||
| 535 | 535 | require.NoError(t, err) | |
| 536 | 536 | ||
| 537 | 537 | // Verify the navigation reached the correct webview. | |
| 538 | - | assert.True(t, fakeWv.loadURICalled, "webview must receive navigation") | |
| 539 | - | assert.Equal(t, navigateURL, fakeWv.loadURILastURI) | |
| 538 | + | assert.True(t, recordingWv.loadURICalled, "webview must receive navigation") | |
| 539 | + | assert.Equal(t, navigateURL, recordingWv.loadURILastURI) | |
| 540 | 540 | assert.True(t, bw.sidebarVisible, "default history navigation should keep the sidebar open") | |
| 541 | 541 | } | |
| 542 | 542 | ||
| @@ -557,9 +557,9 @@ func TestApp_NavigateHistorySidebarSelection_KeepsSidebarVisible(t *testing.T) { | |||
| 557 | 557 | sidebarVisible: true, | |
| 558 | 558 | } | |
| 559 | 559 | ||
| 560 | - | fakeWv := &fakeRecordingWebView{id: 1} | |
| 560 | + | recordingWv := &recordingWebView{id: 1} | |
| 561 | 561 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | |
| 562 | - | contentCoord.RegisterPopupWebView(paneID, fakeWv) | |
| 562 | + | contentCoord.RegisterPopupWebView(paneID, recordingWv) | |
| 563 | 563 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | |
| 564 | 564 | ||
| 565 | 565 | app := &App{ | |
| @@ -575,8 +575,8 @@ func TestApp_NavigateHistorySidebarSelection_KeepsSidebarVisible(t *testing.T) { | |||
| 575 | 575 | ||
| 576 | 576 | err := app.navigateHistorySidebarSelection(ctx, bw, "https://open.com") | |
| 577 | 577 | require.NoError(t, err) | |
| 578 | - | assert.True(t, fakeWv.loadURICalled) | |
| 579 | - | assert.Equal(t, "https://open.com", fakeWv.loadURILastURI) | |
| 578 | + | assert.True(t, recordingWv.loadURICalled) | |
| 579 | + | assert.Equal(t, "https://open.com", recordingWv.loadURILastURI) | |
| 580 | 580 | assert.True(t, bw.sidebarVisible, "history selection navigation should not hide the sidebar") | |
| 581 | 581 | } | |
| 582 | 582 | ||
| @@ -599,12 +599,12 @@ func TestApp_HistorySidebarConfig_NavigateCallbackOwnership(t *testing.T) { | |||
| 599 | 599 | secondTabs.SetActive(tab2.ID) | |
| 600 | 600 | second := &browserWindow{id: "window-2", tabs: secondTabs} | |
| 601 | 601 | ||
| 602 | - | fakeWv1 := &fakeRecordingWebView{id: 1} | |
| 603 | - | fakeWv2 := &fakeRecordingWebView{id: 2} | |
| 602 | + | recordingWv1 := &recordingWebView{id: 1} | |
| 603 | + | recordingWv2 := &recordingWebView{id: 2} | |
| 604 | 604 | ||
| 605 | 605 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | |
| 606 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv1) | |
| 607 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), fakeWv2) | |
| 606 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv1) | |
| 607 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), recordingWv2) | |
| 608 | 608 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | |
| 609 | 609 | ||
| 610 | 610 | app := &App{ | |
| @@ -629,11 +629,11 @@ func TestApp_HistorySidebarConfig_NavigateCallbackOwnership(t *testing.T) { | |||
| 629 | 629 | require.NoError(t, err) | |
| 630 | 630 | ||
| 631 | 631 | // Second window's webview must receive navigation. | |
| 632 | - | assert.True(t, fakeWv2.loadURICalled, "second window webview must receive navigation") | |
| 633 | - | assert.Equal(t, "https://ownership.com", fakeWv2.loadURILastURI) | |
| 632 | + | assert.True(t, recordingWv2.loadURICalled, "second window webview must receive navigation") | |
| 633 | + | assert.Equal(t, "https://ownership.com", recordingWv2.loadURILastURI) | |
| 634 | 634 | ||
| 635 | 635 | // First window's webview must NOT be touched. | |
| 636 | - | assert.False(t, fakeWv1.loadURICalled, "first window webview must not receive navigation") | |
| 636 | + | assert.False(t, recordingWv1.loadURICalled, "first window webview must not receive navigation") | |
| 637 | 637 | } | |
| 638 | 638 | ||
| 639 | 639 | // TestApp_HistorySidebarConfig_KeepOpenCallback verifies that | |
| @@ -655,9 +655,9 @@ func TestApp_HistorySidebarConfig_KeepOpenCallback(t *testing.T) { | |||
| 655 | 655 | sidebarVisible: true, | |
| 656 | 656 | } | |
| 657 | 657 | ||
| 658 | - | fakeWv := &fakeRecordingWebView{id: 1} | |
| 658 | + | recordingWv := &recordingWebView{id: 1} | |
| 659 | 659 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | |
| 660 | - | contentCoord.RegisterPopupWebView(paneID, fakeWv) | |
| 660 | + | contentCoord.RegisterPopupWebView(paneID, recordingWv) | |
| 661 | 661 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | |
| 662 | 662 | ||
| 663 | 663 | app := &App{ | |
| @@ -678,8 +678,8 @@ func TestApp_HistorySidebarConfig_KeepOpenCallback(t *testing.T) { | |||
| 678 | 678 | err := cfg.OnNavigateKeepOpen(ctx, navigateURL) | |
| 679 | 679 | require.NoError(t, err) | |
| 680 | 680 | ||
| 681 | - | assert.True(t, fakeWv.loadURICalled, "webview must receive navigation") | |
| 682 | - | assert.Equal(t, navigateURL, fakeWv.loadURILastURI) | |
| 681 | + | assert.True(t, recordingWv.loadURICalled, "webview must receive navigation") | |
| 682 | + | assert.Equal(t, navigateURL, recordingWv.loadURILastURI) | |
| 683 | 683 | ||
| 684 | 684 | // Sidebar must remain visible (keep-open contract). | |
| 685 | 685 | assert.True(t, bw.sidebarVisible, "sidebar must stay visible after keep-open navigation") | |
M internal/ui/browser_window_test.go +7 -7
| @@ -130,8 +130,8 @@ func TestTabCoordinator_CloseReleasesClosedTabWorkspaceWebViews(t *testing.T) { | |||
| 130 | 130 | ||
| 131 | 131 | pool := &recordingWebViewPool{} | |
| 132 | 132 | contentCoord := contentcoord.NewCoordinator(ctx, pool, nil, nil, nil, nil, nil, nil) | |
| 133 | - | closedWV := &fakeRecordingWebView{id: 101} | |
| 134 | - | survivingWV := &fakeRecordingWebView{id: 102} | |
| 133 | + | closedWV := &recordingWebView{id: 101} | |
| 134 | + | survivingWV := &recordingWebView{id: 102} | |
| 135 | 135 | contentCoord.RegisterPopupWebView(closedTab.Workspace.ActivePaneID, closedWV) | |
| 136 | 136 | contentCoord.RegisterPopupWebView(survivingTab.Workspace.ActivePaneID, survivingWV) | |
| 137 | 137 | ||
| @@ -173,8 +173,8 @@ func TestTabCoordinator_SwitchDoesNotReleaseWorkspaceWebViews(t *testing.T) { | |||
| 173 | 173 | ||
| 174 | 174 | pool := &recordingWebViewPool{} | |
| 175 | 175 | contentCoord := contentcoord.NewCoordinator(ctx, pool, nil, nil, nil, nil, nil, nil) | |
| 176 | - | firstWV := &fakeRecordingWebView{id: 111} | |
| 177 | - | secondWV := &fakeRecordingWebView{id: 112} | |
| 176 | + | firstWV := &recordingWebView{id: 111} | |
| 177 | + | secondWV := &recordingWebView{id: 112} | |
| 178 | 178 | contentCoord.RegisterPopupWebView(firstTab.Workspace.ActivePaneID, firstWV) | |
| 179 | 179 | contentCoord.RegisterPopupWebView(secondTab.Workspace.ActivePaneID, secondWV) | |
| 180 | 180 | ||
| @@ -209,7 +209,7 @@ func TestTabCoordinator_CloseLastTabReleasesWorkspaceBeforeWindowRemoval(t *test | |||
| 209 | 209 | ||
| 210 | 210 | pool := &recordingWebViewPool{} | |
| 211 | 211 | contentCoord := contentcoord.NewCoordinator(ctx, pool, nil, nil, nil, nil, nil, nil) | |
| 212 | - | closedWV := &fakeRecordingWebView{id: 121} | |
| 212 | + | closedWV := &recordingWebView{id: 121} | |
| 213 | 213 | contentCoord.RegisterPopupWebView(closedTab.Workspace.ActivePaneID, closedWV) | |
| 214 | 214 | ||
| 215 | 215 | bw := &browserWindow{id: "window-1", tabs: tabs} | |
| @@ -247,8 +247,8 @@ func TestBrowserWindow_RemoveBrowserWindowReleasesOwnedTabWorkspaceWebViews(t *t | |||
| 247 | 247 | ||
| 248 | 248 | pool := &recordingWebViewPool{} | |
| 249 | 249 | contentCoord := contentcoord.NewCoordinator(ctx, pool, nil, nil, nil, nil, nil, nil) | |
| 250 | - | ownedWV := &fakeRecordingWebView{id: 201} | |
| 251 | - | otherWV := &fakeRecordingWebView{id: 202} | |
| 250 | + | ownedWV := &recordingWebView{id: 201} | |
| 251 | + | otherWV := &recordingWebView{id: 202} | |
| 252 | 252 | contentCoord.RegisterPopupWebView(ownedTab.Workspace.ActivePaneID, ownedWV) | |
| 253 | 253 | contentCoord.RegisterPopupWebView(otherTab.Workspace.ActivePaneID, otherWV) | |
| 254 | 254 | ||
A internal/ui/coordinator/mocks/navigation.go +118 -0
| @@ -0,0 +1,118 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | mock "github.com/stretchr/testify/mock" | |
| 11 | + | ) | |
| 12 | + | ||
| 13 | + | // NewMockOmniboxProvider creates a new instance of MockOmniboxProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 14 | + | // The first argument is typically a *testing.T value. | |
| 15 | + | func NewMockOmniboxProvider(t interface { | |
| 16 | + | mock.TestingT | |
| 17 | + | Cleanup(func()) | |
| 18 | + | }) *MockOmniboxProvider { | |
| 19 | + | mock := &MockOmniboxProvider{} | |
| 20 | + | mock.Mock.Test(t) | |
| 21 | + | ||
| 22 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 23 | + | ||
| 24 | + | return mock | |
| 25 | + | } | |
| 26 | + | ||
| 27 | + | // MockOmniboxProvider is an autogenerated mock type for the OmniboxProvider type | |
| 28 | + | type MockOmniboxProvider struct { | |
| 29 | + | mock.Mock | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | type MockOmniboxProvider_Expecter struct { | |
| 33 | + | mock *mock.Mock | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | func (_m *MockOmniboxProvider) EXPECT() *MockOmniboxProvider_Expecter { | |
| 37 | + | return &MockOmniboxProvider_Expecter{mock: &_m.Mock} | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | // ToggleOmnibox provides a mock function for the type MockOmniboxProvider | |
| 41 | + | func (_mock *MockOmniboxProvider) ToggleOmnibox(ctx context.Context) { | |
| 42 | + | _mock.Called(ctx) | |
| 43 | + | return | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | // MockOmniboxProvider_ToggleOmnibox_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ToggleOmnibox' | |
| 47 | + | type MockOmniboxProvider_ToggleOmnibox_Call struct { | |
| 48 | + | *mock.Call | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | // ToggleOmnibox is a helper method to define mock.On call | |
| 52 | + | // - ctx context.Context | |
| 53 | + | func (_e *MockOmniboxProvider_Expecter) ToggleOmnibox(ctx any) *MockOmniboxProvider_ToggleOmnibox_Call { | |
| 54 | + | return &MockOmniboxProvider_ToggleOmnibox_Call{Call: _e.mock.On("ToggleOmnibox", ctx)} | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | func (_c *MockOmniboxProvider_ToggleOmnibox_Call) Run(run func(ctx context.Context)) *MockOmniboxProvider_ToggleOmnibox_Call { | |
| 58 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 59 | + | var arg0 context.Context | |
| 60 | + | if args[0] != nil { | |
| 61 | + | arg0 = args[0].(context.Context) | |
| 62 | + | } | |
| 63 | + | run( | |
| 64 | + | arg0, | |
| 65 | + | ) | |
| 66 | + | }) | |
| 67 | + | return _c | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | func (_c *MockOmniboxProvider_ToggleOmnibox_Call) Return() *MockOmniboxProvider_ToggleOmnibox_Call { | |
| 71 | + | _c.Call.Return() | |
| 72 | + | return _c | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | func (_c *MockOmniboxProvider_ToggleOmnibox_Call) RunAndReturn(run func(ctx context.Context)) *MockOmniboxProvider_ToggleOmnibox_Call { | |
| 76 | + | _c.Run(run) | |
| 77 | + | return _c | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | // UpdateOmniboxZoom provides a mock function for the type MockOmniboxProvider | |
| 81 | + | func (_mock *MockOmniboxProvider) UpdateOmniboxZoom(factor float64) { | |
| 82 | + | _mock.Called(factor) | |
| 83 | + | return | |
| 84 | + | } | |
| 85 | + | ||
| 86 | + | // MockOmniboxProvider_UpdateOmniboxZoom_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateOmniboxZoom' | |
| 87 | + | type MockOmniboxProvider_UpdateOmniboxZoom_Call struct { | |
| 88 | + | *mock.Call | |
| 89 | + | } | |
| 90 | + | ||
| 91 | + | // UpdateOmniboxZoom is a helper method to define mock.On call | |
| 92 | + | // - factor float64 | |
| 93 | + | func (_e *MockOmniboxProvider_Expecter) UpdateOmniboxZoom(factor any) *MockOmniboxProvider_UpdateOmniboxZoom_Call { | |
| 94 | + | return &MockOmniboxProvider_UpdateOmniboxZoom_Call{Call: _e.mock.On("UpdateOmniboxZoom", factor)} | |
| 95 | + | } | |
| 96 | + | ||
| 97 | + | func (_c *MockOmniboxProvider_UpdateOmniboxZoom_Call) Run(run func(factor float64)) *MockOmniboxProvider_UpdateOmniboxZoom_Call { | |
| 98 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 99 | + | var arg0 float64 | |
| 100 | + | if args[0] != nil { | |
| 101 | + | arg0 = args[0].(float64) | |
| 102 | + | } | |
| 103 | + | run( | |
| 104 | + | arg0, | |
| 105 | + | ) | |
| 106 | + | }) | |
| 107 | + | return _c | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | func (_c *MockOmniboxProvider_UpdateOmniboxZoom_Call) Return() *MockOmniboxProvider_UpdateOmniboxZoom_Call { | |
| 111 | + | _c.Call.Return() | |
| 112 | + | return _c | |
| 113 | + | } | |
| 114 | + | ||
| 115 | + | func (_c *MockOmniboxProvider_UpdateOmniboxZoom_Call) RunAndReturn(run func(factor float64)) *MockOmniboxProvider_UpdateOmniboxZoom_Call { | |
| 116 | + | _c.Run(run) | |
| 117 | + | return _c | |
| 118 | + | } | |
M internal/ui/coordinator/navigation_test.go +8 -17
| @@ -4,28 +4,19 @@ import ( | |||
| 4 | 4 | "context" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/stretchr/testify/require" | |
| 7 | + | coordinatormocks "github.com/bnema/dumber/internal/ui/coordinator/mocks" | |
| 8 | + | "github.com/stretchr/testify/mock" | |
| 8 | 9 | ) | |
| 9 | 10 | ||
| 10 | - | type fakeOmniboxProvider struct { | |
| 11 | - | toggled bool | |
| 12 | - | zoomUpdates []float64 | |
| 13 | - | } | |
| 14 | - | ||
| 15 | - | func (f *fakeOmniboxProvider) ToggleOmnibox(context.Context) { f.toggled = true } | |
| 16 | - | ||
| 17 | - | func (f *fakeOmniboxProvider) UpdateOmniboxZoom(factor float64) { | |
| 18 | - | f.zoomUpdates = append(f.zoomUpdates, factor) | |
| 19 | - | } | |
| 20 | - | ||
| 21 | 11 | func TestNavigationCoordinator_OmniboxProviderOpenAndZoom(t *testing.T) { | |
| 22 | 12 | c := &NavigationCoordinator{} | |
| 23 | - | provider := &fakeOmniboxProvider{} | |
| 13 | + | provider := coordinatormocks.NewMockOmniboxProvider(t) | |
| 14 | + | provider.EXPECT().ToggleOmnibox(mock.Anything).Once() | |
| 15 | + | provider.EXPECT().UpdateOmniboxZoom(1.25).Once() | |
| 24 | 16 | ||
| 25 | 17 | c.SetOmniboxProvider(provider) | |
| 26 | - | require.NoError(t, c.OpenOmnibox(context.Background())) | |
| 18 | + | if err := c.OpenOmnibox(context.Background()); err != nil { | |
| 19 | + | t.Fatal(err) | |
| 20 | + | } | |
| 27 | 21 | c.NotifyZoomChanged(context.Background(), 1.25) | |
| 28 | - | ||
| 29 | - | require.True(t, provider.toggled) | |
| 30 | - | require.Equal(t, []float64{1.25}, provider.zoomUpdates) | |
| 31 | 22 | } | |
A internal/ui/dialog/mocks/permission.go +96 -0
| @@ -0,0 +1,96 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "context" | |
| 9 | + | ||
| 10 | + | mock "github.com/stretchr/testify/mock" | |
| 11 | + | ) | |
| 12 | + | ||
| 13 | + | // NewMockPermissionPopup creates a new instance of MockPermissionPopup. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 14 | + | // The first argument is typically a *testing.T value. | |
| 15 | + | func NewMockPermissionPopup(t interface { | |
| 16 | + | mock.TestingT | |
| 17 | + | Cleanup(func()) | |
| 18 | + | }) *MockPermissionPopup { | |
| 19 | + | mock := &MockPermissionPopup{} | |
| 20 | + | mock.Mock.Test(t) | |
| 21 | + | ||
| 22 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 23 | + | ||
| 24 | + | return mock | |
| 25 | + | } | |
| 26 | + | ||
| 27 | + | // MockPermissionPopup is an autogenerated mock type for the permissionPopup type | |
| 28 | + | type MockPermissionPopup struct { | |
| 29 | + | mock.Mock | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | type MockPermissionPopup_Expecter struct { | |
| 33 | + | mock *mock.Mock | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | func (_m *MockPermissionPopup) EXPECT() *MockPermissionPopup_Expecter { | |
| 37 | + | return &MockPermissionPopup_Expecter{mock: &_m.Mock} | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | // Show provides a mock function for the type MockPermissionPopup | |
| 41 | + | func (_mock *MockPermissionPopup) Show(ctx context.Context, heading string, body string, callback func(allowed bool, persistent bool)) { | |
| 42 | + | _mock.Called(ctx, heading, body, callback) | |
| 43 | + | return | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | // MockPermissionPopup_Show_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Show' | |
| 47 | + | type MockPermissionPopup_Show_Call struct { | |
| 48 | + | *mock.Call | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | // Show is a helper method to define mock.On call | |
| 52 | + | // - ctx context.Context | |
| 53 | + | // - heading string | |
| 54 | + | // - body string | |
| 55 | + | // - callback func(allowed bool, persistent bool) | |
| 56 | + | func (_e *MockPermissionPopup_Expecter) Show(ctx any, heading any, body any, callback any) *MockPermissionPopup_Show_Call { | |
| 57 | + | return &MockPermissionPopup_Show_Call{Call: _e.mock.On("Show", ctx, heading, body, callback)} | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | func (_c *MockPermissionPopup_Show_Call) Run(run func(ctx context.Context, heading string, body string, callback func(allowed bool, persistent bool))) *MockPermissionPopup_Show_Call { | |
| 61 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 62 | + | var arg0 context.Context | |
| 63 | + | if args[0] != nil { | |
| 64 | + | arg0 = args[0].(context.Context) | |
| 65 | + | } | |
| 66 | + | var arg1 string | |
| 67 | + | if args[1] != nil { | |
| 68 | + | arg1 = args[1].(string) | |
| 69 | + | } | |
| 70 | + | var arg2 string | |
| 71 | + | if args[2] != nil { | |
| 72 | + | arg2 = args[2].(string) | |
| 73 | + | } | |
| 74 | + | var arg3 func(allowed bool, persistent bool) | |
| 75 | + | if args[3] != nil { | |
| 76 | + | arg3 = args[3].(func(allowed bool, persistent bool)) | |
| 77 | + | } | |
| 78 | + | run( | |
| 79 | + | arg0, | |
| 80 | + | arg1, | |
| 81 | + | arg2, | |
| 82 | + | arg3, | |
| 83 | + | ) | |
| 84 | + | }) | |
| 85 | + | return _c | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | func (_c *MockPermissionPopup_Show_Call) Return() *MockPermissionPopup_Show_Call { | |
| 89 | + | _c.Call.Return() | |
| 90 | + | return _c | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | func (_c *MockPermissionPopup_Show_Call) RunAndReturn(run func(ctx context.Context, heading string, body string, callback func(allowed bool, persistent bool))) *MockPermissionPopup_Show_Call { | |
| 94 | + | _c.Run(run) | |
| 95 | + | return _c | |
| 96 | + | } | |
M internal/ui/dialog/permission_test.go +31 -34
| @@ -6,43 +6,40 @@ import ( | |||
| 6 | 6 | ||
| 7 | 7 | "github.com/bnema/dumber/internal/application/port" | |
| 8 | 8 | "github.com/bnema/dumber/internal/domain/entity" | |
| 9 | + | dialogmocks "github.com/bnema/dumber/internal/ui/dialog/mocks" | |
| 9 | 10 | "github.com/stretchr/testify/assert" | |
| 11 | + | "github.com/stretchr/testify/mock" | |
| 10 | 12 | ) | |
| 11 | 13 | ||
| 12 | - | type fakePermissionPopup struct { | |
| 13 | - | showCalls []fakePopupShowCall | |
| 14 | - | callback func(allowed, persistent bool) | |
| 15 | - | } | |
| 16 | - | ||
| 17 | - | type fakePopupShowCall struct { | |
| 14 | + | type permissionPopupShowCall struct { | |
| 18 | 15 | heading string | |
| 19 | 16 | body string | |
| 20 | 17 | } | |
| 21 | 18 | ||
| 22 | - | func (f *fakePermissionPopup) Show( | |
| 23 | - | _ context.Context, | |
| 24 | - | heading string, | |
| 25 | - | body string, | |
| 26 | - | callback func(allowed, persistent bool), | |
| 27 | - | ) { | |
| 28 | - | f.showCalls = append(f.showCalls, fakePopupShowCall{ | |
| 29 | - | heading: heading, | |
| 30 | - | body: body, | |
| 31 | - | }) | |
| 32 | - | f.callback = callback | |
| 33 | - | } | |
| 34 | - | ||
| 35 | - | func (f *fakePermissionPopup) Respond(allowed, persistent bool) { | |
| 36 | - | if f.callback == nil { | |
| 37 | - | return | |
| 19 | + | func TestPermissionDialog_QueuesRequestsWhilePopupVisible(t *testing.T) { | |
| 20 | + | popup := dialogmocks.NewMockPermissionPopup(t) | |
| 21 | + | var showCalls []permissionPopupShowCall | |
| 22 | + | var callback func(allowed, persistent bool) | |
| 23 | + | popup.EXPECT(). | |
| 24 | + | Show(mock.Anything, mock.Anything, mock.Anything, mock.Anything). | |
| 25 | + | Run(func(_ context.Context, heading string, body string, cb func(allowed, persistent bool)) { | |
| 26 | + | showCalls = append(showCalls, permissionPopupShowCall{ | |
| 27 | + | heading: heading, | |
| 28 | + | body: body, | |
| 29 | + | }) | |
| 30 | + | callback = cb | |
| 31 | + | }). | |
| 32 | + | Twice() | |
| 33 | + | ||
| 34 | + | respond := func(allowed, persistent bool) { | |
| 35 | + | if callback == nil { | |
| 36 | + | return | |
| 37 | + | } | |
| 38 | + | cb := callback | |
| 39 | + | callback = nil | |
| 40 | + | cb(allowed, persistent) | |
| 38 | 41 | } | |
| 39 | - | cb := f.callback | |
| 40 | - | f.callback = nil | |
| 41 | - | cb(allowed, persistent) | |
| 42 | - | } | |
| 43 | 42 | ||
| 44 | - | func TestPermissionDialog_QueuesRequestsWhilePopupVisible(t *testing.T) { | |
| 45 | - | popup := &fakePermissionPopup{} | |
| 46 | 43 | d := &PermissionDialog{popup: popup} | |
| 47 | 44 | ||
| 48 | 45 | var firstResult port.PermissionDialogResult | |
| @@ -71,22 +68,22 @@ func TestPermissionDialog_QueuesRequestsWhilePopupVisible(t *testing.T) { | |||
| 71 | 68 | }, | |
| 72 | 69 | ) | |
| 73 | 70 | ||
| 74 | - | if assert.Len(t, popup.showCalls, 1) { | |
| 75 | - | assert.Equal(t, "Allow Microphone Access?", popup.showCalls[0].heading) | |
| 71 | + | if assert.Len(t, showCalls, 1) { | |
| 72 | + | assert.Equal(t, "Allow Microphone Access?", showCalls[0].heading) | |
| 76 | 73 | } | |
| 77 | 74 | assert.False(t, firstCalled) | |
| 78 | 75 | assert.False(t, secondCalled) | |
| 79 | 76 | ||
| 80 | - | popup.Respond(true, false) | |
| 77 | + | respond(true, false) | |
| 81 | 78 | assert.True(t, firstCalled) | |
| 82 | 79 | assert.Equal(t, port.PermissionDialogResult{Allowed: true, Persistent: false}, firstResult) | |
| 83 | 80 | ||
| 84 | - | if assert.Len(t, popup.showCalls, 2) { | |
| 85 | - | assert.Equal(t, "Allow Camera Access?", popup.showCalls[1].heading) | |
| 81 | + | if assert.Len(t, showCalls, 2) { | |
| 82 | + | assert.Equal(t, "Allow Camera Access?", showCalls[1].heading) | |
| 86 | 83 | } | |
| 87 | 84 | assert.False(t, secondCalled) | |
| 88 | 85 | ||
| 89 | - | popup.Respond(false, true) | |
| 86 | + | respond(false, true) | |
| 90 | 87 | assert.True(t, secondCalled) | |
| 91 | 88 | assert.Equal(t, port.PermissionDialogResult{Allowed: false, Persistent: true}, secondResult) | |
| 92 | 89 | } | |
M internal/ui/systemviews/app_test.go +98 -98
| @@ -48,7 +48,7 @@ func TestParseRoute(t *testing.T) { | |||
| 48 | 48 | func TestAppRunMountsPlaceholderAndRecordsRoute(t *testing.T) { | |
| 49 | 49 | t.Parallel() | |
| 50 | 50 | ||
| 51 | - | dom := &fakeDOM{} | |
| 51 | + | dom := &recordingDOM{} | |
| 52 | 52 | app := NewApp(Dependencies{ | |
| 53 | 53 | DOM: dom, | |
| 54 | 54 | LocationURI: "dumb://history", | |
| @@ -64,7 +64,7 @@ func TestAppRunMountsPlaceholderAndRecordsRoute(t *testing.T) { | |||
| 64 | 64 | func TestAppLoadInitialHistoryRouteUsesStyledSections(t *testing.T) { | |
| 65 | 65 | t.Parallel() | |
| 66 | 66 | ||
| 67 | - | history := &fakeHistoryService{entries: []*entity.HistoryEntry{{ | |
| 67 | + | history := &recordingHistoryService{entries: []*entity.HistoryEntry{{ | |
| 68 | 68 | URL: "https://example.com", | |
| 69 | 69 | Title: "Example", | |
| 70 | 70 | }}} | |
| @@ -98,7 +98,7 @@ func TestAppLoadInitialHistoryRouteUsesStyledSections(t *testing.T) { | |||
| 98 | 98 | func TestAppLoadInitialHistoryRouteShowsTotalStatsWithWindowedTimeline(t *testing.T) { | |
| 99 | 99 | t.Parallel() | |
| 100 | 100 | ||
| 101 | - | history := &fakeHistoryService{ | |
| 101 | + | history := &recordingHistoryService{ | |
| 102 | 102 | entries: []*entity.HistoryEntry{ | |
| 103 | 103 | {ID: 1, URL: "https://older.example", Title: "Older", VisitCount: 2}, | |
| 104 | 104 | {ID: 2, URL: "https://newer.example", Title: "Newer", VisitCount: 3}, | |
| @@ -119,8 +119,8 @@ func TestAppLoadInitialHistoryRouteShowsTotalStatsWithWindowedTimeline(t *testin | |||
| 119 | 119 | func TestAppRunMountsHistoryLoadingBeforeAsyncHydration(t *testing.T) { | |
| 120 | 120 | t.Parallel() | |
| 121 | 121 | ||
| 122 | - | dom := &fakeActionDOM{fakeDOM: fakeDOM{mounts: make(chan string, 4)}} | |
| 123 | - | history := &fakeHistoryService{ | |
| 122 | + | dom := &recordingActionDOM{recordingDOM: recordingDOM{mounts: make(chan string, 4)}} | |
| 123 | + | history := &recordingHistoryService{ | |
| 124 | 124 | entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com", Title: "Loaded entry"}}, | |
| 125 | 125 | timelineStarted: make(chan struct{}), | |
| 126 | 126 | releaseTimeline: make(chan struct{}), | |
| @@ -147,7 +147,7 @@ func TestAppRunMountsHistoryLoadingBeforeAsyncHydration(t *testing.T) { | |||
| 147 | 147 | func TestSurfaceActionErrorPreventsStaleAsyncHydrationMount(t *testing.T) { | |
| 148 | 148 | t.Parallel() | |
| 149 | 149 | ||
| 150 | - | dom := &fakeDOM{mounts: make(chan string, 4)} | |
| 150 | + | dom := &recordingDOM{mounts: make(chan string, 4)} | |
| 151 | 151 | app := NewApp(Dependencies{DOM: dom, LocationURI: "dumb://history"}) | |
| 152 | 152 | app.currentRoute = RouteHistory | |
| 153 | 153 | staleHTML := "<div>Stale hydrated entry</div>" | |
| @@ -168,8 +168,8 @@ func TestSurfaceActionErrorPreventsStaleAsyncHydrationMount(t *testing.T) { | |||
| 168 | 168 | func TestSurfaceActionErrorWhileAsyncHydrationBlockedPreventsStaleMount(t *testing.T) { | |
| 169 | 169 | t.Parallel() | |
| 170 | 170 | ||
| 171 | - | dom := &fakeActionDOM{fakeDOM: fakeDOM{mounts: make(chan string, 4)}} | |
| 172 | - | history := &fakeHistoryService{ | |
| 171 | + | dom := &recordingActionDOM{recordingDOM: recordingDOM{mounts: make(chan string, 4)}} | |
| 172 | + | history := &recordingHistoryService{ | |
| 173 | 173 | entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com", Title: "Stale hydrated entry"}}, | |
| 174 | 174 | timelineStarted: make(chan struct{}), | |
| 175 | 175 | releaseTimeline: make(chan struct{}), | |
| @@ -200,7 +200,7 @@ func TestSurfaceActionErrorWhileAsyncHydrationBlockedPreventsStaleMount(t *testi | |||
| 200 | 200 | ||
| 201 | 201 | func TestCurrentHistoryRouteSnapshotIncludesWindowAfter(t *testing.T) { | |
| 202 | 202 | cursor := time.Date(2026, 4, 25, 9, 0, 0, 0, time.UTC) | |
| 203 | - | app := NewApp(Dependencies{History: &fakeHistoryService{}, LocationURI: "dumb://history"}) | |
| 203 | + | app := NewApp(Dependencies{History: &recordingHistoryService{}, LocationURI: "dumb://history"}) | |
| 204 | 204 | app.currentRoute = RouteHistory | |
| 205 | 205 | app.historyWindowAfter = cursor | |
| 206 | 206 | ||
| @@ -212,13 +212,13 @@ func TestAppHandleHistoryLoadMoreAppendsOlderWindow(t *testing.T) { | |||
| 212 | 212 | t.Parallel() | |
| 213 | 213 | ||
| 214 | 214 | cursor := time.Date(2026, 4, 25, 9, 0, 0, 0, time.UTC) | |
| 215 | - | history := &fakeHistoryService{window: &entity.HistoryWindow{ | |
| 215 | + | history := &recordingHistoryService{window: &entity.HistoryWindow{ | |
| 216 | 216 | Entries: []*entity.HistoryEntry{{ID: 2, URL: "https://older.example", Title: "Older entry", LastVisited: cursor.Add(-time.Hour)}}, | |
| 217 | 217 | Before: cursor, | |
| 218 | 218 | After: cursor.Add(-24 * time.Hour), | |
| 219 | 219 | HasMore: true, | |
| 220 | 220 | }} | |
| 221 | - | dom := &fakeActionDOM{} | |
| 221 | + | dom := &recordingActionDOM{} | |
| 222 | 222 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | |
| 223 | 223 | app.currentRoute = RouteHistory | |
| 224 | 224 | app.historyEntries = []*entity.HistoryEntry{{ID: 1, URL: "https://newer.example", Title: "Newer entry", LastVisited: cursor}} | |
| @@ -240,13 +240,13 @@ func TestAppHandleHistoryLoadMoreRemountsWhenAppendUnavailable(t *testing.T) { | |||
| 240 | 240 | t.Parallel() | |
| 241 | 241 | ||
| 242 | 242 | cursor := time.Date(2026, 4, 25, 9, 0, 0, 0, time.UTC) | |
| 243 | - | history := &fakeHistoryService{window: &entity.HistoryWindow{ | |
| 243 | + | history := &recordingHistoryService{window: &entity.HistoryWindow{ | |
| 244 | 244 | Entries: []*entity.HistoryEntry{{ID: 2, URL: "https://older.example", Title: "Older entry", LastVisited: cursor.Add(-time.Hour)}}, | |
| 245 | 245 | Before: cursor, | |
| 246 | 246 | After: cursor.Add(-24 * time.Hour), | |
| 247 | 247 | HasMore: true, | |
| 248 | 248 | }} | |
| 249 | - | dom := &fakeDOM{} | |
| 249 | + | dom := &recordingDOM{} | |
| 250 | 250 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | |
| 251 | 251 | app.currentRoute = RouteHistory | |
| 252 | 252 | app.historyEntries = []*entity.HistoryEntry{{ID: 1, URL: "https://newer.example", Title: "Newer entry", LastVisited: cursor}} | |
| @@ -267,8 +267,8 @@ func TestAppHandleHistoryLoadMoreIgnoresStaleCursor(t *testing.T) { | |||
| 267 | 267 | t.Parallel() | |
| 268 | 268 | ||
| 269 | 269 | cursor := time.Date(2026, 4, 25, 9, 0, 0, 0, time.UTC) | |
| 270 | - | history := &fakeHistoryService{window: &entity.HistoryWindow{Entries: []*entity.HistoryEntry{{ID: 2, URL: "https://older.example"}}}} | |
| 271 | - | dom := &fakeActionDOM{} | |
| 270 | + | history := &recordingHistoryService{window: &entity.HistoryWindow{Entries: []*entity.HistoryEntry{{ID: 2, URL: "https://older.example"}}}} | |
| 271 | + | dom := &recordingActionDOM{} | |
| 272 | 272 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | |
| 273 | 273 | app.currentRoute = RouteHistory | |
| 274 | 274 | app.historyWindowAfter = cursor | |
| @@ -285,7 +285,7 @@ func TestAppHandleHistoryLoadMoreIgnoresStaleCursor(t *testing.T) { | |||
| 285 | 285 | func TestAppLoadInitialHistoryRouteRendersManagementActions(t *testing.T) { | |
| 286 | 286 | t.Parallel() | |
| 287 | 287 | ||
| 288 | - | history := &fakeHistoryService{ | |
| 288 | + | history := &recordingHistoryService{ | |
| 289 | 289 | entries: []*entity.HistoryEntry{{ | |
| 290 | 290 | ID: 42, | |
| 291 | 291 | URL: "https://example.com/page", | |
| @@ -314,8 +314,8 @@ func TestAppLoadInitialHistoryRouteRendersManagementActions(t *testing.T) { | |||
| 314 | 314 | } | |
| 315 | 315 | ||
| 316 | 316 | func TestAppHandleHistoryActionsRefreshesDOM(t *testing.T) { | |
| 317 | - | dom := &fakeDOM{} | |
| 318 | - | history := &fakeHistoryService{ | |
| 317 | + | dom := &recordingDOM{} | |
| 318 | + | history := &recordingHistoryService{ | |
| 319 | 319 | entries: []*entity.HistoryEntry{{ID: 42, URL: "https://example.com", Title: "Example"}}, | |
| 320 | 320 | searchEntries: []*entity.HistoryEntry{{ID: 7, URL: "https://search.example", Title: "Search result"}}, | |
| 321 | 321 | } | |
| @@ -364,8 +364,8 @@ func TestAppHandleHistoryActionsRefreshesDOM(t *testing.T) { | |||
| 364 | 364 | } | |
| 365 | 365 | ||
| 366 | 366 | func TestAppCloseStopsActionWorkerAndReleasesDOM(t *testing.T) { | |
| 367 | - | dom := &fakeActionDOM{} | |
| 368 | - | history := &fakeHistoryService{entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com"}}} | |
| 367 | + | dom := &recordingActionDOM{} | |
| 368 | + | history := &recordingHistoryService{entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com"}}} | |
| 369 | 369 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | |
| 370 | 370 | ||
| 371 | 371 | ctx, cancel := context.WithCancel(context.Background()) | |
| @@ -396,8 +396,8 @@ func TestAppRunCleansActionWorkerWhenBindingFails(t *testing.T) { | |||
| 396 | 396 | } | |
| 397 | 397 | ||
| 398 | 398 | func TestAppRunReleasesDOMWhenActionWorkerCannotStart(t *testing.T) { | |
| 399 | - | dom := &fakeActionDOM{} | |
| 400 | - | history := &fakeHistoryService{entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com"}}} | |
| 399 | + | dom := &recordingActionDOM{} | |
| 400 | + | history := &recordingHistoryService{entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com"}}} | |
| 401 | 401 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | |
| 402 | 402 | ||
| 403 | 403 | ctx, cancel := context.WithCancel(context.Background()) | |
| @@ -415,7 +415,7 @@ func TestAppRunReleasesDOMWhenActionWorkerCannotStart(t *testing.T) { | |||
| 415 | 415 | func TestAppLoadInitialHistoryRouteRendersErrorState(t *testing.T) { | |
| 416 | 416 | t.Parallel() | |
| 417 | 417 | ||
| 418 | - | history := &fakeHistoryService{err: errors.New("database unavailable")} | |
| 418 | + | history := &recordingHistoryService{err: errors.New("database unavailable")} | |
| 419 | 419 | app := NewApp(Dependencies{ | |
| 420 | 420 | History: history, | |
| 421 | 421 | LocationURI: "dumb://history", | |
| @@ -432,11 +432,11 @@ func TestAppLoadInitialHistoryRouteRendersErrorState(t *testing.T) { | |||
| 432 | 432 | func TestAppLoadInitialHistoryRouteAppliesThemeTokens(t *testing.T) { | |
| 433 | 433 | t.Parallel() | |
| 434 | 434 | ||
| 435 | - | history := &fakeHistoryService{entries: []*entity.HistoryEntry{{ | |
| 435 | + | history := &recordingHistoryService{entries: []*entity.HistoryEntry{{ | |
| 436 | 436 | URL: "https://example.com", | |
| 437 | 437 | Title: "Example", | |
| 438 | 438 | }}} | |
| 439 | - | config := &fakeConfigService{current: dto.SystemviewConfigPayload{ | |
| 439 | + | config := &recordingConfigService{current: dto.SystemviewConfigPayload{ | |
| 440 | 440 | Appearance: dto.WebUIAppearanceConfig{ | |
| 441 | 441 | ColorScheme: "prefer-light", | |
| 442 | 442 | LightPalette: dto.ColorPalette{ | |
| @@ -479,7 +479,7 @@ func TestAppLoadInitialHistoryRouteAppliesThemeTokens(t *testing.T) { | |||
| 479 | 479 | func TestAppLoadInitialFavoritesRouteRendersData(t *testing.T) { | |
| 480 | 480 | t.Parallel() | |
| 481 | 481 | ||
| 482 | - | favorites := &fakeFavoritesService{ | |
| 482 | + | favorites := &recordingFavoritesService{ | |
| 483 | 483 | favorites: []*entity.Favorite{{URL: "https://example.com", Title: "Example"}}, | |
| 484 | 484 | folders: []*entity.Folder{{Name: "Read Later"}}, | |
| 485 | 485 | tags: []*entity.Tag{{Name: "Go"}}, | |
| @@ -519,7 +519,7 @@ func TestAppLoadInitialFavoritesRouteRendersCRUDControls(t *testing.T) { | |||
| 519 | 519 | ||
| 520 | 520 | folderID := entity.FolderID(1) | |
| 521 | 521 | shortcut := 3 | |
| 522 | - | favorites := &fakeFavoritesService{ | |
| 522 | + | favorites := &recordingFavoritesService{ | |
| 523 | 523 | favorites: []*entity.Favorite{{ID: 42, URL: "https://example.com", Title: "Example", FolderID: &folderID, ShortcutKey: &shortcut}}, | |
| 524 | 524 | folders: []*entity.Folder{{ID: folderID, Name: "Read Later", Icon: "📚"}}, | |
| 525 | 525 | tags: []*entity.Tag{{ID: 7, Name: "Go", Color: "#00add8"}}, | |
| @@ -546,8 +546,8 @@ func TestAppLoadInitialFavoritesRouteRendersCRUDControls(t *testing.T) { | |||
| 546 | 546 | } | |
| 547 | 547 | ||
| 548 | 548 | func TestAppHandleFavoriteActionsRefreshesDOM(t *testing.T) { | |
| 549 | - | dom := &fakeDOM{} | |
| 550 | - | favorites := &fakeFavoritesService{ | |
| 549 | + | dom := &recordingDOM{} | |
| 550 | + | favorites := &recordingFavoritesService{ | |
| 551 | 551 | favorites: []*entity.Favorite{{ID: 42, URL: "https://example.com", Title: "Example"}}, | |
| 552 | 552 | folders: []*entity.Folder{{ID: 5, Name: "Read Later"}}, | |
| 553 | 553 | tags: []*entity.Tag{{ID: 7, Name: "Go", Color: "#00add8"}}, | |
| @@ -675,7 +675,7 @@ func TestAppLoadInitialConfigRouteRendersData(t *testing.T) { | |||
| 675 | 675 | }, | |
| 676 | 676 | }, | |
| 677 | 677 | } | |
| 678 | - | service := &fakeConfigService{ | |
| 678 | + | service := &recordingConfigService{ | |
| 679 | 679 | current: config, | |
| 680 | 680 | keybindings: port.KeybindingsConfig{Groups: []port.KeybindingGroup{ | |
| 681 | 681 | { | |
| @@ -756,7 +756,7 @@ func TestAppLoadInitialConfigRouteRendersData(t *testing.T) { | |||
| 756 | 756 | func TestAppLoadInitialConfigRouteRendersEditControls(t *testing.T) { | |
| 757 | 757 | t.Parallel() | |
| 758 | 758 | ||
| 759 | - | service := &fakeConfigService{ | |
| 759 | + | service := &recordingConfigService{ | |
| 760 | 760 | current: testConfigPayload(), | |
| 761 | 761 | keybindings: port.KeybindingsConfig{Groups: []port.KeybindingGroup{{ | |
| 762 | 762 | Mode: "default", | |
| @@ -793,8 +793,8 @@ func TestAppLoadInitialConfigRouteRendersEditControls(t *testing.T) { | |||
| 793 | 793 | } | |
| 794 | 794 | ||
| 795 | 795 | func TestAppHandleConfigActionsRefreshesDOM(t *testing.T) { | |
| 796 | - | dom := &fakeDOM{} | |
| 797 | - | service := &fakeConfigService{ | |
| 796 | + | dom := &recordingDOM{} | |
| 797 | + | service := &recordingConfigService{ | |
| 798 | 798 | current: testConfigPayload(), | |
| 799 | 799 | defaultCfg: testDefaultConfigPayload(), | |
| 800 | 800 | keybindings: port.KeybindingsConfig{Groups: []port.KeybindingGroup{{ | |
| @@ -883,7 +883,7 @@ func TestAppHandleConfigActionsRefreshesDOM(t *testing.T) { | |||
| 883 | 883 | func TestAppRejectsSearchURLsWithoutPlaceholder(t *testing.T) { | |
| 884 | 884 | t.Parallel() | |
| 885 | 885 | ||
| 886 | - | service := &fakeConfigService{current: testConfigPayload(), defaultCfg: testDefaultConfigPayload()} | |
| 886 | + | service := &recordingConfigService{current: testConfigPayload(), defaultCfg: testDefaultConfigPayload()} | |
| 887 | 887 | app := NewApp(Dependencies{Config: service, LocationURI: "dumb://config"}) | |
| 888 | 888 | ||
| 889 | 889 | err := app.handleConfigAction(context.Background(), DOMAction{ | |
| @@ -909,7 +909,7 @@ func TestAppLoadInitialConfigRouteUsesResolvedAppearanceOnlyForShellTheme(t *tes | |||
| 909 | 909 | resolved.DarkPalette.Background = "#222222" | |
| 910 | 910 | resolved.DarkPalette.SurfaceVariant = "#444444" | |
| 911 | 911 | cfg.ResolvedAppearance = &resolved | |
| 912 | - | service := &fakeConfigService{ | |
| 912 | + | service := &recordingConfigService{ | |
| 913 | 913 | current: cfg, | |
| 914 | 914 | keybindings: port.KeybindingsConfig{}, | |
| 915 | 915 | } | |
| @@ -956,8 +956,8 @@ func testDefaultConfigPayload() dto.SystemviewConfigPayload { | |||
| 956 | 956 | return cfg | |
| 957 | 957 | } | |
| 958 | 958 | ||
| 959 | - | // Handwritten fake to capture DOM mounts for stateful render assertions. | |
| 960 | - | type fakeDOM struct { | |
| 959 | + | // Recording fixture to capture DOM mounts for stateful render assertions. | |
| 960 | + | type recordingDOM struct { | |
| 961 | 961 | mu sync.Mutex | |
| 962 | 962 | mounted bool | |
| 963 | 963 | html string | |
| @@ -965,7 +965,7 @@ type fakeDOM struct { | |||
| 965 | 965 | mounts chan string | |
| 966 | 966 | } | |
| 967 | 967 | ||
| 968 | - | func (d *fakeDOM) Mount(markup string) error { | |
| 968 | + | func (d *recordingDOM) Mount(markup string) error { | |
| 969 | 969 | d.mu.Lock() | |
| 970 | 970 | d.mounted = true | |
| 971 | 971 | d.html = markup | |
| @@ -980,19 +980,19 @@ func (d *fakeDOM) Mount(markup string) error { | |||
| 980 | 980 | return nil | |
| 981 | 981 | } | |
| 982 | 982 | ||
| 983 | - | func (d *fakeDOM) Mounted() bool { | |
| 983 | + | func (d *recordingDOM) Mounted() bool { | |
| 984 | 984 | d.mu.Lock() | |
| 985 | 985 | defer d.mu.Unlock() | |
| 986 | 986 | return d.mounted | |
| 987 | 987 | } | |
| 988 | 988 | ||
| 989 | - | func (d *fakeDOM) HTML() string { | |
| 989 | + | func (d *recordingDOM) HTML() string { | |
| 990 | 990 | d.mu.Lock() | |
| 991 | 991 | defer d.mu.Unlock() | |
| 992 | 992 | return d.html | |
| 993 | 993 | } | |
| 994 | 994 | ||
| 995 | - | func (d *fakeDOM) AppendedHTML() string { | |
| 995 | + | func (d *recordingDOM) AppendedHTML() string { | |
| 996 | 996 | d.mu.Lock() | |
| 997 | 997 | defer d.mu.Unlock() | |
| 998 | 998 | return d.appendedHTML | |
| @@ -1018,30 +1018,30 @@ func assertNoMount(t *testing.T, mounts <-chan string, wait time.Duration) { | |||
| 1018 | 1018 | } | |
| 1019 | 1019 | } | |
| 1020 | 1020 | ||
| 1021 | - | type fakeActionDOM struct { | |
| 1022 | - | fakeDOM | |
| 1021 | + | type recordingActionDOM struct { | |
| 1022 | + | recordingDOM | |
| 1023 | 1023 | handler DOMActionHandler | |
| 1024 | 1024 | released bool | |
| 1025 | 1025 | } | |
| 1026 | 1026 | ||
| 1027 | - | func (d *fakeActionDOM) BindActions(handler DOMActionHandler) error { | |
| 1027 | + | func (d *recordingActionDOM) BindActions(handler DOMActionHandler) error { | |
| 1028 | 1028 | d.handler = handler | |
| 1029 | 1029 | return nil | |
| 1030 | 1030 | } | |
| 1031 | 1031 | ||
| 1032 | - | func (d *fakeActionDOM) AppendHistoryTimeline(markup string) error { | |
| 1032 | + | func (d *recordingActionDOM) AppendHistoryTimeline(markup string) error { | |
| 1033 | 1033 | d.mu.Lock() | |
| 1034 | 1034 | d.appendedHTML += markup | |
| 1035 | 1035 | d.mu.Unlock() | |
| 1036 | 1036 | return nil | |
| 1037 | 1037 | } | |
| 1038 | 1038 | ||
| 1039 | - | func (d *fakeActionDOM) Release() { | |
| 1039 | + | func (d *recordingActionDOM) Release() { | |
| 1040 | 1040 | d.released = true | |
| 1041 | 1041 | } | |
| 1042 | 1042 | ||
| 1043 | 1043 | type failingActionDOM struct { | |
| 1044 | - | fakeDOM | |
| 1044 | + | recordingDOM | |
| 1045 | 1045 | bindErr error | |
| 1046 | 1046 | } | |
| 1047 | 1047 | ||
| @@ -1049,8 +1049,8 @@ func (d *failingActionDOM) BindActions(DOMActionHandler) error { | |||
| 1049 | 1049 | return d.bindErr | |
| 1050 | 1050 | } | |
| 1051 | 1051 | ||
| 1052 | - | // Handwritten fake to capture history state for stateful render assertions. | |
| 1053 | - | type fakeHistoryService struct { | |
| 1052 | + | // Recording fixture to capture history state for stateful render assertions. | |
| 1053 | + | type recordingHistoryService struct { | |
| 1054 | 1054 | called bool | |
| 1055 | 1055 | limit int | |
| 1056 | 1056 | offset int | |
| @@ -1080,7 +1080,7 @@ type fakeHistoryService struct { | |||
| 1080 | 1080 | startOnce sync.Once | |
| 1081 | 1081 | } | |
| 1082 | 1082 | ||
| 1083 | - | func (s *fakeHistoryService) Timeline(_ context.Context, limit, offset int) ([]*entity.HistoryEntry, error) { | |
| 1083 | + | func (s *recordingHistoryService) Timeline(_ context.Context, limit, offset int) ([]*entity.HistoryEntry, error) { | |
| 1084 | 1084 | s.called = true | |
| 1085 | 1085 | s.limit = limit | |
| 1086 | 1086 | s.offset = offset | |
| @@ -1093,7 +1093,7 @@ func (s *fakeHistoryService) Timeline(_ context.Context, limit, offset int) ([]* | |||
| 1093 | 1093 | return s.entries, s.err | |
| 1094 | 1094 | } | |
| 1095 | 1095 | ||
| 1096 | - | func (s *fakeHistoryService) TimelineByDomain(_ context.Context, domain string, limit, offset int) ([]*entity.HistoryEntry, error) { | |
| 1096 | + | func (s *recordingHistoryService) TimelineByDomain(_ context.Context, domain string, limit, offset int) ([]*entity.HistoryEntry, error) { | |
| 1097 | 1097 | s.domainCalled = true | |
| 1098 | 1098 | s.domain = domain | |
| 1099 | 1099 | s.limit = limit | |
| @@ -1101,7 +1101,7 @@ func (s *fakeHistoryService) TimelineByDomain(_ context.Context, domain string, | |||
| 1101 | 1101 | return s.entries, s.err | |
| 1102 | 1102 | } | |
| 1103 | 1103 | ||
| 1104 | - | func (s *fakeHistoryService) TimelineWindow(_ context.Context, before time.Time, domain string) (*entity.HistoryWindow, error) { | |
| 1104 | + | func (s *recordingHistoryService) TimelineWindow(_ context.Context, before time.Time, domain string) (*entity.HistoryWindow, error) { | |
| 1105 | 1105 | s.called = true | |
| 1106 | 1106 | s.windowBefore = before | |
| 1107 | 1107 | s.windowDomain = domain | |
| @@ -1120,7 +1120,7 @@ func (s *fakeHistoryService) TimelineWindow(_ context.Context, before time.Time, | |||
| 1120 | 1120 | return &entity.HistoryWindow{Entries: s.entries, Before: before, After: before.Add(-24 * time.Hour)}, nil | |
| 1121 | 1121 | } | |
| 1122 | 1122 | ||
| 1123 | - | func (s *fakeHistoryService) Search(_ context.Context, query string, limit int) ([]*entity.HistoryEntry, error) { | |
| 1123 | + | func (s *recordingHistoryService) Search(_ context.Context, query string, limit int) ([]*entity.HistoryEntry, error) { | |
| 1124 | 1124 | s.searchCalled = true | |
| 1125 | 1125 | s.query = query | |
| 1126 | 1126 | s.searchLimit = limit | |
| @@ -1130,37 +1130,37 @@ func (s *fakeHistoryService) Search(_ context.Context, query string, limit int) | |||
| 1130 | 1130 | return s.entries, nil | |
| 1131 | 1131 | } | |
| 1132 | 1132 | ||
| 1133 | - | func (s *fakeHistoryService) DeleteEntry(_ context.Context, id int64) error { | |
| 1133 | + | func (s *recordingHistoryService) DeleteEntry(_ context.Context, id int64) error { | |
| 1134 | 1134 | s.deletedEntryID = id | |
| 1135 | 1135 | return nil | |
| 1136 | 1136 | } | |
| 1137 | 1137 | ||
| 1138 | - | func (s *fakeHistoryService) DeleteRange(_ context.Context, rangeID string) error { | |
| 1138 | + | func (s *recordingHistoryService) DeleteRange(_ context.Context, rangeID string) error { | |
| 1139 | 1139 | s.deletedRangeID = rangeID | |
| 1140 | 1140 | return nil | |
| 1141 | 1141 | } | |
| 1142 | 1142 | ||
| 1143 | - | func (s *fakeHistoryService) Stats(context.Context) (*entity.HistoryStats, error) { | |
| 1143 | + | func (s *recordingHistoryService) Stats(context.Context) (*entity.HistoryStats, error) { | |
| 1144 | 1144 | s.statsCalled = true | |
| 1145 | 1145 | return s.stats, nil | |
| 1146 | 1146 | } | |
| 1147 | 1147 | ||
| 1148 | - | func (s *fakeHistoryService) Analytics(context.Context) (*entity.HistoryAnalytics, error) { | |
| 1148 | + | func (s *recordingHistoryService) Analytics(context.Context) (*entity.HistoryAnalytics, error) { | |
| 1149 | 1149 | s.analyticsCalled = true | |
| 1150 | 1150 | return nil, nil | |
| 1151 | 1151 | } | |
| 1152 | 1152 | ||
| 1153 | - | func (s *fakeHistoryService) DomainStats(context.Context, int) ([]*entity.DomainStat, error) { | |
| 1153 | + | func (s *recordingHistoryService) DomainStats(context.Context, int) ([]*entity.DomainStat, error) { | |
| 1154 | 1154 | return s.domainStats, nil | |
| 1155 | 1155 | } | |
| 1156 | 1156 | ||
| 1157 | - | func (s *fakeHistoryService) DeleteDomain(_ context.Context, domain string) error { | |
| 1157 | + | func (s *recordingHistoryService) DeleteDomain(_ context.Context, domain string) error { | |
| 1158 | 1158 | s.deletedDomain = domain | |
| 1159 | 1159 | return nil | |
| 1160 | 1160 | } | |
| 1161 | 1161 | ||
| 1162 | - | // Handwritten fake to capture favorites state for stateful render assertions. | |
| 1163 | - | type fakeFavoritesService struct { | |
| 1162 | + | // Recording fixture to capture favorites state for stateful render assertions. | |
| 1163 | + | type recordingFavoritesService struct { | |
| 1164 | 1164 | calledList bool | |
| 1165 | 1165 | calledFolders bool | |
| 1166 | 1166 | calledTags bool | |
| @@ -1184,85 +1184,85 @@ type fakeFavoritesService struct { | |||
| 1184 | 1184 | removedTagID int64 | |
| 1185 | 1185 | } | |
| 1186 | 1186 | ||
| 1187 | - | func (s *fakeFavoritesService) List(context.Context) ([]*entity.Favorite, error) { | |
| 1187 | + | func (s *recordingFavoritesService) List(context.Context) ([]*entity.Favorite, error) { | |
| 1188 | 1188 | s.calledList = true | |
| 1189 | 1189 | return s.favorites, nil | |
| 1190 | 1190 | } | |
| 1191 | 1191 | ||
| 1192 | - | func (s *fakeFavoritesService) ListFolders(context.Context) ([]*entity.Folder, error) { | |
| 1192 | + | func (s *recordingFavoritesService) ListFolders(context.Context) ([]*entity.Folder, error) { | |
| 1193 | 1193 | s.calledFolders = true | |
| 1194 | 1194 | return s.folders, nil | |
| 1195 | 1195 | } | |
| 1196 | 1196 | ||
| 1197 | - | func (s *fakeFavoritesService) ListTags(context.Context) ([]*entity.Tag, error) { | |
| 1197 | + | func (s *recordingFavoritesService) ListTags(context.Context) ([]*entity.Tag, error) { | |
| 1198 | 1198 | s.calledTags = true | |
| 1199 | 1199 | return s.tags, nil | |
| 1200 | 1200 | } | |
| 1201 | 1201 | ||
| 1202 | - | func (s *fakeFavoritesService) CreateFavorite(_ context.Context, input dto.FavoriteCreateInput) (*entity.Favorite, error) { | |
| 1202 | + | func (s *recordingFavoritesService) CreateFavorite(_ context.Context, input dto.FavoriteCreateInput) (*entity.Favorite, error) { | |
| 1203 | 1203 | s.createdFavorite = input | |
| 1204 | 1204 | return &entity.Favorite{ID: 99, URL: input.URL, Title: input.Title, FolderID: input.FolderID}, nil | |
| 1205 | 1205 | } | |
| 1206 | 1206 | ||
| 1207 | - | func (s *fakeFavoritesService) UpdateFavorite(_ context.Context, input dto.FavoriteUpdateInput) (*entity.Favorite, error) { | |
| 1207 | + | func (s *recordingFavoritesService) UpdateFavorite(_ context.Context, input dto.FavoriteUpdateInput) (*entity.Favorite, error) { | |
| 1208 | 1208 | s.updatedFavorite = input | |
| 1209 | 1209 | return &entity.Favorite{ID: input.ID, URL: "https://example.com", Title: input.Title, FaviconURL: input.FaviconURL, FolderID: input.FolderID, ShortcutKey: input.ShortcutKey}, nil | |
| 1210 | 1210 | } | |
| 1211 | 1211 | ||
| 1212 | - | func (s *fakeFavoritesService) DeleteFavorite(_ context.Context, id int64) error { | |
| 1212 | + | func (s *recordingFavoritesService) DeleteFavorite(_ context.Context, id int64) error { | |
| 1213 | 1213 | s.deletedFavorite = id | |
| 1214 | 1214 | return nil | |
| 1215 | 1215 | } | |
| 1216 | 1216 | ||
| 1217 | - | func (s *fakeFavoritesService) SetShortcut(context.Context, int64, *int) error { return nil } | |
| 1217 | + | func (s *recordingFavoritesService) SetShortcut(context.Context, int64, *int) error { return nil } | |
| 1218 | 1218 | ||
| 1219 | - | func (s *fakeFavoritesService) SetFolder(context.Context, int64, *int64) error { return nil } | |
| 1219 | + | func (s *recordingFavoritesService) SetFolder(context.Context, int64, *int64) error { return nil } | |
| 1220 | 1220 | ||
| 1221 | - | func (s *fakeFavoritesService) CreateFolder(_ context.Context, name, icon string, _ *int64) (*entity.Folder, error) { | |
| 1221 | + | func (s *recordingFavoritesService) CreateFolder(_ context.Context, name, icon string, _ *int64) (*entity.Folder, error) { | |
| 1222 | 1222 | s.createdFolder = name | |
| 1223 | 1223 | s.createdFolderIcon = icon | |
| 1224 | 1224 | return &entity.Folder{ID: 77, Name: name, Icon: icon}, nil | |
| 1225 | 1225 | } | |
| 1226 | 1226 | ||
| 1227 | - | func (s *fakeFavoritesService) UpdateFolder(_ context.Context, id int64, _, _ string) error { | |
| 1227 | + | func (s *recordingFavoritesService) UpdateFolder(_ context.Context, id int64, _, _ string) error { | |
| 1228 | 1228 | s.updatedFolderID = id | |
| 1229 | 1229 | return nil | |
| 1230 | 1230 | } | |
| 1231 | 1231 | ||
| 1232 | - | func (s *fakeFavoritesService) DeleteFolder(_ context.Context, id int64) error { | |
| 1232 | + | func (s *recordingFavoritesService) DeleteFolder(_ context.Context, id int64) error { | |
| 1233 | 1233 | s.deletedFolderID = id | |
| 1234 | 1234 | return nil | |
| 1235 | 1235 | } | |
| 1236 | 1236 | ||
| 1237 | - | func (s *fakeFavoritesService) CreateTag(_ context.Context, name, color string) (*entity.Tag, error) { | |
| 1237 | + | func (s *recordingFavoritesService) CreateTag(_ context.Context, name, color string) (*entity.Tag, error) { | |
| 1238 | 1238 | s.createdTag = name | |
| 1239 | 1239 | return &entity.Tag{ID: 55, Name: name, Color: color}, nil | |
| 1240 | 1240 | } | |
| 1241 | 1241 | ||
| 1242 | - | func (s *fakeFavoritesService) UpdateTag(_ context.Context, id int64, _, _ string) error { | |
| 1242 | + | func (s *recordingFavoritesService) UpdateTag(_ context.Context, id int64, _, _ string) error { | |
| 1243 | 1243 | s.updatedTagID = id | |
| 1244 | 1244 | return nil | |
| 1245 | 1245 | } | |
| 1246 | 1246 | ||
| 1247 | - | func (s *fakeFavoritesService) DeleteTag(_ context.Context, id int64) error { | |
| 1247 | + | func (s *recordingFavoritesService) DeleteTag(_ context.Context, id int64) error { | |
| 1248 | 1248 | s.deletedTagID = id | |
| 1249 | 1249 | return nil | |
| 1250 | 1250 | } | |
| 1251 | 1251 | ||
| 1252 | - | func (s *fakeFavoritesService) AssignTag(_ context.Context, favoriteID, tagID int64) error { | |
| 1252 | + | func (s *recordingFavoritesService) AssignTag(_ context.Context, favoriteID, tagID int64) error { | |
| 1253 | 1253 | s.assignedFavoriteID = favoriteID | |
| 1254 | 1254 | s.assignedTagID = tagID | |
| 1255 | 1255 | return nil | |
| 1256 | 1256 | } | |
| 1257 | 1257 | ||
| 1258 | - | func (s *fakeFavoritesService) RemoveTag(_ context.Context, favoriteID, tagID int64) error { | |
| 1258 | + | func (s *recordingFavoritesService) RemoveTag(_ context.Context, favoriteID, tagID int64) error { | |
| 1259 | 1259 | s.removedFavoriteID = favoriteID | |
| 1260 | 1260 | s.removedTagID = tagID | |
| 1261 | 1261 | return nil | |
| 1262 | 1262 | } | |
| 1263 | 1263 | ||
| 1264 | - | // Handwritten fake to capture config state for stateful render assertions. | |
| 1265 | - | type fakeConfigService struct { | |
| 1264 | + | // Recording fixture to capture config state for stateful render assertions. | |
| 1265 | + | type recordingConfigService struct { | |
| 1266 | 1266 | calledCurrent bool | |
| 1267 | 1267 | calledDefault bool | |
| 1268 | 1268 | calledSave bool | |
| @@ -1280,17 +1280,17 @@ type fakeConfigService struct { | |||
| 1280 | 1280 | resetReq port.ResetKeybindingRequest | |
| 1281 | 1281 | } | |
| 1282 | 1282 | ||
| 1283 | - | func (s *fakeConfigService) Current(context.Context) (dto.SystemviewConfigPayload, error) { | |
| 1283 | + | func (s *recordingConfigService) Current(context.Context) (dto.SystemviewConfigPayload, error) { | |
| 1284 | 1284 | s.calledCurrent = true | |
| 1285 | 1285 | return s.current, nil | |
| 1286 | 1286 | } | |
| 1287 | 1287 | ||
| 1288 | - | func (s *fakeConfigService) Default(context.Context) (dto.SystemviewConfigPayload, error) { | |
| 1288 | + | func (s *recordingConfigService) Default(context.Context) (dto.SystemviewConfigPayload, error) { | |
| 1289 | 1289 | s.calledDefault = true | |
| 1290 | 1290 | return s.defaultCfg, nil | |
| 1291 | 1291 | } | |
| 1292 | 1292 | ||
| 1293 | - | func (s *fakeConfigService) Save(_ context.Context, cfg dto.WebUIConfig) error { | |
| 1293 | + | func (s *recordingConfigService) Save(_ context.Context, cfg dto.WebUIConfig) error { | |
| 1294 | 1294 | s.calledSave = true | |
| 1295 | 1295 | s.savedConfig = cfg | |
| 1296 | 1296 | s.current.Appearance = cfg.Appearance | |
| @@ -1308,30 +1308,30 @@ func (s *fakeConfigService) Save(_ context.Context, cfg dto.WebUIConfig) error { | |||
| 1308 | 1308 | return nil | |
| 1309 | 1309 | } | |
| 1310 | 1310 | ||
| 1311 | - | func (s *fakeConfigService) GetKeybindings(context.Context) (port.KeybindingsConfig, error) { | |
| 1311 | + | func (s *recordingConfigService) GetKeybindings(context.Context) (port.KeybindingsConfig, error) { | |
| 1312 | 1312 | s.calledKeybindings = true | |
| 1313 | 1313 | return s.keybindings, nil | |
| 1314 | 1314 | } | |
| 1315 | 1315 | ||
| 1316 | - | func (s *fakeConfigService) SetKeybinding(_ context.Context, req port.SetKeybindingRequest) (port.SetKeybindingResponse, error) { | |
| 1316 | + | func (s *recordingConfigService) SetKeybinding(_ context.Context, req port.SetKeybindingRequest) (port.SetKeybindingResponse, error) { | |
| 1317 | 1317 | s.calledSet = true | |
| 1318 | 1318 | s.setReq = req | |
| 1319 | 1319 | return s.setResp, nil | |
| 1320 | 1320 | } | |
| 1321 | 1321 | ||
| 1322 | - | func (s *fakeConfigService) ResetKeybinding(_ context.Context, req port.ResetKeybindingRequest) error { | |
| 1322 | + | func (s *recordingConfigService) ResetKeybinding(_ context.Context, req port.ResetKeybindingRequest) error { | |
| 1323 | 1323 | s.calledReset = true | |
| 1324 | 1324 | s.resetReq = req | |
| 1325 | 1325 | return nil | |
| 1326 | 1326 | } | |
| 1327 | 1327 | ||
| 1328 | - | func (s *fakeConfigService) ResetAllKeybindings(context.Context) error { | |
| 1328 | + | func (s *recordingConfigService) ResetAllKeybindings(context.Context) error { | |
| 1329 | 1329 | s.calledResetAll = true | |
| 1330 | 1330 | return nil | |
| 1331 | 1331 | } | |
| 1332 | 1332 | ||
| 1333 | 1333 | func TestHandleFavoriteActionValidatesCreateURL(t *testing.T) { | |
| 1334 | - | app := NewApp(Dependencies{Favorites: &fakeFavoritesService{}}) | |
| 1334 | + | app := NewApp(Dependencies{Favorites: &recordingFavoritesService{}}) | |
| 1335 | 1335 | ||
| 1336 | 1336 | err := app.handleFavoriteAction(context.Background(), DOMAction{ | |
| 1337 | 1337 | Action: favoriteActionCreate, | |
| @@ -1356,7 +1356,7 @@ func TestHandleFavoriteActionValidatesCreateURL(t *testing.T) { | |||
| 1356 | 1356 | } | |
| 1357 | 1357 | ||
| 1358 | 1358 | func TestHandleFavoriteCreateAcceptsInternalDumbRoutes(t *testing.T) { | |
| 1359 | - | favorites := &fakeFavoritesService{} | |
| 1359 | + | favorites := &recordingFavoritesService{} | |
| 1360 | 1360 | app := NewApp(Dependencies{Favorites: favorites}) | |
| 1361 | 1361 | ||
| 1362 | 1362 | require.NoError(t, app.handleFavoriteAction(context.Background(), DOMAction{ | |
| @@ -1373,7 +1373,7 @@ func TestHandleFavoriteCreateAcceptsInternalDumbRoutes(t *testing.T) { | |||
| 1373 | 1373 | } | |
| 1374 | 1374 | ||
| 1375 | 1375 | func TestHandleFavoriteCreateRejectsUnknownDumbRoutes(t *testing.T) { | |
| 1376 | - | favorites := &fakeFavoritesService{} | |
| 1376 | + | favorites := &recordingFavoritesService{} | |
| 1377 | 1377 | app := NewApp(Dependencies{Favorites: favorites}) | |
| 1378 | 1378 | ||
| 1379 | 1379 | err := app.handleFavoriteAction(context.Background(), DOMAction{ | |
| @@ -1397,7 +1397,7 @@ func TestParsePositiveInt64DistinguishesParseAndRangeErrors(t *testing.T) { | |||
| 1397 | 1397 | } | |
| 1398 | 1398 | ||
| 1399 | 1399 | func TestHandleFavoriteTagActionsAcceptSnakeCaseIDs(t *testing.T) { | |
| 1400 | - | favorites := &fakeFavoritesService{} | |
| 1400 | + | favorites := &recordingFavoritesService{} | |
| 1401 | 1401 | app := NewApp(Dependencies{Favorites: favorites}) | |
| 1402 | 1402 | ||
| 1403 | 1403 | require.NoError(t, app.handleFavoriteAction(context.Background(), DOMAction{ | |
| @@ -1416,8 +1416,8 @@ func TestHandleFavoriteTagActionsAcceptSnakeCaseIDs(t *testing.T) { | |||
| 1416 | 1416 | } | |
| 1417 | 1417 | ||
| 1418 | 1418 | func TestHandleHistorySearchClearsDomainFilter(t *testing.T) { | |
| 1419 | - | dom := &fakeDOM{} | |
| 1420 | - | history := &fakeHistoryService{searchEntries: []*entity.HistoryEntry{{ID: 7, URL: "https://search.example", Title: "Search result"}}} | |
| 1419 | + | dom := &recordingDOM{} | |
| 1420 | + | history := &recordingHistoryService{searchEntries: []*entity.HistoryEntry{{ID: 7, URL: "https://search.example", Title: "Search result"}}} | |
| 1421 | 1421 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | |
| 1422 | 1422 | app.historyDomainFilter = "example.com" | |
| 1423 | 1423 | ||
| @@ -1433,7 +1433,7 @@ func TestHandleHistorySearchClearsDomainFilter(t *testing.T) { | |||
| 1433 | 1433 | } | |
| 1434 | 1434 | ||
| 1435 | 1435 | func TestAppRunWithContextRejectsNilContext(t *testing.T) { | |
| 1436 | - | app := NewApp(Dependencies{DOM: &fakeDOM{}, LocationURI: "dumb://history"}) | |
| 1436 | + | app := NewApp(Dependencies{DOM: &recordingDOM{}, LocationURI: "dumb://history"}) | |
| 1437 | 1437 | ||
| 1438 | 1438 | var ctx context.Context | |
| 1439 | 1439 | err := app.RunWithContext(ctx) | |
| @@ -1442,7 +1442,7 @@ func TestAppRunWithContextRejectsNilContext(t *testing.T) { | |||
| 1442 | 1442 | } | |
| 1443 | 1443 | ||
| 1444 | 1444 | func TestAppRunWithContextRequiresDOMBeforeLoading(t *testing.T) { | |
| 1445 | - | history := &fakeHistoryService{entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com"}}} | |
| 1445 | + | history := &recordingHistoryService{entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com"}}} | |
| 1446 | 1446 | app := NewApp(Dependencies{History: history, LocationURI: "dumb://history"}) | |
| 1447 | 1447 | ||
| 1448 | 1448 | err := app.RunWithContext(context.Background()) | |
| @@ -1453,9 +1453,9 @@ func TestAppRunWithContextRequiresDOMBeforeLoading(t *testing.T) { | |||
| 1453 | 1453 | ||
| 1454 | 1454 | func TestHandleActionsRejectUnknownAction(t *testing.T) { | |
| 1455 | 1455 | app := NewApp(Dependencies{ | |
| 1456 | - | History: &fakeHistoryService{}, | |
| 1457 | - | Favorites: &fakeFavoritesService{}, | |
| 1458 | - | Config: &fakeConfigService{current: testConfigPayload()}, | |
| 1456 | + | History: &recordingHistoryService{}, | |
| 1457 | + | Favorites: &recordingFavoritesService{}, | |
| 1458 | + | Config: &recordingConfigService{current: testConfigPayload()}, | |
| 1459 | 1459 | }) | |
| 1460 | 1460 | ||
| 1461 | 1461 | require.ErrorContains(t, app.handleHistoryAction(context.Background(), DOMAction{Action: "history.unknown"}), "unknown history action") | |
| @@ -1464,7 +1464,7 @@ func TestHandleActionsRejectUnknownAction(t *testing.T) { | |||
| 1464 | 1464 | } | |
| 1465 | 1465 | ||
| 1466 | 1466 | func TestSavePerformanceConfigRejectsOutOfRangeValues(t *testing.T) { | |
| 1467 | - | service := &fakeConfigService{current: testConfigPayload()} | |
| 1467 | + | service := &recordingConfigService{current: testConfigPayload()} | |
| 1468 | 1468 | app := NewApp(Dependencies{Config: service}) | |
| 1469 | 1469 | ||
| 1470 | 1470 | err := app.handleConfigAction(context.Background(), DOMAction{ | |