b8_http_verify.ps1 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. $ErrorActionPreference = "Stop"
  2. $base = "http://127.0.0.1:8755/api/v1"
  3. $origin = "http://127.0.0.1:9346"
  4. function Invoke-Json {
  5. param(
  6. [string]$Method,
  7. [string]$Path,
  8. [string]$Token,
  9. [object]$Body = $null,
  10. [hashtable]$ExtraHeaders = @{}
  11. )
  12. $headers = @{}
  13. if ($Token) {
  14. $headers.Authorization = "Bearer $Token"
  15. }
  16. foreach ($key in $ExtraHeaders.Keys) {
  17. $headers[$key] = $ExtraHeaders[$key]
  18. }
  19. $parameters = @{
  20. Method = $Method
  21. Uri = "$base$Path"
  22. Headers = $headers
  23. UseBasicParsing = $true
  24. }
  25. if ($null -ne $Body) {
  26. $parameters.ContentType = "application/json"
  27. $parameters.Body = ($Body | ConvertTo-Json -Depth 10 -Compress)
  28. }
  29. try {
  30. $response = Invoke-WebRequest @parameters
  31. return @{
  32. Status = [int]$response.StatusCode
  33. Body = ($response.Content | ConvertFrom-Json)
  34. Headers = $response.Headers
  35. }
  36. }
  37. catch {
  38. $response = $_.Exception.Response
  39. $content = $_.ErrorDetails.Message
  40. if (-not $content) {
  41. $reader = New-Object System.IO.StreamReader($response.GetResponseStream())
  42. $content = $reader.ReadToEnd()
  43. $reader.Dispose()
  44. }
  45. return @{
  46. Status = [int]$response.StatusCode
  47. Body = ($content | ConvertFrom-Json)
  48. Headers = $response.Headers
  49. }
  50. }
  51. }
  52. function Assert-True {
  53. param([bool]$Condition, [string]$Message)
  54. if (-not $Condition) {
  55. throw $Message
  56. }
  57. }
  58. function Upload-Document {
  59. param(
  60. [string]$Token,
  61. [string]$File,
  62. [hashtable]$Metadata,
  63. [bool]$Attachment = $false
  64. )
  65. $path = if ($Attachment) { "/attachments" } else { "/documents" }
  66. $metadataJson = $Metadata | ConvertTo-Json -Depth 10 -Compress
  67. $metadataFile = [System.IO.Path]::GetTempFileName()
  68. try {
  69. [System.IO.File]::WriteAllText(
  70. $metadataFile,
  71. $metadataJson,
  72. [System.Text.UTF8Encoding]::new($false)
  73. )
  74. $output = & curl.exe -sS -X POST "$base$path" `
  75. -H "Authorization: Bearer $Token" `
  76. -F "file=@$File" `
  77. -F "metadata=<$metadataFile"
  78. if ($LASTEXITCODE -ne 0) {
  79. throw "curl上传失败"
  80. }
  81. }
  82. finally {
  83. Remove-Item -LiteralPath $metadataFile -Force -ErrorAction SilentlyContinue
  84. }
  85. $body = $output | ConvertFrom-Json
  86. Assert-True ($body.code -eq "OK") "上传失败:$output"
  87. return $body.data
  88. }
  89. function Issue-Token {
  90. param([string]$Username)
  91. $response = Invoke-Json POST "/auth/login" "" @{
  92. username = $Username
  93. password = $env:DMS_TEST_USER_PASSWORD
  94. keepSignedIn = $false
  95. }
  96. Assert-True ($response.Status -eq 200) "$Username 登录失败"
  97. return $response.Body.data.accessToken
  98. }
  99. $adminToken = Issue-Token "admin"
  100. $userToken = Issue-Token "user"
  101. $auditorToken = Issue-Token "auditor"
  102. $health = Invoke-Json GET "/health" "" $null @{Origin = $origin}
  103. Assert-True ($health.Status -eq 200) "健康检查失败"
  104. Assert-True (
  105. $health.Headers["Access-Control-Expose-Headers"] -match "Content-Disposition" -and
  106. $health.Headers["Access-Control-Expose-Headers"] -match "X-Request-Id"
  107. ) "CORS暴露响应头缺失"
  108. $tree = Invoke-Json GET "/categories/tree" $adminToken
  109. Assert-True ($tree.Status -eq 200) "分类树查询失败"
  110. $categoryId = [string]$tree.Body.data[0].id
  111. $sampleFile = (
  112. Get-ChildItem -LiteralPath (
  113. Join-Path $PSScriptRoot "..\..\dms-test-storage\original"
  114. ) -Filter "*.docx" -File | Select-Object -First 1
  115. ).FullName
  116. Assert-True ([bool]$sampleFile) "未找到联调上传样本文件"
  117. $main = Upload-Document $adminToken $sampleFile @{
  118. documentName = "F8_RECYCLE_MAIN"
  119. documentType = "MAIN"
  120. summary = "B8 HTTP MAIN"
  121. categoryId = $categoryId
  122. securityLevel = "INTERNAL"
  123. visibilityType = "CUSTOM"
  124. status = "PUBLISHED"
  125. tags = @("B8", "F8")
  126. }
  127. $attachment = Upload-Document $adminToken $sampleFile @{
  128. documentName = "F8_RECYCLE_ATTACHMENT"
  129. attachmentType = "OTHER"
  130. summary = "B8 HTTP ATTACHMENT"
  131. tags = @("B8", "F8")
  132. } $true
  133. $users = Invoke-Json GET "/users?keyword=user&page=1&pageSize=20" $adminToken
  134. $subjectUser = $users.Body.data.items | Where-Object {$_.username -eq "user"} |
  135. Select-Object -First 1
  136. $permission = Invoke-Json PUT "/documents/$($main.id)/permissions" $adminToken @{
  137. visibilityType = "CUSTOM"
  138. documentRowVersion = $main.rowVersion
  139. entries = @(
  140. @{
  141. subjectType = "USER"
  142. subjectId = [string]$subjectUser.id
  143. canView = $true
  144. canDownload = $true
  145. canEdit = $false
  146. canManagePermission = $false
  147. canDelete = $false
  148. }
  149. )
  150. }
  151. Assert-True ($permission.Status -eq 200) "权限配置失败"
  152. $binding = Invoke-Json POST "/main-plans/$($main.id)/attachments/bind" $adminToken @{
  153. attachmentIds = @([string]$attachment.id)
  154. mainPlanRowVersion = $permission.Body.data.documentRowVersion
  155. }
  156. Assert-True ($binding.Status -eq 200) "附件挂载失败"
  157. $main.rowVersion = $binding.Body.data.mainPlanRowVersion
  158. $deleteMain = Invoke-Json DELETE "/documents/$($main.id)?rowVersion=$($main.rowVersion)" $adminToken
  159. Assert-True ($deleteMain.Status -eq 200) "主案删除失败"
  160. $bin = Invoke-Json GET "/recycle-bin/documents?keyword=F8_RECYCLE_MAIN" $adminToken $null @{Origin = $origin}
  161. Assert-True ($bin.Status -eq 200 -and $bin.Body.data.total -eq 1) "主案未进入回收站"
  162. $deletedMain = $bin.Body.data.items[0]
  163. Assert-True ($null -ne $deletedMain.deletedBy) "删除人快照缺失"
  164. $wrongVersion = Invoke-Json POST "/recycle-bin/documents/$($main.id)/restore" $adminToken @{
  165. rowVersion = [Math]::Max(0, [int]$deletedMain.rowVersion - 1)
  166. }
  167. Assert-True (
  168. $wrongVersion.Status -eq 409 -and
  169. $wrongVersion.Body.code -eq "DATA_VERSION_CONFLICT"
  170. ) "rowVersion冲突未按契约返回"
  171. $restoreMain = Invoke-Json POST "/recycle-bin/documents/$($main.id)/restore" $adminToken @{
  172. rowVersion = [int]$deletedMain.rowVersion
  173. }
  174. Assert-True ($restoreMain.Status -eq 200) "主案恢复失败"
  175. Assert-True (
  176. $restoreMain.Body.data.restoredPermissionCount -eq 1 -and
  177. $restoreMain.Body.data.restoredBindingCount -eq 1
  178. ) "主案ACL或挂载未恢复"
  179. $restoredPermissions = Invoke-Json GET "/documents/$($main.id)/permissions" $adminToken
  180. $restoredAttachments = Invoke-Json GET "/main-plans/$($main.id)/attachments" $adminToken
  181. Assert-True ($restoredPermissions.Body.data.entries.Count -eq 1) "恢复后ACL不同步"
  182. Assert-True ($restoredAttachments.Body.data.total -eq 1) "恢复后挂载不同步"
  183. $sub = Upload-Document $adminToken $sampleFile @{
  184. documentName = "F8_RECYCLE_SUB_PLAN"
  185. documentType = "SUB_PLAN"
  186. summary = "B8 HTTP SUB PLAN"
  187. categoryId = $categoryId
  188. securityLevel = "INTERNAL"
  189. visibilityType = "CUSTOM"
  190. status = "PUBLISHED"
  191. tags = @("B8", "F8")
  192. parentDocumentId = [string]$main.id
  193. }
  194. $deleteSub = Invoke-Json DELETE "/documents/$($sub.id)?rowVersion=$($sub.rowVersion)" $adminToken
  195. Assert-True ($deleteSub.Status -eq 200) "子方案删除失败"
  196. $subBin = Invoke-Json GET "/recycle-bin/documents?keyword=F8_RECYCLE_SUB_PLAN" $adminToken
  197. $deletedSub = $subBin.Body.data.items[0]
  198. $currentMainPermission = Invoke-Json GET "/documents/$($main.id)/permissions" $adminToken
  199. $deleteParent = Invoke-Json DELETE (
  200. "/documents/$($main.id)?rowVersion=" +
  201. $currentMainPermission.Body.data.documentRowVersion
  202. ) $adminToken
  203. Assert-True ($deleteParent.Status -eq 200) "父主案二次删除失败"
  204. $invalidParent = Invoke-Json POST "/recycle-bin/documents/$($sub.id)/restore" $adminToken @{
  205. rowVersion = [int]$deletedSub.rowVersion
  206. }
  207. Assert-True (
  208. $invalidParent.Status -eq 409 -and
  209. $invalidParent.Body.code -eq "RESTORE_PARENT_INVALID"
  210. ) "无效父主案冲突未返回"
  211. $mainBinAgain = Invoke-Json GET "/recycle-bin/documents?keyword=F8_RECYCLE_MAIN" $adminToken
  212. $restoreMainAgain = Invoke-Json POST "/recycle-bin/documents/$($main.id)/restore" $adminToken @{
  213. rowVersion = [int]$mainBinAgain.Body.data.items[0].rowVersion
  214. }
  215. Assert-True ($restoreMainAgain.Status -eq 200) "主案二次恢复失败"
  216. $restoreSub = Invoke-Json POST "/recycle-bin/documents/$($sub.id)/restore" $adminToken @{
  217. rowVersion = [int]$deletedSub.rowVersion
  218. }
  219. Assert-True ($restoreSub.Status -eq 200) "子方案恢复失败"
  220. $mainPermissionAfterSub = Invoke-Json GET "/documents/$($main.id)/permissions" $adminToken
  221. $unbind = Invoke-Json DELETE (
  222. "/main-plans/$($main.id)/attachments/$($attachment.id)" +
  223. "?mainPlanRowVersion=" +
  224. $mainPermissionAfterSub.Body.data.documentRowVersion
  225. ) $adminToken
  226. Assert-True ($unbind.Status -eq 200) "解除挂载失败"
  227. $deleteAttachment = Invoke-Json DELETE (
  228. "/attachments/$($attachment.id)?rowVersion=$($attachment.rowVersion)"
  229. ) $adminToken
  230. Assert-True ($deleteAttachment.Status -eq 200) "附件删除失败"
  231. $attachmentBin = Invoke-Json GET "/recycle-bin/documents?keyword=F8_RECYCLE_ATTACHMENT" $adminToken
  232. $restoreAttachment = Invoke-Json POST (
  233. "/recycle-bin/documents/$($attachment.id)/restore"
  234. ) $adminToken @{
  235. rowVersion = [int]$attachmentBin.Body.data.items[0].rowVersion
  236. }
  237. Assert-True ($restoreAttachment.Status -eq 200) "附件恢复失败"
  238. Assert-True (
  239. $restoreAttachment.Body.data.restoredBindingCount -eq 0 -and
  240. $restoreAttachment.Body.data.document.mountedPlanCount -eq 0
  241. ) "附件错误恢复了历史挂载"
  242. foreach ($token in @($userToken, $auditorToken)) {
  243. $forbidden = Invoke-Json GET "/recycle-bin/documents" $token
  244. Assert-True (
  245. $forbidden.Status -eq 403 -and $forbidden.Body.code -eq "FORBIDDEN"
  246. ) "非管理员未被拒绝"
  247. }
  248. $oldPath = Invoke-Json POST "/documents/$($main.id)/restore" $adminToken @{
  249. rowVersion = 0
  250. }
  251. Assert-True ($oldPath.Status -eq 404) "旧恢复路径未保持404"
  252. $restoreAudits = Invoke-Json GET (
  253. "/audit/logs?actionType=RESTORE_DOCUMENT&page=1&pageSize=100"
  254. ) $auditorToken
  255. Assert-True ($restoreAudits.Body.data.total -ge 3) "恢复审计缺失"
  256. $viewAudits = Invoke-Json GET (
  257. "/audit/logs?actionType=VIEW_DOCUMENT&targetId=$($main.id)&page=1&pageSize=100"
  258. ) $auditorToken
  259. Assert-True ($viewAudits.Body.data.total -eq 0) "恢复响应产生了VIEW_DOCUMENT"
  260. # 最终仅保留三条F8回收站样本,全部通过正式DELETE接口产生。
  261. $deleteSubFinal = Invoke-Json DELETE (
  262. "/documents/$($sub.id)?rowVersion=$($restoreSub.Body.data.document.rowVersion)"
  263. ) $adminToken
  264. Assert-True ($deleteSubFinal.Status -eq 200) "F8子方案最终删除失败"
  265. $mainPermissionFinal = Invoke-Json GET "/documents/$($main.id)/permissions" $adminToken
  266. $deleteMainFinal = Invoke-Json DELETE (
  267. "/documents/$($main.id)?rowVersion=" +
  268. $mainPermissionFinal.Body.data.documentRowVersion
  269. ) $adminToken
  270. Assert-True ($deleteMainFinal.Status -eq 200) "F8主案最终删除失败"
  271. $deleteAttachmentFinal = Invoke-Json DELETE (
  272. "/attachments/$($attachment.id)" +
  273. "?rowVersion=$($restoreAttachment.Body.data.document.rowVersion)"
  274. ) $adminToken
  275. Assert-True ($deleteAttachmentFinal.Status -eq 200) "F8附件最终删除失败"
  276. $finalBin = Invoke-Json GET "/recycle-bin/documents?page=1&pageSize=100" $adminToken
  277. $finalItems = @($finalBin.Body.data.items | Where-Object {
  278. $_.documentName -in @(
  279. "F8_RECYCLE_MAIN",
  280. "F8_RECYCLE_SUB_PLAN",
  281. "F8_RECYCLE_ATTACHMENT"
  282. )
  283. })
  284. Assert-True ($finalItems.Count -eq 3) "F8三条回收站样本不完整"
  285. [pscustomobject]@{
  286. mainId = [string]$main.id
  287. subPlanId = [string]$sub.id
  288. attachmentId = [string]$attachment.id
  289. recycleBinTotal = $finalBin.Body.data.total
  290. restoreAuditTotal = $restoreAudits.Body.data.total
  291. corsExposeHeaders = $health.Headers["Access-Control-Expose-Headers"]
  292. } | ConvertTo-Json -Compress