tentd 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.
Files changed (101) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +9 -0
  5. data/Guardfile +6 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +49 -0
  8. data/Rakefile +8 -0
  9. data/bin/tent-server +3 -0
  10. data/lib/tentd.rb +31 -0
  11. data/lib/tentd/api.rb +58 -0
  12. data/lib/tentd/api/apps.rb +196 -0
  13. data/lib/tentd/api/authentication_finalize.rb +12 -0
  14. data/lib/tentd/api/authentication_lookup.rb +27 -0
  15. data/lib/tentd/api/authentication_verification.rb +50 -0
  16. data/lib/tentd/api/authorizable.rb +21 -0
  17. data/lib/tentd/api/authorization.rb +14 -0
  18. data/lib/tentd/api/core_profile_data.rb +45 -0
  19. data/lib/tentd/api/followers.rb +218 -0
  20. data/lib/tentd/api/followings.rb +241 -0
  21. data/lib/tentd/api/groups.rb +161 -0
  22. data/lib/tentd/api/middleware.rb +32 -0
  23. data/lib/tentd/api/posts.rb +373 -0
  24. data/lib/tentd/api/profile.rb +78 -0
  25. data/lib/tentd/api/router.rb +123 -0
  26. data/lib/tentd/api/router/caching_headers.rb +49 -0
  27. data/lib/tentd/api/router/extract_params.rb +88 -0
  28. data/lib/tentd/api/router/serialize_response.rb +38 -0
  29. data/lib/tentd/api/user_lookup.rb +10 -0
  30. data/lib/tentd/core_ext/hash/slice.rb +29 -0
  31. data/lib/tentd/datamapper/array_property.rb +23 -0
  32. data/lib/tentd/datamapper/query.rb +19 -0
  33. data/lib/tentd/json_patch.rb +181 -0
  34. data/lib/tentd/model.rb +30 -0
  35. data/lib/tentd/model/app.rb +68 -0
  36. data/lib/tentd/model/app_authorization.rb +113 -0
  37. data/lib/tentd/model/follower.rb +105 -0
  38. data/lib/tentd/model/following.rb +100 -0
  39. data/lib/tentd/model/group.rb +24 -0
  40. data/lib/tentd/model/mention.rb +19 -0
  41. data/lib/tentd/model/notification_subscription.rb +56 -0
  42. data/lib/tentd/model/permissible.rb +227 -0
  43. data/lib/tentd/model/permission.rb +28 -0
  44. data/lib/tentd/model/post.rb +178 -0
  45. data/lib/tentd/model/post_attachment.rb +27 -0
  46. data/lib/tentd/model/post_version.rb +64 -0
  47. data/lib/tentd/model/profile_info.rb +80 -0
  48. data/lib/tentd/model/random_public_id.rb +46 -0
  49. data/lib/tentd/model/serializable.rb +58 -0
  50. data/lib/tentd/model/type_properties.rb +36 -0
  51. data/lib/tentd/model/user.rb +39 -0
  52. data/lib/tentd/model/user_scoped.rb +14 -0
  53. data/lib/tentd/notifications.rb +13 -0
  54. data/lib/tentd/notifications/girl_friday.rb +30 -0
  55. data/lib/tentd/notifications/sidekiq.rb +50 -0
  56. data/lib/tentd/tent_type.rb +20 -0
  57. data/lib/tentd/tent_version.rb +41 -0
  58. data/lib/tentd/version.rb +3 -0
  59. data/spec/fabricators/app_authorizations_fabricator.rb +5 -0
  60. data/spec/fabricators/apps_fabricator.rb +11 -0
  61. data/spec/fabricators/followers_fabricator.rb +14 -0
  62. data/spec/fabricators/followings_fabricator.rb +17 -0
  63. data/spec/fabricators/groups_fabricator.rb +3 -0
  64. data/spec/fabricators/mentions_fabricator.rb +3 -0
  65. data/spec/fabricators/notification_subscriptions_fabricator.rb +4 -0
  66. data/spec/fabricators/permissions_fabricator.rb +1 -0
  67. data/spec/fabricators/post_attachments_fabricator.rb +8 -0
  68. data/spec/fabricators/post_versions_fabricator.rb +12 -0
  69. data/spec/fabricators/posts_fabricator.rb +12 -0
  70. data/spec/fabricators/profile_infos_fabricator.rb +30 -0
  71. data/spec/integration/api/apps_spec.rb +466 -0
  72. data/spec/integration/api/followers_spec.rb +535 -0
  73. data/spec/integration/api/followings_spec.rb +688 -0
  74. data/spec/integration/api/groups_spec.rb +207 -0
  75. data/spec/integration/api/posts_spec.rb +874 -0
  76. data/spec/integration/api/profile_spec.rb +285 -0
  77. data/spec/integration/api/router_spec.rb +102 -0
  78. data/spec/integration/model/app_authorization_spec.rb +59 -0
  79. data/spec/integration/model/app_spec.rb +63 -0
  80. data/spec/integration/model/follower_spec.rb +344 -0
  81. data/spec/integration/model/following_spec.rb +97 -0
  82. data/spec/integration/model/group_spec.rb +39 -0
  83. data/spec/integration/model/notification_subscription_spec.rb +145 -0
  84. data/spec/integration/model/post_spec.rb +658 -0
  85. data/spec/spec_helper.rb +37 -0
  86. data/spec/support/expect_server.rb +3 -0
  87. data/spec/support/json_request.rb +54 -0
  88. data/spec/support/with_constant.rb +23 -0
  89. data/spec/support/with_warnings.rb +6 -0
  90. data/spec/unit/api/authentication_finalize_spec.rb +45 -0
  91. data/spec/unit/api/authentication_lookup_spec.rb +65 -0
  92. data/spec/unit/api/authentication_verification_spec.rb +50 -0
  93. data/spec/unit/api/authorizable_spec.rb +50 -0
  94. data/spec/unit/api/authorization_spec.rb +44 -0
  95. data/spec/unit/api/caching_headers_spec.rb +121 -0
  96. data/spec/unit/core_profile_data_spec.rb +64 -0
  97. data/spec/unit/json_patch_spec.rb +407 -0
  98. data/spec/unit/tent_type_spec.rb +28 -0
  99. data/spec/unit/tent_version_spec.rb +68 -0
  100. data/tentd.gemspec +36 -0
  101. metadata +435 -0
