functions/_Get-GistMapData.Tests.ps1

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

Describe "_Get-GistMapData" {

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

        $sut = "$PSScriptRoot\_Get-GistMapData.ps1"
        . $sut
    }

    BeforeEach {
        # Reset cache between tests to avoid cross-test interference
        $script:cachedMap = $null
        $script:cacheTimestamp = [datetime]::MinValue
    }

    Context "Golden Path" {
        BeforeAll {
            Mock Test-Path { return $true }
            Mock Get-Content { return "group1:`n - name: gist1" }
            Mock ConvertFrom-Yaml { return @{ 'group1' = @(@{ name = 'gist1'; source = 'https://example.com'; ref = 'main'; includePaths = @('**/*') }) } }
        }

        It "Should return parsed hashtable when file exists" {
            $result = _Get-GistMapData -ScriptRoot '/fake/path' -NoCache

            $result | Should -BeOfType [hashtable]
            $result.ContainsKey('group1') | Should -Be $true
            $result['group1'][0].name | Should -Be 'gist1'
        }

        It "Should call Get-Content with correct path" {
            # Ensure path comparison is cross-platform compatible, since the 'Join-Path' inside '_Get-GistMapData' will change the slashes
            $fakePathRoot = Join-Path '/fake' 'path'
            _Get-GistMapData -ScriptRoot $fakePathRoot -NoCache

            $fakeFullPath = Join-Path $fakePathRoot 'gist-map.yml'
            Should -Invoke Get-Content -ParameterFilter { $Path -eq $fakeFullPath }
        }

        It "Should pipe content to ConvertFrom-Yaml" {
            _Get-GistMapData -ScriptRoot '/fake/path' -NoCache

            Should -Invoke ConvertFrom-Yaml -Times 1
        }
    }

    Context "File Not Found" {
        BeforeAll {
            Mock Test-Path { return $false }
            Mock Get-Content { }
            Mock ConvertFrom-Yaml { }
        }

        It "Should return null when gist-map.yml doesn't exist" {
            $result = _Get-GistMapData -ScriptRoot '/nonexistent/path' -NoCache

            $result | Should -BeNullOrEmpty
        }

        It "Should not attempt to read file when it doesn't exist" {
            _Get-GistMapData -ScriptRoot '/nonexistent/path' -NoCache

            Should -Invoke Get-Content -Times 0
            Should -Invoke ConvertFrom-Yaml -Times 0
        }
    }

    Context "Schema Validation" {
        BeforeAll {
            Mock Test-Path { return $true }
            Mock Get-Content { return "yaml content" }
        }

        It "Should throw when gist entry is missing a required field" {
            Mock ConvertFrom-Yaml {
                return @{
                    'test-group' = @(
                        @{
                            name = 'broken-gist'
                            ref = 'main'
                            includePaths = @('**/*')
                            # 'source' is missing
                        }
                    )
                }
            }

            { _Get-GistMapData -ScriptRoot '/fake/path' -NoCache } | Should -Throw "*missing required field: source*"
        }

        It "Should accept valid gist entries without error" {
            Mock ConvertFrom-Yaml {
                return @{
                    'test-group' = @(
                        @{
                            name = 'valid-gist'
                            source = 'https://github.com/org/repo.git'
                            ref = 'main'
                            includePaths = @('**/*')
                        }
                    )
                }
            }

            { _Get-GistMapData -ScriptRoot '/fake/path' -NoCache } | Should -Not -Throw
        }
    }

    Context "Path Construction" {
        BeforeAll {
            $testScriptRoot = Join-Path $TestDrive 'module' 'functions'
            $expectedMapPath = Join-Path $testScriptRoot 'gist-map.yml'

            Mock Test-Path { return $true } -ParameterFilter { $Path -eq $expectedMapPath }
            Mock Test-Path { return $false }
            Mock Get-Content { return "yaml content" }
            Mock ConvertFrom-Yaml { return @{ 'group1' = @(@{ name = 'gist1'; source = 'https://example.com'; ref = 'main'; includePaths = @('**/*') }) } }
        }

        It "Should build correct path from ScriptRoot using Join-Path" {
            $testScriptRoot = Join-Path $TestDrive 'module' 'functions'
            $expectedMapPath = Join-Path $testScriptRoot 'gist-map.yml'

            _Get-GistMapData -ScriptRoot $testScriptRoot -NoCache

            Should -Invoke Test-Path -ParameterFilter { $Path -eq $expectedMapPath }
        }
    }

    Context "Caching" {
        BeforeAll {
            Mock Test-Path { return $true }
            Mock Get-Content { return "yaml content" }
            Mock ConvertFrom-Yaml {
                return @{
                    'group1' = @(
                        @{ name = 'gist1'; source = 'https://example.com'; ref = 'main'; includePaths = @('**/*') }
                    )
                }
            }
        }

        It "Should return cached data on subsequent calls within TTL" {
            _Get-GistMapData -ScriptRoot '/fake/path'
            _Get-GistMapData -ScriptRoot '/fake/path'

            Should -Invoke Get-Content -Times 1
        }

        It "Should re-read file when -NoCache is specified" {
            _Get-GistMapData -ScriptRoot '/fake/path'
            _Get-GistMapData -ScriptRoot '/fake/path' -NoCache

            Should -Invoke Get-Content -Times 2
        }
    }
}