fake_fb_marketing_api 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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