Fastly Fiddle 用スクリプトテストの紹介

Principal Developer Advocate, Fastly
Fastly Fiddle では、Fastly アカウントのセットアップを行うことなく Fastly のエッジクラウドプラットフォームの動作をすぐに試すことができます。現在、Fastly はユーザーが作成を試みている動作を指定するアサーションを定義する機能を追加しようとしています。
Fastly での構築をより簡単にするクラウドソリューションのレシピライブラリが先日公開され、その中でFastly 上で実行するために作成された一部のソリューションの提供が開始されました。増え続けているレシピライブラリは 100 近くに迫ります。こういったレシピを構築することで、私たちのネットワークの進化に伴って継続的にソリューションが機能していることを確認するだけでなく、そのソリューションによってどんなことができているかを明確にする優れた方法が必要であるということに気付きました。
Fiddle 用テスト
Fiddle が表示する情報は多く、圧倒されるかもしれません。ソリューションが実際に達成できた鍵となるポイントは何か、どうすればわかるのでしょうか。これまでは、皆さんの VCL コードの分析に基づいて自動で「注目すべき」HTTP ヘッダーがハイライトされていましたが、現在は Fiddle の想定動作を明確に定義できるようになりました。

テストを定義するには、まずテスト対象のロジックを含む Fiddle を記述します。簡単な例として、新しいヘッダーの情報がオリジンサーバーに転送されるように Fastly が受け取る各リクエストにヘッダーを付与してみます。
こちらがこの動作を行う Fiddle です。自由に試してみてください。
外部監視によって通常の Fastly サービス内でのこの動作をテストするのは困難です。オリジンサーバーへのリクエストにヘッダーが付与された証拠はブラウザに返されるレスポンス内にはありません。ここで必要なのは、Fastly 内部の動作に関することをアサートする機能です。
ターゲットのテスト: テストできること
Fastly を通過するリクエストのデータには主に 3 つのソース (クライアントサイドのリクエスト/レスポンス、オリジンサーバーのリクエスト/レスポンス、および Fastly プラットフォーム内でトリガーされたイベント) があります。これらをテストシンタックス内でそれぞれ clientFetch 、originFetches (複数の可能性があります)、および events として扱います。
この3つのトップレベルオブジェクトには多くの興味深い便利なプロパティがあります。
| clientFetch | Obj | クライアントから Fastly へのリクエスト (および Fastly からのレスポンス) |
| .req | Str | リクエストメソッド、パス、HTTP バージョン、ヘッダーのキー/値のペア、リクエストボディを含む HTTP リクエストのブロック |
| .resp | Str | レスポンスのステータス行とレスポンスヘッダー (ボディ以外) を含む HTTP レスポンスヘッダー |
| .respType | Str | 解析された Content-type レスポンスヘッダーの値 (MIME タイプのみ) |
| .isText | Bool | レスポンスボディをテキストとして扱うことができるかどうか |
| .isImage | Bool | レスポンスボディを画像として扱うことができるかどうか |
| .status | Num | HTTP レスポンスのステータス |
| .bodyPreview | Str | ボディを UTF-8 テキストでプレビュー (1,000字で切り捨て) |
| .bodyBytesReceived | Num | 受信したデータ量 |
| .bodyChunkCount | Num | 受信したチャンクの数 |
| .complete | Bool | レスポンスが完了したかどうか |
| .trailers | Str | HTTP レスポンスのトレーラー |
| originFetches | Array | リクエスト中に行われたオリジンフェッチ |
| [idx] | Obj | 各フェッチは1つのオブジェクトです |
| .vclFlowKey | Str | このフェッチをトリガーした VCL フローの ID |
| .req | Str | リクエストメソッド、パス、HTTP バージョン、ヘッダーのキー/値のペア、リクエストボディを含む HTTP リクエストのブロック |
| .resp | Str | レスポンスのステータス行とレスポンスヘッダー (ボディ以外) を含む HTTP レスポンスヘッダー |
| .remoteAddr | Str | オリジンの解決済み IP アドレス |
| .remotePort | Num | オリジンサーバーのポート |
| .remoteHost | Str | オリジンサーバーのホスト名 |
| .elapsedTime | Num | オリジンフェッチに要した合計時間 (ミリ秒) |
| events | Array | リクエストに関連する、VCL フロー順の Fastly VCL イベント。リスタートや ESI、オリジンシールドがある場合は、複数の VCL フローが含まれる可能性があります。 |
| [idx] | Obj | 各配列要素は1つの VCL イベントです |
| .fnName | Str | イベントの種類。「recv」、「hash」、「hit」、「miss」、「pass」、「waf」、「fetch」、「deliver」、「error」、「log」のいずれかです。 |
| .datacenter | Str | このイベントが発生した Fastly データセンターの所在地を識別する3文字のコード (「LCY」、「JFK」、「SYD」など) |
| .nodeID | Str | このイベントが発生したサーバーの識別番号 |
| .originFetch | Obj | このイベントに関連するオリジンフェッチ (前述のこのモデルの originFetches を参照)。originFetch プロパティを使用するのは FETCH イベントのみです。 |
| .logs | Array | このイベントから記録されたメッセージ文字列の配列 |
| .<various> | イベントについて Fiddle UI で報告されるあらゆるプロパティをテスト対象にすることができます。たとえば、MISS イベントでは staleExists プロパティが報告されます。 | |
| logs | Array | すべての VCL イベントから記録されたメッセージ文字列の配列 |
| insights | Array | このリクエストのインサイトタグ。インサイトタグは、推奨事項またはベストプラクティスとの相違を識別します。例 : [client-cc-missing、invalid-header ] |
オリジンサーバーのリクエストに追加したヘッダーを取得するには、originRequests コレクション内の 1 つ目の項目にある req プロパティにアクセスする必要があります。
originRequests[0].reqこれは簡単ですが、キャッシュされたオリジンサーバーからのレスポンスもチェックしたい場合はどうすれば良いのでしょうか。この場合は、フェッチイベントの .ttlプロパティを利用する必要がありますが、どれがイベントコレクション内のフェッチイベントを示すインデックスかわかりません。必要なのはフェッチイベントのイベントコレクションをフィルタリングして 1 つ目のものを取得する方法です。
集合関数
テスト対象により簡単にアクセスできるよう集合関数を用意しています。対象の文字列にこれらを使用してデータを変換してください。
| listBy(field) | Array => Array |
オブジェクトの配列を取得し、配列の配列を作成します。その配列の各サブ配列では、すべてのオブジェクトが同じ「field」の値を共有します。出力配列の項目の順序は、選択したフィールドの値が最初に入力に出現する順序に基づきます。
|
| where(field=val) | Array => Array |
オブジェクトの配列を取得し、「field」プロパティの値が「val」であるオブジェクトだけを残すようにフィルタリングします。
|
| groupBy(field) | Array => Obj |
オブジェクトの配列を取得して、それぞれが同じプロパティ「field」の値を持つ複数の配列に分割し、結果のデータを「field」値をキーとするオブジェクトに編成します。
|
| transpose() | Array => Obj |
オブジェクトの配列を取得し、配列のオブジェクトを作成します。複数の入力オブジェクトが同じプロパティ名を共有する場合、すべての値を含む配列が返され、そのプロパティが最上位レベルのプロパティになります。
|
| count() | Array => Num |
配列を取得し、長さを返します
|
| concat() | Array => Str |
配列を取得し、改行処理で区切られた、すべての配列項目を結合した文字列表現を返します。
|
1 つ目のフェッチイベントの ttl プロパティは、次の方法で識別できます。
events.where(fnName=fetch)[0].ttl最後に、この対象データにアサーションを行う必要があります。
アサーション
Fastly では、Chai で定義されたアサーションをサポートするほか、いくつかの追加を行いました。以下に便利なものを紹介します。
| Name | 値のタイプ | 説明 |
|---|---|---|
| is | 任意 | 非厳密な等価 (== を使用) |
| isJSON | なし | ターゲットが有効な JSON である (基準値は不要であるため、2つのパラメーターのみ) |
| isTrue | なし | ターゲットが True である |
| isAtLeast、 isAbove |
JSON の数値 | ターゲットが数値的に基準値よりも高い |
| isAtMost、 isBelow |
JSON の数値 | ターゲットが数値的に基準値よりも低い |
| includes | 任意 | ターゲットに基準値が含まれている。配列、文字列の部分文字列、またはオブジェクト内のプロパティのサブセットに値が含まれていることをアサートするのに使用できる |
| matches | JS 正規表現 | ターゲットが正規表現に一致する。正規表現は / で区切る必要があり、修飾子 (/abc/i など) が後に続く場合がある |
| oneOf | JSON 配列 | ターゲットの値が基準の配列内の1つ以上の値と等しいことを確認する |
| startsWith | JSON 文字列 | ターゲットの値が基準の文字列で始まることを確認する |
| endsWith | JSON 文字列 | ターゲットの値が基準の文字列で終わることを確認する |
これでサンプルの Fiddle のテストが作成できます。
originFetches[0].req includes "custom-header"
events.where(fnName=fetch)[0].ttl isAtLeast 3600000000ttl プロパティと Fastly が報告するすべてのタイミングプロパティはマイクロ秒表記であることに注意してください。つまり、3600 秒 (1時間) であれば 3,600,000,000 マイクロ秒になります。では、この UI からこれらのテスト Fiddle に追加する方法を確認してみましょう。

