functions/_Resolve-GistMapConfig.Tests.ps1

# <copyright file="_Resolve-GistMapConfig.Tests.ps1" company="Endjin Limited">
# Copyright (c) Endjin Limited. All rights reserved.
# </copyright>

Describe "_Resolve-GistMapConfig" {

    BeforeAll {
        # Define stubs for external module functions to avoid dependency and ensure mocking works
        function ConvertFrom-Yaml { param([switch]$Ordered) }

        # Define the module-level variables that _Resolve-GistMapConfig references
        $script:DefaultGistMapUrl = 'https://raw.githubusercontent.com/endjin/endjin-gists/refs/heads/main/module/gist-map.yml'
        $script:DefaultGistMapGitSource = @{
            repo = 'endjin/endjin-gists'
            url  = 'https://github.com/endjin/endjin-gists.git'
            ref  = 'main'
            path = 'module/gist-map.yml'
        }

        # Import dependencies to make them available for mocking
        . $PSScriptRoot/_Get-GistMapData.ps1
        . $PSScriptRoot/_Get-RemoteGistMap.ps1

        $sut = "$PSScriptRoot/_Resolve-GistMapConfig.ps1"
        . $sut

        $script:mockGistMap = @{
            'test-group' = @(
                @{
                    name         = 'test-gist'
                    source       = 'https://github.com/org/repo.git'
                    ref          = 'main'
                    includePaths = @('path/**/*')
                }
            )
        }

        # Create a real temp file so GistMapPath validation passes
        $script:dummyGistMapPath = Join-Path $TestDrive 'gist-map.yml'
        Set-Content -Path $script:dummyGistMapPath -Value 'placeholder'
    }

    Context "GistMapPath — local file loading" {
        BeforeAll {
            Mock Get-Content { return "yaml content" }
            Mock ConvertFrom-Yaml { return $script:mockGistMap }
        }

        It "Should return a PSCustomObject with GistMap populated" {
            $result = _Resolve-GistMapConfig -GistMapPath $script:dummyGistMapPath

            $result | Should -BeOfType [PSCustomObject]
            $result.GistMap | Should -Not -BeNullOrEmpty
        }

        It "Should set EffectiveUrl to null when GistMapPath is supplied" {
            $result = _Resolve-GistMapConfig -GistMapPath $script:dummyGistMapPath

            $result.EffectiveUrl | Should -BeNullOrEmpty
        }

        It "Should set EffectiveGitSource to null when GistMapPath is supplied" {
            $result = _Resolve-GistMapConfig -GistMapPath $script:dummyGistMapPath

            $result.EffectiveGitSource | Should -BeNullOrEmpty
        }

        It "Should read from the correct file path" {
            $customPath = Join-Path $TestDrive 'custom-gist-map.yml'
            Set-Content -Path $customPath -Value 'placeholder'

            _Resolve-GistMapConfig -GistMapPath $customPath

            Should -Invoke Get-Content -Times 1 -ParameterFilter { $Path -eq $customPath }
        }

        It "Should throw when GistMapPath does not exist" {
            { _Resolve-GistMapConfig -GistMapPath '/nonexistent/path/gist-map.yml' } |
                Should -Throw "*does not exist*"
        }
    }

    Context "GistMapRepo — git source" {
        It "Should not return an HTTP URL when specifying GistMapRepo, GistMapRepoRef, and GistMapRepoPath" {
            Mock _Get-GistMapData { return $script:mockGistMap }

            $result = _Resolve-GistMapConfig -GistMapRepo 'myorg/my-gists' -GistMapRepoRef 'v2.0' -GistMapRepoPath 'config/gist-map.yml'

            $result.EffectiveUrl | Should -Be $null
        }

        It "Should construct the correct git source from GistMapRepo, GistMapRepoRef, and GistMapRepoPath" {
            Mock _Get-GistMapData { return $script:mockGistMap }

            $result = _Resolve-GistMapConfig -GistMapRepo 'myorg/my-gists' -GistMapRepoRef 'v2.0' -GistMapRepoPath 'config/gist-map.yml'

            $result.EffectiveUrl | Should -Be $null
            $result.EffectiveGitSource.url  | Should -Be 'https://github.com/myorg/my-gists.git'
            $result.EffectiveGitSource.ref  | Should -Be 'v2.0'
            $result.EffectiveGitSource.path | Should -Be 'config/gist-map.yml'
        }

        It "Should pass the derived git source details to _Get-GistMapData" {
            Mock _Get-GistMapData { return $script:mockGistMap }

            _Resolve-GistMapConfig -GistMapRepo 'myorg/my-gists' -GistMapRepoRef 'v2.0' -GistMapRepoPath 'config/gist-map.yml'

            Should -Invoke _Get-GistMapData -Times 1 -ParameterFilter {
                $GistMapGitSource.url  -eq 'https://github.com/myorg/my-gists.git' -and
                $GistMapGitSource.ref  -eq 'v2.0' -and
                $GistMapGitSource.path -eq 'config/gist-map.yml'
            }
        }

        It "Should use default ref 'main' and path 'module/gist-map.yml' when only GistMapRepo is specified" {
            Mock _Get-GistMapData { return $script:mockGistMap }

            $result = _Resolve-GistMapConfig -GistMapRepo 'myorg/my-gists'

            $result.EffectiveUrl | Should -Be $null
            $result.EffectiveGitSource.ref  | Should -Be 'main'
            $result.EffectiveGitSource.path | Should -Be 'module/gist-map.yml'
        }
    }

    Context "Default GistMapUrl — no overrides" {
        It "Should use the supplied GistMapUrl and DefaultGistMapGitSource when GistMapRepo is not specified" {
            Mock _Get-GistMapData { return $script:mockGistMap }

            _Resolve-GistMapConfig -GistMapUrl $script:DefaultGistMapUrl

            Should -Invoke _Get-GistMapData -Times 1 -ParameterFilter {
                $GistMapUrl -eq $script:DefaultGistMapUrl -and
                $GistMapGitSource.url  -eq 'https://github.com/endjin/endjin-gists.git' -and
                $GistMapGitSource.ref  -eq 'main' -and
                $GistMapGitSource.path -eq 'module/gist-map.yml'
            }
        }
    }

    Context "GistMapRepoRef override without GistMapRepo" {
        It "Should override only the ref when GistMapRepoRef is specified without GistMapRepo" {
            Mock _Get-GistMapData { return $script:mockGistMap }

            $result = _Resolve-GistMapConfig -GistMapRepoRef 'v2.0'

            $result.EffectiveGitSource.ref  | Should -Be 'v2.0'
            $result.EffectiveGitSource.path | Should -Be 'module/gist-map.yml'
            $result.EffectiveGitSource.url  | Should -Be 'https://github.com/endjin/endjin-gists.git'
            $result.EffectiveUrl | Should -Be $null
        }

        It "Should override only the path when GistMapRepoPath is specified without GistMapRepo" {
            Mock _Get-GistMapData { return $script:mockGistMap }

            $result = _Resolve-GistMapConfig -GistMapRepoPath 'custom/path.yml'

            $result.EffectiveGitSource.path | Should -Be 'custom/path.yml'
            $result.EffectiveGitSource.ref  | Should -Be 'main'
            $result.EffectiveGitSource.url  | Should -Be 'https://github.com/endjin/endjin-gists.git'
            $result.EffectiveUrl | Should -Be $null
        }

        It "Should override both ref and path when both are specified without GistMapRepo" {
            Mock _Get-GistMapData { return $script:mockGistMap }

            $result = _Resolve-GistMapConfig -GistMapRepoRef 'v2.0' -GistMapRepoPath 'custom/path.yml'

            $result.EffectiveGitSource.ref  | Should -Be 'v2.0'
            $result.EffectiveGitSource.path | Should -Be 'custom/path.yml'
            $result.EffectiveGitSource.url  | Should -Be 'https://github.com/endjin/endjin-gists.git'
            $result.EffectiveUrl | Should -Be $null
        }
    }

    Context "GistMapRepo with partial ref/path overrides" {
        It "Should default path to 'gist-map.yml' when GistMapRepo and GistMapRepoRef are specified without GistMapRepoPath" {
            Mock _Get-GistMapData { return $script:mockGistMap }

            $result = _Resolve-GistMapConfig -GistMapRepo 'myorg/my-gists' -GistMapRepoRef 'v2.0'

            $result.EffectiveGitSource.ref  | Should -Be 'v2.0'
            $result.EffectiveGitSource.path | Should -Be 'module/gist-map.yml'
            $result.EffectiveUrl | Should -Be $null
        }

        It "Should default ref to 'main' when GistMapRepo and GistMapRepoPath are specified without GistMapRepoRef" {
            Mock _Get-GistMapData { return $script:mockGistMap }

            $result = _Resolve-GistMapConfig -GistMapRepo 'myorg/my-gists' -GistMapRepoPath 'custom/path.yml'

            $result.EffectiveGitSource.ref  | Should -Be 'main'
            $result.EffectiveGitSource.path | Should -Be 'custom/path.yml'
            $result.EffectiveUrl | Should -Be $null
        }
    }

    Context "Remote load failure" {
        It "Should return a PSCustomObject with null GistMap when _Get-GistMapData returns null" {
            Mock _Get-GistMapData { return $null }

            $result = _Resolve-GistMapConfig -GistMapUrl $script:DefaultGistMapUrl

            $result.GistMap | Should -BeNullOrEmpty
        }

        It "Should still populate EffectiveUrl and EffectiveGitSource even on remote failure" {
            Mock _Get-GistMapData { return $null }

            $result = _Resolve-GistMapConfig -GistMapRepo 'myorg/my-gists'

            $result.EffectiveGitSource | Should -Not -BeNullOrEmpty
            $result.EffectiveUrl | Should -Be $null
        }
    }

    Context "NoCache passthrough" {
        It "Should forward the NoCache switch to _Get-GistMapData" {
            Mock _Get-GistMapData { return $script:mockGistMap }

            _Resolve-GistMapConfig -GistMapUrl $script:DefaultGistMapUrl -NoCache

            Should -Invoke _Get-GistMapData -Times 1 -ParameterFilter { $NoCache -eq $true }
        }

        It "Should not set NoCache when switch is absent" {
            Mock _Get-GistMapData { return $script:mockGistMap }

            _Resolve-GistMapConfig -GistMapUrl $script:DefaultGistMapUrl

            Should -Invoke _Get-GistMapData -Times 1 -ParameterFilter { $NoCache -eq $false }
        }
    }

    Context "Return object structure" {
        It "Should always return an object with GistMap, EffectiveUrl, and EffectiveGitSource properties" {
            Mock Get-Content { return "yaml content" }
            Mock ConvertFrom-Yaml { return $script:mockGistMap }

            $result = _Resolve-GistMapConfig -GistMapPath $script:dummyGistMapPath

            $result.PSObject.Properties.Name | Should -Contain 'GistMap'
            $result.PSObject.Properties.Name | Should -Contain 'EffectiveUrl'
            $result.PSObject.Properties.Name | Should -Contain 'EffectiveGitSource'
        }

        It "Should always return an object with all three properties even for remote calls" {
            Mock _Get-GistMapData { return $script:mockGistMap }

            $result = _Resolve-GistMapConfig -GistMapUrl $script:DefaultGistMapUrl

            $result.PSObject.Properties.Name | Should -Contain 'GistMap'
            $result.PSObject.Properties.Name | Should -Contain 'EffectiveUrl'
            $result.PSObject.Properties.Name | Should -Contain 'EffectiveGitSource'
        }
    }
}