Tests/Get-DadJoke.Tests.ps1

Describe "Get-DadJoke" {

    BeforeAll {
        function script:Test-JokeAPI {
            param($Uri)
            return $true
        }

        function script:Request-DadJoke {
            param($Uri)
            return [PSCustomObject]@{
                Setup = "Default mock setup"
                Punchline = "Default mock punchline"
            }
        }

        Mock Test-JokeAPI { return $true } -ParameterFilter { $Uri -eq 'https://official-joke-api.appspot.com/random_joke' }
        Mock Request-DadJoke {
            return [PSCustomObject]@{
                Setup = "Test setup"
                Punchline = "Test punchline"
            }
        } -ParameterFilter { $Uri -eq 'https://official-joke-api.appspot.com/random_joke' }

        $ValidUri = "https://official-joke-api.appspot.com/random_joke"
    }

    Context "Function Dependencies" {
        It "Depends on Test-JokeAPI function" {
            Get-DadJoke

            Assert-MockCalled Test-JokeAPI -Times 1 -ParameterFilter {
                $Uri -eq 'https://official-joke-api.appspot.com/random_joke'
            }
        }

        It "Depends on Request-DadJoke function" {
            Get-DadJoke

            Assert-MockCalled Request-DadJoke -Times 1 -ParameterFilter {
                $Uri -eq 'https://official-joke-api.appspot.com/random_joke'
            }
        }
    }

    Context "Successful API Responses" {
        It "Returns setup and punchline as separate strings when API is available" {
            $testSetup = "Why don't scientists trust atoms?"
            $testPunchline = "Because they make up everything!"

            Mock Request-DadJoke {
                return [PSCustomObject]@{
                    Setup = $testSetup
                    Punchline = $testPunchline
                }
            } -ParameterFilter { $Uri -eq $ValidUri }

            $result = Get-DadJoke

            $result | Should -BeOfType [System.String]
            $result.Count | Should -Be 2
            $result[0] | Should -Be $testSetup
            $result[1] | Should -Be $testPunchline
        }

        It "Uses the hardcoded API endpoint" {
            Get-DadJoke

            Assert-MockCalled Test-JokeAPI -ParameterFilter {
                $Uri -eq 'https://official-joke-api.appspot.com/random_joke'
            } -Times 1

            Assert-MockCalled Request-DadJoke -ParameterFilter {
                $Uri -eq 'https://official-joke-api.appspot.com/random_joke'
            } -Times 1
        }

        It "Outputs the joke exactly as received from Request-DadJoke" {
            $testSetup = "What do you call a fake noodle?"
            $testPunchline = "An impasta!"

            Mock Request-DadJoke {
                return [PSCustomObject]@{
                    Setup = $testSetup
                    Punchline = $testPunchline
                }
            } -ParameterFilter { $Uri -eq $ValidUri }

            $result = Get-DadJoke

            $result[0] | Should -Be $testSetup
            $result[1] | Should -Be $testPunchline
        }

        It "Handles jokes with special characters" {
            $testSetup = "What's E.T. short for? (dad joke version)"
            $testPunchline = "He's only got little legs!"

            Mock Request-DadJoke {
                return [PSCustomObject]@{
                    Setup = $testSetup
                    Punchline = $testPunchline
                }
            } -ParameterFilter { $Uri -eq $ValidUri }

            $result = Get-DadJoke

            $result[0] | Should -Be $testSetup
            $result[1] | Should -Be $testPunchline
        }
    }

    Context "Error Handling" {
        It "Throws error when Test-JokeAPI returns false" {
            Mock Test-JokeAPI { return $false } -ParameterFilter { $Uri -eq $ValidUri }

            { Get-DadJoke } | Should -Throw "Test-JokeAPI error: API endpoint is unavailable or returned non-200 status code"
        }

        It "Does not call Request-DadJoke when Test-JokeAPI returns false" {
            Mock Test-JokeAPI { return $false } -ParameterFilter { $Uri -eq $ValidUri }
            Mock Request-DadJoke { } -ParameterFilter { $Uri -eq $ValidUri }

            try { Get-DadJoke } catch {}

            Assert-MockCalled Request-DadJoke -Times 0
        }

        It "Throws error when Request-DadJoke throws an exception" {
            $errorMessage = "API is currently unavailable"
            Mock Request-DadJoke {
                throw $errorMessage
            } -ParameterFilter { $Uri -eq $ValidUri }

            { Get-DadJoke } | Should -Throw "Request-Dadjoke Error: $errorMessage"
        }
    }

    Context "Output Format" {
        It "Returns exactly 2 lines of output on success" {
            Mock Request-DadJoke {
                return [PSCustomObject]@{
                    Setup = "Line 1"
                    Punchline = "Line 2"
                }
            } -ParameterFilter { $Uri -eq $ValidUri }

            $output = Get-DadJoke

            $output.Count | Should -Be 2
        }

        It "Outputs strings, not objects" {
            Mock Request-DadJoke {
                return [PSCustomObject]@{
                    Setup = "Test"
                    Punchline = "Test"
                }
            } -ParameterFilter { $Uri -eq $ValidUri }

            $output = Get-DadJoke

            $output[0] | Should -BeOfType [System.String]
            $output[1] | Should -BeOfType [System.String]
        }


        It "Outputs to the pipeline, not to host" {
            Mock Write-Host {} -ParameterFilter { $InputObject -eq $null } # Should not be called
            Mock Request-DadJoke {
                return [PSCustomObject]@{
                    Setup = "Test"
                    Punchline = "Test"
                }
            } -ParameterFilter { $Uri -eq $ValidUri }

            $output = Get-DadJoke

            Assert-MockCalled Write-Host -Times 0
            $output | Should -Not -BeNullOrEmpty
        }
    }


    Context "Edge Cases" {
        It "Handles empty setup from Request-DadJoke" {
            Mock Request-DadJoke {
                return [PSCustomObject]@{
                    Setup = ""
                    Punchline = "Valid punchline"
                }
            } -ParameterFilter { $Uri -eq $ValidUri }

            $result = Get-DadJoke

            $result[0] | Should -Be ""
            $result[1] | Should -Be "Valid punchline"
        }

        It "Handles empty punchline from Request-DadJoke" {
            Mock Request-DadJoke {
                return [PSCustomObject]@{
                    Setup = "Valid setup"
                    Punchline = ""
                }
            } -ParameterFilter { $Uri -eq $ValidUri }

            $result = Get-DadJoke

            $result[0] | Should -Be "Valid setup"
            $result[1] | Should -Be ""
        }

        It "Handles both setup and punchline being empty" {
            Mock Request-DadJoke {
                return [PSCustomObject]@{
                    Setup = ""
                    Punchline = ""
                }
            } -ParameterFilter { $Uri -eq $ValidUri }

            $result = Get-DadJoke

            $result[0] | Should -Be ""
            $result[1] | Should -Be ""
            $result.Count | Should -Be 2
        }
    }

}