GH GambleHub

契約テスト

1)契約の適用先

HTTP REST/JSON:リソース、ページネーション、フィルタ、idempotency、エラーコード。
gRPC/Protobuf:メッセージタイプ、ステータス、'deadline'セマンティクス、backward-compat v。proto。
GraphQL:スキーマ、非null、ディレクティブ、フィールドへのパーミッシブ。
メッセージ/ストリーム(Kafka/Pulsar/SQS):イベントスキーム(Avro/JSON/Protobuf)、パーティションキー、オーダー、idempotentキー。
内部SDK/ライブラリ: Public Features/Exceptions/Performance Contracts。


2) CDCモデル: 役割とアーティファクト

消費者は期待契約(サンプル要求/応答、タイプマッチャー、不変量)を発行します。
サプライヤは、サービス/アダプタ/ハンドラに対して契約検証を実行します。
契約ブローカー(Pact Broker/Backstage/repo artifact)は、バージョン、タグ('prod'、 'staging'、 'canary')、および'consumer@v→provider@v'互換性マトリックスを格納します。
リリースポリシー:プロバイダを送信することは禁止されています。


3)契約で何を修正するか(HTTPの例)

最低:
  • メソッド/パス/パラメータ/ヘッダ(Auth、 idempotentキーを含む)。
  • Bodyと典型的なマッチャー(type/format/regexp/range)。
  • エラーコードと構造;stable 'error_code'
  • セマンティック不変量:並べ替え、一意性、単調な'created_at'。
  • 機能しない期待値(オプション):p95、サイズ制限、レート制限ヘッダ。
契約フラグメント(簡略化):
json
{
"interaction": "GET /v1/users/{id}",
"request": { "method": "GET", "path": "/v1/users/123", "headers": {"Accept":"application/json"} },
"matchers": {
"response.body.id": "type:number",
"response.body.email": "regex:^.+@.+\\..+$",
"response.body.created_at": "format:rfc3339"
},
"response": {
"status": 200,
"headers": {"Content-Type":"application/json"},
"body": {"id": 123, "email": "alice@example.com", "created_at": "2025-10-31T12:00:00Z"}
},
"error_cases": [
{
"name":"not_found",
"request":{"path":"/v1/users/9999"},
"response":{"status":404, "body":{"error_code":"USER_NOT_FOUND"}}
}
]
}

4)イベント主導の契約

イベントスキーマ:'type'、 'version'、 'id'、 'overced_at_utc'、 'producer'、 'subject'、 'payload'。
不変量:'(type、 id)'による不変'id'とidempotence、部品のキー内の順序、単調'sequence'。
スキーマレジストリ-進化と互換性のルールを格納します(後方/前方/完全)。
消費者契約テスト:リプレイ「ゴールデン」イベントとネガのフェーズ(不明なフィールド、無効)。

Avroダイアグラムの例(フラグメント):
json
{
"type":"record","name":"UserRegistered","namespace":"events.v1",
"fields":[
{"name":"id","type":"string"},
{"name":"occurred_at_utc","type":{"type":"long","logicalType":"timestamp-millis"}},
{"name":"email","type":"string"},
{"name":"marketing_opt_in","type":["null","boolean"],"default":null}
]
}

5)進化と互換性

契約バージョン:'MAJOR'セマンティクス。マイナー。PATCH '(MAJOR-breaking)。

RESTのルール:
  • 破損しない:フィールドを削除しない、type/value 'error_code'を変更しないでください。
  • デフォルトでオプションのフィールドを追加します。「魔法」の代わりに新しいエンドポイント。
  • Decrement:宣言、同時サポート、メトリックによる削除。
  • GraphQL: add-onlyフィールド、null以外はフェーズを入力します。ディレクティブを削除します。
  • gRPC/Proto:フィールド番号を再利用しない。オプションで新しいものだけを追加します。
  • イベント:スキーム'vN';消費者は未知の分野(寛大さ)を無視する必要があります。

6)否定的で、不変な点検

負:不正確なタイプ、禁止された値、競合するパラメータ、制限を超えています。
不変量:応答の並べ替え、'id'の一意性、'next_cursor'の正確性、繰り返したときのidempotent応答の安定性。
一時的な側面の契約:'created_at' RFC3339/UTC、現地の日の正しい予測は輸送契約の一部ではありません-ビジネス不変量に送信されます。


7)刺しゅうの生成およびローカル開発

契約から、プロバイダスタックは消費者開発のために生成されます。
イベントの場合-スキームに従って「有効/ボーダーライン」メッセージのジェネレータ。
スタッフは契約バージョンとビルド日でマークされています。prodの出版物。


8) CI/CD(参照パイプライン)に埋め込むこと)

1.消費者CI:

Lint/build→contract generation→unit/test contract→contract-broker(タグ: 'consumer@1。7.0`).

2.プロバイダCI:

コンテナ内/ローカルでサービスを調達する→関連する契約('prod'/'staging')→検証→ブローカー内のステータスの公開を取得する。

3.リリースゲート:

プロバイダのデプロイは、未契約がある場合にブロックされます。

4.夜行行列:

互換性マトリックス'コンシューマーバージョン×プロバイダバージョン';レポートおよび警報。


9)ドメイン別の実践例

