neverbounce-api 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +8 -0
  3. data/.gitignore +21 -0
  4. data/.rspec +4 -0
  5. data/.rubocop.yml +1161 -0
  6. data/.travis.yml +27 -0
  7. data/.yardopts +2 -0
  8. data/Gemfile +17 -0
  9. data/Gemfile.lock +48 -0
  10. data/LICENSE +9 -0
  11. data/README.md +235 -0
  12. data/lib/never_bounce/api/client.rb +259 -0
  13. data/lib/never_bounce/api/error.rb +21 -0
  14. data/lib/never_bounce/api/feature/basic_initialize.rb +20 -0
  15. data/lib/never_bounce/api/feature/eigencache.rb +48 -0
  16. data/lib/never_bounce/api/feature/igetset.rb +41 -0
  17. data/lib/never_bounce/api/feature/oattrs.rb +100 -0
  18. data/lib/never_bounce/api/feature/require_attr.rb +27 -0
  19. data/lib/never_bounce/api/request/account_info.rb +29 -0
  20. data/lib/never_bounce/api/request/base.rb +102 -0
  21. data/lib/never_bounce/api/request/jobs_create.rb +90 -0
  22. data/lib/never_bounce/api/request/jobs_delete.rb +34 -0
  23. data/lib/never_bounce/api/request/jobs_download.rb +34 -0
  24. data/lib/never_bounce/api/request/jobs_parse.rb +55 -0
  25. data/lib/never_bounce/api/request/jobs_results.rb +53 -0
  26. data/lib/never_bounce/api/request/jobs_search.rb +57 -0
  27. data/lib/never_bounce/api/request/jobs_start.rb +47 -0
  28. data/lib/never_bounce/api/request/jobs_status.rb +37 -0
  29. data/lib/never_bounce/api/request/single_check.rb +67 -0
  30. data/lib/never_bounce/api/response/account_info.rb +39 -0
  31. data/lib/never_bounce/api/response/account_info/job_counts.rb +26 -0
  32. data/lib/never_bounce/api/response/address_info.rb +43 -0
  33. data/lib/never_bounce/api/response/base.rb +15 -0
  34. data/lib/never_bounce/api/response/container.rb +126 -0
  35. data/lib/never_bounce/api/response/credits_info/base.rb +30 -0
  36. data/lib/never_bounce/api/response/credits_info/monthly.rb +16 -0
  37. data/lib/never_bounce/api/response/credits_info/paid.rb +20 -0
  38. data/lib/never_bounce/api/response/error_message.rb +17 -0
  39. data/lib/never_bounce/api/response/feature/job_status_fields.rb +68 -0
  40. data/lib/never_bounce/api/response/feature/job_status_fields/total.rb +46 -0
  41. data/lib/never_bounce/api/response/jobs_create.rb +10 -0
  42. data/lib/never_bounce/api/response/jobs_delete.rb +8 -0
  43. data/lib/never_bounce/api/response/jobs_download.rb +19 -0
  44. data/lib/never_bounce/api/response/jobs_parse.rb +10 -0
  45. data/lib/never_bounce/api/response/jobs_results.rb +34 -0
  46. data/lib/never_bounce/api/response/jobs_results/item.rb +65 -0
  47. data/lib/never_bounce/api/response/jobs_results/query.rb +20 -0
  48. data/lib/never_bounce/api/response/jobs_search.rb +33 -0
  49. data/lib/never_bounce/api/response/jobs_search/query.rb +16 -0
  50. data/lib/never_bounce/api/response/jobs_search/result.rb +13 -0
  51. data/lib/never_bounce/api/response/jobs_start.rb +10 -0
  52. data/lib/never_bounce/api/response/jobs_status.rb +11 -0
  53. data/lib/never_bounce/api/response/message.rb +32 -0
  54. data/lib/never_bounce/api/response/single_check.rb +68 -0
  55. data/lib/never_bounce/api/response/status_message.rb +17 -0
  56. data/lib/never_bounce/api/response/success_message.rb +12 -0
  57. data/lib/never_bounce/api/session.rb +141 -0
  58. data/lib/never_bounce/api/version.rb +4 -0
  59. data/lib/neverbounce-api.rb +4 -0
  60. data/lib/neverbounce.rb +3 -0
  61. data/neverbounce-api.gemspec +20 -0
  62. data/spec/lib/never_bounce/api/client_spec.rb +199 -0
  63. data/spec/lib/never_bounce/api/feature/basic_initialize_spec.rb +25 -0
  64. data/spec/lib/never_bounce/api/feature/eigencache_spec.rb +28 -0
  65. data/spec/lib/never_bounce/api/feature/igetset_spec.rb +45 -0
  66. data/spec/lib/never_bounce/api/feature/oattrs_spec.rb +72 -0
  67. data/spec/lib/never_bounce/api/feature/require_attr_spec.rb +25 -0
  68. data/spec/lib/never_bounce/api/request/account_info_spec.rb +29 -0
  69. data/spec/lib/never_bounce/api/request/base_spec.rb +6 -0
  70. data/spec/lib/never_bounce/api/request/jobs_create_spec.rb +89 -0
  71. data/spec/lib/never_bounce/api/request/jobs_delete_spec.rb +31 -0
  72. data/spec/lib/never_bounce/api/request/jobs_download_spec.rb +31 -0
  73. data/spec/lib/never_bounce/api/request/jobs_parse_spec.rb +44 -0
  74. data/spec/lib/never_bounce/api/request/jobs_results_spec.rb +42 -0
  75. data/spec/lib/never_bounce/api/request/jobs_search_spec.rb +40 -0
  76. data/spec/lib/never_bounce/api/request/jobs_start_spec.rb +44 -0
  77. data/spec/lib/never_bounce/api/request/jobs_status_spec.rb +31 -0
  78. data/spec/lib/never_bounce/api/request/single_check_spec.rb +44 -0
  79. data/spec/lib/never_bounce/api/response/account_info/job_counts_spec.rb +7 -0
  80. data/spec/lib/never_bounce/api/response/account_info_spec.rb +9 -0
  81. data/spec/lib/never_bounce/api/response/address_info_spec.rb +7 -0
  82. data/spec/lib/never_bounce/api/response/base_spec.rb +6 -0
  83. data/spec/lib/never_bounce/api/response/container_spec.rb +142 -0
  84. data/spec/lib/never_bounce/api/response/credits_info/base_spec.rb +31 -0
  85. data/spec/lib/never_bounce/api/response/credits_info/monthly_spec.rb +11 -0
  86. data/spec/lib/never_bounce/api/response/credits_info/paid_spec.rb +11 -0
  87. data/spec/lib/never_bounce/api/response/feature/job_status_fields/total_spec.rb +7 -0
  88. data/spec/lib/never_bounce/api/response/feature/job_status_fields_spec.rb +42 -0
  89. data/spec/lib/never_bounce/api/response/jobs_create_spec.rb +7 -0
  90. data/spec/lib/never_bounce/api/response/jobs_delete_spec.rb +7 -0
  91. data/spec/lib/never_bounce/api/response/jobs_download_spec.rb +7 -0
  92. data/spec/lib/never_bounce/api/response/jobs_parse_spec.rb +7 -0
  93. data/spec/lib/never_bounce/api/response/jobs_results/jobs_results_item_spec.rb +7 -0
  94. data/spec/lib/never_bounce/api/response/jobs_results/jobs_results_query_spec.rb +7 -0
  95. data/spec/lib/never_bounce/api/response/jobs_results_spec.rb +7 -0
  96. data/spec/lib/never_bounce/api/response/jobs_search/jobs_search_query_spec.rb +7 -0
  97. data/spec/lib/never_bounce/api/response/jobs_search/jobs_search_result_spec.rb +7 -0
  98. data/spec/lib/never_bounce/api/response/jobs_search_spec.rb +7 -0
  99. data/spec/lib/never_bounce/api/response/jobs_start_spec.rb +7 -0
  100. data/spec/lib/never_bounce/api/response/jobs_status_spec.rb +7 -0
  101. data/spec/lib/never_bounce/api/response/single_check_spec.rb +7 -0
  102. data/spec/lib/never_bounce/api/response/spec_helper.rb +8 -0
  103. data/spec/lib/never_bounce/api/session_spec.rb +140 -0
  104. data/spec/lib/never_bounce/api_spec.rb +8 -0
  105. data/spec/spec_helper.rb +49 -0
  106. data/spec/spec_support/include_dir_context.rb +22 -0
  107. data/spec/spec_support/simplecov.rb +18 -0
  108. metadata +210 -0
