require 'minitest/autorun' require 'rack/mock' require 'google/protobuf' require 'json' require_relative '../lib/twirp/client_json' require_relative './fake_services' class ClientJSONTest < Minitest::Test def test_client_json_requires_service assert_raises ArgumentError do Twirp::ClientJSON.new("http://localhost:8080") # missing service end Twirp::ClientJSON.new("http://localhost:8080", service: "FooBar") # ok end def test_client_json_success c = Twirp::ClientJSON.new(conn_stub("/my.pkg.Talking/Blah") {|req| assert_equal "application/json", req.request_headers['Content-Type'] assert_equal '{"blah1":1,"blah2":2}', req.body # body is json [200, {}, '{"blah_resp": 3}'] }, package: "my.pkg", service: "Talking") resp = c.rpc :Blah, blah1: 1, blah2: 2 assert_nil resp.error refute_nil resp.data assert_equal 3, resp.data["blah_resp"] refute_nil resp.headers end def test_client_json_thennable c = Twirp::ClientJSON.new(conn_stub_thennable("/my.pkg.Talking/Blah") {|req| assert_equal "application/json", req.request_headers['Content-Type'] assert_equal '{"blah1":1,"blah2":2}', req.body # body is json [200, {}, '{"blah_resp": 3}'] }, package: "my.pkg", service: "Talking") resp_thennable = c.rpc :Blah, blah1: 1, blah2: 2 # the final `.then {}` call will yield a ClientResp assert resp_thennable.is_a?(Thennable) resp = resp_thennable.value assert resp.is_a?(Twirp::ClientResp) # the final Thennable will have come from one with a faraday response assert resp_thennable.parent.is_a?(Thennable) assert resp_thennable.parent.value.is_a?(Faraday::Response) # the final ClientResp should look the same as when then isn't used assert_nil resp.error refute_nil resp.data assert_equal 3, resp.data["blah_resp"] end def test_client_json_strict_encoding c = Twirp::ClientJSON.new(conn_stub("/my.pkg.Talking/Blah") {|req| assert_equal "application/json; strict=true", req.request_headers['Content-Type'] assert_equal '{"blah1":1,"blah2":2}', req.body # body is json [200, {}, '{"blah_resp": 3}'] }, package: "my.pkg", service: "Talking", strict: true) resp = c.rpc :Blah, blah1: 1, blah2: 2 assert_nil resp.error refute_nil resp.data assert_equal 3, resp.data["blah_resp"] end def test_client_json_error c = Twirp::ClientJSON.new(conn_stub("/Foo/Foomo") {|req| [400, {}, '{"code": "invalid_argument", "msg": "dont like empty"}'] }, service: "Foo") resp = c.rpc :Foomo, foo: "" assert_nil resp.data refute_nil resp.error assert_equal :invalid_argument, resp.error.code assert_equal "dont like empty", resp.error.msg refute_nil resp.headers end def test_client_bad_json_route c = Twirp::ClientJSON.new(conn_stub("/Foo/OtherMethod") {|req| [404, {}, 'not here buddy'] }, service: "Foo") resp = c.rpc :OtherMethod, foo: "" assert_nil resp.data refute_nil resp.error assert_equal :bad_route, resp.error.code refute_nil resp.headers end def conn_stub(path) Faraday.new do |conn| conn.adapter :test do |stub| stub.post(path) do |env| yield(env) end end end end # mock of a promise-like thennable, allowing a call to ".then" to get the real value class Thennable attr_reader :value, :parent def initialize(value, parent = nil) @value = value @parent = parent end def then(&block) # similar to a promise, but runs immediately Thennable.new(block.call(@value), self) end end module ThennableFaraday def post(*) Thennable.new(super) end end def conn_stub_thennable(path, &block) s = conn_stub(path, &block) s.extend(ThennableFaraday) s end end