Tests/GenXdev.Helpers/Get-LLMJsonOutput.Tests.ps1

Pester\Describe 'Get-LLMJsonOutput' {

    Pester\It 'Should return clean JSON unchanged' {
        $inputStr = '{"name":"test","value":42}'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly '{"name":"test","value":42}'
    }

    Pester\It 'Should strip markdown fences from JSON object' {
        $jsonFence = '```json'
        $fence = '```'
        $inputStr = "${jsonFence}`n{`"result`": `"hello`"}`n${fence}"
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly '{"result": "hello"}'
    }

    Pester\It 'Should extract JSON object from surrounding commentary' {
        $inputStr = 'Here is the answer: {"key": "value"} Hope that helps.'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly '{"key": "value"}'
    }

    Pester\It 'Should extract JSON array from surrounding text' {
        $inputStr = 'Results: ["a", "b", "c"] done.'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly '["a", "b", "c"]'
    }

    Pester\It 'Should extract nested JSON object' {
        $inputStr = 'Output: {"outer":{"inner":{"key":"val"}}} end'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly '{"outer":{"inner":{"key":"val"}}}'
    }

    Pester\It 'Should use schema to guide object extraction' {
        $schema = @{
            type        = 'json_schema'
            json_schema = @{
                name   = 'response'
                strict = $true
                schema = @{
                    type       = 'object'
                    properties = @{
                        explanation      = @{ type = 'string' }
                        suggestedCmdline = @{ type = 'string' }
                    }
                    required   = @('explanation', 'suggestedCmdline')
                }
            }
        } | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 10 -Compress

        $inputStr = 'Some text here {"explanation":"do stuff","suggestedCmdline":"Get-Process"} trailing'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr -ResponseFormat $schema
        $result | Pester\Should -BeExactly '{"explanation":"do stuff","suggestedCmdline":"Get-Process"}'
    }

    Pester\It 'Should use schema to guide array extraction' {
        $schema = @{
            type        = 'json_schema'
            json_schema = @{
                name   = 'response'
                strict = $true
                schema = @{
                    type  = 'array'
                    items = @{ type = 'string' }
                }
            }
        } | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 10 -Compress

        $inputStr = 'List: ["item1", "item2"] end'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr -ResponseFormat $schema
        $result | Pester\Should -BeExactly '["item1", "item2"]'
    }

    Pester\It 'Should extract quoted string via heuristic' {
        $inputStr = 'The value is "extracted-string" in the middle'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly '"extracted-string"'
    }

    Pester\It 'Should extract number via heuristic' {
        $inputStr = 'The count is 42 items total.'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly '42'
    }

    Pester\It 'Should extract negative number' {
        $inputStr = 'Temperature is -15.5 degrees'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly '-15.5'
    }

    Pester\It 'Should extract boolean true' {
        $inputStr = 'enabled: true, configured: yes'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly 'true'
    }

    Pester\It 'Should extract boolean false' {
        $inputStr = 'The flag is false in the config'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly 'false'
    }

    Pester\It 'Should extract date-time via schema' {
        $schema = @{
            type        = 'json_schema'
            json_schema = @{
                name   = 'response'
                strict = $true
                schema = @{
                    type   = 'string'
                    format = 'date-time'
                }
            }
        } | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 10 -Compress

        $inputStr = 'Timestamp: 2026-07-20T14:30:00Z some text'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr -ResponseFormat $schema
        $result | Pester\Should -BeExactly '"2026-07-20T14:30:00Z"'
    }

    Pester\It 'Should return original text if no JSON found' {
        $inputStr = 'This is just plain text with nothing structured.'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly 'This is just plain text with nothing structured.'
    }

    Pester\It 'Should handle JSON with markdown fences and surrounding text' {
        $jsonFence = '```json'
        $fence = '```'
        $inputStr = "Sure! here's the command:`n${jsonFence}`n{`"explanation`": `"list files`", `"suggestedCmdline`": `"Get-ChildItem`"}`n${fence}"
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly '{"explanation": "list files", "suggestedCmdline": "Get-ChildItem"}'
    }

    Pester\It 'Should prefer object over array when both present' {
        $inputStr = 'Array first: [1,2,3] then object: {"a":1}'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly '{"a":1}'
    }

    Pester\It 'Should handle scientific notation number' {
        $inputStr = 'Result: 1.5e10 units'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly '1.5e10'
    }

    Pester\It 'Should extract number when schema type is integer' {
        $schema = @{
            type        = 'json_schema'
            json_schema = @{
                name   = 'response'
                strict = $true
                schema = @{
                    type = 'integer'
                }
            }
        } | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 10 -Compress

        $inputStr = 'Count: 99 results'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr -ResponseFormat $schema
        $result | Pester\Should -BeExactly '99'
    }

    Pester\It 'Should extract boolean via schema' {
        $schema = @{
            type        = 'json_schema'
            json_schema = @{
                name   = 'response'
                strict = $true
                schema = @{
                    type = 'boolean'
                }
            }
        } | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 10 -Compress

        $inputStr = 'Success: true, everything worked.'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr -ResponseFormat $schema
        $result | Pester\Should -BeExactly 'true'
    }

    Pester\It 'Should handle empty object' {
        $inputStr = 'Data: {} end'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly '{}'
    }

    Pester\It 'Should handle empty array' {
        $inputStr = 'List: [] done'
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr
        $result | Pester\Should -BeExactly '[]'
    }

    Pester\It 'Should strip markdown fences when schema is provided' {
        $schema = @{
            type        = 'json_schema'
            json_schema = @{
                name   = 'response'
                strict = $true
                schema = @{
                    type       = 'object'
                    properties = @{
                        explanation      = @{ type = 'string' }
                        suggestedCmdline = @{ type = 'string' }
                    }
                    required   = @('explanation', 'suggestedCmdline')
                }
            }
        } | Microsoft.PowerShell.Utility\ConvertTo-Json -Depth 10 -Compress

        $jsonFence = '```json'
        $fence = '```'
        $inputStr = "${jsonFence}`n{`"explanation`": `"list files`", `"suggestedCmdline`": `"Get-ChildItem`"}`n${fence}"
        $result = GenXdev\Get-LLMJsonOutput -Text $inputStr -ResponseFormat $schema
        $result | Pester\Should -BeExactly '{"explanation": "list files", "suggestedCmdline": "Get-ChildItem"}'
    }

}