chore: establish local platform baseline
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$PSNativeCommandUseErrorActionPreference = $true
|
||||
|
||||
$projectRoot = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location -LiteralPath $projectRoot
|
||||
$apiBase = 'http://localhost:58080'
|
||||
$tokenUrl = 'http://localhost:58180/realms/voice-local/protocol/openid-connect/token'
|
||||
$tenantId = '10000000-0000-0000-0000-000000000010'
|
||||
$householdId = $null
|
||||
|
||||
function Get-LocalAccessToken([string]$Username) {
|
||||
$response = Invoke-RestMethod -Method Post -Uri $tokenUrl `
|
||||
-ContentType 'application/x-www-form-urlencoded' `
|
||||
-Body @{
|
||||
grant_type = 'password'
|
||||
client_id = 'voice-platform-web'
|
||||
username = $Username
|
||||
password = 'LocalOnly123!'
|
||||
scope = 'openid'
|
||||
}
|
||||
return $response.access_token
|
||||
}
|
||||
|
||||
function Invoke-JSONRequest(
|
||||
[string]$Method,
|
||||
[string]$Uri,
|
||||
[string]$Token,
|
||||
[hashtable]$Headers = @{},
|
||||
[object]$Body = $null
|
||||
) {
|
||||
$requestHeaders = @{ Authorization = "Bearer $Token" }
|
||||
foreach ($key in $Headers.Keys) { $requestHeaders[$key] = $Headers[$key] }
|
||||
$arguments = @{
|
||||
Method = $Method
|
||||
Uri = $Uri
|
||||
Headers = $requestHeaders
|
||||
}
|
||||
if ($null -ne $Body) {
|
||||
$arguments.ContentType = 'application/json; charset=utf-8'
|
||||
$arguments.Body = $Body | ConvertTo-Json -Depth 10 -Compress
|
||||
}
|
||||
return Invoke-WebRequest @arguments
|
||||
}
|
||||
|
||||
function Assert-Status([scriptblock]$Operation, [int]$ExpectedStatus) {
|
||||
try {
|
||||
& $Operation | Out-Null
|
||||
throw "请求不应成功,预期HTTP $ExpectedStatus。"
|
||||
} catch {
|
||||
if ($null -eq $_.Exception.Response -or [int]$_.Exception.Response.StatusCode -ne $ExpectedStatus) { throw }
|
||||
}
|
||||
}
|
||||
|
||||
function Convert-Response($Response) {
|
||||
return $Response.Content | ConvertFrom-Json
|
||||
}
|
||||
|
||||
try {
|
||||
$ownerToken = Get-LocalAccessToken -Username 'owner'
|
||||
$editorToken = Get-LocalAccessToken -Username 'editor'
|
||||
$outsiderToken = Get-LocalAccessToken -Username 'outsider'
|
||||
|
||||
$createdResponse = Invoke-JSONRequest -Method Post -Uri "$apiBase/api/v1/households" `
|
||||
-Token $ownerToken -Headers @{ 'X-Tenant-Id' = $tenantId } -Body @{ name = 'SaaS纵向烟测家庭' }
|
||||
if ([int]$createdResponse.StatusCode -ne 201) { throw '创建家庭未返回201。' }
|
||||
$created = Convert-Response $createdResponse
|
||||
$householdId = $created.data.id
|
||||
$householdETag = [string]$createdResponse.Headers.ETag
|
||||
if ([string]::IsNullOrWhiteSpace($householdETag)) { throw '创建家庭未返回ETag。' }
|
||||
|
||||
Assert-Status { Invoke-JSONRequest -Method Get -Uri "$apiBase/api/v1/households/$householdId" -Token $outsiderToken } 403
|
||||
Assert-Status { Invoke-JSONRequest -Method Patch -Uri "$apiBase/api/v1/households/$householdId" -Token $editorToken -Headers @{ 'If-Match' = $householdETag } -Body @{ name = '禁止修改' } } 403
|
||||
|
||||
docker compose exec -T postgres psql -U voice -d voice_platform -v ON_ERROR_STOP=1 -c `
|
||||
"INSERT INTO household_member (tenant_id,household_id,user_id,role,status) SELECT h.tenant_id,h.id,ua.id,'editor','active' FROM household h JOIN user_account ua ON ua.tenant_id=h.tenant_id WHERE h.public_id='$householdId'::uuid AND ua.oidc_subject='00000000-0000-0000-0000-000000000102';" | Out-Null
|
||||
|
||||
$patchedResponse = Invoke-JSONRequest -Method Patch -Uri "$apiBase/api/v1/households/$householdId" `
|
||||
-Token $ownerToken -Headers @{ 'If-Match' = $householdETag } -Body @{ name = 'SaaS纵向烟测家庭-已修改' }
|
||||
if ([int]$patchedResponse.StatusCode -ne 200) { throw '修改家庭未返回200。' }
|
||||
Assert-Status { Invoke-JSONRequest -Method Patch -Uri "$apiBase/api/v1/households/$householdId" -Token $ownerToken -Headers @{ 'If-Match' = $householdETag } -Body @{ name = '过期写入' } } 412
|
||||
|
||||
$subjectResponse = Invoke-JSONRequest -Method Post -Uri "$apiBase/api/v1/households/$householdId/subjects" `
|
||||
-Token $editorToken -Body @{
|
||||
preferred_name = '烟测长者'
|
||||
language = 'zh-CN'
|
||||
timezone = 'Asia/Shanghai'
|
||||
profile_data = @{ synthetic = $true }
|
||||
conversation_preferences = @{ pace = 'slow' }
|
||||
}
|
||||
if ([int]$subjectResponse.StatusCode -ne 201) { throw '创建长者未返回201。' }
|
||||
$subject = Convert-Response $subjectResponse
|
||||
$subjectId = $subject.data.id
|
||||
$subjectETag = [string]$subjectResponse.Headers.ETag
|
||||
Assert-Status { Invoke-JSONRequest -Method Get -Uri "$apiBase/api/v1/subjects/$subjectId" -Token $outsiderToken } 403
|
||||
|
||||
$subjectPatched = Invoke-JSONRequest -Method Patch -Uri "$apiBase/api/v1/subjects/$subjectId" `
|
||||
-Token $editorToken -Headers @{ 'If-Match' = $subjectETag } -Body @{ preferred_name = '烟测长者-已修改' }
|
||||
if ([int]$subjectPatched.StatusCode -ne 200) { throw '修改长者未返回200。' }
|
||||
|
||||
$factResponse = Invoke-JSONRequest -Method Post -Uri "$apiBase/api/v1/subjects/$subjectId/facts" -Token $editorToken -Body @{
|
||||
fact_type = 'hometown'
|
||||
content = '合成城市'
|
||||
source_type = 'model_candidate'
|
||||
source_ref = 'synthetic://conversation/turn/1'
|
||||
confirmation_status = 'pending'
|
||||
sensitivity = 'normal'
|
||||
}
|
||||
$fact = Convert-Response $factResponse
|
||||
$factId = $fact.data.id
|
||||
$factETag = [string]$factResponse.Headers.ETag
|
||||
$confirmedFact = Invoke-JSONRequest -Method Patch -Uri "$apiBase/api/v1/subjects/$subjectId/facts/$factId" `
|
||||
-Token $editorToken -Headers @{ 'If-Match' = $factETag } -Body @{ confirmation_status = 'confirmed' }
|
||||
if ([int]$confirmedFact.StatusCode -ne 200) { throw '确认稳定事实未返回200。' }
|
||||
Assert-Status { Invoke-JSONRequest -Method Patch -Uri "$apiBase/api/v1/subjects/$subjectId/facts/$factId" -Token $editorToken -Headers @{ 'If-Match' = $factETag } -Body @{ content = '过期写入' } } 412
|
||||
|
||||
$consentResponse = Invoke-JSONRequest -Method Post -Uri "$apiBase/api/v1/subjects/$subjectId/consents" `
|
||||
-Token $editorToken -Body @{
|
||||
type = 'story_extraction'
|
||||
scope = @{ scope = 'future_conversations' }
|
||||
policy_version = 'local-v1'
|
||||
notice_digest = ('c' * 64)
|
||||
evidence_ref = 'synthetic://http-smoke'
|
||||
}
|
||||
if ([int]$consentResponse.StatusCode -ne 201) { throw '授予授权未返回201。' }
|
||||
$consent = Convert-Response $consentResponse
|
||||
$consentId = $consent.data.id
|
||||
Assert-Status { Invoke-JSONRequest -Method Post -Uri "$apiBase/api/v1/subjects/$subjectId/consents" -Token $editorToken -Body @{
|
||||
type = 'story_extraction'; scope = @{}; policy_version = 'local-v1'; notice_digest = ('d' * 64)
|
||||
} } 409
|
||||
|
||||
$withdrawn = Convert-Response (Invoke-JSONRequest -Method Post -Uri "$apiBase/api/v1/subjects/$subjectId/consents/$consentId/withdraw" -Token $editorToken)
|
||||
$withdrawnReplay = Convert-Response (Invoke-JSONRequest -Method Post -Uri "$apiBase/api/v1/subjects/$subjectId/consents/$consentId/withdraw" -Token $editorToken)
|
||||
if ($null -eq $withdrawn.data.withdrawn_at -or $withdrawn.data.version -ne $withdrawnReplay.data.version) {
|
||||
throw '授权撤回或重复撤回结果不正确。'
|
||||
}
|
||||
$snapshotCount = docker compose exec -T postgres psql -U voice -d voice_platform -Atc `
|
||||
"SELECT count(*) FROM consent_snapshot WHERE subject_id=(SELECT id FROM subject_profile WHERE public_id='$subjectId'::uuid);"
|
||||
if ([int]$snapshotCount -ne 2) { throw "授权快照数量是$snapshotCount,预期为2。" }
|
||||
Write-Host "SaaS纵向烟测通过:家庭 $householdId,长者 $subjectId,授权 $consentId。"
|
||||
} finally {
|
||||
if (-not [string]::IsNullOrWhiteSpace($householdId)) {
|
||||
docker compose exec -T postgres psql -U voice -d voice_platform -v ON_ERROR_STOP=1 -c `
|
||||
"BEGIN; DELETE FROM audit_event WHERE resource_id='$householdId'::uuid OR resource_id IN (SELECT s.public_id FROM subject_profile s JOIN household h ON h.id=s.household_id WHERE h.public_id='$householdId'::uuid UNION SELECT c.public_id FROM consent_record c JOIN subject_profile s ON s.id=c.subject_id JOIN household h ON h.id=s.household_id WHERE h.public_id='$householdId'::uuid UNION SELECT f.public_id FROM subject_fact f JOIN subject_profile s ON s.id=f.subject_id JOIN household h ON h.id=s.household_id WHERE h.public_id='$householdId'::uuid); DELETE FROM consent_snapshot WHERE subject_id IN (SELECT id FROM subject_profile WHERE household_id=(SELECT id FROM household WHERE public_id='$householdId'::uuid)); DELETE FROM consent_record WHERE subject_id IN (SELECT id FROM subject_profile WHERE household_id=(SELECT id FROM household WHERE public_id='$householdId'::uuid)); DELETE FROM subject_fact WHERE subject_id IN (SELECT id FROM subject_profile WHERE household_id=(SELECT id FROM household WHERE public_id='$householdId'::uuid)); DELETE FROM subject_profile WHERE household_id=(SELECT id FROM household WHERE public_id='$householdId'::uuid); DELETE FROM household_member WHERE household_id=(SELECT id FROM household WHERE public_id='$householdId'::uuid); DELETE FROM household WHERE public_id='$householdId'::uuid; COMMIT;" | Out-Null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user