From c5c9f81c50b6d2e6eed387b4ccc492284a0e54e6 Mon Sep 17 00:00:00 2001 From: Raymond LaRose Date: Fri, 25 Apr 2025 17:33:38 -0400 Subject: [PATCH] Add unit tests for Set-GiteaConfiguration, Get-GiteaConfiguration, and Get-GiteaFileContent functions --- .../Tests/Get-GiteaChildItem.Tests.ps1 | 173 ++++++++++++++++++ .../Tests/Get-GiteaConfiguration.tests.ps1 | 75 ++++++++ .../Tests/Get-GiteaFileContent.Tests.ps1 | 119 ++++++++++++ .../Tests/Invoke-GiteaFileDownload.Tests.ps1 | 122 ++++++++++++ .../Tests/Set-GiteaConfiguration.Tests.ps1 | 64 +++++++ 5 files changed, 553 insertions(+) create mode 100644 PS-GiteaUtilities/Tests/Get-GiteaChildItem.Tests.ps1 create mode 100644 PS-GiteaUtilities/Tests/Get-GiteaConfiguration.tests.ps1 create mode 100644 PS-GiteaUtilities/Tests/Get-GiteaFileContent.Tests.ps1 create mode 100644 PS-GiteaUtilities/Tests/Invoke-GiteaFileDownload.Tests.ps1 create mode 100644 PS-GiteaUtilities/Tests/Set-GiteaConfiguration.Tests.ps1 diff --git a/PS-GiteaUtilities/Tests/Get-GiteaChildItem.Tests.ps1 b/PS-GiteaUtilities/Tests/Get-GiteaChildItem.Tests.ps1 new file mode 100644 index 0000000..dfe292c --- /dev/null +++ b/PS-GiteaUtilities/Tests/Get-GiteaChildItem.Tests.ps1 @@ -0,0 +1,173 @@ +# Get-GiteaChildItem.Tests.ps1 + +# Import the module under test +$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities.psm1' +Import-Module -Name $modulePath -Force + +Describe 'Get-GiteaChildItem' { + + BeforeEach { + # Mock config load + Mock -CommandName Get-GiteaConfiguration -ModuleName PS-GiteaUtilities -MockWith { + return @{ + giteaURL = 'https://mock.gitea.com' + defaultOwner = 'mockuser' + defaultRepo = 'mockrepo' + defaultBranch = 'main' + token = 'mocktoken' + } + } + + # Mock LFS config + Mock -CommandName Get-GiteaLFSConfiguration -ModuleName PS-GiteaUtilities -MockWith { + return @(".lfs") + } + + # Mock web call to list files + Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith { + return @( + @{ + name = "file1.txt" + path = "file1.txt" + type = "file" + size = 123 + sha = "abc123" + download_url = "https://mock.gitea.com/file1.txt" + }, + @{ + name = "subdir" + path = "subdir" + type = "dir" + size = 0 + sha = $null + download_url = $null + } + ) + } + + Mock -CommandName Write-Error -ModuleName PS-GiteaUtilities -MockWith { } + } + + Context 'When listing basic items' { + It 'Should return files and directories correctly' { + $result = Get-GiteaChildItem -repoOwner 'mockuser' -repoName 'mockrepo' -Path '' -token 'mocktoken' + + $result | Should -Not -BeNullOrEmpty + $result.Count | Should -BeGreaterThan 0 + + $file = $result | Where-Object { $_.type -eq 'file' } + $dir = $result | Where-Object { $_.type -eq 'dir' } + + $file.name | Should -Be 'file1.txt' + $dir.name | Should -Be 'subdir' + } + } + + Context 'When filtering only files' { + It 'Should return only files when -File is used' { + $result = Get-GiteaChildItem -repoOwner 'mockuser' -repoName 'mockrepo' -Path '' -token 'mocktoken' -File + + $result | Should -Not -BeNullOrEmpty + $result.Count | Should -Be 1 + $result[0].type | Should -Be 'file' + } + } + + Context 'When recursing into directories' { + BeforeEach { + # Mock recursion call + Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith { + param($Uri) + + if ($Uri -like "*subdir*") { + return @( + @{ + name = "nestedfile.txt" + path = "subdir/nestedfile.txt" + type = "file" + size = 456 + sha = "def456" + download_url = "https://mock.gitea.com/subdir/nestedfile.txt" + } + ) + } else { + return @( + @{ + name = "file1.txt" + path = "file1.txt" + type = "file" + size = 123 + sha = "abc123" + download_url = "https://mock.gitea.com/file1.txt" + }, + @{ + name = "subdir" + path = "subdir" + type = "dir" + size = 0 + sha = $null + download_url = $null + } + ) + } + } + } + + It 'Should retrieve nested files when -Recurse is used' { + $result = Get-GiteaChildItem -repoOwner 'mockuser' -repoName 'mockrepo' -Path '' -token 'mocktoken' -Recurse + + $nested = $result | Where-Object { $_.Path -eq 'subdir/nestedfile.txt' } + + $nested | Should -Not -BeNullOrEmpty + $nested.size | Should -Be 456 + } + } + + Context 'When recursion depth limit is enforced' { + BeforeEach { + # Same recursion mock as above + Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith { + param($Uri) + + if ($Uri -like "*subdir*") { + return @( + @{ + name = "nestedfile.txt" + path = "subdir/nestedfile.txt" + type = "file" + size = 456 + sha = "def456" + download_url = "https://mock.gitea.com/subdir/nestedfile.txt" + } + ) + } else { + return @( + @{ + name = "file1.txt" + path = "file1.txt" + type = "file" + size = 123 + sha = "abc123" + download_url = "https://mock.gitea.com/file1.txt" + }, + @{ + name = "subdir" + path = "subdir" + type = "dir" + size = 0 + sha = $null + download_url = $null + } + ) + } + } + } + + It 'Should not recurse deeper than depth limit' { + $result = Get-GiteaChildItem -repoOwner 'mockuser' -repoName 'mockrepo' -Path '' -token 'mocktoken' -Recurse -Depth 0 + + $nested = $result | Where-Object { $_.Path -eq 'subdir/nestedfile.txt' } + $nested | Should -BeNullOrEmpty + } + } +} diff --git a/PS-GiteaUtilities/Tests/Get-GiteaConfiguration.tests.ps1 b/PS-GiteaUtilities/Tests/Get-GiteaConfiguration.tests.ps1 new file mode 100644 index 0000000..f02e330 --- /dev/null +++ b/PS-GiteaUtilities/Tests/Get-GiteaConfiguration.tests.ps1 @@ -0,0 +1,75 @@ +# Get-GiteaConfiguration.Tests.ps1 + +# Import the module under test +$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities.psm1' +Import-Module -Name $modulePath -Force + +Describe 'Get-GiteaConfiguration' { + + Context 'When configuration file exists' { + BeforeEach { + # Mock filesystem and import + Mock -CommandName Test-Path -ModuleName PS-GiteaUtilities -MockWith { $true } + Mock -CommandName Import-Clixml -ModuleName PS-GiteaUtilities -MockWith { + return @{ + giteaURL = 'https://gitea.example.com' + token = 'ABC123' + } + } + } + + It 'Should import configuration and return it' { + $result = Get-GiteaConfiguration + + $result | Should -Not -BeNullOrEmpty + $result.giteaURL | Should -Be 'https://gitea.example.com' + $result.token | Should -Be 'ABC123' + } + + It 'Should create global variables when LoadVariables is used' { + # Remove any existing variables first + Remove-Variable -Name giteaURL, token -Scope Global -ErrorAction SilentlyContinue + + Get-GiteaConfiguration -LoadVariables + + (Get-Variable -Name giteaURL -Scope Global).Value | Should -Be 'https://gitea.example.com' + (Get-Variable -Name token -Scope Global).Value | Should -Be 'ABC123' + } + + It 'Should overwrite existing variables when Force is used' { + # Set initial variables + Set-Variable -Name giteaURL -Value 'OldURL' -Scope Global + Set-Variable -Name token -Value 'OldToken' -Scope Global + + Get-GiteaConfiguration -LoadVariables -Force + + (Get-Variable -Name giteaURL -Scope Global).Value | Should -Be 'https://gitea.example.com' + (Get-Variable -Name token -Scope Global).Value | Should -Be 'ABC123' + } + + It 'Should not overwrite existing variables without Force' { + # Set initial variables + Set-Variable -Name giteaURL -Value 'OldURL' -Scope Global + Set-Variable -Name token -Value 'OldToken' -Scope Global + + Get-GiteaConfiguration -LoadVariables + + (Get-Variable -Name giteaURL -Scope Global).Value | Should -Be 'OldURL' + (Get-Variable -Name token -Scope Global).Value | Should -Be 'OldToken' + } + } + + Context 'When configuration file does not exist' { + BeforeEach { + Mock -CommandName Test-Path -ModuleName PS-GiteaUtilities -MockWith { $false } + Mock -CommandName Write-Warning -ModuleName PS-GiteaUtilities -MockWith { } + } + + It 'Should return null and warn the user' { + $result = Get-GiteaConfiguration + + $result | Should -BeNullOrEmpty + Assert-MockCalled -CommandName Write-Warning -ModuleName PS-GiteaUtilities -Times 1 + } + } +} diff --git a/PS-GiteaUtilities/Tests/Get-GiteaFileContent.Tests.ps1 b/PS-GiteaUtilities/Tests/Get-GiteaFileContent.Tests.ps1 new file mode 100644 index 0000000..ccf8586 --- /dev/null +++ b/PS-GiteaUtilities/Tests/Get-GiteaFileContent.Tests.ps1 @@ -0,0 +1,119 @@ +# Get-GiteaFileContent.Tests.ps1 + +# Import the module under test +$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities.psm1' +Import-Module -Name $modulePath -Force + +Describe 'Get-GiteaFileContent' { + + Context 'When all parameters are provided' { + BeforeEach { + Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith { + return @{ + content = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('Hello World')) + size = 11 + sha = 'fake-sha' + } + } + } + + It 'Should return file content without decoding by default' { + $result = Get-GiteaFileContent -repoOwner 'user' -repoName 'repo' -filePath 'README.md' -token 'abc123' + + $result | Should -Not -BeNullOrEmpty + $result.Success | Should -Be $true + $result.Content | Should -Be ([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('Hello World'))) + } + + It 'Should decode content when -decode is used' { + $result = Get-GiteaFileContent -repoOwner 'user' -repoName 'repo' -filePath 'README.md' -token 'abc123' -decode + + $result | Should -Not -BeNullOrEmpty + $result.Success | Should -Be $true + $result.Content | Should -Be 'Hello World' + } + } + + Context 'When missing parameters and config is loaded' { + BeforeEach { + Mock -CommandName Get-GiteaConfiguration -ModuleName PS-GiteaUtilities -MockWith { + return @{ + giteaURL = 'https://mock.gitea.com' + defaultOwner = 'mockuser' + defaultRepo = 'mockrepo' + defaultBranch = 'main' + token = 'mocktoken' + } + } + + Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith { + return @{ + content = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('Mock Config Content')) + size = 20 + sha = 'mock-sha' + } + } + } + + It 'Should load configuration from Get-GiteaConfiguration if not all parameters are given' { + $result = Get-GiteaFileContent -filePath 'test.ps1' + + $result | Should -Not -BeNullOrEmpty + $result.Path | Should -Be 'test.ps1' + $result.Content | Should -Be ([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('Mock Config Content'))) + } + } + + Context 'When API call fails' { + BeforeEach { + Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith { throw "API call failed" } + Mock -CommandName Write-Error -ModuleName PS-GiteaUtilities -MockWith { } + } + + It 'Should capture error and mark result as unsuccessful' { + $result = Get-GiteaFileContent -repoOwner 'user' -repoName 'repo' -filePath 'badfile.ps1' -token 'abc123' + + $result.Success | Should -Be $false + $result.Error | Should -Match 'API call failed' + } + } + + + Context 'Parameter validation' { + It 'Should throw if required parameters are missing and no config' { + Mock -CommandName Get-GiteaConfiguration -ModuleName PS-GiteaUtilities -MockWith { return $null } + + { Get-GiteaFileContent -filePath 'something.ps1' } | Should -Throw + } + } + + Context 'When decoding file content' { + BeforeEach { + # Simulate API returning Base64 content + Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith { + return @{ + content = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('Hello from API!')) + size = 15 + sha = 'fake-sha-for-decode' + } + } + } + + It 'Should return base64 encoded content by default (no decode switch)' { + $result = Get-GiteaFileContent -repoOwner 'user' -repoName 'repo' -filePath 'testfile.txt' -token 'abc123' + + $result | Should -Not -BeNullOrEmpty + $result.Success | Should -Be $true + $result.Content | Should -Be ([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('Hello from API!'))) + } + + It 'Should decode base64 content into plain text when -decode is used' { + $result = Get-GiteaFileContent -repoOwner 'user' -repoName 'repo' -filePath 'testfile.txt' -token 'abc123' -decode + + $result | Should -Not -BeNullOrEmpty + $result.Success | Should -Be $true + $result.Content | Should -Be 'Hello from API!' + } + } + +} diff --git a/PS-GiteaUtilities/Tests/Invoke-GiteaFileDownload.Tests.ps1 b/PS-GiteaUtilities/Tests/Invoke-GiteaFileDownload.Tests.ps1 new file mode 100644 index 0000000..99e0ec8 --- /dev/null +++ b/PS-GiteaUtilities/Tests/Invoke-GiteaFileDownload.Tests.ps1 @@ -0,0 +1,122 @@ +# Invoke-GiteaFileDownload.Tests.ps1 + +# Import the module under test +$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities.psm1' +Import-Module -Name $modulePath -Force + +Describe 'Invoke-GiteaFileDownload' { + + BeforeEach { + # Always mock Get-GiteaConfiguration to avoid needing real config + Mock -CommandName Get-GiteaConfiguration -ModuleName PS-GiteaUtilities -MockWith { + return @{ token = 'mocktoken' } + } + + # Correct webClient Mock: headers, download, dispose + Mock -CommandName New-Object -ModuleName PS-GiteaUtilities -MockWith { + $headers = New-Object System.Net.WebHeaderCollection + + # Create a real object + $webClient = New-Object PSObject + + # Add Headers + Add-Member -InputObject $webClient -MemberType NoteProperty -Name Headers -Value $headers + + # Add a DownloadFile method + Add-Member -InputObject $webClient -MemberType ScriptMethod -Name DownloadFile -Value { + param($url, $path) + # Fake download (do nothing) + } + + # Add a Dispose method + Add-Member -InputObject $webClient -MemberType ScriptMethod -Name Dispose -Value { + # Fake dispose (do nothing) + } + + return $webClient + } + # Mock filesystem behavior + Mock -CommandName Test-Path -ModuleName PS-GiteaUtilities -MockWith { $false } + Mock -CommandName New-Item -ModuleName PS-GiteaUtilities -MockWith { } + + # Clean captured download data before each test + $script:LastDownloadUrl = $null + $script:LastDownloadPath = $null + } + + Context 'When all parameters are valid' { + It 'Should download a file successfully' { + $result = Invoke-GiteaFileDownload -downloadURL 'https://gitea.example.com/raw/path/to/file.txt' -token 'abc123' + + $result | Should -Not -BeNullOrEmpty + $result.Success | Should -Be $true + $result.Path | Should -Match 'file\.txt$' + } + } + + Context 'When preserving relative paths' { + It 'Should create a path based on filePath with PreserveRelativePath' { + $result = Invoke-GiteaFileDownload -downloadURL 'https://gitea.example.com/raw/path/to/file.txt' ` + -filePath 'docs/manual/file.txt' -PreserveRelativePath -token 'abc123' + + $result.Path | Should -Match 'docs[\\/]+manual[\\/]+file\.txt$' + $result.Success | Should -Be $true + } + } + + Context 'When skipping directories' { + It 'Should skip items with type = dir' { + $result = Invoke-GiteaFileDownload -filePath 'docs/' -type 'dir' -token 'abc123' + + $result.Success | Should -Be $true + $result.Error | Should -Match 'Skipped' + } + } + + Context 'When file exists and -Force is not used' { + BeforeEach { + # Simulate file already exists + Mock -CommandName Test-Path -ModuleName PS-GiteaUtilities -MockWith { + param($Path, $PathType) + if ($PathType -eq 'Leaf') { return $true } else { return $false } + } + Mock -CommandName Write-Error -ModuleName PS-GiteaUtilities -MockWith { } + } + + It 'Should not overwrite existing file without Force' { + $result = Invoke-GiteaFileDownload -downloadURL 'https://gitea.example.com/raw/path/to/existingfile.txt' -token 'abc123' + + $result.Success | Should -Be $false + $result.Error | Should -Match 'already exists' + } + } + + Context 'When download fails' { + BeforeEach { + Mock -CommandName New-Object -ModuleName PS-GiteaUtilities -MockWith { + $headers = New-Object System.Net.WebHeaderCollection + + $webClient = New-Object PSObject + Add-Member -InputObject $webClient -MemberType NoteProperty -Name Headers -Value $headers + Add-Member -InputObject $webClient -MemberType ScriptMethod -Name DownloadFile -Value { + throw "Download failed intentionally" + } + Add-Member -InputObject $webClient -MemberType ScriptMethod -Name Dispose -Value { + return $true + } + + return $webClient + } + Mock -CommandName Test-Path -ModuleName PS-GiteaUtilities -MockWith { $false } + Mock -CommandName New-Item -ModuleName PS-GiteaUtilities -MockWith { } + Mock -CommandName Write-Error -ModuleName PS-GiteaUtilities -MockWith { } + } + + It 'Should capture error and mark download as failed' { + $result = Invoke-GiteaFileDownload -downloadURL 'https://gitea.example.com/raw/path/to/badfile.txt' -token 'abc123' + + $result.Success | Should -Be $false + $result.Error | Should -Match 'Failed to download' + } + } +} \ No newline at end of file diff --git a/PS-GiteaUtilities/Tests/Set-GiteaConfiguration.Tests.ps1 b/PS-GiteaUtilities/Tests/Set-GiteaConfiguration.Tests.ps1 new file mode 100644 index 0000000..6330223 --- /dev/null +++ b/PS-GiteaUtilities/Tests/Set-GiteaConfiguration.Tests.ps1 @@ -0,0 +1,64 @@ +# Set-GiteaConfiguration.Tests.ps1 + +Describe 'Set-GiteaConfiguration' { + + BeforeAll { + # Import the module dynamically + $modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities.psm1' + Import-Module -Name $modulePath -Force + + } + + Context 'Parameter Validation' { + It 'Should throw a MissingArgument exception if required parameters are missing' { + { Set-GiteaConfiguration -giteaURL -token} | Should -Throw + } + } + + + Context 'Configuration Setup' { + BeforeEach { + Mock -CommandName Test-Path -ModuleName PS-GiteaUtilities -MockWith { $false } + Mock -CommandName New-Item -ModuleName PS-GiteaUtilities -MockWith { return $null } + Mock -CommandName Export-Clixml -ModuleName PS-GiteaUtilities -MockWith { return $null } + } + + It 'Should create the configuration directory if it does not exist' { + Set-GiteaConfiguration -giteaURL 'https://gitea.example.com' -token 'ABC123' -defaultOwner 'testuser' -defaultRepo 'testrepo' + + Assert-MockCalled -CommandName New-Item -ModuleName PS-GiteaUtilities -Times 1 + } + + It 'Should export the configuration to an XML file' { + Set-GiteaConfiguration -giteaURL 'https://gitea.example.com' -token 'ABC123' -defaultOwner 'testuser' -defaultRepo 'testrepo' + + Assert-MockCalled -CommandName Export-Clixml -ModuleName PS-GiteaUtilities -Times 1 + } + + It 'Should set defaultBranch to "main" if not specified' { + # Important: override the Export-Clixml mock to capture the config + Mock -CommandName Export-Clixml -ModuleName PS-GiteaUtilities -MockWith { + param($Path, $InputObject) + $script:CapturedConfig = $InputObject + } + + Set-GiteaConfiguration -giteaURL 'https://gitea.example.com' -token 'ABC123' -defaultOwner 'testuser' -defaultRepo 'testrepo' + + $script:CapturedConfig.defaultBranch | Should -Be 'main' + } + } + + Context 'When directory already exists' { + BeforeEach { + Mock -CommandName Test-Path -MockWith { $true } + Mock -CommandName New-Item -MockWith { throw "Should not be called" } + Mock -CommandName Export-Clixml -MockWith { return $null } + } + + It 'Should not attempt to create the directory' { + Set-GiteaConfiguration -giteaURL 'https://gitea.example.com' -token 'ABC123' -defaultOwner 'testuser' -defaultRepo 'testrepo' + + Assert-MockCalled -CommandName New-Item -Times 0 + } + } +}