functions/Get-EndjinGists.Tests.ps1

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

Describe "Get-EndjinGists" {

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

        $sut = "$PSScriptRoot\Get-EndjinGists.ps1"
        . $sut

        # Mock Data
        # Create a real temp file so ValidateScript({Test-Path $_ -PathType Leaf}) passes
        $script:dummyGistMapPath = Join-Path $TestDrive 'gist-map.yml'
        Set-Content -Path $script:dummyGistMapPath -Value 'placeholder'

        $script:mockGistMap = @{
            'llm-kb' = @(
                @{
                    name = 'dotnet'
                    description = 'LLM knowledge base for .NET development'
                    source = 'https://github.com/endjin/dotnet-llm-knowledge-base.git'
                    ref = 'main'
                    includePaths = @('knowledge-base/**/*')
                }
                @{
                    name = 'powershell'
                    description = 'LLM knowledge base for PowerShell development'
                    source = 'https://github.com/endjin/powershell-llm-knowledge-base.git'
                    ref = 'main'
                    includePaths = @('knowledge-base/**/*')
                }
            )
            'devcontainer' = @(
                @{
                    name = 'ai-agent'
                    description = 'Development container for AI agents'
                    source = 'https://github.com/endjin/ai-agent-devcontainer.git'
                    ref = 'main'
                    includePaths = @('.devcontainer/**/*')
                }
            )
        }
    }

    Context "Machine Output (PSCustomObject)" {
        BeforeAll {
            Mock Get-Content { return "yaml content" }
            Mock ConvertFrom-Yaml { return $script:mockGistMap }
        }

        It "Should return an array of PSCustomObjects" {
            $result = Get-EndjinGists -GistMapPath $script:dummyGistMapPath -PassThru

            $result | Should -BeOfType [PSCustomObject]
            $result.Count | Should -Be 3
        }

        It "Should include correct properties on each object" {
            $result = Get-EndjinGists -GistMapPath $script:dummyGistMapPath -PassThru

            $result[0].PSObject.Properties.Name | Should -Contain 'Group'
            $result[0].PSObject.Properties.Name | Should -Contain 'Name'
            $result[0].PSObject.Properties.Name | Should -Contain 'Description'
            $result[0].PSObject.Properties.Name | Should -Contain 'Source'
            $result[0].PSObject.Properties.Name | Should -Contain 'Ref'
        }

        It "Should return correct data for each gist" {
            $result = Get-EndjinGists -GistMapPath $script:dummyGistMapPath -PassThru

            $dotnetGist = $result | Where-Object { $_.Name -eq 'dotnet' }
            $dotnetGist.Group | Should -Be 'llm-kb'
            $dotnetGist.Description | Should -Be 'LLM knowledge base for .NET development'
            $dotnetGist.Source | Should -Be 'https://github.com/endjin/dotnet-llm-knowledge-base.git'
            $dotnetGist.Ref | Should -Be 'main'
        }

        It "Should support pipeline filtering" {
            $result = Get-EndjinGists -GistMapPath $script:dummyGistMapPath -PassThru | Where-Object { $_.Group -eq 'devcontainer' }

            $result.Count | Should -Be 1
            $result.Name | Should -Be 'ai-agent'
        }
    }

    Context "Default Output (Summary)" {
        BeforeAll {
            Mock Get-Content { return "yaml content" }
            Mock ConvertFrom-Yaml { return $script:mockGistMap }
        }

        It "Should output text when -PassThru is not specified" {
            $result = Get-EndjinGists -GistMapPath $script:dummyGistMapPath

            $result | Should -BeOfType [string]
        }

        It "Should include 'Available Gists:' header" {
            $result = Get-EndjinGists -GistMapPath $script:dummyGistMapPath

            $result | Should -Contain 'Available Gists:'
        }

        It "Should include group names in output" {
            $result = Get-EndjinGists -GistMapPath $script:dummyGistMapPath

            $result | Should -Contain '🗂️ llm-kb'
            $result | Should -Contain '🗂️ devcontainer'
        }

        It "Should include gist names and descriptions under groups" {
            $result = Get-EndjinGists -GistMapPath $script:dummyGistMapPath

            $result | Should -Contain ' 📑 dotnet - LLM knowledge base for .NET development'
            $result | Should -Contain ' 📑 powershell - LLM knowledge base for PowerShell development'
            $result | Should -Contain ' 📑 ai-agent - Development container for AI agents'
        }
    }

    Context "Custom GistMapPath" {
        It "Should read from the specified path" {
            $customPath = Join-Path $TestDrive 'custom-gist-map.yml'
            Set-Content -Path $customPath -Value 'placeholder'
            Mock Get-Content { return "yaml content" } -Verifiable
            Mock ConvertFrom-Yaml { return $script:mockGistMap }

            Get-EndjinGists -GistMapPath $customPath

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

    Context "Gists Without Description" {
        BeforeAll {
            Mock Get-Content { return "yaml content" }
            Mock ConvertFrom-Yaml {
                return @{
                    'test-group' = @(
                        @{
                            name = 'no-description-gist'
                            source = 'https://github.com/example/repo.git'
                            ref = 'main'
                        }
                    )
                }
            }
        }

        It "Should handle gists without description property" {
            $result = Get-EndjinGists -GistMapPath $script:dummyGistMapPath -PassThru

            $result[0].Description | Should -BeNullOrEmpty
        }

        It "Should output gist name without description suffix in Summary" {
            $result = Get-EndjinGists -GistMapPath $script:dummyGistMapPath

            $result | Should -Contain ' 📑 no-description-gist'
        }
    }

    Context "Input Validation" {
        It "Should throw when GistMapPath does not exist" {
            { Get-EndjinGists -GistMapPath '/nonexistent/path/gist-map.yml' } | Should -Throw "*Cannot validate argument on parameter 'GistMapPath'*"
        }
    }

    Context "Empty Gist Map" {
        BeforeAll {
            Mock Get-Content { return "yaml content" }
        }

        It "Should return empty array when gist map is empty" {
            Mock ConvertFrom-Yaml { return @{} }

            $result = Get-EndjinGists -GistMapPath $script:dummyGistMapPath -PassThru

            $result.Count | Should -Be 0
        }

        It "Should output only header when gist map is empty without -PassThru" {
            Mock ConvertFrom-Yaml { return @{} }

            $result = Get-EndjinGists -GistMapPath $script:dummyGistMapPath

            $result | Should -Contain 'Available Gists:'
        }
    }

    Context "Group Filtering" {
        BeforeAll {
            Mock Get-Content { return "yaml content" }
            Mock ConvertFrom-Yaml { return $script:mockGistMap }
        }

        It "Should return only gists in the specified group with -PassThru" {
            $result = Get-EndjinGists -Group 'devcontainer' -GistMapPath $script:dummyGistMapPath -PassThru

            $result.Count | Should -Be 1
            $result[0].Group | Should -Be 'devcontainer'
            $result[0].Name | Should -Be 'ai-agent'
        }

        It "Should display only the specified group in text output" {
            $result = Get-EndjinGists -Group 'devcontainer' -GistMapPath $script:dummyGistMapPath

            $result | Should -Contain '🗂️ devcontainer'
            $result | Should -Not -Contain '🗂️ llm-kb'
        }

        It "Should support positional Group parameter" {
            $result = Get-EndjinGists 'llm-kb' -GistMapPath $script:dummyGistMapPath -PassThru

            $result.Count | Should -Be 2
            $result | ForEach-Object { $_.Group | Should -Be 'llm-kb' }
        }

        It "Should warn when Group is not found" {
            $result = Get-EndjinGists -Group 'nonexistent' -GistMapPath $script:dummyGistMapPath -PassThru 3>&1

            $result | Where-Object { $_ -is [System.Management.Automation.WarningRecord] } |
                ForEach-Object { $_.Message | Should -BeLike "*Group 'nonexistent' not found*" }
        }
    }

    Context "Group and Name Filtering" {
        BeforeAll {
            Mock Get-Content { return "yaml content" }
            Mock ConvertFrom-Yaml { return $script:mockGistMap }
        }

        It "Should return only the named gist with -PassThru" {
            $result = Get-EndjinGists -Group 'llm-kb' -Name 'dotnet' -GistMapPath $script:dummyGistMapPath -PassThru

            $result.Count | Should -Be 1
            $result[0].Group | Should -Be 'llm-kb'
            $result[0].Name | Should -Be 'dotnet'
        }

        It "Should display only the named gist in text output" {
            $result = Get-EndjinGists -Group 'devcontainer' -Name 'ai-agent' -GistMapPath $script:dummyGistMapPath

            $result | Should -Contain '🗂️ devcontainer'
            $result | Should -Contain ' 📑 ai-agent - Development container for AI agents'
            $result | Should -Not -Contain '🗂️ llm-kb'
        }

        It "Should support positional Group and Name parameters" {
            $result = Get-EndjinGists 'devcontainer' 'ai-agent' -GistMapPath $script:dummyGistMapPath -PassThru

            $result.Count | Should -Be 1
            $result[0].Name | Should -Be 'ai-agent'
        }

        It "Should warn when Name is not found in Group" {
            $result = Get-EndjinGists -Group 'devcontainer' -Name 'nonexistent' -GistMapPath $script:dummyGistMapPath -PassThru 3>&1

            $result | Where-Object { $_ -is [System.Management.Automation.WarningRecord] } |
                ForEach-Object { $_.Message | Should -BeLike "*Gist 'nonexistent' not found in group 'devcontainer'*" }
        }
    }

    Context "Name Without Group" {
        BeforeAll {
            Mock Get-Content { return "yaml content" }
            Mock ConvertFrom-Yaml { return $script:mockGistMap }
        }

        It "Should warn when Name is specified without Group" {
            $result = Get-EndjinGists -Name 'dotnet' -GistMapPath $script:dummyGistMapPath -PassThru 3>&1

            $result | Where-Object { $_ -is [System.Management.Automation.WarningRecord] } |
                ForEach-Object { $_.Message | Should -BeLike "*-Name parameter requires -Group*" }
        }
    }
}