[CLAUDE] Scripts: seed 20 test user prod cho UAT (S22+2)
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
Bro request: tạo mỗi phòng 2-3 user + phân quyền để test scenarios. Script `scripts/seed-test-users-prod.ps1` ASCII-only (gotcha #30 PS 5.1 diacritics) gọi API admin token, idempotent (skip 409 conflict). Tạo 20 user mới: | Phòng | Trước | Sau | Pattern | |---|---:|---:|---| | ACT | 0 | 3 | NV (Drafter+Accounting) / PP / TP (DeptManager, CanBypassReview=true) | | BOD | 1 | 3 | +2 Director (no PositionLevel) | | CCM | 7 | 7 | SKIP existing đủ | | EQU | 0 | 3 | NV / PP / TP (DeptManager+Equipment) | | FIN | 0 | 3 | NV / PP (AllowDrafterSkipToFinal=true) / TP | | HRA | 0 | 3 | NV / PP / TP (CanBypassReview=true) | | PM | 0 | 3 | NV (AllowDrafterSkipToFinal=true, ProjectManager) / PP / TP | | PRO | 5 | 5 | SKIP existing đủ | | QS | 0 | 3 | NV / PP / TP (Drafter-only, no role chuyên) | Total active prod: 13 → 33 users. UAT scenarios covered: - N-stage workflow inner step (Mig 18): NV/PP/TP per phòng test sequential + bypass - 2-stage dept approval (Mig 16): 2 user CanBypassReview=true (ACT.tp + HRA.tp) - F2 per-Drafter skip (Mig 29): 2 user AllowDrafterSkipToFinal=true (FIN.pp + PM.nv) - Plan E strict V2 scope: 33 user × 9 dept × various roles (test diverse approver match) Password tất cả: TestUser@2026 (>=12 chars per Identity policy). Discoveries: - Identity password policy: >=12 chars (HANDOFF "User@123456" 11 chars FAIL 400) - API auth response: field `accessToken` không phải `token` - Rate limit awareness: Start-Sleep 500ms giữa requests Verify: sqlcmd Prod 9 phòng × 2-7 user, 33 total active. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
148
scripts/seed-test-users-prod.ps1
Normal file
148
scripts/seed-test-users-prod.ps1
Normal file
@ -0,0 +1,148 @@
|
||||
# Seed 20 test user prod (Session 22+2 — UAT testing scenarios)
|
||||
#
|
||||
# Moi phong con trong tao 3 user (NV/PP/TP) + BOD bo sung 2 user.
|
||||
# Skip CCM (7 user da du) + PRO (5 user da du).
|
||||
#
|
||||
# Pattern: goi API admin token, idempotent (skip 409 conflict email exists).
|
||||
# Sau tao user -> PATCH /position-level + /bypass-review + /allow-skip-final flag.
|
||||
#
|
||||
# ASCII-only per gotcha #30 (PS 5.1 diacritics parser error). FullName trong API
|
||||
# se la ASCII; bro co the rename qua UI Users page neu can dau tieng Viet.
|
||||
#
|
||||
# Usage:
|
||||
# .\scripts\seed-test-users-prod.ps1
|
||||
#
|
||||
# Pre-req: Admin credentials admin@solutions.com.vn / Admin@123456 prod.
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ApiBase = 'https://api.solutions.com.vn'
|
||||
$AdminEmail = 'admin@solutions.com.vn'
|
||||
$AdminPassword = 'Admin@123456'
|
||||
$DefaultPassword = 'TestUser@2026' # >=12 chars per Identity policy (gotcha discovered S22+2)
|
||||
|
||||
Write-Host "==> Authenticating admin..." -ForegroundColor Cyan
|
||||
$authBody = @{ email = $AdminEmail; password = $AdminPassword } | ConvertTo-Json
|
||||
$authResp = Invoke-RestMethod -Method POST -Uri "$ApiBase/api/auth/login" `
|
||||
-ContentType 'application/json' -Body $authBody
|
||||
$token = $authResp.accessToken
|
||||
Write-Host " Token acquired (length=$($token.Length))" -ForegroundColor Green
|
||||
|
||||
$headers = @{ Authorization = "Bearer $token" }
|
||||
|
||||
Write-Host "==> Fetching departments..." -ForegroundColor Cyan
|
||||
$depts = Invoke-RestMethod -Method GET -Uri "$ApiBase/api/departments?page=1&pageSize=50" -Headers $headers
|
||||
$deptMap = @{}
|
||||
foreach ($d in $depts.items) { $deptMap[$d.code] = $d.id }
|
||||
Write-Host " Mapped $($deptMap.Count) departments" -ForegroundColor Green
|
||||
|
||||
# === USER SEEDS (20 total) ===
|
||||
$users = @(
|
||||
# ACT - Phong Ke toan (3)
|
||||
@{ Dept='ACT'; Email='hoa.nguyen@solutions.com.vn'; FullName='Nguyen Thi Hoa'; Position='Nhan vien Ke toan'; Roles=@('Drafter','Accounting'); PositionLevel=1; CanBypass=$false; AllowSkip=$false },
|
||||
@{ Dept='ACT'; Email='lan.pham@solutions.com.vn'; FullName='Pham Thi Lan'; Position='Pho phong Ke toan'; Roles=@('Drafter','Accounting'); PositionLevel=2; CanBypass=$false; AllowSkip=$false },
|
||||
@{ Dept='ACT'; Email='minh.le@solutions.com.vn'; FullName='Le Van Minh'; Position='Truong phong Ke toan'; Roles=@('DeptManager','Accounting'); PositionLevel=3; CanBypass=$true; AllowSkip=$false },
|
||||
|
||||
# BOD - Ban Giam doc (2 - khong hierarchy)
|
||||
@{ Dept='BOD'; Email='tuan.tran@solutions.com.vn'; FullName='Tran Anh Tuan'; Position='Giam doc'; Roles=@('Director'); PositionLevel=$null; CanBypass=$false; AllowSkip=$false },
|
||||
@{ Dept='BOD'; Email='hung.do@solutions.com.vn'; FullName='Do Quoc Hung'; Position='Pho Giam doc'; Roles=@('Director','AuthorizedSigner'); PositionLevel=$null; CanBypass=$false; AllowSkip=$false },
|
||||
|
||||
# EQU - Phong Thiet bi (3)
|
||||
@{ Dept='EQU'; Email='dung.bui@solutions.com.vn'; FullName='Bui Van Dung'; Position='Nhan vien Thiet bi'; Roles=@('Drafter','Equipment'); PositionLevel=1; CanBypass=$false; AllowSkip=$false },
|
||||
@{ Dept='EQU'; Email='hai.vu@solutions.com.vn'; FullName='Vu Thanh Hai'; Position='Pho phong Thiet bi'; Roles=@('Drafter','Equipment'); PositionLevel=2; CanBypass=$false; AllowSkip=$false },
|
||||
@{ Dept='EQU'; Email='tho.do@solutions.com.vn'; FullName='Do Van Tho'; Position='Truong phong Thiet bi'; Roles=@('DeptManager','Equipment'); PositionLevel=3; CanBypass=$false; AllowSkip=$false },
|
||||
|
||||
# FIN - Phong Tai chinh (3)
|
||||
@{ Dept='FIN'; Email='linh.dao@solutions.com.vn'; FullName='Dao Thi Linh'; Position='Nhan vien Tai chinh'; Roles=@('Drafter','Finance'); PositionLevel=1; CanBypass=$false; AllowSkip=$false },
|
||||
@{ Dept='FIN'; Email='nga.bui@solutions.com.vn'; FullName='Bui Hong Nga'; Position='Pho phong Tai chinh'; Roles=@('Drafter','Finance'); PositionLevel=2; CanBypass=$false; AllowSkip=$true },
|
||||
@{ Dept='FIN'; Email='thu.pham@solutions.com.vn'; FullName='Pham Anh Thu'; Position='Truong phong Tai chinh'; Roles=@('DeptManager','Finance'); PositionLevel=3; CanBypass=$false; AllowSkip=$false },
|
||||
|
||||
# HRA - Phong Nhan su - Hanh chinh (3)
|
||||
@{ Dept='HRA'; Email='mai.tran@solutions.com.vn'; FullName='Tran Thi Mai'; Position='Nhan vien HC-NS'; Roles=@('Drafter','HrAdmin'); PositionLevel=1; CanBypass=$false; AllowSkip=$false },
|
||||
@{ Dept='HRA'; Email='hong.le@solutions.com.vn'; FullName='Le Thi Hong'; Position='Pho phong HC-NS'; Roles=@('Drafter','HrAdmin'); PositionLevel=2; CanBypass=$false; AllowSkip=$false },
|
||||
@{ Dept='HRA'; Email='tam.nguyen@solutions.com.vn'; FullName='Nguyen Van Tam'; Position='Truong phong HC-NS'; Roles=@('DeptManager','HrAdmin'); PositionLevel=3; CanBypass=$true; AllowSkip=$false },
|
||||
|
||||
# PM - Ban Quan ly Du an (3)
|
||||
@{ Dept='PM'; Email='khoi.do@solutions.com.vn'; FullName='Do Dang Khoi'; Position='Nhan vien QLDA'; Roles=@('Drafter','ProjectManager'); PositionLevel=1; CanBypass=$false; AllowSkip=$true },
|
||||
@{ Dept='PM'; Email='phong.vu@solutions.com.vn'; FullName='Vu Trong Phong'; Position='Pho BQL Du an'; Roles=@('Drafter','ProjectManager'); PositionLevel=2; CanBypass=$false; AllowSkip=$false },
|
||||
@{ Dept='PM'; Email='quan.bui@solutions.com.vn'; FullName='Bui Huu Quan'; Position='Truong BQL Du an'; Roles=@('DeptManager','ProjectManager'); PositionLevel=3; CanBypass=$false; AllowSkip=$false },
|
||||
|
||||
# QS - Phong Quantity Surveyor (3 - khong kem role chuyen)
|
||||
@{ Dept='QS'; Email='hieu.nguyen@solutions.com.vn'; FullName='Nguyen Van Hieu'; Position='Nhan vien QS'; Roles=@('Drafter'); PositionLevel=1; CanBypass=$false; AllowSkip=$false },
|
||||
@{ Dept='QS'; Email='thanh.pham@solutions.com.vn'; FullName='Pham Van Thanh'; Position='Pho phong QS'; Roles=@('Drafter'); PositionLevel=2; CanBypass=$false; AllowSkip=$false },
|
||||
@{ Dept='QS'; Email='duc.le@solutions.com.vn'; FullName='Le Quang Duc'; Position='Truong phong QS'; Roles=@('DeptManager'); PositionLevel=3; CanBypass=$false; AllowSkip=$false }
|
||||
)
|
||||
|
||||
Write-Host "==> Seeding $($users.Count) users..." -ForegroundColor Cyan
|
||||
$createdCount = 0
|
||||
$skippedCount = 0
|
||||
$errorCount = 0
|
||||
|
||||
foreach ($u in $users) {
|
||||
$deptId = $deptMap[$u.Dept]
|
||||
if (-not $deptId) {
|
||||
Write-Host " [SKIP] $($u.Email): department $($u.Dept) not found" -ForegroundColor Yellow
|
||||
$skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
$createBody = @{
|
||||
email = $u.Email
|
||||
fullName = $u.FullName
|
||||
password = $DefaultPassword
|
||||
roles = $u.Roles
|
||||
departmentId = $deptId
|
||||
position = $u.Position
|
||||
} | ConvertTo-Json -Compress
|
||||
|
||||
try {
|
||||
$createResp = Invoke-RestMethod -Method POST -Uri "$ApiBase/api/users" `
|
||||
-Headers $headers -ContentType 'application/json' -Body $createBody
|
||||
$userId = $createResp.id
|
||||
Write-Host " [+] Created $($u.Email) [$($u.Dept)] id=$userId" -ForegroundColor Green
|
||||
$createdCount++
|
||||
|
||||
# Apply position-level flag
|
||||
if ($null -ne $u.PositionLevel) {
|
||||
$plBody = @{ positionLevel = $u.PositionLevel } | ConvertTo-Json -Compress
|
||||
Invoke-RestMethod -Method PATCH -Uri "$ApiBase/api/users/$userId/position-level" `
|
||||
-Headers $headers -ContentType 'application/json' -Body $plBody | Out-Null
|
||||
Write-Host " PositionLevel=$($u.PositionLevel)" -ForegroundColor DarkGray
|
||||
}
|
||||
# Apply canBypassReview flag (Mig 16)
|
||||
if ($u.CanBypass) {
|
||||
$bpBody = @{ canBypassReview = $true } | ConvertTo-Json -Compress
|
||||
Invoke-RestMethod -Method PATCH -Uri "$ApiBase/api/users/$userId/bypass-review" `
|
||||
-Headers $headers -ContentType 'application/json' -Body $bpBody | Out-Null
|
||||
Write-Host " CanBypassReview=true (2-stage bypass)" -ForegroundColor DarkGray
|
||||
}
|
||||
# Apply allowDrafterSkipToFinal flag (Mig 29 F2)
|
||||
if ($u.AllowSkip) {
|
||||
$skBody = @{ allowDrafterSkipToFinal = $true } | ConvertTo-Json -Compress
|
||||
Invoke-RestMethod -Method PATCH -Uri "$ApiBase/api/users/$userId/allow-skip-final" `
|
||||
-Headers $headers -ContentType 'application/json' -Body $skBody | Out-Null
|
||||
Write-Host " AllowDrafterSkipToFinal=true (F2)" -ForegroundColor DarkGray
|
||||
}
|
||||
}
|
||||
catch {
|
||||
$statusCode = $_.Exception.Response.StatusCode.value__
|
||||
if ($statusCode -eq 409) {
|
||||
Write-Host " [SKIP] $($u.Email): already exists (409)" -ForegroundColor Yellow
|
||||
$skippedCount++
|
||||
}
|
||||
else {
|
||||
Write-Host " [ERR] $($u.Email): $($_.Exception.Message)" -ForegroundColor Red
|
||||
$errorCount++
|
||||
}
|
||||
}
|
||||
|
||||
# Rate limit backoff (CICD Monitor S22 discovery - auth ~5 req/min)
|
||||
Start-Sleep -Milliseconds 500
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "==> Summary" -ForegroundColor Cyan
|
||||
Write-Host " Created: $createdCount" -ForegroundColor Green
|
||||
Write-Host " Skipped (already exists): $skippedCount" -ForegroundColor Yellow
|
||||
Write-Host " Errors: $errorCount" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Write-Host "==> All users password: $DefaultPassword" -ForegroundColor Magenta
|
||||
Reference in New Issue
Block a user