--- phase: 02-permissions plan: 06 subsystem: ui tags: [wpf, mvvm, csharp, permissions, viewmodel, dialog, export] requires: - phase: 02-permissions provides: IPermissionsService (ScanSiteAsync), ISiteListService (GetSitesAsync), CsvExportService, HtmlExportService, FeatureViewModelBase provides: - PermissionsViewModel: full scan orchestrator extending FeatureViewModelBase - SitePickerDialog: multi-site selection dialog with checkboxes and filter - ISessionManager interface: abstraction over SessionManager for testability affects: - 02-07 (DI wiring — must register PermissionsViewModel, SitePickerDialog, ISessionManager) - 03-storage (same FeatureViewModelBase + ISessionManager pattern) tech-stack: added: [] patterns: - "ISessionManager interface extracted from concrete SessionManager for ViewModel testability" - "Flat ObservableProperty booleans (IncludeInherited, ScanFolders, FolderDepth, IncludeSubsites) assembled into ScanOptions record at scan time" - "Dialog factory pattern: PermissionsViewModel.OpenSitePickerDialog is Func? set by View layer" - "TestRunOperationAsync internal method bridges protected RunOperationAsync for xUnit tests" - "Dispatcher null-guard: Application.Current?.Dispatcher handles test context with no WPF message pump" key-files: created: - SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs - SharepointToolbox/Views/Dialogs/SitePickerDialog.xaml - SharepointToolbox/Views/Dialogs/SitePickerDialog.xaml.cs - SharepointToolbox/Services/ISessionManager.cs modified: - SharepointToolbox/Services/SessionManager.cs - SharepointToolbox.Tests/ViewModels/PermissionsViewModelTests.cs key-decisions: - "ISessionManager interface extracted — SessionManager is a concrete class; interface required for Moq-based unit testing of PermissionsViewModel" - "Test constructor (internal) omits CsvExportService/HtmlExportService — export services not needed for scan loop unit test, avoids null noise" - "Application.Current?.Dispatcher null-guard — WPF Dispatcher is null in xUnit test context; fall-through to direct assignment preserves testability" - "PermissionsViewModel uses ILogger — matches established pattern from SettingsViewModel" patterns-established: - "ISessionManager: all future feature ViewModels should inject ISessionManager (not concrete SessionManager) for testability" - "TestRunOperationAsync internal method: expose protected scan methods via internal test hook + InternalsVisibleTo" requirements-completed: [PERM-01, PERM-02, PERM-04, PERM-05, PERM-06] duration: 4min completed: 2026-04-02 --- # Phase 2 Plan 6: PermissionsViewModel and SitePickerDialog Summary **PermissionsViewModel orchestrates multi-site CSOM permission scans with TDD-verified scan loop, CSV/HTML export commands, and SitePickerDialog for multi-site selection via factory pattern** ## Performance - **Duration:** 4 min - **Started:** 2026-04-02T12:02:49Z - **Completed:** 2026-04-02T12:06:55Z - **Tasks:** 2 - **Files modified:** 6 ## Accomplishments - PermissionsViewModel fully implements FeatureViewModelBase with scan loop, export, and tenant-switch reset - SitePickerDialog XAML + code-behind: filterable ListView with checkboxes, loads via ISiteListService on Window.Loaded - ISessionManager interface extracted so ViewModels can be unit-tested without live MSAL/SharePoint - TDD: RED→GREEN cycle with StartScanAsync_WithMultipleSiteUrls_CallsServiceOncePerUrl passing; 60/60 tests pass ## Task Commits Each task was committed atomically: 1. **Task 1 RED: Failing test for PermissionsViewModel** - `c462a0b` (test) 2. **Task 1 GREEN + Task 2: Full PermissionsViewModel and SitePickerDialog** - `f98ca60` (feat) ## Files Created/Modified - `SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs` - Feature orchestrator: scan loop, export commands, dialog factory, tenant switch - `SharepointToolbox/Views/Dialogs/SitePickerDialog.xaml` - Multi-site picker: filterable list with CheckBox + Title + URL columns - `SharepointToolbox/Views/Dialogs/SitePickerDialog.xaml.cs` - Code-behind: loads sites on Loaded, exposes SelectedUrls, filter/select-all/deselect-all - `SharepointToolbox/Services/ISessionManager.cs` - Interface for SessionManager (new) - `SharepointToolbox/Services/SessionManager.cs` - Now implements ISessionManager - `SharepointToolbox.Tests/ViewModels/PermissionsViewModelTests.cs` - Real test replacing the previous stub ## Decisions Made - **ISessionManager extracted** — SessionManager is a concrete class with MSAL dependencies; interface required to mock it in unit tests. Matches "extract interface for testability" pattern from Phase 1 (IPermissionsService, ISiteListService already existed). - **Test constructor** — Internal constructor omits CsvExportService and HtmlExportService since export commands are not exercised in the scan loop test. Keeps tests lean. - **Dispatcher null-guard** — `Application.Current?.Dispatcher` is null in xUnit test context (no WPF thread). Guard ensures Results assignment succeeds in both test and production contexts. ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 2 - Missing Critical] ISessionManager interface extracted for testability** - **Found during:** Task 1 (PermissionsViewModel TDD setup) - **Issue:** Plan specified injecting concrete `SessionManager`. Moq cannot mock concrete classes without virtual methods; unit test required a mockable abstraction. - **Fix:** Created `ISessionManager` interface with `GetOrCreateContextAsync`, `ClearSessionAsync`, `IsAuthenticated`; `SessionManager` implements it. - **Files modified:** SharepointToolbox/Services/ISessionManager.cs (new), SharepointToolbox/Services/SessionManager.cs - **Verification:** Build succeeds, existing 60 tests still pass - **Committed in:** c462a0b (RED phase commit) --- **Total deviations:** 1 auto-fixed (1 missing critical) **Impact on plan:** Required for correct testability. SessionManager DI registration changes to `services.AddSingleton()` — handled in Plan 07. ## Issues Encountered None — plan executed as written with one necessary interface extraction for testability. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - PermissionsViewModel and SitePickerDialog complete — all business logic for Permissions tab is done - Plan 07 (DI wiring) must: register ISessionManager as singleton, register SitePickerDialog as Transient, set OpenSitePickerDialog factory in PermissionsView code-behind - 60 tests passing, 3 skipped (known interactive MSAL tests) --- *Phase: 02-permissions* *Completed: 2026-04-02* ## Self-Check: PASSED - FOUND: SharepointToolbox/ViewModels/Tabs/PermissionsViewModel.cs - FOUND: SharepointToolbox/Views/Dialogs/SitePickerDialog.xaml - FOUND: SharepointToolbox/Views/Dialogs/SitePickerDialog.xaml.cs - FOUND: SharepointToolbox/Services/ISessionManager.cs - FOUND commits: c462a0b (test), f98ca60 (feat) - Tests: 60 passed, 3 skipped, 0 failed