__tests__/Vercel.Tests.ps1
|
BeforeAll { Import-Module "$PSScriptRoot\..\PSAISuite.psd1" -Force } Describe 'Invoke-VercelProvider' { BeforeEach { $global:vercelOriginalGatewayKey = $env:AI_GATEWAY_API_KEY $global:vercelOriginalOidcToken = $env:VERCEL_OIDC_TOKEN $env:AI_GATEWAY_API_KEY = 'test-gateway-key' Remove-Item Env:VERCEL_OIDC_TOKEN -ErrorAction SilentlyContinue $global:vercelRequest = $null Mock -ModuleName PSAISuite Invoke-RestMethod { param($Uri, $Method, $Headers, $Body) [void]($global:vercelRequest = [PSCustomObject]@{ Uri = $Uri Method = $Method Headers = $Headers Body = $Body | ConvertFrom-Json }) [PSCustomObject]@{ choices = @( [PSCustomObject]@{ message = [PSCustomObject]@{ content = 'Vercel response' } } ) } } } AfterEach { if ($null -eq $global:vercelOriginalGatewayKey) { Remove-Item Env:AI_GATEWAY_API_KEY -ErrorAction SilentlyContinue } else { $env:AI_GATEWAY_API_KEY = $global:vercelOriginalGatewayKey } if ($null -eq $global:vercelOriginalOidcToken) { Remove-Item Env:VERCEL_OIDC_TOKEN -ErrorAction SilentlyContinue } else { $env:VERCEL_OIDC_TOKEN = $global:vercelOriginalOidcToken } } It 'sends an OpenAI-compatible request to AI Gateway' { InModuleScope PSAISuite { $global:vercelResult = Invoke-VercelProvider -ModelName 'anthropic/claude-sonnet-4.6' -Messages @(@{ role = 'user'; content = 'Hello' }) } $global:vercelResult | Should -Be 'Vercel response' $global:vercelRequest.Uri | Should -Be 'https://ai-gateway.vercel.sh/v1/chat/completions' $global:vercelRequest.Method | Should -Be 'POST' $global:vercelRequest.Headers.Authorization | Should -Be 'Bearer test-gateway-key' $global:vercelRequest.Body.model | Should -Be 'anthropic/claude-sonnet-4.6' $global:vercelRequest.Body.messages[0].content | Should -Be 'Hello' $global:vercelRequest.Body.stream | Should -BeFalse } It 'uses the OIDC token when an AI Gateway API key is absent' { Remove-Item Env:AI_GATEWAY_API_KEY -ErrorAction SilentlyContinue $env:VERCEL_OIDC_TOKEN = 'test-oidc-token' InModuleScope PSAISuite { Invoke-VercelProvider -ModelName 'openai/gpt-5.6' -Messages @(@{ role = 'user'; content = 'Hello' }) | Out-Null } $global:vercelRequest.Headers.Authorization | Should -Be 'Bearer test-oidc-token' } It 'executes tool calls and sends tool output in the next request' { $global:vercelRequestCount = 0 $global:vercelSecondRequest = $null Mock -ModuleName PSAISuite Invoke-RestMethod { param($Uri, $Method, $Headers, $Body) [void]($global:vercelRequestCount++) $request = $Body | ConvertFrom-Json if ($global:vercelRequestCount -eq 1) { return [PSCustomObject]@{ choices = @( [PSCustomObject]@{ message = [PSCustomObject]@{ role = 'assistant' content = $null tool_calls = @( [PSCustomObject]@{ id = 'call-1' function = [PSCustomObject]@{ name = 'Get-Date' arguments = '{}' } } ) } } ) } } [void]($global:vercelSecondRequest = $request) [PSCustomObject]@{ choices = @( [PSCustomObject]@{ message = [PSCustomObject]@{ content = 'Tool completed' } } ) } } InModuleScope PSAISuite { $global:vercelResult = Invoke-VercelProvider -ModelName 'openai/gpt-5.6' -Messages @(@{ role = 'user'; content = 'Use a tool' }) -MaxIterations 2 } $global:vercelResult | Should -Be 'Tool completed' $global:vercelRequestCount | Should -Be 2 @($global:vercelSecondRequest.messages | Where-Object { $_.role -eq 'tool' }).Count | Should -Be 1 ($global:vercelSecondRequest.messages | Where-Object { $_.role -eq 'tool' }).tool_call_id | Should -Be 'call-1' } It 'stops after the configured number of tool-calling rounds' { Mock -ModuleName PSAISuite Invoke-RestMethod { [PSCustomObject]@{ choices = @( [PSCustomObject]@{ message = [PSCustomObject]@{ content = $null tool_calls = @( [PSCustomObject]@{ id = 'call-loop' function = [PSCustomObject]@{ name = 'Get-Date'; arguments = '{}' } } ) } } ) } } InModuleScope PSAISuite { $global:vercelResult = Invoke-VercelProvider -ModelName 'openai/gpt-5.6' -Messages @(@{ role = 'user'; content = 'Use a tool' }) -MaxIterations 2 } $global:vercelResult | Should -Be 'Maximum iterations reached without completing the response after 2 iterations.' } It 'reports a clear error when no credential is configured' { Remove-Item Env:AI_GATEWAY_API_KEY -ErrorAction SilentlyContinue Remove-Item Env:VERCEL_OIDC_TOKEN -ErrorAction SilentlyContinue $global:vercelError = $null Mock -ModuleName PSAISuite Write-Error { param($Message) $global:vercelError = $Message } InModuleScope PSAISuite { Invoke-VercelProvider -ModelName 'openai/gpt-5.6' -Messages @(@{ role = 'user'; content = 'Hello' }) | Out-Null } $global:vercelError | Should -Match 'AI_GATEWAY_API_KEY or VERCEL_OIDC_TOKEN' } } Describe 'Vercel provider dispatch' { It 'accepts Vercel models and forwards MaxIterations' { $global:vercelDispatch = $null Mock -ModuleName PSAISuite Invoke-VercelProvider { param($ModelName, $Messages, $MaxIterations) [void]($global:vercelDispatch = @{ ModelName = $ModelName MaxIterations = $MaxIterations }) 'dispatch response' } $result = Invoke-ChatCompletion -Messages 'Test prompt' -Model 'vercel:openai/gpt-5.6' -MaxIterations 3 $result | Should -Be 'dispatch response' $global:vercelDispatch.ModelName | Should -Be 'openai/gpt-5.6' $global:vercelDispatch.MaxIterations | Should -Be 3 } } |