d3ro-voice/server/supabase/tests/content-reporting.edge.integration.ps1
2026-08-29 18:33:45 +09:00

155 lines
6.2 KiB
PowerShell

param(
[string]$SupabaseExe = 'supabase',
[string]$ProjectUrl = 'http://127.0.0.1:55321'
)
$ErrorActionPreference = 'Stop'
$supabaseProjectDirectory = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
$previousErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
$statusEnv = & $SupabaseExe status --workdir $supabaseProjectDirectory -o env 2>$null
$statusExitCode = $LASTEXITCODE
$ErrorActionPreference = $previousErrorActionPreference
if ($statusExitCode -ne 0) {
throw 'D3RO local Supabase stack is not running.'
}
function Get-StatusValue {
param([string]$Name)
$line = $statusEnv | Where-Object { $_ -like ($Name + '=*') }
if (-not $line) { throw "Supabase status did not provide $Name" }
return (($line -split '=', 2)[1]).Trim('"')
}
$anonKey = Get-StatusValue 'ANON_KEY'
$serviceRoleKey = Get-StatusValue 'SERVICE_ROLE_KEY'
$testPassword = 'D3ro-Content-Report-E2E-2026!'
$createdUserId = $null
$createdReportId = $null
$passCount = 0
function Assert-Equal {
param([string]$Name, [object]$Actual, [object]$Expected)
if ($Actual -ne $Expected) {
throw "FAIL $Name expected=[$Expected] actual=[$Actual]"
}
$script:passCount += 1
Write-Host "PASS $Name"
}
function Assert-True {
param([string]$Name, [bool]$Condition)
if (-not $Condition) { throw "FAIL $Name" }
$script:passCount += 1
Write-Host "PASS $Name"
}
function Invoke-JsonRequest {
param(
[ValidateSet('Get', 'Post', 'Delete')][string]$Method,
[string]$Path,
[string]$ApiKey,
[string]$Bearer,
[object]$Body,
[hashtable]$AdditionalHeaders = @{},
[string]$ContentType = 'application/json'
)
$headers = @{ apikey = $ApiKey; Authorization = "Bearer $Bearer" }
foreach ($entry in $AdditionalHeaders.GetEnumerator()) {
$headers[$entry.Key] = $entry.Value
}
$parameters = @{
Uri = $ProjectUrl + $Path
Method = $Method
Headers = $headers
TimeoutSec = 30
SkipHttpErrorCheck = $true
}
if ($null -ne $Body) {
$parameters.ContentType = $ContentType
$parameters.Body = $Body | ConvertTo-Json -Depth 8 -Compress
}
return Invoke-WebRequest @parameters
}
try {
$email = "content-report-$([guid]::NewGuid().ToString('N'))@example.test"
$signup = Invoke-JsonRequest -Method Post -Path '/auth/v1/signup' `
-ApiKey $anonKey -Bearer $anonKey -Body @{ email = $email; password = $testPassword }
Assert-Equal 'signup-status' ([int]$signup.StatusCode) 200
$session = $signup.Content | ConvertFrom-Json
$createdUserId = [string]$session.user.id
$accessToken = [string]$session.access_token
$unauthenticated = Invoke-JsonRequest -Method Post -Path '/functions/v1/content-report' `
-ApiKey $anonKey -Bearer $anonKey -Body @{}
Assert-Equal 'unauthenticated-denied' ([int]$unauthenticated.StatusCode) 401
Assert-Equal 'unauthenticated-no-store' ([string]$unauthenticated.Headers['Cache-Control']) 'no-store'
$wrongContentType = Invoke-JsonRequest -Method Post -Path '/functions/v1/content-report' `
-ApiKey $anonKey -Bearer $accessToken -Body @{} -ContentType 'application/jsonp' `
-AdditionalHeaders @{ 'Idempotency-Key' = [guid]::NewGuid().ToString() }
Assert-Equal 'wrong-content-type-denied' ([int]$wrongContentType.StatusCode) 415
$receiptResponse = Invoke-JsonRequest -Method Post `
-Path '/rest/v1/rpc/issue_content_generation_receipt_v1' `
-ApiKey $serviceRoleKey -Bearer $serviceRoleKey `
-Body @{
p_actor_id = $createdUserId
p_purpose = 'talk_response'
p_model = 'edge-integration-fixture'
}
Assert-Equal 'service-receipt-status' ([int]$receiptResponse.StatusCode) 200
$generationId = [string](($receiptResponse.Content | ConvertFrom-Json).generationId)
Assert-True 'service-receipt-id' (-not [string]::IsNullOrWhiteSpace($generationId))
$idempotencyKey = [guid]::NewGuid().ToString()
$payload = @{
kind = 'ai_output'
source = @{ type = 'talk_response'; generationId = $generationId }
reason = 'privacy'
comment = 'HTTP integration review'
snapshot = 'Reporter-selected generated response evidence'
}
$first = Invoke-JsonRequest -Method Post -Path '/functions/v1/content-report' `
-ApiKey $anonKey -Bearer $accessToken -Body $payload `
-AdditionalHeaders @{ 'Idempotency-Key' = $idempotencyKey }
Assert-Equal 'first-submit-status' ([int]$first.StatusCode) 201
Assert-Equal 'first-submit-no-store' ([string]$first.Headers['Cache-Control']) 'no-store'
$firstBody = $first.Content | ConvertFrom-Json
$createdReportId = [string]$firstBody.reportId
Assert-Equal 'first-submit-idempotent' ([bool]$firstBody.idempotent) $false
$replay = Invoke-JsonRequest -Method Post -Path '/functions/v1/content-report' `
-ApiKey $anonKey -Bearer $accessToken -Body $payload `
-AdditionalHeaders @{ 'Idempotency-Key' = $idempotencyKey }
$replayBody = $replay.Content | ConvertFrom-Json
Assert-Equal 'replay-status' ([int]$replay.StatusCode) 200
Assert-Equal 'replay-idempotent' ([bool]$replayBody.idempotent) $true
Assert-Equal 'replay-same-report' ([string]$replayBody.reportId) $createdReportId
$changedPayload = $payload.Clone()
$changedPayload.snapshot = 'Changed evidence'
$conflict = Invoke-JsonRequest -Method Post -Path '/functions/v1/content-report' `
-ApiKey $anonKey -Bearer $accessToken -Body $changedPayload `
-AdditionalHeaders @{ 'Idempotency-Key' = $idempotencyKey }
Assert-Equal 'idempotency-conflict-status' ([int]$conflict.StatusCode) 409
Assert-True 'idempotency-conflict-code' $conflict.Content.Contains('idempotency_conflict')
$directRead = Invoke-JsonRequest -Method Get -Path '/rest/v1/content_reports?select=id' `
-ApiKey $anonKey -Bearer $accessToken -Body $null
Assert-Equal 'direct-table-read-denied' ([int]$directRead.StatusCode) 403
Write-Host "content-reporting edge integration: $passCount assertions passed"
} finally {
if (-not [string]::IsNullOrWhiteSpace($createdReportId)) {
$null = Invoke-JsonRequest -Method Delete `
-Path "/rest/v1/content_reports?id=eq.$createdReportId" `
-ApiKey $serviceRoleKey -Bearer $serviceRoleKey -Body $null
}
if (-not [string]::IsNullOrWhiteSpace($createdUserId)) {
$null = Invoke-JsonRequest -Method Delete `
-Path "/auth/v1/admin/users/$createdUserId" `
-ApiKey $serviceRoleKey -Bearer $serviceRoleKey -Body $null
}
}