fix(alerts): подтвердить риск ручного повтора сообщения
This commit is contained in:
1 parent
7d0608bbce
commit
b109532f33
11 files changed
+103
-16
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import assert from "node:assert/strict"
|
||||
import {test} from "node:test"
|
||||
import {getAlertsOutbox} from "./alerts-outbox"
|
||||
import {getAlertsOutbox,retryAlertsOutbox} from "./alerts-outbox"
|
||||
const empty={counts:{total:0,pending:0,processing:0,unknown:0,sent:0,failed:0,unrecognized:0},items:[],itemsLimit:20}
|
||||
test("queue strict no-store reader distinguishes confirmed zero from malformed or missing held data",async()=>{
|
||||
const original=globalThis.fetch;try{
|
||||
@@ -10,3 +10,10 @@ test("queue strict no-store reader distinguishes confirmed zero from malformed o
|
||||
const late=new AbortController();globalThis.fetch=async()=>{late.abort();return Response.json(empty)};await assert.rejects(getAlertsOutbox("https://backend.test",{signal:late.signal}))
|
||||
}finally{globalThis.fetch=original}
|
||||
})
|
||||
|
||||
test("queue retry requires risk acknowledgement and strict matching receipt, one POST with no retry",async()=>{
|
||||
const original=globalThis.fetch;try{let calls=0;globalThis.fetch=async(url,init)=>{calls++;assert.ok(String(url).endsWith("/api/alerts/outbox/a%2Fb/retry"));assert.equal(init?.method,"POST");assert.deepEqual(JSON.parse(String(init?.body)),{expectedVersion:3,acknowledgedDuplicateRisk:true});return Response.json({ok:true,id:"a/b",status:"pending",resolutionVersion:4})}
|
||||
await assert.rejects(retryAlertsOutbox("https://backend.test","a/b",{expectedVersion:3,acknowledgedDuplicateRisk:false}));assert.equal(calls,0);assert.equal((await retryAlertsOutbox("https://backend.test","a/b",{expectedVersion:3,acknowledgedDuplicateRisk:true})).status,"pending");assert.equal(calls,1)
|
||||
for(const raw of [{},{ok:true,id:"other",status:"pending",resolutionVersion:4},{ok:true,id:"a/b",status:"sent",resolutionVersion:4},{ok:true,id:"a/b",status:"pending",resolutionVersion:3}]){calls=0;globalThis.fetch=async()=>{calls++;return Response.json(raw)};await assert.rejects(retryAlertsOutbox("https://backend.test","a/b",{expectedVersion:3,acknowledgedDuplicateRisk:true}),/не подтверждена/);assert.equal(calls,1)}
|
||||
}finally{globalThis.fetch=original}
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
import {alertsOutboxOverviewSchema} from "@mmapp/contracts/alerts"
|
||||
import {alertsOutboxOverviewSchema,alertsOutboxRetrySchema,alertsOutboxRetryReceiptSchema} from "@mmapp/contracts/alerts"
|
||||
import {requestJson} from "./http-client"
|
||||
export async function getAlertsOutbox(base:string,options?:{signal?:AbortSignal}){
|
||||
const signal=options?.signal?AbortSignal.any([options.signal,AbortSignal.timeout(30_000)]):AbortSignal.timeout(30_000);signal.throwIfAborted()
|
||||
@@ -6,3 +6,12 @@ export async function getAlertsOutbox(base:string,options?:{signal?:AbortSignal}
|
||||
if(!parsed.success)throw Error("Состояние очереди не подтверждено сервером")
|
||||
return parsed.data
|
||||
}
|
||||
|
||||
export async function retryAlertsOutbox(base:string,id:string,input:unknown,options?:{signal?:AbortSignal}){
|
||||
const body=alertsOutboxRetrySchema.safeParse(input);if(!body.success||!id.trim()||id.length>200)throw Error("Проверьте подтверждение и версию сообщения")
|
||||
const signal=options?.signal?AbortSignal.any([options.signal,AbortSignal.timeout(30_000)]):AbortSignal.timeout(30_000);signal.throwIfAborted()
|
||||
const raw=await requestJson<unknown>(base,"/api/alerts/outbox/"+encodeURIComponent(id)+"/retry",{method:"POST",body:JSON.stringify(body.data),signal,cache:"no-store"});signal.throwIfAborted()
|
||||
const parsed=alertsOutboxRetryReceiptSchema.safeParse(raw)
|
||||
if(!parsed.success||parsed.data.id!==id||parsed.data.resolutionVersion!==body.data.expectedVersion+1)throw Error("Постановка сообщения в очередь не подтверждена сервером")
|
||||
return parsed.data
|
||||
}
|
||||
Reference in new issue
Block a user