fake_fb_marketing_api 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fake_fb_marketing_api/version'
4
+
5
+ module FakeFbMarketingApi
6
+ class Error < StandardError; end
7
+ # Your code goes here...
8
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack/builder'
4
+
5
+ require_relative './versions/v3_2'
6
+ require_relative './versions/v3_0'
7
+
8
+ module FakeFbMarketingApi
9
+ class Application
10
+ VERSIONS = [
11
+ FakeFbMarketingApi::Versions::V32,
12
+ FakeFbMarketingApi::Versions::V30
13
+ ].freeze
14
+
15
+ attr_reader :app
16
+
17
+ def initialize(_params = {})
18
+ @app = begin
19
+ Rack::Builder.new do
20
+ VERSIONS.each do |e|
21
+ map "/#{e.namespace}" do
22
+ run e
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ def call(env)
30
+ @app.call(env)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sinatra'
4
+ require 'faraday'
5
+ require 'we-call'
6
+
7
+ require_relative './fake_facebook'
8
+
9
+ module FakeFbMarketingApi
10
+ class Base < Sinatra::Base
11
+ class << self
12
+ attr_reader :namespace
13
+
14
+ def set_namespace(name)
15
+ @namespace = name
16
+ end
17
+ end
18
+
19
+ configure do
20
+ FakeFacebook.setup
21
+
22
+ # setup WeCall
23
+ We::Call.configure do |config|
24
+ config.app_name = 'fb-graph-proxy'
25
+ config.app_env = 'staging'
26
+ config.detect_deprecations = false
27
+ end
28
+ end
29
+
30
+ before do
31
+ Faraday::Response::Logger::DEFAULT_OPTIONS[:headers] = false
32
+ Faraday::Response::Logger::DEFAULT_OPTIONS[:bodies] = true
33
+ @conn = We::Call::Connection.new(host: 'https://graph.facebook.com', timeout: 2) do |faraday|
34
+ faraday.adapter :typhoeus
35
+ faraday.response :logger do |logger|
36
+ logger.filter(/(access_token=)(\w+)/, '\1[FILTERED]')
37
+ logger.filter(/("access_token":)(.[^"]+)/, '\1[FILTERED]')
38
+ logger.filter(/("token":)(.[^"]+)/, '\1[FILTERED]')
39
+ end
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def proxy_get_to_fb(request, _response)
46
+ resp = @conn.get("#{request.path}?#{request.query_string}") do |req|
47
+ request.params.each do |key, value|
48
+ req.params[key] = value
49
+ end
50
+ end
51
+ headers = resp.headers.select { |_header, value| value != 'keep-alive' && value != 'chunked' }
52
+ [resp.status, headers, resp.body]
53
+ end
54
+
55
+ def proxy_post_to_fb(request, _response)
56
+ resp = @conn.post("#{request.path}?#{request.query_string}") do |req|
57
+ request.params.each do |key, value|
58
+ req.params[key] = value
59
+ end
60
+ end
61
+ headers = resp.headers.select { |_header, value| value != 'keep-alive' && value != 'chunked' }
62
+ [resp.status, headers, resp.body]
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,23 @@
1
+ module FakeFbMarketingApi
2
+ class FakeFacebook
3
+
4
+ attr_accessor :instance
5
+ attr_accessor :owned_ad_accounts
6
+
7
+ def self.setup
8
+ @instance = FakeFbMarketingApi::FakeFacebook.new
9
+ end
10
+
11
+ def initialize
12
+ @owned_ad_accounts = []
13
+ end
14
+
15
+ def self.owned_ad_accounts
16
+ @instance.owned_ad_accounts
17
+ end
18
+
19
+ def self.add_owned_ad_account(ad_account_hash)
20
+ @instance.owned_ad_accounts << ad_account_hash
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FakeFbMarketingApi
4
+ VERSION = '0.0.1'
5
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../base'
4
+
5
+ module FakeFbMarketingApi
6
+ module Versions
7
+ class V30 < Base
8
+ set_namespace 'v3.0'
9
+
10
+ get '/:graph_id/*' do
11
+ content_type :json
12
+ proxy_get_to_fb(request, response)
13
+ end
14
+
15
+ post '/*' do
16
+ content_type :json
17
+ proxy_post_to_fb(request, response)
18
+ end
19
+
20
+ get '/*' do
21
+ proxy_get_to_fb(request, response)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../base'
4
+ require_relative '../fake_facebook'
5
+
6
+ module FakeFbMarketingApi
7
+ module Versions
8
+ class V32 < Base
9
+ set_namespace 'v3.2'
10
+
11
+ get '/:business_id/owned_ad_accounts' do
12
+ content_type :json
13
+ FakeFacebook.owned_ad_accounts.to_json
14
+ end
15
+
16
+ post '/:business_id/adaccounts' do
17
+ content_type :json
18
+ FakeFacebook.add_owned_ad_account(
19
+ {
20
+ 'name' => params[:name],
21
+ 'id' => ENV.fetch('FACEBOOK_AD_ACCOUNT_ID')
22
+ }
23
+ )
24
+ if params.key?('adaccount_id')
25
+ proxy_post_to_fb(request, response)
26
+ else
27
+ {
28
+ end_advertiser_id: params[:end_advertiser_id],
29
+ media_agency_id: params[:media_agency_id],
30
+ business_id: params[:business_id],
31
+ account_id: ENV['FACEBOOK_AD_ACCOUNT_ID'],
32
+ id: "act_%{ENV['FACEBOOK_AD_ACCOUNT_ID']}",
33
+ partner_id: 'NONE'
34
+ }.to_json
35
+ end
36
+ end
37
+
38
+ post '/:ad_account_id/assigned_users' do
39
+ proxy_post_to_fb(request, response)
40
+ end
41
+
42
+ post '/:business_id/businessprojects' do
43
+ proxy_post_to_fb(request, response)
44
+ end
45
+
46
+ post '/:ad_account_id/campaigns' do
47
+ content_type :json
48
+ case params[:objective]
49
+ when 'BRAND_AWARENESS'
50
+ {
51
+ id: ENV['BRAND_AWARENESS_CAMPAIGN_ID']
52
+ }.to_json
53
+ when 'LINK_CLICKS'
54
+ {
55
+ id: ENV['LINK_CLICKS_CAMPAIGN_ID']
56
+ }.to_json
57
+ when 'VIDEO_VIEWS'
58
+ {
59
+ id: ENV['VIDEO_VIEWS_CAMPAIGN_ID']
60
+ }.to_json
61
+ when 'REACH'
62
+ {
63
+ id: ENV['REACH_CAMPAIGN_ID']
64
+ }.to_json
65
+ when 'POST_ENGAGEMENT'
66
+ {
67
+ id: ENV['POST_ENGAGEMENT_CAMPAIGN_ID']
68
+ }.to_json
69
+ when 'PAGE_LIKES'
70
+ {
71
+ id: ENV['PAGE_LIKES_CAMPAIGN_ID']
72
+ }.to_json
73
+ when 'CONVERSIONS_COUNT'
74
+ {
75
+ id: ENV['CONVERSIONS_COUNT_CAMPAIGN_ID']
76
+ }.to_json
77
+ when 'CONVERSIONS_FUNDRAISE'
78
+ {
79
+ id: ENV['CONVERSIONS_FUNDRAISE_CAMPAIGN_ID']
80
+ }.to_json
81
+ end
82
+ end
83
+
84
+ get '/:graph_id/*' do
85
+ content_type :json
86
+ proxy_get_to_fb(request, response)
87
+ end
88
+
89
+ post '/*' do
90
+ content_type :json
91
+ return proxy_post_to_fb(request, response)
92
+ end
93
+
94
+ get '/*' do
95
+ proxy_get_to_fb(request, response)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,339 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'koala'
4
+ require 'capybara_discoball'
5
+
6
+ RSpec.describe FakeFbMarketingApi::Application do
7
+ before { ENV['PROJECT_ID'] = Faker::Number.number(10) }
8
+ before { ENV['BUSINESS_ID'] = Faker::Number.number(10) }
9
+ before { ENV['FACEBOOK_AD_ACCOUNT_ID'] = Faker::Number.number(10) }
10
+ before { ENV['FACEBOOK_AD_ACCOUNT_NAME'] = Faker::Seinfeld.character }
11
+ before { ENV['BRAND_AWARENESS_CAMPAIGN_ID'] = Faker::Number.number(10) }
12
+ before { ENV['LINK_CLICKS_CAMPAIGN_ID'] = Faker::Number.number(10) }
13
+ before { ENV['VIDEO_VIEWS_CAMPAIGN_ID'] = Faker::Number.number(10) }
14
+ before { ENV['REACH_CAMPAIGN_ID'] = Faker::Number.number(10) }
15
+ before { ENV['POST_ENGAGEMENT_CAMPAIGN_ID'] = Faker::Number.number(10) }
16
+ before { ENV['PAGE_LIKES_CAMPAIGN_ID'] = Faker::Number.number(10) }
17
+ before do
18
+ WebMock.disable_net_connect!(allow: /127.0.0.1/)
19
+ Capybara::Discoball.spin(FakeFbMarketingApi::Application) do |server|
20
+ Koala.http_service.http_options = {
21
+ use_ssl: false
22
+ }
23
+ Koala.configure do |config|
24
+ config.api_version = 'v3.2'
25
+ config.graph_server = server.url.gsub 'http://', ''
26
+ end
27
+ end
28
+ end
29
+
30
+ let(:access_token) { Faker::Number.number(20) }
31
+ let(:graph) { Koala::Facebook::API.new(access_token) }
32
+
33
+ it 'has a version number' do
34
+ expect(FakeFbMarketingApi::VERSION).not_to be nil
35
+ end
36
+
37
+ describe 'GET /:business_id/owned_ad_acocunts' do
38
+ it 'does not show any ad accounts until one is created' do
39
+ response = graph.get_object("#{ENV['BUSINESS_ID']}/owned_ad_accounts", fields: 'id,name')
40
+
41
+ expect(response).to eq([])
42
+ end
43
+
44
+ it 'returns a single ad account after one has been created' do
45
+ end_advertiser_id = Faker::Number.number(10)
46
+ media_agency_id = Faker::Number.number(10)
47
+ name = Faker::Seinfeld.character
48
+ response = graph.put_connections "#{ENV['BUSINESS_ID']}", 'adaccounts',
49
+ name: name,
50
+ currency: 'USD', timezone_id: 6, end_advertiser_id: end_advertiser_id,
51
+ media_agency_id: media_agency_id, partner: 'NONE'
52
+ response = graph.get_object("#{ENV['BUSINESS_ID']}/owned_ad_accounts", fields: 'id,name')
53
+
54
+ expect(response).to eq(
55
+ [
56
+ {
57
+ 'id' => ENV.fetch('FACEBOOK_AD_ACCOUNT_ID'),
58
+ 'name' => name
59
+ }
60
+ ]
61
+ )
62
+ end
63
+ end
64
+
65
+ describe 'POST /:business_id/adaccounts' do
66
+ context 'when creating an ad account' do
67
+ it 'works' do
68
+ end_advertiser_id = Faker::Number.number(10)
69
+ media_agency_id = Faker::Number.number(10)
70
+ response = graph.put_connections (ENV['BUSINESS_ID']).to_s, 'adaccounts',
71
+ name: 'Test Ad Account',
72
+ currency: 'USD', timezone_id: 6, end_advertiser_id: end_advertiser_id,
73
+ media_agency_id: media_agency_id, partner: 'NONE'
74
+
75
+ expect(response).to include
76
+ {
77
+ 'business_id' => ENV['BUSINESS_ID'],
78
+ 'account_id' => ENV['FACEBOOK_AD_ACCOUNT_ID'],
79
+ 'id' => "act_%{ENV['FACEBOOK_AD_ACCOUNT_ID']}",
80
+ 'end_advertiser_id' => end_advertiser_id,
81
+ 'media_agency_id' => media_agency_id,
82
+ 'partner_id' => 'NONE'
83
+ }
84
+ end
85
+ end
86
+
87
+ context 'when adding a user to an ad account' do
88
+ it 'calls out to facebook' do
89
+ project_id = Faker::Number.number(15).to_s
90
+ fb_ad_account = ENV['FACEBOOK_AD_ACCOUNT_ID']
91
+ stub_request(:post, "https://graph.facebook.com/v3.2/#{project_id}/adaccounts?access_token=#{access_token}&adaccount_id=#{fb_ad_account}")
92
+ .with(
93
+ headers: {
94
+ 'Expect' => '',
95
+ 'User-Agent' => 'fb-graph-proxy',
96
+ 'X-App-Env' => 'staging',
97
+ 'X-App-Name' => 'fb-graph-proxy'
98
+ }
99
+ ).to_return(status: 200, body: { 'success' => true }.to_json, headers: {})
100
+
101
+ response = graph.put_connections(project_id, 'adaccounts', adaccount_id: fb_ad_account)
102
+
103
+ expect(response).to eq('success' => true)
104
+ end
105
+ end
106
+ end
107
+
108
+ describe 'POST /:ad_account_id/assigned_users' do
109
+ it 'passes makes a post to fb' do
110
+ user_id = Faker::Number.number(14).to_s
111
+ ad_account_id = Faker::Number.number(15).to_s
112
+ stub_request(:post, "https://graph.facebook.com/v3.2/#{ad_account_id}/assigned_users?access_token=#{access_token}&tasks=ANALYZE,MANAGE,ADVERTISE&user=#{user_id}")
113
+ .with(
114
+ headers: {
115
+ 'Content-Length' => '0',
116
+ 'Expect' => '',
117
+ 'User-Agent' => 'fb-graph-proxy',
118
+ 'X-App-Env' => 'staging',
119
+ 'X-App-Name' => 'fb-graph-proxy'
120
+ }
121
+ )
122
+ .to_return(status: 200, body: { 'success' => true }.to_json, headers: {})
123
+
124
+ response = graph.put_connections(ad_account_id, 'assigned_users', user: user_id, tasks: %w[ANALYZE MANAGE ADVERTISE])
125
+
126
+ expect(response).to eq 'success' => true
127
+ end
128
+ end
129
+
130
+ describe 'POST /:business_id/businessprojects' do
131
+ it 'passes a static project_id' do
132
+ stub_request(:post, "https://graph.facebook.com/v3.2/#{ENV['BUSINESS_ID']}/businessprojects?access_token=#{access_token}&name=test_project")
133
+ .to_return(status: 200, body: { 'id' => ENV['PROJECT_ID'] }.to_json, headers: {})
134
+
135
+ result = graph.put_connections(ENV['BUSINESS_ID'], 'businessprojects', name: 'test_project')
136
+
137
+ expect(result).to include 'id' => ENV['PROJECT_ID']
138
+ end
139
+ end
140
+
141
+ describe 'POST /:ad_account_id/campaigns' do
142
+ it 'works for brand awareness' do
143
+ objective = 'BRAND_AWARENESS'
144
+
145
+ campaign = graph.put_connections("act_#{ENV['FACEBOOK_AD_ACCOUNT_ID']}", 'campaigns', budget_rebalance_flag: false, name: "#{objective} Test Campaign", objective: objective, status: 'ACTIVE')
146
+
147
+ expect(campaign).to eq 'id' => ENV['BRAND_AWARENESS_CAMPAIGN_ID']
148
+ end
149
+
150
+ it 'works for link clicks' do
151
+ objective = 'LINK_CLICKS'
152
+
153
+ campaign = graph.put_connections("act_#{ENV['FACEBOOK_AD_ACCOUNT_ID']}", 'campaigns', budget_rebalance_flag: false, name: "#{objective} Test Campaign", objective: objective, status: 'ACTIVE')
154
+
155
+ expect(campaign).to eq 'id' => ENV['LINK_CLICKS_CAMPAIGN_ID']
156
+ end
157
+
158
+ it 'works for video views' do
159
+ objective = 'VIDEO_VIEWS'
160
+
161
+ campaign = graph.put_connections("act_#{ENV['FACEBOOK_AD_ACCOUNT_ID']}", 'campaigns', budget_rebalance_flag: false, name: "#{objective} Test Campaign", objective: objective, status: 'ACTIVE')
162
+
163
+ expect(campaign).to eq 'id' => ENV['VIDEO_VIEWS_CAMPAIGN_ID']
164
+ end
165
+
166
+ it 'works for reach' do
167
+ objective = 'REACH'
168
+
169
+ campaign = graph.put_connections("act_#{ENV['FACEBOOK_AD_ACCOUNT_ID']}", 'campaigns', budget_rebalance_flag: false, name: "#{objective} Test Campaign", objective: objective, status: 'ACTIVE')
170
+
171
+ expect(campaign).to eq 'id' => ENV['REACH_CAMPAIGN_ID']
172
+ end
173
+
174
+ it 'works for post engagement' do
175
+ objective = 'POST_ENGAGEMENT'
176
+
177
+ campaign = graph.put_connections("act_#{ENV['FACEBOOK_AD_ACCOUNT_ID']}", 'campaigns', budget_rebalance_flag: false, name: "#{objective} Test Campaign", objective: objective, status: 'ACTIVE')
178
+
179
+ expect(campaign).to eq 'id' => ENV['POST_ENGAGEMENT_CAMPAIGN_ID']
180
+ end
181
+
182
+ it 'works for page likes' do
183
+ objective = 'PAGE_LIKES'
184
+
185
+ campaign = graph.put_connections("act_#{ENV['FACEBOOK_AD_ACCOUNT_ID']}", 'campaigns', budget_rebalance_flag: false, name: "#{objective} Test Campaign", objective: objective, status: 'ACTIVE')
186
+
187
+ expect(campaign).to eq 'id' => ENV['PAGE_LIKES_CAMPAIGN_ID']
188
+ end
189
+ end
190
+
191
+ describe 'GET /:ad_account_id/campaigns' do
192
+ context 'when no campaigns have been created' do
193
+ it 'returns nothing' do
194
+ end
195
+ end
196
+ end
197
+
198
+ describe 'GET /:graph_id/insights' do
199
+ it 'passes through insight requests' do
200
+ graph_id = Faker::Number.number(10)
201
+ stub_request(:get, "https://graph.facebook.com/v3.2/#{graph_id}/insights?access_token=#{access_token}&date_preset=lifetime&fields=ad_id")
202
+ .to_return(status: 200, body: "{\"data\":[{\"ad_id\":\"#{graph_id}\",\"date_start\":\"2018-05-30\",\"date_stop\":\"2019-01-05\"}],\"paging\":{\"cursors\":{\"before\":\"MAZDZD\",\"after\":\"MAZDZD\"}}}", headers: {})
203
+
204
+ ad_insights = graph.get_object("#{graph_id}/insights", fields: 'ad_id', date_preset: 'lifetime')
205
+
206
+ expect(ad_insights.count).to eq 1
207
+ expect(ad_insights.first['ad_id']).to eq graph_id
208
+ end
209
+
210
+ it 'returns headers of the api call' do
211
+ graph_id = Faker::Number.number(10)
212
+ headers = { 'etag' => '423144fb7fd642308ea9666e20cceb65ee4f6650' }
213
+ stub_request(:get, "https://graph.facebook.com/v3.2/#{graph_id}/insights?access_token=#{access_token}&date_preset=lifetime&fields=ad_id")
214
+ .to_return(status: 200, body: "{\"data\":[{\"ad_id\":\"#{graph_id}\",\"date_start\":\"2018-05-30\",\"date_stop\":\"2019-01-05\"}],\"paging\":{\"cursors\":{\"before\":\"MAZDZD\",\"after\":\"MAZDZD\"}}}", headers: headers)
215
+
216
+ ad_insights = graph.get_object("#{graph_id}/insights", fields: 'ad_id', date_preset: 'lifetime')
217
+
218
+ expect(ad_insights.headers).to include 'etag'
219
+ end
220
+
221
+ it 'returns the status of the api call' do
222
+ graph_id = Faker::Number.number(10)
223
+ headers = { 'etag' => '423144fb7fd642308ea9666e20cceb65ee4f6650' }
224
+ stub_request(:get, "https://graph.facebook.com/v3.2/#{graph_id}/insights?access_token=#{access_token}&date_preset=lifetime&fields=ad_id")
225
+ .to_return(status: 400, body: "{\"error\":{\"message\":\"(#100) date_preset must be \",\"type\":\"OAuthException\",\"code\":100,\"fbtrace_id\":\"GB8SawFk\/47\"}}", headers: headers)
226
+
227
+ expect do
228
+ graph.get_object("#{graph_id}/insights", fields: 'ad_id', date_preset: 'lifetime')
229
+ end.to raise_error Koala::KoalaError
230
+ end
231
+ end
232
+
233
+ describe 'POST / batch requests' do
234
+ it 'passes through insight requests' do
235
+ graph_id = Faker::Number.number(10)
236
+ json = File.open("#{Dir.pwd}/spec/fb_batch_response.json").read
237
+ json.gsub!('replace_ad_id', graph_id)
238
+ stub_request(:post, "https://graph.facebook.com/v3.2/?access_token=#{access_token}&batch=%5B%7B%22method%22:%22get%22,%22relative_url%22:%22#{graph_id}/insights?date_preset=lifetime%26fields=ad_id%22%7D,%7B%22method%22:%22get%22,%22relative_url%22:%22#{graph_id}/insights?date_preset=lifetime%26fields=ad_id%22%7D%5D")
239
+ .to_return(status: 200, body: json, headers: {})
240
+
241
+ result = graph.batch do |batch|
242
+ batch.get_object("#{graph_id}/insights", fields: 'ad_id', date_preset: 'lifetime')
243
+ batch.get_object("#{graph_id}/insights", fields: 'ad_id', date_preset: 'lifetime')
244
+ end
245
+
246
+ expect(result.first.first).to include 'ad_id' => graph_id
247
+ end
248
+
249
+ it 'returns errror when they happen' do
250
+ graph_id = Faker::Number.number(10)
251
+ json = File.open("#{Dir.pwd}/spec/fb_batch_error_response.json").read
252
+ json.gsub!('replace_ad_id', graph_id)
253
+ stub_request(:post, "https://graph.facebook.com/v3.2/?access_token=#{access_token}&batch=%5B%7B%22method%22:%22get%22,%22relative_url%22:%22doesnotexist/insights?date_preset=lifetime%26fields=ad_id%22%7D,%7B%22method%22:%22get%22,%22relative_url%22:%22doesnotexist/insights?date_preset=lifetime%26fields=ad_id%22%7D%5D").
254
+ # stub_request(:get, "https://graph.facebook.com/v3.0/?access_token=#{access_token}&batch=%5B%7B%22method%22:%22get%22,%22relative_url%22:%22#{graph_id}/insights?date_preset=lifetime%26fields=ad_id%22%7D%5D").
255
+ to_return(status: 200, body: json)
256
+
257
+ result = graph.batch do |batch|
258
+ batch.get_object('doesnotexist/insights', fields: 'ad_id', date_preset: 'lifetime')
259
+ batch.get_object('doesnotexist/insights', fields: 'ad_id', date_preset: 'lifetime')
260
+ end
261
+
262
+ expect(result[0].fb_error_code).to eq 803
263
+ expect(result[1].fb_error_code).to eq 803
264
+ end
265
+
266
+ it 'passes headers through' do
267
+ graph_id = Faker::Number.number(10)
268
+ json = File.open("#{Dir.pwd}/spec/fb_batch_error_response.json").read
269
+ json.gsub!('replace_ad_id', graph_id)
270
+ stub_request(:post, "https://graph.facebook.com/v3.2/?access_token=#{access_token}&batch=%5B%7B%22method%22:%22get%22,%22relative_url%22:%22doesnotexist/insights?date_preset=lifetime%26fields=ad_id%22%7D,%7B%22method%22:%22get%22,%22relative_url%22:%22doesnotexist/insights?date_preset=lifetime%26fields=ad_id%22%7D%5D")
271
+ .to_return(status: 200,
272
+ body: json,
273
+ headers: { 'Content-Type' => 'text/javascript; charset=UTF-8',
274
+ 'Facebook-API-Version' => 'v3.2',
275
+ 'X-App-Usage' => '{"call_count":0,"total_cputime":0,"total_time":0}',
276
+ 'ETag' => '9d4067db4e21a79fc53d45e0f487e67c5c0b50a1',
277
+ 'Access-Control-Allow-Origin' => '*',
278
+ 'Cache-Control' => 'private, no-cache, no-store, must-revalidate',
279
+ 'Vary' => 'Accept-Encoding',
280
+ 'Expires' => 'Sat, 01 Jan 2000 00:00:00 GMT',
281
+ 'X-Ad-Account-Usage' => '{"acc_id_util_pct":0}',
282
+ 'Strict-Transport-Security' => 'max-age=15552000; preload',
283
+ 'Transfer-Encoding' => 'chunked',
284
+ 'Connection' => 'keep-alive',
285
+ 'Pragma' => 'no-cache' })
286
+
287
+ result = graph.batch do |batch|
288
+ batch.get_object('doesnotexist/insights', fields: 'ad_id', date_preset: 'lifetime')
289
+ batch.get_object('doesnotexist/insights', fields: 'ad_id', date_preset: 'lifetime')
290
+ end
291
+
292
+ expect(result).not_to be_nil
293
+ end
294
+ end
295
+
296
+ describe 'POST /:ad_set_id' do
297
+ it 'passes through requests to pause an ad' do
298
+ graph_id = Faker::Number.number(10)
299
+ stub_request(:post, "https://graph.facebook.com/v3.2/#{graph_id}/?access_token=#{access_token}&status=PAUSED")
300
+ .to_return(status: 200, body: { success: true }.to_json, headers: {})
301
+
302
+ result = graph.put_connections(graph_id, '', status: 'PAUSED')
303
+
304
+ expect(result).to include 'success' => true
305
+ end
306
+ end
307
+
308
+ describe 'GET /' do
309
+ it 'gets graph objects' do
310
+ graph_id = Faker::Number.number(10)
311
+ json = File.open("#{Dir.pwd}/spec/fb_graph_object_response.json").read
312
+ stub_request(:get, "https://graph.facebook.com/v3.2/?access_token=#{access_token}&fields=og_object%7Btitle,description,image%7D&id=https://fox2now.com/2018/04/02/5-myths-about-organ-donation/")
313
+ .to_return(
314
+ status: 200,
315
+ body: json,
316
+ headers: { 'Vary' => 'Accept-Encoding',
317
+ 'ETag' => '"90f0a9d85d04bf2760528d1f834dfa8444145dfb"',
318
+ 'x-app-usage' => '{"call_count":0,"total_cputime":0,"total_time":0}',
319
+ 'Content-Type' => 'application/json; charset=UTF-8',
320
+ 'facebook-api-version' => 'v3.0',
321
+ 'x-fb-rev' => '4669664',
322
+ 'Access-Control-Allow-Origin' => '*',
323
+ 'Cache-Control' => 'private, no-cache, no-store, must-revalidate',
324
+ 'x-fb-trace-id' => 'HwmUZwadEmw',
325
+ 'Expires' => 'Sat, 01 Jan 2000 00:00:00 GMT',
326
+ 'Strict-Transport-Security' => 'max-age=15552000; preload',
327
+ 'Pragma' => 'no-cache',
328
+ 'X-FB-Debug' => 'ykeEji7+g4+BKuq0fR8pJC2k3egR1GLILfEN7eL2VcGOBqKa7u2nLHGrLOE5DfB6A7YlPTalEVgbAx8oDyIDnQ==',
329
+ 'Date' => 'Tue, 08 Jan 2019 20:17:41 GMT',
330
+ 'Transfer-Encoding' => 'chunked',
331
+ 'Connection' => 'keep-alive' }
332
+ )
333
+
334
+ result = graph.get_object('', fields: 'og_object{title,description,image}', id: 'https://fox2now.com/2018/04/02/5-myths-about-organ-donation/')
335
+
336
+ expect(result.dig('og_object')['title']).to eq '5 myths about organ donation'
337
+ end
338
+ end
339
+ end