detached f91ca27

refactor(permission): inject logger boundary into use case

commit
f91ca27fe8020d07146c618f080e8fe3372a0b74
author
brice · · 2026-02-08 12:20 UTC
parent
2595e45
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
5 files · +89 -33
M cmd/dumber/main.go +1 -1
@@ -463,7 +463,7 @@ func createUseCases(repos *repositories, cfg *config.Config) *useCases {
463463 updateApplier := updater.NewApplier(stateDir)
464464
465465 // Permission use case will be initialized later with dialog presenter
466- permissionUC := usecase.NewHandlePermissionUseCase(repos.permission, nil)
466+ permissionUC := usecase.NewHandlePermissionUseCase(repos.permission, nil, logging.FromContext)
467467
468468 return &useCases{
469469 tabs: usecase.NewManageTabsUseCase(idGenerator),
@@ -463,7 +463,7 @@ func createUseCases(repos *repositories, cfg *config.Config) *useCases {
463 updateApplier := updater.NewApplier(stateDir)463 updateApplier := updater.NewApplier(stateDir)
464 464
465 // Permission use case will be initialized later with dialog presenter465 // Permission use case will be initialized later with dialog presenter
466- permissionUC := usecase.NewHandlePermissionUseCase(repos.permission, nil)466+ permissionUC := usecase.NewHandlePermissionUseCase(repos.permission, nil, logging.FromContext)
467 467
468 return &useCases{468 return &useCases{
469 tabs: usecase.NewManageTabsUseCase(idGenerator),469 tabs: usecase.NewManageTabsUseCase(idGenerator),
M docs/reference/permissions.md +6 -3
@@ -35,9 +35,12 @@ When a site requests mic/camera access, a dialog appears with four options:
3535
3636 ### Origin-Based Storage
3737
38-Permissions are stored per-origin (scheme + host), not per-page. This means:
38+Permissions are stored per-origin (scheme + host + port), not per-page.
39+Default ports are normalized away (`:80` for `http`, `:443` for `https`). This means:
3940
40-- `https://meet.google.com/room1` and `https://meet.google.com/room2` share the same permission (same host)
41+- `https://meet.google.com/room1` and `https://meet.google.com/room2` share the same permission (same origin)
42+- `https://meet.google.com:443/room1` and `https://meet.google.com/room1` share the same permission (default `https` port)
43+- `https://meet.google.com:8443/room1` and `https://meet.google.com/room1` have different permissions (different port)
4144 - `https://github.com` and `https://gist.github.com` have different permissions (different hosts)
4245
4346 ### Permission States
@@ -52,7 +55,7 @@ The permission system follows W3C specifications:
5255
5356 - Display capture permissions are **never persisted** (always prompt via portal)
5457 - Mic/camera permissions **can be persisted** with user consent
55-- Permissions are scoped to origins (scheme + host)
58+- Permissions are scoped to origins (scheme + host + port)
5659
5760 ## Managing Stored Permissions
5861
@@ -35,9 +35,12 @@ When a site requests mic/camera access, a dialog appears with four options:
35 35
36 ### Origin-Based Storage36 ### Origin-Based Storage
37 37
38-Permissions are stored per-origin (scheme + host), not per-page. This means:38+Permissions are stored per-origin (scheme + host + port), not per-page.
39+Default ports are normalized away (`:80` for `http`, `:443` for `https`). This means:
39 40
40-- `https://meet.google.com/room1` and `https://meet.google.com/room2` share the same permission (same host)41+- `https://meet.google.com/room1` and `https://meet.google.com/room2` share the same permission (same origin)
42+- `https://meet.google.com:443/room1` and `https://meet.google.com/room1` share the same permission (default `https` port)
43+- `https://meet.google.com:8443/room1` and `https://meet.google.com/room1` have different permissions (different port)
41 - `https://github.com` and `https://gist.github.com` have different permissions (different hosts)44 - `https://github.com` and `https://gist.github.com` have different permissions (different hosts)
42 45
43 ### Permission States46 ### Permission States
@@ -52,7 +55,7 @@ The permission system follows W3C specifications:
52 55
53 - Display capture permissions are **never persisted** (always prompt via portal)56 - Display capture permissions are **never persisted** (always prompt via portal)
54 - Mic/camera permissions **can be persisted** with user consent57 - Mic/camera permissions **can be persisted** with user consent
55-- Permissions are scoped to origins (scheme + host)58+- Permissions are scoped to origins (scheme + host + port)
56 59
57 ## Managing Stored Permissions60 ## Managing Stored Permissions
58 61
A internal/application/port/logger.go +11 -0
@@ -0,0 +1,11 @@
1+package port
2+
3+import (
4+ "context"
5+
6+ "github.com/rs/zerolog"
7+)
8+
9+// LoggerFromContext resolves the current request/session logger from context.
10+// Use cases depend on this boundary instead of importing infrastructure logging.
11+type LoggerFromContext func(ctx context.Context) *zerolog.Logger
@@ -0,0 +1,11 @@
1+package port
2+
3+import (
4+ "context"
5+
6+ "github.com/rs/zerolog"
7+)
8+
9+// LoggerFromContext resolves the current request/session logger from context.
10+// Use cases depend on this boundary instead of importing infrastructure logging.
11+type LoggerFromContext func(ctx context.Context) *zerolog.Logger
M internal/application/usecase/handle_permission.go +28 -11
@@ -9,7 +9,7 @@ import (
99
1010 "github.com/bnema/dumber/internal/application/port"
1111 "github.com/bnema/dumber/internal/domain/entity"
12- "github.com/bnema/dumber/internal/logging"
12+ "github.com/rs/zerolog"
1313 )
1414
1515 // PermissionCallback provides allow/deny functions for the permission request.
@@ -24,22 +24,39 @@ type PermissionCallback struct {
2424 // - Device enumeration: auto-allow (low risk)
2525 // - Mic/Camera: check stored → dialog → persist if "Always"
2626 type HandlePermissionUseCase struct {
27- permRepo port.PermissionRepository
28- dialog port.PermissionDialogPresenter
29- dialogMu sync.RWMutex
27+ permRepo port.PermissionRepository
28+ dialog port.PermissionDialogPresenter
29+ loggerFromContext port.LoggerFromContext
30+ dialogMu sync.RWMutex
3031 }
3132
3233 // NewHandlePermissionUseCase creates a new permission handling use case.
3334 func NewHandlePermissionUseCase(
3435 permRepo port.PermissionRepository,
3536 dialog port.PermissionDialogPresenter,
37+ loggerFromContext port.LoggerFromContext,
3638 ) *HandlePermissionUseCase {
3739 return &HandlePermissionUseCase{
38- permRepo: permRepo,
39- dialog: dialog,
40+ permRepo: permRepo,
41+ dialog: dialog,
42+ loggerFromContext: loggerFromContext,
4043 }
4144 }
4245
46+func (uc *HandlePermissionUseCase) logger(ctx context.Context) *zerolog.Logger {
47+ if ctx == nil {
48+ ctx = context.Background()
49+ }
50+ if uc == nil || uc.loggerFromContext == nil {
51+ return zerolog.Ctx(ctx)
52+ }
53+ log := uc.loggerFromContext(ctx)
54+ if log == nil {
55+ return zerolog.Ctx(ctx)
56+ }
57+ return log
58+}
59+
4360 // SetDialogPresenter sets the dialog presenter. This can be called after
4461 // initialization when the UI window is available.
4562 func (uc *HandlePermissionUseCase) SetDialogPresenter(dialog port.PermissionDialogPresenter) {
@@ -69,7 +86,7 @@ func (uc *HandlePermissionUseCase) HandlePermissionRequest(
6986 permTypes []entity.PermissionType,
7087 callback PermissionCallback,
7188 ) {
72- log := logging.FromContext(ctx).With().
89+ log := uc.logger(ctx).With().
7390 Str("component", "permission").
7491 Str("origin", origin).
7592 Strs("types", entity.PermissionTypesToStrings(permTypes)).
@@ -128,7 +145,7 @@ func (uc *HandlePermissionUseCase) QueryPermissionState(
128145 origin string,
129146 permType entity.PermissionType,
130147 ) entity.PermissionDecision {
131- log := logging.FromContext(ctx).With().
148+ log := uc.logger(ctx).With().
132149 Str("component", "permission").
133150 Str("origin", origin).
134151 Str("type", string(permType)).
@@ -172,7 +189,7 @@ func (uc *HandlePermissionUseCase) checkStoredPermissions(
172189 origin string,
173190 permTypes []entity.PermissionType,
174191 ) entity.PermissionDecision {
175- log := logging.FromContext(ctx)
192+ log := uc.logger(ctx)
176193
177194 hasPrompt := false
178195
@@ -229,7 +246,7 @@ func (uc *HandlePermissionUseCase) showPermissionDialog(
229246 permTypes []entity.PermissionType,
230247 callback PermissionCallback,
231248 ) {
232- log := logging.FromContext(ctx)
249+ log := uc.logger(ctx)
233250
234251 dialog := uc.getDialog()
235252
@@ -268,7 +285,7 @@ func (uc *HandlePermissionUseCase) persistPermission(
268285 permTypes []entity.PermissionType,
269286 decision entity.PermissionDecision,
270287 ) {
271- log := logging.FromContext(ctx)
288+ log := uc.logger(ctx)
272289
273290 for _, permType := range permTypes {
274291 // Only persist allowed types
@@ -9,7 +9,7 @@ import (
9 9
10 "github.com/bnema/dumber/internal/application/port"10 "github.com/bnema/dumber/internal/application/port"
11 "github.com/bnema/dumber/internal/domain/entity"11 "github.com/bnema/dumber/internal/domain/entity"
12- "github.com/bnema/dumber/internal/logging"12+ "github.com/rs/zerolog"
13 )13 )
14 14
15 // PermissionCallback provides allow/deny functions for the permission request.15 // PermissionCallback provides allow/deny functions for the permission request.
@@ -24,22 +24,39 @@ type PermissionCallback struct {
24 // - Device enumeration: auto-allow (low risk)24 // - Device enumeration: auto-allow (low risk)
25 // - Mic/Camera: check stored → dialog → persist if "Always"25 // - Mic/Camera: check stored → dialog → persist if "Always"
26 type HandlePermissionUseCase struct {26 type HandlePermissionUseCase struct {
27- permRepo port.PermissionRepository27+ permRepo port.PermissionRepository
28- dialog port.PermissionDialogPresenter28+ dialog port.PermissionDialogPresenter
29- dialogMu sync.RWMutex29+ loggerFromContext port.LoggerFromContext
30+ dialogMu sync.RWMutex
30 }31 }
31 32
32 // NewHandlePermissionUseCase creates a new permission handling use case.33 // NewHandlePermissionUseCase creates a new permission handling use case.
33 func NewHandlePermissionUseCase(34 func NewHandlePermissionUseCase(
34 permRepo port.PermissionRepository,35 permRepo port.PermissionRepository,
35 dialog port.PermissionDialogPresenter,36 dialog port.PermissionDialogPresenter,
37+ loggerFromContext port.LoggerFromContext,
36 ) *HandlePermissionUseCase {38 ) *HandlePermissionUseCase {
37 return &HandlePermissionUseCase{39 return &HandlePermissionUseCase{
38- permRepo: permRepo,40+ permRepo: permRepo,
39- dialog: dialog,41+ dialog: dialog,
42+ loggerFromContext: loggerFromContext,
40 }43 }
41 }44 }
42 45
46+func (uc *HandlePermissionUseCase) logger(ctx context.Context) *zerolog.Logger {
47+ if ctx == nil {
48+ ctx = context.Background()
49+ }
50+ if uc == nil || uc.loggerFromContext == nil {
51+ return zerolog.Ctx(ctx)
52+ }
53+ log := uc.loggerFromContext(ctx)
54+ if log == nil {
55+ return zerolog.Ctx(ctx)
56+ }
57+ return log
58+}
59+
43 // SetDialogPresenter sets the dialog presenter. This can be called after60 // SetDialogPresenter sets the dialog presenter. This can be called after
44 // initialization when the UI window is available.61 // initialization when the UI window is available.
45 func (uc *HandlePermissionUseCase) SetDialogPresenter(dialog port.PermissionDialogPresenter) {62 func (uc *HandlePermissionUseCase) SetDialogPresenter(dialog port.PermissionDialogPresenter) {
@@ -69,7 +86,7 @@ func (uc *HandlePermissionUseCase) HandlePermissionRequest(
69 permTypes []entity.PermissionType,86 permTypes []entity.PermissionType,
70 callback PermissionCallback,87 callback PermissionCallback,
71 ) {88 ) {
72- log := logging.FromContext(ctx).With().89+ log := uc.logger(ctx).With().
73 Str("component", "permission").90 Str("component", "permission").
74 Str("origin", origin).91 Str("origin", origin).
75 Strs("types", entity.PermissionTypesToStrings(permTypes)).92 Strs("types", entity.PermissionTypesToStrings(permTypes)).
@@ -128,7 +145,7 @@ func (uc *HandlePermissionUseCase) QueryPermissionState(
128 origin string,145 origin string,
129 permType entity.PermissionType,146 permType entity.PermissionType,
130 ) entity.PermissionDecision {147 ) entity.PermissionDecision {
131- log := logging.FromContext(ctx).With().148+ log := uc.logger(ctx).With().
132 Str("component", "permission").149 Str("component", "permission").
133 Str("origin", origin).150 Str("origin", origin).
134 Str("type", string(permType)).151 Str("type", string(permType)).
@@ -172,7 +189,7 @@ func (uc *HandlePermissionUseCase) checkStoredPermissions(
172 origin string,189 origin string,
173 permTypes []entity.PermissionType,190 permTypes []entity.PermissionType,
174 ) entity.PermissionDecision {191 ) entity.PermissionDecision {
175- log := logging.FromContext(ctx)192+ log := uc.logger(ctx)
176 193
177 hasPrompt := false194 hasPrompt := false
178 195
@@ -229,7 +246,7 @@ func (uc *HandlePermissionUseCase) showPermissionDialog(
229 permTypes []entity.PermissionType,246 permTypes []entity.PermissionType,
230 callback PermissionCallback,247 callback PermissionCallback,
231 ) {248 ) {
232- log := logging.FromContext(ctx)249+ log := uc.logger(ctx)
233 250
234 dialog := uc.getDialog()251 dialog := uc.getDialog()
235 252
@@ -268,7 +285,7 @@ func (uc *HandlePermissionUseCase) persistPermission(
268 permTypes []entity.PermissionType,285 permTypes []entity.PermissionType,
269 decision entity.PermissionDecision,286 decision entity.PermissionDecision,
270 ) {287 ) {
271- log := logging.FromContext(ctx)288+ log := uc.logger(ctx)
272 289
273 for _, permType := range permTypes {290 for _, permType := range permTypes {
274 // Only persist allowed types291 // Only persist allowed types
M internal/application/usecase/handle_permission_test.go +43 -18
@@ -10,17 +10,22 @@ import (
1010 "github.com/bnema/dumber/internal/application/usecase"
1111 "github.com/bnema/dumber/internal/domain/entity"
1212 repomocks "github.com/bnema/dumber/internal/domain/repository/mocks"
13+ "github.com/rs/zerolog"
1314 "github.com/stretchr/testify/assert"
1415 "github.com/stretchr/testify/mock"
1516 "github.com/stretchr/testify/require"
1617 )
1718
19+func permissionLoggerFromContext(ctx context.Context) *zerolog.Logger {
20+ return zerolog.Ctx(ctx)
21+}
22+
1823 func TestHandlePermissionUseCase_AutoAllowDisplayCapture(t *testing.T) {
1924 ctx := testContext()
2025 permRepo := repomocks.NewMockPermissionRepository(t)
2126 dialog := portmocks.NewMockPermissionDialogPresenter(t)
2227
23- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
28+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
2429
2530 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeDisplay).
2631 Return(nil, nil)
@@ -47,7 +52,7 @@ func TestHandlePermissionUseCase_AutoAllowDeviceInfo(t *testing.T) {
4752 permRepo := repomocks.NewMockPermissionRepository(t)
4853 dialog := portmocks.NewMockPermissionDialogPresenter(t)
4954
50- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
55+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
5156
5257 permRepo.EXPECT().Get(mock.Anything, "https://example.com", entity.PermissionTypeDeviceInfo).
5358 Return(nil, nil)
@@ -71,7 +76,7 @@ func TestHandlePermissionUseCase_StoredPermissionGranted(t *testing.T) {
7176 permRepo := repomocks.NewMockPermissionRepository(t)
7277 dialog := portmocks.NewMockPermissionDialogPresenter(t)
7378
74- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
79+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
7580
7681 // Return stored granted permission
7782 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).
@@ -101,7 +106,7 @@ func TestHandlePermissionUseCase_StoredPermissionDenied(t *testing.T) {
101106 permRepo := repomocks.NewMockPermissionRepository(t)
102107 dialog := portmocks.NewMockPermissionDialogPresenter(t)
103108
104- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
109+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
105110
106111 // Return stored denied permission
107112 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeCamera).
@@ -131,7 +136,7 @@ func TestHandlePermissionUseCase_ShowDialogForPrompt(t *testing.T) {
131136 permRepo := repomocks.NewMockPermissionRepository(t)
132137 dialog := portmocks.NewMockPermissionDialogPresenter(t)
133138
134- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
139+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
135140
136141 // No stored permission (returns nil)
137142 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).
@@ -164,7 +169,7 @@ func TestHandlePermissionUseCase_PersistAllowed(t *testing.T) {
164169 permRepo := repomocks.NewMockPermissionRepository(t)
165170 dialog := portmocks.NewMockPermissionDialogPresenter(t)
166171
167- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
172+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
168173
169174 // No stored permission
170175 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).
@@ -203,7 +208,7 @@ func TestHandlePermissionUseCase_PersistDenied(t *testing.T) {
203208 permRepo := repomocks.NewMockPermissionRepository(t)
204209 dialog := portmocks.NewMockPermissionDialogPresenter(t)
205210
206- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
211+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
207212
208213 // No stored permission
209214 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeCamera).
@@ -241,7 +246,7 @@ func TestHandlePermissionUseCase_EmptyOrigin(t *testing.T) {
241246 permRepo := repomocks.NewMockPermissionRepository(t)
242247 dialog := portmocks.NewMockPermissionDialogPresenter(t)
243248
244- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
249+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
245250
246251 denied := false
247252 callback := usecase.PermissionCallback{
@@ -262,7 +267,7 @@ func TestHandlePermissionUseCase_CombinedPermissions(t *testing.T) {
262267 permRepo := repomocks.NewMockPermissionRepository(t)
263268 dialog := portmocks.NewMockPermissionDialogPresenter(t)
264269
265- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
270+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
266271
267272 // Both mic and camera - one denied means whole request denied
268273 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).
@@ -298,7 +303,7 @@ func TestHandlePermissionUseCase_QueryPermissionState_AutoAllow(t *testing.T) {
298303 permRepo := repomocks.NewMockPermissionRepository(t)
299304 dialog := portmocks.NewMockPermissionDialogPresenter(t)
300305
301- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
306+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
302307 permRepo.EXPECT().Get(mock.Anything, "https://example.com", entity.PermissionTypeDisplay).
303308 Return(nil, nil)
304309
@@ -312,7 +317,7 @@ func TestHandlePermissionUseCase_QueryPermissionState_AutoAllowManualOverride(t
312317 permRepo := repomocks.NewMockPermissionRepository(t)
313318 dialog := portmocks.NewMockPermissionDialogPresenter(t)
314319
315- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
320+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
316321
317322 permRepo.EXPECT().Get(mock.Anything, "https://example.com", entity.PermissionTypeDisplay).
318323 Return(&entity.PermissionRecord{
@@ -331,7 +336,7 @@ func TestHandlePermissionUseCase_QueryPermissionState_Stored(t *testing.T) {
331336 permRepo := repomocks.NewMockPermissionRepository(t)
332337 dialog := portmocks.NewMockPermissionDialogPresenter(t)
333338
334- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
339+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
335340
336341 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).
337342 Return(&entity.PermissionRecord{
@@ -350,7 +355,7 @@ func TestHandlePermissionUseCase_QueryPermissionState_NoRecord(t *testing.T) {
350355 permRepo := repomocks.NewMockPermissionRepository(t)
351356 dialog := portmocks.NewMockPermissionDialogPresenter(t)
352357
353- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
358+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
354359
355360 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeCamera).
356361 Return(nil, nil)
@@ -365,7 +370,7 @@ func TestHandlePermissionUseCase_NonPersistableNotSaved(t *testing.T) {
365370 permRepo := repomocks.NewMockPermissionRepository(t)
366371 dialog := portmocks.NewMockPermissionDialogPresenter(t)
367372
368- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
373+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
369374
370375 // Display capture is not persistable per W3C spec
371376 // A stored override may exist, so a Get call is expected before auto-allow.
@@ -392,7 +397,7 @@ func TestHandlePermissionUseCase_NoDialogPresenter(t *testing.T) {
392397 permRepo := repomocks.NewMockPermissionRepository(t)
393398
394399 // Create use case with nil dialog presenter
395- uc := usecase.NewHandlePermissionUseCase(permRepo, nil)
400+ uc := usecase.NewHandlePermissionUseCase(permRepo, nil, permissionLoggerFromContext)
396401
397402 denied := false
398403 callback := usecase.PermissionCallback{
@@ -416,7 +421,7 @@ func TestHandlePermissionUseCase_DisplayOverrideDenied(t *testing.T) {
416421 permRepo := repomocks.NewMockPermissionRepository(t)
417422 dialog := portmocks.NewMockPermissionDialogPresenter(t)
418423
419- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)
424+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
420425
421426 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeDisplay).
422427 Return(&entity.PermissionRecord{
@@ -442,7 +447,7 @@ func TestHandlePermissionUseCase_DisplayOverrideDenied(t *testing.T) {
442447 func TestHandlePermissionUseCase_GetSetResetManualDecision(t *testing.T) {
443448 ctx := testContext()
444449 permRepo := repomocks.NewMockPermissionRepository(t)
445- uc := usecase.NewHandlePermissionUseCase(permRepo, nil)
450+ uc := usecase.NewHandlePermissionUseCase(permRepo, nil, permissionLoggerFromContext)
446451
447452 permRepo.EXPECT().Set(mock.Anything, mock.AnythingOfType("*entity.PermissionRecord")).
448453 Run(func(_ context.Context, record *entity.PermissionRecord) {
@@ -482,7 +487,7 @@ func TestHandlePermissionUseCase_GetSetResetManualDecision(t *testing.T) {
482487 func TestHandlePermissionUseCase_SetManualPermissionDecision_NonPersistable(t *testing.T) {
483488 ctx := testContext()
484489 permRepo := repomocks.NewMockPermissionRepository(t)
485- uc := usecase.NewHandlePermissionUseCase(permRepo, nil)
490+ uc := usecase.NewHandlePermissionUseCase(permRepo, nil, permissionLoggerFromContext)
486491
487492 err := uc.SetManualPermissionDecision(
488493 ctx,
@@ -496,3 +501,23 @@ func TestHandlePermissionUseCase_SetManualPermissionDecision_NonPersistable(t *t
496501 permRepo.AssertNotCalled(t, "Set")
497502 permRepo.AssertNotCalled(t, "Delete")
498503 }
504+
505+func TestHandlePermissionUseCase_UsesInjectedLoggerFactory(t *testing.T) {
506+ ctx := testContext()
507+ permRepo := repomocks.NewMockPermissionRepository(t)
508+ dialog := portmocks.NewMockPermissionDialogPresenter(t)
509+
510+ called := false
511+ loggerFactory := func(ctx context.Context) *zerolog.Logger {
512+ called = true
513+ return zerolog.Ctx(ctx)
514+ }
515+
516+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, loggerFactory)
517+ uc.HandlePermissionRequest(ctx, "", []entity.PermissionType{entity.PermissionTypeMicrophone}, usecase.PermissionCallback{
518+ Allow: func() {},
519+ Deny: func() {},
520+ })
521+
522+ assert.True(t, called)
523+}
@@ -10,17 +10,22 @@ import (
10 "github.com/bnema/dumber/internal/application/usecase"10 "github.com/bnema/dumber/internal/application/usecase"
11 "github.com/bnema/dumber/internal/domain/entity"11 "github.com/bnema/dumber/internal/domain/entity"
12 repomocks "github.com/bnema/dumber/internal/domain/repository/mocks"12 repomocks "github.com/bnema/dumber/internal/domain/repository/mocks"
13+ "github.com/rs/zerolog"
13 "github.com/stretchr/testify/assert"14 "github.com/stretchr/testify/assert"
14 "github.com/stretchr/testify/mock"15 "github.com/stretchr/testify/mock"
15 "github.com/stretchr/testify/require"16 "github.com/stretchr/testify/require"
16 )17 )
17 18
19+func permissionLoggerFromContext(ctx context.Context) *zerolog.Logger {
20+ return zerolog.Ctx(ctx)
21+}
22+
18 func TestHandlePermissionUseCase_AutoAllowDisplayCapture(t *testing.T) {23 func TestHandlePermissionUseCase_AutoAllowDisplayCapture(t *testing.T) {
19 ctx := testContext()24 ctx := testContext()
20 permRepo := repomocks.NewMockPermissionRepository(t)25 permRepo := repomocks.NewMockPermissionRepository(t)
21 dialog := portmocks.NewMockPermissionDialogPresenter(t)26 dialog := portmocks.NewMockPermissionDialogPresenter(t)
22 27
23- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)28+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
24 29
25 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeDisplay).30 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeDisplay).
26 Return(nil, nil)31 Return(nil, nil)
@@ -47,7 +52,7 @@ func TestHandlePermissionUseCase_AutoAllowDeviceInfo(t *testing.T) {
47 permRepo := repomocks.NewMockPermissionRepository(t)52 permRepo := repomocks.NewMockPermissionRepository(t)
48 dialog := portmocks.NewMockPermissionDialogPresenter(t)53 dialog := portmocks.NewMockPermissionDialogPresenter(t)
49 54
50- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)55+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
51 56
52 permRepo.EXPECT().Get(mock.Anything, "https://example.com", entity.PermissionTypeDeviceInfo).57 permRepo.EXPECT().Get(mock.Anything, "https://example.com", entity.PermissionTypeDeviceInfo).
53 Return(nil, nil)58 Return(nil, nil)
@@ -71,7 +76,7 @@ func TestHandlePermissionUseCase_StoredPermissionGranted(t *testing.T) {
71 permRepo := repomocks.NewMockPermissionRepository(t)76 permRepo := repomocks.NewMockPermissionRepository(t)
72 dialog := portmocks.NewMockPermissionDialogPresenter(t)77 dialog := portmocks.NewMockPermissionDialogPresenter(t)
73 78
74- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)79+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
75 80
76 // Return stored granted permission81 // Return stored granted permission
77 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).82 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).
@@ -101,7 +106,7 @@ func TestHandlePermissionUseCase_StoredPermissionDenied(t *testing.T) {
101 permRepo := repomocks.NewMockPermissionRepository(t)106 permRepo := repomocks.NewMockPermissionRepository(t)
102 dialog := portmocks.NewMockPermissionDialogPresenter(t)107 dialog := portmocks.NewMockPermissionDialogPresenter(t)
103 108
104- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)109+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
105 110
106 // Return stored denied permission111 // Return stored denied permission
107 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeCamera).112 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeCamera).
@@ -131,7 +136,7 @@ func TestHandlePermissionUseCase_ShowDialogForPrompt(t *testing.T) {
131 permRepo := repomocks.NewMockPermissionRepository(t)136 permRepo := repomocks.NewMockPermissionRepository(t)
132 dialog := portmocks.NewMockPermissionDialogPresenter(t)137 dialog := portmocks.NewMockPermissionDialogPresenter(t)
133 138
134- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)139+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
135 140
136 // No stored permission (returns nil)141 // No stored permission (returns nil)
137 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).142 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).
@@ -164,7 +169,7 @@ func TestHandlePermissionUseCase_PersistAllowed(t *testing.T) {
164 permRepo := repomocks.NewMockPermissionRepository(t)169 permRepo := repomocks.NewMockPermissionRepository(t)
165 dialog := portmocks.NewMockPermissionDialogPresenter(t)170 dialog := portmocks.NewMockPermissionDialogPresenter(t)
166 171
167- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)172+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
168 173
169 // No stored permission174 // No stored permission
170 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).175 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).
@@ -203,7 +208,7 @@ func TestHandlePermissionUseCase_PersistDenied(t *testing.T) {
203 permRepo := repomocks.NewMockPermissionRepository(t)208 permRepo := repomocks.NewMockPermissionRepository(t)
204 dialog := portmocks.NewMockPermissionDialogPresenter(t)209 dialog := portmocks.NewMockPermissionDialogPresenter(t)
205 210
206- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)211+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
207 212
208 // No stored permission213 // No stored permission
209 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeCamera).214 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeCamera).
@@ -241,7 +246,7 @@ func TestHandlePermissionUseCase_EmptyOrigin(t *testing.T) {
241 permRepo := repomocks.NewMockPermissionRepository(t)246 permRepo := repomocks.NewMockPermissionRepository(t)
242 dialog := portmocks.NewMockPermissionDialogPresenter(t)247 dialog := portmocks.NewMockPermissionDialogPresenter(t)
243 248
244- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)249+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
245 250
246 denied := false251 denied := false
247 callback := usecase.PermissionCallback{252 callback := usecase.PermissionCallback{
@@ -262,7 +267,7 @@ func TestHandlePermissionUseCase_CombinedPermissions(t *testing.T) {
262 permRepo := repomocks.NewMockPermissionRepository(t)267 permRepo := repomocks.NewMockPermissionRepository(t)
263 dialog := portmocks.NewMockPermissionDialogPresenter(t)268 dialog := portmocks.NewMockPermissionDialogPresenter(t)
264 269
265- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)270+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
266 271
267 // Both mic and camera - one denied means whole request denied272 // Both mic and camera - one denied means whole request denied
268 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).273 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).
@@ -298,7 +303,7 @@ func TestHandlePermissionUseCase_QueryPermissionState_AutoAllow(t *testing.T) {
298 permRepo := repomocks.NewMockPermissionRepository(t)303 permRepo := repomocks.NewMockPermissionRepository(t)
299 dialog := portmocks.NewMockPermissionDialogPresenter(t)304 dialog := portmocks.NewMockPermissionDialogPresenter(t)
300 305
301- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)306+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
302 permRepo.EXPECT().Get(mock.Anything, "https://example.com", entity.PermissionTypeDisplay).307 permRepo.EXPECT().Get(mock.Anything, "https://example.com", entity.PermissionTypeDisplay).
303 Return(nil, nil)308 Return(nil, nil)
304 309
@@ -312,7 +317,7 @@ func TestHandlePermissionUseCase_QueryPermissionState_AutoAllowManualOverride(t
312 permRepo := repomocks.NewMockPermissionRepository(t)317 permRepo := repomocks.NewMockPermissionRepository(t)
313 dialog := portmocks.NewMockPermissionDialogPresenter(t)318 dialog := portmocks.NewMockPermissionDialogPresenter(t)
314 319
315- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)320+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
316 321
317 permRepo.EXPECT().Get(mock.Anything, "https://example.com", entity.PermissionTypeDisplay).322 permRepo.EXPECT().Get(mock.Anything, "https://example.com", entity.PermissionTypeDisplay).
318 Return(&entity.PermissionRecord{323 Return(&entity.PermissionRecord{
@@ -331,7 +336,7 @@ func TestHandlePermissionUseCase_QueryPermissionState_Stored(t *testing.T) {
331 permRepo := repomocks.NewMockPermissionRepository(t)336 permRepo := repomocks.NewMockPermissionRepository(t)
332 dialog := portmocks.NewMockPermissionDialogPresenter(t)337 dialog := portmocks.NewMockPermissionDialogPresenter(t)
333 338
334- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)339+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
335 340
336 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).341 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeMicrophone).
337 Return(&entity.PermissionRecord{342 Return(&entity.PermissionRecord{
@@ -350,7 +355,7 @@ func TestHandlePermissionUseCase_QueryPermissionState_NoRecord(t *testing.T) {
350 permRepo := repomocks.NewMockPermissionRepository(t)355 permRepo := repomocks.NewMockPermissionRepository(t)
351 dialog := portmocks.NewMockPermissionDialogPresenter(t)356 dialog := portmocks.NewMockPermissionDialogPresenter(t)
352 357
353- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)358+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
354 359
355 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeCamera).360 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeCamera).
356 Return(nil, nil)361 Return(nil, nil)
@@ -365,7 +370,7 @@ func TestHandlePermissionUseCase_NonPersistableNotSaved(t *testing.T) {
365 permRepo := repomocks.NewMockPermissionRepository(t)370 permRepo := repomocks.NewMockPermissionRepository(t)
366 dialog := portmocks.NewMockPermissionDialogPresenter(t)371 dialog := portmocks.NewMockPermissionDialogPresenter(t)
367 372
368- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)373+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
369 374
370 // Display capture is not persistable per W3C spec375 // Display capture is not persistable per W3C spec
371 // A stored override may exist, so a Get call is expected before auto-allow.376 // A stored override may exist, so a Get call is expected before auto-allow.
@@ -392,7 +397,7 @@ func TestHandlePermissionUseCase_NoDialogPresenter(t *testing.T) {
392 permRepo := repomocks.NewMockPermissionRepository(t)397 permRepo := repomocks.NewMockPermissionRepository(t)
393 398
394 // Create use case with nil dialog presenter399 // Create use case with nil dialog presenter
395- uc := usecase.NewHandlePermissionUseCase(permRepo, nil)400+ uc := usecase.NewHandlePermissionUseCase(permRepo, nil, permissionLoggerFromContext)
396 401
397 denied := false402 denied := false
398 callback := usecase.PermissionCallback{403 callback := usecase.PermissionCallback{
@@ -416,7 +421,7 @@ func TestHandlePermissionUseCase_DisplayOverrideDenied(t *testing.T) {
416 permRepo := repomocks.NewMockPermissionRepository(t)421 permRepo := repomocks.NewMockPermissionRepository(t)
417 dialog := portmocks.NewMockPermissionDialogPresenter(t)422 dialog := portmocks.NewMockPermissionDialogPresenter(t)
418 423
419- uc := usecase.NewHandlePermissionUseCase(permRepo, dialog)424+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
420 425
421 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeDisplay).426 permRepo.EXPECT().Get(mock.Anything, "https://meet.example.com", entity.PermissionTypeDisplay).
422 Return(&entity.PermissionRecord{427 Return(&entity.PermissionRecord{
@@ -442,7 +447,7 @@ func TestHandlePermissionUseCase_DisplayOverrideDenied(t *testing.T) {
442 func TestHandlePermissionUseCase_GetSetResetManualDecision(t *testing.T) {447 func TestHandlePermissionUseCase_GetSetResetManualDecision(t *testing.T) {
443 ctx := testContext()448 ctx := testContext()
444 permRepo := repomocks.NewMockPermissionRepository(t)449 permRepo := repomocks.NewMockPermissionRepository(t)
445- uc := usecase.NewHandlePermissionUseCase(permRepo, nil)450+ uc := usecase.NewHandlePermissionUseCase(permRepo, nil, permissionLoggerFromContext)
446 451
447 permRepo.EXPECT().Set(mock.Anything, mock.AnythingOfType("*entity.PermissionRecord")).452 permRepo.EXPECT().Set(mock.Anything, mock.AnythingOfType("*entity.PermissionRecord")).
448 Run(func(_ context.Context, record *entity.PermissionRecord) {453 Run(func(_ context.Context, record *entity.PermissionRecord) {
@@ -482,7 +487,7 @@ func TestHandlePermissionUseCase_GetSetResetManualDecision(t *testing.T) {
482 func TestHandlePermissionUseCase_SetManualPermissionDecision_NonPersistable(t *testing.T) {487 func TestHandlePermissionUseCase_SetManualPermissionDecision_NonPersistable(t *testing.T) {
483 ctx := testContext()488 ctx := testContext()
484 permRepo := repomocks.NewMockPermissionRepository(t)489 permRepo := repomocks.NewMockPermissionRepository(t)
485- uc := usecase.NewHandlePermissionUseCase(permRepo, nil)490+ uc := usecase.NewHandlePermissionUseCase(permRepo, nil, permissionLoggerFromContext)
486 491
487 err := uc.SetManualPermissionDecision(492 err := uc.SetManualPermissionDecision(
488 ctx,493 ctx,
@@ -496,3 +501,23 @@ func TestHandlePermissionUseCase_SetManualPermissionDecision_NonPersistable(t *t
496 permRepo.AssertNotCalled(t, "Set")501 permRepo.AssertNotCalled(t, "Set")
497 permRepo.AssertNotCalled(t, "Delete")502 permRepo.AssertNotCalled(t, "Delete")
498 }503 }
504+
505+func TestHandlePermissionUseCase_UsesInjectedLoggerFactory(t *testing.T) {
506+ ctx := testContext()
507+ permRepo := repomocks.NewMockPermissionRepository(t)
508+ dialog := portmocks.NewMockPermissionDialogPresenter(t)
509+
510+ called := false
511+ loggerFactory := func(ctx context.Context) *zerolog.Logger {
512+ called = true
513+ return zerolog.Ctx(ctx)
514+ }
515+
516+ uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, loggerFactory)
517+ uc.HandlePermissionRequest(ctx, "", []entity.PermissionType{entity.PermissionTypeMicrophone}, usecase.PermissionCallback{
518+ Allow: func() {},
519+ Deny: func() {},
520+ })
521+
522+ assert.True(t, called)
523+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close