9.1 REST:カーソルページネーション(contract invariant)

応答には'items[]'、'next_cursor' (nullable)、 'limit'、 'total' (optional)が含まれます。
不変量:'len (items) ≤ limit'、同じ'cursor'→idempotentセットで繰り返される呼び出し。
'cursor'と'page'の両方が指定されている場合にエラーが発生します。

9.2 POST idempotency

契約には「Idempotency-Key」ヘッダーが必要です。
不変性:同じキーを持つ繰り返しクエリは、同じ'id '/statusを返します。

9.3イベント:注文の保証

契約のパーティションキーは'partition_key=user_id'です。
不変性:'sequence'はキー内で単調に増加します。消費者はリプレイを処理しなければなりません。


10)契約におけるセキュリティとプライバシー

個人データ/秘密を例に含めないでください-合成だけ。
必須のセキュリティヘッダーを修正:'Authorization'、 'X-Signature'、 'Replay-Prevention'。
Webhookの場合-署名と応答の契約'2xx'/リトレイ。
契約テストのログ-機密フィールドのマスキング。


11)ツール

Pact/Pactflow/Pact Broker-HTTP/Message契約、互換性マトリックス。
OpenAPI/AsyncAPI-仕様+テストジェネレータ(Dredd、 Schemathesis)。
空手/REST保証-REST契約のシナリオチェック。
Protobuf/gRPC-'buf'、 'protolint'、互換性試験;ストリーム内のAvro/JSON/Protoのスキーマレジストリ。
GraphQL (graphql-compat)の準拠テスト、スナップショット回路テスト。


12)プロバイダ検証の擬似コード(簡略化)

python def verify_contract(provider, contract):
for case in contract["cases"]:
req = build_request(case["request"])
res = provider.handle(req) # локально/контейнер assert match_status(res.status, case["response"]["status"])
assert match_headers(res.headers, case["response"].get("headers", {}))
assert match_body(res.body, case["matchers"], allow_extra_fields=True)
for neg in contract.get("error_cases", []):
res = provider.handle(build_request(neg["request"]))
assert res.status == neg["response"]["status"]
assert res.json.get("error_code") == neg["response"]["body"]["error_code"]

13)アンチパターン

「ポストマンのスクリーンショットは契約です」:バージョン/典型的なマッチャー/自動検証はありません。
オーバーナップ:コントラクトは、type/patterns→falseの値ではなく、正確な値を修正します。
異なった地域/チャネルのための1つの共通の契約:変動性(フラグ、地理規則)を無視します。
ブローカー/マトリックスなしの契約:どのバージョンが互換性があるかを理解することは不可能です。
契約の代わりにe2eに賭ける:遅く、高価で、不安定。
負/不変性のケースはありません:「グリーントラック」のみがテストされます。


14) Observabilityおよび操作

ブローカー+ダッシュボード「健康契約」にステータスをエクスポートします。
アラート:プロバイダの"prod'契約に対する新しいドロップ、イベントの"不明なフィールド"の増加。
トレース:'contract_id'、 'version'、 'decision_id'を検証ログに記録します。


15)うつ病プロセス

1.フィールド/エンドポイント(非破壊)を追加します。
2.指定で古いものを「非推奨」としてマークし、日付を発表します。
3.ログ/ブローカーで消費者を追跡する。マイグレーションガイド。
4.ステージで「shadow」 denyを有効にして(dry-run)、強制します。
5.使用率と互換性がゼロになった後に削除します。


16)建築家チェックリスト

1.消費者とその所有者が特定されましたか?契約はバージョン管理されていますか?
2.ブローカーと環境タグとの互換性マトリックスはありますか?
3.マイナスと不変量(特異性、カーソル、ソート)は契約に含まれていますか?
4.Schema Registryと互換性モードはイベントに設定されていますか?
5.パイプラインは、生産契約に違反した場合にプロバイダのリリースをブロックしますか?
6.減価償却プロセスと進化政策は説明されていますか?
7.スタブは契約から生成されますが、ローカルのイベントジェネレータはありますか?
8.PDマスキングと必須の安全見出しは文書化されていますか?
9.契約に関するメトリック/アラートが接続されていますが、ドリフトレポートはありますか?
10.契約は、双方の当事者(消費者とプロバイダー)によってCIでレビューされますか?


おわりに

契約テストは、インタラクションに関する「真実」をバージョン管理されたアーティファクトに転送し、統合を予測可能にします。CDC、コントラクト・ブローカー、スキーム・エボリューションの分野では「、驚き」を迅速なチェック、不変量のクリア、透明なバージョンの互換性などの管理プロセスに置き換えています。これにより、e2eのコストを削減し、リリースをスピードアップし、プラットフォーム全体の品質を向上させます。

Contact

お問い合わせ

ご質問やサポートが必要な場合はお気軽にご連絡ください。いつでもお手伝いします!

統合を開始

Email は 必須。Telegram または WhatsApp は 任意

お名前 任意
Email 任意
件名 任意
メッセージ 任意
Telegram 任意
@
Telegram を入力いただいた場合、Email に加えてそちらにもご連絡します。
WhatsApp 任意
形式:+国番号と電話番号(例:+81XXXXXXXXX)。

ボタンを押すことで、データ処理に同意したものとみなされます。