@@ -0,0 +1,285 @@
1
+ require 'spec_helper'
2
+
3
+ describe TentD::API::Profile do
4
+ def app
5
+ TentD::API.new
6
+ end
7
+
8
+ let(:env) { {} }
9
+ let(:params) { Hash.new }
10
+
11
+ let(:authorized_info_types) { [] }
12
+ let(:authorized_scopes) { [] }
13
+ let(:app_authorization) do
14
+ Fabricate(
15
+ :app_authorization,
16
+ :app => Fabricate(:app),
17
+ :profile_info_types => authorized_info_types,
18
+ :scopes => authorized_scopes
19
+ )
20
+ end
21
+
22
+ let(:basic_info_type) { "https://tent.io/types/info/basic/v0.1.0" }
23
+ let(:basic_info_content) do
24
+ {
25
+ "name" => "John Smith",
26
+ "age" => 21
27
+ }
28
+ end
29
+
30
+ let(:work_history_type) { "https://tent.io/types/info/work-history/v0.1.0" }
31
+ let(:work_history_content) do
32
+ {
33
+ "employers" => ["Foo Corp"]
34
+ }
35
+ end
36
+
37
+ def url_encode_type(type)
38
+ URI.encode(type, ":/")
39
+ end
40
+
41
+ def create_info(type, content, options = {})
42
+ Fabricate(:profile_info, :public => options[:public], :type => type, :content => content)
43
+ end
44
+
45
+ describe 'GET /profile' do
46
+ context 'when read_profile scope authorized' do
47
+ let(:authorized_scopes) { [:read_profile] }
48
+ before { env['current_auth'] = app_authorization }
49
+
50
+ context 'when authorized for all info types' do
51
+ let(:authorized_info_types) { ['all'] }
52
+
53
+ it 'should return all info types' do
54
+ TentD::Model::ProfileInfo.all.destroy
55
+
56
+ profile_infos = []
57
+ profile_infos << Fabricate(:profile_info, :public => false)
58
+ profile_infos << Fabricate(:basic_profile_info, :public => false)
59
+
60
+ json_get '/profile', params, env
61
+ expect(last_response.body).to eq({
62
+ "#{ profile_infos.first.type.uri }" => profile_infos.first.content.merge(:permissions => profile_infos.first.permissions_json),
63
+ "#{ profile_infos.last.type.uri }" => profile_infos.last.content.merge(:permissions => profile_infos.first.permissions_json)
64
+ }.to_json)
65
+ end
66
+ end
67
+
68
+ context 'when authorized for specific info types' do
69
+ let(:authorized_info_types) { ['https://tent.io/types/info/basic'] }
70
+
71
+ it 'should only return authorized info types' do
72
+ TentD::Model::ProfileInfo.all.destroy
73
+
74
+ profile_infos = []
75
+ profile_infos << Fabricate(:profile_info, :public => false, :type => "https://tent.io/types/info/basic/v0.1.0")
76
+ profile_infos << Fabricate(:profile_info, :public => false)
77
+
78
+ json_get '/profile', params, env
79
+ expect(last_response.body).to eq({
80
+ "#{ profile_infos.first.type.uri }" => profile_infos.first.content.merge(:permissions => profile_infos.first.permissions_json)
81
+ }.to_json)
82
+ end
83
+ end
84
+ end
85
+
86
+ context 'when read_profile scope unauthorized' do
87
+ it 'should only return public profile into types' do
88
+ TentD::Model::ProfileInfo.all.destroy
89
+
90
+ profile_infos = []
91
+ profile_infos << Fabricate(:profile_info, :public => true)
92
+ profile_infos << Fabricate(:basic_profile_info, :public => false)
93
+
94
+ json_get '/profile', params, env
95
+ expect(last_response.body).to eq({
96
+ "#{ profile_infos.first.type.uri }" => profile_infos.first.content.merge(:permissions => profile_infos.first.permissions_json)
97
+ }.to_json)
98
+ end
99
+ end
100
+ end
101
+
102
+ describe 'PUT /profile/:type_url' do
103
+ before { env['current_auth'] = app_authorization }
104
+
105
+ context 'when authorized' do
106
+ let(:authorized_scopes) { [:write_profile] }
107
+
108
+ can_update_basic_info_type = proc do
109
+ it 'should update info type' do
110
+ info = create_info(basic_info_type, basic_info_content, :public => false)
111
+
112
+ expect(info.type.uri).to eq(basic_info_type)
113
+
114
+ data = {
115
+ "name" => "John Doe"
116
+ }
117
+
118
+ json_put "/profile/#{url_encode_type(basic_info_type)}", data, env
119
+
120
+ expect(last_response.status).to eq(200)
121
+ expect(info.reload.content).to eq(data)
122
+
123
+ expect(info.reload.type.uri).to eq(basic_info_type)
124
+
125
+ end
126
+
127
+ it 'should create unless exists' do
128
+ TentD::Model::ProfileInfo.all.destroy
129
+
130
+ data = {
131
+ "name" => "John Doe"
132
+ }
133
+
134
+ expect(lambda {
135
+ json_put "/profile/#{url_encode_type(basic_info_type)}", data, env
136
+ }).to change(TentD::Model::ProfileInfo, :count).by(1)
137
+
138
+ info = TentD::Model::ProfileInfo.last
139
+ expect(last_response.status).to eq(200)
140
+ expect(info.content).to eq(data)
141
+ expect(info.type.version).to eq('0.1.0')
142
+ end
143
+ end
144
+
145
+ context 'when all info types authorized' do
146
+ let(:authorized_info_types) { ['all'] }
147
+
148
+ context '', &can_update_basic_info_type
149
+ end
150
+
151
+ context 'when specific info types authorized' do
152
+ context 'when :type_url authorized info type' do
153
+ let(:authorized_info_types) { [basic_info_type] }
154
+
155
+ context '', &can_update_basic_info_type
156
+ end
157
+
158
+ context 'when :type_url not authoried info type' do
159
+ it 'should return 403' do
160
+ json_put "/profile/#{url_encode_type(basic_info_type)}", params, env
161
+ expect(last_response.status).to eq(403)
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ context 'when not authorized' do
168
+ it 'should return 403' do
169
+ json_put "/profile/#{url_encode_type(basic_info_type)}", params, env
170
+ expect(last_response.status).to eq(403)
171
+ end
172
+ end
173
+ end
174
+
175
+ describe 'PATCH /profile' do
176
+ before { env['current_auth'] = app_authorization }
177
+
178
+ def diff_encode_type(type)
179
+ type.gsub(/~/, '~0').gsub(/\//, '~1')
180
+ end
181
+
182
+ context 'when authorized' do
183
+ let(:authorized_scopes) { [:write_profile] }
184
+
185
+ can_update_basic_info_type = proc do
186
+ it 'should update basic info with diff' do
187
+ TentD::Model::ProfileInfo.all.destroy
188
+
189
+ info = create_info(basic_info_type, basic_info_content, :public => false)
190
+
191
+ diff = [
192
+ { "add" => "#{diff_encode_type(basic_info_type)}/city", "value" => "New York" },
193
+ { "remove" => "#{diff_encode_type(basic_info_type)}/age" },
194
+ { "replace" => "#{diff_encode_type(basic_info_type)}/name", "value" => "Alex Smith" }
195
+ ]
196
+
197
+ expected_data = {
198
+ "city" => "New York",
199
+ "name" => "Alex Smith",
200
+ }
201
+
202
+ json_patch "/profile", diff, env
203
+
204
+ expect(last_response.status).to eq(200)
205
+ expect(info.reload.content).to eq(expected_data)
206
+ end
207
+
208
+ it 'should return 422 if diff tests fail' do
209
+ TentD::Model::ProfileInfo.all.destroy
210
+
211
+ info = create_info(basic_info_type, basic_info_content, :public => false)
212
+
213
+ diff = [
214
+ { "add" => "#{diff_encode_type(basic_info_type)}/city", "value" => "New York" },
215
+ { "remove" => "#{diff_encode_type(basic_info_type)}/age" },
216
+ { "replace" => "#{diff_encode_type(basic_info_type)}/name", "value" => "Alex Smith" },
217
+ { "test" => "#{diff_encode_type(basic_info_type)}/age", "value" => 40 },
218
+ ]
219
+
220
+ expected_data = info.content
221
+
222
+ json_patch "/profile", diff, env
223
+
224
+ expect(last_response.status).to eq(422)
225
+ expect(info.reload.content).to eq(expected_data)
226
+ end
227
+ end
228
+
229
+ context 'when all info types authorized' do
230
+ let(:authorized_info_types) { ['all'] }
231
+
232
+ context '', &can_update_basic_info_type
233
+
234
+ it 'should update any info' do
235
+ TentD::Model::ProfileInfo.all.destroy
236
+
237
+ info = create_info(basic_info_type, basic_info_content, :public => false)
238
+
239
+ diff = [
240
+ { "add" => "#{diff_encode_type(basic_info_type)}/city", "value" => "New York" },
241
+ { "remove" => "#{diff_encode_type(basic_info_type)}/age" },
242
+ { "replace" => "#{diff_encode_type(basic_info_type)}/name", "value" => "Alex Smith" },
243
+ { "add" => "#{diff_encode_type(work_history_type)}/employers", "value" => ["Foo Corp"] },
244
+ { "move" => "#{diff_encode_type(basic_info_type)}/name", "to" => "#{diff_encode_type(work_history_type)}/name" },
245
+ { "add" => "#{diff_encode_type(work_history_type)}/employers/1", "value" => "Bar Corp" },
246
+ { "add" => "#{diff_encode_type(work_history_type)}/city", "value" => "London" }
247
+ ]
248
+
249
+ expected_basic_data = {
250
+ "city" => "New York",
251
+ }
252
+
253
+ expected_work_data = {
254
+ "name" => "Alex Smith",
255
+ "employers" => ["Foo Corp", "Bar Corp"],
256
+ "city" => "London",
257
+ }
258
+
259
+ expect(lambda {
260
+ json_patch "/profile", diff, env
261
+ }).to change(TentD::Model::ProfileInfo, :count).by(1)
262
+
263
+ expect(last_response.status).to eq(200)
264
+ expect(info.reload.content).to eq(expected_basic_data)
265
+
266
+ work_info = TentD::Model::ProfileInfo.last
267
+ expect(work_info.content).to eq(expected_work_data)
268
+ end
269
+ end
270
+
271
+ context 'when specific info types authorized' do
272
+ let(:authorized_info_types) { [basic_info_type] }
273
+
274
+ context '', &can_update_basic_info_type
275
+ end
276
+ end
277
+
278
+ context 'when not authorized' do
279
+ it 'should return 403' do
280
+ json_patch '/profile', params, env
281
+ expect(last_response.status).to eq(403)
282
+ end
283
+ end
284
+ end
285
+ end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe TentD::API::Router do
4
+ class TestMiddleware < TentD::API::Middleware
5
+ def action(env)
6
+ env['response'] = { 'params' => env['params'] }
7
+ env
8
+ end
9
+ end
10
+
11
+ class TestMiddlewarePrematureResponse < TentD::API::Middleware
12
+ def action(env)
13
+ [200, { 'Content-Type' => 'text/plain' }, 'Premature-Response']
14
+ end
15
+ end
16
+
17
+ class TestMountedApp
18
+ include TentD::API::Router
19
+
20
+ get '/chunky/:bacon' do |b|
21
+ b.use TestMiddleware
22
+ end
23
+ end
24
+
25
+ class PrefixMountedApp
26
+ def initialize(app)
27
+ @app = app
28
+ end
29
+
30
+ def call(env)
31
+ myprefix = '/prefix'
32
+
33
+ if env['PATH_INFO'].start_with?(myprefix)
34
+ env['SCRIPT_NAME'] = env['SCRIPT_NAME'][0..-2] if env['SCRIPT_NAME'].end_with?('/') # strip trailing slash
35
+ env['SCRIPT_NAME'] += myprefix
36
+
37
+ env['PATH_INFO'].sub! myprefix, ''
38
+ @app.call(env)
39
+ end
40
+ end
41
+ end
42
+
43
+ class TestApp
44
+ include TentD::API::Router
45
+
46
+ get '/foo/:bar' do |b|
47
+ b.use TestMiddleware
48
+ end
49
+
50
+ get '/premature/response' do |b|
51
+ b.use TestMiddlewarePrematureResponse
52
+ b.use TestMiddleware
53
+ end
54
+
55
+ post %r{^/foo/([^/]+)/bar} do |b|
56
+ b.use TestMiddleware
57
+ end
58
+
59
+ mount TestMountedApp
60
+ end
61
+
62
+ def app
63
+ TestApp.new
64
+ end
65
+
66
+ let(:env) { { 'authorized_scopes' => [] } }
67
+
68
+ context "as a mounted app with a prefix" do
69
+ let(:app) { PrefixMountedApp.new(TestApp.new) }
70
+
71
+ it "still matches the path name" do
72
+ json_get '/prefix/foo/baz', nil, env
73
+ expect(last_response.status).to eq(200)
74
+ expect(JSON.parse(last_response.body)['params']['bar']).to eq('baz')
75
+ end
76
+ end
77
+
78
+ it "should extract params" do
79
+ json_get '/foo/baz', nil, env
80
+ expect(last_response.status).to eq(200)
81
+ expect(JSON.parse(last_response.body)['params']['bar']).to eq('baz')
82
+ end
83
+
84
+ it "should merge both sets of params" do
85
+ json_post '/foo/baz/bar?chunky=bacon', nil, env
86
+ expect(last_response.status).to eq(200)
87
+ actual_body = JSON.parse(last_response.body)
88
+ expect(actual_body['params']['chunky']).to eq('bacon')
89
+ expect(actual_body['params']['captures']).to include('baz')
90
+ end
91
+
92
+ it "should work with mount" do
93
+ json_get '/chunky/crunch', nil, env
94
+ expect(last_response.status).to eq(200)
95
+ expect(JSON.parse(last_response.body)['params']['bacon']).to eq('crunch')
96
+ end
97
+
98
+ it "should allow middleware to prematurely respond" do
99
+ json_get '/premature/response', nil, env
100
+ expect(last_response.body).to eq('Premature-Response')
101
+ end
102
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe TentD::Model::AppAuthorization do
4
+ let(:app_authorization) { Fabricate(:app_authorization, :app => Fabricate(:app)) }
5
+
6
+ describe '.follow_url' do
7
+ it 'should find app authorization with follow_ui scope and follow_url' do
8
+ Fabricate(:app_authorization, :app => Fabricate(:app), :scopes => %w{ follow_ui }, :follow_url => 'https://follow.example.org/awesome-ui')
9
+ app_auth = Fabricate(:app_authorization, :app => Fabricate(:app), :scopes => %w{ read_posts follow_ui write_posts }, :follow_url => 'https://follow.example.com')
10
+ entity = 'https://johndoe.example.org'
11
+
12
+ follow_url = described_class.follow_url(entity)
13
+ expect(follow_url).to eq("#{app_auth.follow_url}?entity=#{URI.encode_www_form_component(entity)}")
14
+ end
15
+ end
16
+
17
+ describe '#as_json' do
18
+ let(:app_attributes) do
19
+ {
20
+ :id => app_authorization.public_id,
21
+ :post_types => app_authorization.post_types,
22
+ :profile_info_types => app_authorization.profile_info_types,
23
+ :scopes => app_authorization.scopes,
24
+ :notification_url => app_authorization.notification_url,
25
+ :created_at => app_authorization.created_at.to_time.to_i,
26
+ :updated_at => app_authorization.updated_at.to_time.to_i
27
+ }
28
+ end
29
+
30
+ context 'with options[:app]' do
31
+ let(:options) { { :app => true } }
32
+
33
+ it 'should return everything except mac stuff' do
34
+ expect(app_authorization.as_json(options)).to eq(app_attributes)
35
+ end
36
+
37
+ context 'and options[:authorization_token]' do
38
+ before { options[:authorization_token] = true }
39
+ it 'should return token code' do
40
+ expect(app_authorization.as_json(options)).to eq(app_attributes.merge(
41
+ :token_code => app_authorization.token_code
42
+ ))
43
+ end
44
+ end
45
+
46
+ context 'and options[:mac]' do
47
+ before { options[:mac] = true }
48
+
49
+ it 'should return mac stuff' do
50
+ expect(app_authorization.as_json(options)).to eq(app_attributes.merge(
51
+ :mac_key_id => app_authorization.mac_key_id,
52
+ :mac_key => app_authorization.mac_key,
53
+ :mac_algorithm => app_authorization.mac_algorithm
54
+ ))
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe TentD::Model::App do
4
+ let(:app) { Fabricate(:app) }
5
+
6
+ describe '#as_json' do
7
+ let(:public_attributes) do
8
+ {
9
+ :id => app.public_id,
10
+ :name => app.name,
11
+ :description => app.description,
12
+ :url => app.url,
13
+ :icon => app.icon,
14
+ :redirect_uris => app.redirect_uris,
15
+ :scopes => app.scopes,
16
+ :authorizations => []
17
+ }
18
+ end
19
+
20
+ context 'without options' do
21
+ it 'should return public_attributes' do
22
+ expect(app.as_json).to eq(public_attributes)
23
+ end
24
+ end
25
+
26
+ with_mac_keys = proc do
27
+ it 'should return mac keys' do
28
+ expect(app.as_json(options)).to eq(public_attributes.merge(
29
+ :mac_key_id => app.mac_key_id,
30
+ :mac_key => app.mac_key,
31
+ :mac_algorithm => app.mac_algorithm
32
+ ))
33
+ end
34
+ end
35
+
36
+ context 'with options[:mac]' do
37
+ let(:options) { { :mac => true } }
38
+ context &with_mac_keys
39
+ end
40
+
41
+ context 'with options[:exclude]' do
42
+ it 'should excude specified keys' do
43
+ attribtues = public_attributes
44
+ attribtues.delete(:id)
45
+ expect(app.as_json(:exclude => [:id])).to eq(attribtues)
46
+ end
47
+ end
48
+
49
+ context 'with options[:app]' do
50
+ let!(:app) { Fabricate(:app) }
51
+ let!(:authorization) { Fabricate(:app_authorization, :app => app) }
52
+
53
+ it 'should return authorizations' do
54
+ options = { :app => true }
55
+ expect(app.as_json(options)).to eq(public_attributes.merge(
56
+ :authorizations => [authorization.as_json(options)],
57
+ :created_at => app.created_at.to_time.to_i,
58
+ :updated_at => app.updated_at.to_time.to_i
59
+ ))
60
+ end
61
+ end
62
+ end
63
+ end