introspective_grape 0.0.3

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 (165) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +35 -0
  4. data/Gemfile +15 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +103 -0
  7. data/Rakefile +26 -0
  8. data/app/assets/images/introspective_grape/.keep +0 -0
  9. data/app/assets/stylesheets/introspective_grape/.keep +0 -0
  10. data/app/controllers/.keep +0 -0
  11. data/app/helpers/.keep +0 -0
  12. data/app/mailers/.keep +0 -0
  13. data/app/models/.keep +0 -0
  14. data/app/views/.keep +0 -0
  15. data/bin/rails +12 -0
  16. data/introspective_grape.gemspec +49 -0
  17. data/lib/introspective_grape/api.rb +445 -0
  18. data/lib/introspective_grape/camel_snake.rb +71 -0
  19. data/lib/introspective_grape/version.rb +3 -0
  20. data/lib/introspective_grape.rb +4 -0
  21. data/lib/tasks/introspective_grape_tasks.rake +4 -0
  22. data/spec/dummy/Gemfile +4 -0
  23. data/spec/dummy/README.rdoc +28 -0
  24. data/spec/dummy/Rakefile +6 -0
  25. data/spec/dummy/app/api/active_record_helpers.rb +17 -0
  26. data/spec/dummy/app/api/api_helpers.rb +36 -0
  27. data/spec/dummy/app/api/dummy/chat_api.rb +108 -0
  28. data/spec/dummy/app/api/dummy/company_api.rb +8 -0
  29. data/spec/dummy/app/api/dummy/entities.rb +25 -0
  30. data/spec/dummy/app/api/dummy/location_api.rb +37 -0
  31. data/spec/dummy/app/api/dummy/project_api.rb +51 -0
  32. data/spec/dummy/app/api/dummy/role_api.rb +7 -0
  33. data/spec/dummy/app/api/dummy/sessions.rb +55 -0
  34. data/spec/dummy/app/api/dummy/user_api.rb +32 -0
  35. data/spec/dummy/app/api/dummy_api.rb +57 -0
  36. data/spec/dummy/app/api/error_handlers.rb +28 -0
  37. data/spec/dummy/app/api/permissions_helper.rb +7 -0
  38. data/spec/dummy/app/assets/images/.keep +0 -0
  39. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  40. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  41. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  42. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  43. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  44. data/spec/dummy/app/mailers/.keep +0 -0
  45. data/spec/dummy/app/models/.keep +0 -0
  46. data/spec/dummy/app/models/abstract_adapter.rb +13 -0
  47. data/spec/dummy/app/models/admin_user.rb +6 -0
  48. data/spec/dummy/app/models/chat.rb +18 -0
  49. data/spec/dummy/app/models/chat_message.rb +34 -0
  50. data/spec/dummy/app/models/chat_message_user.rb +17 -0
  51. data/spec/dummy/app/models/chat_user.rb +16 -0
  52. data/spec/dummy/app/models/company.rb +14 -0
  53. data/spec/dummy/app/models/concerns/.keep +0 -0
  54. data/spec/dummy/app/models/image.rb +21 -0
  55. data/spec/dummy/app/models/job.rb +10 -0
  56. data/spec/dummy/app/models/locatable.rb +6 -0
  57. data/spec/dummy/app/models/location.rb +26 -0
  58. data/spec/dummy/app/models/location_beacon.rb +16 -0
  59. data/spec/dummy/app/models/location_gps.rb +14 -0
  60. data/spec/dummy/app/models/project.rb +20 -0
  61. data/spec/dummy/app/models/project_job.rb +7 -0
  62. data/spec/dummy/app/models/role.rb +30 -0
  63. data/spec/dummy/app/models/super_user.rb +11 -0
  64. data/spec/dummy/app/models/team.rb +9 -0
  65. data/spec/dummy/app/models/team_user.rb +13 -0
  66. data/spec/dummy/app/models/user/chatter.rb +79 -0
  67. data/spec/dummy/app/models/user.rb +84 -0
  68. data/spec/dummy/app/models/user_location.rb +28 -0
  69. data/spec/dummy/app/models/user_project_job.rb +16 -0
  70. data/spec/dummy/app/policies/application_policy.rb +47 -0
  71. data/spec/dummy/app/policies/chat_policy.rb +22 -0
  72. data/spec/dummy/app/policies/company_policy.rb +32 -0
  73. data/spec/dummy/app/policies/location_policy.rb +29 -0
  74. data/spec/dummy/app/policies/project_policy.rb +42 -0
  75. data/spec/dummy/app/policies/role_policy.rb +33 -0
  76. data/spec/dummy/app/policies/user_location_policy.rb +12 -0
  77. data/spec/dummy/app/policies/user_policy.rb +8 -0
  78. data/spec/dummy/app/views/layouts/application.html.erb +13 -0
  79. data/spec/dummy/bin/bundle +3 -0
  80. data/spec/dummy/bin/rails +4 -0
  81. data/spec/dummy/bin/rake +4 -0
  82. data/spec/dummy/bin/setup +29 -0
  83. data/spec/dummy/config/application.rb +38 -0
  84. data/spec/dummy/config/boot.rb +6 -0
  85. data/spec/dummy/config/database.yml +23 -0
  86. data/spec/dummy/config/environment.rb +11 -0
  87. data/spec/dummy/config/environments/development.rb +41 -0
  88. data/spec/dummy/config/environments/production.rb +79 -0
  89. data/spec/dummy/config/environments/test.rb +43 -0
  90. data/spec/dummy/config/initializers/assets.rb +11 -0
  91. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  92. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  93. data/spec/dummy/config/initializers/devise.rb +262 -0
  94. data/spec/dummy/config/initializers/devise_async.rb +2 -0
  95. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  96. data/spec/dummy/config/initializers/inflections.rb +16 -0
  97. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  98. data/spec/dummy/config/initializers/paperclip.rb +13 -0
  99. data/spec/dummy/config/initializers/paperclip_adapter.rb +13 -0
  100. data/spec/dummy/config/initializers/session_store.rb +3 -0
  101. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  102. data/spec/dummy/config/locales/devise.en.yml +60 -0
  103. data/spec/dummy/config/locales/en.yml +23 -0
  104. data/spec/dummy/config/routes.rb +8 -0
  105. data/spec/dummy/config/secrets.yml +22 -0
  106. data/spec/dummy/config.ru +4 -0
  107. data/spec/dummy/db/migrate/20141002205024_devise_create_users.rb +42 -0
  108. data/spec/dummy/db/migrate/20141002211055_devise_create_admin_users.rb +48 -0
  109. data/spec/dummy/db/migrate/20141002211057_create_active_admin_comments.rb +19 -0
  110. data/spec/dummy/db/migrate/20141002220722_add_lockable_to_users.rb +8 -0
  111. data/spec/dummy/db/migrate/20150406213646_create_companies.rb +11 -0
  112. data/spec/dummy/db/migrate/20150414213154_add_user_authentication_token.rb +11 -0
  113. data/spec/dummy/db/migrate/20150415222005_create_roles.rb +12 -0
  114. data/spec/dummy/db/migrate/20150505181635_create_chats.rb +9 -0
  115. data/spec/dummy/db/migrate/20150505181636_create_chat_users.rb +11 -0
  116. data/spec/dummy/db/migrate/20150505181640_create_chat_messages.rb +11 -0
  117. data/spec/dummy/db/migrate/20150507191529_create_chat_message_users.rb +11 -0
  118. data/spec/dummy/db/migrate/20150601200526_create_locations.rb +12 -0
  119. data/spec/dummy/db/migrate/20150601200533_create_locatables.rb +10 -0
  120. data/spec/dummy/db/migrate/20150601212924_create_location_beacons.rb +15 -0
  121. data/spec/dummy/db/migrate/20150601213542_create_location_gps.rb +12 -0
  122. data/spec/dummy/db/migrate/20150609201823_create_user_locations.rb +14 -0
  123. data/spec/dummy/db/migrate/20150616205336_add_role_user_constraint.rb +9 -0
  124. data/spec/dummy/db/migrate/20150617232519_create_projects.rb +10 -0
  125. data/spec/dummy/db/migrate/20150617232521_create_jobs.rb +9 -0
  126. data/spec/dummy/db/migrate/20150617232522_create_project_jobs.rb +11 -0
  127. data/spec/dummy/db/migrate/20150623170133_create_user_project_jobs.rb +12 -0
  128. data/spec/dummy/db/migrate/20150701234929_create_teams.rb +11 -0
  129. data/spec/dummy/db/migrate/20150701234930_create_team_users.rb +11 -0
  130. data/spec/dummy/db/migrate/20150727214950_add_confirmable_to_devise.rb +11 -0
  131. data/spec/dummy/db/migrate/20150820190524_add_user_names.rb +6 -0
  132. data/spec/dummy/db/migrate/20150824215701_create_images.rb +15 -0
  133. data/spec/dummy/db/migrate/20150909225019_add_password_to_project.rb +5 -0
  134. data/spec/dummy/db/schema.rb +278 -0
  135. data/spec/dummy/lib/assets/.keep +0 -0
  136. data/spec/dummy/log/.keep +0 -0
  137. data/spec/dummy/public/404.html +67 -0
  138. data/spec/dummy/public/422.html +67 -0
  139. data/spec/dummy/public/500.html +66 -0
  140. data/spec/dummy/public/favicon.ico +0 -0
  141. data/spec/fixtures/images/avatar.jpeg +0 -0
  142. data/spec/fixtures/images/exif.jpeg +0 -0
  143. data/spec/models/chat_spec.rb +32 -0
  144. data/spec/models/image_spec.rb +14 -0
  145. data/spec/models/locatable_spec.rb +10 -0
  146. data/spec/models/project_spec.rb +17 -0
  147. data/spec/models/role_spec.rb +63 -0
  148. data/spec/models/team_spec.rb +17 -0
  149. data/spec/models/team_user_spec.rb +20 -0
  150. data/spec/models/user_location_spec.rb +35 -0
  151. data/spec/models/user_project_job_spec.rb +30 -0
  152. data/spec/models/user_spec.rb +125 -0
  153. data/spec/rails_helper.rb +23 -0
  154. data/spec/requests/chat_api_spec.rb +174 -0
  155. data/spec/requests/company_api_spec.rb +61 -0
  156. data/spec/requests/location_api_spec.rb +96 -0
  157. data/spec/requests/project_api_spec.rb +151 -0
  158. data/spec/requests/role_api_spec.rb +37 -0
  159. data/spec/requests/sessions_api_spec.rb +55 -0
  160. data/spec/requests/user_api_spec.rb +191 -0
  161. data/spec/support/blueprints.rb +103 -0
  162. data/spec/support/location_helper.rb +56 -0
  163. data/spec/support/pundit_helpers.rb +13 -0
  164. data/spec/support/request_helpers.rb +22 -0
  165. metadata +562 -0