@@ -0,0 +1,142 @@
1
+
2
+ module NeverBounce; module API; module Response
3
+ describe Container do
4
+ include_dir_context __dir__
5
+
6
+ it_behaves_like "instantiatable"
7
+ it_behaves_like "properly inherited"
8
+
9
+ describe "#body_hash" do
10
+ it "requires `raw`" do
11
+ expect { newo.body_hash }.to raise_error(AttributeError, "Attribute must be set: raw")
12
+ end
13
+
14
+ it "generally works" do
15
+ r = newo(raw: '{"a":10}')
16
+ expect(r.body_hash).to eq({"a" => 10})
17
+ end
18
+ end
19
+
20
+ describe ".oattr" do
21
+ # NOTE: Tests for this section are a few "matrix" scenarios which reasonably cover possible combinations.
22
+ context "case 1" do
23
+ let(:klass) do
24
+ Class.new(described_class) do
25
+ oattr :a, :scalar
26
+ oattr :b, :scalar, type: :integer
27
+ oattr :c, :scalar, type: :integer, allow_nil: true
28
+ oattr :d, :writer
29
+ oattr :e, :custom
30
+ oattr :f, :scalar, type: :float
31
+ oattr :g, :scalar, type: :float, allow_nil: true
32
+
33
+ def d
34
+ @d ||= body_hash.fetch("d")
35
+ end
36
+
37
+ def e
38
+ @e ||= body_hash.fetch("e")
39
+ end
40
+
41
+ def e=(v)
42
+ @e = v
43
+ end
44
+ end
45
+ end
46
+
47
+ it "generally works" do
48
+ expect(klass.oattrs).to eq [:a, :b, :c, :d, :e, :f, :g]
49
+
50
+ r = klass.new(body_hash: {})
51
+ expect { r.a }.to raise_error KeyError
52
+ expect { r.d }.to raise_error KeyError
53
+ expect { r.e }.to raise_error KeyError
54
+ expect { r.f }.to raise_error KeyError
55
+
56
+ # Numeric conversion, valid values.
57
+ r = klass.new(body_hash: {
58
+ "b" => "12",
59
+ "c" => "34",
60
+ "f" => "1.2",
61
+ "g" => "3.4",
62
+ })
63
+ expect(r.b).to eq 12
64
+ expect(r.c).to eq 34
65
+ expect(r.f).to eq 1.2
66
+ expect(r.g).to eq 3.4
67
+
68
+ # Numeric conversion, empty strings.
69
+ r = klass.new(body_hash: {
70
+ "b" => "",
71
+ "c" => "",
72
+ "f" => "",
73
+ "g" => "",
74
+ })
75
+ expect { r.b }.to raise_error(ArgumentError, /^invalid value for Integer/)
76
+ expect { r.c }.to raise_error(ArgumentError, /^invalid value for Integer/)
77
+ expect { r.f }.to raise_error(ArgumentError, /^invalid value for Float/)
78
+ expect { r.g }.to raise_error(ArgumentError, /^invalid value for Float/)
79
+
80
+ # Numeric conversion, invalid strings.
81
+ r = klass.new(body_hash: {
82
+ "b" => "xyz",
83
+ "c" => "xyz",
84
+ "f" => "xyz",
85
+ "g" => "xyz",
86
+ })
87
+ expect { r.b }.to raise_error(ArgumentError, /^invalid value for Integer/)
88
+ expect { r.c }.to raise_error(ArgumentError, /^invalid value for Integer/)
89
+ expect { r.f }.to raise_error(ArgumentError, /^invalid value for Float/)
90
+ expect { r.g }.to raise_error(ArgumentError, /^invalid value for Float/)
91
+
92
+ # Nill allowance.
93
+ r = klass.new(body_hash: {
94
+ "b" => nil,
95
+ "c" => nil,
96
+ "f" => nil,
97
+ "g" => nil,
98
+ })
99
+ expect { r.b }.to raise_error(TypeError, /nil into Integer/)
100
+ expect(r.c).to be nil
101
+ expect { r.f }.to raise_error(TypeError, /nil into Float/)
102
+ expect(r.g).to be nil
103
+
104
+ # Complete set.
105
+ r = klass.new(body_hash: {
106
+ "a" => "hello",
107
+ "b" => 10,
108
+ "c" => 20,
109
+ "d" => [1, 2, 3],
110
+ "e" => {kk: 1},
111
+ "f" => 1.5,
112
+ "g" => 2.6,
113
+ })
114
+
115
+ expect { r.touch }.not_to raise_error # An important one.
116
+
117
+ expect(r.a).to eq "hello"
118
+ expect(r.b).to eq 10
119
+ expect(r.c).to eq 20
120
+ expect(r.d).to eq([1, 2, 3])
121
+ expect(r.e).to eq({kk: 1})
122
+ expect(r.f).to eq 1.5
123
+ expect(r.g).to eq 2.6
124
+
125
+ # Accessors.
126
+ r.a += "!"
127
+ expect(r.a).to eq "hello!"
128
+ r.b += 1
129
+ expect(r.b).to eq 11
130
+ r.c += 1
131
+ expect(r.c).to eq 21
132
+ r.d += [4]
133
+ expect(r.d).to eq [1, 2, 3, 4]
134
+ r.f += 0.1
135
+ expect(r.f).to eq 1.6
136
+ r.g += 0.1
137
+ expect(r.g).to eq 2.7
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end; end; end
@@ -0,0 +1,31 @@
1
+
2
+ describe NeverBounce::API::Response::CreditsInfo::Base do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+
8
+ let(:klass) do
9
+ Class.new(described_class) do
10
+ attr_accessor :subscription_type
11
+ end
12
+ end
13
+
14
+ describe "#monthly?" do
15
+ it "generally works" do
16
+ r = klass.new
17
+ r.subscription_type = :monthly
18
+ expect(r.monthly?).to be true
19
+ expect(r.paid?).to be false
20
+ end
21
+ end
22
+
23
+ describe "#paid?" do
24
+ it "generally works" do
25
+ r = klass.new
26
+ r.subscription_type = :paid
27
+ expect(r.monthly?).to be false
28
+ expect(r.paid?).to be true
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,11 @@
1
+
2
+ describe NeverBounce::API::Response::CreditsInfo::Monthly do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+
8
+ describe "#subscription_type" do
9
+ it { expect(newo.subscription_type).to eq :monthly }
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+
2
+ describe NeverBounce::API::Response::CreditsInfo::Paid do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+
8
+ describe "#subscription_type" do
9
+ it { expect(newo.subscription_type).to eq :paid }
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+
2
+ describe NeverBounce::API::Response::Feature::JobStatusFields::Total do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+ end
@@ -0,0 +1,42 @@
1
+
2
+ module NeverBounce; module API; module Response; module Feature
3
+ describe JobStatusFields do
4
+ context "when invalid class" do
5
+ it "generally works" do
6
+ feature = described_class
7
+ expect { Class.new { feature.load(self) } }.to raise_error(ArgumentError, /is not an ancestor of Response::Container$/)
8
+ end
9
+ end
10
+
11
+ context "when valid class" do
12
+ it "generally works" do
13
+ feature = described_class
14
+
15
+ klass = Class.new(Response::StatusMessage) { feature.load(self) }
16
+
17
+ # Use a complete real-world example.
18
+ r = klass.new(body_hash: {"status"=>"success", "id"=>302499, "job_status"=>"complete", "filename"=>"20170815-103321.csv", "created_at"=>"2017-08-15 17:19:41", "started_at"=>"2017-08-15 17:19:42", "finished_at"=>"2017-08-15 17:19:45", "total"=>{"records"=>11, "billable"=>12, "processed"=>13, "valid"=>14, "invalid"=>15, "catchall"=>16, "disposable"=>17, "unknown"=>18, "duplicates"=>19, "bad_syntax"=>20}, "bounce_estimate"=>34.56, "percent_complete"=>100, "execution_time"=>99})
19
+ expect(r.id).to eq 302499
20
+ expect(r.job_status).to eq "complete"
21
+ expect(r.filename).to eq "20170815-103321.csv"
22
+ expect(r.created_at).to eq "2017-08-15 17:19:41"
23
+ expect(r.started_at).to eq "2017-08-15 17:19:42"
24
+ expect(r.finished_at).to eq "2017-08-15 17:19:45"
25
+ expect(r.bounce_estimate).to eq 34.56
26
+ expect(r.percent_complete).to eq 100
27
+ expect(r.execution_time).to eq 99
28
+ expect(total = r.total).to be_a described_class::Total
29
+ expect(total.records).to eq 11
30
+ expect(total.billable).to eq 12
31
+ expect(total.processed).to eq 13
32
+ expect(total.valid).to eq 14
33
+ expect(total.invalid).to eq 15
34
+ expect(total.catchall).to eq 16
35
+ expect(total.disposable).to eq 17
36
+ expect(total.unknown).to eq 18
37
+ expect(total.duplicates).to eq 19
38
+ expect(total.bad_syntax).to eq 20
39
+ end
40
+ end
41
+ end
42
+ end; end; end; end
@@ -0,0 +1,7 @@
1
+
2
+ describe NeverBounce::API::Response::JobsCreate do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+ end
@@ -0,0 +1,7 @@
1
+
2
+ describe NeverBounce::API::Response::JobsDelete do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+ end
@@ -0,0 +1,7 @@
1
+
2
+ describe NeverBounce::API::Response::JobsDownload do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+ end
@@ -0,0 +1,7 @@
1
+
2
+ describe NeverBounce::API::Response::JobsParse do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+ end
@@ -0,0 +1,7 @@
1
+
2
+ describe NeverBounce::API::Response::JobsResults::Item do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+ end
@@ -0,0 +1,7 @@
1
+
2
+ describe NeverBounce::API::Response::JobsResults::Query do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+ end
@@ -0,0 +1,7 @@
1
+
2
+ describe NeverBounce::API::Response::JobsResults do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+ end
@@ -0,0 +1,7 @@
1
+
2
+ describe NeverBounce::API::Response::JobsSearch::Query do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+ end
@@ -0,0 +1,7 @@
1
+
2
+ describe NeverBounce::API::Response::JobsSearch::Result do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+ end
@@ -0,0 +1,7 @@
1
+
2
+ describe NeverBounce::API::Response::JobsSearch do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+ end
@@ -0,0 +1,7 @@
1
+
2
+ describe NeverBounce::API::Response::JobsStart do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+ end
@@ -0,0 +1,7 @@
1
+
2
+ describe NeverBounce::API::Response::JobsStatus do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+ end
@@ -0,0 +1,7 @@
1
+
2
+ describe NeverBounce::API::Response::SingleCheck do
3
+ include_dir_context __dir__
4
+
5
+ it_behaves_like "instantiatable"
6
+ it_behaves_like "properly inherited"
7
+ end
@@ -0,0 +1,8 @@
1
+
2
+ shared_context __dir__ do
3
+ shared_examples "properly inherited" do
4
+ it "doesn't have duplicate oattrs" do
5
+ expect((oattrs = described_class.oattrs).uniq).to eq oattrs
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,140 @@
1
+
2
+ module NeverBounce; module API
3
+ describe Session do
4
+ include_dir_context __dir__
5
+
6
+ it_behaves_like "instantiatable"
7
+
8
+ describe "#server_raw" do
9
+ let(:m) { :server_raw }
10
+ context "when NOK" do
11
+ it "generally works" do
12
+ r = newo(
13
+ server_obj: Class.new do
14
+ def code; 123; end
15
+ def ok?; false; end
16
+ end.new
17
+ )
18
+ expect { r.send(m) }.to raise_error(RequestError, "Code not OK: 123")
19
+ end
20
+ end
21
+
22
+ context "when OK" do
23
+ it "generally works" do
24
+ r = newo(
25
+ server_obj: Class.new do
26
+ def body; "abc"; end
27
+ def ok?; true; end
28
+ end.new,
29
+ )
30
+ expect(r.send(m)).to eq "abc"
31
+ end
32
+ end
33
+ end # describe "#server_raw"
34
+
35
+ describe "#robj_hash_preview" do
36
+ let(:m) { :robj_hash_preview }
37
+
38
+ context "when unknown content type" do
39
+ it "generally works" do
40
+ r = newo(server_content_type: "type/a")
41
+ expect(r.send(m)).to be false
42
+ end
43
+ end
44
+
45
+ context "when 'application/json'" do
46
+ it "generally works" do
47
+ r = newo(
48
+ server_content_type: "application/json",
49
+ server_raw: "xyz",
50
+ )
51
+ expect { r.send(m) }.to raise_error(FormatError, /^JSON::ParserError: /)
52
+
53
+ r = newo(
54
+ server_content_type: "application/json",
55
+ server_raw: '{"a":12,"b":"abc"}',
56
+ )
57
+ expect(r.send(m)).to eq({"a" => 12, "b" => "abc"})
58
+ end
59
+ end
60
+ end
61
+
62
+ describe "#robj_klass_and_attrs" do
63
+ let(:m) { :robj_klass_and_attrs }
64
+
65
+ # A minimum viable instance for tests below.
66
+ def newo(attrs = {})
67
+ request_klass = Class.new do
68
+ def self.response_klass; Response::JobsParse; end
69
+ end
70
+
71
+ Class.new(described_class) do
72
+ def server_obj; raise "This must never be called"; end
73
+ end.new({
74
+ request: request_klass.new,
75
+ server_raw: "server_raw",
76
+ }.merge(attrs))
77
+ end
78
+
79
+ it "validates `robj_hash_preview`" do
80
+ r = newo(robj_hash_preview: "abc")
81
+ expect { r.send(m) }.to raise_error(AttributeError, "Unknown `robj_hash_preview`: \"abc\"")
82
+ end
83
+
84
+ it "works for raw data" do
85
+ r = newo(
86
+ robj_hash_preview: false,
87
+ )
88
+ #expect { r.send(m) }.to raise_error(FormatError, /^Key 'status' not found: /)
89
+ expect(r.send(m)).to eq [Response::JobsParse, {:raw => "server_raw"}]
90
+ end
91
+
92
+ it "works for invalid data" do
93
+ # Valid JSON.
94
+ r = newo(
95
+ robj_hash_preview: {"a" => 12},
96
+ )
97
+ expect { r.send(m) }.to raise_error(FormatError, /^Key 'status' not found: /)
98
+ end
99
+
100
+ it "works for valid data" do
101
+ r = newo(
102
+ robj_hash_preview: {"status" => "success"},
103
+ )
104
+ expect(r.send(m)).to eq [Response::JobsParse, {:body_hash=>{"status"=>"success"}}]
105
+
106
+ r = newo(
107
+ robj_hash_preview: {"status" => "not-a-success"}, # Not necessarily "error".
108
+ )
109
+ expect(r.send(m)).to eq [Response::ErrorMessage, {:body_hash=>{"status"=>"not-a-success"}}]
110
+ end
111
+ end
112
+
113
+ describe "#server_obj" do
114
+ it "requires `request`" do
115
+ r = newo
116
+ expect { r.server_obj }.to raise_error "Attribute must be set: request"
117
+ end
118
+
119
+ it "generally works" do
120
+ # See implementation notes below.
121
+ r = newo(
122
+ httparty: (httparty = cllggr.new),
123
+ request: Request::JobsParse.new(api_key: "api_key", job_id: "123"),
124
+ )
125
+ expect(r.server_obj).to eq 1 # Result of `cllggr`.
126
+ args = httparty._calls[0]
127
+ expect(args).to be_a Array
128
+ expect(args).not_to be_empty
129
+ end
130
+ end
131
+ end
132
+ end; end
133
+
134
+ # Implementation notes:
135
+ #
136
+ # * We test class's general abilities on a real request class.
137
+ # * Consider this great hack to debug tests easier:
138
+ # Class.new(described_class) do
139
+ # def server_obj; raise "This must never be called"; end
140
+ # end.new(...)