From a76a35c14df05fef763b3df163dd3d39ebe1059a Mon Sep 17 00:00:00 2001 From: ht Date: Sun, 6 Sep 2026 18:14:31 +0300 Subject: [PATCH] extract exchange amount validation --- docs/quality/uygulama-durumu.md | 6 ++++++ src/components/TenderDetailModal.jsx | 5 +++-- src/services/exchangeAmount.js | 9 +++++++++ tests/exchange-amount.test.mjs | 11 +++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 src/services/exchangeAmount.js create mode 100644 tests/exchange-amount.test.mjs diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 2955d12..1717b43 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -2006,3 +2006,9 @@ Doğrulama: 287 test geçti, üretim derlemesi başarılı. İhale kartı ve detay görünümündeki şirket uyum skoru rozetleri artık yüzde değerini ve eşleşme nedenlerine nasıl erişileceğini ARIA etiketiyle bildiriyor; skor yalnızca görsel rozet veya masaüstü tooltip’ine bağlı değil. Doğrulama: 287 test geçti, üretim derlemesi başarılı. + +### 2026-09-06 · Kur tutarı doğrulama servisleştirildi + +Kur hesaplayıcısının tutar sınırı bileşen içinden çıkarılıp saf doğrulama servisine taşındı; boş, negatif, sonlu olmayan ve sınır dışı değerler için bağımsız regresyon testi eklendi. + +Doğrulama: 288 test geçti, üretim derlemesi başarılı. diff --git a/src/components/TenderDetailModal.jsx b/src/components/TenderDetailModal.jsx index 34633cd..7976915 100644 --- a/src/components/TenderDetailModal.jsx +++ b/src/components/TenderDetailModal.jsx @@ -13,6 +13,7 @@ import { import { acknowledgeNoteDraft, editNoteDraft } from '../services/noteDrafts'; import { tenderStateKey } from '../services/tenderState'; import { createTenderICS } from '../services/tenderCalendar'; +import { validateExchangeAmount, MAX_EXCHANGE_AMOUNT } from '../services/exchangeAmount'; export const TenderDetailModal = () => { const { activeTenderDetail, setActiveTenderDetail, toggleTrackTender, userProfile, setIsAiSpecAnalyzerOpen, gorevAc, gorevKontrol, aiAnalysisResults, tenders, mutateTender, savingCount, saveError, authUser, detailLoad, retryTenderDetail } = useApp(); @@ -49,7 +50,7 @@ export const TenderDetailModal = () => { const usdRate = fx?.rates?.USD; const eurRate = fx?.rates?.EUR; const fxIsStale = Boolean(fx?.refreshError); - const bidAmountValid = Number.isFinite(bidAmount) && bidAmount >= 0 && bidAmount <= 1e12; + const bidAmountValid = validateExchangeAmount(bidAmount); if (!activeTenderDetail) return null; // Havuzdaki güncel kayıt: takip/görev değişince düğme etiketleri de değişsin. @@ -385,7 +386,7 @@ export const TenderDetailModal = () => { onChange={(e) => { const value = Number(e.target.value); setBidAmount(value); - setBidAmountError(Number.isFinite(value) && value >= 0 && value <= 1e12 ? null : 'Tutar 0 ile 1.000.000.000.000 TL arasında olmalıdır.'); + setBidAmountError(validateExchangeAmount(value) ? null : `Tutar 0 ile ${MAX_EXCHANGE_AMOUNT.toLocaleString('tr-TR')} TL arasında olmalıdır.`); }} className="w-full bg-white/10 border border-white/20 rounded-lg px-3 py-1.5 text-xs text-white" /> diff --git a/src/services/exchangeAmount.js b/src/services/exchangeAmount.js new file mode 100644 index 0000000..741a11a --- /dev/null +++ b/src/services/exchangeAmount.js @@ -0,0 +1,9 @@ +const MAX_EXCHANGE_AMOUNT = 1e12; + +export function validateExchangeAmount(value) { + if (value === null || value === undefined || (typeof value === 'string' && !value.trim())) return false; + const amount = Number(value); + return Number.isFinite(amount) && amount >= 0 && amount <= MAX_EXCHANGE_AMOUNT; +} + +export { MAX_EXCHANGE_AMOUNT }; diff --git a/tests/exchange-amount.test.mjs b/tests/exchange-amount.test.mjs new file mode 100644 index 0000000..38e6fc3 --- /dev/null +++ b/tests/exchange-amount.test.mjs @@ -0,0 +1,11 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { validateExchangeAmount, MAX_EXCHANGE_AMOUNT } from '../src/services/exchangeAmount.js'; + +test('exchange amount validator accepts finite non-negative amounts within limit', () => { + assert.equal(validateExchangeAmount(0), true); + assert.equal(validateExchangeAmount('125,50'), false); + assert.equal(validateExchangeAmount(125.5), true); + assert.equal(validateExchangeAmount(MAX_EXCHANGE_AMOUNT), true); + for (const value of [-1, Infinity, NaN, MAX_EXCHANGE_AMOUNT + 1, null, '']) assert.equal(validateExchangeAmount(value), false); +});