@@ -0,0 +1,151 @@
1
+ require 'rails_helper'
2
+
3
+ describe Dummy::ProjectAPI, type: :request do
4
+ before :all do
5
+ [User,Project,Company,Location].map(&:destroy_all)
6
+ cm = User.make!(email:'company.admin@springshot.com')
7
+ pm = User.make!(email:'project.admin@springshot.com')
8
+
9
+ 2.times { Project.make! }
10
+
11
+ c = Company.make!(name:"Sprockets")
12
+ p = Project.make!(name:"Manufacture Sprockets", owner: c)
13
+ Project.make!(name:"Disassemble Sprockets", owner: c)
14
+
15
+ cm.admin_companies.push c
16
+ pm.admin_projects.push p
17
+
18
+ cm.save!
19
+ pm.save!
20
+ end
21
+
22
+ let(:company) { Company.find_by_name("Sprockets") }
23
+ let(:project) { Project.find_by_name("Manufacture Sprockets") }
24
+
25
+ context "As a super admin" do
26
+ it "should return a list of all projects" do
27
+ get '/api/v1/projects'
28
+ response.should be_success
29
+ json.length.should == Project.count
30
+ json.map{|c| c['id'].to_i}.include?(project.id).should == true
31
+ end
32
+
33
+ it "should return the specified project" do
34
+ get "/api/v1/projects/#{project.id}"
35
+ response.should be_success
36
+ json['name'].should == project.name
37
+ end
38
+
39
+ it "should return an error if the project doesn't exist" do
40
+ get "/api/v1/projects/#{Project.last.id+1}"
41
+ response.code.should == "404"
42
+ end
43
+
44
+ context "edit a project team" do
45
+
46
+ before(:each) do
47
+ @team = Team.make!(project: project)
48
+ @u1 = User.make!
49
+ @u2 = User.make!
50
+ UserProjectJob.make!(project: project, job: project.jobs.first, user: @u1)
51
+ UserProjectJob.make!(project: project, job: project.jobs.first, user: @u2)
52
+ end
53
+
54
+ context "via nested attributes" do
55
+ it "should create a team with users" do
56
+ p = { name: 'New Team',
57
+ team_users_attributes: [{ user_id: @u1.id }, { user_id: @u2.id }]
58
+ }
59
+ post "/api/v1/projects/#{project.id}/teams", p
60
+ response.should be_success
61
+ Team.last.name.should == 'New Team'
62
+ Team.last.users.to_a.should == [@u1,@u2]
63
+ end
64
+
65
+ it "should add a team member" do
66
+ p = { team_users_attributes: [
67
+ { user_id: @u1.id }, { user_id: @u2.id }
68
+ ] }
69
+ put "/api/v1/projects/#{project.id}/teams/#{@team.id}", p
70
+ response.should be_success
71
+
72
+ Team.last.users.to_a.should == [@u1,@u2]
73
+ end
74
+
75
+ it "should delete a team member" do
76
+ @team.users << [@u1,@u2]
77
+ @team.save!
78
+ p = { team_users_attributes: [
79
+ { id: @team.team_users.where(user_id:@u1.id).first.id, _destroy: 1 }
80
+ ] }
81
+ put "/api/v1/projects/#{project.id}/teams/#{@team.id}", p
82
+ response.should be_success
83
+ Team.last.users.to_a.should == [@u2]
84
+ end
85
+ end
86
+
87
+ context "edit a project team via nested routes" do
88
+ it "should add a team member" do
89
+ p = { user_id: @u1.id }
90
+ post "/api/v1/projects/#{project.id}/teams/#{@team.id}/team_users", p
91
+ response.should be_success
92
+ Team.last.users.to_a.should == [@u1]
93
+ end
94
+
95
+ it "should delete a team member" do
96
+ @team.users << [@u1,@u2]
97
+ @team.save!
98
+ id = @team.team_users.where(user_id:@u1.id).first.id
99
+ delete "/api/v1/projects/#{project.id}/teams/#{@team.id}/team_users/#{id}"
100
+ response.should be_success
101
+ Team.last.users.to_a.should == [@u2]
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ context "As a company admin" do
108
+ before :all do
109
+ @without_authentication = true
110
+ end
111
+
112
+ before :each do
113
+ Grape::Endpoint.before_each do |endpoint|
114
+ allow(endpoint).to receive(:current_user) do
115
+ User.find_by_email("company.admin@springshot.com")
116
+ end
117
+ end
118
+ end
119
+
120
+ it "should return a list of all the company's projects" do
121
+ get '/api/v1/projects'
122
+ response.should be_success
123
+ json.length.should == 2
124
+ json.map{|c| c['name']}.include?("Manufacture Sprockets").should == true
125
+ json.map{|c| c['name']}.include?("Disassemble Sprockets").should == true
126
+ end
127
+
128
+ end
129
+
130
+ context "As a project admin" do
131
+ before :all do
132
+ @without_authentication = true
133
+ end
134
+ before :each do
135
+ Grape::Endpoint.before_each do |endpoint|
136
+ allow(endpoint).to receive(:current_user) do
137
+ User.find_by_email("project.admin@springshot.com")
138
+ end
139
+ end
140
+ end
141
+
142
+ it "should return a list of all the project admin's projects" do
143
+ get '/api/v1/projects'
144
+ response.should be_success
145
+ json.length.should == 1
146
+ json.map{|c| c['name']}.include?("Manufacture Sprockets").should == true
147
+ json.map{|c| c['name']}.include?("Disassemble Sprockets").should == false
148
+ end
149
+ end
150
+
151
+ end
@@ -0,0 +1,37 @@
1
+ require 'rails_helper'
2
+
3
+ describe Dummy::RoleAPI, type: :request do
4
+ let(:role) { Role.last }
5
+ let(:user) { User.last }
6
+
7
+ before :all do
8
+ Role.destroy_all
9
+ User.make!
10
+ Role.make!(user_id: User.last.id, ownable_type: 'SuperUser')
11
+ end
12
+
13
+ it 'should return a list of user roles' do
14
+ get '/api/v1/roles'
15
+ response.should be_success
16
+ json.length.should == 1
17
+ json.first['id'].to_i.should == role.id
18
+ end
19
+
20
+ it 'should return the specified user role' do
21
+ get "/api/v1/roles/#{role.id}"
22
+ response.should be_success
23
+ json['email'].should == role.email
24
+ end
25
+
26
+ it "should return an error if the role doesn't exist" do
27
+ get "/api/v1/roles/#{role.id+1}"
28
+ response.code.should == '404'
29
+ end
30
+
31
+ it 'should not duplicate user roles' do
32
+ post '/api/v1/roles', { user_id: user.id, ownable_type: 'SuperUser' }
33
+ response.code.should == '400'
34
+ json['error'].should =~ /user has already been assigned that role/
35
+ end
36
+
37
+ end
@@ -0,0 +1,55 @@
1
+ require 'rails_helper'
2
+ describe Dummy::Sessions, type: :request do
3
+ @without_authentication = true
4
+
5
+ before :all do
6
+ @without_authentication = true
7
+ User.make!
8
+ end
9
+
10
+ let(:user) { User.last }
11
+
12
+ context :sign_in do
13
+
14
+ it "should set a user token on login" do
15
+ post '/api/v1/sessions', { login: user.email, password: 'abc12345', token: true }
16
+ response.should be_success
17
+ json['id'].to_i.should == user.id
18
+ json['email'].should == user.email
19
+ json['authentication_token'].should be_truthy
20
+ end
21
+
22
+ it "should not set a token if the login fails" do
23
+ post '/api/v1/sessions', { login: user.email, password: 'bad password', token: true }
24
+ response.should_not be_success
25
+ json['error'].should be_truthy
26
+ json['error']['type'].should == 'unauthorized'
27
+ user.authentication_token.should be_nil
28
+ end
29
+ end
30
+
31
+ context :sign_out do
32
+ it "should reset a user's auth token" do
33
+ user.authentication_token = "1234567890"
34
+ user.save!
35
+ delete "/api/v1/sessions", { api_key: "1234567890" }
36
+ response.should be_success
37
+ user.reload
38
+ user.authentication_token.should be_nil
39
+ end
40
+
41
+ it "signing out an already signed-out user should look fine, right?" do
42
+ user.authentication_token = "1234567890"
43
+ user.save!
44
+ delete "/api/v1/sessions", { api_key: "1234567890" }
45
+ response.should be_success
46
+ user.reload
47
+ user.authentication_token.should be_nil
48
+ delete "/api/v1/sessions", { api_key: "1234567890" }
49
+ response.should be_success
50
+ user.reload
51
+ user.authentication_token.should be_nil
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,191 @@
1
+ require 'rails_helper'
2
+ describe Dummy::UserAPI, type: :request do
3
+
4
+ let(:user) { User.last || User.make!}
5
+ let(:company) { Company.last || Company.make! }
6
+
7
+ before :all do
8
+ User.destroy_all
9
+ c = Company.make
10
+ u = User.make
11
+ u.roles.push Role.new(ownable: c)
12
+ u.save
13
+ end
14
+
15
+ context :index do
16
+
17
+ it "should return a list of users" do
18
+ get '/api/v1/users'
19
+ response.should be_success
20
+ json.length.should == 1
21
+ json.first['id'].to_i.should == user.id
22
+ json.first['first_name'].should == user.first_name
23
+ json.first['last_name'].should == user.last_name
24
+ json.first['roles_attributes'].size.should == 1
25
+ json.first['roles_attributes'].first['ownable_type'].should == 'Company'
26
+ json.first['roles_attributes'].first['ownable_id'].should == company.id
27
+ end
28
+
29
+ it "should not expose users' encrypted_passwords" do
30
+ get "/api/v1/users"
31
+ response.should be_success
32
+ json.first['encrypted_password'].should be_nil
33
+ end
34
+ end
35
+
36
+
37
+ context :show do
38
+ it "should return the specified user" do
39
+ get "/api/v1/users/#{user.id}"
40
+ response.should be_success
41
+ json['email'].should == user.email
42
+ end
43
+
44
+ it "should not expose a user's encrypted_password" do
45
+ get "/api/v1/users/#{user.id}"
46
+ response.should be_success
47
+ json['encrypted_password'].should be_nil
48
+ end
49
+
50
+ it "should return an error if the user doesn't exist" do
51
+ get "/api/v1/users/#{user.id+1}"
52
+ response.code.should == "404"
53
+ end
54
+ end
55
+
56
+
57
+ context :create do
58
+
59
+ it "should create a user and send the confirmation email" do
60
+ post "/api/v1/users", { email: 'email@test.com', password: 'abc12345' }
61
+ response.should be_success
62
+ json['email'].should == user.email
63
+ User.last.confirmed_at.should == nil
64
+ User.last.confirmation_sent_at.should_not == nil
65
+ end
66
+
67
+ it "should create a user and skip the confirmation email" do
68
+ post "/api/v1/users", { email: 'email@test.com', password: 'abc12345', skip_confirmation_email: true }
69
+ response.should be_success
70
+ json['email'].should == user.email
71
+ User.last.confirmed_at.should_not == nil
72
+ User.last.confirmation_sent_at.should == nil
73
+ end
74
+
75
+ it "should validate a new user" do
76
+ post "/api/v1/users", { email: 'a'*257, password: '' }
77
+ response.code.should == "400"
78
+ json['error'].should == "Email: is invalid, Password: can't be blank"
79
+ end
80
+
81
+ let(:params) do
82
+ { email: 'test@test.com', password: 'abc12345', roles_attributes:[] }
83
+ end
84
+
85
+ let(:role) do
86
+ { ownable_id: company.id, ownable_type: 'Company' }
87
+ end
88
+
89
+ it "should create a company admin" do
90
+ params[:roles_attributes].push(role)
91
+ post "/api/v1/users", params
92
+ response.should be_success
93
+ User.last.admin?(company).should be_truthy
94
+ end
95
+
96
+
97
+ context "Project default passwords for new users" do
98
+ let(:job) { Job.make! }
99
+ let(:project) { Project.make!(jobs: [job], default_password: "super secret") }
100
+ let(:params) do
101
+ {
102
+ email: 'test@test.com', password: '',
103
+ user_project_jobs_attributes: [ job_id: project.jobs.first.id, project_id: project.id ]
104
+ }
105
+ end
106
+
107
+ it "should set an empty password to an assigned project's default password" do
108
+ post "/api/v1/users", params
109
+ response.should be_success
110
+ json['user_project_jobs_attributes'][0]['name'].should == project.name
111
+ json['user_project_jobs_attributes'][0]['title'].should == job.title
112
+ end
113
+
114
+ it "should return a validation error if the user's assigned project has no default password" do
115
+ project.update_attributes(default_password: nil)
116
+ post "/api/v1/users", params
117
+ response.status.should == 400
118
+ json['error'].should == "Password: can't be blank"
119
+ end
120
+ end
121
+
122
+ end
123
+
124
+ context :update do
125
+ it "should upload a user avatar via the root route" do
126
+ params = { avatar_attributes: { file: Rack::Test::UploadedFile.new(Rails.root+'../fixtures/images/avatar.jpeg', 'image/jpeg', true) } }
127
+
128
+ put "/api/v1/users/#{user.id}", params
129
+
130
+ response.should be_success
131
+ user.avatar.should == Image.last
132
+ user.avatar_url.should == Image.last.file.url(:medium)
133
+ end
134
+
135
+ it "should upload a user avatar via the nested route, to test the restful api's handling of has_one associations" do
136
+ params = { file: Rack::Test::UploadedFile.new(Rails.root+'../fixtures/images/avatar.jpeg', 'image/jpeg', true) }
137
+
138
+ post "/api/v1/users/#{user.id}/avatars", params
139
+
140
+ response.should be_success
141
+ user.avatar.should == Image.last
142
+ user.avatar_url.should == Image.last.file.url(:medium)
143
+ user.avatar_url
144
+ end
145
+
146
+ it "should require a devise re-confirmation email to update a user's email address" do
147
+ new_email = 'new.email@test.com'
148
+ old_email = user.email
149
+ put "/api/v1/users/#{user.id}", { email: new_email }
150
+ response.should be_success
151
+ user.reload
152
+ user.email.should == old_email
153
+ user.unconfirmed_email.should == new_email
154
+ json['email'].should == old_email
155
+ end
156
+
157
+ it "should skip the confirmation and update a user's email address" do
158
+ new_email = 'new.email@test.com'
159
+ put "/api/v1/users/#{user.id}", { email: new_email, skip_confirmation_email: true }
160
+ response.should be_success
161
+ json['email'].should == new_email
162
+ user.reload
163
+ user.email.should == new_email
164
+ end
165
+
166
+ it "should validate the uniqueness of a user role" do
167
+ put "/api/v1/users/#{user.id}", { roles_attributes: [{ownable_type: 'Company', ownable_id: company.id}] }
168
+ response.should_not be_success
169
+ json['error'].should =~ /user has already been assigned that role/
170
+ user.admin?(company).should be_truthy
171
+ end
172
+
173
+ it "should update a user to be company admin" do
174
+ c = Company.make
175
+ c.save!
176
+ put "/api/v1/users/#{user.id}", { roles_attributes: [{ownable_type: 'Company', ownable_id: c.id}] }
177
+ response.should be_success
178
+ user.reload
179
+ user.admin?(c).should be_truthy
180
+ end
181
+
182
+ it "should destroy a user's company admin role" do
183
+ user.admin?(company).should be_truthy
184
+ put "/api/v1/users/#{user.id}", { roles_attributes: [{id: user.roles.last.id, _destroy: '1'}] }
185
+ response.should be_success
186
+ user.reload
187
+ user.admin?(company).should be_falsey
188
+ end
189
+ end
190
+
191
+ end
@@ -0,0 +1,103 @@
1
+ require 'machinist/active_record'
2
+ require 'rufus/mnemo'
3
+
4
+ def _index # prevent unique string collisions over the test cycle
5
+ @_uniq_idx ||= 0
6
+ (@_uniq_idx+=1).to_s(36)
7
+ end
8
+
9
+ def syllable(length=-1)
10
+ s = Rufus::Mnemo::from_integer(rand(8**5)+1) #+ _index
11
+ s[0..length]
12
+ end
13
+
14
+ def word(max_syl=3)
15
+ (1+rand(max_syl)).times.collect { syllable }.join
16
+ end
17
+
18
+ def words(n=3)
19
+ n.times.collect { word }.join
20
+ end
21
+
22
+ def paragraph(n=25)
23
+ words(n)
24
+ end
25
+
26
+ Company.blueprint do
27
+ name { words }
28
+ short_name { syllable(10) }
29
+ end
30
+
31
+ User.blueprint do
32
+ email { "test-"+syllable+'@springshot.com' }
33
+ first_name { word(4) }
34
+ last_name { word(5) }
35
+ password { 'abc12345' }
36
+ confirmed_at { Time.now }
37
+ end
38
+
39
+ Role.blueprint {
40
+ user_id { User.first||User.make }
41
+ ownable_id { Company.first||Company.make }
42
+ ownable_type { 'Company' }
43
+ }
44
+
45
+ Locatable.blueprint {
46
+ location { Location.make }
47
+ locatable { Company.make }
48
+ }
49
+ Location.blueprint {
50
+ name { (65+rand(8)).chr+"1"}
51
+ kind { 'gate' }
52
+ gps { LocationGps.new(lat: 37.615223, lng: -122.389977 ) }
53
+ }
54
+ LocationBeacon.blueprint {
55
+ location { Location.make }
56
+ company { Company.make }
57
+ mac_address { SecureRandom.hex(6) }
58
+ # e.g. 2F234454-CF6D-4A0F-ADF2-F4911BA9FFA6
59
+ uuid { SecureRandom.hex(4)+'-'+SecureRandom.hex(2)+'-'+SecureRandom.hex(2)+'-'+SecureRandom.hex(2)+'-'+SecureRandom.hex(6) }
60
+ major { rand(9999) }
61
+ minor { rand(9999) }
62
+ }
63
+ LocationGps.blueprint {
64
+ location { Location.make }
65
+ # place the point randomly within about a mile radius of the TEST airport (LocationHelper)
66
+ lat { 37.615223 + 0.01609*rand(0.1) * (rand(2) > 0 ? 1 : -1) }
67
+ lng { -122.389977 + 0.01609*rand(0.1)*Math.cos(37.615223*Math::PI/180) * (rand(2) > 0 ? 1 : -1) }
68
+ alt { 0 }
69
+ }
70
+
71
+ Project.blueprint {
72
+ name { words(2) }
73
+ owner { Company.make }
74
+ jobs { [Job.make, Job.make] }
75
+ admins { [User.make] }
76
+ }
77
+ Job.blueprint {
78
+ title { words(2) }
79
+ }
80
+ UserProjectJob.blueprint {
81
+ user { User.make }
82
+ project { Project.make }
83
+ job { Job.make }
84
+ }
85
+ ProjectJob.blueprint {
86
+ project { Project.make }
87
+ job { Job.make }
88
+ }
89
+
90
+
91
+ Team.blueprint {
92
+ p = Project.make
93
+ project { p }
94
+ creator { p.admins.first }
95
+ name { words(2) }
96
+ }
97
+ TeamUser.blueprint {
98
+ t = Team.make
99
+ t.project.users.push User.make(projects: [t.project])
100
+ team { t }
101
+ user { t.project.users.first }
102
+ }
103
+
@@ -0,0 +1,56 @@
1
+ module LocationHelper
2
+
3
+ def rand_coords # generate a random point somewhere around the test airport
4
+ l = LocationGps.make
5
+ [l.lat,l.lng,l.alt]
6
+ end
7
+
8
+ def create_test_airport
9
+ ### build an airport with 8 terminals each with 3 gates along each cardinal and ordinal
10
+ ### axis, for two companies each with their own set of bluetooth beacons at every gate:
11
+ ###
12
+ ### (1)A B C
13
+ ### \|/
14
+ ### (8)H-*-D e.g.->(Terminal D with gates D1, D2, and D3)
15
+ ### /|\
16
+ ### G F E
17
+
18
+ @sprocketCo = Company.find_by_name("Sprockets") || Company.make(name:'Sprockets')
19
+ @widgetCo = Company.find_by_name("Widgets") || Company.make(name:'Widgets')
20
+ if @airport = Location.find_by_name("TEST")
21
+ return @airport
22
+ end
23
+
24
+ @airport = Location.new(name:'TEST',kind:'airport')
25
+ @airport.companies.push @sprocketCo
26
+ @airport.companies.push @widgetCo
27
+ @airport.gps = LocationGps.new(lat: 37.615223, lng: -122.389977 )
28
+ (1..8).each do |terminal|
29
+ gate = (64+terminal).chr # A-H
30
+ t = Location.new(name:"Terminal #{gate}",kind:'terminal')
31
+
32
+ @airport.child_locations.push t
33
+
34
+ (1..3).each do |number|
35
+
36
+ lat, lng = [@airport.gps.lat, @airport.gps.lng]
37
+ adj = 0.003*number # push successive gates ~0.21 miles out
38
+
39
+ lat += (1..3).include?(terminal) ? adj : 0
40
+ lat -= (5..7).include?(terminal) ? adj : 0
41
+
42
+ adj *= Math.cos(37.615223*Math::PI/180)
43
+ lng += (3..5).include?( terminal) ? adj : 0
44
+ lng -= [1,7,8].include?(terminal) ? adj : 0
45
+
46
+ g = Location.new(name:"Gate #{gate}#{number}", kind:'gate')
47
+ g.gps = LocationGps.make(location: g, lat: lat, lng: lng)
48
+ g.beacons.push LocationBeacon.make(company: @sprocketCo, location: g)
49
+ g.beacons.push LocationBeacon.make(company: @widgetCo, location: g)
50
+ t.child_locations.push g
51
+ end
52
+ end
53
+ @airport.save!
54
+ @airport
55
+ end
56
+ end
@@ -0,0 +1,13 @@
1
+ RSpec::Matchers.define :permit do |action|
2
+ match do |policy|
3
+ policy.public_send("#{action}?")
4
+ end
5
+
6
+ failure_message_when_negated do |policy|
7
+ "#{policy.class} does not permit #{action} on #{policy.record} for #{policy.user.inspect}."
8
+ end
9
+
10
+ failure_message do |policy|
11
+ "#{policy.class} does not forbid #{action} on #{policy.record} for #{policy.user.inspect}."
12
+ end
13
+ end
@@ -0,0 +1,22 @@
1
+ require 'introspective_grape/camel_snake'
2
+ module RequestHelpers
3
+ include IntrospectiveGrape::CamelSnake
4
+
5
+ def json
6
+ @json ||= snake_keys(JSON.parse(response.body))
7
+ end
8
+
9
+ def with_authentication(role=:superuser)
10
+ return if @without_authentication
11
+ current_user = User.new #double('User')
12
+ allow(current_user).to receive(:admin?) { true } if role == :superuser
13
+ allow(current_user).to receive(:superuser?) { true } if role == :superuser
14
+
15
+ # Stubbing API helper methods requires this very nearly undocumented invokation
16
+ Grape::Endpoint.before_each do |endpoint|
17
+ allow(endpoint).to receive(:current_user) { current_user }
18
+ end
19
+ end
20
+ end
21
+
22
+