lib/rules/marketplace/MarketplaceRulesHelper.Tests.ps1
|
############################################################################# # Tests for MarketplaceRulesHelper functions ############################################################################# BeforeAll { . "$PSScriptRoot/../../StateModel.ps1" . "$PSScriptRoot/../../Logging.ps1" . "$PSScriptRoot/../../GitHubApi.ps1" . "$PSScriptRoot/MarketplaceRulesHelper.ps1" } Describe "ConvertTo-MarketplaceSlug" { It "should convert simple name to lowercase slug" { $result = ConvertTo-MarketplaceSlug -ActionName "Test Action" $result | Should -Be "test-action" } It "should handle multiple spaces" { $result = ConvertTo-MarketplaceSlug -ActionName "Actions SemVer Checker" $result | Should -Be "actions-semver-checker" } It "should remove special characters" { $result = ConvertTo-MarketplaceSlug -ActionName "My Action! (v2.0)" $result | Should -Be "my-action-v20" } It "should handle already-lowercase names" { $result = ConvertTo-MarketplaceSlug -ActionName "my-action" $result | Should -Be "my-action" } It "should trim leading and trailing hyphens" { $result = ConvertTo-MarketplaceSlug -ActionName " Test Action " $result | Should -Be "test-action" } It "should convert 'Actions SemVer Checker' to 'actions-semver-checker'" { $result = ConvertTo-MarketplaceSlug -ActionName "Actions SemVer Checker" $result | Should -Be "actions-semver-checker" } } Describe "Test-MarketplaceVersionPublished" { BeforeAll { # Define the wrapper function so the helper uses it (and we can mock it) function global:Invoke-WebRequestWrapper { param($Uri, $Method, $ErrorAction, $TimeoutSec) throw "Invoke-WebRequestWrapper should be mocked in tests" } } AfterAll { # Clean up global function Remove-Item -Path "Function:\global:Invoke-WebRequestWrapper" -ErrorAction SilentlyContinue } Context "Successful marketplace query" { It "should return IsPublished=true when version is published" { # Mock Invoke-WebRequestWrapper to return a page with embedded JSON containing the version Mock Invoke-WebRequestWrapper { return @{ Content = @" <html> <head><title>Test Action</title></head> <body> <script type="application/json" data-target="react-app.embeddedData"> {"payload":{"releaseData":{"selectedRelease":{"tagName":"v1.0.0"},"releases":[{"tagName":"v1.0.0","name":"v1.0.0","isPrerelease":false}]}}} </script> </body> </html> "@ } } $result = Test-MarketplaceVersionPublished -ActionName "Test Action" -Version "v1.0.0" $result.IsPublished | Should -Be $true $result.Error | Should -BeNullOrEmpty $result.MarketplaceUrl | Should -Match "marketplace/actions/test-action.*version=v1.0.0" } It "should return IsPublished=false when version is not in releases" { # Mock Invoke-WebRequestWrapper to return a page with embedded JSON NOT containing the version Mock Invoke-WebRequestWrapper { return @{ Content = @" <html> <head><title>Test Action</title></head> <body> <script type="application/json" data-target="react-app.embeddedData"> {"payload":{"releaseData":{"selectedRelease":{"tagName":"v1.0.0"},"releases":[{"tagName":"v1.0.0","name":"v1.0.0","isPrerelease":false}]}}} </script> </body> </html> "@ } } $result = Test-MarketplaceVersionPublished -ActionName "Test Action" -Version "v99.0.0" $result.IsPublished | Should -Be $false $result.Error | Should -BeNullOrEmpty } } Context "Network errors" { It "should return error when request fails with 404" { Mock Invoke-WebRequestWrapper { $response = New-Object System.Net.Http.HttpResponseMessage([System.Net.HttpStatusCode]::NotFound) $exception = [System.Net.Http.HttpRequestException]::new("Not Found") $exception | Add-Member -NotePropertyName 'Response' -NotePropertyValue $response -Force throw $exception } $result = Test-MarketplaceVersionPublished -ActionName "Nonexistent Action" -Version "v1.0.0" $result.IsPublished | Should -Be $false $result.Error | Should -Not -BeNullOrEmpty } It "should return error with null IsPublished for network errors" { Mock Invoke-WebRequestWrapper { throw [System.Net.WebException]::new("Network error") } $result = Test-MarketplaceVersionPublished -ActionName "Test Action" -Version "v1.0.0" $result.IsPublished | Should -BeNullOrEmpty $result.Error | Should -Not -BeNullOrEmpty $result.Error | Should -Match "Failed to check marketplace" } } Context "Custom server URL" { It "should use custom server URL when provided" { # Mock to return embedded JSON with version Mock Invoke-WebRequestWrapper { return @{ Content = @" <html> <body> <script type="application/json" data-target="react-app.embeddedData"> {"payload":{"releaseData":{"releases":[{"tagName":"v1.0.0","name":"v1.0.0","isPrerelease":false}]}}} </script> </body> </html> "@ } } $result = Test-MarketplaceVersionPublished -ActionName "Test Action" -Version "v1.0.0" -ServerUrl "https://github.mycompany.com" $result.MarketplaceUrl | Should -Match "github.mycompany.com" } } } Describe "Get-ActionMarketplaceMetadata" { Context "YAML parsing" { BeforeAll { # Mock Get-GitHubFileContents to return test YAML Mock Get-GitHubFileContents { param($State, $Path, $Ref) if ($Path -eq 'action.yaml') { return @" name: 'Test Action' description: 'A test action for unit testing' branding: icon: 'check-circle' color: 'blue' inputs: test-input: description: 'A test input' runs: using: composite steps: - run: echo test shell: bash "@ } return $null } Mock Test-GitHubFileExists { param($State, $Path, $Ref) return $Path -eq 'README.md' } # Mock directory listing for README check Mock Get-GitHubDirectoryContents { param($State, $Path, $Ref) return @( [PSCustomObject]@{ Name = 'action.yaml'; Path = 'action.yaml'; Type = 'file'; Sha = 'abc123' } [PSCustomObject]@{ Name = 'README.md'; Path = 'README.md'; Type = 'file'; Sha = 'def456' } [PSCustomObject]@{ Name = 'lib'; Path = 'lib'; Type = 'dir'; Sha = 'ghi789' } ) } } It "should detect action.yaml exists" { $state = [RepositoryState]::new() $state.RepoOwner = "test" $state.RepoName = "repo" $metadata = Get-ActionMarketplaceMetadata -State $state $metadata.ActionFileExists | Should -Be $true $metadata.ActionFilePath | Should -Be 'action.yaml' } It "should extract name property" { $state = [RepositoryState]::new() $state.RepoOwner = "test" $state.RepoName = "repo" $metadata = Get-ActionMarketplaceMetadata -State $state $metadata.HasName | Should -Be $true $metadata.Name | Should -Be 'Test Action' } It "should extract description property" { $state = [RepositoryState]::new() $state.RepoOwner = "test" $state.RepoName = "repo" $metadata = Get-ActionMarketplaceMetadata -State $state $metadata.HasDescription | Should -Be $true $metadata.Description | Should -Be 'A test action for unit testing' } It "should extract branding icon" { $state = [RepositoryState]::new() $state.RepoOwner = "test" $state.RepoName = "repo" $metadata = Get-ActionMarketplaceMetadata -State $state $metadata.HasBrandingIcon | Should -Be $true $metadata.BrandingIcon | Should -Be 'check-circle' } It "should extract branding color" { $state = [RepositoryState]::new() $state.RepoOwner = "test" $state.RepoName = "repo" $metadata = Get-ActionMarketplaceMetadata -State $state $metadata.HasBrandingColor | Should -Be $true $metadata.BrandingColor | Should -Be 'blue' } It "should detect README.md exists" { $state = [RepositoryState]::new() $state.RepoOwner = "test" $state.RepoName = "repo" $metadata = Get-ActionMarketplaceMetadata -State $state $metadata.ReadmeExists | Should -Be $true } It "should return valid metadata when all requirements are met" { $state = [RepositoryState]::new() $state.RepoOwner = "test" $state.RepoName = "repo" $metadata = Get-ActionMarketplaceMetadata -State $state $metadata.IsValid() | Should -Be $true } } Context "Missing action file" { BeforeAll { Mock Get-GitHubFileContents { param($State, $Path, $Ref) return $null # File not found } Mock Test-GitHubFileExists { param($State, $Path, $Ref) return $false } # Mock empty directory listing Mock Get-GitHubDirectoryContents { param($State, $Path, $Ref) return @() } } It "should report action file missing" { $state = [RepositoryState]::new() $state.RepoOwner = "test" $state.RepoName = "repo" $metadata = Get-ActionMarketplaceMetadata -State $state $metadata.ActionFileExists | Should -Be $false $metadata.IsValid() | Should -Be $false } } Context "Partial metadata" { BeforeAll { Mock Get-GitHubFileContents { param($State, $Path, $Ref) if ($Path -eq 'action.yaml') { return @" name: 'Test Action' # Missing description and branding inputs: test-input: description: 'A test input' "@ } return $null } Mock Test-GitHubFileExists { param($State, $Path, $Ref) return $false # No README } # Mock empty directory listing (no README) Mock Get-GitHubDirectoryContents { param($State, $Path, $Ref) return @( [PSCustomObject]@{ Name = 'action.yaml'; Path = 'action.yaml'; Type = 'file'; Sha = 'abc123' } ) } } It "should report missing fields" { $state = [RepositoryState]::new() $state.RepoOwner = "test" $state.RepoName = "repo" $metadata = Get-ActionMarketplaceMetadata -State $state $metadata.ActionFileExists | Should -Be $true $metadata.HasName | Should -Be $true $metadata.HasDescription | Should -Be $false $metadata.HasBrandingIcon | Should -Be $false $metadata.HasBrandingColor | Should -Be $false $metadata.ReadmeExists | Should -Be $false $metadata.IsValid() | Should -Be $false } It "should list missing requirements" { $state = [RepositoryState]::new() $state.RepoOwner = "test" $state.RepoName = "repo" $metadata = Get-ActionMarketplaceMetadata -State $state $missing = $metadata.GetMissingRequirements() $missing | Should -Contain "description property in action.yaml" $missing | Should -Contain "branding.icon property in action.yaml" $missing | Should -Contain "branding.color property in action.yaml" $missing | Should -Contain "README.md file in repository root" } } Context "action.yml fallback" { BeforeAll { Mock Get-GitHubFileContents { param($State, $Path, $Ref) if ($Path -eq 'action.yml') { return @" name: 'Test Action YML' description: 'Found via yml fallback' branding: icon: 'star' color: 'yellow' "@ } return $null # action.yaml not found } Mock Test-GitHubFileExists { param($State, $Path, $Ref) return $Path -eq 'README.md' } # Mock directory listing with README (for action.yml fallback) Mock Get-GitHubDirectoryContents { param($State, $Path, $Ref) return @( [PSCustomObject]@{ Name = 'action.yml'; Path = 'action.yml'; Type = 'file'; Sha = 'abc123' } [PSCustomObject]@{ Name = 'README.md'; Path = 'README.md'; Type = 'file'; Sha = 'def456' } ) } } It "should find action.yml when action.yaml doesn't exist" { $state = [RepositoryState]::new() $state.RepoOwner = "test" $state.RepoName = "repo" $metadata = Get-ActionMarketplaceMetadata -State $state $metadata.ActionFileExists | Should -Be $true $metadata.ActionFilePath | Should -Be 'action.yml' $metadata.Name | Should -Be 'Test Action YML' } } } |