From 7e24c63dd87b6feb1e50b39768d5c526bb904116 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Fri, 17 Jul 2026 03:03:34 +0300 Subject: [PATCH] =?UTF-8?q?test:=20=D8=B3=D9=83=D8=B1=D8=A8=D8=AA=20=D8=AA?= =?UTF-8?q?=D8=AD=D9=82=D9=91=D9=82=20=D9=85=D8=B3=D8=A7=D8=B1=20=D8=A7?= =?UTF-8?q?=D9=84=D9=85=D9=88=D8=A7=D9=82=D8=B9=20=D8=B9=D9=84=D9=89=20?= =?UTF-8?q?=D8=A7=D9=84=D8=B3=D9=8A=D8=B1=D9=81=D8=B1=20(H)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit loadtest يرسل نبضة واحدة لكل سائق فلا يختبر العتبات إطلاقاً. هذا السكربت يرسل 10 نبضات من مكان ثابت ثم 8 نبضات بتحرّك 30م، ويتحقق من علم significant الذي ترجعه POST /drivers/location — أي العتبات على Redis حقيقي عبر الـAPI. Co-Authored-By: Claude Opus 4.8 --- backend/scripts/location-test.mjs | 104 ++++++++++++++++++++++++++++++ docs/15-deploy-flow.md | 6 ++ 2 files changed, 110 insertions(+) create mode 100644 backend/scripts/location-test.mjs diff --git a/backend/scripts/location-test.mjs b/backend/scripts/location-test.mjs new file mode 100644 index 0000000..7acd27a --- /dev/null +++ b/backend/scripts/location-test.mjs @@ -0,0 +1,104 @@ +// اختبار مسار المواقع (docs/17 — H): يثبت أن العتبات تعمل على Redis حقيقي، +// وأن سائقاً واقفاً لا يكلّف شيئاً. يستخدم fetch المدمج — بلا تبعيات. +// +// يعتمد أن POST /drivers/location يرجع { ok, significant } — فالعتبة تُتحقَّق +// عبر الـAPI الحقيقي بلا حاجة للنظر في القاعدة. +// +// التشغيل: +// docker run --rm --network tripz-net -e BASE=http://tripz-api:4010/api \ +// -v /home/tripz-llc/backend/scripts:/s node:22-alpine node /s/location-test.mjs +// +// بعده تحقَّق من كتابة الـworker (بعد ~6 ثوانٍ): +// SELECT count(*) FROM tripz_driver_tracks; -- يجب أن يساوي عدد «ذات الدلالة» +// SELECT loc_status, speed, loc_updated_at FROM tripz_drivers WHERE loc_updated_at IS NOT NULL; + +const BASE = process.env.BASE || 'http://localhost:4010/api'; +const TENANT = process.env.TENANT || 'siro'; +const CODE = '1234'; + +const STATIONARY_PINGS = 10; +const MOVING_PINGS = 8; +const ORIGIN = { lat: 31.9539, lng: 35.9106 }; +const metersToLat = (m) => m / 111_320; + +const H = (t) => ({ 'Content-Type': 'application/json', ...(t ? { Authorization: `Bearer ${t}` } : {}) }); + +async function api(method, path, token, body) { + const res = await fetch(`${BASE}${path}`, { + method, + headers: { ...H(token), 'x-tenant-id': TENANT }, + body: body ? JSON.stringify(body) : undefined, + }); + const text = await res.text().catch(() => ''); + let json = {}; + try { json = text ? JSON.parse(text) : {}; } catch { /* غير JSON */ } + if (!res.ok) throw new Error(`${method} ${path} -> ${res.status} ${text.slice(0, 200)}`); + return json; +} + +async function main() { + const phone = `0797${String(Date.now()).slice(-6)}`; + const { access_token: t } = await api('POST', '/auth/verify-otp', null, { phone, code: CODE }); + + const driver = await api('POST', '/drivers/apply', t, { + vehicle_make: 'LocationTest', + service_class: 'economy', + }); + await api('PATCH', `/drivers/${driver.id}/approve`, t, {}); + await api('PATCH', '/drivers/status', t, { online: true }); + console.log(`سائق: ${driver.id} (${phone}) — متصل\n`); + + // 1) واقف مكانه: الأولى ذات دلالة، والباقي يجب أن تُهمَل كلها. + let stationarySignificant = 0; + for (let i = 0; i < STATIONARY_PINGS; i++) { + const r = await api('POST', '/drivers/location', t, { ...ORIGIN, speed: 0, heading: 0 }); + if (r.significant) stationarySignificant++; + } + console.log(`واقف : ${STATIONARY_PINGS} نبضة → ${stationarySignificant} ذات دلالة (المتوقع 1: الأولى فقط)`); + + // 2) متحرّك 30م كل نبضة: كلها يجب أن تكون ذات دلالة. + let movingSignificant = 0; + for (let i = 1; i <= MOVING_PINGS; i++) { + const r = await api('POST', '/drivers/location', t, { + lat: ORIGIN.lat + metersToLat(30 * i), + lng: ORIGIN.lng, + speed: 40, + heading: 0, + }); + if (r.significant) movingSignificant++; + } + console.log(`متحرّك : ${MOVING_PINGS} نبضة × 30م → ${movingSignificant} ذات دلالة (المتوقع ${MOVING_PINGS})`); + + const totalPings = STATIONARY_PINGS + MOVING_PINGS; + const totalSignificant = stationarySignificant + movingSignificant; + const saved = Math.round(((totalPings - totalSignificant) / totalPings) * 100); + + console.log(''); + console.log(`الإجمالي: ${totalPings} نبضة → ${totalSignificant} فقط لمست Redis/الفهرس`); + console.log(`أي أن ${saved}% من النبضات كلّفت أمر EXPIRE واحداً لا أكثر.`); + console.log(''); + + const checks = [ + ['النبضة الأولى ذات دلالة', stationarySignificant >= 1], + ['الوقوف لا يُنتج نبضات ذات دلالة', stationarySignificant === 1], + ['كل تحرّك 30م ذو دلالة', movingSignificant === MOVING_PINGS], + ]; + let failed = 0; + for (const [name, pass] of checks) { + console.log(`${pass ? '✅' : '❌'} ${name}`); + if (!pass) failed++; + } + + console.log(''); + console.log(`بعد ~6 ثوانٍ يجب أن يكون الـworker كتب ${totalSignificant} نقطة مسار. تحقَّق:`); + console.log(` SELECT count(*) FROM tripz_driver_tracks WHERE driver_id = '${driver.id}';`); + console.log(` SELECT loc_status, speed, loc_updated_at FROM tripz_drivers WHERE id = '${driver.id}';`); + console.log(''); + console.log(failed === 0 ? '✅ العتبات تعمل.' : `❌ ${failed} محكّ فشل.`); + process.exit(failed === 0 ? 0 : 1); +} + +main().catch((e) => { + console.error('خطأ:', e.message); + process.exit(1); +}); diff --git a/docs/15-deploy-flow.md b/docs/15-deploy-flow.md index 037f78e..e395354 100644 --- a/docs/15-deploy-flow.md +++ b/docs/15-deploy-flow.md @@ -66,3 +66,9 @@ docker run --rm --network tripz-net -e BASE=http://tripz-api:4010/api \ docker run --rm --network tripz-net -e BASE=http://tripz-api:4010/api \ -v /home/tripz-llc/backend/scripts:/s node:22-alpine node /s/loadtest.mjs 300 30 ``` + +``` +# مسار المواقع (docs/17 — H): يثبت أن العتبات تعمل وأن الواقف لا يكلّف شيئاً +docker run --rm --network tripz-net -e BASE=http://tripz-api:4010/api \ + -v /home/tripz-llc/backend/scripts:/s node:22-alpine node /s/location-test.mjs +```