پایپلاین فروشگاه ایرانی میگوید پرداخت RTL با موفقیت تمام شده، اما ابزار مدیریت تست هنوز همان تست را Failed نشان میدهد؛ سامانه باگ دو Ticket یکسان ساخته و داشبورد نیز دو بار «پایان Run» را شمرده است. مشکل از خود تست نیست: Webhook یک بار تکرار و یک بار نامرتب تحویل شده و اتصال، Event ID و Attempt را نمیفهمد. یکپارچهسازی ابزارهای تست وقتی ارزش دارد که معنی، هویت و اثر هر پیام را حفظ کند؛ وصلشدن دو API بهتنهایی موفقیت نیست.
در این راهنما از Inventory و معماری تا Data Contract، Webhook امن، Idempotency، Retry، Trace، Reconciliation و Exit پیش میرویم. یک آزمایش قابلبازتولید نیز نشان میدهد چرا «ارسال شد» با «دقیق و فقط یک بار اعمال شد» فرق دارد.
پاسخ کوتاه: یکپارچهسازی قابلاعتماد چه شکلی است؟
برای هر جریان، Producer، Consumer، مالک، Entity، System of Record، Trigger، Contract version، Delivery semantics، Idempotency key، ترتیب، Error policy، SLO، داده حساس و روش Reconciliation را ثبت کنید. سپس Adapter را با Fixture واقعی، Duplicate، Out-of-order، Timeout، Rate limit، Schema change و Replay آزمایش کنید. اتصال را ابتدا در Shadow mode اجرا کنید و فقط وقتی Drift و side effect کنترل شد، نوشتن دوطرفه را فعال کنید.
- Contract: معنی و Schema داده قبل از Endpoint؛
- Identity: شناسه پایدار برای Run، Test، Attempt، Result، Finding و Defect؛
- Reliability: Queue، Retry محدود، Dedupe، ترتیب و Dead-letter؛
- Security: امضا، Least privilege، Secret rotation و حداقلسازی Evidence؛
- Evidence: Trace، Metric، Audit log و Reconciliation؛
- Ownership: Owner، SLO، Runbook، Compatibility policy و Exit.
این مقاله درباره چه چیزی است و چه چیزی نیست؟
موضوع این صفحه، اتصال Runner، CI، Test Management، Defect Tracker، Source Control، Security Scanner، Observability و Reporting است. تست یکپارچهسازی نرمافزار رفتار Componentهای محصول را بررسی میکند؛ این مقاله خود Toolchain کیفیت و جریان Evidence بین ابزارها را مهندسی میکند.
همچنین این صفحه جایگزین راهنمای انتخاب ابزار مدیریت تست یا طراحی Continuous Testing در CI/CD نیست. آن دو بهترتیب Procurement و جایگاه تست در Pipeline را پوشش میدهند؛ اینجا سؤال اصلی این است: «اگر ابزارها تغییر، Timeout یا Replay کردند، آیا Evidence و تصمیم Release هنوز درست میماند؟»
چه زمانی اصلاً نباید ابزارها را یکپارچه کرد؟
هر Integration یک محصول کوچک با کد، Credential، Storage، Alert، Upgrade و On-call است. اگر ماهی یک بار یک فایل کمحجم را وارد میکنید، Export/Import کنترلشده شاید از یک Sync دائمی امنتر و ارزانتر باشد. اگر داده مقصد تصمیمی را تغییر نمیدهد، داشبورد تازه فقط نسخه دیگری از داده میسازد.
Value hypothesis قابلاندازهگیری
پیش از ساخت، وضعیت پایه را اندازه بگیرید: ساعت ورود دستی، Lead time از نتیجه تا مشاهده، درصد Result بدون Build/Commit، Defectهای تکراری، اختلاف دو سیستم، زمان تعمیر Sync و هزینه خطای تصمیم. هدف «یکپارچهشدن» نیست؛ مثلاً «۹۹٪ نتیجههای معتبر حداکثر طی پنج دقیقه با Run/Commit/Environment قابلردیابی باشند و duplicate side effect صفر باشد» هدف است.
Hard gateهای توقف
اگر API رسمی/Export قابل اتکا، مجوز استفاده، دسترسی فنی پایدار، Authentication قابلمدیریت، Data residency پذیرفتهشده، Owner یا مسیر خروج ندارید، Integration را متوقف یا یک Read-only/Batch boundary انتخاب کنید. نبود API را با UI scraping شکننده پنهان نکنید.
Inventory: قبل از فلشهای معماری، واقعیت را ثبت کنید
یک جدول برای تمام Producerها، Consumerها و واسطها بسازید. نام Vendor کافی نیست؛ Edition، Version، Hosting model، Tenant، Region، Plugin، API version و Account type بر قابلیت واقعی اثر دارند.
| فیلد | پرسش عملی | Evidence |
|---|---|---|
| System/Owner | چه تیمی Change و Incident را مالک است؟ | Owner و Backup نامدار |
| Interface | REST، GraphQL، Webhook، Queue، File یا DB؟ | Versioned docs و Sandbox |
| Data | چه Entity و داده حساسی جابهجا میشود؟ | نمونه Payload پاکسازیشده |
| Limits | Rate، Page، Payload، Retention و Timeout چیست؟ | آزمون و Contract |
| Delivery | Retry، Duplicate، Ordering و Redelivery چگونهاند؟ | Failure drill |
| Change | Deprecation و Compatibility policy چیست؟ | Changelog/Notice |
| Exit | Backfill، Export و Rebuild ممکن است؟ | Restore rehearsal |
جریان را بهعنوان یک محصول تعریف کنید
«Jenkins را به Test Manager وصل میکنیم» Definition نیست. یک Integration Slice باید Subject، جهت، Trigger، Preconditions، Transformation، Destination command، Success acknowledgement، Error paths و Reconciliation داشته باشد. نمونه: «پس از بستهشدن Run معتبر، آخرین Attempt هر Test برای Commit/Artifact/Environment مشخص Upsert شود؛ Failure تأییدشده فقط یک Defect بسازد و Evidence URL را پیوند دهد.»
Command، Event و Query را قاطی نکنید
- Command: درخواست انجام کار؛ ممکن است رد شود، مانند Create defect؛
- Event: واقعیتی که رخ داده، مانند TestRunFinished؛
- Query: خواندن State بدون وعده تغییر، مانند Get latest result.
نام `test.finished` را روی پیام «لطفاً تست را تمام کن» نگذارید. این ابهام Retry و مالکیت اثر را خطرناک میکند.
نقشه Entity و هویت مشترک
بیشتر خرابیها از JSON نیست؛ از این است که دو ابزار «همان چیز» را متفاوت میشناسند. حداقل Project، Repository، Commit، Build، Artifact digest، Environment، Test definition، Suite، Run، Attempt، Result، Finding، Defect و Evidence را مدل کنید.
شناسه نمایشی را کلید نکنید
نام تست، عنوان باگ، Branch و شماره Build ممکن است عوض یا در چند Tenant تکرار شوند. کلید مرکب یا Namespaceدار بسازید: `tenant/source/project/run/test/attempt`. Mapping بین شناسه محلی و Canonical را Versioned نگه دارید و Merge دستی را Audit کنید.
Attempt با Result فرق دارد
اگر Attempt اول Failed و Attempt دوم Passed است، حذف نتیجه اول تاریخچه را تحریف میکند و نگهداشتن آخرین Delivery نیز با پیام نامرتب اشتباه است. Attemptها Immutable باشند و یک Policy جدا Verdict نهایی Run را محاسبه کند: First-attempt، Latest-valid-attempt، Any-failure یا Quarantine-aware.
System of Record برای هر Entity
یک «منبع حقیقت کل اکوسیستم» معمولاً افسانه است. Source Control مالک Commit، CI مالک Execution، Test Management مالک Plan/Case mapping و Defect Tracker مالک Workflow نقص است. برای هر Field مشخص کنید چه سیستمی Authoritative است، کدام Replica گزارشدهی است و Conflict چگونه حل میشود.
| Entity/Field | System of Record نمونه | قانون نوشتن |
|---|---|---|
| Commit/Artifact | SCM/Build registry | Immutable reference؛ مقصد حق بازنویسی ندارد |
| Run/Attempt | Runner/CI | Append-only؛ اصلاح با Event جدید |
| Test case ownership | Test Management | Explicit mapping، نه تطبیق عنوان |
| Defect state | Defect Tracker | Integration فقط Command مجاز میفرستد |
| Dashboard aggregate | Analytics replica | قابل بازسازی از Evidence |
انتخاب معماری Integration
Point-to-point
برای یک یا دو جریان کمریسک، ساده و سریع است؛ اما با رشد ابزارها Mapping، Credential و Retry در هر اتصال تکثیر میشود. هزینه فقط تعداد فلشها نیست؛ تغییر یک Status taxonomy ممکن است چندین Adapter را بشکند.
Hub-and-spoke یا Integration service
Adapterها به یک Canonical model متصل میشوند. Governance، Retry و Observability متمرکز میشود، اما Hub میتواند Bottleneck و Single Point of Failure شود. Canonical model را «مدل همهچیز» نکنید؛ Sliceهای کوچک و Versioned بسازید.
Event-driven
Producer رخداد را Publish و Consumer مستقل پردازش میکند. Decoupling و Replay بهتر میشود، ولی Eventual consistency، Duplicate، Ordering، Retention و Schema evolution به مسئله درجهیک تبدیل میشوند. Broker تضمین تجاری شما را خودکار نمیسازد.
Batch/File
برای Migration، Backfill، ابزار Legacy یا شبکه ناپایدار مناسب است. Manifest، checksum، row count، watermark، atomic import، reject file و Resume لازم دارد. CSV بدون Encoding/Timezone/Delimiter contract Integration نیست.
Data Contract پیش از کدنویسی Adapter
برای APIهای HTTP، OpenAPI 3.2.0 یک توصیف استاندارد و مستقل از زبان ارائه میکند؛ برای APIهای message-driven، AsyncAPI 3.1.0 Channel، Message، Operation، Binding و Correlation ID را مدل میکند. این مشخصات نقطه شروعاند، نه جایگزین معنی کسبوکار و Failure policy.
Payload را با نسخه مشخصِ JSON Schema Draft 2020-12 اعتبارسنجی کنید. Schema باید Required/Optional، Enum، Null، Unicode، Format، Unknown field و Size limit را روشن کند. Validation سبز ثابت نمیکند Mapping معنایی درست است؛ مثلاً هر دو `passed` و `success` String معتبرند ولی شاید Verdict متفاوتی بسازند.
Envelope پیشنهادی برای رخداد تست
{
"spec_version": "1.0",
"event_id": "01JQA...",
"event_type": "ir.example.qa.test-attempt.finished.v1",
"source": "ci/checkout",
"subject": "run-42/checkout-rtl/attempt-2",
"occurred_at": "2026-08-12T09:15:32.481+03:30",
"sequence": 3,
"correlation_id": "release-1405",
"schema_uri": "urn:qa:test-attempt-finished:1",
"data": {
"run_id": "run-42",
"test_id": "checkout-rtl",
"attempt": 2,
"status": "passed",
"duration_ms": 841,
"evidence_ref": "ev-9a3..."
}
}
CloudEvents 1.0.2 یک Envelope بیطرف برای توصیف Event و ترکیب `source + id` یکتا فراهم میکند. CDEvents 0.5.0 روی CloudEvents، واژگان رخدادهای Continuous Delivery از جمله Testing و Ticket را اضافه میکند. اینها را فقط وقتی Adopt کنید که Producer و Consumer واقعی شما Interoperability را آزمودهاند؛ Standard label بدون Contract test تضمین نیست.
واژگان Result را صریح کنید
`passed/failed` برای یک Toolchain واقعی کافی نیست. حداقل Passed، Failed، Error، Skipped، Blocked، Cancelled، Timed-out، Inconclusive و Unknown را تعریف کنید و معلوم کنید هرکدام Test outcome است یا Infrastructure outcome. «رکوردی نرسیده» هرگز معادل Passed نیست.
Mapping باید Loss را آشکار کند
اگر مقصد فقط سه State دارد، تبدیل Inconclusive به Failed یا Skipped تصمیم Release را عوض میکند. جدول Mapping باید Source value، Canonical value، Destination value، Loss، Owner و Rule version داشته باشد. داده خام را برای Replay نگه دارید و در UI علامت بزنید که مقدار Derived است.
هر Format برای هر نتیجهای مناسب نیست
خروجی JUnit-like بین ابزارها رایج است اما Variantهای Vendor درباره Suite، Retry، Attachment و Status یکسان نیستند؛ با Fixture ابزار واقعی تست کنید. برای نتایج تحلیل ایستا، SARIF 2.1.0 استاندارد تخصصی OASIS است، نه قالب عمومی همه تستهای Functional و Performance. Canonical model داخلی نباید ظرافت Domain را حذف کند.
Delivery semantics: پیام ممکن است تکراری و نامرتب برسد
شبکه میتواند پس از اعمال درخواست اما پیش از دریافت Response قطع شود. Producer نمیداند اثر رخ داده یا نه و Retry میکند. Queue/Webhook نیز ممکن است Redelivery داشته باشد. بنابراین «Exactly once» را بهعنوان ویژگی End-to-end فرض نکنید؛ Effect-once را با شناسه رویداد، Idempotent consumer و Transaction طراحی و آزمایش کنید.
چهار زمان را جدا نگه دارید
- Occurred at: رخداد در منبع چه زمانی اتفاق افتاد؛
- Produced at: پیام چه زمانی ساخته شد؛
- Received at: Integration چه زمانی آن را گرفت؛
- Applied at: تغییر مقصد چه زمانی Commit شد.
Latency شبکه را با Duration تست مخلوط نکنید. Timezone و offset را نگه دارید، ساعتها را Monitor کنید و برای ترتیب از timestamp تنها استفاده نکنید؛ Clockها میتوانند Skew داشته باشند.
Sequence باید Scope داشته باشد
یک عدد Global برای همه پروژهها Bottleneck میشود. Sequence یا Revision را در Scope مشخص—مثلاً هر Run/Test—تعریف کنید. Consumer مقدار کمتر یا مساوی را Duplicate/Stale تشخیص دهد، اما Gap را نیز Alert کند؛ ردکردن `seq=۷` چون `seq=۸` زودتر رسیده، بدون Reconciliation ممکن است Evidence دیگری را گم کند.
الگوی Idempotent consumer
- Signature و Schema را پیش از Parse/Apply بررسی کنید؛
- `event_id + source` را در یک Dedupe store با Retention مناسب جستوجو کنید؛
- نسخه یا Sequence Entity را با State فعلی مقایسه کنید؛
- Mutation و ثبت processed-event را در یک Transaction یا الگوی سازگار Commit کنید؛
- پس از Commit، Ack بدهید؛
- نتیجه Duplicate/Stale/Applied/Rejected را قابلمشاهده کنید.
اگر Dedupe record زودتر از Mutation Commit شود، Crash میتواند Event اجرانشده را برای همیشه «پردازششده» نشان دهد. اگر دیرتر ثبت شود، Side effect تکرار میشود. برای مقصد بیرونی، Idempotency key و Upsert رسمی آن را آزمایش کنید؛ اگر ندارد، یک Outbox/Inbox و State machine محلی لازم است.
Retry را طبقهبندی کنید، نه اینکه همهچیز را دوباره بفرستید
| خطا | نمونه | رفتار |
|---|---|---|
| Transient | Timeout، ۵۰۲/۵۰۳، قطع موقت | Backoff نمایی + Jitter + سقف + Budget |
| Throttling | ۴۲۹ یا Quota | احترام به Retry-After و کاهش Concurrency |
| Permanent | ۴۰۱/۴۰۳، Mapping نامعتبر | Retry کور ممنوع؛ Alert و Repair |
| Poison data | Schema/Enum ناشناخته | Quarantine/DLQ با Payload reference |
| Conflict | Revision قدیمی یا Owner متفاوت | Policy صریح، نه Last-write-wins مخفی |
Retry بینهایت هم Load incident را تشدید میکند و هم داده قدیمی را بعداً روی State جدید مینویسد. Max attempts، max age، deadline، Circuit breaker و Manual replay authorization را ثبت کنید. DLQ قبرستان نیست؛ Owner، SLA، ابزار Inspect/Redact/Replay و تست Recovery میخواهد.
Side effectهای خطرناک: ساخت باگ و تغییر Gate
ایجاد Defect، ارسال اعلان و تغییر Release Gate باید پشت State transition معتبر باشد، نه پشت دریافت هر Payload. Fingerprint نقص میتواند از Project، Canonical test ID، normalized failure signature، Environment class و affected version ساخته شود؛ عنوان متنی کلید خوبی نیست. قواعد Reopen، Link، Suppress، Quarantine و Close را با چرخه عمر باگ همراستا کنید.
received -> authenticated -> validated -> deduplicated
-> ordered -> mapped -> applied -> reconciled
هر انتقال:
owner + timestamp + input digest + rule version + outcome
دو Worker ممکن است همزمان همان Failure را ببینند. Unique constraint یا Conditional write را در مرز ساخت Defect قرار دهید؛ «اول Query کن، سپس Create» بدون Lock اتمیک Race دارد.
Polling، Webhook یا Queue؟
| روش | نقطه قوت | ریسک اصلی | کنترل |
|---|---|---|---|
| Polling | کنترل Consumer و Backfill ساده | Lag، Rate و Page drift | Cursor/Watermark + overlap + dedupe |
| Webhook | Feedback سریع | Duplicate، Forgery، downtime | HMAC + queue + redelivery + reconcile |
| Broker/Queue | Buffer و decoupling | Ordering/retention/poison | Partition key + DLQ + replay policy |
| Batch | ممیزی و Migration | Partial import و stale snapshot | Manifest + atomicity + watermark |
اغلب الگوی سالم ترکیبی است: Webhook برای سرعت، Poll/Reconciliation برای Completeness و Batch برای Backfill. اگر Vendor delivery history کوتاهی دارد، ذخیره Raw envelope و Watermark محلی حیاتی میشود.
قرارداد API فقط Endpoint نیست
برای هر عملیات، Authentication، Authorization، Version، Pagination، Filtering، Sorting stability، Rate limit، Timeout، Idempotency، concurrency control، Error body و Deprecation را آزمایش کنید. RFC status code بهتنهایی برای Repair کافی نیست؛ error code پایدار و Correlation ID لازم است.
Pagination و Incremental sync
Offset pagination روی Dataset در حال تغییر میتواند رکورد را جا بیندازد یا تکرار کند. Cursor پایدار یا `(updated_at, stable_id)` با overlap و dedupe بهتر است. Watermark را فقط پس از Commit کامل Page جلو ببرید و Delete/tombstone را فراموش نکنید.
Partial success
Bulk API ممکن است ۲۰۰ بدهد اما چند Item رد شده باشند. Success را در سطح Item ثبت کنید، Failureها را با همان Idempotency key Replay و Total/accepted/rejected را Reconcile کنید. HTML error page پشت status ۲۰۰ را JSON موفق نخوانید.
Webhook امن و قابلبازیابی
راهنمای رسمی Webhook گیتهاب نمونه عملی خوبی از Subscribe حداقلی، Secret، HTTPS، پاسخ سریع، بررسی Event type، Redelivery و Delivery ID است. این Contract مخصوص Vendor خودتان را جایگزین نمیکند، اما Failure modeهای رایج را ملموس میسازد.
- روی Raw body—پیش از تغییر Encoding/JSON—امضا را Verify کنید؛
- Algorithm و Secret version را Allowlist و مقایسه را constant-time کنید؛
- Timestamp/window و Delivery ID را برای Replay کنترل کنید؛
- Event type/action و Tenant را قبل از Queue validate کنید؛
- پس از Durable enqueue سریع 2xx بدهید، نه پس از همه Business logic؛
- Secret rotation دوکلیدی، Revocation و Audit را تمرین کنید.
در راهنمای اعتبارسنجی delivery، HMAC-SHA256 روی Payload و مقایسه امن توضیح داده شده است. IP allowlist را دفاع کمکی بدانید، نه جای امضا؛ Proxy/CDN و تغییر Range آن را شکننده میکند.
Endpoint را به SSRF تبدیل نکنید
اگر Integration URL مقصد، artifact URL یا callback را از Payload میخواند، fetch آزادانه شبکه داخلی/metadata خطرناک است. Exact destination policy، DNS/IP validation در زمان Dial، محدودیت Redirect، Egress deny-by-default و Credential isolation را مطابق راهنمای تست SSRF و کنترل Egress اعمال کنید.
Security و Privacy داده تست
Log تست میتواند Token، Cookie، شماره موبایل، داده هویتی، Payload پرداخت، Screenshot و Source snippet داشته باشد. «محیط تست» برچسب غیرحساس نیست. Data classification، Minimize/Redact، Retention، Encryption، Tenant isolation، access review و deletion flow را در Contract وارد کنید.
- برای هر Adapter یک Service identity جدا با Scope حداقلی؛
- Credential کوتاهعمر و Secret manager، نه Token در URL/Log؛
- دسترسی Read و Write جدا و قابل لغو؛
- Artifact بزرگ خارج Payload با Reference امضاشده و انقضا؛
- Audit برای Replay، Mapping override و Manual repair؛
- Sandbox با داده مصنوعی و ممنوعیت Production secret.
Schema evolution و Compatibility
افزودن Field اختیاری معمولاً آسانتر از تغییر Type/Meaning یا حذف Enum است، اما Consumerی که Unknown field را رد میکند همان Add را نیز Breaking میکند. Policy بنویسید: Producer چه مدت Version قبلی را میفرستد، Consumer با Unknown چه میکند، Enum چگونه توسعه مییابد و Deprecation چه Notice/Sunsetی دارد.
Expand، migrate، contract
- Field/Version جدید را کنار قبلی اضافه کنید؛
- Consumerها را Dual-read و Telemetry را مقایسه کنید؛
- Producer را Switch و Backfill لازم را اجرا کنید؛
- پس از اثبات عدم مصرف، Field قدیمی را حذف کنید.
برای Interface میان تیمها، همان انضباط تست قراردادی API مفید است: نمونههای واقعی، Provider state، backward compatibility و Gate مبتنی بر مصرفکننده. Contract test نمیتواند Queue outage، Permission drift یا Mapping کسبوکار را بهتنهایی پوشش دهد.
Traceability: از Release تا یک Attempt
Dashboard که فقط تعداد Passed/Failed دارد برای Incident کافی نیست. هر Result باید به Tenant/Project، Commit، Artifact digest، Pipeline run، Environment، Test definition، Attempt، Adapter version، Contract version و Evidence reference متصل باشد. Correlation ID را همهجا کپی نکنید اگر معنی یکسان ندارد؛ Scope و Lifecycle آن را تعریف کنید.
W3C Trace Context قالب `traceparent` و `tracestate` را برای propagation بین سیستمها استاندارد میکند. برای Toolchainهایی که آن را پشتیبانی میکنند، Trace میتواند مسیر CI→Integration→Test Manager→Defect Tracker را نشان دهد؛ اما شناسه Trace جای Business IDهای Run/Test/Attempt نیست و سیاست Sampling نباید Evidence ممیزی را تصادفی حذف کند.
Observability خود Integration
Semantic Conventions مربوط به CI/CD در OpenTelemetry برای Span، Metric و Log منتشر شدهاند و در زمان نگارش Release Candidate هستند؛ Stability را Pin و تغییرات را مدیریت کنید. نامهای مشترک به correlation کمک میکنند، ولی Metricهای اختصاصی صحت Sync همچنان لازماند.
Metricهای حداقلی
- Received، authenticated، schema-rejected، applied، duplicate، stale و quarantined؛
- Delivery lag و apply latency با P50/P95/P99؛
- Retry rate، oldest retry age و DLQ depth؛
- Source-to-destination count/hash drift؛
- Duplicate side effect و conflict count؛
- Trace coverage و Resultهای بدون Commit/Artifact/Environment؛
- Manual repair volume و Mean time to reconcile.
Cardinality را کنترل کنید: `run_id` و `test_id` معمولاً Label مناسب Metric تجمیعی نیستند و هزینه/حافظه را منفجر میکنند؛ آنها را در Trace/Log قابل جستوجو نگه دارید. Alert باید actionable باشد—مثلاً «oldest unapplied event بیش از SLO» بهتر از «تعداد خطا > صفر» است.
Structured log بدون نشت Evidence
{
"event_id": "e3",
"source": "ci/checkout",
"subject_hash": "sha256:...",
"contract_version": "test-result/1.2",
"adapter_version": "git:91af...",
"outcome": "stale_ignored",
"reason_code": "SEQUENCE_BEHIND",
"trace_id": "..."
}
Payload کامل، Secret و Screenshot را در Log عمومی نریزید. Digest و Reference دسترسیدار برای Debug معمولاً کافی است.
Reconciliation: شبکه سالم هم خطاهای گذشته را درمان نمیکند
Webhook سریع است، اما Completeness را ثابت نمیکند. یک Reconciler مستقل باید Snapshot یا Window منبع را با مقصد مقایسه کند: Missing، Extra، Duplicate، Stale، Mapping mismatch، Orphan و Unauthorized mutation. مقایسه صرف Count کافی نیست؛ دو مجموعه با Count برابر میتوانند اعضای متفاوت داشته باشند.
الگوی Window و Watermark
- بازه `[last_safe_watermark – overlap, now – settle_delay]` را بخوانید؛
- Entityها را با Canonical key و version مقایسه کنید؛
- Drift را Classify و Repair plan بسازید؛
- Repair را با همان Idempotency و Authorization اجرا کنید؛
- Watermark را پس از ثبت Evidence جلو ببرید.
Overlap، رکوردهای دیررس را میگیرد و Dedupe تکرار را خنثی میکند. `settle_delay` را از توزیع واقعی Lag بسازید، نه عدد دلخواه. Full reconciliation دورهای نیز خطاهای خارج Window و Bugهای Watermark را کشف میکند.
Backfill، Replay و Repair
Replay ابزار قدرتمند و خطرناک است؛ ممکن است اعلان، Ticket یا Gate را دوباره فعال کند. Dry-run، Scope، max records، approval، destination sandbox، side-effect suppression، immutable manifest و before/after diff لازم است. Replay ID را از original event ID جدا نکنید مگر Contract دقیقاً رفتار جدید را تعریف کند.
Backfill تاریخی ممکن است Contract قدیمی داشته باشد. Adapter version و Schema registry قدیمی را قابلبازتولید نگه دارید یا یک Migration صریح بنویسید. Raw data بدون Provenance و Digest Evidence قابل اتکا نیست.
چگونه خود Integration را تست کنیم؟
Unit و property tests
Mapperها را برای Enum، Null، Unknown، Unicode فارسی، رقم فارسی/عربی/لاتین، timezone نیمساعته، large payload و malformed input تست کنید. Propertyها: یک Event دوباره اثری نسازد؛ Sequence قدیمی State جدید را عقب نبرد؛ serialize/deserialize معنی را حفظ کند؛ Redaction داده حساس را برنگرداند.
Contract و compatibility tests
Spec را Lint کنید، Fixture واقعی Producer را علیه Consumer و برعکس اجرا کنید و Version قبلی/بعدی را در Matrix نگه دارید. Mock خوشرفتار کافی نیست؛ Sandbox Vendor را با Permission، Pagination، Rate limit و خطای واقعی Qualification کنید.
Failure injection
Timeout پیش و پس از Commit، ۴۲۹ با Retry-After، ۴۰۱ پس از Rotation، duplicate، reorder، missing event، malformed signature، clock skew، queue outage، DLQ، partial bulk success، API deprecation و destination rollback را تزریق کنید. Oracle باید State و side effect نهایی را بسنجد، نه فقط HTTP response را.
Shadow، canary و rollback
نسخه جدید ابتدا Read/Transform کند اما ننویسد؛ خروجی قدیم و جدید را Diff کنید. سپس درصدی از Projectها یا جریانهای کمریسک را Canary کنید. Rollback فقط Deploy قبلی نیست: Schema، queued message، Mapping و Mutationهای مقصد نیز باید سازگار یا جبرانپذیر باشند.
Adapter را مثل بخشی از معماری فریمورک اتوماسیون Versioned، تستپذیر و دارای Exit نگه دارید؛ Script ناشناس روی یک Runner، Integration production-grade نیست.
آزمایش قطعی: Duplicate و Out-of-order چه میکنند؟
برای اعتبارسنجی مثال، یک برنامه مستقل با Node.js ۲۴.۱۸.۰ اجرا شد. داده کاملاً ساختگی است: چهار Event معنایی `e1..e4` در شش Delivery میرسند؛ Attempt دوم Passed (`e3`) پیش از Failure قدیمی Attempt اول (`e2`) تحویل میشود و `e2` و `e4` هرکدام تکرار میشوند.
const deliveries = [
{ id: 'e1', type: 'run.started', sequence: 1 },
{ id: 'e3', type: 'test.finished', attempt: 2, sequence: 3, status: 'passed' },
{ id: 'e2', type: 'test.finished', attempt: 1, sequence: 2, status: 'failed' },
{ id: 'e2', type: 'test.finished', attempt: 1, sequence: 2, status: 'failed' },
{ id: 'e4', type: 'run.finished', sequence: 4, status: 'passed' },
{ id: 'e4', type: 'run.finished', sequence: 4, status: 'passed' },
]
// naive: هر delivery را مستقیماً اعمال میکند.
// reliable: event_id را dedupe و sequence قدیمی هر subject را رد میکند.
خروجی واقعی اجرا:
delivery_count=6
semantic_events=4
delivery_order=e1>e3>e2>e2>e4>e4
naive processed=6 final_test_status=failed tickets=2 finish_actions=2
reliable accepted=3 duplicates=2 stale=1 final_test_status=passed tickets=0 finish_actions=1
تفسیر نتیجه
مصرفکننده ساده آخرین Delivery را حقیقت گرفت: Failure قدیمی روی Pass جدید نشست، دو Defect ساخت و عملیات پایان Run را دوبار اجرا کرد. مصرفکننده قابلاعتماد دو Duplicate را حذف، Event قدیمی را Stale تشخیص و فقط یک Finish action اعمال کرد. `accepted=۳` به معنی گمشدن چهارمین Event نیست؛ `e2` دیده و بهدلیل Sequence پایینتر عمداً اعمال نشد.
این Simulation اثبات عملکرد یک Broker یا Vendor نیست. Partition، crash بین DB و API خارجی، Retention، concurrent workers و Reconciliation واقعی را مدل نمیکند؛ هدف آن روشنکردن Contract تست PoC است. در Production باید همان سناریو را روی Stack منتخب با Failure injection اجرا کنید.
سناریوی کامل: فروشگاه ایرانی و پرداخت
فرض کنید Git hosting، CI، Runner UI/API، Test Management، Defect Tracker و داشبورد Release دارید. Release `۱۴۰۵.۰۵.۲۲-۳` شامل Commit، Artifact digest و Environment staging است. تست Checkout باید تومان نمایش دهد اما مبلغ PSP را با ریال بفرستد؛ متن فارسی، رقم `۱۲۳` و `۱۲۳`، callback دیررس و Retry پرداخت نیز در Evidenceاند.
Contract جریان
- CI یک Run ID Canonical و Artifact digest میسازد؛
- Runner برای هر Attempt رخداد Immutable با Sequence منتشر میکند؛
- Integration امضا/Schema را Verify و Raw envelope را Digest میکند؛
- Result با Explicit environment و currency unit Upsert میشود؛
- Failure واجدشرایط پس از Policy نهایی فقط یک Defect میسازد؛
- Reconciler Result/Defect/Run را با Source مقایسه میکند؛
- Release Gate فقط روی Complete/valid/reconciled evidence تصمیم میگیرد.
تست API خود محصول و Contract پرداخت را مطابق راهنمای تست API طراحی کنید؛ Integration ابزارها نباید Business correctness پرداخت را از یک HTTP ۲۰۰ نتیجه بگیرد.
Failure drill سناریو
- Webhook Attempt ۲ پیش از Attempt ۱؛
- Timeout بعد از ساخت Defect اما قبل از Ack؛
- تغییر `blocked` به Enum ناشناخته؛
- Rate limit مقصد در اوج Pipeline؛
- Secret rotation وسط delivery؛
- قطع دسترسی SaaS و Buffer محلی؛
- Backfill یک روز و کنترل Side effect؛
- اختلاف ریال/تومان و Unicode در Mapping.
شرایط ایران را به Gate قابلآزمون تبدیل کنید
دسترسی Vendor، ثبت Account، پرداخت، License activation، Region، Support، Marketplace plugin، IP reputation، DNS/TLS و دریافت Update ممکن است برای تیم ایرانی ناپایدار باشد. این وضعیت را با ادعای کلی «تحریم است/نیست» نبندید؛ Counsel/Procurement/IT باید Offering و زمان مشخص را بررسی و Evidence تاریخدار نگه دارند.
Unavailable scenario را آزمایش کنید: Queue محلی چه مدت Buffer میکند؟ آیا Result با CLI/File صادر و بعداً Backfill میشود؟ Credential جایگزین و DNS/Proxy مجاز چیست؟ داده حساس در Relay کجا میماند؟ اتصال degraded باید وضعیت `stale/unknown` نشان دهد، نه سبز قدیمی.
Operating model و مالکیت
Tool integration پروژهای یکباره نیست. برای هر Slice یک Product owner، Technical owner، Security owner، Data owner و On-call/Backup مشخص کنید. Vendor owner نیز باید Noticeهای API، Quota، Incident و Renewal را دنبال کند. «تیم QA» اسم Owner نیست.
Integration catalog
Catalog باید Producer/Consumer، Diagram، Contract و Adapter version، Credential owner، Data classification، SLO، Dashboard، Alert، Runbook، Replay procedure، Dependencies، Change window، Review date و Decommission plan داشته باشد. اتصال ناشناخته Shadow IT است، حتی اگر خوب کار کند.
ADR و Decision record
چرا Webhook+Reconcile بهجای Polling، چرا Canonical model، چه Consistency قابلقبول، کدام Failureها Risk accepted و چه Triggerی بازنگری را فعال میکند؟ Assumption و گزینه ردشده را بنویسید تا تیم بعدی معماری را از روی کد حدس نزند.
SLO برای Integration
Availability Endpoint بهتنهایی کافی نیست. SLIهای Completeness، Correctness، Freshness و Recoverability لازماند. نمونه قرارداد—اعداد باید از Criticality واقعی شما بیایند:
- ۹۹٫۵٪ Eventهای معتبر در پنج دقیقه Applied شوند؛
- ۱۰۰٪ Release gate Resultها Commit/Artifact/Environment داشته باشند؛
- Duplicate side effect در Defect creation برابر صفر باشد؛
- Drift بحرانی حداکثر طی ۳۰ دقیقه کشف و طی چهار ساعت Repair شود؛
- Backfill ۲۴ ساعت داده در Runbook آزمایششده جا شود.
Error budget را برای Lag و Repair تعریف کنید، اما Correctness مالی/امنیتی یا ساخت باگ تکراری را صرفاً با درصد میانگین پنهان نکنید. Hard invariantها Budgetپذیر نیستند.
هزینه و TCO Integration
License middleware فقط یک جزء است. Build/Adapter، Broker/Storage/Egress، Sandbox، Observability، Secret management، On-call، Vendor API tier، Schema migration، Backfill، Incident، Training و Exit را حساب کنید. یک اتصال ارزان که هر Upgrade دو روز کار دستی میسازد، ارزان نیست.
TCO = build + platform + vendor_tier + operations
+ change_and_upgrade + incident_and_repair
+ security_compliance + exit_and_rebuild
سناریوی Base/High-change/Access-loss را جدا کنید. حجم Event، Attachment، Retention، API call و نفر-ساعت Repair را با داده Pilot بسنجید. ROI را از زمان دستی حذفشده منهای هزینه و Risk جدید گزارش کنید، نه از تعداد Endpointها.
Scorecard آمادگی Integration
هر محور را از صفر تا پنج با Evidence امتیاز دهید؛ صفر یعنی ناشناخته/غایب، سه یعنی در Pilot اثباتشده و پنج یعنی در Failure/Recovery drill و عملیات پایدار اثباتشده. Hard gate را با Score جبران نکنید.
| محور | وزن نمونه | Evidence |
|---|---|---|
| معنی/هویت/Contract | ۲۰٪ | Entity map، Schema، Mapping، Fixture |
| Delivery/Correctness | ۲۰٪ | Duplicate/reorder/crash drill |
| Security/Privacy | ۱۵٪ | Threat model، HMAC، scope، redaction |
| Observability/Reconcile | ۱۵٪ | Trace، SLI، drift/repair exercise |
| Change/Compatibility | ۱۰٪ | Version matrix و upgrade rehearsal |
| Operations/Ownership | ۱۰٪ | Owner، Runbook، on-call |
| Access/Exit/TCO | ۱۰٪ | Backfill/export/unavailable scenario |
Confidence را جدا از Score ثبت کنید؛ مستند Vendor با اجرای Buyer-operated برابر نیست. وزنها را پیش از دیدن امتیاز نامزدها Freeze و Sensitivity را با تغییر ±۲۰٪ وزنهای اصلی بررسی کنید.
برنامه اجرایی ۳۰روزه
روز ۱ تا ۵: Inventory و Outcome
یک Slice پرتکرار و قابلبازگشت انتخاب کنید. Baseline، Entity map، System of Record، داده حساس، Access و Hard gateها را Freeze کنید.
روز ۶ تا ۱۰: Contract و Threat model
Envelope/Payload/Status/Identity/Version/Error را بنویسید. امضا، Credential، Egress، Retention، replay و abuse case را Threat-model کنید.
روز ۱۱ تا ۱۷: Adapter و Reliability
Inbox/Dedupe، Queue، bounded retry، DLQ، structured log و idempotent destination operation را بسازید. Fixtureهای Persian/Unicode و Failure injection را اجرا کنید.
روز ۱۸ تا ۲۲: Shadow و Reconciliation
بدون نوشتن مقصد، خروجی Canonical را با جریان فعلی Diff کنید. Reconciler، Drift classification، Dry-run repair و Backfill را کامل کنید.
روز ۲۳ تا ۲۷: Canary و Incident drill
یک Project کمریسک را فعال کنید. Timeout-after-commit، duplicate، reorder، rate limit، Secret rotation و Vendor outage را تمرین کنید.
روز ۲۸ تا ۳۰: Decision و Scale
SLO، Drift، Manual effort، TCO و Residual risk را مرور کنید. Adopt، Extend، Keep batch، Replace یا Stop همگی نتیجه معتبرند. Scale را SliceبهSlice انجام دهید.
Runbook رخداد Integration
- Detect: Lag/Drift/DLQ/side-effect alert را با Scope تأیید کنید؛
- Contain: Write یا side effect را Pause کنید، Raw ingestion را در صورت امنبودن ادامه دهید؛
- Preserve: Event IDs، offsets، payload digests، versions و traces را نگه دارید؛
- Classify: Source، transport، contract، mapping، destination یا permission؛
- Repair: Dry-run، approval، bounded replay و reconciliation؛
- Verify: State، side effect، count/set و Release decisions؛
- Learn: Contract/test/alert/runbook را اصلاح و Risk window را گزارش کنید.
در Incident، «صف را پاک کن» یا «همه را Replay کن» دستور امنی نیست. Scope و اثر کسبوکاری باید معلوم باشد و امکان توقف فوری وجود داشته باشد.
معیارهای سالم و ضدبازی
- Freshness همراه Completeness: سریع اما ناقص سبز نیست؛
- Applied همراه Reconciled: شمارش Ack بهتنهایی موفقیت نیست؛
- Duplicate delivery و duplicate effect جدا: اولی ممکن است طبیعی، دومی عیب است؛
- Schema rejection با source/version: Aggregate کلی عیب Producer را پنهان نکند؛
- Manual repair با زمان و علت: Automation نباید کار انسانی پنهان بسازد؛
- Unknown/Stale visible: نبود داده به Pass تبدیل نشود؛
- Change failure rate: Upgradeهای Adapter/Vendor چه Driftی ساختهاند؛
- Traceability coverage: نسبت Resultهای قابل اتصال به Release artifact.
۱۵ ضدالگوی یکپارچهسازی ابزارهای تست
- اتصال Endpoint پیش از تعریف Entity و Outcome؛
- یک System of Record برای همهچیز؛
- کلیدکردن بر نام تست یا عنوان باگ؛
- Last-write-wins بدون Version/Sequence؛
- فرض Exactly-once بدون آزمایش Crash/Retry؛
- Retry بینهایت برای ۴۰۱ و Schema error؛
- ساخت Ticket برای هر Failure delivery؛
- Webhook بدون امضا یا Verify پس از Parse؛
- پردازش کامل پیش از پاسخ Webhook؛
- لاگکردن Payload/Token/Screenshot کامل؛
- Dashboard بدون Reconciliation؛
- Mock-only testing بدون Sandbox واقعی؛
- Big Bang دوطرفه برای همه ابزارها؛
- DLQ بدون Owner و Replay procedure؛
- Integration بدون Exit، Backfill و Decommission plan.
چکلیست نهایی
- □ Outcome و Baseline اندازهگیری شدهاند.
- □ Producer/Consumer/Owner/Backup مشخصاند.
- □ Edition/API version/limits مستندند.
- □ Entity map و Canonical identity وجود دارند.
- □ System of Record هر Field معلوم است.
- □ Command/Event/Query تفکیک شدهاند.
- □ Contract و Mapping versioned هستند.
- □ Statusهای Error/Skipped/Unknown/Inconclusive صریحاند.
- □ Event ID، Attempt، Sequence و چهار Timestamp وجود دارند.
- □ Dedupe و Mutation سازگاری تراکنشی دارند.
- □ Retry محدود، DLQ و Manual replay کنترل شدهاند.
- □ Side effectها Unique/conditional هستند.
- □ Webhook HMAC/HTTPS/replay/rotation آزموده شدهاند.
- □ Service account و Scope حداقلیاند.
- □ PII/Secret/Artifact retention و Redaction روشن است.
- □ Compatibility matrix و deprecation path وجود دارند.
- □ Trace، SLI، Alert و Runbook فعالاند.
- □ Reconciliation، Backfill و Repair تمرین شدهاند.
- □ Duplicate/reorder/timeout/rate-limit/outage تزریق شدهاند.
- □ Shadow/Canary/Rollback و Exit با Evidence تأیید شدهاند.
سؤالات متداول یکپارچهسازی ابزارهای تست
بهترین معماری برای اتصال ابزارهای QA چیست؟
نسخه جهانی وجود ندارد. Point-to-point برای یک Slice ساده، Hub برای کنترل Mappingهای متعدد، Event-driven برای Decoupling/Replay و Batch برای Legacy/Backfill مناسب است. حجم، Freshness، Failure semantics، Skill، Security، TCO و Exit را با PoC بسنجید؛ معماری Hybrid اغلب عملیتر است.
آیا Webhook از Polling بهتر است؟
Webhook معمولاً سریعتر است اما delivery کامل و یکتا را تضمین نمیکند. Polling کنترل Backfill بهتری دارد اما Lag/Rate/Pagination میسازد. ترکیب Webhook برای سرعت و Reconciliation poll برای Completeness الگوی مقاومی است.
چگونه از ساخت باگ تکراری جلوگیری کنیم؟
Event را با source+event_id dedupe کنید، Failure را به Attempt/Run/Environment وصل کنید، eligibility policy داشته باشید و ساخت Defect را با Idempotency key یا Unique conditional write انجام دهید. Query-then-create بدون Lock کافی نیست؛ Reopen/close/suppress نیز State machine میخواهند.
اگر ابزار مقصد API مناسبی نداشت چه کنیم؟
ابتدا Export/Import یا Batch کنترلشده، Plugin رسمی و Read-only integration را بررسی کنید. UI scraping آخرین انتخاب و نیازمند browser/version fixture، Monitoring و Exit است. اگر Evidence بحرانی است و Interface پایدار ندارید، Replace یا عدم Integration ممکن است تصمیم درست باشد.
موفقیت Integration را با چه KPI بسنجیم؟
Completeness، Correctness، Freshness، Drift، duplicate effect، oldest retry، reconciliation/repair time، traceability coverage و ساعت کار دستی را کنار هم ببینید. تعداد API call، Dashboard یا Event پردازششده بهتنهایی Outcome کیفیت نیست.
جمعبندی
یکپارچهسازی ابزارهای تست، پروژه اتصال چند Logo نیست؛ یک سیستم توزیعشده کوچک است که Evidence و تصمیم Release را حمل میکند. در چنین سیستمی Duplicate، Reorder، Timeout، Schema change، Permission drift و Vendor outage حالت استثنایی نادر نیستند؛ جزئی از Contract عملیاتیاند.
آزمایش ساختگی نشان داد شش Delivery برای چهار Event چگونه مصرفکننده ساده را به وضعیت غلط، دو Ticket و دو Finish action میرساند، در حالی که Identity، Dedupe و Sequence اثر درست را حفظ میکنند. از Outcome و Entity شروع کنید، Data Contract و System of Record را Freeze کنید، Webhook/API را امن و Idempotent بسازید، Trace و Reconciliation را همزمان با Adapter تحویل دهید و Failure/Replay/Exit را پیش از Scale تمرین کنید.

