docs: map existing codebase
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
256
.planning/codebase/TESTING.md
Normal file
256
.planning/codebase/TESTING.md
Normal file
@@ -0,0 +1,256 @@
|
||||
# Testing Patterns
|
||||
|
||||
**Analysis Date:** 2026-04-02
|
||||
|
||||
## Test Framework
|
||||
|
||||
**Status:** No automated testing framework detected
|
||||
|
||||
**Infrastructure:** Not applicable
|
||||
- No test runner (Jest, Vitest, Pester)
|
||||
- No test configuration files
|
||||
- No test suite in codebase
|
||||
- No CI/CD pipeline test stage configured
|
||||
|
||||
## Testing Approach
|
||||
|
||||
**Current Testing Model:** Manual testing via GUI
|
||||
|
||||
**Test Methods:**
|
||||
- **GUI Testing:** All functionality tested through WinForms UI
|
||||
- Manual interaction with controls and dialogs
|
||||
- Visual verification of results in generated reports
|
||||
- Log output observation in RichTextBox
|
||||
- **Report Validation:** HTML and CSV exports manually reviewed for correctness
|
||||
- **API Integration:** Manual testing of PnP.PowerShell operations against live SharePoint tenant
|
||||
- **Regression Testing:** Ad-hoc manual verification of features after changes
|
||||
|
||||
## Code Organization for Testing
|
||||
|
||||
**Testability Patterns:** Limited
|
||||
|
||||
The monolithic single-file architecture (`Sharepoint_ToolBox.ps1` at 6408 lines) makes isolated unit testing challenging. Key observations:
|
||||
|
||||
**Tight Coupling to UI:**
|
||||
- Core business logic embedded in event handlers
|
||||
- Heavy reliance on global `$script:` scope for state
|
||||
- Example: `Load-Profiles` reads from `Get-ProfilesFilePath`, which is file-system dependent
|
||||
- Site picker functionality (`Show-SitePicker`) spawns background runspace but depends on form being instantiated
|
||||
|
||||
**Hard Dependencies:**
|
||||
- PnP.PowerShell module imported dynamically in background runspace blocks
|
||||
- File system access (profiles, templates, settings) not abstracted
|
||||
- SharePoint connection state implicit in PnP connection context
|
||||
|
||||
**Areas with Better Isolation:**
|
||||
- Pure utility functions like `Format-Bytes`, `EscHtml` could be unit tested
|
||||
- Data transformation functions like `Merge-PermissionRows` accept input arrays and return structured output
|
||||
- HTML generation in `Export-PermissionsToHTML` and `Export-StorageToHTML` could be tested against expected markup
|
||||
|
||||
## Background Runspace Pattern
|
||||
|
||||
**Async Execution Model:**
|
||||
Most long-running operations execute in separate PowerShell runspace to prevent UI blocking:
|
||||
|
||||
```powershell
|
||||
# 1. Create synchronized hashtable for communication
|
||||
$sync = [hashtable]::Synchronized(@{
|
||||
Done = $false
|
||||
Error = $null
|
||||
Result = $null
|
||||
Queue = [System.Collections.Generic.Queue[object]]::new()
|
||||
})
|
||||
|
||||
# 2. Define background script block (has access to passed parameters only)
|
||||
$bgScript = {
|
||||
param($Url, $ClientId, $Sync)
|
||||
try {
|
||||
Import-Module PnP.PowerShell -ErrorAction Stop
|
||||
Connect-PnPOnline -Url $Url -Interactive -ClientId $ClientId
|
||||
# Perform work
|
||||
$Sync.Result = $data
|
||||
} catch {
|
||||
$Sync.Error = $_.Exception.Message
|
||||
} finally {
|
||||
$Sync.Done = $true
|
||||
}
|
||||
}
|
||||
|
||||
# 3. Launch in runspace
|
||||
$rs = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
|
||||
$rs.ApartmentState = "STA"; $rs.ThreadOptions = "ReuseThread"; $rs.Open()
|
||||
$ps = [System.Management.Automation.PowerShell]::Create()
|
||||
$ps.Runspace = $rs
|
||||
[void]$ps.AddScript($bgScript)
|
||||
[void]$ps.AddArgument($url)
|
||||
$hnd = $ps.BeginInvoke()
|
||||
|
||||
# 4. Poll completion with timer
|
||||
$tmr = New-Object System.Windows.Forms.Timer
|
||||
$tmr.Interval = 300
|
||||
$tmr.Add_Tick({
|
||||
if ($sync.Done) {
|
||||
[void]$ps.EndInvoke($hnd)
|
||||
$rs.Close(); $rs.Dispose()
|
||||
# Update UI with $sync.Result
|
||||
}
|
||||
})
|
||||
$tmr.Start()
|
||||
```
|
||||
|
||||
**Used for:**
|
||||
- Site picker loading: `Show-SitePicker` (lines 212-471)
|
||||
- Template capture: Background job in template manager (lines 900+)
|
||||
- Site creation: Background job in bulk creation (lines 1134+)
|
||||
- Permission/storage export: Operations triggered from event handlers
|
||||
- File search: Background search execution
|
||||
|
||||
## Message Queue Pattern
|
||||
|
||||
**For logging from background runspaces:**
|
||||
|
||||
```powershell
|
||||
# Background function enqueues messages
|
||||
function BgLog([string]$m, [string]$c="LightGreen") {
|
||||
$Sync.Queue.Enqueue([PSCustomObject]@{ Text=$m; Color=$c })
|
||||
}
|
||||
|
||||
# Main thread timer dequeues and displays
|
||||
$tmr.Add_Tick({
|
||||
while ($sync.Queue.Count -gt 0) {
|
||||
$msg = $sync.Queue.Dequeue()
|
||||
_Tpl-Log -Box $textBox -Msg $msg.Text -Color $msg.Color
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Rationale:** Avoids cross-thread UI access violations by queueing messages from worker thread.
|
||||
|
||||
## Common Testing Patterns in Code
|
||||
|
||||
**Null/Existence Checks:**
|
||||
```powershell
|
||||
# Before using objects
|
||||
if ($script:LogBox -and !$script:LogBox.IsDisposed) { ... }
|
||||
if ($data -and $data.Count -gt 0) { ... }
|
||||
if ([string]::IsNullOrWhiteSpace($value)) { return $false }
|
||||
```
|
||||
|
||||
**Error Logging in Loops:**
|
||||
```powershell
|
||||
# Catch errors in data processing, log, continue
|
||||
foreach ($item in $items) {
|
||||
try {
|
||||
# Process item
|
||||
} catch {
|
||||
BgLog " Skipped: $($_.Exception.Message)" "DarkGray"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Validation Before Operations:**
|
||||
```powershell
|
||||
function Validate-Inputs {
|
||||
if ([string]::IsNullOrWhiteSpace($script:txtClientId.Text)) {
|
||||
[System.Windows.Forms.MessageBox]::Show(...)
|
||||
return $false
|
||||
}
|
||||
return $true
|
||||
}
|
||||
|
||||
# Called before starting long operations
|
||||
if (-not (Validate-Inputs)) { return }
|
||||
```
|
||||
|
||||
## Data Flow Testing Approach
|
||||
|
||||
**For Feature Development:**
|
||||
|
||||
1. **Manual Test Cases** (observed pattern, not formalized):
|
||||
- Permissions Export:
|
||||
- Select site with multiple libraries
|
||||
- Choose CSV format
|
||||
- Verify CSV contains all libraries and permissions
|
||||
- Test HTML format in browser for interactivity
|
||||
|
||||
- Storage Metrics:
|
||||
- Run with `PerLibrary` flag
|
||||
- Verify folder hierarchy is captured
|
||||
- Test recursive subsite inclusion
|
||||
- Validate byte calculations
|
||||
|
||||
- Template Capture/Apply:
|
||||
- Capture from source site
|
||||
- Verify JSON structure
|
||||
- Create new site from template
|
||||
- Verify structure, permissions, settings applied
|
||||
|
||||
- File Search:
|
||||
- Test regex patterns
|
||||
- Verify date filtering
|
||||
- Test large result sets (pagination)
|
||||
- Check CSV/HTML output
|
||||
|
||||
2. **Visual Verification:**
|
||||
- Log output reviewed in RichTextBox for progress
|
||||
- Generated HTML reports tested in multiple browsers
|
||||
- CSV files opened in Excel for format verification
|
||||
|
||||
## Fragility Points & Testing Considerations
|
||||
|
||||
**PnP Connection Management:**
|
||||
- No connection pooling; each operation may create new connection
|
||||
- Interactive auth prompt appears per runspace
|
||||
- **Risk:** Auth failures not consistently handled
|
||||
- **Testing Need:** Mock PnP module or use test tenant
|
||||
|
||||
**HTML Generation:**
|
||||
- String concatenation for large HTML documents (lines 1475+)
|
||||
- Inline CSS for styling
|
||||
- JavaScript for interactivity
|
||||
- **Risk:** Complex HTML fragments prone to markup errors
|
||||
- **Testing Need:** Validate HTML structure and JavaScript functionality
|
||||
|
||||
**JSON Persistence:**
|
||||
- Profiles, templates, settings stored in JSON
|
||||
- ConvertTo-Json/-From-Json without depth specification can truncate
|
||||
- **Risk:** Nested objects may not round-trip correctly
|
||||
- **Testing Need:** Validate all object types persist/restore
|
||||
|
||||
**Background Runspace Cleanup:**
|
||||
- Runspace and PowerShell objects must be disposed
|
||||
- Timer must be stopped and disposed
|
||||
- **Risk:** Resource leaks if exception occurs before cleanup
|
||||
- **Testing Need:** Verify cleanup in error paths
|
||||
|
||||
## Suggested Testing Improvements
|
||||
|
||||
**Unit Testing:**
|
||||
1. Extract pure functions (no UI/file system dependencies)
|
||||
- `Format-Bytes`, `EscHtml`, `Merge-PermissionRows`
|
||||
- HTML generation functions
|
||||
|
||||
2. Use Pester framework for PowerShell unit tests:
|
||||
```powershell
|
||||
Describe "Format-Bytes" {
|
||||
It "Formats bytes to GB" {
|
||||
Format-Bytes (1GB * 2) | Should -Be "2 GB"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Mock file system and PnP operations for integration tests
|
||||
|
||||
**Integration Testing:**
|
||||
1. Use test SharePoint tenant for functional testing
|
||||
2. Automate report generation and validation
|
||||
3. Script common user workflows
|
||||
|
||||
**Regression Testing:**
|
||||
1. Maintain test suite of sites with known structures
|
||||
2. Generate reports, compare outputs
|
||||
3. Run before major releases
|
||||
|
||||
---
|
||||
|
||||
*Testing analysis: 2026-04-02*
|
||||
Reference in New Issue
Block a user