再度 Fiddle を実行してみます。今回はテストが実行されるのを確認してください。
Fiddle が実行されるとテストが解析され、テストシンタックスに解析不可能なものがあるかどうかが通知されます。
デバッグ
テストが成功すれば問題ありませんが、失敗したらどうすれば良いのでしょうか。テストを正しく記述できたのか、それとも Fiddle 自体にバグがあったのか、どう判断すればいいででしょうか。
実行時にテストが失敗した場合、Fiddle は障害の状態だけでなく対象の実際の値も表示します。

これは対象の文字列を構築する際にも大いに役立ちます。対象となるパスにどんなプロパティがあるのかわからない場合は、そのパスを使用したテストを記述してみてください。そのテストが失敗した場合、対象がどのようになっているのか確認することができます。
テストは非同期で実行され、非同期の計測が配信されるまで待機する必要があります。対象によっては計測データの配信が他より時間がかかります。これは各テストの砂時計アイコンで表示されます。砂時計アイコンがなければ遅延なし、砂が落ちている途中の砂時計の表示なら少々の遅れ、砂が落ち終わった砂時計の表示の場合はより大きな遅延ということです。

Fastly は Fiddle でのテスト実行が可能になったことで、Fastly の設定の確認ができ、また皆さんのロジックをエッジに移すことができると願っています。ご意見がある場合は本投稿にコメントを記載していただくか、私までご連絡ください。