chore(tests): подтвердить цельную доставку очереди Telegram

This commit is contained in:
Denozordec committed 2026-10-13 16:24:54 +07:00
1 parent cbe44e8072
commit bf39b3de18
8 files changed
+170 -25

No files matched your search

@@ -3,6 +3,8 @@ import fs from 'node:fs'
import {spawn} from 'node:child_process'
// Never part of test:all: full mode sends three messages; resume mode sends one intentional duplicate.
const label=process.env.MMAPP_LIVE_TELEGRAM_TEST_LABEL??'197'
assert(/^\d{3}$/.test(label))
const resume=process.env.MMAPP_LIVE_TELEGRAM_RESUME==='manual-retry-only'
const database=process.env.MMAPP_OWN_SQL_DB
assert(/^mm_accept_166_\d+$/.test(database??''),'An explicitly owned test database is required')
@@ -23,16 +25,16 @@ const {enqueueTelegramOutbox,dispatchPendingOutbox,getAlertsOutboxOverview}=awai
assert.equal((await pool.query('SELECT current_database() AS name')).rows[0].name,database)
const permissions=(await pool.query('SELECT rolsuper,rolcreatedb,rolcreaterole,rolbypassrls FROM pg_roles WHERE rolname=current_user')).rows[0]
assert(Object.values(permissions).every(v=>v===false))
const root='/app/backend/acceptance',prefix='own-live197-'+Date.now(),ownedUsers=[],cases=[]
const report={date:'2026-10-13',block:'12.5.197',database,resumeOnly:resume,scope:'Production compiled outbox, owned PostgreSQL, actual Portal JWT and real Telegram provider',cases,providerSendCalls:0,providerAcceptanceIds:[],ownPortalUsersRemoved:false,passed:false}
const root='/app/backend/acceptance',prefix='own-live'+label+'-'+Date.now(),ownedUsers=[],cases=[]
const report={date:'2026-10-13',block:'12.5.'+label,database,resumeOnly:resume,scope:'Production compiled outbox, owned PostgreSQL, actual Portal JWT and real Telegram provider',cases,providerSendCalls:0,requestTrace:[],providerAcceptanceIds:[],ownPortalUsersRemoved:false,passed:false}
let app,adminToken,originalFetch=globalThis.fetch
const portalUrl='http://portal:8080'
async function portalCall(path,method='GET',body,token){
const r=await originalFetch(portalUrl+path,{method,headers:{...(body!==undefined?{'content-type':'application/json'}:{}),...(token?{Authorization:'Bearer '+token}:{})},body:body===undefined?undefined:JSON.stringify(body)})
const text=await r.text();return {status:r.status,data:text?JSON.parse(text):null}
}
const payload=(number)=>({text:`TEST197 №${number} — очередь Telegram. ${number===1?'Проверка dedupe и двух отправителей.':'Проверка unknown и намеренного ручного повтора.'}`,chatId:bot.chatId,
history:{id:prefix+'-history'+number,ruleId:null,groupId:null,ruleName:'Own TEST197 №'+number,severity:'info',message:'Контролируемая приёмка очереди Telegram',firedAt:new Date().toISOString()}})
const payload=(number)=>({text:`TEST${label} №${number} — очередь Telegram. ${number===1?'Проверка dedupe и двух отправителей.':'Проверка unknown и намеренного ручного повтора.'}`,chatId:bot.chatId,
history:{id:prefix+'-history'+number,ruleId:null,groupId:null,ruleName:'Own TEST'+label+' №'+number,severity:'info',message:'Контролируемая приёмка очереди Telegram',firedAt:new Date().toISOString()}})
async function sender(){
const code=`import assert from 'node:assert/strict';import {pool,closePool} from '/app/backend/dist/db/index.js';import {dispatchPendingOutbox} from '/app/backend/dist/services/alert-engine/outbox.js';assert.equal((await pool.query('SELECT current_database() AS name')).rows[0].name,process.env.MMAPP_OWN_SQL_DB);let calls=0,ids=[];const native=globalThis.fetch;globalThis.fetch=async(...args)=>{const r=await native(...args);if(new URL(String(args[0])).pathname.endsWith('/sendMessage')){calls++;const d=await r.clone().json();if(d.ok)ids.push(d.result.message_id)}return r};try{const result=await dispatchPendingOutbox(1);console.log(JSON.stringify({result,calls,ids}))}finally{await closePool()}`
return new Promise((resolve,reject)=>{
@@ -47,7 +49,7 @@ try{
await updateTelegramSettings({token:bot.token,chatId:bot.chatId,messageThreadId:null})
const login=await portalCall('/api/v1/auth/login','POST',{email:portal.email,password:portal.password});assert.equal(login.status,200);adminToken=login.data.access_token
const readerEmail=prefix+'@mmapp.test'
const created=await portalCall('/api/v1/admin/users','POST',{email:readerEmail,password:portal.password,name:'Own TEST197 reader',apps:['mm'],permissions:['mm:alerts:read']},adminToken)
const created=await portalCall('/api/v1/admin/users','POST',{email:readerEmail,password:portal.password,name:'Own TEST'+label+' reader',apps:['mm'],permissions:['mm:alerts:read']},adminToken)
assert.equal(created.status,201);ownedUsers.push(created.data.id)
fs.writeFileSync(root+'/owned-portal-users-private.json',JSON.stringify(ownedUsers),{mode:0o600})
const readerLogin=await portalCall('/api/v1/auth/login','POST',{email:readerEmail,password:portal.password});assert.equal(readerLogin.status,200)
@@ -63,15 +65,21 @@ try{
assert.equal(Number((await pool.query('SELECT count(*) AS n FROM alert_history WHERE id=$1 AND sent_ok=true',[prefix+'-history1'])).rows[0].n),1)
cases.push({name:'dedupe and independent senders',senders:2,queuedRows:1,providerSends:1,sentHistoryRows:1})
}
let lastSendAt=Date.now()
globalThis.fetch=async(...args)=>{
const sending=new URL(String(args[0])).pathname.endsWith('/sendMessage')
if(sending){assert(report.providerSendCalls<(resume?1:3),'Live send budget exhausted');report.providerSendCalls++}
const response=await originalFetch(...args)
if(sending){await new Promise(resolve=>setTimeout(resolve,Math.max(0,1500-(Date.now()-lastSendAt))));lastSendAt=Date.now();assert(report.providerSendCalls<(resume?1:3),'Live send budget exhausted');report.providerSendCalls++}
const trace=sending?{requestNumber:report.providerSendCalls,startedAt:new Date().toISOString()}:null
if(trace)report.requestTrace.push(trace)
let response
try{response=await originalFetch(...args)}catch(e){if(trace)trace.fetchFailed=true;throw e}
if(sending){
const accepted=await response.clone().json()
trace.httpStatus=response.status
const accepted=await response.clone().json().catch(()=>({ok:false}))
trace.providerAccepted=accepted.ok===true
if(accepted.ok!==true){report.providerRejectionCodes??=[];report.providerRejectionCodes.push(accepted.error_code??null);return response}
report.providerAcceptanceIds.push(accepted.result.message_id)
if(suppressReceipt){suppressReceipt=false;throw Error('Own TEST197 receipt suppressed after confirmed provider acceptance')}
if(suppressReceipt){trace.receiptSuppressed=true;suppressReceipt=false;throw Error('Own TEST197 receipt suppressed after confirmed provider acceptance')}
}
return response
}