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 | template-data: | 2 | template-data: | ||
| 3 | unroll-variadic: true | 3 | unroll-variadic: true | ||
| 4 | packages: | 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 | github.com/bnema/dumber/internal/application/port: | 46 | github.com/bnema/dumber/internal/application/port: | ||
| 6 | config: | 47 | config: | ||
| 7 | dir: '{{.InterfaceDir}}/mocks' | 48 | dir: '{{.InterfaceDir}}/mocks' | ||
| @@ -58,6 +99,12 @@ packages: | |||||
| 58 | DesktopIntegration: {} | 99 | DesktopIntegration: {} | ||
| 59 | DownloadEventHandler: {} | 100 | DownloadEventHandler: {} | ||
| 60 | DownloadPreparer: {} | 101 | DownloadPreparer: {} | ||
| 102 | + | FaviconBlobStore: {} | |||
| 103 | + | FaviconFetcher: {} | |||
| 104 | + | FaviconImageConverter: {} | |||
| 105 | + | FaviconInvalidators: {} | |||
| 106 | + | FaviconRefreshScheduler: {} | |||
| 107 | + | FaviconRepository: {} | |||
| 61 | FaviconService: {} | 108 | FaviconService: {} | ||
| 62 | FileSystem: {} | 109 | FileSystem: {} | ||
| 63 | FilterManager: {} | 110 | FilterManager: {} | ||
| @@ -134,3 +181,19 @@ packages: | |||||
| 134 | SpinnerWidget: {} | 181 | SpinnerWidget: {} | ||
| 135 | Widget: {} | 182 | Widget: {} | ||
| 136 | WidgetFactory: {} | 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 | func TestNewBridgeApp_WiresHistoryService(t *testing.T) { | 17 | func TestNewBridgeApp_WiresHistoryService(t *testing.T) { | ||
| 18 | t.Parallel() | 18 | t.Parallel() | ||
| 19 | 19 | ||||
| 20 | - | bridge := &fakeBridgeService{historyEntries: []*entity.HistoryEntry{{Title: "Example", URL: "https://example.com"}}} | 20 | + | bridge := &bridgeServiceRecorder{historyEntries: []*entity.HistoryEntry{{Title: "Example", URL: "https://example.com"}}} |
| 21 | - | app := newBridgeApp(&fakeDOM{}, "dumb://history", bridge) | 21 | + | app := newBridgeApp(&recordingDOM{}, "dumb://history", bridge) |
| 22 | 22 | ||||
| 23 | require.NoError(t, app.LoadInitial(context.Background())) | 23 | require.NoError(t, app.LoadInitial(context.Background())) | ||
| 24 | assert.True(t, bridge.calledHistory.Load()) | 24 | assert.True(t, bridge.calledHistory.Load()) | ||
| @@ -30,7 +30,7 @@ func TestNewBridgeApp_WiresHistoryService(t *testing.T) { | |||||
| 30 | func TestNewBridgeApp_UsesCurrentConfigForNonConfigRoutes(t *testing.T) { | 30 | func TestNewBridgeApp_UsesCurrentConfigForNonConfigRoutes(t *testing.T) { | ||
| 31 | t.Parallel() | 31 | t.Parallel() | ||
| 32 | 32 | ||||
| 33 | - | bridge := &fakeBridgeService{ | 33 | + | bridge := &bridgeServiceRecorder{ |
| 34 | historyEntries: []*entity.HistoryEntry{{Title: "Example", URL: "https://example.com"}}, | 34 | historyEntries: []*entity.HistoryEntry{{Title: "Example", URL: "https://example.com"}}, | ||
| 35 | currentConfig: dto.SystemviewConfigPayload{ | 35 | currentConfig: dto.SystemviewConfigPayload{ | ||
| 36 | Appearance: dto.WebUIAppearanceConfig{ | 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 | app := newBridgeApp(dom, "dumb://history", bridge) | 51 | app := newBridgeApp(dom, "dumb://history", bridge) | ||
| 52 | 52 | ||||
| 53 | require.NoError(t, app.Run()) | 53 | require.NoError(t, app.Run()) | ||
| @@ -62,12 +62,12 @@ func TestNewBridgeApp_UsesCurrentConfigForNonConfigRoutes(t *testing.T) { | |||||
| 62 | func TestNewBridgeApp_WiresFavoritesService(t *testing.T) { | 62 | func TestNewBridgeApp_WiresFavoritesService(t *testing.T) { | ||
| 63 | t.Parallel() | 63 | t.Parallel() | ||
| 64 | 64 | ||||
| 65 | - | bridge := &fakeBridgeService{ | 65 | + | bridge := &bridgeServiceRecorder{ |
| 66 | favorites: []*entity.Favorite{{Title: "Example", URL: "https://example.com"}}, | 66 | favorites: []*entity.Favorite{{Title: "Example", URL: "https://example.com"}}, | ||
| 67 | folders: []*entity.Folder{{Name: "Read Later"}}, | 67 | folders: []*entity.Folder{{Name: "Read Later"}}, | ||
| 68 | tags: []*entity.Tag{{Name: "Go"}}, | 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 | require.NoError(t, app.LoadInitial(context.Background())) | 72 | require.NoError(t, app.LoadInitial(context.Background())) | ||
| 73 | assert.False(t, bridge.calledHistory.Load()) | 73 | assert.False(t, bridge.calledHistory.Load()) | ||
| @@ -79,11 +79,11 @@ func TestNewBridgeApp_WiresFavoritesService(t *testing.T) { | |||||
| 79 | func TestNewBridgeApp_WiresConfigService(t *testing.T) { | 79 | func TestNewBridgeApp_WiresConfigService(t *testing.T) { | ||
| 80 | t.Parallel() | 80 | t.Parallel() | ||
| 81 | 81 | ||||
| 82 | - | bridge := &fakeBridgeService{ | 82 | + | bridge := &bridgeServiceRecorder{ |
| 83 | currentConfig: dto.SystemviewConfigPayload{EngineType: "webkit"}, | 83 | currentConfig: dto.SystemviewConfigPayload{EngineType: "webkit"}, | ||
| 84 | keybindings: port.KeybindingsConfig{Groups: []port.KeybindingGroup{{DisplayName: "Default"}}}, | 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 | require.NoError(t, app.LoadInitial(context.Background())) | 88 | require.NoError(t, app.LoadInitial(context.Background())) | ||
| 89 | assert.False(t, bridge.calledHistory.Load()) | 89 | assert.False(t, bridge.calledHistory.Load()) | ||
| @@ -92,12 +92,12 @@ func TestNewBridgeApp_WiresConfigService(t *testing.T) { | |||||
| 92 | assert.True(t, bridge.calledKeybindings.Load()) | 92 | assert.True(t, bridge.calledKeybindings.Load()) | ||
| 93 | } | 93 | } | ||
| 94 | 94 | ||||
| 95 | - | type fakeDOM struct { | 95 | + | type recordingDOM struct { |
| 96 | html string | 96 | html string | ||
| 97 | mounts chan string | 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 | f.html = html | 101 | f.html = html | ||
| 102 | if f.mounts != nil { | 102 | if f.mounts != nil { | ||
| 103 | select { | 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 | // and keybindings boundaries for the composite bootstrap assertions. | 135 | // and keybindings boundaries for the composite bootstrap assertions. | ||
| 136 | - | type fakeBridgeService struct { | 136 | + | type bridgeServiceRecorder struct { |
| 137 | calledHistory atomic.Bool | 137 | calledHistory atomic.Bool | ||
| 138 | calledFavorites atomic.Bool | 138 | calledFavorites atomic.Bool | ||
| 139 | calledConfig atomic.Bool | 139 | calledConfig atomic.Bool | ||
| @@ -147,112 +147,112 @@ type fakeBridgeService struct { | |||||
| 147 | keybindings port.KeybindingsConfig | 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 | f.calledHistory.Store(true) | 151 | f.calledHistory.Store(true) | ||
| 152 | return f.historyEntries, nil | 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 | f.calledHistory.Store(true) | 156 | f.calledHistory.Store(true) | ||
| 157 | return f.historyEntries, nil | 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 | f.calledHistory.Store(true) | 161 | f.calledHistory.Store(true) | ||
| 162 | return &entity.HistoryWindow{Entries: f.historyEntries, Before: before, After: before.Add(-24 * time.Hour)}, nil | 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 | return nil, nil | 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 | return nil, nil | 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 | return nil, nil | 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 | return nil, nil | 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 | f.calledFavorites.Store(true) | 188 | f.calledFavorites.Store(true) | ||
| 189 | return f.favorites, nil | 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 | return nil, nil | 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 | return nil, nil | 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 | return f.folders, nil | 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 | return f.tags, nil | 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 | return nil, nil | 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 | return nil, nil | 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 | f.calledConfig.Store(true) | 235 | f.calledConfig.Store(true) | ||
| 236 | return f.currentConfig, nil | 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 | return dto.SystemviewConfigPayload{}, nil | 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 | f.calledKeybindings.Store(true) | 246 | f.calledKeybindings.Store(true) | ||
| 247 | return f.keybindings, nil | 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 | return port.SetKeybindingResponse{}, nil | 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 | return nil | 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 | "time" | 7 | "time" | ||
| 8 | 8 | ||||
| 9 | appport "github.com/bnema/dumber/internal/application/port" | 9 | appport "github.com/bnema/dumber/internal/application/port" | ||
| 10 | + | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | |||
| 10 | "github.com/bnema/dumber/internal/domain/favicon" | 11 | "github.com/bnema/dumber/internal/domain/favicon" | ||
| 12 | + | "github.com/stretchr/testify/mock" | |||
| 11 | ) | 13 | ) | ||
| 12 | 14 | ||||
| 13 | func TestFaviconResolveExactHit(t *testing.T) { | 15 | func TestFaviconResolveExactHit(t *testing.T) { | ||
| 14 | - | fx := newFaviconFixture() | 16 | + | fx := newFaviconFixture(t) |
| 15 | fx.seed("docs.example.com", []byte("png32"), false) | 17 | fx.seed("docs.example.com", []byte("png32"), false) | ||
| 16 | got, err := fx.uc.Resolve(context.Background(), "https://docs.example.com/a", 32, ResolveOptions{Purpose: ResolvePurposeUI}) | 18 | got, err := fx.uc.Resolve(context.Background(), "https://docs.example.com/a", 32, ResolveOptions{Purpose: ResolvePurposeUI}) | ||
| 17 | if err != nil { | 19 | if err != nil { | ||
| @@ -23,7 +25,7 @@ func TestFaviconResolveExactHit(t *testing.T) { | |||||
| 23 | } | 25 | } | ||
| 24 | 26 | ||||
| 25 | func TestFaviconResolveParentFallback(t *testing.T) { | 27 | func TestFaviconResolveParentFallback(t *testing.T) { | ||
| 26 | - | fx := newFaviconFixture() | 28 | + | fx := newFaviconFixture(t) |
| 27 | fx.seed("example.com", []byte("parent"), false) | 29 | fx.seed("example.com", []byte("parent"), false) | ||
| 28 | got, err := fx.uc.Resolve(context.Background(), "https://docs.example.com/a", 32, ResolveOptions{Purpose: ResolvePurposeUI}) | 30 | got, err := fx.uc.Resolve(context.Background(), "https://docs.example.com/a", 32, ResolveOptions{Purpose: ResolvePurposeUI}) | ||
| 29 | if err != nil { | 31 | if err != nil { | ||
| @@ -35,7 +37,7 @@ func TestFaviconResolveParentFallback(t *testing.T) { | |||||
| 35 | } | 37 | } | ||
| 36 | 38 | ||||
| 37 | func TestFaviconResolvePrefersParentPathFallbackBeforeDomain(t *testing.T) { | 39 | func TestFaviconResolvePrefersParentPathFallbackBeforeDomain(t *testing.T) { | ||
| 38 | - | fx := newFaviconFixture() | 40 | + | fx := newFaviconFixture(t) |
| 39 | fx.seed("github.com", []byte("github-default"), false) | 41 | fx.seed("github.com", []byte("github-default"), false) | ||
| 40 | fx.seed("github.com/bnema/gordon", []byte("repo-default"), false) | 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 | func TestFaviconResolvePathSpecificDoesNotLeakToParentOrSiblings(t *testing.T) { | 53 | func TestFaviconResolvePathSpecificDoesNotLeakToParentOrSiblings(t *testing.T) { | ||
| 52 | - | fx := newFaviconFixture() | 54 | + | fx := newFaviconFixture(t) |
| 53 | fx.seed("github.com", []byte("github-default"), false) | 55 | fx.seed("github.com", []byte("github-default"), false) | ||
| 54 | 56 | ||||
| 55 | if _, err := fx.uc.Observe( | 57 | if _, err := fx.uc.Observe( | ||
| @@ -91,7 +93,7 @@ func TestFaviconResolvePathSpecificDoesNotLeakToParentOrSiblings(t *testing.T) { | |||||
| 91 | } | 93 | } | ||
| 92 | 94 | ||||
| 93 | func TestFaviconResolveRepairsOrphanCanonicalBlobMetadata(t *testing.T) { | 95 | func TestFaviconResolveRepairsOrphanCanonicalBlobMetadata(t *testing.T) { | ||
| 94 | - | fx := newFaviconFixture() | 96 | + | fx := newFaviconFixture(t) |
| 95 | fx.blobs.png["example.com"] = []byte("png") | 97 | fx.blobs.png["example.com"] = []byte("png") | ||
| 96 | fx.blobs.sized["example.com"] = map[int][]byte{SystemviewIconSize: []byte("png32")} | 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 | func TestFaviconResolveRepairOrphanWithoutConverterReturnsMiss(t *testing.T) { | 116 | func TestFaviconResolveRepairOrphanWithoutConverterReturnsMiss(t *testing.T) { | ||
| 115 | - | fx := newFaviconFixture() | 117 | + | fx := newFaviconFixture(t) |
| 116 | fx.uc.converter = nil | 118 | fx.uc.converter = nil | ||
| 117 | fx.blobs.original["example.com"] = []byte("original") | 119 | fx.blobs.original["example.com"] = []byte("original") | ||
| 118 | fx.blobs.contentTypes["example.com"] = "image/png" | 120 | fx.blobs.contentTypes["example.com"] = "image/png" | ||
| @@ -124,7 +126,7 @@ func TestFaviconResolveRepairOrphanWithoutConverterReturnsMiss(t *testing.T) { | |||||
| 124 | } | 126 | } | ||
| 125 | 127 | ||||
| 126 | func TestFaviconResolveStaleSchedulesDedupedBackgroundRefresh(t *testing.T) { | 128 | func TestFaviconResolveStaleSchedulesDedupedBackgroundRefresh(t *testing.T) { | ||
| 127 | - | fx := newFaviconFixture() | 129 | + | fx := newFaviconFixture(t) |
| 128 | fx.seed("example.com", []byte("old"), true) | 130 | fx.seed("example.com", []byte("old"), true) | ||
| 129 | for i := 0; i < 2; i++ { | 131 | for i := 0; i < 2; i++ { | ||
| 130 | got, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{ScheduleBackgroundRefresh: true}) | 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 | func TestFaviconResolveStaleExactRefreshIgnoresFreshParent(t *testing.T) { | 148 | func TestFaviconResolveStaleExactRefreshIgnoresFreshParent(t *testing.T) { | ||
| 147 | - | fx := newFaviconFixture() | 149 | + | fx := newFaviconFixture(t) |
| 148 | fx.seed("docs.example.com", []byte("old-exact"), true) | 150 | fx.seed("docs.example.com", []byte("old-exact"), true) | ||
| 149 | fx.seed("example.com", []byte("fresh-parent"), false) | 151 | fx.seed("example.com", []byte("fresh-parent"), false) | ||
| 150 | got, err := fx.uc.Resolve( | 152 | got, err := fx.uc.Resolve( | ||
| @@ -166,7 +168,7 @@ func TestFaviconResolveStaleExactRefreshIgnoresFreshParent(t *testing.T) { | |||||
| 166 | } | 168 | } | ||
| 167 | 169 | ||||
| 168 | func TestFaviconObserveSameHashUpdatesMetadataOnly(t *testing.T) { | 170 | func TestFaviconObserveSameHashUpdatesMetadataOnly(t *testing.T) { | ||
| 169 | - | fx := newFaviconFixture() | 171 | + | fx := newFaviconFixture(t) |
| 170 | fx.seedOriginal([]byte("same")) | 172 | fx.seedOriginal([]byte("same")) | ||
| 171 | if _, err := fx.uc.Observe(context.Background(), "https://example.com", "https://cdn.test/icon.png", []byte("same"), favicon.SourceEngine, "image/png"); err != nil { | 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 | t.Fatal(err) | 174 | t.Fatal(err) | ||
| @@ -180,7 +182,7 @@ func TestFaviconObserveSameHashUpdatesMetadataOnly(t *testing.T) { | |||||
| 180 | } | 182 | } | ||
| 181 | 183 | ||||
| 182 | func TestFaviconObserveChangedHashRegeneratesAndInvalidates(t *testing.T) { | 184 | func TestFaviconObserveChangedHashRegeneratesAndInvalidates(t *testing.T) { | ||
| 183 | - | fx := newFaviconFixture() | 185 | + | fx := newFaviconFixture(t) |
| 184 | fx.seedOriginal([]byte("old")) | 186 | fx.seedOriginal([]byte("old")) | ||
| 185 | if _, err := fx.uc.Observe(context.Background(), "https://example.com", "https://cdn.test/icon.png", []byte("new"), favicon.SourceEngine, "image/png"); err != nil { | 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 | t.Fatal(err) | 188 | t.Fatal(err) | ||
| @@ -197,7 +199,7 @@ func TestFaviconObserveChangedHashRegeneratesAndInvalidates(t *testing.T) { | |||||
| 197 | } | 199 | } | ||
| 198 | 200 | ||||
| 199 | func TestFaviconObserveWithoutConverterStoresOriginalOnly(t *testing.T) { | 201 | func TestFaviconObserveWithoutConverterStoresOriginalOnly(t *testing.T) { | ||
| 200 | - | fx := newFaviconFixture() | 202 | + | fx := newFaviconFixture(t) |
| 201 | fx.uc.converter = nil | 203 | fx.uc.converter = nil | ||
| 202 | if _, err := fx.uc.Observe(context.Background(), "https://example.com", "https://example.com/icon.png", []byte("raw"), favicon.SourceEngine, "image/png"); err != nil { | 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 | t.Fatal(err) | 205 | t.Fatal(err) | ||
| @@ -211,7 +213,7 @@ func TestFaviconObserveWithoutConverterStoresOriginalOnly(t *testing.T) { | |||||
| 211 | } | 213 | } | ||
| 212 | 214 | ||||
| 213 | func TestFaviconResolveMissingFetchesOnlyWhenBlockingAllowed(t *testing.T) { | 215 | func TestFaviconResolveMissingFetchesOnlyWhenBlockingAllowed(t *testing.T) { | ||
| 214 | - | fx := newFaviconFixture() | 216 | + | fx := newFaviconFixture(t) |
| 215 | if _, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{}); !errors.Is(err, ErrFaviconMiss) { | 217 | if _, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{}); !errors.Is(err, ErrFaviconMiss) { | ||
| 216 | t.Fatalf("default resolve err=%v", err) | 218 | t.Fatalf("default resolve err=%v", err) | ||
| 217 | } | 219 | } | ||
| @@ -228,7 +230,7 @@ func TestFaviconResolveMissingFetchesOnlyWhenBlockingAllowed(t *testing.T) { | |||||
| 228 | } | 230 | } | ||
| 229 | 231 | ||||
| 230 | func TestFaviconRefreshIfStaleUsesParentFallbackBeforeFetching(t *testing.T) { | 232 | func TestFaviconRefreshIfStaleUsesParentFallbackBeforeFetching(t *testing.T) { | ||
| 231 | - | fx := newFaviconFixture() | 233 | + | fx := newFaviconFixture(t) |
| 232 | fx.seed("example.com", []byte("parent"), false) | 234 | fx.seed("example.com", []byte("parent"), false) | ||
| 233 | if err := fx.uc.RefreshIfStale(context.Background(), "https://docs.example.com/page"); err != nil { | 235 | if err := fx.uc.RefreshIfStale(context.Background(), "https://docs.example.com/page"); err != nil { | ||
| 234 | t.Fatal(err) | 236 | t.Fatal(err) | ||
| @@ -239,7 +241,7 @@ func TestFaviconRefreshIfStaleUsesParentFallbackBeforeFetching(t *testing.T) { | |||||
| 239 | } | 241 | } | ||
| 240 | 242 | ||||
| 241 | func TestFaviconUseCasePropagatesRepositoryErrors(t *testing.T) { | 243 | func TestFaviconUseCasePropagatesRepositoryErrors(t *testing.T) { | ||
| 242 | - | fx := newFaviconFixture() | 244 | + | fx := newFaviconFixture(t) |
| 243 | fx.repo.err = errors.New("database unavailable") | 245 | fx.repo.err = errors.New("database unavailable") | ||
| 244 | if _, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{}); !errors.Is(err, fx.repo.err) { | 246 | if _, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{}); !errors.Is(err, fx.repo.err) { | ||
| 245 | t.Fatalf("Resolve err=%v, want %v", err, fx.repo.err) | 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 | func TestFaviconResolvePropagatesBlobErrors(t *testing.T) { | 257 | func TestFaviconResolvePropagatesBlobErrors(t *testing.T) { | ||
| 256 | - | fx := newFaviconFixture() | 258 | + | fx := newFaviconFixture(t) |
| 257 | fx.seed("example.com", []byte("cached"), false) | 259 | fx.seed("example.com", []byte("cached"), false) | ||
| 258 | fx.blobs.err = errors.New("disk unavailable") | 260 | fx.blobs.err = errors.New("disk unavailable") | ||
| 259 | if _, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{}); !errors.Is(err, fx.blobs.err) { | 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 | func TestFaviconResolveBlockingRefreshesWhenMetadataFreshButBlobMissing(t *testing.T) { | 266 | func TestFaviconResolveBlockingRefreshesWhenMetadataFreshButBlobMissing(t *testing.T) { | ||
| 265 | - | fx := newFaviconFixture() | 267 | + | fx := newFaviconFixture(t) |
| 266 | fx.repo.byKey["example.com"] = &favicon.Metadata{Key: "example.com", PageURL: "https://example.com", ContentHash: favicon.Hash([]byte("old")), LastCheckedAt: fx.now} | 268 | fx.repo.byKey["example.com"] = &favicon.Metadata{Key: "example.com", PageURL: "https://example.com", ContentHash: favicon.Hash([]byte("old")), LastCheckedAt: fx.now} | ||
| 267 | got, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{AllowBlockingRefresh: true}) | 269 | got, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{AllowBlockingRefresh: true}) | ||
| 268 | if err != nil { | 270 | if err != nil { | ||
| @@ -274,7 +276,7 @@ func TestFaviconResolveBlockingRefreshesWhenMetadataFreshButBlobMissing(t *testi | |||||
| 274 | } | 276 | } | ||
| 275 | 277 | ||||
| 276 | func TestFaviconResolveBlockingPropagatesFetchErrors(t *testing.T) { | 278 | func TestFaviconResolveBlockingPropagatesFetchErrors(t *testing.T) { | ||
| 277 | - | fx := newFaviconFixture() | 279 | + | fx := newFaviconFixture(t) |
| 278 | fx.fetcher.err = errors.New("network unavailable") | 280 | fx.fetcher.err = errors.New("network unavailable") | ||
| 279 | if _, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{AllowBlockingRefresh: true}); !errors.Is(err, fx.fetcher.err) { | 281 | if _, err := fx.uc.Resolve(context.Background(), "https://example.com", 32, ResolveOptions{AllowBlockingRefresh: true}); !errors.Is(err, fx.fetcher.err) { | ||
| 280 | t.Fatalf("Resolve err=%v, want %v", err, fx.fetcher.err) | 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 | func TestFaviconRefreshFromIconURLsUsesPageURLKey(t *testing.T) { | 286 | func TestFaviconRefreshFromIconURLsUsesPageURLKey(t *testing.T) { | ||
| 285 | - | fx := newFaviconFixture() | 287 | + | fx := newFaviconFixture(t) |
| 286 | if err := fx.uc.RefreshFromIconURLs(context.Background(), "https://docs.example.com/page", []string{"https://cdn.example.net/icon.png"}); err != nil { | 288 | if err := fx.uc.RefreshFromIconURLs(context.Background(), "https://docs.example.com/page", []string{"https://cdn.example.net/icon.png"}); err != nil { | ||
| 287 | t.Fatal(err) | 289 | t.Fatal(err) | ||
| 288 | } | 290 | } | ||
| @@ -295,8 +297,8 @@ func TestFaviconRefreshFromIconURLsUsesPageURLKey(t *testing.T) { | |||||
| 295 | } | 297 | } | ||
| 296 | 298 | ||||
| 297 | func TestFaviconRefreshStoresDuckDuckGoFallbackUnderHostKey(t *testing.T) { | 299 | func TestFaviconRefreshStoresDuckDuckGoFallbackUnderHostKey(t *testing.T) { | ||
| 298 | - | fx := newFaviconFixture() | 300 | + | fx := newFaviconFixture(t) |
| 299 | - | fx.fetcher.responses = map[string]fakeFetchedIcon{ | 301 | + | fx.fetcher.responses = map[string]faviconFetchResponse{ |
| 300 | "https://example.com/favicon.ico": { | 302 | "https://example.com/favicon.ico": { | ||
| 301 | bytes: []byte("host-fallback"), | 303 | bytes: []byte("host-fallback"), | ||
| 302 | contentType: "image/png", | 304 | contentType: "image/png", | ||
| @@ -316,8 +318,8 @@ func TestFaviconRefreshStoresDuckDuckGoFallbackUnderHostKey(t *testing.T) { | |||||
| 316 | } | 318 | } | ||
| 317 | 319 | ||||
| 318 | func TestFaviconRefreshRejectsResolvedKeyOutsideCandidateHierarchy(t *testing.T) { | 320 | func TestFaviconRefreshRejectsResolvedKeyOutsideCandidateHierarchy(t *testing.T) { | ||
| 319 | - | fx := newFaviconFixture() | 321 | + | fx := newFaviconFixture(t) |
| 320 | - | fx.fetcher.responses = map[string]fakeFetchedIcon{ | 322 | + | fx.fetcher.responses = map[string]faviconFetchResponse{ |
| 321 | "https://example.com/favicon.ico": { | 323 | "https://example.com/favicon.ico": { | ||
| 322 | bytes: []byte("bad-fallback"), | 324 | bytes: []byte("bad-fallback"), | ||
| 323 | contentType: "image/png", | 325 | contentType: "image/png", | ||
| @@ -335,8 +337,8 @@ func TestFaviconRefreshRejectsResolvedKeyOutsideCandidateHierarchy(t *testing.T) | |||||
| 335 | } | 337 | } | ||
| 336 | 338 | ||||
| 337 | func TestFaviconRefreshRejectsFetcherPageURLOutsideRequestedHierarchy(t *testing.T) { | 339 | func TestFaviconRefreshRejectsFetcherPageURLOutsideRequestedHierarchy(t *testing.T) { | ||
| 338 | - | fx := newFaviconFixture() | 340 | + | fx := newFaviconFixture(t) |
| 339 | - | fx.fetcher.responses = map[string]fakeFetchedIcon{ | 341 | + | fx.fetcher.responses = map[string]faviconFetchResponse{ |
| 340 | "https://example.com/favicon.ico": { | 342 | "https://example.com/favicon.ico": { | ||
| 341 | bytes: []byte("bad-page-url"), | 343 | bytes: []byte("bad-page-url"), | ||
| 342 | contentType: "image/png", | 344 | contentType: "image/png", | ||
| @@ -355,8 +357,8 @@ func TestFaviconRefreshRejectsFetcherPageURLOutsideRequestedHierarchy(t *testing | |||||
| 355 | } | 357 | } | ||
| 356 | 358 | ||||
| 357 | func TestFaviconRefreshFromIconURLsSkipsUnsupportedCandidates(t *testing.T) { | 359 | func TestFaviconRefreshFromIconURLsSkipsUnsupportedCandidates(t *testing.T) { | ||
| 358 | - | fx := newFaviconFixture() | 360 | + | fx := newFaviconFixture(t) |
| 359 | - | fx.fetcher.responses = map[string]fakeFetchedIcon{ | 361 | + | fx.fetcher.responses = map[string]faviconFetchResponse{ |
| 360 | "https://cdn.example.net/icon.svg": {bytes: []byte("svg"), contentType: "image/svg+xml"}, | 362 | "https://cdn.example.net/icon.svg": {bytes: []byte("svg"), contentType: "image/svg+xml"}, | ||
| 361 | "https://cdn.example.net/icon.png": {bytes: []byte("png"), contentType: "image/png"}, | 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 | func TestFaviconObserveConversionErrorDoesNotMutateExistingAssets(t *testing.T) { | 389 | func TestFaviconObserveConversionErrorDoesNotMutateExistingAssets(t *testing.T) { | ||
| 388 | - | fx := newFaviconFixture() | 390 | + | fx := newFaviconFixture(t) |
| 389 | fx.seedOriginal([]byte("old")) | 391 | fx.seedOriginal([]byte("old")) | ||
| 390 | fx.blobs.png["example.com"] = []byte("old-png") | 392 | fx.blobs.png["example.com"] = []byte("old-png") | ||
| 391 | fx.blobs.sized["example.com"] = map[int][]byte{32: []byte("old-32")} | 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 | func TestFaviconEnsureSizedPropagatesReadSizedErrors(t *testing.T) { | 407 | func TestFaviconEnsureSizedPropagatesReadSizedErrors(t *testing.T) { | ||
| 406 | - | fx := newFaviconFixture() | 408 | + | fx := newFaviconFixture(t) |
| 407 | fx.seedOriginal([]byte("old")) | 409 | fx.seedOriginal([]byte("old")) | ||
| 408 | fx.blobs.readSizedErr = errors.New("sized store unavailable") | 410 | fx.blobs.readSizedErr = errors.New("sized store unavailable") | ||
| 409 | if err := fx.uc.EnsureSized(context.Background(), "example.com", 32); !errors.Is(err, fx.blobs.readSizedErr) { | 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 | type faviconFixture struct { | 419 | type faviconFixture struct { | ||
| 418 | - | repo *fakeFaviconRepo | 420 | + | repo *faviconRepoState |
| 419 | - | blobs *fakeBlobStore | 421 | + | blobs *faviconBlobStoreState |
| 420 | - | converter *fakeConverter | 422 | + | converter *faviconConverterState |
| 421 | - | scheduler *fakeScheduler | 423 | + | scheduler *faviconSchedulerState |
| 422 | - | invalidators *fakeInvalidators | 424 | + | invalidators *faviconInvalidatorsState |
| 423 | - | fetcher *fakeFetcher | 425 | + | fetcher *faviconFetcherState |
| 424 | uc *FaviconUseCase | 426 | uc *FaviconUseCase | ||
| 425 | now time.Time | 427 | now time.Time | ||
| 426 | } | 428 | } | ||
| @@ -429,29 +431,98 @@ type faviconTestContextKey string | |||||
| 429 | 431 | ||||
| 430 | const faviconFixtureBackgroundKey faviconTestContextKey = "bg" | 432 | const faviconFixtureBackgroundKey faviconTestContextKey = "bg" | ||
| 431 | 433 | ||||
| 432 | - | func newFaviconFixture() *faviconFixture { | 434 | + | func newFaviconFixture(t *testing.T) *faviconFixture { |
| 435 | + | t.Helper() | |||
| 436 | + | ||||
| 433 | now := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) | 437 | now := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) | ||
| 434 | fx := &faviconFixture{ | 438 | fx := &faviconFixture{ | ||
| 435 | - | repo: newFakeRepo(), | 439 | + | repo: newFaviconRepoState(), |
| 436 | - | blobs: newFakeBlobStore(), | 440 | + | blobs: newFaviconBlobStoreState(), |
| 437 | - | converter: &fakeConverter{}, | 441 | + | converter: &faviconConverterState{}, |
| 438 | - | scheduler: &fakeScheduler{seen: map[favicon.Key]bool{}}, | 442 | + | scheduler: &faviconSchedulerState{seen: map[favicon.Key]bool{}}, |
| 439 | - | invalidators: &fakeInvalidators{}, | 443 | + | invalidators: &faviconInvalidatorsState{}, |
| 440 | - | fetcher: &fakeFetcher{}, | 444 | + | fetcher: &faviconFetcherState{}, |
| 441 | now: now, | 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 | fx.uc = NewFaviconUseCase(FaviconDeps{ | 455 | fx.uc = NewFaviconUseCase(FaviconDeps{ | ||
| 444 | - | Repository: fx.repo, | 456 | + | Repository: repo, |
| 445 | - | BlobStore: fx.blobs, | 457 | + | BlobStore: blobs, |
| 446 | - | Converter: fx.converter, | 458 | + | Converter: converter, |
| 447 | - | Scheduler: fx.scheduler, | 459 | + | Scheduler: scheduler, |
| 448 | - | Invalidators: fx.invalidators, | 460 | + | Invalidators: invalidators, |
| 449 | - | Fetcher: fx.fetcher, | 461 | + | Fetcher: fetcher, |
| 450 | Now: func() time.Time { return fx.now }, | 462 | Now: func() time.Time { return fx.now }, | ||
| 451 | Background: context.WithValue(context.Background(), faviconFixtureBackgroundKey, true), | 463 | Background: context.WithValue(context.Background(), faviconFixtureBackgroundKey, true), | ||
| 452 | }) | 464 | }) | ||
| 453 | return fx | 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 | func (f *faviconFixture) seed(key string, sized []byte, stale bool) { | 526 | func (f *faviconFixture) seed(key string, sized []byte, stale bool) { | ||
| 456 | checked := f.now | 527 | checked := f.now | ||
| 457 | if stale { | 528 | if stale { | ||
| @@ -460,6 +531,7 @@ func (f *faviconFixture) seed(key string, sized []byte, stale bool) { | |||||
| 460 | f.repo.byKey[favicon.Key(key)] = &favicon.Metadata{Key: favicon.Key(key), PageURL: "https://" + key, ContentHash: favicon.Hash([]byte("orig")), LastCheckedAt: checked} | 531 | f.repo.byKey[favicon.Key(key)] = &favicon.Metadata{Key: favicon.Key(key), PageURL: "https://" + key, ContentHash: favicon.Hash([]byte("orig")), LastCheckedAt: checked} | ||
| 461 | f.blobs.sized[favicon.Key(key)] = map[int][]byte{32: sized} | 532 | f.blobs.sized[favicon.Key(key)] = map[int][]byte{32: sized} | ||
| 462 | } | 533 | } | ||
| 534 | + | ||||
| 463 | func (f *faviconFixture) seedOriginal(b []byte) { | 535 | func (f *faviconFixture) seedOriginal(b []byte) { | ||
| 464 | const contentType = "image/png" | 536 | const contentType = "image/png" | ||
| 465 | key := favicon.Key("example.com") | 537 | key := favicon.Key("example.com") | ||
| @@ -474,15 +546,16 @@ func (f *faviconFixture) seedOriginal(b []byte) { | |||||
| 474 | f.blobs.contentTypes[key] = contentType | 546 | f.blobs.contentTypes[key] = contentType | ||
| 475 | } | 547 | } | ||
| 476 | 548 | ||||
| 477 | - | type fakeFaviconRepo struct { | 549 | + | type faviconRepoState struct { |
| 478 | byKey map[favicon.Key]*favicon.Metadata | 550 | byKey map[favicon.Key]*favicon.Metadata | ||
| 479 | err error | 551 | err error | ||
| 480 | } | 552 | } | ||
| 481 | 553 | ||||
| 482 | - | func newFakeRepo() *fakeFaviconRepo { | 554 | + | func newFaviconRepoState() *faviconRepoState { |
| 483 | - | return &fakeFaviconRepo{byKey: map[favicon.Key]*favicon.Metadata{}} | 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 | if r.err != nil { | 559 | if r.err != nil { | ||
| 487 | return nil, r.err | 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 | return nil, nil | 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 | if r.err != nil { | 570 | if r.err != nil { | ||
| 497 | return nil, r.err | 571 | return nil, r.err | ||
| 498 | } | 572 | } | ||
| 499 | for _, k := range keys { | 573 | for _, k := range keys { | ||
| 500 | - | if m, _ := r.Get(ctx, k); m != nil { | 574 | + | if m, _ := r.get(ctx, k); m != nil { |
| 501 | return m, nil | 575 | return m, nil | ||
| 502 | } | 576 | } | ||
| 503 | } | 577 | } | ||
| 504 | return nil, nil | 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 | if r.err != nil { | 582 | if r.err != nil { | ||
| 508 | return r.err | 583 | return r.err | ||
| 509 | } | 584 | } | ||
| @@ -511,7 +586,8 @@ func (r *fakeFaviconRepo) Upsert(_ context.Context, meta favicon.Metadata) error | |||||
| 511 | r.byKey[meta.Key] = &cp | 586 | r.byKey[meta.Key] = &cp | ||
| 512 | return nil | 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 | if r.err != nil { | 591 | if r.err != nil { | ||
| 516 | return r.err | 592 | return r.err | ||
| 517 | } | 593 | } | ||
| @@ -519,7 +595,8 @@ func (r *fakeFaviconRepo) UpdateLastChecked(_ context.Context, key favicon.Key, | |||||
| 519 | r.byKey[key].LastCheckedAt = checkedAt | 595 | r.byKey[key].LastCheckedAt = checkedAt | ||
| 520 | return nil | 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 | if r.err != nil { | 600 | if r.err != nil { | ||
| 524 | return r.err | 601 | return r.err | ||
| 525 | } | 602 | } | ||
| @@ -527,7 +604,7 @@ func (r *fakeFaviconRepo) Delete(_ context.Context, key favicon.Key) error { | |||||
| 527 | return nil | 604 | return nil | ||
| 528 | } | 605 | } | ||
| 529 | 606 | ||||
| 530 | - | type fakeBlobStore struct { | 607 | + | type faviconBlobStoreState struct { |
| 531 | original, png map[favicon.Key][]byte | 608 | original, png map[favicon.Key][]byte | ||
| 532 | sized map[favicon.Key]map[int][]byte | 609 | sized map[favicon.Key]map[int][]byte | ||
| 533 | contentTypes map[favicon.Key]string | 610 | contentTypes map[favicon.Key]string | ||
| @@ -536,10 +613,16 @@ type fakeBlobStore struct { | |||||
| 536 | readSizedErr error | 613 | readSizedErr error | ||
| 537 | } | 614 | } | ||
| 538 | 615 | ||||
| 539 | - | func newFakeBlobStore() *fakeBlobStore { | 616 | + | func newFaviconBlobStoreState() *faviconBlobStoreState { |
| 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{}} | 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 | if b.err != nil { | 626 | if b.err != nil { | ||
| 544 | return nil, "", b.err | 627 | return nil, "", b.err | ||
| 545 | } | 628 | } | ||
| @@ -549,7 +632,8 @@ func (b *fakeBlobStore) ReadOriginal(_ context.Context, key favicon.Key) ([]byte | |||||
| 549 | } | 632 | } | ||
| 550 | return v, b.contentTypes[key], nil | 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 | if b.err != nil { | 637 | if b.err != nil { | ||
| 554 | return b.err | 638 | return b.err | ||
| 555 | } | 639 | } | ||
| @@ -558,7 +642,8 @@ func (b *fakeBlobStore) WriteOriginal(_ context.Context, key favicon.Key, data [ | |||||
| 558 | b.contentTypes[key] = ct | 642 | b.contentTypes[key] = ct | ||
| 559 | return nil | 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 | if b.err != nil { | 647 | if b.err != nil { | ||
| 563 | return nil, "", b.err | 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 | return v, "image/png", nil | 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 | if b.err != nil { | 658 | if b.err != nil { | ||
| 573 | return b.err | 659 | return b.err | ||
| 574 | } | 660 | } | ||
| 575 | b.png[key] = data | 661 | b.png[key] = data | ||
| 576 | return nil | 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 | if b.err != nil { | 666 | if b.err != nil { | ||
| 580 | return nil, "", b.err | 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 | return b.sized[key][size], "image/png", nil | 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 | if b.err != nil { | 679 | if b.err != nil { | ||
| 592 | return b.err | 680 | return b.err | ||
| 593 | } | 681 | } | ||
| @@ -597,7 +685,8 @@ func (b *fakeBlobStore) WriteSizedPNG(_ context.Context, key favicon.Key, size i | |||||
| 597 | b.sized[key][size] = data | 685 | b.sized[key][size] = data | ||
| 598 | return nil | 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 | if b.err != nil { | 690 | if b.err != nil { | ||
| 602 | return b.err | 691 | return b.err | ||
| 603 | } | 692 | } | ||
| @@ -607,13 +696,13 @@ func (b *fakeBlobStore) RemoveDerived(_ context.Context, key favicon.Key) error | |||||
| 607 | return nil | 696 | return nil | ||
| 608 | } | 697 | } | ||
| 609 | 698 | ||||
| 610 | - | type fakeConverter struct { | 699 | + | type faviconConverterState struct { |
| 611 | calls int | 700 | calls int | ||
| 612 | err error | 701 | err error | ||
| 613 | errByContentType map[string]error | 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 | c.calls++ | 706 | c.calls++ | ||
| 618 | if c.err != nil { | 707 | if c.err != nil { | ||
| 619 | return nil, c.err | 708 | return nil, c.err | ||
| @@ -628,14 +717,14 @@ func (c *fakeConverter) Convert(_ context.Context, original []byte, contentType | |||||
| 628 | return out, nil | 717 | return out, nil | ||
| 629 | } | 718 | } | ||
| 630 | 719 | ||||
| 631 | - | type fakeScheduler struct { | 720 | + | type faviconSchedulerState struct { |
| 632 | seen map[favicon.Key]bool | 721 | seen map[favicon.Key]bool | ||
| 633 | works map[favicon.Key]func(context.Context) | 722 | works map[favicon.Key]func(context.Context) | ||
| 634 | schedules int | 723 | schedules int | ||
| 635 | usedCallerContext bool | 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 | if ctx.Value(faviconFixtureBackgroundKey) != true { | 728 | if ctx.Value(faviconFixtureBackgroundKey) != true { | ||
| 640 | s.usedCallerContext = true | 729 | s.usedCallerContext = true | ||
| 641 | } | 730 | } | ||
| @@ -651,33 +740,33 @@ func (s *fakeScheduler) Schedule(ctx context.Context, key favicon.Key, work func | |||||
| 651 | return true | 740 | return true | ||
| 652 | } | 741 | } | ||
| 653 | 742 | ||||
| 654 | - | func (s *fakeScheduler) run(key favicon.Key) { | 743 | + | func (s *faviconSchedulerState) run(key favicon.Key) { |
| 655 | if work := s.works[key]; work != nil { | 744 | if work := s.works[key]; work != nil { | ||
| 656 | work(context.Background()) | 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 | i.calls++ | 752 | i.calls++ | ||
| 664 | return nil | 753 | return nil | ||
| 665 | } | 754 | } | ||
| 666 | 755 | ||||
| 667 | - | type fakeFetchedIcon struct { | 756 | + | type faviconFetchResponse struct { |
| 668 | bytes []byte | 757 | bytes []byte | ||
| 669 | contentType string | 758 | contentType string | ||
| 670 | pageURL string | 759 | pageURL string | ||
| 671 | resolvedKey favicon.Key | 760 | resolvedKey favicon.Key | ||
| 672 | } | 761 | } | ||
| 673 | 762 | ||||
| 674 | - | type fakeFetcher struct { | 763 | + | type faviconFetcherState struct { |
| 675 | calls int | 764 | calls int | ||
| 676 | err error | 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 | f.calls++ | 770 | f.calls++ | ||
| 682 | if f.err != nil { | 771 | if f.err != nil { | ||
| 683 | return nil, f.err | 772 | return nil, f.err | ||
| @@ -688,11 +777,18 @@ func (f *fakeFetcher) Fetch(_ context.Context, req appport.FaviconFetchRequest) | |||||
| 688 | } | 777 | } | ||
| 689 | response := f.responses[iconURL] | 778 | response := f.responses[iconURL] | ||
| 690 | if response.bytes == nil { | 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 | pageURL := req.PageURL | 782 | pageURL := req.PageURL | ||
| 694 | if response.pageURL != "" { | 783 | if response.pageURL != "" { | ||
| 695 | pageURL = response.pageURL | 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 | "testing" | 5 | "testing" | ||
| 6 | 6 | ||||
| 7 | "github.com/bnema/dumber/internal/application/port" | 7 | "github.com/bnema/dumber/internal/application/port" | ||
| 8 | + | bootstrapmocks "github.com/bnema/dumber/internal/bootstrap/mocks" | |||
| 8 | "github.com/bnema/dumber/internal/domain/entity" | 9 | "github.com/bnema/dumber/internal/domain/entity" | ||
| 9 | "github.com/bnema/dumber/internal/infrastructure/config" | 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 | current *config.Config | 15 | current *config.Config | ||
| 14 | watchCalls int | 16 | watchCalls int | ||
| 15 | callbacks []func(*config.Config) | 17 | callbacks []func(*config.Config) | ||
| 16 | } | 18 | } | ||
| 17 | 19 | ||||
| 18 | - | func (f *fakeConfigManager) Watch() error { | 20 | + | func newMockConfigManager(t *testing.T, state *configManagerState) *bootstrapmocks.MockConfigChangeManager { |
| 19 | - | f.watchCalls++ | 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 | return nil | 32 | return nil | ||
| 21 | } | 33 | } | ||
| 22 | 34 | ||||
| 23 | - | func (f *fakeConfigManager) OnConfigChange(callback func(*config.Config)) { | 35 | + | func (s *configManagerState) onConfigChange(callback func(*config.Config)) { |
| 24 | - | f.callbacks = append(f.callbacks, callback) | 36 | + | s.callbacks = append(s.callbacks, callback) |
| 25 | } | 37 | } | ||
| 26 | 38 | ||||
| 27 | - | func (f *fakeConfigManager) Get() *config.Config { | 39 | + | func (s *configManagerState) get() *config.Config { |
| 28 | - | return f.current | 40 | + | return s.current |
| 29 | } | 41 | } | ||
| 30 | 42 | ||||
| 31 | - | func (f *fakeConfigManager) emit(next *config.Config) { | 43 | + | func (s *configManagerState) emit(next *config.Config) { |
| 32 | - | f.current = next | 44 | + | s.current = next |
| 33 | - | for _, callback := range f.callbacks { | 45 | + | for _, callback := range s.callbacks { |
| 34 | callback(next) | 46 | callback(next) | ||
| 35 | } | 47 | } | ||
| 36 | } | 48 | } | ||
| @@ -71,8 +83,8 @@ func TestEngineSettingsPayloadFromConfigMapsRuntimeFields(t *testing.T) { | |||||
| 71 | func TestRuntimeConfigProviderUpdatesSnapshotBeforeCallback(t *testing.T) { | 83 | func TestRuntimeConfigProviderUpdatesSnapshotBeforeCallback(t *testing.T) { | ||
| 72 | initial := config.DefaultConfig() | 84 | initial := config.DefaultConfig() | ||
| 73 | initial.DefaultUIScale = 1 | 85 | initial.DefaultUIScale = 1 | ||
| 74 | - | manager := &fakeConfigManager{} | 86 | + | manager := &configManagerState{} |
| 75 | - | provider := NewRuntimeConfigProvider(initial, manager) | 87 | + | provider := NewRuntimeConfigProvider(initial, newMockConfigManager(t, manager)) |
| 76 | 88 | ||||
| 77 | var seen port.RuntimeConfigSnapshot | 89 | var seen port.RuntimeConfigSnapshot | ||
| 78 | provider.OnChange(func(snapshot port.RuntimeConfigSnapshot) { | 90 | provider.OnChange(func(snapshot port.RuntimeConfigSnapshot) { | ||
| @@ -94,8 +106,8 @@ func TestRuntimeConfigProviderUpdatesSnapshotBeforeCallback(t *testing.T) { | |||||
| 94 | func TestRuntimeConfigProviderCurrentReflectsManagerGetWithoutSubscription(t *testing.T) { | 106 | func TestRuntimeConfigProviderCurrentReflectsManagerGetWithoutSubscription(t *testing.T) { | ||
| 95 | initial := config.DefaultConfig() | 107 | initial := config.DefaultConfig() | ||
| 96 | initial.DefaultUIScale = 1 | 108 | initial.DefaultUIScale = 1 | ||
| 97 | - | manager := &fakeConfigManager{current: initial} | 109 | + | manager := &configManagerState{current: initial} |
| 98 | - | provider := NewRuntimeConfigProvider(initial, manager) | 110 | + | provider := NewRuntimeConfigProvider(initial, newMockConfigManager(t, manager)) |
| 99 | 111 | ||||
| 100 | next := config.DefaultConfig() | 112 | next := config.DefaultConfig() | ||
| 101 | next.DefaultUIScale = 1.8 | 113 | next.DefaultUIScale = 1.8 | ||
| @@ -109,8 +121,8 @@ func TestRuntimeConfigProviderCurrentReflectsManagerGetWithoutSubscription(t *te | |||||
| 109 | func TestRuntimeConfigProviderOnChangeCallbackCanCallCurrent(t *testing.T) { | 121 | func TestRuntimeConfigProviderOnChangeCallbackCanCallCurrent(t *testing.T) { | ||
| 110 | initial := config.DefaultConfig() | 122 | initial := config.DefaultConfig() | ||
| 111 | initial.DefaultUIScale = 1 | 123 | initial.DefaultUIScale = 1 | ||
| 112 | - | manager := &fakeConfigManager{current: initial} | 124 | + | manager := &configManagerState{current: initial} |
| 113 | - | provider := NewRuntimeConfigProvider(initial, manager) | 125 | + | provider := NewRuntimeConfigProvider(initial, newMockConfigManager(t, manager)) |
| 114 | 126 | ||||
| 115 | completed := false | 127 | completed := false | ||
| 116 | var seen port.RuntimeConfigSnapshot | 128 | var seen port.RuntimeConfigSnapshot | ||
| @@ -132,8 +144,8 @@ func TestRuntimeConfigProviderOnChangeCallbackCanCallCurrent(t *testing.T) { | |||||
| 132 | } | 144 | } | ||
| 133 | 145 | ||||
| 134 | func TestRuntimeConfigProviderOnChangeNilCallbackDoesNotRegister(t *testing.T) { | 146 | func TestRuntimeConfigProviderOnChangeNilCallbackDoesNotRegister(t *testing.T) { | ||
| 135 | - | manager := &fakeConfigManager{current: config.DefaultConfig()} | 147 | + | manager := &configManagerState{current: config.DefaultConfig()} |
| 136 | - | provider := NewRuntimeConfigProvider(config.DefaultConfig(), manager) | 148 | + | provider := NewRuntimeConfigProvider(config.DefaultConfig(), newMockConfigManager(t, manager)) |
| 137 | 149 | ||||
| 138 | provider.OnChange(nil) | 150 | provider.OnChange(nil) | ||
| 139 | 151 | ||||
| @@ -143,8 +155,8 @@ func TestRuntimeConfigProviderOnChangeNilCallbackDoesNotRegister(t *testing.T) { | |||||
| 143 | } | 155 | } | ||
| 144 | 156 | ||||
| 145 | func TestRuntimeConfigProviderWatchDelegatesToManager(t *testing.T) { | 157 | func TestRuntimeConfigProviderWatchDelegatesToManager(t *testing.T) { | ||
| 146 | - | manager := &fakeConfigManager{} | 158 | + | manager := &configManagerState{} |
| 147 | - | provider := NewRuntimeConfigProvider(config.DefaultConfig(), manager) | 159 | + | provider := NewRuntimeConfigProvider(config.DefaultConfig(), newMockConfigManager(t, manager)) |
| 148 | 160 | ||||
| 149 | if err := provider.Watch(); err != nil { | 161 | if err := provider.Watch(); err != nil { | ||
| 150 | t.Fatalf("Watch returned error: %v", err) | 162 | t.Fatalf("Watch returned error: %v", err) | ||
| @@ -159,7 +171,7 @@ func TestRuntimeConfigProviderCurrentReturnsMapClone(t *testing.T) { | |||||
| 159 | cfg.SearchShortcuts = map[string]config.SearchShortcut{ | 171 | cfg.SearchShortcuts = map[string]config.SearchShortcut{ | ||
| 160 | "gh": {URL: "https://github.com/search?q=%s", Description: "GitHub"}, | 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 | first := provider.Current() | 176 | first := provider.Current() | ||
| 165 | first.UI.SearchShortcuts["gh"] = port.RuntimeSearchShortcut{URL: "mutated"} | 177 | first.UI.SearchShortcuts["gh"] = port.RuntimeSearchShortcut{URL: "mutated"} | ||
| @@ -172,7 +184,7 @@ func TestRuntimeConfigProviderCurrentReturnsMapClone(t *testing.T) { | |||||
| 172 | 184 | ||||
| 173 | func TestRuntimeConfigProviderCurrentReturnsNestedConfigClone(t *testing.T) { | 185 | func TestRuntimeConfigProviderCurrentReturnsNestedConfigClone(t *testing.T) { | ||
| 174 | cfg := runtimeConfigWithNestedMutableFields() | 186 | cfg := runtimeConfigWithNestedMutableFields() | ||
| 175 | - | provider := NewRuntimeConfigProvider(cfg, &fakeConfigManager{current: cfg}) | 187 | + | provider := NewRuntimeConfigProvider(cfg, newMockConfigManager(t, &configManagerState{current: cfg})) |
| 176 | 188 | ||||
| 177 | first := provider.Current() | 189 | first := provider.Current() | ||
| 178 | mutateNestedRuntimeConfigSnapshot(first) | 190 | mutateNestedRuntimeConfigSnapshot(first) | ||
| @@ -182,8 +194,8 @@ func TestRuntimeConfigProviderCurrentReturnsNestedConfigClone(t *testing.T) { | |||||
| 182 | } | 194 | } | ||
| 183 | 195 | ||||
| 184 | func TestRuntimeConfigProviderOnChangePassesNestedConfigClone(t *testing.T) { | 196 | func TestRuntimeConfigProviderOnChangePassesNestedConfigClone(t *testing.T) { | ||
| 185 | - | manager := &fakeConfigManager{} | 197 | + | manager := &configManagerState{} |
| 186 | - | provider := NewRuntimeConfigProvider(config.DefaultConfig(), manager) | 198 | + | provider := NewRuntimeConfigProvider(config.DefaultConfig(), newMockConfigManager(t, manager)) |
| 187 | 199 | ||||
| 188 | provider.OnChange(func(snapshot port.RuntimeConfigSnapshot) { | 200 | provider.OnChange(func(snapshot port.RuntimeConfigSnapshot) { | ||
| 189 | mutateNestedRuntimeConfigSnapshot(snapshot) | 201 | mutateNestedRuntimeConfigSnapshot(snapshot) | ||
| @@ -246,7 +258,7 @@ func mutateNestedRuntimeConfigSnapshot(snapshot port.RuntimeConfigSnapshot) { | |||||
| 246 | snapshot.UI.Session.SessionMode.Actions["added-session"] = entity.ActionBinding{Keys: []string{"added"}} | 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 | binding := actions[action] | 262 | binding := actions[action] | ||
| 251 | binding.Keys[0] = key | 263 | binding.Keys[0] = key | ||
| 252 | actions[action] = binding | 264 | actions[action] = binding | ||
| @@ -279,7 +291,7 @@ func assertNestedRuntimeConfigSnapshotUnchanged(t *testing.T, snapshot port.Runt | |||||
| 279 | assertMapEntryAbsent(t, snapshot.UI.Session.SessionMode.Actions, "added-session") | 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 | t.Helper() | 295 | t.Helper() | ||
| 284 | 296 | ||||
| 285 | binding, ok := actions[action] | 297 | binding, ok := actions[action] | ||
M internal/cli/cmd/logs_test.go +17 -15
| @@ -8,23 +8,13 @@ import ( | |||||
| 8 | "testing" | 8 | "testing" | ||
| 9 | "time" | 9 | "time" | ||
| 10 | 10 | ||||
| 11 | + | cmdmocks "github.com/bnema/dumber/internal/cli/cmd/mocks" | |||
| 11 | "github.com/bnema/dumber/internal/domain/entity" | 12 | "github.com/bnema/dumber/internal/domain/entity" | ||
| 12 | "github.com/bnema/dumber/internal/logging" | 13 | "github.com/bnema/dumber/internal/logging" | ||
| 14 | + | "github.com/stretchr/testify/mock" | |||
| 13 | "github.com/stretchr/testify/require" | 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 | func TestGetSessionsMerged_DBAndFS(t *testing.T) { | 18 | func TestGetSessionsMerged_DBAndFS(t *testing.T) { | ||
| 29 | logDir := t.TempDir() | 19 | logDir := t.TempDir() | ||
| 30 | 20 | ||||
| @@ -34,7 +24,11 @@ func TestGetSessionsMerged_DBAndFS(t *testing.T) { | |||||
| 34 | require.NoError(t, os.WriteFile(filepath.Join(logDir, logging.SessionFilename(string(s1.ID))), []byte("hi\n"), 0o600)) | 24 | require.NoError(t, os.WriteFile(filepath.Join(logDir, logging.SessionFilename(string(s1.ID))), []byte("hi\n"), 0o600)) | ||
| 35 | require.NoError(t, os.WriteFile(filepath.Join(logDir, logging.SessionFilename("legacy_only")), []byte("legacy\n"), 0o600)) | 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 | merged, err := getSessionsMerged(context.Background(), mgr, logDir) | 33 | merged, err := getSessionsMerged(context.Background(), mgr, logDir) | ||
| 40 | require.NoError(t, err) | 34 | require.NoError(t, err) | ||
| @@ -54,7 +48,11 @@ func TestGetSessionsMerged_MissingTableFallsBack(t *testing.T) { | |||||
| 54 | logDir := t.TempDir() | 48 | logDir := t.TempDir() | ||
| 55 | require.NoError(t, os.WriteFile(filepath.Join(logDir, logging.SessionFilename("legacy_only")), []byte("legacy\n"), 0o600)) | 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 | merged, err := getSessionsMerged(context.Background(), mgr, logDir) | 56 | merged, err := getSessionsMerged(context.Background(), mgr, logDir) | ||
| 59 | require.NoError(t, err) | 57 | require.NoError(t, err) | ||
| 60 | require.Len(t, merged, 1) | 58 | require.Len(t, merged, 1) | ||
| @@ -65,7 +63,11 @@ func TestFindSession_DBShortIDMatch(t *testing.T) { | |||||
| 65 | logDir := t.TempDir() | 63 | logDir := t.TempDir() | ||
| 66 | 64 | ||||
| 67 | s1 := &entity.Session{ID: entity.SessionID("20251217_205106_a7b3"), Type: entity.SessionTypeBrowser, StartedAt: time.Date(2025, 12, 17, 20, 51, 6, 0, time.UTC)} | 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 | info, err := findSession(context.Background(), mgr, logDir, "a7b3") | 72 | info, err := findSession(context.Background(), mgr, logDir, "a7b3") | ||
| 71 | require.NoError(t, err) | 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 | "testing" | 8 | "testing" | ||
| 9 | 9 | ||||
| 10 | "github.com/bnema/dumber/internal/application/port" | 10 | "github.com/bnema/dumber/internal/application/port" | ||
| 11 | + | pipewiremocks "github.com/bnema/dumber/internal/infrastructure/audio/pipewire/mocks" | |||
| 11 | pwpipewire "github.com/bnema/purego-pipewire/pipewire" | 12 | pwpipewire "github.com/bnema/purego-pipewire/pipewire" | ||
| 12 | "github.com/stretchr/testify/assert" | 13 | "github.com/stretchr/testify/assert" | ||
| 13 | "github.com/stretchr/testify/require" | 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 | started bool | 20 | started bool | ||
| 21 | paused bool | 21 | paused bool | ||
| 22 | stopped bool | 22 | stopped bool | ||
| @@ -28,7 +28,19 @@ type fakePlayer struct { | |||||
| 28 | closeErr error | 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 | if p.startErr != nil { | 44 | if p.startErr != nil { | ||
| 33 | return p.startErr | 45 | return p.startErr | ||
| 34 | } | 46 | } | ||
| @@ -37,13 +49,13 @@ func (p *fakePlayer) Start() error { | |||||
| 37 | return nil | 49 | return nil | ||
| 38 | } | 50 | } | ||
| 39 | 51 | ||||
| 40 | - | func (p *fakePlayer) Pause() error { | 52 | + | func (p *playerRecorder) pause() error { |
| 41 | p.paused = true | 53 | p.paused = true | ||
| 42 | p.state = pwpipewire.PlayerStatePaused | 54 | p.state = pwpipewire.PlayerStatePaused | ||
| 43 | return nil | 55 | return nil | ||
| 44 | } | 56 | } | ||
| 45 | 57 | ||||
| 46 | - | func (p *fakePlayer) Stop() error { | 58 | + | func (p *playerRecorder) stop() error { |
| 47 | if p.stopErr != nil { | 59 | if p.stopErr != nil { | ||
| 48 | return p.stopErr | 60 | return p.stopErr | ||
| 49 | } | 61 | } | ||
| @@ -52,7 +64,7 @@ func (p *fakePlayer) Stop() error { | |||||
| 52 | return nil | 64 | return nil | ||
| 53 | } | 65 | } | ||
| 54 | 66 | ||||
| 55 | - | func (p *fakePlayer) Close() error { | 67 | + | func (p *playerRecorder) close() error { |
| 56 | if p.closeErr != nil { | 68 | if p.closeErr != nil { | ||
| 57 | return p.closeErr | 69 | return p.closeErr | ||
| 58 | } | 70 | } | ||
| @@ -61,7 +73,7 @@ func (p *fakePlayer) Close() error { | |||||
| 61 | return nil | 73 | return nil | ||
| 62 | } | 74 | } | ||
| 63 | 75 | ||||
| 64 | - | func (p *fakePlayer) State() pwpipewire.PlayerState { | 76 | + | func (p *playerRecorder) currentState() pwpipewire.PlayerState { |
| 65 | return p.state | 77 | return p.state | ||
| 66 | } | 78 | } | ||
| 67 | 79 | ||||
| @@ -69,7 +81,7 @@ func (p *fakePlayer) State() pwpipewire.PlayerState { | |||||
| 69 | 81 | ||||
| 70 | func TestFactory_NewStream_ValidFormat_CreatesStream(t *testing.T) { | 82 | func TestFactory_NewStream_ValidFormat_CreatesStream(t *testing.T) { | ||
| 71 | var capturedConfig pwpipewire.PlayerConfig | 83 | var capturedConfig pwpipewire.PlayerConfig | ||
| 72 | - | player := &fakePlayer{} | 84 | + | player := newPlayerRecorder(t) |
| 73 | 85 | ||||
| 74 | factory := &Factory{ | 86 | factory := &Factory{ | ||
| 75 | createPlayer: func(config pwpipewire.PlayerConfig, _ pwpipewire.PlayerCallbacks) (pwpipewire.Player, error) { | 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 | func TestFactory_NewStream_PlayerStartFails_ClosesAndReturnsError(t *testing.T) { | 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 | factory := &Factory{ | 192 | factory := &Factory{ | ||
| 180 | createPlayer: func(_ pwpipewire.PlayerConfig, _ pwpipewire.PlayerCallbacks) (pwpipewire.Player, error) { | 193 | createPlayer: func(_ pwpipewire.PlayerConfig, _ pwpipewire.PlayerCallbacks) (pwpipewire.Player, error) { | ||
| 181 | return player, nil | 194 | return player, nil | ||
| @@ -224,7 +237,7 @@ func TestFactory_NewStream_MultipleFormats_Accepted(t *testing.T) { | |||||
| 224 | assert.Equal(t, fmt.SampleRate, config.SampleRate) | 237 | assert.Equal(t, fmt.SampleRate, config.SampleRate) | ||
| 225 | assert.Equal(t, fmt.ChannelCount, config.Channels) | 238 | assert.Equal(t, fmt.ChannelCount, config.Channels) | ||
| 226 | assert.Equal(t, fmt.FramesPerBuffer, config.FramesPerBuffer) | 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 | // --- Stream adapter tests --- | 257 | // --- Stream adapter tests --- | ||
| 245 | 258 | ||||
| 246 | func TestPlayerStreamAdapter_Write_SendsToBuffer(t *testing.T) { | 259 | func TestPlayerStreamAdapter_Write_SendsToBuffer(t *testing.T) { | ||
| 247 | - | player := &fakePlayer{} | 260 | + | player := newPlayerRecorder(t) |
| 248 | adapter := &playerStreamAdapter{ | 261 | adapter := &playerStreamAdapter{ | ||
| 249 | player: player, | 262 | player: player, | ||
| 250 | buf: make(chan [][]float32, 4), | 263 | buf: make(chan [][]float32, 4), | ||
| @@ -265,7 +278,7 @@ func TestPlayerStreamAdapter_Write_SendsToBuffer(t *testing.T) { | |||||
| 265 | } | 278 | } | ||
| 266 | 279 | ||||
| 267 | func TestPlayerStreamAdapter_Write_AfterClose_ReturnsError(t *testing.T) { | 280 | func TestPlayerStreamAdapter_Write_AfterClose_ReturnsError(t *testing.T) { | ||
| 268 | - | player := &fakePlayer{} | 281 | + | player := newPlayerRecorder(t) |
| 269 | adapter := &playerStreamAdapter{ | 282 | adapter := &playerStreamAdapter{ | ||
| 270 | player: player, | 283 | player: player, | ||
| 271 | buf: make(chan [][]float32, 4), | 284 | buf: make(chan [][]float32, 4), | ||
| @@ -279,7 +292,7 @@ func TestPlayerStreamAdapter_Write_AfterClose_ReturnsError(t *testing.T) { | |||||
| 279 | } | 292 | } | ||
| 280 | 293 | ||||
| 281 | func TestPlayerStreamAdapter_Close_StopsAndClosesPlayer(t *testing.T) { | 294 | func TestPlayerStreamAdapter_Close_StopsAndClosesPlayer(t *testing.T) { | ||
| 282 | - | player := &fakePlayer{} | 295 | + | player := newPlayerRecorder(t) |
| 283 | adapter := &playerStreamAdapter{ | 296 | adapter := &playerStreamAdapter{ | ||
| 284 | player: player, | 297 | player: player, | ||
| 285 | buf: make(chan [][]float32, 4), | 298 | buf: make(chan [][]float32, 4), | ||
| @@ -294,7 +307,7 @@ func TestPlayerStreamAdapter_Close_StopsAndClosesPlayer(t *testing.T) { | |||||
| 294 | } | 307 | } | ||
| 295 | 308 | ||||
| 296 | func TestPlayerStreamAdapter_Close_IsIdempotent(t *testing.T) { | 309 | func TestPlayerStreamAdapter_Close_IsIdempotent(t *testing.T) { | ||
| 297 | - | player := &fakePlayer{} | 310 | + | player := newPlayerRecorder(t) |
| 298 | adapter := &playerStreamAdapter{ | 311 | adapter := &playerStreamAdapter{ | ||
| 299 | player: player, | 312 | player: player, | ||
| 300 | buf: make(chan [][]float32, 4), | 313 | buf: make(chan [][]float32, 4), | ||
| @@ -306,7 +319,8 @@ func TestPlayerStreamAdapter_Close_IsIdempotent(t *testing.T) { | |||||
| 306 | } | 319 | } | ||
| 307 | 320 | ||||
| 308 | func TestPlayerStreamAdapter_Close_PropagatesStopError(t *testing.T) { | 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 | adapter := &playerStreamAdapter{ | 324 | adapter := &playerStreamAdapter{ | ||
| 311 | player: player, | 325 | player: player, | ||
| 312 | buf: make(chan [][]float32, 4), | 326 | buf: make(chan [][]float32, 4), | ||
| @@ -319,7 +333,8 @@ func TestPlayerStreamAdapter_Close_PropagatesStopError(t *testing.T) { | |||||
| 319 | } | 333 | } | ||
| 320 | 334 | ||||
| 321 | func TestPlayerStreamAdapter_Close_PropagatesCloseError(t *testing.T) { | 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 | adapter := &playerStreamAdapter{ | 338 | adapter := &playerStreamAdapter{ | ||
| 324 | player: player, | 339 | player: player, | ||
| 325 | buf: make(chan [][]float32, 4), | 340 | buf: make(chan [][]float32, 4), | ||
| @@ -427,7 +442,7 @@ func TestCopyToPCM_EmptySamples_ReturnsZero(t *testing.T) { | |||||
| 427 | 442 | ||||
| 428 | func TestPlayerStreamAdapter_Write_DropsWhenBufferFull(t *testing.T) { | 443 | func TestPlayerStreamAdapter_Write_DropsWhenBufferFull(t *testing.T) { | ||
| 429 | adapter := &playerStreamAdapter{ | 444 | adapter := &playerStreamAdapter{ | ||
| 430 | - | player: &fakePlayer{}, | 445 | + | player: newPlayerRecorder(t), |
| 431 | buf: make(chan [][]float32, 4), | 446 | buf: make(chan [][]float32, 4), | ||
| 432 | closed: make(chan struct{}), | 447 | closed: make(chan struct{}), | ||
| 433 | } | 448 | } | ||
| @@ -450,7 +465,7 @@ func TestPlayerStreamAdapter_Write_DropsWhenBufferFull(t *testing.T) { | |||||
| 450 | 465 | ||||
| 451 | func TestPlayerStreamAdapter_Write_DropsLogOnlyOnFirstAndPowerOfTwo(t *testing.T) { | 466 | func TestPlayerStreamAdapter_Write_DropsLogOnlyOnFirstAndPowerOfTwo(t *testing.T) { | ||
| 452 | adapter := &playerStreamAdapter{ | 467 | adapter := &playerStreamAdapter{ | ||
| 453 | - | player: &fakePlayer{}, | 468 | + | player: newPlayerRecorder(t), |
| 454 | buf: make(chan [][]float32, 1), // tiny buffer to force drops | 469 | buf: make(chan [][]float32, 1), // tiny buffer to force drops | ||
| 455 | closed: make(chan struct{}), | 470 | closed: make(chan struct{}), | ||
| 456 | } | 471 | } | ||
| @@ -470,7 +485,7 @@ func TestPlayerStreamAdapter_Write_DropsLogOnlyOnFirstAndPowerOfTwo(t *testing.T | |||||
| 470 | // --- Concurrent Write / Close tests --- | 485 | // --- Concurrent Write / Close tests --- | ||
| 471 | 486 | ||||
| 472 | func TestPlayerStreamAdapter_ConcurrentWriteClose_NoRace(t *testing.T) { | 487 | func TestPlayerStreamAdapter_ConcurrentWriteClose_NoRace(t *testing.T) { | ||
| 473 | - | player := &fakePlayer{} | 488 | + | player := newPlayerRecorder(t) |
| 474 | adapter := &playerStreamAdapter{ | 489 | adapter := &playerStreamAdapter{ | ||
| 475 | player: player, | 490 | player: player, | ||
| 476 | buf: make(chan [][]float32, 4), | 491 | buf: make(chan [][]float32, 4), | ||
| @@ -507,7 +522,7 @@ func TestNewFactory_Succeeds(t *testing.T) { | |||||
| 507 | 522 | ||||
| 508 | func TestStream_WriteFlowsThroughToFill(t *testing.T) { | 523 | func TestStream_WriteFlowsThroughToFill(t *testing.T) { | ||
| 509 | var capturedCallbacks pwpipewire.PlayerCallbacks | 524 | var capturedCallbacks pwpipewire.PlayerCallbacks | ||
| 510 | - | player := &fakePlayer{} | 525 | + | player := newPlayerRecorder(t) |
| 511 | 526 | ||||
| 512 | factory := &Factory{ | 527 | factory := &Factory{ | ||
| 513 | createPlayer: func(_ pwpipewire.PlayerConfig, callbacks pwpipewire.PlayerCallbacks) (pwpipewire.Player, error) { | 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 | DataKind: "json", | 66 | DataKind: "json", | ||
| 67 | TargetOrigin: "https://app.example", | 67 | TargetOrigin: "https://app.example", | ||
| 68 | SourceOrigin: "https://evil.example", | 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 | "testing" | 9 | "testing" | ||
| 10 | 10 | ||||
| 11 | "github.com/bnema/dumber/internal/domain/entity" | 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 | "github.com/stretchr/testify/require" | 14 | "github.com/stretchr/testify/require" | ||
| 13 | ) | 15 | ) | ||
| 14 | 16 | ||||
| 15 | - | type fakeToolkitClipboard struct { | 17 | + | type toolkitClipboardRecorder struct { |
| 18 | + | *clipboardmocks.MockToolkitClipboard | |||
| 19 | + | ||||
| 16 | writeTextCalls int | 20 | writeTextCalls int | ||
| 17 | readTextCalls int | 21 | readTextCalls int | ||
| 18 | writeImageCalls int | 22 | writeImageCalls int | ||
| @@ -23,67 +27,81 @@ type fakeToolkitClipboard struct { | |||||
| 23 | writeImageErr error | 27 | writeImageErr error | ||
| 24 | } | 28 | } | ||
| 25 | 29 | ||||
| 26 | - | func (f *fakeToolkitClipboard) WriteText(_ context.Context, text string) error { | 30 | + | func newToolkitClipboardRecorder(t *testing.T) *toolkitClipboardRecorder { |
| 27 | - | f.writeTextCalls++ | 31 | + | t.Helper() |
| 28 | - | f.text = text | 32 | + | |
| 29 | - | return f.writeTextErr | 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) { | 46 | + | func (r *toolkitClipboardRecorder) readText(_ context.Context) (string, error) { |
| 33 | - | f.readTextCalls++ | 47 | + | r.readTextCalls++ |
| 34 | - | return f.text, f.readTextErr | 48 | + | return r.text, r.readTextErr |
| 35 | } | 49 | } | ||
| 36 | 50 | ||||
| 37 | - | func (f *fakeToolkitClipboard) WriteImage(_ context.Context, image entity.ImageData) error { | 51 | + | func (r *toolkitClipboardRecorder) writeImage(_ context.Context, image entity.ImageData) error { |
| 38 | - | f.writeImageCalls++ | 52 | + | r.writeImageCalls++ |
| 39 | - | f.image = image | 53 | + | r.image = image |
| 40 | - | return f.writeImageErr | 54 | + | return r.writeImageErr |
| 41 | } | 55 | } | ||
| 42 | 56 | ||||
| 43 | func TestAdapter_ReadTextFallsBackToToolkitClipboardWhenNoSystemTool(t *testing.T) { | 57 | func TestAdapter_ReadTextFallsBackToToolkitClipboardWhenNoSystemTool(t *testing.T) { | ||
| 44 | - | fake := &fakeToolkitClipboard{text: "toolkit text"} | 58 | + | toolkit := newToolkitClipboardRecorder(t) |
| 45 | - | adapter := &Adapter{toolkit: fake} | 59 | + | toolkit.text = "toolkit text" |
| 60 | + | adapter := &Adapter{toolkit: toolkit} | |||
| 46 | 61 | ||||
| 47 | text, err := adapter.ReadText(context.Background()) | 62 | text, err := adapter.ReadText(context.Background()) | ||
| 48 | 63 | ||||
| 49 | require.NoError(t, err) | 64 | require.NoError(t, err) | ||
| 50 | require.Equal(t, "toolkit text", text) | 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 | func TestAdapter_HasTextReturnsTrueWhenToolkitFallbackHasText(t *testing.T) { | 69 | func TestAdapter_HasTextReturnsTrueWhenToolkitFallbackHasText(t *testing.T) { | ||
| 55 | - | fake := &fakeToolkitClipboard{text: "toolkit text"} | 70 | + | toolkit := newToolkitClipboardRecorder(t) |
| 56 | - | adapter := &Adapter{toolkit: fake} | 71 | + | toolkit.text = "toolkit text" |
| 72 | + | adapter := &Adapter{toolkit: toolkit} | |||
| 57 | 73 | ||||
| 58 | hasText, err := adapter.HasText(context.Background()) | 74 | hasText, err := adapter.HasText(context.Background()) | ||
| 59 | 75 | ||||
| 60 | require.NoError(t, err) | 76 | require.NoError(t, err) | ||
| 61 | require.True(t, hasText) | 77 | require.True(t, hasText) | ||
| 62 | - | require.Equal(t, 1, fake.readTextCalls) | 78 | + | require.Equal(t, 1, toolkit.readTextCalls) |
| 63 | } | 79 | } | ||
| 64 | 80 | ||||
| 65 | func TestAdapter_WriteTextReturnsToolkitErrorWhenNoSystemTool(t *testing.T) { | 81 | func TestAdapter_WriteTextReturnsToolkitErrorWhenNoSystemTool(t *testing.T) { | ||
| 66 | sentinel := errors.New("toolkit text failed") | 82 | sentinel := errors.New("toolkit text failed") | ||
| 67 | - | fake := &fakeToolkitClipboard{writeTextErr: sentinel} | 83 | + | toolkit := newToolkitClipboardRecorder(t) |
| 68 | - | adapter := &Adapter{toolkit: fake} | 84 | + | toolkit.writeTextErr = sentinel |
| 85 | + | adapter := &Adapter{toolkit: toolkit} | |||
| 69 | 86 | ||||
| 70 | err := adapter.WriteText(context.Background(), "hello") | 87 | err := adapter.WriteText(context.Background(), "hello") | ||
| 71 | 88 | ||||
| 72 | require.Equal(t, sentinel, err) | 89 | require.Equal(t, sentinel, err) | ||
| 73 | - | require.Equal(t, 1, fake.writeTextCalls) | 90 | + | require.Equal(t, 1, toolkit.writeTextCalls) |
| 74 | - | require.Equal(t, "hello", fake.text) | 91 | + | require.Equal(t, "hello", toolkit.text) |
| 75 | } | 92 | } | ||
| 76 | 93 | ||||
| 77 | func TestAdapter_WriteImageReturnsToolkitErrorWhenNoSystemTool(t *testing.T) { | 94 | func TestAdapter_WriteImageReturnsToolkitErrorWhenNoSystemTool(t *testing.T) { | ||
| 78 | sentinel := errors.New("toolkit image failed") | 95 | sentinel := errors.New("toolkit image failed") | ||
| 79 | - | fake := &fakeToolkitClipboard{writeImageErr: sentinel} | 96 | + | toolkit := newToolkitClipboardRecorder(t) |
| 80 | - | adapter := &Adapter{toolkit: fake} | 97 | + | toolkit.writeImageErr = sentinel |
| 98 | + | adapter := &Adapter{toolkit: toolkit} | |||
| 81 | 99 | ||||
| 82 | err := adapter.WriteImage(context.Background(), entity.ImageData{Bytes: []byte{1}, MimeType: "image/png"}) | 100 | err := adapter.WriteImage(context.Background(), entity.ImageData{Bytes: []byte{1}, MimeType: "image/png"}) | ||
| 83 | 101 | ||||
| 84 | require.Equal(t, sentinel, err) | 102 | require.Equal(t, sentinel, err) | ||
| 85 | - | require.Equal(t, 1, fake.writeImageCalls) | 103 | + | require.Equal(t, 1, toolkit.writeImageCalls) |
| 86 | - | require.Equal(t, entity.ImageData{Bytes: []byte{1}, MimeType: "image/png"}, fake.image) | 104 | + | require.Equal(t, entity.ImageData{Bytes: []byte{1}, MimeType: "image/png"}, toolkit.image) |
| 87 | } | 105 | } | ||
| 88 | 106 | ||||
| 89 | func TestNew_FallsBackToToolkitClipboardWhenSystemToolsUnavailable(t *testing.T) { | 107 | func TestNew_FallsBackToToolkitClipboardWhenSystemToolsUnavailable(t *testing.T) { | ||
| @@ -94,9 +112,9 @@ func TestNew_FallsBackToToolkitClipboardWhenSystemToolsUnavailable(t *testing.T) | |||||
| 94 | lookPath = oldLookPath | 112 | lookPath = oldLookPath | ||
| 95 | }) | 113 | }) | ||
| 96 | 114 | ||||
| 97 | - | fake := &fakeToolkitClipboard{} | 115 | + | toolkit := newToolkitClipboardRecorder(t) |
| 98 | newToolkitClipboard = func() toolkitClipboard { | 116 | newToolkitClipboard = func() toolkitClipboard { | ||
| 99 | - | return fake | 117 | + | return toolkit |
| 100 | } | 118 | } | ||
| 101 | lookPath = func(_ string) (string, error) { | 119 | lookPath = func(_ string) (string, error) { | ||
| 102 | return "", errors.New("missing") | 120 | return "", errors.New("missing") | ||
| @@ -107,8 +125,8 @@ func TestNew_FallsBackToToolkitClipboardWhenSystemToolsUnavailable(t *testing.T) | |||||
| 107 | 125 | ||||
| 108 | adapter := New().(*Adapter) | 126 | adapter := New().(*Adapter) | ||
| 109 | require.NoError(t, adapter.WriteImage(context.Background(), entity.ImageData{Bytes: []byte{1, 2, 3}})) | 127 | require.NoError(t, adapter.WriteImage(context.Background(), entity.ImageData{Bytes: []byte{1, 2, 3}})) | ||
| 110 | - | require.Equal(t, 1, fake.writeImageCalls) | 128 | + | require.Equal(t, 1, toolkit.writeImageCalls) |
| 111 | - | require.Equal(t, []byte{1, 2, 3}, fake.image.Bytes) | 129 | + | require.Equal(t, []byte{1, 2, 3}, toolkit.image.Bytes) |
| 112 | } | 130 | } | ||
| 113 | 131 | ||||
| 114 | func TestAdapter_WriteImagePrefersToolkitClipboardWhenAvailable(t *testing.T) { | 132 | func TestAdapter_WriteImagePrefersToolkitClipboardWhenAvailable(t *testing.T) { | ||
| @@ -123,14 +141,14 @@ func TestAdapter_WriteImagePrefersToolkitClipboardWhenAvailable(t *testing.T) { | |||||
| 123 | script := "#!/bin/sh\ncat > \"" + stdinPath + "\"\n" | 141 | script := "#!/bin/sh\ncat > \"" + stdinPath + "\"\n" | ||
| 124 | require.NoError(t, os.WriteFile(scriptPath, []byte(script), 0o755)) | 142 | require.NoError(t, os.WriteFile(scriptPath, []byte(script), 0o755)) | ||
| 125 | 143 | ||||
| 126 | - | fake := &fakeToolkitClipboard{} | 144 | + | toolkit := newToolkitClipboardRecorder(t) |
| 127 | - | newToolkitClipboard = func() toolkitClipboard { return fake } | 145 | + | newToolkitClipboard = func() toolkitClipboard { return toolkit } |
| 128 | adapter := &Adapter{copyCmd: scriptPath} | 146 | adapter := &Adapter{copyCmd: scriptPath} | ||
| 129 | image := entity.ImageData{Bytes: []byte{1, 2, 3}, MimeType: "image/png"} | 147 | image := entity.ImageData{Bytes: []byte{1, 2, 3}, MimeType: "image/png"} | ||
| 130 | 148 | ||||
| 131 | require.NoError(t, adapter.WriteImage(context.Background(), image)) | 149 | require.NoError(t, adapter.WriteImage(context.Background(), image)) | ||
| 132 | - | require.Equal(t, 1, fake.writeImageCalls) | 150 | + | require.Equal(t, 1, toolkit.writeImageCalls) |
| 133 | - | require.Equal(t, image, fake.image) | 151 | + | require.Equal(t, image, toolkit.image) |
| 134 | 152 | ||||
| 135 | _, err := os.Stat(stdinPath) | 153 | _, err := os.Stat(stdinPath) | ||
| 136 | require.Error(t, err) | 154 | require.Error(t, err) | ||
| @@ -193,14 +211,14 @@ func TestAdapter_WriteImageRejectsEmptyBytes(t *testing.T) { | |||||
| 193 | lookPath = oldLookPath | 211 | lookPath = oldLookPath | ||
| 194 | }) | 212 | }) | ||
| 195 | 213 | ||||
| 196 | - | fake := &fakeToolkitClipboard{} | 214 | + | toolkit := newToolkitClipboardRecorder(t) |
| 197 | - | newToolkitClipboard = func() toolkitClipboard { return fake } | 215 | + | newToolkitClipboard = func() toolkitClipboard { return toolkit } |
| 198 | lookPath = func(_ string) (string, error) { return "/usr/bin/clipboard", nil } | 216 | lookPath = func(_ string) (string, error) { return "/usr/bin/clipboard", nil } | ||
| 199 | 217 | ||||
| 200 | err := New().(*Adapter).WriteImage(context.Background(), entity.ImageData{}) | 218 | err := New().(*Adapter).WriteImage(context.Background(), entity.ImageData{}) | ||
| 201 | require.Error(t, err) | 219 | require.Error(t, err) | ||
| 202 | require.Contains(t, err.Error(), "empty image data") | 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 | func TestAdapter_WriteImageWithCommandUsesImageMimeType(t *testing.T) { | 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 | "github.com/bnema/dumber/internal/application/dto" | 10 | "github.com/bnema/dumber/internal/application/dto" | ||
| 11 | "github.com/bnema/dumber/internal/application/port" | 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 | func TestBuildMessageEnvelope(t *testing.T) { | 16 | func TestBuildMessageEnvelope(t *testing.T) { | ||
| @@ -41,8 +43,8 @@ func TestBuildMessageEnvelope(t *testing.T) { | |||||
| 41 | func TestClientSendPrefersNativeTransport(t *testing.T) { | 43 | func TestClientSendPrefersNativeTransport(t *testing.T) { | ||
| 42 | t.Parallel() | 44 | t.Parallel() | ||
| 43 | 45 | ||||
| 44 | - | native := &fakeTransport{available: true, response: []byte(`{"transport":"native"}`)} | 46 | + | native := newTransportRecorder(t, true, []byte(`{"transport":"native"}`)) |
| 45 | - | fetch := &fakeTransport{available: true, response: []byte(`{"transport":"fetch"}`)} | 47 | + | fetch := newTransportRecorder(t, true, []byte(`{"transport":"fetch"}`)) |
| 46 | 48 | ||||
| 47 | client := NewClient(native, fetch) | 49 | client := NewClient(native, fetch) | ||
| 48 | got, err := client.Send(context.Background(), "favorite_list", struct { | 50 | got, err := client.Send(context.Background(), "favorite_list", struct { | ||
| @@ -75,8 +77,8 @@ func TestClientSendPrefersNativeTransport(t *testing.T) { | |||||
| 75 | func TestClientSendFallsBackToFetchTransport(t *testing.T) { | 77 | func TestClientSendFallsBackToFetchTransport(t *testing.T) { | ||
| 76 | t.Parallel() | 78 | t.Parallel() | ||
| 77 | 79 | ||||
| 78 | - | native := &fakeTransport{available: false, response: []byte(`{"transport":"native"}`)} | 80 | + | native := newTransportRecorder(t, false, []byte(`{"transport":"native"}`)) |
| 79 | - | fetch := &fakeTransport{available: true, response: []byte(`{"transport":"fetch"}`)} | 81 | + | fetch := newTransportRecorder(t, true, []byte(`{"transport":"fetch"}`)) |
| 80 | 82 | ||||
| 81 | client := NewClient(native, fetch) | 83 | client := NewClient(native, fetch) | ||
| 82 | got, err := client.Send(context.Background(), "favorite_list", struct { | 84 | got, err := client.Send(context.Background(), "favorite_list", struct { | ||
| @@ -101,7 +103,7 @@ func TestClientSendFallsBackToFetchTransport(t *testing.T) { | |||||
| 101 | func TestClientTimelineDecodesEntries(t *testing.T) { | 103 | func TestClientTimelineDecodesEntries(t *testing.T) { | ||
| 102 | t.Parallel() | 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 | client := NewClient(native, nil) | 107 | client := NewClient(native, nil) | ||
| 106 | 108 | ||||
| 107 | entries, err := client.Timeline(context.Background(), 25, 0) | 109 | entries, err := client.Timeline(context.Background(), 25, 0) | ||
| @@ -130,7 +132,7 @@ func TestClientTimelineDecodesEntries(t *testing.T) { | |||||
| 130 | func TestClientTimelineWindowOmitsZeroCursor(t *testing.T) { | 132 | func TestClientTimelineWindowOmitsZeroCursor(t *testing.T) { | ||
| 131 | t.Parallel() | 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 | client := NewClient(native, nil) | 136 | client := NewClient(native, nil) | ||
| 135 | 137 | ||||
| 136 | _, err := client.TimelineWindow(context.Background(), time.Time{}, "") | 138 | _, err := client.TimelineWindow(context.Background(), time.Time{}, "") | ||
| @@ -157,7 +159,7 @@ func TestClientTimelineWindowOmitsZeroCursor(t *testing.T) { | |||||
| 157 | func TestClientDeleteRangeRejectsEmptyRange(t *testing.T) { | 159 | func TestClientDeleteRangeRejectsEmptyRange(t *testing.T) { | ||
| 158 | t.Parallel() | 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 | client := NewClient(transport, nil) | 163 | client := NewClient(transport, nil) | ||
| 162 | 164 | ||||
| 163 | err := client.DeleteRange(context.Background(), " ") | 165 | err := client.DeleteRange(context.Background(), " ") | ||
| @@ -172,7 +174,7 @@ func TestClientDeleteRangeRejectsEmptyRange(t *testing.T) { | |||||
| 172 | func TestClientDeleteRangeSendsRange(t *testing.T) { | 174 | func TestClientDeleteRangeSendsRange(t *testing.T) { | ||
| 173 | t.Parallel() | 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 | client := NewClient(native, nil) | 178 | client := NewClient(native, nil) | ||
| 177 | 179 | ||||
| 178 | if err := client.DeleteRange(context.Background(), "week"); err != nil { | 180 | if err := client.DeleteRange(context.Background(), "week"); err != nil { | ||
| @@ -191,7 +193,7 @@ func TestClientDeleteRangeSendsRange(t *testing.T) { | |||||
| 191 | func TestClientListDecodesFavorites(t *testing.T) { | 193 | func TestClientListDecodesFavorites(t *testing.T) { | ||
| 192 | t.Parallel() | 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 | client := NewClient(native, nil) | 197 | client := NewClient(native, nil) | ||
| 196 | 198 | ||||
| 197 | favorites, err := client.List(context.Background()) | 199 | favorites, err := client.List(context.Background()) | ||
| @@ -217,7 +219,7 @@ func TestClientListDecodesFavorites(t *testing.T) { | |||||
| 217 | func TestClientListFoldersDecodesFolders(t *testing.T) { | 219 | func TestClientListFoldersDecodesFolders(t *testing.T) { | ||
| 218 | t.Parallel() | 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 | client := NewClient(native, nil) | 223 | client := NewClient(native, nil) | ||
| 222 | 224 | ||||
| 223 | folders, err := client.ListFolders(context.Background()) | 225 | folders, err := client.ListFolders(context.Background()) | ||
| @@ -243,7 +245,7 @@ func TestClientListFoldersDecodesFolders(t *testing.T) { | |||||
| 243 | func TestClientListTagsDecodesTags(t *testing.T) { | 245 | func TestClientListTagsDecodesTags(t *testing.T) { | ||
| 244 | t.Parallel() | 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 | client := NewClient(native, nil) | 249 | client := NewClient(native, nil) | ||
| 248 | 250 | ||||
| 249 | tags, err := client.ListTags(context.Background()) | 251 | tags, err := client.ListTags(context.Background()) | ||
| @@ -271,7 +273,8 @@ func TestClientCurrentAndDefaultDecodeConfigPayload(t *testing.T) { | |||||
| 271 | 273 | ||||
| 272 | resp := []byte(`{"requestId":"req-14","success":true,"data":{"engine_type":"webkit","default_search_engine":"DuckDuckGo"}}`) | 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 | current, err := currentClient.Current(context.Background()) | 278 | current, err := currentClient.Current(context.Background()) | ||
| 276 | if err != nil { | 279 | if err != nil { | ||
| 277 | t.Fatalf("Current() error = %v", err) | 280 | t.Fatalf("Current() error = %v", err) | ||
| @@ -281,14 +284,15 @@ func TestClientCurrentAndDefaultDecodeConfigPayload(t *testing.T) { | |||||
| 281 | } | 284 | } | ||
| 282 | 285 | ||||
| 283 | var msg port.WebUIMessage | 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 | t.Fatalf("unmarshal sent envelope: %v", unmarshalErr) | 288 | t.Fatalf("unmarshal sent envelope: %v", unmarshalErr) | ||
| 286 | } | 289 | } | ||
| 287 | if msg.Type != "/api/config" { | 290 | if msg.Type != "/api/config" { | ||
| 288 | t.Fatalf("sent type = %q, want %q", msg.Type, "/api/config") | 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 | defaultCfg, err := defaultClient.Default(context.Background()) | 296 | defaultCfg, err := defaultClient.Default(context.Background()) | ||
| 293 | if err != nil { | 297 | if err != nil { | ||
| 294 | t.Fatalf("Default() error = %v", err) | 298 | t.Fatalf("Default() error = %v", err) | ||
| @@ -297,7 +301,7 @@ func TestClientCurrentAndDefaultDecodeConfigPayload(t *testing.T) { | |||||
| 297 | t.Fatalf("Default() = %+v", defaultCfg) | 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 | t.Fatalf("unmarshal sent envelope: %v", err) | 305 | t.Fatalf("unmarshal sent envelope: %v", err) | ||
| 302 | } | 306 | } | ||
| 303 | if msg.Type != "/api/config/default" { | 307 | if msg.Type != "/api/config/default" { | ||
| @@ -372,7 +376,7 @@ func TestClientConfigActionsUseExpectedMessageTypes(t *testing.T) { | |||||
| 372 | t.Run(tt.name, func(t *testing.T) { | 376 | t.Run(tt.name, func(t *testing.T) { | ||
| 373 | t.Parallel() | 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 | if tt.name == "save config" || tt.name == "reset keybinding" || tt.name == "reset all keybindings" { | 380 | if tt.name == "save config" || tt.name == "reset keybinding" || tt.name == "reset all keybindings" { | ||
| 377 | transport.response = []byte(`{"requestId":"req-15","success":true}`) | 381 | transport.response = []byte(`{"requestId":"req-15","success":true}`) | ||
| 378 | } | 382 | } | ||
| @@ -418,7 +422,7 @@ func TestClientCreateFolderIncludesParentID(t *testing.T) { | |||||
| 418 | t.Parallel() | 422 | t.Parallel() | ||
| 419 | 423 | ||||
| 420 | parentID := int64(42) | 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 | client := NewClient(transport, nil) | 426 | client := NewClient(transport, nil) | ||
| 423 | 427 | ||||
| 424 | folder, err := client.CreateFolder(context.Background(), "Nested", " 📁 ", &parentID) | 428 | folder, err := client.CreateFolder(context.Background(), "Nested", " 📁 ", &parentID) | ||
| @@ -472,7 +476,7 @@ func TestClientCreateFolderIncludesParentID(t *testing.T) { | |||||
| 472 | func TestClientDomainStatsRejectsNegativeLimit(t *testing.T) { | 476 | func TestClientDomainStatsRejectsNegativeLimit(t *testing.T) { | ||
| 473 | t.Parallel() | 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 | client := NewClient(transport, nil) | 480 | client := NewClient(transport, nil) | ||
| 477 | 481 | ||||
| 478 | _, err := client.DomainStats(context.Background(), -1) | 482 | _, err := client.DomainStats(context.Background(), -1) | ||
| @@ -487,7 +491,7 @@ func TestClientDomainStatsRejectsNegativeLimit(t *testing.T) { | |||||
| 487 | func TestClientDeleteDomainRejectsEmptyDomain(t *testing.T) { | 491 | func TestClientDeleteDomainRejectsEmptyDomain(t *testing.T) { | ||
| 488 | t.Parallel() | 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 | client := NewClient(transport, nil) | 495 | client := NewClient(transport, nil) | ||
| 492 | 496 | ||||
| 493 | err := client.DeleteDomain(context.Background(), " ") | 497 | err := client.DeleteDomain(context.Background(), " ") | ||
| @@ -502,7 +506,7 @@ func TestClientDeleteDomainRejectsEmptyDomain(t *testing.T) { | |||||
| 502 | func TestClientUpdateFolderTrimsIcon(t *testing.T) { | 506 | func TestClientUpdateFolderTrimsIcon(t *testing.T) { | ||
| 503 | t.Parallel() | 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 | client := NewClient(transport, nil) | 510 | client := NewClient(transport, nil) | ||
| 507 | 511 | ||||
| 508 | if err := client.UpdateFolder(context.Background(), 9, "Read", " 📚 "); err != nil { | 512 | if err := client.UpdateFolder(context.Background(), 9, "Read", " 📚 "); err != nil { | ||
| @@ -530,7 +534,7 @@ func TestClientUpdateFolderTrimsIcon(t *testing.T) { | |||||
| 530 | func TestClientUpdateFolderOmitsBlankIcon(t *testing.T) { | 534 | func TestClientUpdateFolderOmitsBlankIcon(t *testing.T) { | ||
| 531 | t.Parallel() | 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 | client := NewClient(transport, nil) | 538 | client := NewClient(transport, nil) | ||
| 535 | 539 | ||||
| 536 | if err := client.UpdateFolder(context.Background(), 9, "Read", " "); err != nil { | 540 | if err := client.UpdateFolder(context.Background(), 9, "Read", " "); err != nil { | ||
| @@ -555,7 +559,7 @@ func TestClientUpdateFolderOmitsBlankIcon(t *testing.T) { | |||||
| 555 | func TestClientDeleteEntryRejectsInvalidID(t *testing.T) { | 559 | func TestClientDeleteEntryRejectsInvalidID(t *testing.T) { | ||
| 556 | t.Parallel() | 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 | client := NewClient(transport, nil) | 563 | client := NewClient(transport, nil) | ||
| 560 | 564 | ||||
| 561 | err := client.DeleteEntry(context.Background(), 0) | 565 | err := client.DeleteEntry(context.Background(), 0) | ||
| @@ -570,7 +574,7 @@ func TestClientDeleteEntryRejectsInvalidID(t *testing.T) { | |||||
| 570 | func TestClientTagActionsTrimFields(t *testing.T) { | 574 | func TestClientTagActionsTrimFields(t *testing.T) { | ||
| 571 | t.Parallel() | 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 | client := NewClient(createTransport, nil) | 578 | client := NewClient(createTransport, nil) | ||
| 575 | if _, err := client.CreateTag(context.Background(), " Go ", " #00add8 "); err != nil { | 579 | if _, err := client.CreateTag(context.Background(), " Go ", " #00add8 "); err != nil { | ||
| 576 | t.Fatalf("CreateTag() error = %v", err) | 580 | t.Fatalf("CreateTag() error = %v", err) | ||
| @@ -590,7 +594,7 @@ func TestClientTagActionsTrimFields(t *testing.T) { | |||||
| 590 | t.Fatalf("CreateTag payload = %#v", createPayload) | 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 | client = NewClient(updateTransport, nil) | 598 | client = NewClient(updateTransport, nil) | ||
| 595 | if err := client.UpdateTag(context.Background(), 5, " Go ", " "); err != nil { | 599 | if err := client.UpdateTag(context.Background(), 5, " Go ", " "); err != nil { | ||
| 596 | t.Fatalf("UpdateTag() error = %v", err) | 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 | available bool | 620 | available bool | ||
| 615 | called bool | 621 | called bool | ||
| 616 | last []byte | 622 | last []byte | ||
| 617 | response []byte | 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 | t.called = true | 640 | t.called = true | ||
| 624 | t.last = append(t.last[:0], body...) | 641 | t.last = append(t.last[:0], body...) | ||
| 625 | return t.response, nil | 642 | return t.response, nil | ||
| @@ -641,7 +658,7 @@ func (t *blockingTransport) Send(ctx context.Context, _ []byte) ([]byte, error) | |||||
| 641 | func TestClientRejectsInvalidFavoriteMutationIDs(t *testing.T) { | 658 | func TestClientRejectsInvalidFavoriteMutationIDs(t *testing.T) { | ||
| 642 | t.Parallel() | 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 | client := NewClient(transport, nil) | 662 | client := NewClient(transport, nil) | ||
| 646 | 663 | ||||
| 647 | checks := []struct { | 664 | checks := []struct { | ||
| @@ -679,7 +696,7 @@ func TestClientRejectsInvalidFavoriteMutationIDs(t *testing.T) { | |||||
| 679 | func TestClientRejectsNegativeHistoryLimits(t *testing.T) { | 696 | func TestClientRejectsNegativeHistoryLimits(t *testing.T) { | ||
| 680 | t.Parallel() | 697 | t.Parallel() | ||
| 681 | 698 | ||||
| 682 | - | client := NewClient(&fakeTransport{available: true}, nil) | 699 | + | client := NewClient(newTransportRecorder(t, true, nil), nil) |
| 683 | 700 | ||||
| 684 | if _, err := client.Timeline(context.Background(), -1, 0); err == nil { | 701 | if _, err := client.Timeline(context.Background(), -1, 0); err == nil { | ||
| 685 | t.Fatal("Timeline() error = nil, want error") | 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 | applier := NewApplier(tmpDir) | 122 | applier := NewApplier(tmpDir) | ||
| 123 | ctx := context.Background() | 123 | ctx := context.Background() | ||
| 124 | 124 | ||||
| 125 | - | // Create a fake binary to stage | 125 | + | // Create a dummy binary to stage |
| 126 | srcBinary := filepath.Join(tmpDir, "new-binary") | 126 | srcBinary := filepath.Join(tmpDir, "new-binary") | ||
| 127 | binaryContent := []byte("#!/bin/sh\necho 'new version'\n") | 127 | binaryContent := []byte("#!/bin/sh\necho 'new version'\n") | ||
| 128 | require.NoError(t, os.WriteFile(srcBinary, binaryContent, 0o755)) | 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 | // methods were called and with what arguments. It does not use reflect or unsafe. | 2445 | // methods were called and with what arguments. It does not use reflect or unsafe. | ||
| 2446 | - | type fakeRecordingWebView struct { | 2446 | + | type recordingWebView struct { |
| 2447 | id port.WebViewID | 2447 | id port.WebViewID | ||
| 2448 | 2448 | ||||
| 2449 | loadURICalled bool | 2449 | loadURICalled bool | ||
| @@ -2461,79 +2461,79 @@ type fakeRecordingWebView struct { | |||||
| 2461 | destroyCalls int | 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 | f.loadURICalled = true | 2467 | f.loadURICalled = true | ||
| 2468 | f.loadURILastURI = uri | 2468 | f.loadURILastURI = uri | ||
| 2469 | return nil | 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 | f.loadHTMLCalls++ | 2473 | f.loadHTMLCalls++ | ||
| 2474 | return nil | 2474 | return nil | ||
| 2475 | } | 2475 | } | ||
| 2476 | 2476 | ||||
| 2477 | - | func (f *fakeRecordingWebView) Reload(_ context.Context) error { | 2477 | + | func (f *recordingWebView) Reload(_ context.Context) error { |
| 2478 | f.reloadCalls++ | 2478 | f.reloadCalls++ | ||
| 2479 | return nil | 2479 | return nil | ||
| 2480 | } | 2480 | } | ||
| 2481 | 2481 | ||||
| 2482 | - | func (f *fakeRecordingWebView) ReloadBypassCache(_ context.Context) error { | 2482 | + | func (f *recordingWebView) ReloadBypassCache(_ context.Context) error { |
| 2483 | f.reloadBypassCacheCalls++ | 2483 | f.reloadBypassCacheCalls++ | ||
| 2484 | return nil | 2484 | return nil | ||
| 2485 | } | 2485 | } | ||
| 2486 | 2486 | ||||
| 2487 | - | func (f *fakeRecordingWebView) Stop(_ context.Context) error { | 2487 | + | func (f *recordingWebView) Stop(_ context.Context) error { |
| 2488 | f.stopCalls++ | 2488 | f.stopCalls++ | ||
| 2489 | return nil | 2489 | return nil | ||
| 2490 | } | 2490 | } | ||
| 2491 | 2491 | ||||
| 2492 | - | func (f *fakeRecordingWebView) GoBack(_ context.Context) error { | 2492 | + | func (f *recordingWebView) GoBack(_ context.Context) error { |
| 2493 | f.goBackCalls++ | 2493 | f.goBackCalls++ | ||
| 2494 | return nil | 2494 | return nil | ||
| 2495 | } | 2495 | } | ||
| 2496 | 2496 | ||||
| 2497 | - | func (f *fakeRecordingWebView) GoForward(_ context.Context) error { | 2497 | + | func (f *recordingWebView) GoForward(_ context.Context) error { |
| 2498 | f.goForwardCalls++ | 2498 | f.goForwardCalls++ | ||
| 2499 | return nil | 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 } | 2504 | + | func (f *recordingWebView) URI() string { return f.loadURILastURI } |
| 2505 | - | func (f *fakeRecordingWebView) Title() string { return "" } | 2505 | + | func (f *recordingWebView) Title() string { return "" } |
| 2506 | 2506 | ||||
| 2507 | - | func (f *fakeRecordingWebView) IsLoading() bool { return false } | 2507 | + | func (f *recordingWebView) IsLoading() bool { return false } |
| 2508 | - | func (f *fakeRecordingWebView) EstimatedProgress() float64 { return 0 } | 2508 | + | func (f *recordingWebView) EstimatedProgress() float64 { return 0 } |
| 2509 | - | func (f *fakeRecordingWebView) CanGoBack() bool { return false } | 2509 | + | func (f *recordingWebView) CanGoBack() bool { return false } |
| 2510 | - | func (f *fakeRecordingWebView) CanGoForward() 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 | f.setZoomLevelCalls++ | 2513 | f.setZoomLevelCalls++ | ||
| 2514 | f.setZoomLevelLastLevel = level | 2514 | f.setZoomLevelLastLevel = level | ||
| 2515 | return nil | 2515 | return nil | ||
| 2516 | } | 2516 | } | ||
| 2517 | 2517 | ||||
| 2518 | - | func (f *fakeRecordingWebView) OpenDevTools() { f.openDevToolsCalls++ } | 2518 | + | func (f *recordingWebView) OpenDevTools() { f.openDevToolsCalls++ } |
| 2519 | - | func (f *fakeRecordingWebView) PrintPage() { f.printPageCalls++ } | 2519 | + | func (f *recordingWebView) PrintPage() { f.printPageCalls++ } |
| 2520 | 2520 | ||||
| 2521 | - | func (f *fakeRecordingWebView) GetZoomLevel() float64 { return 1.0 } | 2521 | + | func (f *recordingWebView) GetZoomLevel() float64 { return 1.0 } |
| 2522 | - | func (f *fakeRecordingWebView) GetFindController() port.FindController { return nil } | 2522 | + | func (f *recordingWebView) GetFindController() port.FindController { return nil } |
| 2523 | - | func (f *fakeRecordingWebView) SetCallbacks(_ *port.WebViewCallbacks) {} | 2523 | + | func (f *recordingWebView) SetCallbacks(_ *port.WebViewCallbacks) {} |
| 2524 | - | func (f *fakeRecordingWebView) RunJavaScript(_ context.Context, _ string) {} | 2524 | + | func (f *recordingWebView) RunJavaScript(_ context.Context, _ string) {} |
| 2525 | - | func (f *fakeRecordingWebView) SetBackgroundColor(_, _, _, _ float64) {} | 2525 | + | func (f *recordingWebView) SetBackgroundColor(_, _, _, _ float64) {} |
| 2526 | - | func (f *fakeRecordingWebView) ResetBackgroundToDefault() {} | 2526 | + | func (f *recordingWebView) ResetBackgroundToDefault() {} |
| 2527 | - | func (f *fakeRecordingWebView) Favicon() port.Texture { return nil } | 2527 | + | func (f *recordingWebView) Favicon() port.Texture { return nil } |
| 2528 | - | func (f *fakeRecordingWebView) Generation() uint64 { return 0 } | 2528 | + | func (f *recordingWebView) Generation() uint64 { return 0 } |
| 2529 | - | func (f *fakeRecordingWebView) IsFullscreen() bool { return false } | 2529 | + | func (f *recordingWebView) IsFullscreen() bool { return false } |
| 2530 | - | func (f *fakeRecordingWebView) IsPlayingAudio() bool { return false } | 2530 | + | func (f *recordingWebView) IsPlayingAudio() bool { return false } |
| 2531 | - | func (f *fakeRecordingWebView) IsDestroyed() bool { return f.destroyCalls > 0 } | 2531 | + | func (f *recordingWebView) IsDestroyed() bool { return f.destroyCalls > 0 } |
| 2532 | - | func (f *fakeRecordingWebView) Destroy() { f.destroyCalls++ } | 2532 | + | func (f *recordingWebView) Destroy() { f.destroyCalls++ } |
| 2533 | 2533 | ||||
| 2534 | func TestApp_AttachPopupToTabDestroysPopupWhenWorkspaceViewMissing(t *testing.T) { | 2534 | func TestApp_AttachPopupToTabDestroysPopupWhenWorkspaceViewMissing(t *testing.T) { | ||
| 2535 | ctx := context.Background() | 2535 | ctx := context.Background() | ||
| 2536 | - | popupWV := &fakeRecordingWebView{id: 1} | 2536 | + | popupWV := &recordingWebView{id: 1} |
| 2537 | app := &App{workspaceViews: map[entity.TabID]*component.WorkspaceView{}} | 2537 | app := &App{workspaceViews: map[entity.TabID]*component.WorkspaceView{}} | ||
| 2538 | 2538 | ||||
| 2539 | app.attachPopupToTab(ctx, entity.TabID("missing-tab"), entity.NewPane(entity.PaneID("popup-pane")), popupWV) | 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 | func TestApp_AttachPopupToTabDestroysPopupWhenPaneNil(t *testing.T) { | 2546 | func TestApp_AttachPopupToTabDestroysPopupWhenPaneNil(t *testing.T) { | ||
| 2547 | ctx := context.Background() | 2547 | ctx := context.Background() | ||
| 2548 | - | popupWV := &fakeRecordingWebView{id: 1} | 2548 | + | popupWV := &recordingWebView{id: 1} |
| 2549 | tabID := entity.TabID("tab-1") | 2549 | tabID := entity.TabID("tab-1") | ||
| 2550 | app := &App{workspaceViews: map[entity.TabID]*component.WorkspaceView{tabID: &component.WorkspaceView{}}} | 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 | app.attachPopupToTab(ctx, tabID, pane, popupWV) | 2572 | app.attachPopupToTab(ctx, tabID, pane, popupWV) | ||
| 2573 | 2573 | ||||
| 2574 | if got := contentCoord.GetWebView(pane.ID); got != nil { | 2574 | if got := contentCoord.GetWebView(pane.ID); got != nil { | ||
| @@ -2608,7 +2608,7 @@ func TestApp_AttachPopupToTabReleasesRegistrationWhenWrapFails(t *testing.T) { | |||||
| 2608 | tabID: wsView, | 2608 | tabID: wsView, | ||
| 2609 | }, | 2609 | }, | ||
| 2610 | } | 2610 | } | ||
| 2611 | - | popupWV := &fakeRecordingWebView{id: 1} | 2611 | + | popupWV := &recordingWebView{id: 1} |
| 2612 | 2612 | ||||
| 2613 | app.attachPopupToTab(ctx, tabID, pane, popupWV) | 2613 | app.attachPopupToTab(ctx, tabID, pane, popupWV) | ||
| 2614 | 2614 | ||||
| @@ -2637,15 +2637,15 @@ func TestApp_BrowserWindowWebViewActionsIgnoreStaleFocusedWindow(t *testing.T) { | |||||
| 2637 | secondTabs.SetActive(tab2.ID) | 2637 | secondTabs.SetActive(tab2.ID) | ||
| 2638 | second := &browserWindow{id: "window-2", tabs: secondTabs} | 2638 | second := &browserWindow{id: "window-2", tabs: secondTabs} | ||
| 2639 | 2639 | ||||
| 2640 | - | // Create fake recording webviews. | 2640 | + | // Create recording webviews. |
| 2641 | - | fakeWv1 := &fakeRecordingWebView{id: 1} | 2641 | + | recordingWv1 := &recordingWebView{id: 1} |
| 2642 | - | fakeWv2 := &fakeRecordingWebView{id: 2} | 2642 | + | recordingWv2 := &recordingWebView{id: 2} |
| 2643 | 2643 | ||||
| 2644 | // Create content coordinator with initialized internal maps to avoid | 2644 | // Create content coordinator with initialized internal maps to avoid | ||
| 2645 | // nil-map panics in SetNavigationOrigin during NavigateWebView calls. | 2645 | // nil-map panics in SetNavigationOrigin during NavigateWebView calls. | ||
| 2646 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | 2646 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | ||
| 2647 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv1) | 2647 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv1) |
| 2648 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), fakeWv2) | 2648 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), recordingWv2) |
| 2649 | 2649 | ||||
| 2650 | // Create NavigationCoordinator (no navigateUC needed for direct webview calls). | 2650 | // Create NavigationCoordinator (no navigateUC needed for direct webview calls). | ||
| 2651 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | 2651 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | ||
| @@ -2684,11 +2684,11 @@ func TestApp_BrowserWindowWebViewActionsIgnoreStaleFocusedWindow(t *testing.T) { | |||||
| 2684 | t.Fatalf("goForwardBrowserWindow returned error: %v", err) | 2684 | t.Fatalf("goForwardBrowserWindow returned error: %v", err) | ||
| 2685 | } | 2685 | } | ||
| 2686 | 2686 | ||||
| 2687 | - | assertRecordingWebViewIdle(t, "first", fakeWv1) | 2687 | + | assertRecordingWebViewIdle(t, "first", recordingWv1) |
| 2688 | - | assertRecordingWebViewActions(t, "second", fakeWv2, "https://google.com") | 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 | t.Helper() | 2692 | t.Helper() | ||
| 2693 | if wv.loadURICalled || wv.loadURILastURI != "" { | 2693 | if wv.loadURICalled || wv.loadURILastURI != "" { | ||
| 2694 | t.Errorf("%s webview LoadURI was called, expected zero calls", name) | 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 | t.Helper() | 2714 | t.Helper() | ||
| 2715 | if !wv.loadURICalled { | 2715 | if !wv.loadURICalled { | ||
| 2716 | t.Errorf("%s webview LoadURI was not called", name) | 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 | t.Helper() | 2739 | t.Helper() | ||
| 2740 | if wv.reloadCalls > 0 { | 2740 | if wv.reloadCalls > 0 { | ||
| 2741 | t.Errorf("%s webview Reload calls = %d, want 0", name, wv.reloadCalls) | 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 | t.Helper() | 2767 | t.Helper() | ||
| 2768 | if wv.reloadCalls != 1 { | 2768 | if wv.reloadCalls != 1 { | ||
| 2769 | t.Errorf("%s webview Reload calls = %d, want 1", name, wv.reloadCalls) | 2769 | t.Errorf("%s webview Reload calls = %d, want 1", name, wv.reloadCalls) | ||
| @@ -2837,14 +2837,14 @@ func TestApp_DispatchBrowserWindowActionUsesSourceWindow(t *testing.T) { | |||||
| 2837 | secondTabs.SetActive(tab2.ID) | 2837 | secondTabs.SetActive(tab2.ID) | ||
| 2838 | second := &browserWindow{id: "window-2", tabs: secondTabs} | 2838 | second := &browserWindow{id: "window-2", tabs: secondTabs} | ||
| 2839 | 2839 | ||||
| 2840 | - | // Create fake recording webviews. | 2840 | + | // Create recording webviews. |
| 2841 | - | fakeWv1 := &fakeRecordingWebView{id: 1, loadURILastURI: "https://first.example"} | 2841 | + | recordingWv1 := &recordingWebView{id: 1, loadURILastURI: "https://first.example"} |
| 2842 | - | fakeWv2 := &fakeRecordingWebView{id: 2, loadURILastURI: "https://second.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 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | 2845 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | ||
| 2846 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv1) | 2846 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv1) |
| 2847 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), fakeWv2) | 2847 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), recordingWv2) |
| 2848 | 2848 | ||||
| 2849 | // Create navCoord (no navigateUC needed for direct webview calls). | 2849 | // Create navCoord (no navigateUC needed for direct webview calls). | ||
| 2850 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | 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). | 2883 | + | // Assert first recording webview receives zero calls (stale focus is ignored). |
| 2884 | - | assertRecordingWebViewBrowserActionsIdle(t, "first", fakeWv1) | 2884 | + | assertRecordingWebViewBrowserActionsIdle(t, "first", recordingWv1) |
| 2885 | 2885 | ||||
| 2886 | - | // Assert second fake webview receives exactly one call per action. | 2886 | + | // Assert second recording webview receives exactly one call per action. |
| 2887 | - | assertRecordingWebViewBrowserActionsCalledOnce(t, "second", fakeWv2) | 2887 | + | assertRecordingWebViewBrowserActionsCalledOnce(t, "second", recordingWv2) |
| 2888 | } | 2888 | } | ||
| 2889 | 2889 | ||||
| 2890 | func TestApp_DispatchBrowserWindowActionZoomInSupportsFileURLs(t *testing.T) { | 2890 | func TestApp_DispatchBrowserWindowActionZoomInSupportsFileURLs(t *testing.T) { | ||
| @@ -2896,9 +2896,9 @@ func TestApp_DispatchBrowserWindowActionZoomInSupportsFileURLs(t *testing.T) { | |||||
| 2896 | tabs.SetActive(tab.ID) | 2896 | tabs.SetActive(tab.ID) | ||
| 2897 | bw := &browserWindow{id: "window-1", tabs: tabs} | 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 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | 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 | app := &App{ | 2903 | app := &App{ | ||
| 2904 | browserWindows: map[string]*browserWindow{bw.id: bw}, | 2904 | browserWindows: map[string]*browserWindow{bw.id: bw}, | ||
| @@ -2916,11 +2916,11 @@ func TestApp_DispatchBrowserWindowActionZoomInSupportsFileURLs(t *testing.T) { | |||||
| 2916 | if err := app.dispatchBrowserWindowAction(ctx, bw, input.ActionZoomIn); err != nil { | 2916 | if err := app.dispatchBrowserWindowAction(ctx, bw, input.ActionZoomIn); err != nil { | ||
| 2917 | t.Fatalf("dispatchBrowserWindowAction returned error: %v", err) | 2917 | t.Fatalf("dispatchBrowserWindowAction returned error: %v", err) | ||
| 2918 | } | 2918 | } | ||
| 2919 | - | if fakeWv.setZoomLevelCalls != 1 { | 2919 | + | if recordingWv.setZoomLevelCalls != 1 { |
| 2920 | - | t.Fatalf("webview SetZoomLevel calls = %d, want 1", fakeWv.setZoomLevelCalls) | 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 { | 2922 | + | if diff := math.Abs(recordingWv.setZoomLevelLastLevel - 1.1); diff > 0.0001 { |
| 2923 | - | t.Fatalf("webview SetZoomLevel level = %f, want 1.1", fakeWv.setZoomLevelLastLevel) | 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 | first := &browserWindow{id: "window-1", tabs: firstTabs} | 3161 | first := &browserWindow{id: "window-1", tabs: firstTabs} | ||
| 3162 | second := &browserWindow{id: "window-2", tabs: secondTabs} | 3162 | second := &browserWindow{id: "window-2", tabs: secondTabs} | ||
| 3163 | 3163 | ||||
| 3164 | - | fakeWv1 := &fakeRecordingWebView{id: 1} | 3164 | + | recordingWv1 := &recordingWebView{id: 1} |
| 3165 | - | fakeWv2 := &fakeRecordingWebView{id: 2} | 3165 | + | recordingWv2 := &recordingWebView{id: 2} |
| 3166 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | 3166 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | ||
| 3167 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv1) | 3167 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv1) |
| 3168 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), fakeWv2) | 3168 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), recordingWv2) |
| 3169 | 3169 | ||||
| 3170 | app := &App{ | 3170 | app := &App{ | ||
| 3171 | deps: &Dependencies{Config: &config.Config{}}, | 3171 | deps: &Dependencies{Config: &config.Config{}}, | ||
| @@ -3189,11 +3189,11 @@ func TestApp_WorkspaceOmniboxNavigateUsesOwnerWindow(t *testing.T) { | |||||
| 3189 | t.Fatalf("omnibox navigate callback returned error: %v", err) | 3189 | t.Fatalf("omnibox navigate callback returned error: %v", err) | ||
| 3190 | } | 3190 | } | ||
| 3191 | 3191 | ||||
| 3192 | - | if fakeWv1.loadURICalled { | 3192 | + | if recordingWv1.loadURICalled { |
| 3193 | - | t.Errorf("first window webview navigated to %q, want no navigation", fakeWv1.loadURILastURI) | 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" { | 3195 | + | if !recordingWv2.loadURICalled || recordingWv2.loadURILastURI != "https://example.com" { |
| 3196 | - | t.Errorf("second window webview navigation = called %v uri %q, want https://example.com", fakeWv2.loadURICalled, fakeWv2.loadURILastURI) | 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 | secondTabs.SetActive(tab2.ID) | 41 | secondTabs.SetActive(tab2.ID) | ||
| 42 | second := &browserWindow{id: "window-2", tabs: secondTabs} | 42 | second := &browserWindow{id: "window-2", tabs: secondTabs} | ||
| 43 | 43 | ||||
| 44 | - | // Create fake webviews, one per pane. | 44 | + | // Create recording webviews, one per pane. |
| 45 | - | fakeWv1 := &fakeRecordingWebView{id: 1} | 45 | + | recordingWv1 := &recordingWebView{id: 1} |
| 46 | - | fakeWv2 := &fakeRecordingWebView{id: 2} | 46 | + | recordingWv2 := &recordingWebView{id: 2} |
| 47 | 47 | ||||
| 48 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | 48 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | ||
| 49 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv1) | 49 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv1) |
| 50 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), fakeWv2) | 50 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), recordingWv2) |
| 51 | 51 | ||||
| 52 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | 52 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | ||
| 53 | 53 | ||||
| @@ -75,12 +75,12 @@ func TestHistorySidebarConfig_OnNavigateNavigatesActivePaneAndKeepsSidebar(t *te | |||||
| 75 | require.NoError(t, err, "OnNavigate should succeed") | 75 | require.NoError(t, err, "OnNavigate should succeed") | ||
| 76 | 76 | ||||
| 77 | // The second window's webview must have received the navigation. | 77 | // The second window's webview must have received the navigation. | ||
| 78 | - | assert.True(t, fakeWv2.loadURICalled, "second window webview should receive navigation") | 78 | + | assert.True(t, recordingWv2.loadURICalled, "second window webview should receive navigation") |
| 79 | - | assert.Equal(t, navigateURL, fakeWv2.loadURILastURI) | 79 | + | assert.Equal(t, navigateURL, recordingWv2.loadURILastURI) |
| 80 | assert.True(t, second.sidebarVisible, "default history activation should keep the sidebar visible") | 80 | assert.True(t, second.sidebarVisible, "default history activation should keep the sidebar visible") | ||
| 81 | 81 | ||||
| 82 | // The first window's webview must NOT have been touched (stale-focus guard). | 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 | // TestHistorySidebarConfig_OnNavigateKeepOpenNavigatesWithoutClosing verifies | 86 | // TestHistorySidebarConfig_OnNavigateKeepOpenNavigatesWithoutClosing verifies | ||
| @@ -103,8 +103,8 @@ func TestHistorySidebarConfig_OnNavigateKeepOpenNavigatesWithoutClosing(t *testi | |||||
| 103 | } | 103 | } | ||
| 104 | 104 | ||||
| 105 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | 105 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | ||
| 106 | - | fakeWv := &fakeRecordingWebView{id: 1} | 106 | + | recordingWv := &recordingWebView{id: 1} |
| 107 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv) | 107 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv) |
| 108 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | 108 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | ||
| 109 | 109 | ||||
| 110 | app := &App{ | 110 | app := &App{ | ||
| @@ -122,8 +122,8 @@ func TestHistorySidebarConfig_OnNavigateKeepOpenNavigatesWithoutClosing(t *testi | |||||
| 122 | navigateURL := "https://keep-open.com" | 122 | navigateURL := "https://keep-open.com" | ||
| 123 | err := cfg.OnNavigateKeepOpen(ctx, navigateURL) | 123 | err := cfg.OnNavigateKeepOpen(ctx, navigateURL) | ||
| 124 | require.NoError(t, err) | 124 | require.NoError(t, err) | ||
| 125 | - | assert.True(t, fakeWv.loadURICalled) | 125 | + | assert.True(t, recordingWv.loadURICalled) |
| 126 | - | assert.Equal(t, navigateURL, fakeWv.loadURILastURI, "Ctrl+Enter navigation should go to the URL") | 126 | + | assert.Equal(t, navigateURL, recordingWv.loadURILastURI, "Ctrl+Enter navigation should go to the URL") |
| 127 | assert.True(t, bw.sidebarVisible, "Ctrl+Enter navigation should keep the sidebar visible") | 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 | secondTabs.SetActive(tab2.ID) | 148 | secondTabs.SetActive(tab2.ID) | ||
| 149 | second := &browserWindow{id: "window-2", tabs: secondTabs} | 149 | second := &browserWindow{id: "window-2", tabs: secondTabs} | ||
| 150 | 150 | ||||
| 151 | - | fakeWv1 := &fakeRecordingWebView{id: 1} | 151 | + | recordingWv1 := &recordingWebView{id: 1} |
| 152 | - | fakeWv2 := &fakeRecordingWebView{id: 2} | 152 | + | recordingWv2 := &recordingWebView{id: 2} |
| 153 | 153 | ||||
| 154 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | 154 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | ||
| 155 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv1) | 155 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv1) |
| 156 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), fakeWv2) | 156 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), recordingWv2) |
| 157 | 157 | ||||
| 158 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | 158 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | ||
| 159 | 159 | ||||
| @@ -177,11 +177,11 @@ func TestHistorySidebar_OwnershipOnMultiWindowNavigation(t *testing.T) { | |||||
| 177 | require.NoError(t, err) | 177 | require.NoError(t, err) | ||
| 178 | 178 | ||||
| 179 | // Second window's webview must receive the navigation. | 179 | // Second window's webview must receive the navigation. | ||
| 180 | - | assert.True(t, fakeWv2.loadURICalled, "second window webview should receive navigation") | 180 | + | assert.True(t, recordingWv2.loadURICalled, "second window webview should receive navigation") |
| 181 | - | assert.Equal(t, "https://second-window.com", fakeWv2.loadURILastURI) | 181 | + | assert.Equal(t, "https://second-window.com", recordingWv2.loadURILastURI) |
| 182 | 182 | ||||
| 183 | // First window's webview must NOT have been touched. | 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 | sidebarVisible: true, | 509 | sidebarVisible: true, | ||
| 510 | } | 510 | } | ||
| 511 | 511 | ||||
| 512 | - | fakeWv := &fakeRecordingWebView{id: 1} | 512 | + | recordingWv := &recordingWebView{id: 1} |
| 513 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | 513 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | ||
| 514 | - | contentCoord.RegisterPopupWebView(paneID, fakeWv) | 514 | + | contentCoord.RegisterPopupWebView(paneID, recordingWv) |
| 515 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | 515 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | ||
| 516 | 516 | ||||
| 517 | app := &App{ | 517 | app := &App{ | ||
| @@ -535,8 +535,8 @@ func TestApp_HistorySidebarConfig_NavigateCallbackNavigates(t *testing.T) { | |||||
| 535 | require.NoError(t, err) | 535 | require.NoError(t, err) | ||
| 536 | 536 | ||||
| 537 | // Verify the navigation reached the correct webview. | 537 | // Verify the navigation reached the correct webview. | ||
| 538 | - | assert.True(t, fakeWv.loadURICalled, "webview must receive navigation") | 538 | + | assert.True(t, recordingWv.loadURICalled, "webview must receive navigation") |
| 539 | - | assert.Equal(t, navigateURL, fakeWv.loadURILastURI) | 539 | + | assert.Equal(t, navigateURL, recordingWv.loadURILastURI) |
| 540 | assert.True(t, bw.sidebarVisible, "default history navigation should keep the sidebar open") | 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 | sidebarVisible: true, | 557 | sidebarVisible: true, | ||
| 558 | } | 558 | } | ||
| 559 | 559 | ||||
| 560 | - | fakeWv := &fakeRecordingWebView{id: 1} | 560 | + | recordingWv := &recordingWebView{id: 1} |
| 561 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | 561 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | ||
| 562 | - | contentCoord.RegisterPopupWebView(paneID, fakeWv) | 562 | + | contentCoord.RegisterPopupWebView(paneID, recordingWv) |
| 563 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | 563 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | ||
| 564 | 564 | ||||
| 565 | app := &App{ | 565 | app := &App{ | ||
| @@ -575,8 +575,8 @@ func TestApp_NavigateHistorySidebarSelection_KeepsSidebarVisible(t *testing.T) { | |||||
| 575 | 575 | ||||
| 576 | err := app.navigateHistorySidebarSelection(ctx, bw, "https://open.com") | 576 | err := app.navigateHistorySidebarSelection(ctx, bw, "https://open.com") | ||
| 577 | require.NoError(t, err) | 577 | require.NoError(t, err) | ||
| 578 | - | assert.True(t, fakeWv.loadURICalled) | 578 | + | assert.True(t, recordingWv.loadURICalled) |
| 579 | - | assert.Equal(t, "https://open.com", fakeWv.loadURILastURI) | 579 | + | assert.Equal(t, "https://open.com", recordingWv.loadURILastURI) |
| 580 | assert.True(t, bw.sidebarVisible, "history selection navigation should not hide the sidebar") | 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 | secondTabs.SetActive(tab2.ID) | 599 | secondTabs.SetActive(tab2.ID) | ||
| 600 | second := &browserWindow{id: "window-2", tabs: secondTabs} | 600 | second := &browserWindow{id: "window-2", tabs: secondTabs} | ||
| 601 | 601 | ||||
| 602 | - | fakeWv1 := &fakeRecordingWebView{id: 1} | 602 | + | recordingWv1 := &recordingWebView{id: 1} |
| 603 | - | fakeWv2 := &fakeRecordingWebView{id: 2} | 603 | + | recordingWv2 := &recordingWebView{id: 2} |
| 604 | 604 | ||||
| 605 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | 605 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | ||
| 606 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), fakeWv1) | 606 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-1"), recordingWv1) |
| 607 | - | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), fakeWv2) | 607 | + | contentCoord.RegisterPopupWebView(entity.PaneID("pane-2"), recordingWv2) |
| 608 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | 608 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | ||
| 609 | 609 | ||||
| 610 | app := &App{ | 610 | app := &App{ | ||
| @@ -629,11 +629,11 @@ func TestApp_HistorySidebarConfig_NavigateCallbackOwnership(t *testing.T) { | |||||
| 629 | require.NoError(t, err) | 629 | require.NoError(t, err) | ||
| 630 | 630 | ||||
| 631 | // Second window's webview must receive navigation. | 631 | // Second window's webview must receive navigation. | ||
| 632 | - | assert.True(t, fakeWv2.loadURICalled, "second window webview must receive navigation") | 632 | + | assert.True(t, recordingWv2.loadURICalled, "second window webview must receive navigation") |
| 633 | - | assert.Equal(t, "https://ownership.com", fakeWv2.loadURILastURI) | 633 | + | assert.Equal(t, "https://ownership.com", recordingWv2.loadURILastURI) |
| 634 | 634 | ||||
| 635 | // First window's webview must NOT be touched. | 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 | // TestApp_HistorySidebarConfig_KeepOpenCallback verifies that | 639 | // TestApp_HistorySidebarConfig_KeepOpenCallback verifies that | ||
| @@ -655,9 +655,9 @@ func TestApp_HistorySidebarConfig_KeepOpenCallback(t *testing.T) { | |||||
| 655 | sidebarVisible: true, | 655 | sidebarVisible: true, | ||
| 656 | } | 656 | } | ||
| 657 | 657 | ||||
| 658 | - | fakeWv := &fakeRecordingWebView{id: 1} | 658 | + | recordingWv := &recordingWebView{id: 1} |
| 659 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | 659 | contentCoord := contentcoord.NewCoordinator(ctx, nil, nil, nil, nil, nil, nil, nil) | ||
| 660 | - | contentCoord.RegisterPopupWebView(paneID, fakeWv) | 660 | + | contentCoord.RegisterPopupWebView(paneID, recordingWv) |
| 661 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | 661 | navCoord := coordinator.NewNavigationCoordinator(ctx, nil, contentCoord) | ||
| 662 | 662 | ||||
| 663 | app := &App{ | 663 | app := &App{ | ||
| @@ -678,8 +678,8 @@ func TestApp_HistorySidebarConfig_KeepOpenCallback(t *testing.T) { | |||||
| 678 | err := cfg.OnNavigateKeepOpen(ctx, navigateURL) | 678 | err := cfg.OnNavigateKeepOpen(ctx, navigateURL) | ||
| 679 | require.NoError(t, err) | 679 | require.NoError(t, err) | ||
| 680 | 680 | ||||
| 681 | - | assert.True(t, fakeWv.loadURICalled, "webview must receive navigation") | 681 | + | assert.True(t, recordingWv.loadURICalled, "webview must receive navigation") |
| 682 | - | assert.Equal(t, navigateURL, fakeWv.loadURILastURI) | 682 | + | assert.Equal(t, navigateURL, recordingWv.loadURILastURI) |
| 683 | 683 | ||||
| 684 | // Sidebar must remain visible (keep-open contract). | 684 | // Sidebar must remain visible (keep-open contract). | ||
| 685 | assert.True(t, bw.sidebarVisible, "sidebar must stay visible after keep-open navigation") | 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 | pool := &recordingWebViewPool{} | 131 | pool := &recordingWebViewPool{} | ||
| 132 | contentCoord := contentcoord.NewCoordinator(ctx, pool, nil, nil, nil, nil, nil, nil) | 132 | contentCoord := contentcoord.NewCoordinator(ctx, pool, nil, nil, nil, nil, nil, nil) | ||
| 133 | - | closedWV := &fakeRecordingWebView{id: 101} | 133 | + | closedWV := &recordingWebView{id: 101} |
| 134 | - | survivingWV := &fakeRecordingWebView{id: 102} | 134 | + | survivingWV := &recordingWebView{id: 102} |
| 135 | contentCoord.RegisterPopupWebView(closedTab.Workspace.ActivePaneID, closedWV) | 135 | contentCoord.RegisterPopupWebView(closedTab.Workspace.ActivePaneID, closedWV) | ||
| 136 | contentCoord.RegisterPopupWebView(survivingTab.Workspace.ActivePaneID, survivingWV) | 136 | contentCoord.RegisterPopupWebView(survivingTab.Workspace.ActivePaneID, survivingWV) | ||
| 137 | 137 | ||||
| @@ -173,8 +173,8 @@ func TestTabCoordinator_SwitchDoesNotReleaseWorkspaceWebViews(t *testing.T) { | |||||
| 173 | 173 | ||||
| 174 | pool := &recordingWebViewPool{} | 174 | pool := &recordingWebViewPool{} | ||
| 175 | contentCoord := contentcoord.NewCoordinator(ctx, pool, nil, nil, nil, nil, nil, nil) | 175 | contentCoord := contentcoord.NewCoordinator(ctx, pool, nil, nil, nil, nil, nil, nil) | ||
| 176 | - | firstWV := &fakeRecordingWebView{id: 111} | 176 | + | firstWV := &recordingWebView{id: 111} |
| 177 | - | secondWV := &fakeRecordingWebView{id: 112} | 177 | + | secondWV := &recordingWebView{id: 112} |
| 178 | contentCoord.RegisterPopupWebView(firstTab.Workspace.ActivePaneID, firstWV) | 178 | contentCoord.RegisterPopupWebView(firstTab.Workspace.ActivePaneID, firstWV) | ||
| 179 | contentCoord.RegisterPopupWebView(secondTab.Workspace.ActivePaneID, secondWV) | 179 | contentCoord.RegisterPopupWebView(secondTab.Workspace.ActivePaneID, secondWV) | ||
| 180 | 180 | ||||
| @@ -209,7 +209,7 @@ func TestTabCoordinator_CloseLastTabReleasesWorkspaceBeforeWindowRemoval(t *test | |||||
| 209 | 209 | ||||
| 210 | pool := &recordingWebViewPool{} | 210 | pool := &recordingWebViewPool{} | ||
| 211 | contentCoord := contentcoord.NewCoordinator(ctx, pool, nil, nil, nil, nil, nil, nil) | 211 | contentCoord := contentcoord.NewCoordinator(ctx, pool, nil, nil, nil, nil, nil, nil) | ||
| 212 | - | closedWV := &fakeRecordingWebView{id: 121} | 212 | + | closedWV := &recordingWebView{id: 121} |
| 213 | contentCoord.RegisterPopupWebView(closedTab.Workspace.ActivePaneID, closedWV) | 213 | contentCoord.RegisterPopupWebView(closedTab.Workspace.ActivePaneID, closedWV) | ||
| 214 | 214 | ||||
| 215 | bw := &browserWindow{id: "window-1", tabs: tabs} | 215 | bw := &browserWindow{id: "window-1", tabs: tabs} | ||
| @@ -247,8 +247,8 @@ func TestBrowserWindow_RemoveBrowserWindowReleasesOwnedTabWorkspaceWebViews(t *t | |||||
| 247 | 247 | ||||
| 248 | pool := &recordingWebViewPool{} | 248 | pool := &recordingWebViewPool{} | ||
| 249 | contentCoord := contentcoord.NewCoordinator(ctx, pool, nil, nil, nil, nil, nil, nil) | 249 | contentCoord := contentcoord.NewCoordinator(ctx, pool, nil, nil, nil, nil, nil, nil) | ||
| 250 | - | ownedWV := &fakeRecordingWebView{id: 201} | 250 | + | ownedWV := &recordingWebView{id: 201} |
| 251 | - | otherWV := &fakeRecordingWebView{id: 202} | 251 | + | otherWV := &recordingWebView{id: 202} |
| 252 | contentCoord.RegisterPopupWebView(ownedTab.Workspace.ActivePaneID, ownedWV) | 252 | contentCoord.RegisterPopupWebView(ownedTab.Workspace.ActivePaneID, ownedWV) | ||
| 253 | contentCoord.RegisterPopupWebView(otherTab.Workspace.ActivePaneID, otherWV) | 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 | "context" | 4 | "context" | ||
| 5 | "testing" | 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 | func TestNavigationCoordinator_OmniboxProviderOpenAndZoom(t *testing.T) { | 11 | func TestNavigationCoordinator_OmniboxProviderOpenAndZoom(t *testing.T) { | ||
| 22 | c := &NavigationCoordinator{} | 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 | c.SetOmniboxProvider(provider) | 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 | c.NotifyZoomChanged(context.Background(), 1.25) | 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 | "github.com/bnema/dumber/internal/application/port" | 7 | "github.com/bnema/dumber/internal/application/port" | ||
| 8 | "github.com/bnema/dumber/internal/domain/entity" | 8 | "github.com/bnema/dumber/internal/domain/entity" | ||
| 9 | + | dialogmocks "github.com/bnema/dumber/internal/ui/dialog/mocks" | |||
| 9 | "github.com/stretchr/testify/assert" | 10 | "github.com/stretchr/testify/assert" | ||
| 11 | + | "github.com/stretchr/testify/mock" | |||
| 10 | ) | 12 | ) | ||
| 11 | 13 | ||||
| 12 | - | type fakePermissionPopup struct { | 14 | + | type permissionPopupShowCall struct { |
| 13 | - | showCalls []fakePopupShowCall | |||
| 14 | - | callback func(allowed, persistent bool) | |||
| 15 | - | } | |||
| 16 | - | ||||
| 17 | - | type fakePopupShowCall struct { | |||
| 18 | heading string | 15 | heading string | ||
| 19 | body string | 16 | body string | ||
| 20 | } | 17 | } | ||
| 21 | 18 | ||||
| 22 | - | func (f *fakePermissionPopup) Show( | 19 | + | func TestPermissionDialog_QueuesRequestsWhilePopupVisible(t *testing.T) { |
| 23 | - | _ context.Context, | 20 | + | popup := dialogmocks.NewMockPermissionPopup(t) |
| 24 | - | heading string, | 21 | + | var showCalls []permissionPopupShowCall |
| 25 | - | body string, | 22 | + | var callback func(allowed, persistent bool) |
| 26 | - | callback func(allowed, persistent bool), | 23 | + | popup.EXPECT(). |
| 27 | - | ) { | 24 | + | Show(mock.Anything, mock.Anything, mock.Anything, mock.Anything). |
| 28 | - | f.showCalls = append(f.showCalls, fakePopupShowCall{ | 25 | + | Run(func(_ context.Context, heading string, body string, cb func(allowed, persistent bool)) { |
| 29 | - | heading: heading, | 26 | + | showCalls = append(showCalls, permissionPopupShowCall{ |
| 30 | - | body: body, | 27 | + | heading: heading, |
| 31 | - | }) | 28 | + | body: body, |
| 32 | - | f.callback = callback | 29 | + | }) |
| 33 | - | } | 30 | + | callback = cb |
| 34 | - | 31 | + | }). | |
| 35 | - | func (f *fakePermissionPopup) Respond(allowed, persistent bool) { | 32 | + | Twice() |
| 36 | - | if f.callback == nil { | 33 | + | |
| 37 | - | return | 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 | d := &PermissionDialog{popup: popup} | 43 | d := &PermissionDialog{popup: popup} | ||
| 47 | 44 | ||||
| 48 | var firstResult port.PermissionDialogResult | 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) { | 71 | + | if assert.Len(t, showCalls, 1) { |
| 75 | - | assert.Equal(t, "Allow Microphone Access?", popup.showCalls[0].heading) | 72 | + | assert.Equal(t, "Allow Microphone Access?", showCalls[0].heading) |
| 76 | } | 73 | } | ||
| 77 | assert.False(t, firstCalled) | 74 | assert.False(t, firstCalled) | ||
| 78 | assert.False(t, secondCalled) | 75 | assert.False(t, secondCalled) | ||
| 79 | 76 | ||||
| 80 | - | popup.Respond(true, false) | 77 | + | respond(true, false) |
| 81 | assert.True(t, firstCalled) | 78 | assert.True(t, firstCalled) | ||
| 82 | assert.Equal(t, port.PermissionDialogResult{Allowed: true, Persistent: false}, firstResult) | 79 | assert.Equal(t, port.PermissionDialogResult{Allowed: true, Persistent: false}, firstResult) | ||
| 83 | 80 | ||||
| 84 | - | if assert.Len(t, popup.showCalls, 2) { | 81 | + | if assert.Len(t, showCalls, 2) { |
| 85 | - | assert.Equal(t, "Allow Camera Access?", popup.showCalls[1].heading) | 82 | + | assert.Equal(t, "Allow Camera Access?", showCalls[1].heading) |
| 86 | } | 83 | } | ||
| 87 | assert.False(t, secondCalled) | 84 | assert.False(t, secondCalled) | ||
| 88 | 85 | ||||
| 89 | - | popup.Respond(false, true) | 86 | + | respond(false, true) |
| 90 | assert.True(t, secondCalled) | 87 | assert.True(t, secondCalled) | ||
| 91 | assert.Equal(t, port.PermissionDialogResult{Allowed: false, Persistent: true}, secondResult) | 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 | func TestAppRunMountsPlaceholderAndRecordsRoute(t *testing.T) { | 48 | func TestAppRunMountsPlaceholderAndRecordsRoute(t *testing.T) { | ||
| 49 | t.Parallel() | 49 | t.Parallel() | ||
| 50 | 50 | ||||
| 51 | - | dom := &fakeDOM{} | 51 | + | dom := &recordingDOM{} |
| 52 | app := NewApp(Dependencies{ | 52 | app := NewApp(Dependencies{ | ||
| 53 | DOM: dom, | 53 | DOM: dom, | ||
| 54 | LocationURI: "dumb://history", | 54 | LocationURI: "dumb://history", | ||
| @@ -64,7 +64,7 @@ func TestAppRunMountsPlaceholderAndRecordsRoute(t *testing.T) { | |||||
| 64 | func TestAppLoadInitialHistoryRouteUsesStyledSections(t *testing.T) { | 64 | func TestAppLoadInitialHistoryRouteUsesStyledSections(t *testing.T) { | ||
| 65 | t.Parallel() | 65 | t.Parallel() | ||
| 66 | 66 | ||||
| 67 | - | history := &fakeHistoryService{entries: []*entity.HistoryEntry{{ | 67 | + | history := &recordingHistoryService{entries: []*entity.HistoryEntry{{ |
| 68 | URL: "https://example.com", | 68 | URL: "https://example.com", | ||
| 69 | Title: "Example", | 69 | Title: "Example", | ||
| 70 | }}} | 70 | }}} | ||
| @@ -98,7 +98,7 @@ func TestAppLoadInitialHistoryRouteUsesStyledSections(t *testing.T) { | |||||
| 98 | func TestAppLoadInitialHistoryRouteShowsTotalStatsWithWindowedTimeline(t *testing.T) { | 98 | func TestAppLoadInitialHistoryRouteShowsTotalStatsWithWindowedTimeline(t *testing.T) { | ||
| 99 | t.Parallel() | 99 | t.Parallel() | ||
| 100 | 100 | ||||
| 101 | - | history := &fakeHistoryService{ | 101 | + | history := &recordingHistoryService{ |
| 102 | entries: []*entity.HistoryEntry{ | 102 | entries: []*entity.HistoryEntry{ | ||
| 103 | {ID: 1, URL: "https://older.example", Title: "Older", VisitCount: 2}, | 103 | {ID: 1, URL: "https://older.example", Title: "Older", VisitCount: 2}, | ||
| 104 | {ID: 2, URL: "https://newer.example", Title: "Newer", VisitCount: 3}, | 104 | {ID: 2, URL: "https://newer.example", Title: "Newer", VisitCount: 3}, | ||
| @@ -119,8 +119,8 @@ func TestAppLoadInitialHistoryRouteShowsTotalStatsWithWindowedTimeline(t *testin | |||||
| 119 | func TestAppRunMountsHistoryLoadingBeforeAsyncHydration(t *testing.T) { | 119 | func TestAppRunMountsHistoryLoadingBeforeAsyncHydration(t *testing.T) { | ||
| 120 | t.Parallel() | 120 | t.Parallel() | ||
| 121 | 121 | ||||
| 122 | - | dom := &fakeActionDOM{fakeDOM: fakeDOM{mounts: make(chan string, 4)}} | 122 | + | dom := &recordingActionDOM{recordingDOM: recordingDOM{mounts: make(chan string, 4)}} |
| 123 | - | history := &fakeHistoryService{ | 123 | + | history := &recordingHistoryService{ |
| 124 | entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com", Title: "Loaded entry"}}, | 124 | entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com", Title: "Loaded entry"}}, | ||
| 125 | timelineStarted: make(chan struct{}), | 125 | timelineStarted: make(chan struct{}), | ||
| 126 | releaseTimeline: make(chan struct{}), | 126 | releaseTimeline: make(chan struct{}), | ||
| @@ -147,7 +147,7 @@ func TestAppRunMountsHistoryLoadingBeforeAsyncHydration(t *testing.T) { | |||||
| 147 | func TestSurfaceActionErrorPreventsStaleAsyncHydrationMount(t *testing.T) { | 147 | func TestSurfaceActionErrorPreventsStaleAsyncHydrationMount(t *testing.T) { | ||
| 148 | t.Parallel() | 148 | t.Parallel() | ||
| 149 | 149 | ||||
| 150 | - | dom := &fakeDOM{mounts: make(chan string, 4)} | 150 | + | dom := &recordingDOM{mounts: make(chan string, 4)} |
| 151 | app := NewApp(Dependencies{DOM: dom, LocationURI: "dumb://history"}) | 151 | app := NewApp(Dependencies{DOM: dom, LocationURI: "dumb://history"}) | ||
| 152 | app.currentRoute = RouteHistory | 152 | app.currentRoute = RouteHistory | ||
| 153 | staleHTML := "<div>Stale hydrated entry</div>" | 153 | staleHTML := "<div>Stale hydrated entry</div>" | ||
| @@ -168,8 +168,8 @@ func TestSurfaceActionErrorPreventsStaleAsyncHydrationMount(t *testing.T) { | |||||
| 168 | func TestSurfaceActionErrorWhileAsyncHydrationBlockedPreventsStaleMount(t *testing.T) { | 168 | func TestSurfaceActionErrorWhileAsyncHydrationBlockedPreventsStaleMount(t *testing.T) { | ||
| 169 | t.Parallel() | 169 | t.Parallel() | ||
| 170 | 170 | ||||
| 171 | - | dom := &fakeActionDOM{fakeDOM: fakeDOM{mounts: make(chan string, 4)}} | 171 | + | dom := &recordingActionDOM{recordingDOM: recordingDOM{mounts: make(chan string, 4)}} |
| 172 | - | history := &fakeHistoryService{ | 172 | + | history := &recordingHistoryService{ |
| 173 | entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com", Title: "Stale hydrated entry"}}, | 173 | entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com", Title: "Stale hydrated entry"}}, | ||
| 174 | timelineStarted: make(chan struct{}), | 174 | timelineStarted: make(chan struct{}), | ||
| 175 | releaseTimeline: make(chan struct{}), | 175 | releaseTimeline: make(chan struct{}), | ||
| @@ -200,7 +200,7 @@ func TestSurfaceActionErrorWhileAsyncHydrationBlockedPreventsStaleMount(t *testi | |||||
| 200 | 200 | ||||
| 201 | func TestCurrentHistoryRouteSnapshotIncludesWindowAfter(t *testing.T) { | 201 | func TestCurrentHistoryRouteSnapshotIncludesWindowAfter(t *testing.T) { | ||
| 202 | cursor := time.Date(2026, 4, 25, 9, 0, 0, 0, time.UTC) | 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 | app.currentRoute = RouteHistory | 204 | app.currentRoute = RouteHistory | ||
| 205 | app.historyWindowAfter = cursor | 205 | app.historyWindowAfter = cursor | ||
| 206 | 206 | ||||
| @@ -212,13 +212,13 @@ func TestAppHandleHistoryLoadMoreAppendsOlderWindow(t *testing.T) { | |||||
| 212 | t.Parallel() | 212 | t.Parallel() | ||
| 213 | 213 | ||||
| 214 | cursor := time.Date(2026, 4, 25, 9, 0, 0, 0, time.UTC) | 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 | Entries: []*entity.HistoryEntry{{ID: 2, URL: "https://older.example", Title: "Older entry", LastVisited: cursor.Add(-time.Hour)}}, | 216 | Entries: []*entity.HistoryEntry{{ID: 2, URL: "https://older.example", Title: "Older entry", LastVisited: cursor.Add(-time.Hour)}}, | ||
| 217 | Before: cursor, | 217 | Before: cursor, | ||
| 218 | After: cursor.Add(-24 * time.Hour), | 218 | After: cursor.Add(-24 * time.Hour), | ||
| 219 | HasMore: true, | 219 | HasMore: true, | ||
| 220 | }} | 220 | }} | ||
| 221 | - | dom := &fakeActionDOM{} | 221 | + | dom := &recordingActionDOM{} |
| 222 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | 222 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | ||
| 223 | app.currentRoute = RouteHistory | 223 | app.currentRoute = RouteHistory | ||
| 224 | app.historyEntries = []*entity.HistoryEntry{{ID: 1, URL: "https://newer.example", Title: "Newer entry", LastVisited: cursor}} | 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 | t.Parallel() | 240 | t.Parallel() | ||
| 241 | 241 | ||||
| 242 | cursor := time.Date(2026, 4, 25, 9, 0, 0, 0, time.UTC) | 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 | Entries: []*entity.HistoryEntry{{ID: 2, URL: "https://older.example", Title: "Older entry", LastVisited: cursor.Add(-time.Hour)}}, | 244 | Entries: []*entity.HistoryEntry{{ID: 2, URL: "https://older.example", Title: "Older entry", LastVisited: cursor.Add(-time.Hour)}}, | ||
| 245 | Before: cursor, | 245 | Before: cursor, | ||
| 246 | After: cursor.Add(-24 * time.Hour), | 246 | After: cursor.Add(-24 * time.Hour), | ||
| 247 | HasMore: true, | 247 | HasMore: true, | ||
| 248 | }} | 248 | }} | ||
| 249 | - | dom := &fakeDOM{} | 249 | + | dom := &recordingDOM{} |
| 250 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | 250 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | ||
| 251 | app.currentRoute = RouteHistory | 251 | app.currentRoute = RouteHistory | ||
| 252 | app.historyEntries = []*entity.HistoryEntry{{ID: 1, URL: "https://newer.example", Title: "Newer entry", LastVisited: cursor}} | 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 | t.Parallel() | 267 | t.Parallel() | ||
| 268 | 268 | ||||
| 269 | cursor := time.Date(2026, 4, 25, 9, 0, 0, 0, time.UTC) | 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"}}}} | 270 | + | history := &recordingHistoryService{window: &entity.HistoryWindow{Entries: []*entity.HistoryEntry{{ID: 2, URL: "https://older.example"}}}} |
| 271 | - | dom := &fakeActionDOM{} | 271 | + | dom := &recordingActionDOM{} |
| 272 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | 272 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | ||
| 273 | app.currentRoute = RouteHistory | 273 | app.currentRoute = RouteHistory | ||
| 274 | app.historyWindowAfter = cursor | 274 | app.historyWindowAfter = cursor | ||
| @@ -285,7 +285,7 @@ func TestAppHandleHistoryLoadMoreIgnoresStaleCursor(t *testing.T) { | |||||
| 285 | func TestAppLoadInitialHistoryRouteRendersManagementActions(t *testing.T) { | 285 | func TestAppLoadInitialHistoryRouteRendersManagementActions(t *testing.T) { | ||
| 286 | t.Parallel() | 286 | t.Parallel() | ||
| 287 | 287 | ||||
| 288 | - | history := &fakeHistoryService{ | 288 | + | history := &recordingHistoryService{ |
| 289 | entries: []*entity.HistoryEntry{{ | 289 | entries: []*entity.HistoryEntry{{ | ||
| 290 | ID: 42, | 290 | ID: 42, | ||
| 291 | URL: "https://example.com/page", | 291 | URL: "https://example.com/page", | ||
| @@ -314,8 +314,8 @@ func TestAppLoadInitialHistoryRouteRendersManagementActions(t *testing.T) { | |||||
| 314 | } | 314 | } | ||
| 315 | 315 | ||||
| 316 | func TestAppHandleHistoryActionsRefreshesDOM(t *testing.T) { | 316 | func TestAppHandleHistoryActionsRefreshesDOM(t *testing.T) { | ||
| 317 | - | dom := &fakeDOM{} | 317 | + | dom := &recordingDOM{} |
| 318 | - | history := &fakeHistoryService{ | 318 | + | history := &recordingHistoryService{ |
| 319 | entries: []*entity.HistoryEntry{{ID: 42, URL: "https://example.com", Title: "Example"}}, | 319 | entries: []*entity.HistoryEntry{{ID: 42, URL: "https://example.com", Title: "Example"}}, | ||
| 320 | searchEntries: []*entity.HistoryEntry{{ID: 7, URL: "https://search.example", Title: "Search result"}}, | 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 | func TestAppCloseStopsActionWorkerAndReleasesDOM(t *testing.T) { | 366 | func TestAppCloseStopsActionWorkerAndReleasesDOM(t *testing.T) { | ||
| 367 | - | dom := &fakeActionDOM{} | 367 | + | dom := &recordingActionDOM{} |
| 368 | - | history := &fakeHistoryService{entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com"}}} | 368 | + | history := &recordingHistoryService{entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com"}}} |
| 369 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | 369 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | ||
| 370 | 370 | ||||
| 371 | ctx, cancel := context.WithCancel(context.Background()) | 371 | ctx, cancel := context.WithCancel(context.Background()) | ||
| @@ -396,8 +396,8 @@ func TestAppRunCleansActionWorkerWhenBindingFails(t *testing.T) { | |||||
| 396 | } | 396 | } | ||
| 397 | 397 | ||||
| 398 | func TestAppRunReleasesDOMWhenActionWorkerCannotStart(t *testing.T) { | 398 | func TestAppRunReleasesDOMWhenActionWorkerCannotStart(t *testing.T) { | ||
| 399 | - | dom := &fakeActionDOM{} | 399 | + | dom := &recordingActionDOM{} |
| 400 | - | history := &fakeHistoryService{entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com"}}} | 400 | + | history := &recordingHistoryService{entries: []*entity.HistoryEntry{{ID: 1, URL: "https://example.com"}}} |
| 401 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | 401 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | ||
| 402 | 402 | ||||
| 403 | ctx, cancel := context.WithCancel(context.Background()) | 403 | ctx, cancel := context.WithCancel(context.Background()) | ||
| @@ -415,7 +415,7 @@ func TestAppRunReleasesDOMWhenActionWorkerCannotStart(t *testing.T) { | |||||
| 415 | func TestAppLoadInitialHistoryRouteRendersErrorState(t *testing.T) { | 415 | func TestAppLoadInitialHistoryRouteRendersErrorState(t *testing.T) { | ||
| 416 | t.Parallel() | 416 | t.Parallel() | ||
| 417 | 417 | ||||
| 418 | - | history := &fakeHistoryService{err: errors.New("database unavailable")} | 418 | + | history := &recordingHistoryService{err: errors.New("database unavailable")} |
| 419 | app := NewApp(Dependencies{ | 419 | app := NewApp(Dependencies{ | ||
| 420 | History: history, | 420 | History: history, | ||
| 421 | LocationURI: "dumb://history", | 421 | LocationURI: "dumb://history", | ||
| @@ -432,11 +432,11 @@ func TestAppLoadInitialHistoryRouteRendersErrorState(t *testing.T) { | |||||
| 432 | func TestAppLoadInitialHistoryRouteAppliesThemeTokens(t *testing.T) { | 432 | func TestAppLoadInitialHistoryRouteAppliesThemeTokens(t *testing.T) { | ||
| 433 | t.Parallel() | 433 | t.Parallel() | ||
| 434 | 434 | ||||
| 435 | - | history := &fakeHistoryService{entries: []*entity.HistoryEntry{{ | 435 | + | history := &recordingHistoryService{entries: []*entity.HistoryEntry{{ |
| 436 | URL: "https://example.com", | 436 | URL: "https://example.com", | ||
| 437 | Title: "Example", | 437 | Title: "Example", | ||
| 438 | }}} | 438 | }}} | ||
| 439 | - | config := &fakeConfigService{current: dto.SystemviewConfigPayload{ | 439 | + | config := &recordingConfigService{current: dto.SystemviewConfigPayload{ |
| 440 | Appearance: dto.WebUIAppearanceConfig{ | 440 | Appearance: dto.WebUIAppearanceConfig{ | ||
| 441 | ColorScheme: "prefer-light", | 441 | ColorScheme: "prefer-light", | ||
| 442 | LightPalette: dto.ColorPalette{ | 442 | LightPalette: dto.ColorPalette{ | ||
| @@ -479,7 +479,7 @@ func TestAppLoadInitialHistoryRouteAppliesThemeTokens(t *testing.T) { | |||||
| 479 | func TestAppLoadInitialFavoritesRouteRendersData(t *testing.T) { | 479 | func TestAppLoadInitialFavoritesRouteRendersData(t *testing.T) { | ||
| 480 | t.Parallel() | 480 | t.Parallel() | ||
| 481 | 481 | ||||
| 482 | - | favorites := &fakeFavoritesService{ | 482 | + | favorites := &recordingFavoritesService{ |
| 483 | favorites: []*entity.Favorite{{URL: "https://example.com", Title: "Example"}}, | 483 | favorites: []*entity.Favorite{{URL: "https://example.com", Title: "Example"}}, | ||
| 484 | folders: []*entity.Folder{{Name: "Read Later"}}, | 484 | folders: []*entity.Folder{{Name: "Read Later"}}, | ||
| 485 | tags: []*entity.Tag{{Name: "Go"}}, | 485 | tags: []*entity.Tag{{Name: "Go"}}, | ||
| @@ -519,7 +519,7 @@ func TestAppLoadInitialFavoritesRouteRendersCRUDControls(t *testing.T) { | |||||
| 519 | 519 | ||||
| 520 | folderID := entity.FolderID(1) | 520 | folderID := entity.FolderID(1) | ||
| 521 | shortcut := 3 | 521 | shortcut := 3 | ||
| 522 | - | favorites := &fakeFavoritesService{ | 522 | + | favorites := &recordingFavoritesService{ |
| 523 | favorites: []*entity.Favorite{{ID: 42, URL: "https://example.com", Title: "Example", FolderID: &folderID, ShortcutKey: &shortcut}}, | 523 | favorites: []*entity.Favorite{{ID: 42, URL: "https://example.com", Title: "Example", FolderID: &folderID, ShortcutKey: &shortcut}}, | ||
| 524 | folders: []*entity.Folder{{ID: folderID, Name: "Read Later", Icon: "📚"}}, | 524 | folders: []*entity.Folder{{ID: folderID, Name: "Read Later", Icon: "📚"}}, | ||
| 525 | tags: []*entity.Tag{{ID: 7, Name: "Go", Color: "#00add8"}}, | 525 | tags: []*entity.Tag{{ID: 7, Name: "Go", Color: "#00add8"}}, | ||
| @@ -546,8 +546,8 @@ func TestAppLoadInitialFavoritesRouteRendersCRUDControls(t *testing.T) { | |||||
| 546 | } | 546 | } | ||
| 547 | 547 | ||||
| 548 | func TestAppHandleFavoriteActionsRefreshesDOM(t *testing.T) { | 548 | func TestAppHandleFavoriteActionsRefreshesDOM(t *testing.T) { | ||
| 549 | - | dom := &fakeDOM{} | 549 | + | dom := &recordingDOM{} |
| 550 | - | favorites := &fakeFavoritesService{ | 550 | + | favorites := &recordingFavoritesService{ |
| 551 | favorites: []*entity.Favorite{{ID: 42, URL: "https://example.com", Title: "Example"}}, | 551 | favorites: []*entity.Favorite{{ID: 42, URL: "https://example.com", Title: "Example"}}, | ||
| 552 | folders: []*entity.Folder{{ID: 5, Name: "Read Later"}}, | 552 | folders: []*entity.Folder{{ID: 5, Name: "Read Later"}}, | ||
| 553 | tags: []*entity.Tag{{ID: 7, Name: "Go", Color: "#00add8"}}, | 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 | current: config, | 679 | current: config, | ||
| 680 | keybindings: port.KeybindingsConfig{Groups: []port.KeybindingGroup{ | 680 | keybindings: port.KeybindingsConfig{Groups: []port.KeybindingGroup{ | ||
| 681 | { | 681 | { | ||
| @@ -756,7 +756,7 @@ func TestAppLoadInitialConfigRouteRendersData(t *testing.T) { | |||||
| 756 | func TestAppLoadInitialConfigRouteRendersEditControls(t *testing.T) { | 756 | func TestAppLoadInitialConfigRouteRendersEditControls(t *testing.T) { | ||
| 757 | t.Parallel() | 757 | t.Parallel() | ||
| 758 | 758 | ||||
| 759 | - | service := &fakeConfigService{ | 759 | + | service := &recordingConfigService{ |
| 760 | current: testConfigPayload(), | 760 | current: testConfigPayload(), | ||
| 761 | keybindings: port.KeybindingsConfig{Groups: []port.KeybindingGroup{{ | 761 | keybindings: port.KeybindingsConfig{Groups: []port.KeybindingGroup{{ | ||
| 762 | Mode: "default", | 762 | Mode: "default", | ||
| @@ -793,8 +793,8 @@ func TestAppLoadInitialConfigRouteRendersEditControls(t *testing.T) { | |||||
| 793 | } | 793 | } | ||
| 794 | 794 | ||||
| 795 | func TestAppHandleConfigActionsRefreshesDOM(t *testing.T) { | 795 | func TestAppHandleConfigActionsRefreshesDOM(t *testing.T) { | ||
| 796 | - | dom := &fakeDOM{} | 796 | + | dom := &recordingDOM{} |
| 797 | - | service := &fakeConfigService{ | 797 | + | service := &recordingConfigService{ |
| 798 | current: testConfigPayload(), | 798 | current: testConfigPayload(), | ||
| 799 | defaultCfg: testDefaultConfigPayload(), | 799 | defaultCfg: testDefaultConfigPayload(), | ||
| 800 | keybindings: port.KeybindingsConfig{Groups: []port.KeybindingGroup{{ | 800 | keybindings: port.KeybindingsConfig{Groups: []port.KeybindingGroup{{ | ||
| @@ -883,7 +883,7 @@ func TestAppHandleConfigActionsRefreshesDOM(t *testing.T) { | |||||
| 883 | func TestAppRejectsSearchURLsWithoutPlaceholder(t *testing.T) { | 883 | func TestAppRejectsSearchURLsWithoutPlaceholder(t *testing.T) { | ||
| 884 | t.Parallel() | 884 | t.Parallel() | ||
| 885 | 885 | ||||
| 886 | - | service := &fakeConfigService{current: testConfigPayload(), defaultCfg: testDefaultConfigPayload()} | 886 | + | service := &recordingConfigService{current: testConfigPayload(), defaultCfg: testDefaultConfigPayload()} |
| 887 | app := NewApp(Dependencies{Config: service, LocationURI: "dumb://config"}) | 887 | app := NewApp(Dependencies{Config: service, LocationURI: "dumb://config"}) | ||
| 888 | 888 | ||||
| 889 | err := app.handleConfigAction(context.Background(), DOMAction{ | 889 | err := app.handleConfigAction(context.Background(), DOMAction{ | ||
| @@ -909,7 +909,7 @@ func TestAppLoadInitialConfigRouteUsesResolvedAppearanceOnlyForShellTheme(t *tes | |||||
| 909 | resolved.DarkPalette.Background = "#222222" | 909 | resolved.DarkPalette.Background = "#222222" | ||
| 910 | resolved.DarkPalette.SurfaceVariant = "#444444" | 910 | resolved.DarkPalette.SurfaceVariant = "#444444" | ||
| 911 | cfg.ResolvedAppearance = &resolved | 911 | cfg.ResolvedAppearance = &resolved | ||
| 912 | - | service := &fakeConfigService{ | 912 | + | service := &recordingConfigService{ |
| 913 | current: cfg, | 913 | current: cfg, | ||
| 914 | keybindings: port.KeybindingsConfig{}, | 914 | keybindings: port.KeybindingsConfig{}, | ||
| 915 | } | 915 | } | ||
| @@ -956,8 +956,8 @@ func testDefaultConfigPayload() dto.SystemviewConfigPayload { | |||||
| 956 | return cfg | 956 | return cfg | ||
| 957 | } | 957 | } | ||
| 958 | 958 | ||||
| 959 | - | // Handwritten fake to capture DOM mounts for stateful render assertions. | 959 | + | // Recording fixture to capture DOM mounts for stateful render assertions. |
| 960 | - | type fakeDOM struct { | 960 | + | type recordingDOM struct { |
| 961 | mu sync.Mutex | 961 | mu sync.Mutex | ||
| 962 | mounted bool | 962 | mounted bool | ||
| 963 | html string | 963 | html string | ||
| @@ -965,7 +965,7 @@ type fakeDOM struct { | |||||
| 965 | mounts chan string | 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 | d.mu.Lock() | 969 | d.mu.Lock() | ||
| 970 | d.mounted = true | 970 | d.mounted = true | ||
| 971 | d.html = markup | 971 | d.html = markup | ||
| @@ -980,19 +980,19 @@ func (d *fakeDOM) Mount(markup string) error { | |||||
| 980 | return nil | 980 | return nil | ||
| 981 | } | 981 | } | ||
| 982 | 982 | ||||
| 983 | - | func (d *fakeDOM) Mounted() bool { | 983 | + | func (d *recordingDOM) Mounted() bool { |
| 984 | d.mu.Lock() | 984 | d.mu.Lock() | ||
| 985 | defer d.mu.Unlock() | 985 | defer d.mu.Unlock() | ||
| 986 | return d.mounted | 986 | return d.mounted | ||
| 987 | } | 987 | } | ||
| 988 | 988 | ||||
| 989 | - | func (d *fakeDOM) HTML() string { | 989 | + | func (d *recordingDOM) HTML() string { |
| 990 | d.mu.Lock() | 990 | d.mu.Lock() | ||
| 991 | defer d.mu.Unlock() | 991 | defer d.mu.Unlock() | ||
| 992 | return d.html | 992 | return d.html | ||
| 993 | } | 993 | } | ||
| 994 | 994 | ||||
| 995 | - | func (d *fakeDOM) AppendedHTML() string { | 995 | + | func (d *recordingDOM) AppendedHTML() string { |
| 996 | d.mu.Lock() | 996 | d.mu.Lock() | ||
| 997 | defer d.mu.Unlock() | 997 | defer d.mu.Unlock() | ||
| 998 | return d.appendedHTML | 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 { | 1021 | + | type recordingActionDOM struct { |
| 1022 | - | fakeDOM | 1022 | + | recordingDOM |
| 1023 | handler DOMActionHandler | 1023 | handler DOMActionHandler | ||
| 1024 | released bool | 1024 | released bool | ||
| 1025 | } | 1025 | } | ||
| 1026 | 1026 | ||||
| 1027 | - | func (d *fakeActionDOM) BindActions(handler DOMActionHandler) error { | 1027 | + | func (d *recordingActionDOM) BindActions(handler DOMActionHandler) error { |
| 1028 | d.handler = handler | 1028 | d.handler = handler | ||
| 1029 | return nil | 1029 | return nil | ||
| 1030 | } | 1030 | } | ||
| 1031 | 1031 | ||||
| 1032 | - | func (d *fakeActionDOM) AppendHistoryTimeline(markup string) error { | 1032 | + | func (d *recordingActionDOM) AppendHistoryTimeline(markup string) error { |
| 1033 | d.mu.Lock() | 1033 | d.mu.Lock() | ||
| 1034 | d.appendedHTML += markup | 1034 | d.appendedHTML += markup | ||
| 1035 | d.mu.Unlock() | 1035 | d.mu.Unlock() | ||
| 1036 | return nil | 1036 | return nil | ||
| 1037 | } | 1037 | } | ||
| 1038 | 1038 | ||||
| 1039 | - | func (d *fakeActionDOM) Release() { | 1039 | + | func (d *recordingActionDOM) Release() { |
| 1040 | d.released = true | 1040 | d.released = true | ||
| 1041 | } | 1041 | } | ||
| 1042 | 1042 | ||||
| 1043 | type failingActionDOM struct { | 1043 | type failingActionDOM struct { | ||
| 1044 | - | fakeDOM | 1044 | + | recordingDOM |
| 1045 | bindErr error | 1045 | bindErr error | ||
| 1046 | } | 1046 | } | ||
| 1047 | 1047 | ||||
| @@ -1049,8 +1049,8 @@ func (d *failingActionDOM) BindActions(DOMActionHandler) error { | |||||
| 1049 | return d.bindErr | 1049 | return d.bindErr | ||
| 1050 | } | 1050 | } | ||
| 1051 | 1051 | ||||
| 1052 | - | // Handwritten fake to capture history state for stateful render assertions. | 1052 | + | // Recording fixture to capture history state for stateful render assertions. |
| 1053 | - | type fakeHistoryService struct { | 1053 | + | type recordingHistoryService struct { |
| 1054 | called bool | 1054 | called bool | ||
| 1055 | limit int | 1055 | limit int | ||
| 1056 | offset int | 1056 | offset int | ||
| @@ -1080,7 +1080,7 @@ type fakeHistoryService struct { | |||||
| 1080 | startOnce sync.Once | 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 | s.called = true | 1084 | s.called = true | ||
| 1085 | s.limit = limit | 1085 | s.limit = limit | ||
| 1086 | s.offset = offset | 1086 | s.offset = offset | ||
| @@ -1093,7 +1093,7 @@ func (s *fakeHistoryService) Timeline(_ context.Context, limit, offset int) ([]* | |||||
| 1093 | return s.entries, s.err | 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 | s.domainCalled = true | 1097 | s.domainCalled = true | ||
| 1098 | s.domain = domain | 1098 | s.domain = domain | ||
| 1099 | s.limit = limit | 1099 | s.limit = limit | ||
| @@ -1101,7 +1101,7 @@ func (s *fakeHistoryService) TimelineByDomain(_ context.Context, domain string, | |||||
| 1101 | return s.entries, s.err | 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 | s.called = true | 1105 | s.called = true | ||
| 1106 | s.windowBefore = before | 1106 | s.windowBefore = before | ||
| 1107 | s.windowDomain = domain | 1107 | s.windowDomain = domain | ||
| @@ -1120,7 +1120,7 @@ func (s *fakeHistoryService) TimelineWindow(_ context.Context, before time.Time, | |||||
| 1120 | return &entity.HistoryWindow{Entries: s.entries, Before: before, After: before.Add(-24 * time.Hour)}, nil | 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 | s.searchCalled = true | 1124 | s.searchCalled = true | ||
| 1125 | s.query = query | 1125 | s.query = query | ||
| 1126 | s.searchLimit = limit | 1126 | s.searchLimit = limit | ||
| @@ -1130,37 +1130,37 @@ func (s *fakeHistoryService) Search(_ context.Context, query string, limit int) | |||||
| 1130 | return s.entries, nil | 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 | s.deletedEntryID = id | 1134 | s.deletedEntryID = id | ||
| 1135 | return nil | 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 | s.deletedRangeID = rangeID | 1139 | s.deletedRangeID = rangeID | ||
| 1140 | return nil | 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 | s.statsCalled = true | 1144 | s.statsCalled = true | ||
| 1145 | return s.stats, nil | 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 | s.analyticsCalled = true | 1149 | s.analyticsCalled = true | ||
| 1150 | return nil, nil | 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 | return s.domainStats, nil | 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 | s.deletedDomain = domain | 1158 | s.deletedDomain = domain | ||
| 1159 | return nil | 1159 | return nil | ||
| 1160 | } | 1160 | } | ||
| 1161 | 1161 | ||||
| 1162 | - | // Handwritten fake to capture favorites state for stateful render assertions. | 1162 | + | // Recording fixture to capture favorites state for stateful render assertions. |
| 1163 | - | type fakeFavoritesService struct { | 1163 | + | type recordingFavoritesService struct { |
| 1164 | calledList bool | 1164 | calledList bool | ||
| 1165 | calledFolders bool | 1165 | calledFolders bool | ||
| 1166 | calledTags bool | 1166 | calledTags bool | ||
| @@ -1184,85 +1184,85 @@ type fakeFavoritesService struct { | |||||
| 1184 | removedTagID int64 | 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 | s.calledList = true | 1188 | s.calledList = true | ||
| 1189 | return s.favorites, nil | 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 | s.calledFolders = true | 1193 | s.calledFolders = true | ||
| 1194 | return s.folders, nil | 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 | s.calledTags = true | 1198 | s.calledTags = true | ||
| 1199 | return s.tags, nil | 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 | s.createdFavorite = input | 1203 | s.createdFavorite = input | ||
| 1204 | return &entity.Favorite{ID: 99, URL: input.URL, Title: input.Title, FolderID: input.FolderID}, nil | 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 | s.updatedFavorite = input | 1208 | s.updatedFavorite = input | ||
| 1209 | return &entity.Favorite{ID: input.ID, URL: "https://example.com", Title: input.Title, FaviconURL: input.FaviconURL, FolderID: input.FolderID, ShortcutKey: input.ShortcutKey}, nil | 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 | s.deletedFavorite = id | 1213 | s.deletedFavorite = id | ||
| 1214 | return nil | 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 | s.createdFolder = name | 1222 | s.createdFolder = name | ||
| 1223 | s.createdFolderIcon = icon | 1223 | s.createdFolderIcon = icon | ||
| 1224 | return &entity.Folder{ID: 77, Name: name, Icon: icon}, nil | 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 | s.updatedFolderID = id | 1228 | s.updatedFolderID = id | ||
| 1229 | return nil | 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 | s.deletedFolderID = id | 1233 | s.deletedFolderID = id | ||
| 1234 | return nil | 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 | s.createdTag = name | 1238 | s.createdTag = name | ||
| 1239 | return &entity.Tag{ID: 55, Name: name, Color: color}, nil | 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 | s.updatedTagID = id | 1243 | s.updatedTagID = id | ||
| 1244 | return nil | 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 | s.deletedTagID = id | 1248 | s.deletedTagID = id | ||
| 1249 | return nil | 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 | s.assignedFavoriteID = favoriteID | 1253 | s.assignedFavoriteID = favoriteID | ||
| 1254 | s.assignedTagID = tagID | 1254 | s.assignedTagID = tagID | ||
| 1255 | return nil | 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 | s.removedFavoriteID = favoriteID | 1259 | s.removedFavoriteID = favoriteID | ||
| 1260 | s.removedTagID = tagID | 1260 | s.removedTagID = tagID | ||
| 1261 | return nil | 1261 | return nil | ||
| 1262 | } | 1262 | } | ||
| 1263 | 1263 | ||||
| 1264 | - | // Handwritten fake to capture config state for stateful render assertions. | 1264 | + | // Recording fixture to capture config state for stateful render assertions. |
| 1265 | - | type fakeConfigService struct { | 1265 | + | type recordingConfigService struct { |
| 1266 | calledCurrent bool | 1266 | calledCurrent bool | ||
| 1267 | calledDefault bool | 1267 | calledDefault bool | ||
| 1268 | calledSave bool | 1268 | calledSave bool | ||
| @@ -1280,17 +1280,17 @@ type fakeConfigService struct { | |||||
| 1280 | resetReq port.ResetKeybindingRequest | 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 | s.calledCurrent = true | 1284 | s.calledCurrent = true | ||
| 1285 | return s.current, nil | 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 | s.calledDefault = true | 1289 | s.calledDefault = true | ||
| 1290 | return s.defaultCfg, nil | 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 | s.calledSave = true | 1294 | s.calledSave = true | ||
| 1295 | s.savedConfig = cfg | 1295 | s.savedConfig = cfg | ||
| 1296 | s.current.Appearance = cfg.Appearance | 1296 | s.current.Appearance = cfg.Appearance | ||
| @@ -1308,30 +1308,30 @@ func (s *fakeConfigService) Save(_ context.Context, cfg dto.WebUIConfig) error { | |||||
| 1308 | return nil | 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 | s.calledKeybindings = true | 1312 | s.calledKeybindings = true | ||
| 1313 | return s.keybindings, nil | 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 | s.calledSet = true | 1317 | s.calledSet = true | ||
| 1318 | s.setReq = req | 1318 | s.setReq = req | ||
| 1319 | return s.setResp, nil | 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 | s.calledReset = true | 1323 | s.calledReset = true | ||
| 1324 | s.resetReq = req | 1324 | s.resetReq = req | ||
| 1325 | return nil | 1325 | return nil | ||
| 1326 | } | 1326 | } | ||
| 1327 | 1327 | ||||
| 1328 | - | func (s *fakeConfigService) ResetAllKeybindings(context.Context) error { | 1328 | + | func (s *recordingConfigService) ResetAllKeybindings(context.Context) error { |
| 1329 | s.calledResetAll = true | 1329 | s.calledResetAll = true | ||
| 1330 | return nil | 1330 | return nil | ||
| 1331 | } | 1331 | } | ||
| 1332 | 1332 | ||||
| 1333 | func TestHandleFavoriteActionValidatesCreateURL(t *testing.T) { | 1333 | func TestHandleFavoriteActionValidatesCreateURL(t *testing.T) { | ||
| 1334 | - | app := NewApp(Dependencies{Favorites: &fakeFavoritesService{}}) | 1334 | + | app := NewApp(Dependencies{Favorites: &recordingFavoritesService{}}) |
| 1335 | 1335 | ||||
| 1336 | err := app.handleFavoriteAction(context.Background(), DOMAction{ | 1336 | err := app.handleFavoriteAction(context.Background(), DOMAction{ | ||
| 1337 | Action: favoriteActionCreate, | 1337 | Action: favoriteActionCreate, | ||
| @@ -1356,7 +1356,7 @@ func TestHandleFavoriteActionValidatesCreateURL(t *testing.T) { | |||||
| 1356 | } | 1356 | } | ||
| 1357 | 1357 | ||||
| 1358 | func TestHandleFavoriteCreateAcceptsInternalDumbRoutes(t *testing.T) { | 1358 | func TestHandleFavoriteCreateAcceptsInternalDumbRoutes(t *testing.T) { | ||
| 1359 | - | favorites := &fakeFavoritesService{} | 1359 | + | favorites := &recordingFavoritesService{} |
| 1360 | app := NewApp(Dependencies{Favorites: favorites}) | 1360 | app := NewApp(Dependencies{Favorites: favorites}) | ||
| 1361 | 1361 | ||||
| 1362 | require.NoError(t, app.handleFavoriteAction(context.Background(), DOMAction{ | 1362 | require.NoError(t, app.handleFavoriteAction(context.Background(), DOMAction{ | ||
| @@ -1373,7 +1373,7 @@ func TestHandleFavoriteCreateAcceptsInternalDumbRoutes(t *testing.T) { | |||||
| 1373 | } | 1373 | } | ||
| 1374 | 1374 | ||||
| 1375 | func TestHandleFavoriteCreateRejectsUnknownDumbRoutes(t *testing.T) { | 1375 | func TestHandleFavoriteCreateRejectsUnknownDumbRoutes(t *testing.T) { | ||
| 1376 | - | favorites := &fakeFavoritesService{} | 1376 | + | favorites := &recordingFavoritesService{} |
| 1377 | app := NewApp(Dependencies{Favorites: favorites}) | 1377 | app := NewApp(Dependencies{Favorites: favorites}) | ||
| 1378 | 1378 | ||||
| 1379 | err := app.handleFavoriteAction(context.Background(), DOMAction{ | 1379 | err := app.handleFavoriteAction(context.Background(), DOMAction{ | ||
| @@ -1397,7 +1397,7 @@ func TestParsePositiveInt64DistinguishesParseAndRangeErrors(t *testing.T) { | |||||
| 1397 | } | 1397 | } | ||
| 1398 | 1398 | ||||
| 1399 | func TestHandleFavoriteTagActionsAcceptSnakeCaseIDs(t *testing.T) { | 1399 | func TestHandleFavoriteTagActionsAcceptSnakeCaseIDs(t *testing.T) { | ||
| 1400 | - | favorites := &fakeFavoritesService{} | 1400 | + | favorites := &recordingFavoritesService{} |
| 1401 | app := NewApp(Dependencies{Favorites: favorites}) | 1401 | app := NewApp(Dependencies{Favorites: favorites}) | ||
| 1402 | 1402 | ||||
| 1403 | require.NoError(t, app.handleFavoriteAction(context.Background(), DOMAction{ | 1403 | require.NoError(t, app.handleFavoriteAction(context.Background(), DOMAction{ | ||
| @@ -1416,8 +1416,8 @@ func TestHandleFavoriteTagActionsAcceptSnakeCaseIDs(t *testing.T) { | |||||
| 1416 | } | 1416 | } | ||
| 1417 | 1417 | ||||
| 1418 | func TestHandleHistorySearchClearsDomainFilter(t *testing.T) { | 1418 | func TestHandleHistorySearchClearsDomainFilter(t *testing.T) { | ||
| 1419 | - | dom := &fakeDOM{} | 1419 | + | dom := &recordingDOM{} |
| 1420 | - | history := &fakeHistoryService{searchEntries: []*entity.HistoryEntry{{ID: 7, URL: "https://search.example", Title: "Search result"}}} | 1420 | + | history := &recordingHistoryService{searchEntries: []*entity.HistoryEntry{{ID: 7, URL: "https://search.example", Title: "Search result"}}} |
| 1421 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | 1421 | app := NewApp(Dependencies{DOM: dom, History: history, LocationURI: "dumb://history"}) | ||
| 1422 | app.historyDomainFilter = "example.com" | 1422 | app.historyDomainFilter = "example.com" | ||
| 1423 | 1423 | ||||
| @@ -1433,7 +1433,7 @@ func TestHandleHistorySearchClearsDomainFilter(t *testing.T) { | |||||
| 1433 | } | 1433 | } | ||
| 1434 | 1434 | ||||
| 1435 | func TestAppRunWithContextRejectsNilContext(t *testing.T) { | 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 | var ctx context.Context | 1438 | var ctx context.Context | ||
| 1439 | err := app.RunWithContext(ctx) | 1439 | err := app.RunWithContext(ctx) | ||
| @@ -1442,7 +1442,7 @@ func TestAppRunWithContextRejectsNilContext(t *testing.T) { | |||||
| 1442 | } | 1442 | } | ||
| 1443 | 1443 | ||||
| 1444 | func TestAppRunWithContextRequiresDOMBeforeLoading(t *testing.T) { | 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 | app := NewApp(Dependencies{History: history, LocationURI: "dumb://history"}) | 1446 | app := NewApp(Dependencies{History: history, LocationURI: "dumb://history"}) | ||
| 1447 | 1447 | ||||
| 1448 | err := app.RunWithContext(context.Background()) | 1448 | err := app.RunWithContext(context.Background()) | ||
| @@ -1453,9 +1453,9 @@ func TestAppRunWithContextRequiresDOMBeforeLoading(t *testing.T) { | |||||
| 1453 | 1453 | ||||
| 1454 | func TestHandleActionsRejectUnknownAction(t *testing.T) { | 1454 | func TestHandleActionsRejectUnknownAction(t *testing.T) { | ||
| 1455 | app := NewApp(Dependencies{ | 1455 | app := NewApp(Dependencies{ | ||
| 1456 | - | History: &fakeHistoryService{}, | 1456 | + | History: &recordingHistoryService{}, |
| 1457 | - | Favorites: &fakeFavoritesService{}, | 1457 | + | Favorites: &recordingFavoritesService{}, |
| 1458 | - | Config: &fakeConfigService{current: testConfigPayload()}, | 1458 | + | Config: &recordingConfigService{current: testConfigPayload()}, |
| 1459 | }) | 1459 | }) | ||
| 1460 | 1460 | ||||
| 1461 | require.ErrorContains(t, app.handleHistoryAction(context.Background(), DOMAction{Action: "history.unknown"}), "unknown history action") | 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 | func TestSavePerformanceConfigRejectsOutOfRangeValues(t *testing.T) { | 1466 | func TestSavePerformanceConfigRejectsOutOfRangeValues(t *testing.T) { | ||
| 1467 | - | service := &fakeConfigService{current: testConfigPayload()} | 1467 | + | service := &recordingConfigService{current: testConfigPayload()} |
| 1468 | app := NewApp(Dependencies{Config: service}) | 1468 | app := NewApp(Dependencies{Config: service}) | ||
| 1469 | 1469 | ||||
| 1470 | err := app.handleConfigAction(context.Background(), DOMAction{ | 1470 | err := app.handleConfigAction(context.Background(), DOMAction{ | ||