|
312 | 312 | let(:answer) { { "foo" => "bar" } } |
313 | 313 | let(:query) { "my query here" } |
314 | 314 |
|
| 315 | + it "reconnects and retries after the connection times out" do |
| 316 | + response = instance_double(Net::HTTPOK, code: "200", body: JSON.generate(answer)) |
| 317 | + timed_out_http = Net::HTTP.new("github.fake", 443) |
| 318 | + replacement_http = Net::HTTP.new("github.fake", 443) |
| 319 | + |
| 320 | + expect(Net::HTTP).to receive(:new). |
| 321 | + with("github.fake", 443). |
| 322 | + twice. |
| 323 | + and_return(timed_out_http, replacement_http) |
| 324 | + expect(timed_out_http).to receive(:use_ssl=).with(true) |
| 325 | + expect(timed_out_http).to receive(:start) |
| 326 | + expect(timed_out_http).to receive(:request). |
| 327 | + and_raise(Net::ReadTimeout, "execution expired") |
| 328 | + expect(timed_out_http).to receive(:started?).and_return(true) |
| 329 | + expect(timed_out_http).to receive(:finish) |
| 330 | + expect(replacement_http).to receive(:use_ssl=).with(true) |
| 331 | + expect(replacement_http).to receive(:start) |
| 332 | + expect(replacement_http).to receive(:request).and_return(response) |
| 333 | + expect_any_instance_of(Object).to receive(:sleep).with(1).once |
| 334 | + expect(logger).to receive(:error). |
| 335 | + with("Caught Net::ReadTimeout POSTing to https://github.fake/api/v3/graphql: Net::ReadTimeout with \"execution expired\"") |
| 336 | + expect(logger).to receive(:warn). |
| 337 | + with("GraphQL failed on try 1 of 3. Will retry.") |
| 338 | + |
| 339 | + expect(subject.send(:graphql_http_post, query)).to eq(code: 200, data: answer) |
| 340 | + end |
| 341 | + |
315 | 342 | it "returns immediately for success" do |
316 | 343 | expect(subject).to receive(:graphql_http_post_real).with(query).and_return(code: 200, data: answer) |
317 | 344 | expect_any_instance_of(Object).not_to receive(:sleep) |
|
361 | 388 | end |
362 | 389 |
|
363 | 390 | describe "#graphql_http_post_real" do |
| 391 | + it "reuses a started HTTP connection across requests" do |
| 392 | + answer = { "foo" => "bar" } |
| 393 | + response = instance_double(Net::HTTPOK, code: "200", body: JSON.generate(answer)) |
| 394 | + http = Net::HTTP.new("github.fake", 443) |
| 395 | + |
| 396 | + expect(Net::HTTP).to receive(:new).with("github.fake", 443).once.and_return(http) |
| 397 | + expect(http).to receive(:use_ssl=).with(true).once |
| 398 | + expect(http).to receive(:start).once |
| 399 | + expect(http).to receive(:request).twice.and_return(response) |
| 400 | + |
| 401 | + 2.times do |
| 402 | + expect(subject.send(:graphql_http_post_real, "nonsense")).to eq(code: 200, data: answer) |
| 403 | + end |
| 404 | + end |
| 405 | + |
| 406 | + it "discards a failed connection so the next request reconnects" do |
| 407 | + answer = { "foo" => "bar" } |
| 408 | + response = instance_double(Net::HTTPOK, code: "200", body: JSON.generate(answer)) |
| 409 | + failed_http = Net::HTTP.new("github.fake", 443) |
| 410 | + replacement_http = Net::HTTP.new("github.fake", 443) |
| 411 | + |
| 412 | + expect(Net::HTTP).to receive(:new).with("github.fake", 443).twice.and_return(failed_http, replacement_http) |
| 413 | + expect(failed_http).to receive(:use_ssl=).with(true) |
| 414 | + expect(failed_http).to receive(:start) |
| 415 | + expect(failed_http).to receive(:request).and_raise(EOFError, "closed connection") |
| 416 | + expect(failed_http).to receive(:started?).and_return(true) |
| 417 | + expect(failed_http).to receive(:finish) |
| 418 | + expect(replacement_http).to receive(:use_ssl=).with(true) |
| 419 | + expect(replacement_http).to receive(:start) |
| 420 | + expect(replacement_http).to receive(:request).and_return(response) |
| 421 | + expect(logger).to receive(:error). |
| 422 | + with("Caught EOFError POSTing to https://github.fake/api/v3/graphql: closed connection") |
| 423 | + |
| 424 | + expect(subject.send(:graphql_http_post_real, "nonsense")).to eq(code: 500, data: nil) |
| 425 | + expect(subject.send(:graphql_http_post_real, "nonsense")).to eq(code: 200, data: answer) |
| 426 | + end |
| 427 | + |
| 428 | + it "logs a warning if a failed connection cannot be closed" do |
| 429 | + http = instance_double(Net::HTTP, started?: true) |
| 430 | + subject.instance_variable_set(:@graphql_http, http) |
| 431 | + |
| 432 | + expect(http).to receive(:finish).and_raise(IOError, "already closed") |
| 433 | + expect(logger).to receive(:warn).with("Failed to close GraphQL connection: already closed") |
| 434 | + expect(subject.send(:reset_graphql_http)).to be_nil |
| 435 | + end |
| 436 | + |
364 | 437 | it "returns code=200 and parsed JSON for a successful response" do |
365 | 438 | answer = { "foo" => ["bar", "baz" => "fizz"] } |
366 | 439 | stub_request(:post, "https://github.fake/api/v3/graphql").to_return(status: 200, body: JSON.generate(answer)) |
|
0 commit comments