diff --git a/docs/POWERSHELL-THREADING-RULES.md b/docs/POWERSHELL-THREADING-RULES.md new file mode 100644 index 0000000..c4bdd9e --- /dev/null +++ b/docs/POWERSHELL-THREADING-RULES.md @@ -0,0 +1,175 @@ +# PowerShell Cmdlet Threading Rules and Design Patterns + +## 🚨 CRITICAL THREADING RULE + +**PowerShell cmdlets can ONLY call Write-Progress, Write-Verbose, Write-Object, Write-Error from the main cmdlet thread.** + +**❌ NEVER call these methods from:** +- Background threads (`Task.Run`, `Task.Factory.StartNew`) +- Async callbacks (`async/await` continuations) +- Timer callbacks +- Event handlers from other threads +- Any thread other than the main cmdlet thread + +**✅ ONLY call these methods from:** +- `BeginProcessing()` override +- `ProcessRecord()` override +- `EndProcessing()` override +- Main cmdlet thread execution + +## 🔧 DESIGN PATTERN: ThreadSafeProgressCollector + +### Problem +When performing background operations (file uploads, downloads, processing), you need to report progress from background threads, but PowerShell doesn't allow direct calls to Write-Progress from those threads. + +### Solution +Use the **ThreadSafeProgressCollector** pattern: + +```csharp +// Background thread - QUEUE updates (thread-safe) +_progressCollector.QueueProgressUpdate(activityId, "Processing", "Status", percentage); +_progressCollector.QueueVerboseMessage("Processing file: {0}", fileName); + +// Main thread - PROCESS queued updates +_progressCollector.ProcessQueuedUpdates(); // Only call from main thread! +``` + +### Implementation Pattern + +```csharp +public class MyLongRunningCmdlet : PSCmdlet +{ + private ThreadSafeProgressCollector _progressCollector; + + protected override void BeginProcessing() + { + _progressCollector = new ThreadSafeProgressCollector(this); + } + + protected override void ProcessRecord() + { + // Start background work + var tasks = new List(); + + for (int i = 0; i < workItems.Count; i++) + { + var task = Task.Run(() => + { + // ✅ CORRECT: Queue from background thread + _progressCollector.QueueProgressUpdate(1, "Processing", $"Item {i}", progress); + _progressCollector.QueueVerboseMessage("Processing item {0}", i); + + // ❌ WRONG: Never call directly from background thread + // WriteProgress(...); // This will cause threading errors! + // WriteVerbose(...); // This will cause threading errors! + }); + tasks.Add(task); + } + + // ✅ CORRECT: Process updates from main thread with periodic updates + while (!Task.WaitAll(tasks.ToArray(), 1000)) // 1 second intervals + { + _progressCollector.ProcessQueuedUpdates(); // Safe on main thread + } + + // Final processing + _progressCollector.ProcessQueuedUpdates(); + } +} +``` + +## 🚨 COMMON MISTAKES TO AVOID + +### ❌ Mistake 1: Direct calls from background threads +```csharp +Task.Run(() => +{ + WriteProgress(...); // THREADING ERROR! + WriteVerbose(...); // THREADING ERROR! +}); +``` + +### ❌ Mistake 2: ProcessQueuedUpdates from background threads +```csharp +Task.Run(() => +{ + _progressCollector.QueueProgressUpdate(...); + _progressCollector.ProcessQueuedUpdates(); // THREADING ERROR! +}); +``` + +### ❌ Mistake 3: Async/await callbacks +```csharp +await SomeAsyncOperation().ContinueWith(task => +{ + WriteProgress(...); // THREADING ERROR! +}); +``` + +## ✅ CORRECT PATTERNS + +### Pattern 1: Periodic Processing +```csharp +var tasks = StartBackgroundTasks(); +while (!Task.WaitAll(tasks, 1000)) +{ + _progressCollector.ProcessQueuedUpdates(); // Every 1 second +} +_progressCollector.ProcessQueuedUpdates(); // Final update +``` + +### Pattern 2: Completion-based Processing +```csharp +var tasks = StartBackgroundTasks(); +Task.WaitAll(tasks); +_progressCollector.ProcessQueuedUpdates(); // After completion +``` + +### Pattern 3: Manual Processing Points +```csharp +foreach (var item in items) +{ + ProcessItemInBackground(item); // Queues updates + _progressCollector.ProcessQueuedUpdates(); // Process after each item +} +``` + +## 🎯 ERROR SYMPTOMS + +If you violate these rules, you'll see errors like: +- "The WriteObject and WriteError methods cannot be called from outside the overrides..." +- "WriteProgress can only be called from within the same thread" +- Cmdlet failures with threading exceptions +- Progress bars not updating or appearing + +## 📋 CHECKLIST FOR NEW CMDLETS + +Before implementing background operations: + +- [ ] Are you using ThreadSafeProgressCollector? +- [ ] Are all Write* calls from main thread only? +- [ ] Are you calling ProcessQueuedUpdates() only from main thread? +- [ ] Do you have periodic progress processing for long operations? +- [ ] Have you tested with verbose output enabled? +- [ ] Have you tested with progress bars enabled? + +## 🔧 DEBUGGING TIPS + +1. **Enable verbose logging** to see threading violations +2. **Test with progress bars** - they're most sensitive to threading issues +3. **Use Task.WaitAll with timeouts** for periodic processing +4. **Never call ProcessQueuedUpdates from callbacks** +5. **Queue everything from background threads, process from main thread** + +## 📚 RELATED PATTERNS + +- **File Upload/Download**: Use periodic processing during Task.WaitAll +- **Multipart Operations**: Queue from parallel tasks, process periodically +- **Long-running Operations**: Process updates every 1-2 seconds +- **Collection Processing**: Process after each item or batch + +## 🎯 REMEMBER + +**The golden rule: Background threads QUEUE, main thread PROCESSES.** + +This pattern ensures PowerShell cmdlet compliance while providing real-time progress updates to users. diff --git a/docs/PROJECT-STRUCTURE.md b/docs/PROJECT-STRUCTURE.md index f987002..2aa831d 100644 --- a/docs/PROJECT-STRUCTURE.md +++ b/docs/PROJECT-STRUCTURE.md @@ -42,6 +42,7 @@ PSMinIO/ │ ├── USAGE.md # Comprehensive usage guide │ ├── RELEASE-NOTES.md # Release notes │ ├── POWERSHELL-GALLERY-RELEASE.md # Gallery release guide +│ ├── POWERSHELL-THREADING-RULES.md # Critical PowerShell threading patterns │ └── PROJECT-STRUCTURE.md # This file │ ├── Artifacts/ # Build artifacts diff --git a/scripts/examples/01-Basic-Operations.ps1 b/docs/examples/01-Basic-Operations.ps1 similarity index 100% rename from scripts/examples/01-Basic-Operations.ps1 rename to docs/examples/01-Basic-Operations.ps1 diff --git a/scripts/examples/02-Advanced-Object-Listing.ps1 b/docs/examples/02-Advanced-Object-Listing.ps1 similarity index 100% rename from scripts/examples/02-Advanced-Object-Listing.ps1 rename to docs/examples/02-Advanced-Object-Listing.ps1 diff --git a/scripts/examples/03-Directory-Management.ps1 b/docs/examples/03-Directory-Management.ps1 similarity index 100% rename from scripts/examples/03-Directory-Management.ps1 rename to docs/examples/03-Directory-Management.ps1 diff --git a/scripts/examples/04-Chunked-Operations.ps1 b/docs/examples/04-Chunked-Operations.ps1 similarity index 100% rename from scripts/examples/04-Chunked-Operations.ps1 rename to docs/examples/04-Chunked-Operations.ps1 diff --git a/scripts/examples/05-Bulk-Operations.ps1 b/docs/examples/05-Bulk-Operations.ps1 similarity index 100% rename from scripts/examples/05-Bulk-Operations.ps1 rename to docs/examples/05-Bulk-Operations.ps1 diff --git a/scripts/examples/06-Enterprise-Automation.ps1 b/docs/examples/06-Enterprise-Automation.ps1 similarity index 100% rename from scripts/examples/06-Enterprise-Automation.ps1 rename to docs/examples/06-Enterprise-Automation.ps1 diff --git a/scripts/examples/README.md b/docs/examples/README.md similarity index 100% rename from scripts/examples/README.md rename to docs/examples/README.md diff --git a/scripts/Check-Parameters.ps1 b/scripts/Check-Parameters.ps1 deleted file mode 100644 index 8910cfa..0000000 --- a/scripts/Check-Parameters.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -# Check what parameters are available for New-MinIOObject -Import-Module ".\Module\PSMinIO\PSMinIO.psd1" -Force - -Write-Host "=== New-MinIOObject Parameter Information ===" -ForegroundColor Cyan - -# Get command info -$cmdInfo = Get-Command New-MinIOObject - -Write-Host "`nParameter Sets:" -ForegroundColor Yellow -foreach ($paramSet in $cmdInfo.ParameterSets) { - Write-Host " $($paramSet.Name):" -ForegroundColor Green - foreach ($param in $paramSet.Parameters) { - if ($param.Name -notin @('Verbose', 'Debug', 'ErrorAction', 'WarningAction', 'InformationAction', 'ErrorVariable', 'WarningVariable', 'InformationVariable', 'OutVariable', 'OutBuffer', 'PipelineVariable', 'WhatIf', 'Confirm')) { - $mandatory = if ($param.IsMandatory) { " (Mandatory)" } else { "" } - $aliases = if ($param.Aliases.Count -gt 0) { " [Aliases: $($param.Aliases -join ', ')]" } else { "" } - Write-Host " - $($param.Name)$mandatory$aliases" -ForegroundColor White - } - } - Write-Host "" -} - -Write-Host "All Parameters:" -ForegroundColor Yellow -foreach ($param in $cmdInfo.Parameters.Values) { - if ($param.Name -notin @('Verbose', 'Debug', 'ErrorAction', 'WarningAction', 'InformationAction', 'ErrorVariable', 'WarningVariable', 'InformationVariable', 'OutVariable', 'OutBuffer', 'PipelineVariable', 'WhatIf', 'Confirm')) { - $aliases = if ($param.Aliases.Count -gt 0) { " [Aliases: $($param.Aliases -join ', ')]" } else { "" } - $paramSets = $param.ParameterSets.Keys -join ', ' - Write-Host " $($param.Name)$aliases - Sets: $paramSets" -ForegroundColor White - } -} diff --git a/scripts/Test-50-Files-Upload.ps1 b/scripts/Test-50-Files-Upload.ps1 deleted file mode 100644 index 6abb460..0000000 --- a/scripts/Test-50-Files-Upload.ps1 +++ /dev/null @@ -1,156 +0,0 @@ -# Test script for uploading 50 test files to demonstrate enhanced upload functionality -# This tests FileInfo[] support, multi-layer progress tracking, and BucketDirectory features - -param( - [string]$TestBucketName = "psminiotest-50files-$(Get-Date -Format 'yyyyMMdd-HHmmss')", - [string]$TestDirectory = "TestFiles50", - [switch]$Cleanup, - [switch]$Verbose -) - -# Set verbose preference if requested -if ($Verbose) { - $VerbosePreference = 'Continue' -} - -Write-Host "=== PSMinIO 50-File Upload Test ===" -ForegroundColor Cyan -Write-Host "Testing enhanced upload functionality with multi-layer progress tracking" -ForegroundColor Green - -try { - # Import the module - Write-Host "`n1. Loading PSMinIO module..." -ForegroundColor Yellow - Import-Module ".\Module\PSMinIO\PSMinIO.psd1" -Force - - # Connect to MinIO - Write-Host "2. Connecting to MinIO..." -ForegroundColor Yellow - Connect-MinIO -Endpoint "https://api.s3.gracesolution.info" -AccessKey "T34Wg85SAwezUa3sk3m4" -SecretKey "PxEmnbQoQJTJsDocSEV6mSSscDpJMiJCayPv93xe" - Write-Host "✅ Connected successfully!" -ForegroundColor Green - - # Create test directory and files - Write-Host "`n3. Creating test files..." -ForegroundColor Yellow - if (Test-Path $TestDirectory) { - Remove-Item $TestDirectory -Recurse -Force - } - New-Item -ItemType Directory -Path $TestDirectory -Force | Out-Null - - # Create 50 test files with varying sizes and content - $testFiles = @() - for ($i = 1; $i -le 50; $i++) { - $fileName = "testfile-{0:D3}.txt" -f $i - $filePath = Join-Path $TestDirectory $fileName - - # Create files with different sizes (1KB to 100KB) - $fileSize = Get-Random -Minimum 1024 -Maximum 102400 - $content = "Test file $i`n" + ("X" * ($fileSize - 20)) - - Set-Content -Path $filePath -Value $content -NoNewline - $testFiles += Get-Item $filePath - } - - Write-Host "✅ Created 50 test files (total size: $((($testFiles | Measure-Object Length -Sum).Sum / 1MB).ToString('F2')) MB)" -ForegroundColor Green - - # Create test bucket - Write-Host "`n4. Creating test bucket: $TestBucketName" -ForegroundColor Yellow - New-MinIOBucket -BucketName $TestBucketName - Write-Host "✅ Bucket created successfully!" -ForegroundColor Green - - # Test 1: Upload all files using FileInfo[] parameter - Write-Host "`n5. Test 1: Uploading 50 files using FileInfo[] parameter..." -ForegroundColor Yellow - Write-Host " This will demonstrate multi-layer progress tracking:" -ForegroundColor Cyan - Write-Host " • Layer 1: Collection progress (overall files)" -ForegroundColor Cyan - Write-Host " • Layer 2: File progress (current file)" -ForegroundColor Cyan - Write-Host " • Layer 3: Transfer progress (bytes)" -ForegroundColor Cyan - - $uploadStart = Get-Date - $uploadResults = New-MinIOObject -BucketName $TestBucketName -Files $testFiles -BucketDirectory "batch-upload" -PassThru -Verbose - $uploadDuration = (Get-Date) - $uploadStart - - Write-Host "✅ Upload completed in $($uploadDuration.TotalSeconds.ToString('F2')) seconds!" -ForegroundColor Green - Write-Host " Average speed: $(($uploadResults | Measure-Object TotalSize -Sum).Sum / $uploadDuration.TotalSeconds / 1MB | ForEach-Object { $_.ToString('F2') }) MB/s" -ForegroundColor Green - Write-Host " Files uploaded: $($uploadResults.Count)" -ForegroundColor Green - - # Test 2: Upload using Directory parameter with BucketDirectory - Write-Host "`n6. Test 2: Uploading directory with BucketDirectory parameter..." -ForegroundColor Yellow - - $dirUploadStart = Get-Date - $dirUploadResults = New-MinIOObject -BucketName $TestBucketName -Directory (Get-Item $TestDirectory) -BucketDirectory "directory-upload/nested/structure" -PassThru -Verbose - $dirUploadDuration = (Get-Date) - $dirUploadStart - - Write-Host "✅ Directory upload completed in $($dirUploadDuration.TotalSeconds.ToString('F2')) seconds!" -ForegroundColor Green - Write-Host " Files uploaded: $($dirUploadResults.Count)" -ForegroundColor Green - - # Test 3: Verify uploads by listing objects - Write-Host "`n7. Verifying uploads..." -ForegroundColor Yellow - $allObjects = Get-MinIOObject -BucketName $TestBucketName - - $batchObjects = $allObjects | Where-Object { $_.Name -like "batch-upload/*" } - $dirObjects = $allObjects | Where-Object { $_.Name -like "directory-upload/*" } - - Write-Host "✅ Verification complete:" -ForegroundColor Green - Write-Host " Total objects in bucket: $($allObjects.Count)" -ForegroundColor Green - Write-Host " Batch upload objects: $($batchObjects.Count)" -ForegroundColor Green - Write-Host " Directory upload objects: $($dirObjects.Count)" -ForegroundColor Green - - # Test 4: Test with filters (create subdirectories first) - Write-Host "`n8. Test 3: Testing directory upload with filters..." -ForegroundColor Yellow - - # Create subdirectories with different file types - $subDir1 = Join-Path $TestDirectory "SubDir1" - $subDir2 = Join-Path $TestDirectory "SubDir2" - New-Item -ItemType Directory -Path $subDir1 -Force | Out-Null - New-Item -ItemType Directory -Path $subDir2 -Force | Out-Null - - # Create some .log and .json files - for ($i = 1; $i -le 5; $i++) { - Set-Content -Path (Join-Path $subDir1 "logfile$i.log") -Value "Log entry $i" - Set-Content -Path (Join-Path $subDir2 "config$i.json") -Value "{`"test`": $i}" - } - - # Upload only .log files using inclusion filter - $filterStart = Get-Date - $filterResults = New-MinIOObject -BucketName $TestBucketName -Directory (Get-Item $TestDirectory) -Recursive -InclusionFilter { $_.Extension -eq ".log" } -BucketDirectory "filtered-upload" -PassThru -Verbose - $filterDuration = (Get-Date) - $filterStart - - Write-Host "✅ Filtered upload completed in $($filterDuration.TotalSeconds.ToString('F2')) seconds!" -ForegroundColor Green - Write-Host " Log files uploaded: $($filterResults.Count)" -ForegroundColor Green - - # Summary - Write-Host "`n=== TEST SUMMARY ===" -ForegroundColor Cyan - Write-Host "✅ All tests completed successfully!" -ForegroundColor Green - Write-Host "Features tested:" -ForegroundColor Yellow - Write-Host " • FileInfo[] parameter with 50 files" -ForegroundColor White - Write-Host " • Multi-layer progress tracking (3 layers)" -ForegroundColor White - Write-Host " • BucketDirectory parameter for nested structures" -ForegroundColor White - Write-Host " • Directory parameter with recursive upload" -ForegroundColor White - Write-Host " • InclusionFilter for selective file upload" -ForegroundColor White - Write-Host " • PassThru parameter for upload results" -ForegroundColor White - Write-Host " • Thread-safe progress reporting" -ForegroundColor White - - Write-Host "`nPerformance metrics:" -ForegroundColor Yellow - Write-Host " • Batch upload: $($uploadDuration.TotalSeconds.ToString('F2'))s for $($uploadResults.Count) files" -ForegroundColor White - Write-Host " • Directory upload: $($dirUploadDuration.TotalSeconds.ToString('F2'))s for $($dirUploadResults.Count) files" -ForegroundColor White - Write-Host " • Filtered upload: $($filterDuration.TotalSeconds.ToString('F2'))s for $($filterResults.Count) files" -ForegroundColor White - - # Cleanup option - if ($Cleanup) { - Write-Host "`n9. Cleaning up..." -ForegroundColor Yellow - Remove-MinIOBucket -BucketName $TestBucketName -Force - Remove-Item $TestDirectory -Recurse -Force - Write-Host "✅ Cleanup completed!" -ForegroundColor Green - } else { - Write-Host "`nTest bucket '$TestBucketName' and files preserved for inspection." -ForegroundColor Cyan - Write-Host "Use -Cleanup parameter to automatically clean up test resources." -ForegroundColor Cyan - } - -} catch { - Write-Host "❌ Test failed: $($_.Exception.Message)" -ForegroundColor Red - Write-Host "Stack trace: $($_.ScriptStackTrace)" -ForegroundColor Red -} finally { - # Clean up test files if they exist - if (Test-Path $TestDirectory) { - Write-Host "`nCleaning up local test files..." -ForegroundColor Yellow - Remove-Item $TestDirectory -Recurse -Force -ErrorAction SilentlyContinue - } -} - -Write-Host "`n=== Test Complete ===" -ForegroundColor Cyan diff --git a/scripts/Test-Current-Upload.ps1 b/scripts/Test-Current-Upload.ps1 deleted file mode 100644 index f1599c5..0000000 --- a/scripts/Test-Current-Upload.ps1 +++ /dev/null @@ -1,117 +0,0 @@ -# Test script for current upload functionality (single file version) -param( - [string]$TestBucketName = "psminiotest-current-$(Get-Date -Format 'yyyyMMdd-HHmmss')", - [switch]$Cleanup, - [switch]$Verbose -) - -if ($Verbose) { - $VerbosePreference = 'Continue' -} - -Write-Host "=== PSMinIO Current Upload Test ===" -ForegroundColor Cyan -Write-Host "Testing current upload functionality" -ForegroundColor Green - -try { - # Import the module - Write-Host "`n1. Loading PSMinIO module..." -ForegroundColor Yellow - Import-Module ".\Module\PSMinIO\PSMinIO.psd1" -Force - - # Connect to MinIO - Write-Host "2. Connecting to MinIO..." -ForegroundColor Yellow - Connect-MinIO -Endpoint "https://api.s3.gracesolution.info" -AccessKey "T34Wg85SAwezUa3sk3m4" -SecretKey "PxEmnbQoQJTJsDocSEV6mSSscDpJMiJCayPv93xe" - Write-Host "✅ Connected successfully!" -ForegroundColor Green - - # Create test files - Write-Host "`n3. Creating test files..." -ForegroundColor Yellow - $testDir = "TestFilesCurrent" - if (Test-Path $testDir) { - Remove-Item $testDir -Recurse -Force - } - New-Item -ItemType Directory -Path $testDir -Force | Out-Null - - # Create 10 test files - $testFiles = @() - for ($i = 1; $i -le 10; $i++) { - $fileName = "testfile-{0:D3}.txt" -f $i - $filePath = Join-Path $testDir $fileName - $content = "Test file $i - $(Get-Date)`n" + ("Content line $i`n" * 10) - Set-Content -Path $filePath -Value $content - $testFiles += Get-Item $filePath - } - - Write-Host "✅ Created 10 test files" -ForegroundColor Green - - # Create test bucket - Write-Host "`n4. Creating test bucket: $TestBucketName" -ForegroundColor Yellow - New-MinIOBucket -BucketName $TestBucketName - Write-Host "✅ Bucket created successfully!" -ForegroundColor Green - - # Test uploading files one by one (current functionality) - Write-Host "`n5. Uploading files individually..." -ForegroundColor Yellow - $uploadResults = @() - $uploadStart = Get-Date - - foreach ($file in $testFiles) { - Write-Host " Uploading: $($file.Name)" -ForegroundColor Cyan - $result = New-MinIOObject -BucketName $TestBucketName -File $file -BucketDirectory "individual-uploads" -PassThru -Verbose - $uploadResults += $result - } - - $uploadDuration = (Get-Date) - $uploadStart - Write-Host "✅ Upload completed in $($uploadDuration.TotalSeconds.ToString('F2')) seconds!" -ForegroundColor Green - Write-Host " Files uploaded: $($uploadResults.Count)" -ForegroundColor Green - - # Verify uploads - Write-Host "`n6. Verifying uploads..." -ForegroundColor Yellow - $objects = Get-MinIOObject -BucketName $TestBucketName - Write-Host "✅ Found $($objects.Count) objects in bucket" -ForegroundColor Green - - # Test with different bucket directories - Write-Host "`n7. Testing nested bucket directories..." -ForegroundColor Yellow - $nestedResults = @() - foreach ($file in $testFiles[0..2]) { # Upload first 3 files to nested structure - $result = New-MinIOObject -BucketName $TestBucketName -File $file -BucketDirectory "level1/level2/level3" -PassThru -Verbose - $nestedResults += $result - } - Write-Host "✅ Nested directory upload completed: $($nestedResults.Count) files" -ForegroundColor Green - - # Final verification - Write-Host "`n8. Final verification..." -ForegroundColor Yellow - $allObjects = Get-MinIOObject -BucketName $TestBucketName - $individualObjects = $allObjects | Where-Object { $_.Name -like "individual-uploads/*" } - $nestedObjects = $allObjects | Where-Object { $_.Name -like "level1/level2/level3/*" } - - Write-Host "✅ Final verification complete:" -ForegroundColor Green - Write-Host " Total objects: $($allObjects.Count)" -ForegroundColor Green - Write-Host " Individual uploads: $($individualObjects.Count)" -ForegroundColor Green - Write-Host " Nested uploads: $($nestedObjects.Count)" -ForegroundColor Green - - # Summary - Write-Host "`n=== TEST SUMMARY ===" -ForegroundColor Cyan - Write-Host "✅ Current functionality test completed!" -ForegroundColor Green - Write-Host "Features tested:" -ForegroundColor Yellow - Write-Host " • Single file upload with File parameter" -ForegroundColor White - Write-Host " • BucketDirectory parameter for nested structures" -ForegroundColor White - Write-Host " • PassThru parameter for upload results" -ForegroundColor White - Write-Host " • Multiple individual uploads" -ForegroundColor White - Write-Host " • Nested directory structure creation" -ForegroundColor White - - Write-Host "`nNote: Enhanced features (FileInfo[], Directory, Multi-layer progress) require updated DLL" -ForegroundColor Yellow - - if ($Cleanup) { - Write-Host "`n9. Cleaning up..." -ForegroundColor Yellow - Remove-MinIOBucket -BucketName $TestBucketName -Force - Write-Host "✅ Cleanup completed!" -ForegroundColor Green - } - -} catch { - Write-Host "❌ Test failed: $($_.Exception.Message)" -ForegroundColor Red - Write-Host "Stack trace: $($_.ScriptStackTrace)" -ForegroundColor Red -} finally { - if (Test-Path $testDir) { - Remove-Item $testDir -Recurse -Force -ErrorAction SilentlyContinue - } -} - -Write-Host "`n=== Test Complete ===" -ForegroundColor Cyan diff --git a/scripts/Test-Final-Implementation.ps1 b/scripts/Test-Final-Implementation.ps1 deleted file mode 100644 index 3cbe7be..0000000 --- a/scripts/Test-Final-Implementation.ps1 +++ /dev/null @@ -1,176 +0,0 @@ -#!/usr/bin/env pwsh -<# -.SYNOPSIS - Final comprehensive test of the new PSMinIO implementation - -.DESCRIPTION - Tests all core functionality of the rebuilt PSMinIO module: - - Connection management - - Bucket operations - - Object upload/download with progress - - Performance metrics - - Error handling -#> - -[CmdletBinding()] -param() - -# Import the module -Write-Output "=== PSMinIO Final Implementation Test ===" -Write-Output "Importing PSMinIO module..." -Import-Module "$PSScriptRoot\..\Module\PSMinIO\PSMinIO.psd1" -Force - -try { - # Test 1: Connection - Write-Output "`n1. Testing Connection..." - Connect-MinIO -Endpoint "https://api.s3.gracesolution.info" -AccessKey "T34Wg85SAwezUa3sk3m4" -SecretKey "PxEmnbQoQJTJsDocSEV6mSSscDpJMiJCayPv93xe" -TestConnection -Verbose - - # Get the connection from session variable to verify - $connection = Get-Variable -Name "MinIOConnection" -ValueOnly -ErrorAction SilentlyContinue - if ($connection -and $connection.Status -eq 'Connected') { - Write-Output "✅ Connection successful!" - Write-Output " Status: $($connection.Status)" - Write-Output " Endpoint: $($connection.Configuration.Endpoint)" - } else { - throw "Connection failed" - } - - # Test 2: List Buckets - Write-Output "`n2. Testing Bucket Listing..." - $buckets = Get-MinIOBucket -Verbose - - if ($buckets) { - Write-Output "✅ Found $($buckets.Count) buckets" - $buckets | Select-Object Name, CreationDate | Format-Table -AutoSize - } else { - Write-Output "⚠️ No buckets found" - } - - # Test 3: Create Test Bucket - Write-Output "`n3. Testing Bucket Creation..." - $testBucket = "psminiotest-$(Get-Date -Format 'yyyyMMdd-HHmmss')" - $bucketResult = New-MinIOBucket -BucketName $testBucket -PassThru -Verbose - - if ($bucketResult) { - Write-Output "✅ Bucket created: $($bucketResult.Name)" - } else { - throw "Bucket creation failed" - } - - # Test 4: Test Bucket Exists - Write-Output "`n4. Testing Bucket Existence Check..." - $exists = Test-MinIOBucketExists -BucketName $testBucket -Verbose - - if ($exists) { - Write-Output "✅ Bucket existence confirmed: $exists" - } else { - throw "Bucket existence check failed" - } - - # Test 5: Create and Upload Test File - Write-Output "`n5. Testing File Upload with Progress..." - $testFile = Join-Path $env:TEMP "psminiotest-$(Get-Date -Format 'yyyyMMddHHmmss').txt" - $testContent = @" -PSMinIO Final Test File -====================== -Created: $(Get-Date) -Implementation: Custom REST API -Features: Real progress reporting, performance metrics -Test data: $('A' * 2000) -"@ - - $testContent | Out-File -FilePath $testFile -Encoding UTF8 - $fileInfo = Get-Item $testFile - Write-Output " Test file created: $($fileInfo.Length) bytes" - - $uploadResult = New-MinIOObject -BucketName $testBucket -File $fileInfo -PassThru -Verbose - - if ($uploadResult -and $uploadResult.Success) { - Write-Output "✅ Upload successful!" - Write-Output " Object: $($uploadResult.ObjectName)" - Write-Output " Size: $($uploadResult.TotalSizeFormatted)" - Write-Output " Duration: $($uploadResult.DurationFormatted)" - Write-Output " Speed: $($uploadResult.AverageSpeedFormatted)" - } else { - throw "File upload failed" - } - - # Test 6: List Objects - Write-Output "`n6. Testing Object Listing..." - $objects = Get-MinIOObject -BucketName $testBucket -Verbose - - if ($objects -and $objects.Count -gt 0) { - Write-Output "✅ Found $($objects.Count) objects" - $objects | Select-Object Name, Size, LastModified | Format-Table -AutoSize - } else { - throw "Object listing failed" - } - - # Test 7: Download File with Progress - Write-Output "`n7. Testing File Download with Progress..." - $downloadFile = Join-Path $env:TEMP "psminiotest-download-$(Get-Date -Format 'yyyyMMddHHmmss').txt" - $downloadResult = Get-MinIOObjectContent -BucketName $testBucket -ObjectName $fileInfo.Name -LocalPath $downloadFile -PassThru -Verbose - - if ($downloadResult -and $downloadResult.Success) { - Write-Output "✅ Download successful!" - Write-Output " Object: $($downloadResult.ObjectName)" - Write-Output " Size: $($downloadResult.TotalSizeFormatted)" - Write-Output " Duration: $($downloadResult.DurationFormatted)" - Write-Output " Speed: $($downloadResult.AverageSpeedFormatted)" - } else { - throw "File download failed" - } - - # Test 8: Verify Content - Write-Output "`n8. Testing Content Verification..." - $originalContent = Get-Content $testFile -Raw -ErrorAction SilentlyContinue - $downloadedContent = Get-Content $downloadFile -Raw -ErrorAction SilentlyContinue - - if ($originalContent -and $downloadedContent -and ($originalContent -eq $downloadedContent)) { - Write-Output "✅ File content verification PASSED!" - } else { - throw "File content verification FAILED!" - } - - # Test Summary - Write-Output "`n=== Test Summary ===" - Write-Output "✅ All tests PASSED!" - Write-Output "" - Write-Output "New PSMinIO Implementation Features Verified:" - Write-Output " ✓ Custom REST API client (no MinIO SDK dependency)" - Write-Output " ✓ Real progress reporting during transfers" - Write-Output " ✓ Performance metrics (duration, speed)" - Write-Output " ✓ Synchronous operations optimized for PowerShell" - Write-Output " ✓ Enhanced error handling and logging" - Write-Output " ✓ AWS S3 signature v4 authentication" - Write-Output " ✓ Comprehensive cmdlet functionality" - Write-Output "" - Write-Output "Architecture Benefits:" - Write-Output " • No async/await compatibility issues" - Write-Output " • Reduced dependencies (removed 8+ DLLs)" - Write-Output " • True progress from HTTP streams" - Write-Output " • Built-in performance monitoring" - Write-Output " • PowerShell-native design patterns" - -} catch { - Write-Output "❌ Test failed: $($_.Exception.Message)" - Write-Output "Error details: $($_.Exception.ToString())" - exit 1 -} finally { - # Cleanup - Write-Output "`n=== Cleanup ===" - - # Remove test files - if (Test-Path $testFile -ErrorAction SilentlyContinue) { - Remove-Item $testFile -Force -ErrorAction SilentlyContinue - Write-Output "Removed test file: $testFile" - } - - if (Test-Path $downloadFile -ErrorAction SilentlyContinue) { - Remove-Item $downloadFile -Force -ErrorAction SilentlyContinue - Write-Output "Removed download file: $downloadFile" - } - - Write-Output "Note: Test bucket '$testBucket' left for manual cleanup" - Write-Output "Cleanup completed." -} diff --git a/scripts/Test-NewImplementation.ps1 b/scripts/Test-NewImplementation.ps1 deleted file mode 100644 index 40a3d19..0000000 --- a/scripts/Test-NewImplementation.ps1 +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/env pwsh -<# -.SYNOPSIS - Test script for the new PSMinIO implementation with custom REST API - -.DESCRIPTION - This script demonstrates the new PSMinIO module capabilities including: - - Connection management with custom REST API - - Real progress reporting during transfers - - Performance metrics in result objects - - Enhanced error handling and logging - -.PARAMETER Endpoint - MinIO server endpoint (e.g., https://minio.example.com:9000) - -.PARAMETER AccessKey - MinIO access key - -.PARAMETER SecretKey - MinIO secret key - -.PARAMETER TestBucket - Name of test bucket to create (default: psminiotest) - -.EXAMPLE - .\Test-NewImplementation.ps1 -Endpoint "https://play.min.io" -AccessKey "Q3AM3UQ867SPQQA43P2F" -SecretKey "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG" -#> - -[CmdletBinding()] -param( - [Parameter(Mandatory = $true)] - [string]$Endpoint, - - [Parameter(Mandatory = $true)] - [string]$AccessKey, - - [Parameter(Mandatory = $true)] - [string]$SecretKey, - - [Parameter()] - [string]$TestBucket = "psminiotest-$(Get-Date -Format 'yyyyMMdd-HHmmss')" -) - -# Import the module -Write-Host "Importing PSMinIO module..." -ForegroundColor Green -Import-Module "$PSScriptRoot\..\Module\PSMinIO\PSMinIO.psd1" -Force - -try { - # Test 1: Connection - Write-Host "`n=== Test 1: Connection ===" -ForegroundColor Yellow - Write-Host "Connecting to MinIO server: $Endpoint" -ForegroundColor Cyan - - $connection = Connect-MinIO -Endpoint $Endpoint -AccessKey $AccessKey -SecretKey $SecretKey -TestConnection -PassThru -Verbose - Write-Host "✅ Connection successful!" -ForegroundColor Green - Write-Host "Connection Status: $($connection.Status)" -ForegroundColor White - - # Test 2: List Buckets - Write-Host "`n=== Test 2: List Buckets ===" -ForegroundColor Yellow - Write-Host "Listing existing buckets..." -ForegroundColor Cyan - - $buckets = Get-MinIOBucket -Verbose - Write-Host "✅ Found $($buckets.Count) buckets" -ForegroundColor Green - - if ($buckets.Count -gt 0) { - Write-Host "First few buckets:" -ForegroundColor White - $buckets | Select-Object -First 3 | Format-Table Name, CreationDate, @{Name="Age"; Expression={(Get-Date) - $_.CreationDate}} - } - - # Test 3: Create Test Bucket - Write-Host "`n=== Test 3: Create Test Bucket ===" -ForegroundColor Yellow - Write-Host "Creating test bucket: $TestBucket" -ForegroundColor Cyan - - $bucketResult = New-MinIOBucket -Name $TestBucket -PassThru -Verbose - Write-Host "✅ Bucket created successfully!" -ForegroundColor Green - Write-Host "Bucket: $($bucketResult.Name)" -ForegroundColor White - - # Test 4: Test Bucket Exists - Write-Host "`n=== Test 4: Test Bucket Exists ===" -ForegroundColor Yellow - Write-Host "Testing if bucket exists..." -ForegroundColor Cyan - - $exists = Test-MinIOBucketExists -Name $TestBucket -Verbose - Write-Host "✅ Bucket exists: $exists" -ForegroundColor Green - - # Test 5: Create Test File - Write-Host "`n=== Test 5: Create and Upload Test File ===" -ForegroundColor Yellow - $testFile = "$env:TEMP\psminiotest-$(Get-Date -Format 'yyyyMMddHHmmss').txt" - $testContent = @" -PSMinIO Test File -================ -Created: $(Get-Date) -Endpoint: $Endpoint -Bucket: $TestBucket - -This is a test file created by the PSMinIO test script. -The new implementation uses a custom REST API client for better PowerShell compatibility. - -Features demonstrated: -- Real progress reporting -- Performance metrics -- Enhanced error handling -- Synchronous operations optimized for PowerShell - -Test data: $('A' * 100) -"@ - - Write-Host "Creating test file: $testFile" -ForegroundColor Cyan - $testContent | Out-File -FilePath $testFile -Encoding UTF8 - $fileInfo = Get-Item $testFile - Write-Host "✅ Test file created: $($fileInfo.Length) bytes" -ForegroundColor Green - - # Test 6: Upload File - Write-Host "`n=== Test 6: Upload File with Progress ===" -ForegroundColor Yellow - Write-Host "Uploading test file..." -ForegroundColor Cyan - - $uploadResult = New-MinIOObject -BucketName $TestBucket -File $fileInfo -PassThru -Verbose - Write-Host "✅ Upload completed!" -ForegroundColor Green - Write-Host "Upload Result:" -ForegroundColor White - $uploadResult | Format-List ObjectName, TotalSizeFormatted, DurationFormatted, AverageSpeedFormatted, Success - - # Test 7: List Objects - Write-Host "`n=== Test 7: List Objects ===" -ForegroundColor Yellow - Write-Host "Listing objects in test bucket..." -ForegroundColor Cyan - - $objects = Get-MinIOObject -BucketName $TestBucket -Verbose - Write-Host "✅ Found $($objects.Count) objects" -ForegroundColor Green - - if ($objects.Count -gt 0) { - Write-Host "Objects in bucket:" -ForegroundColor White - $objects | Format-Table Name, @{Name="Size"; Expression={$_.Size}}, LastModified - } - - # Test 8: Download File - Write-Host "`n=== Test 8: Download File with Progress ===" -ForegroundColor Yellow - $downloadFile = "$env:TEMP\psminiotest-download-$(Get-Date -Format 'yyyyMMddHHmmss').txt" - Write-Host "Downloading file to: $downloadFile" -ForegroundColor Cyan - - $downloadResult = Get-MinIOObjectContent -BucketName $TestBucket -ObjectName $fileInfo.Name -LocalPath $downloadFile -PassThru -Verbose - Write-Host "✅ Download completed!" -ForegroundColor Green - Write-Host "Download Result:" -ForegroundColor White - $downloadResult | Format-List ObjectName, TotalSizeFormatted, DurationFormatted, AverageSpeedFormatted, Success - - # Verify download - $downloadedContent = Get-Content $downloadFile -Raw - $originalContent = Get-Content $testFile -Raw - if ($downloadedContent -eq $originalContent) { - Write-Host "✅ File content verification passed!" -ForegroundColor Green - } else { - Write-Host "❌ File content verification failed!" -ForegroundColor Red - } - - Write-Host "`n=== Test Summary ===" -ForegroundColor Yellow - Write-Host "✅ All tests completed successfully!" -ForegroundColor Green - Write-Host "New PSMinIO implementation is working correctly with:" -ForegroundColor White - Write-Host " - Custom REST API client" -ForegroundColor White - Write-Host " - Real progress reporting" -ForegroundColor White - Write-Host " - Performance metrics" -ForegroundColor White - Write-Host " - Enhanced error handling" -ForegroundColor White - Write-Host " - PowerShell-optimized synchronous operations" -ForegroundColor White - -} catch { - Write-Host "❌ Test failed: $($_.Exception.Message)" -ForegroundColor Red - Write-Host "Stack trace:" -ForegroundColor Red - Write-Host $_.ScriptStackTrace -ForegroundColor Red -} finally { - # Cleanup - Write-Host "`n=== Cleanup ===" -ForegroundColor Yellow - - # Remove test files - if (Test-Path $testFile) { - Remove-Item $testFile -Force - Write-Host "Removed test file: $testFile" -ForegroundColor Gray - } - - if (Test-Path $downloadFile) { - Remove-Item $downloadFile -Force - Write-Host "Removed download file: $downloadFile" -ForegroundColor Gray - } - - # Note: We're not removing the test bucket to avoid issues with the test environment - Write-Host "Note: Test bucket '$TestBucket' was left for manual cleanup" -ForegroundColor Gray - - Write-Host "Cleanup completed." -ForegroundColor Gray -} diff --git a/scripts/Test-PSMinIOComprehensive.ps1 b/scripts/Test-PSMinIOComprehensive.ps1 deleted file mode 100644 index 0936146..0000000 --- a/scripts/Test-PSMinIOComprehensive.ps1 +++ /dev/null @@ -1,186 +0,0 @@ -# PSMinIO Comprehensive Test Script -# Tests all major functionality against Grace Solution S3 instance - -param( - [string]$TestDirectory = "C:\Temp\PSMinIOTest", - [string]$TestBucket = "psminiotest-$(Get-Date -Format 'yyyyMMdd-HHmmss')" -) - -# Test configuration -$S3Url = "https://api.s3.gracesolution.info" -$AccessKey = "T34Wg85SAwezUa3sk3m4" -$SecretKey = "PxEmnbQoQJTJsDocSEV6mSSscDpJMiJCayPv93xe" - -Write-Verbose "=== PSMinIO Comprehensive Test Suite ===" -Write-Verbose "S3 Endpoint: $S3Url" -Write-Verbose "Test Bucket: $TestBucket" -Write-Verbose "Test Directory: $TestDirectory" - -# Create test directory and files -Write-Verbose "Setting up test environment..." -if (Test-Path $TestDirectory) { - Remove-Item $TestDirectory -Recurse -Force -} -New-Item -ItemType Directory -Path $TestDirectory -Force | Out-Null - -# Create test files of various sizes -$SmallFile = Join-Path $TestDirectory "small-test.txt" -$MediumFile = Join-Path $TestDirectory "medium-test.txt" -$LargeFile = Join-Path $TestDirectory "large-test.txt" - -"This is a small test file for PSMinIO testing." | Out-File $SmallFile -Encoding UTF8 -1..1000 | ForEach-Object { "Line $_ - Medium test file content for PSMinIO testing with more data." } | Out-File $MediumFile -Encoding UTF8 -1..10000 | ForEach-Object { "Line $_ - Large test file content for PSMinIO multipart testing with substantial data to trigger chunked operations." } | Out-File $LargeFile -Encoding UTF8 - -$testFiles = Get-ChildItem $TestDirectory -Write-Verbose "Created test files: $($testFiles.Count) files, Total size: $([math]::Round(($testFiles | Measure-Object Length -Sum).Sum / 1KB, 2)) KB" - -$testResults = @() - -try { - # Test 1: Connection - Write-Verbose "Testing connection..." - $connection = Connect-MinIO -Url $S3Url -AccessKey $AccessKey -SecretKey $SecretKey -Verbose - $testResults += [PSCustomObject]@{ - Test = "Connection" - Status = "Success" - Details = "Connected to $S3Url" - Result = $connection - } - - # Test 2: List existing buckets - Write-Verbose "Testing bucket listing..." - $buckets = Get-MinIOBucket -Verbose - $testResults += [PSCustomObject]@{ - Test = "List Buckets" - Status = "Success" - Details = "Found $($buckets.Count) existing buckets" - Result = $buckets - } - - # Test 3: Create test bucket - Write-Verbose "Testing bucket creation..." - $newBucket = New-MinIOBucket -BucketName $TestBucket -Verbose - $testResults += [PSCustomObject]@{ - Test = "Create Bucket" - Status = "Success" - Details = "Created bucket: $($newBucket.Name)" - Result = $newBucket - } - - # Test 4: Verify bucket exists - Write-Verbose "Testing bucket existence check..." - $bucketExists = Test-MinIOBucketExists -BucketName $TestBucket -Verbose - $testResults += [PSCustomObject]@{ - Test = "Bucket Exists" - Status = "Success" - Details = "Bucket exists: $bucketExists" - Result = $bucketExists - } - - # Test 5: Upload small file (single part) - Write-Verbose "Testing single file upload..." - $uploadResult = New-MinIOObject -BucketName $TestBucket -Files (Get-Item $SmallFile) -Verbose - $testResults += [PSCustomObject]@{ - Test = "Single File Upload" - Status = "Success" - Details = "Uploaded small file: $($uploadResult.Count) objects" - Result = $uploadResult - } - - # Test 6: Upload medium file (single part) - Write-Verbose "Testing medium file upload..." - $uploadResult2 = New-MinIOObject -BucketName $TestBucket -Files (Get-Item $MediumFile) -Verbose - $testResults += [PSCustomObject]@{ - Test = "Medium File Upload" - Status = "Success" - Details = "Uploaded medium file: $($uploadResult2.Count) objects" - Result = $uploadResult2 - } - - # Test 7: Upload large file (multipart) - Write-Verbose "Testing multipart upload..." - $multipartResult = New-MinIOObjectMultipart -BucketName $TestBucket -FilePath (Get-Item $LargeFile) -Verbose - $testResults += [PSCustomObject]@{ - Test = "Multipart Upload" - Status = "Success" - Details = "Multipart upload completed: $($multipartResult.Count) objects" - Result = $multipartResult - } - - # Test 8: List objects in bucket - Write-Verbose "Testing object listing..." - $objects = Get-MinIOObject -BucketName $TestBucket -Verbose - $testResults += [PSCustomObject]@{ - Test = "List Objects" - Status = "Success" - Details = "Found $($objects.Count) objects in bucket" - Result = $objects - } - - # Test 9: Download small file - Write-Verbose "Testing single file download..." - $downloadPath = Join-Path $TestDirectory "downloaded-small.txt" - $downloadResult = Get-MinIOObjectContent -BucketName $TestBucket -ObjectName "small-test.txt" -LocalPath $downloadPath -Verbose - $testResults += [PSCustomObject]@{ - Test = "Single File Download" - Status = "Success" - Details = "Downloaded file: $($downloadResult.FilePath)" - Result = $downloadResult - } - - # Test 10: Download large file (multipart) - Write-Verbose "Testing multipart download..." - $downloadPath2 = Join-Path $TestDirectory "downloaded-large.txt" - $multipartDownload = Get-MinIOObjectContentMultipart -BucketName $TestBucket -ObjectName "large-test.txt" -DestinationPath (New-Object System.IO.FileInfo $downloadPath2) -Verbose - $testResults += [PSCustomObject]@{ - Test = "Multipart Download" - Status = "Success" - Details = "Multipart download completed: $($multipartDownload.FilePath)" - Result = $multipartDownload - } - - # Test 11: Generate presigned URL - Write-Verbose "Testing presigned URL generation..." - $presignedUrl = Get-MinIOPresignedUrl -BucketName $TestBucket -ObjectName "small-test.txt" -Expiration (New-TimeSpan -Hours 1) -Verbose - $testResults += [PSCustomObject]@{ - Test = "Presigned URL" - Status = "Success" - Details = "Generated presigned URL" - Result = $presignedUrl - } - -} catch { - $testResults += [PSCustomObject]@{ - Test = "ERROR" - Status = "Failed" - Details = $_.Exception.Message - Result = $_.ScriptStackTrace - } -} finally { - # Cleanup - Write-Verbose "Cleaning up test resources..." - - try { - # Remove local test directory - if (Test-Path $TestDirectory) { - Remove-Item $TestDirectory -Recurse -Force - Write-Verbose "Removed local test directory" - } - } catch { - Write-Warning "Cleanup warning: $($_.Exception.Message)" - } -} - -# Output test results -Write-Output "PSMinIO Comprehensive Test Results:" -Write-Output "==================================" -$testResults | Format-Table Test, Status, Details -AutoSize -Write-Output "" -Write-Output "Test bucket '$TestBucket' left for manual cleanup" -Write-Output "Total tests: $($testResults.Count)" -Write-Output "Successful: $(($testResults | Where-Object Status -eq 'Success').Count)" -Write-Output "Failed: $(($testResults | Where-Object Status -eq 'Failed').Count)" - -# Return results for further processing -return $testResults diff --git a/scripts/Test-PSMinIOIndividual.ps1 b/scripts/Test-PSMinIOIndividual.ps1 deleted file mode 100644 index 8d3a48b..0000000 --- a/scripts/Test-PSMinIOIndividual.ps1 +++ /dev/null @@ -1,123 +0,0 @@ -# PSMinIO Individual Test Commands -# Copy and paste these commands one by one to test functionality - -# Test Configuration -$S3Url = "https://api.s3.gracesolution.info" -$AccessKey = "T34Wg85SAwezUa3sk3m4" -$SecretKey = "PxEmnbQoQJTJsDocSEV6mSSscDpJMiJCayPv93xe" -$TestBucket = "psminiotest-$(Get-Date -Format 'yyyyMMdd-HHmmss')" - -Write-Output "Test Configuration:" -Write-Output "S3 Endpoint: $S3Url" -Write-Output "Test Bucket: $TestBucket" -Write-Output "" - -# ===== STEP 1: IMPORT MODULE ===== -Write-Output "1. Import PSMinIO Module:" -Import-Module "..\Module\PSMinIO\PSMinIO.psd1" -Force -Verbose - -# ===== STEP 2: CONNECT TO S3 ===== -Write-Output "`n2. Connect to S3:" -$connection = Connect-MinIO -Url $S3Url -AccessKey $AccessKey -SecretKey $SecretKey -Verbose -$connection - -# ===== STEP 3: LIST EXISTING BUCKETS ===== -Write-Output "`n3. List existing buckets:" -$buckets = Get-MinIOBucket -Verbose -$buckets | Format-Table Name, CreationDate, Region -AutoSize - -# ===== STEP 4: CREATE TEST BUCKET ===== -Write-Output "`n4. Create test bucket:" -$newBucket = New-MinIOBucket -BucketName $TestBucket -Verbose -$newBucket - -# ===== STEP 5: VERIFY BUCKET EXISTS ===== -Write-Output "`n5. Verify bucket exists:" -$bucketExists = Test-MinIOBucketExists -BucketName $TestBucket -Verbose -Write-Output "Bucket exists: $bucketExists" - -# ===== STEP 6: CREATE TEST FILES ===== -Write-Output "`n6. Create test files:" -$TestDirectory = "C:\Temp\PSMinIOTest" -if (Test-Path $TestDirectory) { Remove-Item $TestDirectory -Recurse -Force } -New-Item -ItemType Directory -Path $TestDirectory -Force | Out-Null - -# Small file (< 1KB) -$SmallFile = Join-Path $TestDirectory "small-test.txt" -"This is a small test file for PSMinIO testing." | Out-File $SmallFile -Encoding UTF8 - -# Medium file (~50KB) -$MediumFile = Join-Path $TestDirectory "medium-test.txt" -1..1000 | ForEach-Object { "Line $_ - Medium test file content for PSMinIO testing with more data." } | Out-File $MediumFile -Encoding UTF8 - -# Large file (~500KB) -$LargeFile = Join-Path $TestDirectory "large-test.txt" -1..10000 | ForEach-Object { "Line $_ - Large test file content for PSMinIO multipart testing with substantial data to trigger chunked operations." } | Out-File $LargeFile -Encoding UTF8 - -Get-ChildItem $TestDirectory | Select-Object Name, @{Name="Size(KB)";Expression={[math]::Round($_.Length/1KB,2)}} - -# ===== STEP 7: UPLOAD SMALL FILE (SINGLE PART) ===== -Write-Output "`n7. Upload small file (single part):" -$uploadResult1 = New-MinIOObject -BucketName $TestBucket -Files (Get-Item $SmallFile) -Verbose -$uploadResult1 - -# ===== STEP 8: UPLOAD MEDIUM FILE (SINGLE PART) ===== -Write-Output "`n8. Upload medium file (single part):" -$uploadResult2 = New-MinIOObject -BucketName $TestBucket -Files (Get-Item $MediumFile) -Verbose -$uploadResult2 - -# ===== STEP 9: UPLOAD LARGE FILE (MULTIPART) ===== -Write-Output "`n9. Upload large file (multipart):" -$multipartResult = New-MinIOObjectMultipart -BucketName $TestBucket -FilePath (Get-Item $LargeFile) -Verbose -$multipartResult - -# ===== STEP 10: LIST OBJECTS IN BUCKET ===== -Write-Output "`n10. List objects in bucket:" -$objects = Get-MinIOObject -BucketName $TestBucket -Verbose -$objects | Format-Table Key, @{Name="Size(KB)";Expression={[math]::Round($_.Size/1KB,2)}}, LastModified -AutoSize - -# ===== STEP 11: DOWNLOAD SMALL FILE ===== -Write-Output "`n11. Download small file:" -$downloadPath1 = Join-Path $TestDirectory "downloaded-small.txt" -$downloadResult1 = Get-MinIOObjectContent -BucketName $TestBucket -ObjectName "small-test.txt" -LocalPath $downloadPath1 -Verbose -$downloadResult1 - -# ===== STEP 12: DOWNLOAD LARGE FILE (MULTIPART) ===== -Write-Output "`n12. Download large file (multipart):" -$downloadPath2 = Join-Path $TestDirectory "downloaded-large.txt" -$multipartDownload = Get-MinIOObjectContentMultipart -BucketName $TestBucket -ObjectName "large-test.txt" -DestinationPath (New-Object System.IO.FileInfo $downloadPath2) -Verbose -$multipartDownload - -# ===== STEP 13: GENERATE PRESIGNED URL ===== -Write-Output "`n13. Generate presigned URL:" -$presignedUrl = Get-MinIOPresignedUrl -BucketName $TestBucket -ObjectName "small-test.txt" -Expiration (New-TimeSpan -Hours 1) -Verbose -$presignedUrl - -# ===== STEP 14: VERIFY DOWNLOADED FILES ===== -Write-Output "`n14. Verify downloaded files:" -Write-Output "Original files:" -Get-ChildItem $TestDirectory -Filter "*test.txt" | Select-Object Name, @{Name="Size(KB)";Expression={[math]::Round($_.Length/1KB,2)}} - -Write-Output "`nDownloaded files:" -Get-ChildItem $TestDirectory -Filter "downloaded-*" | Select-Object Name, @{Name="Size(KB)";Expression={[math]::Round($_.Length/1KB,2)}} - -# ===== STEP 15: COMPARE FILE CONTENTS ===== -Write-Output "`n15. Compare file contents:" -$originalSmall = Get-Content $SmallFile -$downloadedSmall = Get-Content $downloadPath1 -Write-Output "Small file content match: $(($originalSmall -join '') -eq ($downloadedSmall -join ''))" - -$originalLarge = Get-Content $LargeFile -$downloadedLarge = Get-Content $downloadPath2 -Write-Output "Large file content match: $(($originalLarge -join '') -eq ($downloadedLarge -join ''))" - -# ===== CLEANUP INSTRUCTIONS ===== -Write-Output "`n16. Cleanup (manual):" -Write-Output "Test bucket created: $TestBucket" -Write-Output "Local test directory: $TestDirectory" -Write-Output "" -Write-Output "To clean up:" -Write-Output "1. Remove local directory: Remove-Item '$TestDirectory' -Recurse -Force" -Write-Output "2. Remove S3 bucket objects and bucket manually from S3 console" -Write-Output "" -Write-Output "=== ALL TESTS COMPLETED ===" diff --git a/scripts/Test-Quick-Complete.ps1 b/scripts/Test-Quick-Complete.ps1 deleted file mode 100644 index 18c5a54..0000000 --- a/scripts/Test-Quick-Complete.ps1 +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env pwsh -<# -.SYNOPSIS - Quick complete test of PSMinIO functionality - -.DESCRIPTION - Tests all core functionality quickly to verify the implementation works -#> - -# Import the module -Import-Module "$PSScriptRoot\..\Module\PSMinIO\PSMinIO.psd1" -Force - -Write-Output "=== PSMinIO Complete Functionality Test ===" - -try { - # Test 1: Connection - Write-Output "1. Testing Connection..." - Connect-MinIO -Endpoint "https://api.s3.gracesolution.info" -AccessKey "T34Wg85SAwezUa3sk3m4" -SecretKey "PxEmnbQoQJTJsDocSEV6mSSscDpJMiJCayPv93xe" -TestConnection - Write-Output "✅ Connection successful!" - - # Test 2: List Buckets - Write-Output "2. Testing Bucket Listing..." - $buckets = Get-MinIOBucket - Write-Output "✅ Found $($buckets.Count) buckets" - - # Test 3: Create Test Bucket - Write-Output "3. Testing Bucket Creation..." - $testBucket = "psminiotest-$(Get-Date -Format 'yyyyMMdd-HHmmss')" - $bucketResult = New-MinIOBucket -BucketName $testBucket -PassThru - Write-Output "✅ Bucket created: $($bucketResult.Name)" - - # Test 4: Test Bucket Exists - Write-Output "4. Testing Bucket Existence..." - $exists = Test-MinIOBucketExists -BucketName $testBucket - Write-Output "✅ Bucket exists: $exists" - - # Test 5: Upload File - Write-Output "5. Testing File Upload..." - $testFile = Join-Path $env:TEMP "psminiotest-complete.txt" - $testContent = @" -PSMinIO Complete Test File -========================= -Created: $(Get-Date) -Implementation: Custom REST API -Test data: $('A' * 1000) -"@ - $testContent | Out-File -FilePath $testFile -Encoding UTF8 - $fileInfo = Get-Item $testFile - $uploadResult = New-MinIOObject -BucketName $testBucket -File $fileInfo -PassThru - Write-Output "✅ Upload successful: $($uploadResult.ObjectName) ($($uploadResult.TotalSizeFormatted))" - - # Test 6: List Objects - Write-Output "6. Testing Object Listing..." - $objects = Get-MinIOObject -BucketName $testBucket - Write-Output "✅ Found $($objects.Count) objects" - - # Test 7: Download File - Write-Output "7. Testing File Download..." - $downloadFile = Join-Path $env:TEMP "psminiotest-complete-download.txt" - $downloadResult = Get-MinIOObjectContent -BucketName $testBucket -ObjectName $fileInfo.Name -LocalPath $downloadFile -PassThru - Write-Output "✅ Download successful: $($downloadResult.TotalSizeFormatted)" - - # Test 8: Verify Content - Write-Output "8. Testing Content Verification..." - $originalContent = Get-Content $testFile -Raw - $downloadedContent = Get-Content $downloadFile -Raw - if ($originalContent -eq $downloadedContent) { - Write-Output "✅ Content verification PASSED!" - } else { - Write-Output "❌ Content verification FAILED!" - } - - Write-Output "" - Write-Output "=== ALL TESTS PASSED! ===" - Write-Output "🎉 PSMinIO Custom REST API Implementation is fully functional!" - Write-Output "" - Write-Output "Features Verified:" - Write-Output " ✓ Connection management with AWS S3 signature v4" - Write-Output " ✓ Bucket operations (create, list, exists)" - Write-Output " ✓ Object upload with progress tracking" - Write-Output " ✓ Object download with progress tracking" - Write-Output " ✓ Object listing and filtering" - Write-Output " ✓ Performance metrics and timing" - Write-Output " ✓ Content integrity verification" - Write-Output "" - Write-Output "Architecture Benefits:" - Write-Output " • No MinIO SDK dependency" - Write-Output " • No async/await compatibility issues" - Write-Output " • Real progress reporting from HTTP streams" - Write-Output " • Built-in performance monitoring" - Write-Output " • Reduced dependencies (3 DLLs vs 10+)" - -} catch { - Write-Output "❌ Test failed: $($_.Exception.Message)" - exit 1 -} finally { - # Cleanup - if (Test-Path $testFile -ErrorAction SilentlyContinue) { - Remove-Item $testFile -Force -ErrorAction SilentlyContinue - } - if (Test-Path $downloadFile -ErrorAction SilentlyContinue) { - Remove-Item $downloadFile -Force -ErrorAction SilentlyContinue - } -} diff --git a/scripts/Test-Upload-Download.ps1 b/scripts/Test-Upload-Download.ps1 deleted file mode 100644 index d7ce0b6..0000000 --- a/scripts/Test-Upload-Download.ps1 +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env pwsh -<# -.SYNOPSIS - Quick test of PSMinIO upload and download functionality - -.DESCRIPTION - Tests the new PSMinIO implementation with file upload and download -#> - -# Import the module -Import-Module "$PSScriptRoot\..\Module\PSMinIO\PSMinIO.psd1" -Force - -# Connect to MinIO -Write-Verbose "Connecting to MinIO..." -Verbose -Connect-MinIO -Endpoint "https://api.s3.gracesolution.info" -AccessKey "T34Wg85SAwezUa3sk3m4" -SecretKey "PxEmnbQoQJTJsDocSEV6mSSscDpJMiJCayPv93xe" -Verbose - -# Create test file -$testFile = Join-Path $env:TEMP "psminiotest.txt" -$testContent = @" -PSMinIO Test File -================ -Created: $(Get-Date) -Test data: $('A' * 1000) -"@ - -Write-Verbose "Creating test file: $testFile" -Verbose -$testContent | Out-File -FilePath $testFile -Encoding UTF8 -$fileInfo = Get-Item $testFile -Write-Verbose "Test file created: $($fileInfo.Length) bytes" -Verbose - -# Upload file -$testBucket = "psminiotest-20250711-142415" -Write-Verbose "Uploading file to bucket: $testBucket" -Verbose -$uploadResult = New-MinIOObject -BucketName $testBucket -File $fileInfo -PassThru -Verbose - -Write-Output "Upload Result:" -if ($uploadResult) { - $uploadResult | Format-List ObjectName, TotalSizeFormatted, DurationFormatted, AverageSpeedFormatted, Success -} - -# List objects -Write-Verbose "Listing objects in bucket..." -Verbose -$objects = Get-MinIOObject -BucketName $testBucket -Verbose -$objects | Format-Table Name, Size, LastModified - -# Download file -$downloadFile = Join-Path $env:TEMP "psminiotest-download.txt" -Write-Verbose "Downloading file to: $downloadFile" -Verbose -$downloadResult = Get-MinIOObjectContent -BucketName $testBucket -ObjectName $fileInfo.Name -LocalPath $downloadFile -PassThru -Verbose - -Write-Output "Download Result:" -if ($downloadResult) { - $downloadResult | Format-List ObjectName, TotalSizeFormatted, DurationFormatted, AverageSpeedFormatted, Success -} - -# Verify content -$originalContent = Get-Content $testFile -Raw -ErrorAction SilentlyContinue -$downloadedContent = Get-Content $downloadFile -Raw -ErrorAction SilentlyContinue - -if ($originalContent -and $downloadedContent -and ($originalContent -eq $downloadedContent)) { - Write-Output "✅ File content verification PASSED!" -} else { - Write-Output "❌ File content verification FAILED!" -} - -# Cleanup -Remove-Item $testFile -Force -ErrorAction SilentlyContinue -Remove-Item $downloadFile -Force -ErrorAction SilentlyContinue - -Write-Output "Test completed!" diff --git a/scripts/Test-XMLParsing.ps1 b/scripts/Test-XMLParsing.ps1 deleted file mode 100644 index 28b4336..0000000 --- a/scripts/Test-XMLParsing.ps1 +++ /dev/null @@ -1,74 +0,0 @@ -# Test XML Parsing for Multipart Upload Response -# This script tests the XML parsing logic without modifying the codebase - -# Sample XML response from your S3 server -$xmlResponse = @' - -xylemWin10ISO.zipMzBlZDdmNjUtZjNjYi00YzhhLThhMWQtNzViYTAyNWIzYjFhLmJiYjBmNzcxLWZiODEtNGFhYy1iYWQwLTk3M2EyNDIwMjRkM3gxNzUyNTQwMTc4NzE4NjczNjY1 -'@ - -Write-Output "=== XML Parsing Test ===" -Write-Output "Testing XML response parsing logic" -Write-Output "" - -# Parse the XML -$doc = [System.Xml.Linq.XDocument]::Parse($xmlResponse) - -Write-Output "1. Basic XML parsing:" -Write-Output " Root element: $($doc.Root.Name)" -Write-Output " Root namespace: $($doc.Root.GetDefaultNamespace())" -Write-Output "" - -# Test method 1: Without namespace (current broken method) -Write-Output "2. Method 1 - Without namespace (broken):" -$uploadId1 = $doc.Descendants("UploadId") | Select-Object -First 1 | ForEach-Object { $_.Value } -Write-Output " UploadId found: $($uploadId1 ?? 'NULL')" -Write-Output "" - -# Test method 2: With namespace (fixed method) -Write-Output "3. Method 2 - With namespace (fixed):" -$ns = $doc.Root.GetDefaultNamespace() -if ($ns) { - $uploadId2 = $doc.Descendants([System.Xml.Linq.XName]::Get("UploadId", $ns.NamespaceName)) | Select-Object -First 1 | ForEach-Object { $_.Value } -} else { - $uploadId2 = $doc.Descendants("UploadId") | Select-Object -First 1 | ForEach-Object { $_.Value } -} -Write-Output " Namespace: $($ns.NamespaceName)" -Write-Output " UploadId found: $($uploadId2 ?? 'NULL')" -Write-Output "" - -# Test method 3: Alternative approach -Write-Output "4. Method 3 - Alternative approach:" -$uploadId3 = $doc.Descendants() | Where-Object { $_.Name.LocalName -eq "UploadId" } | Select-Object -First 1 | ForEach-Object { $_.Value } -Write-Output " UploadId found: $($uploadId3 ?? 'NULL')" -Write-Output "" - -# Show all elements -Write-Output "5. All elements in the XML:" -$doc.Descendants() | ForEach-Object { - Write-Output " Element: $($_.Name.LocalName) = $($_.Value)" -} -Write-Output "" - -# Test the exact logic that should work -Write-Output "6. Recommended fix logic:" -$ns = $doc.Root.GetDefaultNamespace() -if ($ns -and $ns.NamespaceName) { - $uploadIdFinal = $doc.Descendants([System.Xml.Linq.XName]::Get("UploadId", $ns.NamespaceName)) | Select-Object -First 1 | ForEach-Object { $_.Value } -} else { - $uploadIdFinal = $doc.Descendants("UploadId") | Select-Object -First 1 | ForEach-Object { $_.Value } -} - -Write-Output " Final UploadId: $($uploadIdFinal ?? 'NULL')" -Write-Output " Success: $($uploadIdFinal -ne $null -and $uploadIdFinal -ne '')" -Write-Output "" - -if ($uploadIdFinal) { - Write-Output "✅ XML parsing would work with namespace handling!" - Write-Output " The UploadId '$uploadIdFinal' was successfully extracted." -} else { - Write-Output "❌ XML parsing still has issues." -} - -Write-Output "" -Write-Output "=== Test Complete ===" diff --git a/scripts/Test-ZipArchive.ps1 b/scripts/Test-ZipArchive.ps1 deleted file mode 100644 index f682ee4..0000000 --- a/scripts/Test-ZipArchive.ps1 +++ /dev/null @@ -1,202 +0,0 @@ -# Test script for the new New-MinIOZipArchive cmdlet -# Demonstrates comprehensive zip functionality with progress tracking - -param( - [string]$TestDirectory = "TestZipFiles", - [switch]$Cleanup, - [switch]$Verbose -) - -if ($Verbose) { - $VerbosePreference = 'Continue' -} - -Write-Host "=== PSMinIO Zip Archive Test ===" -ForegroundColor Cyan -Write-Host "Testing New-MinIOZipArchive cmdlet with comprehensive functionality" -ForegroundColor Green - -try { - # Import the module - Write-Host "`n1. Loading PSMinIO module..." -ForegroundColor Yellow - Import-Module ".\Module\PSMinIO\PSMinIO.psd1" -Force - - # Create test directory and files - Write-Host "2. Creating test files..." -ForegroundColor Yellow - if (Test-Path $TestDirectory) { - Remove-Item $TestDirectory -Recurse -Force - } - New-Item -ItemType Directory -Path $TestDirectory -Force | Out-Null - - # Create main directory files - for ($i = 1; $i -le 10; $i++) { - $fileName = "document-{0:D2}.txt" -f $i - $filePath = Join-Path $TestDirectory $fileName - $content = "Document $i`n" + ("Sample content line $i`n" * (Get-Random -Minimum 5 -Maximum 20)) - Set-Content -Path $filePath -Value $content - } - - # Create subdirectories with different file types - $subDir1 = Join-Path $TestDirectory "Reports" - $subDir2 = Join-Path $TestDirectory "Data" - $subDir3 = Join-Path $TestDirectory "Logs" - New-Item -ItemType Directory -Path $subDir1, $subDir2, $subDir3 -Force | Out-Null - - # Add files to subdirectories - for ($i = 1; $i -le 5; $i++) { - Set-Content -Path (Join-Path $subDir1 "report$i.txt") -Value "Report $i content" - Set-Content -Path (Join-Path $subDir2 "data$i.csv") -Value "ID,Name,Value`n$i,Item$i,$(Get-Random -Minimum 100 -Maximum 1000)" - Set-Content -Path (Join-Path $subDir3 "app$i.log") -Value "$(Get-Date) - Log entry $i" - } - - $allFiles = Get-ChildItem $TestDirectory -Recurse -File - Write-Host "✅ Created test structure: $($allFiles.Count) files in multiple directories" -ForegroundColor Green - - # Test 1: Create zip from FileInfo array - Write-Host "`n3. Test 1: Creating zip from FileInfo array..." -ForegroundColor Yellow - $mainFiles = Get-ChildItem $TestDirectory -File - $zipFile1 = [System.IO.FileInfo]"test-files-array.zip" - - Write-Host " Creating zip with $($mainFiles.Count) files using Files parameter set" -ForegroundColor Cyan - $result1 = New-MinIOZipArchive -DestinationPath $zipFile1 -Path $mainFiles -CompressionLevel Optimal -Verbose - - Write-Host "✅ Files array zip created:" -ForegroundColor Green - Write-Host " Files: $($result1.FileCount)" -ForegroundColor White - Write-Host " Original size: $([math]::Round($result1.TotalUncompressedSize / 1KB, 2)) KB" -ForegroundColor White - Write-Host " Compressed size: $([math]::Round($result1.TotalCompressedSize / 1KB, 2)) KB" -ForegroundColor White - Write-Host " Compression: $($result1.CompressionEfficiency.ToString('F1'))%" -ForegroundColor White - Write-Host " Duration: $($result1.Duration.TotalSeconds.ToString('F2'))s" -ForegroundColor White - - # Test 2: Create zip from directory (non-recursive) - Write-Host "`n4. Test 2: Creating zip from directory (non-recursive)..." -ForegroundColor Yellow - $zipFile2 = [System.IO.FileInfo]"test-directory-flat.zip" - - Write-Host " Creating zip from directory (top-level files only)" -ForegroundColor Cyan - $result2 = New-MinIOZipArchive -DestinationPath $zipFile2 -Directory (Get-Item $TestDirectory) -CompressionLevel Fastest -Verbose - - Write-Host "✅ Directory flat zip created:" -ForegroundColor Green - Write-Host " Files: $($result2.FileCount)" -ForegroundColor White - Write-Host " Compression: $($result2.CompressionEfficiency.ToString('F1'))%" -ForegroundColor White - - # Test 3: Create zip from directory (recursive) - Write-Host "`n5. Test 3: Creating zip from directory (recursive)..." -ForegroundColor Yellow - $zipFile3 = [System.IO.FileInfo]"test-directory-recursive.zip" - - Write-Host " Creating zip from directory (recursive, all subdirectories)" -ForegroundColor Cyan - $result3 = New-MinIOZipArchive -DestinationPath $zipFile3 -Directory (Get-Item $TestDirectory) -Recursive -IncludeBaseDirectory -CompressionLevel Optimal -Verbose - - Write-Host "✅ Directory recursive zip created:" -ForegroundColor Green - Write-Host " Files: $($result3.FileCount)" -ForegroundColor White - Write-Host " Compression: $($result3.CompressionEfficiency.ToString('F1'))%" -ForegroundColor White - - # Test 4: Create zip with file filtering - Write-Host "`n6. Test 4: Creating zip with file filtering..." -ForegroundColor Yellow - $zipFile4 = [System.IO.FileInfo]"test-filtered-logs.zip" - - Write-Host " Creating zip with only .log files using InclusionFilter" -ForegroundColor Cyan - $result4 = New-MinIOZipArchive -DestinationPath $zipFile4 -Directory (Get-Item $TestDirectory) -Recursive -InclusionFilter { $_.Extension -eq ".log" } -CompressionLevel Optimal -Verbose - - Write-Host "✅ Filtered zip created:" -ForegroundColor Green - Write-Host " Log files: $($result4.FileCount)" -ForegroundColor White - Write-Host " Compression: $($result4.CompressionEfficiency.ToString('F1'))%" -ForegroundColor White - - # Test 5: Append to existing zip (Update mode) - Write-Host "`n7. Test 5: Appending to existing zip..." -ForegroundColor Yellow - $csvFiles = Get-ChildItem $TestDirectory -Recurse -Filter "*.csv" - - Write-Host " Appending $($csvFiles.Count) CSV files to existing zip" -ForegroundColor Cyan - $result5 = New-MinIOZipArchive -DestinationPath $zipFile4 -Path $csvFiles -Mode Update -CompressionLevel Optimal -Verbose - - Write-Host "✅ Files appended to zip:" -ForegroundColor Green - Write-Host " Total files now: $($result5.FileCount)" -ForegroundColor White - - # Test 6: Create zip with custom base path - Write-Host "`n8. Test 6: Creating zip with custom base path..." -ForegroundColor Yellow - $zipFile6 = [System.IO.FileInfo]"test-custom-basepath.zip" - - Write-Host " Creating zip with custom base path (flattened structure)" -ForegroundColor Cyan - $result6 = New-MinIOZipArchive -DestinationPath $zipFile6 -Directory (Get-Item $TestDirectory) -Recursive -BasePath $TestDirectory -CompressionLevel Optimal -Verbose - - Write-Host "✅ Custom base path zip created:" -ForegroundColor Green - Write-Host " Files: $($result6.FileCount)" -ForegroundColor White - - # Test 7: Get zip archive information - Write-Host "`n9. Test 7: Reading zip archive information..." -ForegroundColor Yellow - $zipFiles = Get-ChildItem "*.zip" - - foreach ($zipFile in $zipFiles) { - Write-Host " Reading archive: $($zipFile.Name)" -ForegroundColor Cyan - - # Basic archive info - $archiveInfo = Get-MinIOZipArchive -ZipFile $zipFile -Verbose - Write-Host " Entries: $($archiveInfo.EntryCount)" -ForegroundColor White - Write-Host " Size: $([math]::Round($archiveInfo.TotalUncompressedSize / 1KB, 2)) KB -> $([math]::Round($archiveInfo.TotalCompressedSize / 1KB, 2)) KB" -ForegroundColor White - Write-Host " Compression: $($archiveInfo.CompressionEfficiency.ToString('F1'))%" -ForegroundColor White - - # Detailed entries for one archive - if ($zipFile.Name -eq "test-directory-recursive.zip") { - Write-Host " Getting detailed entries for recursive archive..." -ForegroundColor Cyan - $detailedInfo = Get-MinIOZipArchive -ZipFile $zipFile -IncludeEntries -Verbose - Write-Host " Entry details: $($detailedInfo.Entries.Count) entries" -ForegroundColor White - - # Show first few entries - $detailedInfo.Entries | Select-Object -First 3 | ForEach-Object { - Write-Host " $($_.FullName) ($([math]::Round($_.Length / 1KB, 2)) KB)" -ForegroundColor Gray - } - } - - # Validate integrity for one archive - if ($zipFile.Name -eq "test-files-array.zip") { - Write-Host " Validating archive integrity..." -ForegroundColor Cyan - $validatedInfo = Get-MinIOZipArchive -ZipFile $zipFile -ValidateIntegrity -Verbose - $validStatus = if ($validatedInfo.IsValid) { "✅ Valid" } else { "❌ Invalid" } - Write-Host " Validation: $validStatus (took $($validatedInfo.ValidationDuration.TotalMilliseconds.ToString('F0'))ms)" -ForegroundColor White - } - } - - Write-Host "✅ Archive reading tests completed!" -ForegroundColor Green - - # Summary - Write-Host "`n=== TEST SUMMARY ===" -ForegroundColor Cyan - Write-Host "✅ All zip archive tests completed successfully!" -ForegroundColor Green - Write-Host "New-MinIOZipArchive features tested:" -ForegroundColor Yellow - Write-Host " • FileInfo destination path parameter" -ForegroundColor White - Write-Host " • FileInfo[] parameter with Files parameter set" -ForegroundColor White - Write-Host " • Directory parameter with recursive and non-recursive modes" -ForegroundColor White - Write-Host " • File filtering with InclusionFilter ScriptBlock" -ForegroundColor White - Write-Host " • Append mode (Update) for adding files to existing zips" -ForegroundColor White - Write-Host " • Custom base path for entry name control" -ForegroundColor White - Write-Host " • Multiple compression levels (Optimal, Fastest)" -ForegroundColor White - Write-Host " • Comprehensive progress tracking and metrics" -ForegroundColor White - Write-Host " • Always returns result objects (no PassThru needed)" -ForegroundColor White - - Write-Host "Get-MinIOZipArchive features tested:" -ForegroundColor Yellow - Write-Host " • Basic archive information reading" -ForegroundColor White - Write-Host " • Detailed entry information with IncludeEntries" -ForegroundColor White - Write-Host " • Archive integrity validation" -ForegroundColor White - Write-Host " • Proper disposal handling with OpenRead" -ForegroundColor White - Write-Host " • Comprehensive metrics and compression statistics" -ForegroundColor White - - Write-Host "`nZip files created:" -ForegroundColor Yellow - foreach ($zipFile in $zipFiles) { - $sizeKB = [math]::Round($zipFile.Length / 1KB, 2) - Write-Host " • $($zipFile.Name) ($sizeKB KB)" -ForegroundColor White - } - - if ($Cleanup) { - Write-Host "`n10. Cleaning up..." -ForegroundColor Yellow - Remove-Item "*.zip" -Force -ErrorAction SilentlyContinue - Write-Host "✅ Cleanup completed!" -ForegroundColor Green - } else { - Write-Host "`nZip files preserved for inspection. Use -Cleanup to remove them." -ForegroundColor Cyan - } - -} catch { - Write-Host "❌ Test failed: $($_.Exception.Message)" -ForegroundColor Red - Write-Host "Stack trace: $($_.ScriptStackTrace)" -ForegroundColor Red -} finally { - # Clean up test directory - if (Test-Path $TestDirectory) { - Remove-Item $TestDirectory -Recurse -Force -ErrorAction SilentlyContinue - } -} - -Write-Host "`n=== Test Complete ===" -ForegroundColor Cyan diff --git a/src/Utils/ThreadSafeProgressCollector.cs b/src/Utils/ThreadSafeProgressCollector.cs index bd7ebc9..ad323a6 100644 --- a/src/Utils/ThreadSafeProgressCollector.cs +++ b/src/Utils/ThreadSafeProgressCollector.cs @@ -8,6 +8,21 @@ namespace PSMinIO.Utils /// /// Thread-safe progress data collector that accumulates progress updates from background threads /// and allows the main thread to safely report them to PowerShell + /// + /// CRITICAL THREADING RULE: + /// PowerShell cmdlets can ONLY call Write-Progress, Write-Verbose, Write-Object, Write-Error + /// from the main cmdlet thread - NEVER from background threads! + /// + /// USAGE PATTERN: + /// - Background threads: Call QueueProgressUpdate(), QueueVerboseMessage() (thread-safe) + /// - Main thread ONLY: Call ProcessQueuedUpdates() to display queued updates + /// + /// EXAMPLE: + /// Task.Run(() => { + /// collector.QueueProgressUpdate(1, "Processing", "Status", 50); // ✅ Safe + /// // WriteProgress(...); // ❌ THREADING ERROR! + /// }); + /// collector.ProcessQueuedUpdates(); // ✅ Only from main thread /// public class ThreadSafeProgressCollector {