[インデックス 18770] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/rpc/jsonrpc パッケージに、一時的に無効化された失敗するテストケースを追加するものです。このテストは、JSON-RPCサーバーがエラーを返した場合に、結果フィールドが null であるべきという挙動を検証するために書かれました。これは、既存のバグまたは予期せぬ動作を浮き彫りにすることを目的としています。
コミット
commit 31731b27cd9001d399e0e3c38c149c51dacb22ac
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Wed Mar 5 16:01:37 2014 -0800
net/rpc/jsonrpc: add temporarily-disabled failing test
To be enabled by https://golang.org/cl/71230045/
Update #7442
LGTM=adg
R=adg
CC=golang-codereviews
https://golang.org/cl/69860056
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/31731b27cd9001d399e0e3c38c149c51dacb22ac
元コミット内容
net/rpc/jsonrpc: add temporarily-disabled failing test
To be enabled by https://golang.org/cl/71230045/
Update #7442
変更の背景
このコミットは、Goの net/rpc/jsonrpc パッケージにおける特定の挙動、具体的にはRPC呼び出しがエラーを返した場合のJSONレスポンスの構造に関する問題を修正するための準備として行われました。JSON-RPC 2.0の仕様では、RPC呼び出し中にエラーが発生した場合、レスポンスオブジェクトの result メンバーは存在してはならず、代わりに error メンバーが存在する必要があります。しかし、当時の net/rpc/jsonrpc の実装では、エラーが発生した場合でも result フィールドに予期せぬ値が含まれてしまう可能性がありました。
このコミットで追加されたテスト TestServerErrorHasNullResult は、この仕様違反を検出するために設計されました。テストは一時的に無効化されています (t.Skip)。これは、テストが現在の実装では失敗することが既知であり、このコミットの目的がバグの修正ではなく、バグの存在を明確にするためのテストケースの追加であるためです。コミットメッセージにある https://golang.org/cl/71230045/ は、このテストを有効化し、関連するバグ修正を適用する別の変更リスト(Change List)を指しています。また、Update #7442 は、この問題がGoのイシュートラッカーで追跡されていることを示唆しています。
前提知識の解説
JSON-RPC
JSON-RPCは、リモートプロシージャコール(RPC)プロトコルの一種で、データフォーマットとしてJSONを使用します。クライアントがサーバー上のメソッドを呼び出し、その結果をJSON形式で受け取るための軽量なプロトコルです。
JSON-RPC 2.0の仕様では、RPC呼び出しのレスポンスは以下のいずれかの形式を取ります。
-
成功レスポンス:
{ "jsonrpc": "2.0", "result": <結果の値>, "id": <リクエストID> }この場合、
errorメンバーは存在してはなりません。 -
エラーレスポンス:
{ "jsonrpc": "2.0", "error": { "code": <エラーコード>, "message": <エラーメッセージ>, "data": <オプションのエラーデータ> }, "id": <リクエストID> }この場合、
resultメンバーは存在してはなりません。仕様では、resultメンバーが存在しないか、あるいはnullであるべきとされています。特に、エラーが発生した場合はresultフィールドがnullであることが推奨されます。
Go言語の net/rpc および net/rpc/jsonrpc パッケージ
net/rpc: Go言語の標準ライブラリで提供されるRPC(Remote Procedure Call)フレームワークです。これは、ネットワーク経由でGoプログラム間でメソッドを呼び出すための汎用的なメカニズムを提供します。エンコーディングには依存せず、様々なプロトコルをサポートできます。net/rpc/jsonrpc:net/rpcパッケージのサブパッケージで、JSON-RPC 2.0プロトコルをnet/rpcフレームワーク上で実装するためのものです。これにより、GoのRPCサーバーがJSON-RPCクライアントと通信できるようになります。NewServerCodecやNewClientCodecなどの関数を提供し、JSON形式でのリクエストとレスポンスのエンコード/デコードを扱います。
io.Reader, io.Writer, io.Closer インターフェース
Go言語の標準ライブラリ io パッケージで定義されている基本的なインターフェースです。
io.Reader:Read(p []byte) (n int, err error)メソッドを持つインターフェース。データを読み込むための抽象化を提供します。io.Writer:Write(p []byte) (n int, err error)メソッドを持つインターフェース。データを書き込むための抽象化を提供します。io.Closer:Close() errorメソッドを持つインターフェース。リソースをクリーンアップするための抽象化を提供します。
これらのインターフェースは、ネットワーク接続、ファイル、メモリバッファなど、様々なデータストリームを統一的に扱うために広く利用されます。
bytes.Buffer と strings.NewReader
bytes.Buffer:bytesパッケージで提供される可変長のバイトバッファです。io.Writerインターフェースを実装しており、メモリ内でデータを効率的に構築するために使用されます。strings.NewReader:stringsパッケージで提供される関数で、文字列からio.Readerを作成します。これにより、文字列をファイルやネットワーク接続のように読み込むことができます。
ioutil.NopCloser
io/ioutil パッケージで提供される関数で、io.Closer インターフェースを実装するが、Close メソッドが何もしない(no-operation)ラッパーを返します。テストなどで io.Closer が必要な場合に、実際のクローズ処理が不要なダミーとして使用されます。
技術的詳細
このコミットの核心は、net/rpc/jsonrpc パッケージがJSON-RPC 2.0仕様に準拠していることを確認するためのテストケース TestServerErrorHasNullResult の追加です。
テストのシナリオは以下の通りです。
-
サーバーコーデックのセットアップ:
jsonrpc.NewServerCodecを使用してサーバー側のコーデック (sc) を作成します。このコーデックは、io.Reader、io.Writer、io.Closerの組み合わせを引数として取ります。Reader:strings.NewReaderを使用して、RPCリクエストのJSON文字列{"method": "Arith.Add", "id": "123", "params": []}を提供します。これは、サーバーが処理する架空のRPC呼び出しをシミュレートします。Writer:bytes.Buffer(out) を使用します。サーバーからのJSONレスポンスはこのバッファに書き込まれ、後で検証されます。Closer:ioutil.NopCloser(nil)を使用します。これは、テスト中にクローズ操作が不要なためです。
-
リクエストヘッダの読み込み:
sc.ReadRequestHeader(r)を呼び出して、クライアントからのリクエストヘッダを読み込みます。これにより、サーバーは受信したリクエストのメソッド名、IDなどを解析します。 -
エラーレスポンスの書き込み:
sc.WriteResponseを呼び出して、サーバーからのレスポンスを書き込みます。rpc.Responseオブジェクトを作成し、ErrorフィールドにerrorText("some error") を設定します。これは、RPC呼び出しがサーバー側でエラーを発生させたことをシミュレートします。valueText("the value we don't want to see") を結果として渡します。この値は、エラーレスポンスの場合にはJSON出力に含まれるべきではありません。
-
レスポンスの検証:
out.String()に書き込まれたJSONレスポンスを検証します。!strings.Contains(out.String(), errorText): レスポンスにエラーメッセージ ("some error") が含まれていることを確認します。これは、エラーレスポンスが正しく生成されたことを示します。strings.Contains(out.String(), valueText): これがテストの主要なアサーションです。 レスポンスにvalueText("the value we don't want to see") が含まれていないことを確認します。JSON-RPC 2.0の仕様では、エラーレスポンスの場合、resultフィールドは存在しないか、nullであるべきであり、実際の成功時の結果値が含まれていてはなりません。もしvalueTextが含まれていれば、それは仕様違反であり、テストは失敗します。
このテストは、t.Skip("Known failing test; Issue 7442") によって一時的に無効化されています。これは、このコミットが作成された時点では、net/rpc/jsonrpc がこの特定のシナリオでJSON-RPC 2.0の仕様に完全に準拠していなかったことを示しています。テストを追加することで、開発者はこの問題を認識し、将来のコミットで修正を適用できるようになります。
コアとなるコードの変更箇所
変更は src/pkg/net/rpc/jsonrpc/all_test.go ファイルに集中しています。
--- a/src/pkg/net/rpc/jsonrpc/all_test.go
+++ b/src/pkg/net/rpc/jsonrpc/all_test.go
@@ -5,6 +5,7 @@
package jsonrpc
import (
+ "bytes"
"encoding/json"
"errors"
"fmt"
@@ -12,6 +13,7 @@ import (
"io/ioutil"
"net"
"net/rpc"
+ "strings"
"testing"
)
@@ -202,6 +204,40 @@ func TestMalformedOutput(t *testing.T) {
}
}
+func TestServerErrorHasNullResult(t *testing.T) {
+ t.Skip("Known failing test; Issue 7442")
+ var out bytes.Buffer
+ sc := NewServerCodec(struct {
+ io.Reader
+ io.Writer
+ io.Closer
+ }{
+ Reader: strings.NewReader(`{"method": "Arith.Add", "id": "123", "params": []}`),
+ Writer: &out,
+ Closer: ioutil.NopCloser(nil),
+ })
+ r := new(rpc.Request)
+ if err := sc.ReadRequestHeader(r); err != nil {
+ t.Fatal(err)
+ }
+ const valueText = "the value we don't want to see"
+ const errorText = "some error"
+ err := sc.WriteResponse(&rpc.Response{
+ ServiceMethod: "Method",
+ Seq: 1,
+ Error: errorText,
+ }, valueText)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !strings.Contains(out.String(), errorText) {
+ t.Fatalf("Response didn't contain expected error %q: %s", errorText, &out)
+ }
+ if strings.Contains(out.String(), valueText) {
+ t.Errorf("Response contains both an error and value: %s", &out)
+ }
+}
+
func TestUnexpectedError(t *testing.T) {
cli, srv := myPipe()
go cli.PipeWriter.CloseWithError(errors.New("unexpected error!")) // reader will get this error
コアとなるコードの解説
追加された TestServerErrorHasNullResult 関数は、以下の重要な側面をテストしています。
-
bytes.Bufferとstrings.NewReaderの利用: テストは実際のネットワーク接続を使用せず、bytes.Bufferを出力 (io.Writer) として、strings.NewReaderを入力 (io.Reader) として使用することで、メモリ内でJSON-RPCの通信をシミュレートしています。これにより、テストの実行が高速になり、外部依存性がなくなります。 -
NewServerCodecの使用:jsonrpc.NewServerCodecは、net/rpcのサーバー側でJSON-RPCプロトコルを処理するためのコーデックを作成します。テストでは、このコーデックがエラーレスポンスを正しく生成するかどうかを検証します。 -
rpc.ResponseのErrorフィールド:sc.WriteResponseに渡されるrpc.ResponseオブジェクトのErrorフィールドに文字列を設定することで、サーバーがエラーを返した状況をシミュレートします。 -
valueTextの存在チェック: 最も重要なのは、if strings.Contains(out.String(), valueText)の部分です。valueTextは、エラーが発生したにもかかわらず、JSONレスポンスのresultフィールドに誤って含まれてしまう可能性のある値です。JSON-RPC 2.0の仕様では、エラーレスポンスにはresultフィールドが含まれるべきではない(またはnullであるべき)ため、このvalueTextが出力に含まれていないことを確認することが、テストの目的です。もし含まれていれば、それはバグであり、テストはt.Errorfを呼び出して失敗します。 -
t.Skipによる一時的な無効化:t.Skip("Known failing test; Issue 7442")は、このテストが意図的にスキップされることを示します。これは、このテストが現在の実装では失敗することが既知であり、このコミットの目的がバグの修正ではなく、バグの存在を明確にするためのテストケースの追加であるためです。このテストは、関連するバグが修正された後に有効化される予定です。
このテストは、net/rpc/jsonrpc パッケージがJSON-RPC 2.0の厳密な仕様に準拠していることを保証するための重要なステップです。
関連リンク
- Go言語の
net/rpcパッケージのドキュメント: https://pkg.go.dev/net/rpc - Go言語の
net/rpc/jsonrpcパッケージのドキュメント: https://pkg.go.dev/net/rpc/jsonrpc - JSON-RPC 2.0 Specification: https://www.jsonrpc.org/specification
参考にした情報源リンク
- Go言語の公式ドキュメント
- JSON-RPC 2.0 Specification
- GitHubのgolang/goリポジトリのコミット履歴
- Go言語のイシュートラッカー (ただし、Issue 7442の直接的な公開情報は見つからなかったため、内部的な追跡番号である可能性が高い)