openstax_api 2.5.1 → 3.0.0

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +4 -6
  3. data/app/controllers/openstax/api/v1/api_controller.rb +1 -1
  4. data/app/representers/openstax/api/v1/abstract_search_representer.rb +40 -0
  5. data/lib/openstax/api/constraints.rb +3 -4
  6. data/lib/openstax/api/engine.rb +3 -8
  7. data/lib/openstax/api/representable_schema_printer.rb +58 -35
  8. data/lib/openstax/api/roar.rb +50 -93
  9. data/lib/openstax/api/version.rb +1 -1
  10. data/spec/controllers/openstax/api/v1/api_controller_spec.rb +88 -2
  11. data/spec/dummy/README.md +1 -1
  12. data/spec/dummy/Rakefile +1 -2
  13. data/spec/dummy/app/assets/javascripts/application.js +3 -5
  14. data/spec/dummy/app/assets/stylesheets/application.css +5 -3
  15. data/spec/dummy/app/models/user.rb +2 -0
  16. data/spec/dummy/app/representers/user_representer.rb +13 -0
  17. data/spec/dummy/app/representers/user_search_representer.rb +5 -0
  18. data/spec/dummy/app/routines/search_users.rb +22 -0
  19. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  20. data/spec/dummy/bin/bundle +3 -0
  21. data/spec/dummy/bin/rails +4 -0
  22. data/spec/dummy/bin/rake +4 -0
  23. data/spec/dummy/config/application.rb +0 -30
  24. data/spec/dummy/config/boot.rb +4 -9
  25. data/spec/dummy/config/database.yml +8 -8
  26. data/spec/dummy/config/environment.rb +3 -3
  27. data/spec/dummy/config/environments/development.rb +21 -13
  28. data/spec/dummy/config/environments/production.rb +41 -32
  29. data/spec/dummy/config/environments/test.rb +17 -13
  30. data/spec/dummy/config/initializers/assets.rb +8 -0
  31. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  32. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  33. data/spec/dummy/config/initializers/inflections.rb +6 -5
  34. data/spec/dummy/config/initializers/mime_types.rb +0 -1
  35. data/spec/dummy/config/initializers/openstax_api.rb +2 -2
  36. data/spec/dummy/config/initializers/session_store.rb +1 -6
  37. data/spec/dummy/config/initializers/wrap_parameters.rb +6 -6
  38. data/spec/dummy/config/locales/en.yml +20 -2
  39. data/spec/dummy/config/secrets.yml +22 -0
  40. data/spec/dummy/config.ru +1 -1
  41. data/spec/dummy/db/development.sqlite3 +0 -0
  42. data/spec/dummy/db/migrate/1_create_users.rb +16 -0
  43. data/spec/dummy/db/schema.rb +38 -32
  44. data/spec/dummy/db/test.sqlite3 +0 -0
  45. data/spec/dummy/lib/controller_includes.rb +2 -6
  46. data/spec/dummy/log/development.log +486 -1445
  47. data/spec/dummy/log/test.log +16720 -3757
  48. data/spec/dummy/public/404.html +54 -13
  49. data/spec/dummy/public/422.html +54 -13
  50. data/spec/dummy/public/500.html +53 -12
  51. data/spec/factories/user.rb +8 -0
  52. data/spec/lib/openstax/api/apipie_spec.rb +15 -0
  53. data/spec/lib/openstax/api/constraints_spec.rb +6 -6
  54. data/spec/lib/openstax/api/doorkeeper_application_includes_spec.rb +1 -1
  55. data/spec/lib/openstax/api/representable_schema_printer_spec.rb +5 -3
  56. data/spec/lib/openstax/api/roar_spec.rb +22 -0
  57. data/spec/lib/openstax/api/routing_mapper_includes_spec.rb +1 -1
  58. data/spec/lib/openstax_api_spec.rb +19 -0
  59. data/spec/models/openstax/api/api_user_spec.rb +2 -2
  60. data/spec/rails_helper.rb +54 -0
  61. data/spec/representers/openstax/api/v1/abstract_search_representer_spec.rb +127 -0
  62. data/spec/spec_helper.rb +80 -13
  63. metadata +85 -18
  64. data/spec/dummy/app/models/dummy_user.rb +0 -2
  65. data/spec/dummy/app/representers/dummy_user_representer.rb +0 -9
  66. data/spec/dummy/config/initializers/secret_token.rb +0 -7
  67. data/spec/dummy/db/migrate/1_create_dummy_users.rb +0 -10
  68. data/spec/dummy/script/rails +0 -6
  69. data/spec/lib/openstax/api/apipie.rb +0 -11
  70. data/spec/lib/openstax/api/roar.rb +0 -11
@@ -2,25 +2,66 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>The page you were looking for doesn't exist (404)</title>
5
- <style type="text/css">
6
- body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
- div.dialog {
8
- width: 25em;
9
- padding: 0 4em;
10
- margin: 4em auto 0 auto;
11
- border: 1px solid #ccc;
12
- border-right-color: #999;
13
- border-bottom-color: #999;
14
- }
15
- h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
16
54
  </style>
17
55
  </head>
18
56
 
19
57
  <body>
20
58
  <!-- This file lives in public/404.html -->
21
59
  <div class="dialog">
22
- <h1>The page you were looking for doesn't exist.</h1>
23
- <p>You may have mistyped the address or the page may have moved.</p>
60
+ <div>
61
+ <h1>The page you were looking for doesn't exist.</h1>
62
+ <p>You may have mistyped the address or the page may have moved.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
24
65
  </div>
25
66
  </body>
26
67
  </html>
@@ -2,25 +2,66 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>The change you wanted was rejected (422)</title>
5
- <style type="text/css">
6
- body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
- div.dialog {
8
- width: 25em;
9
- padding: 0 4em;
10
- margin: 4em auto 0 auto;
11
- border: 1px solid #ccc;
12
- border-right-color: #999;
13
- border-bottom-color: #999;
14
- }
15
- h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
16
54
  </style>
17
55
  </head>
18
56
 
19
57
  <body>
20
58
  <!-- This file lives in public/422.html -->
21
59
  <div class="dialog">
22
- <h1>The change you wanted was rejected.</h1>
23
- <p>Maybe you tried to change something you didn't have access to.</p>
60
+ <div>
61
+ <h1>The change you wanted was rejected.</h1>
62
+ <p>Maybe you tried to change something you didn't have access to.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
24
65
  </div>
25
66
  </body>
26
67
  </html>
@@ -2,24 +2,65 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>We're sorry, but something went wrong (500)</title>
5
- <style type="text/css">
6
- body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
- div.dialog {
8
- width: 25em;
9
- padding: 0 4em;
10
- margin: 4em auto 0 auto;
11
- border: 1px solid #ccc;
12
- border-right-color: #999;
13
- border-bottom-color: #999;
14
- }
15
- h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
16
54
  </style>
17
55
  </head>
18
56
 
19
57
  <body>
20
58
  <!-- This file lives in public/500.html -->
21
59
  <div class="dialog">
22
- <h1>We're sorry, but something went wrong.</h1>
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
23
64
  </div>
24
65
  </body>
25
66
  </html>
@@ -0,0 +1,8 @@
1
+ FactoryGirl.define do
2
+ factory :user do
3
+ username { SecureRandom.hex.to_s }
4
+ password_hash { SecureRandom.hex.to_s }
5
+ name { Faker::Name.name }
6
+ email { Faker::Internet.email }
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ require 'rails_helper'
2
+
3
+ module OpenStax
4
+ module Api
5
+ module V1
6
+ describe Apipie do
7
+ it 'adds methods to ApiController class' do
8
+ expect(OpenStax::Api::V1::ApiController).to respond_to(:api_example)
9
+ expect(OpenStax::Api::V1::ApiController).to respond_to(:json_schema)
10
+ expect(OpenStax::Api::V1::ApiController).to respond_to(:representer)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require 'rails_helper'
2
2
 
3
3
  module OpenStax
4
4
  module Api
@@ -9,14 +9,14 @@ module OpenStax
9
9
 
10
10
  it 'matches if version is correct in the accept headers' do
11
11
  allow(req).to receive(:headers).and_return({
12
- 'Accept' => 'application/vnd.dummy.openstax.v1'
12
+ 'Accept' => 'application/vnd.openstax.dummy.v1'
13
13
  })
14
14
  expect(constraints.matches? req).to eq true
15
15
  end
16
16
 
17
17
  it 'does not match if version is incorrect in the accept headers' do
18
18
  allow(req).to receive(:headers).and_return({
19
- 'Accept' => 'application/vnd.dummy.openstax.v2'
19
+ 'Accept' => 'application/vnd.openstax.dummy.v2'
20
20
  })
21
21
  expect(constraints.matches? req).to eq false
22
22
  end
@@ -43,13 +43,13 @@ module OpenStax
43
43
 
44
44
  it 'matches if version is correct in the accept headers or if default' do
45
45
  allow(req).to receive(:headers).and_return({
46
- 'Accept' => 'application/vnd.dummy.openstax.v1'
46
+ 'Accept' => 'application/vnd.openstax.dummy.v1'
47
47
  })
48
48
  expect(constraints.matches? req).to eq true
49
49
  expect(constraints_2.matches? req).to eq true
50
50
 
51
51
  allow(req).to receive(:headers).and_return({
52
- 'Accept' => 'application/vnd.dummy.openstax.v2'
52
+ 'Accept' => 'application/vnd.openstax.dummy.v2'
53
53
  })
54
54
  expect(constraints.matches? req).to eq false
55
55
  expect(constraints_2.matches? req).to eq true
@@ -57,7 +57,7 @@ module OpenStax
57
57
 
58
58
  it 'matches if version is invalid' do
59
59
  allow(req).to receive(:headers).and_return({
60
- 'Accept' => 'application/vnd.dummy.openstax.v3'
60
+ 'Accept' => 'application/vnd.openstax.dummy.v3'
61
61
  })
62
62
  expect(constraints.matches? req).to eq false
63
63
  expect(constraints_2.matches? req).to eq true
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require 'rails_helper'
2
2
 
3
3
  module OpenStax
4
4
  module Api
@@ -1,10 +1,10 @@
1
- require 'spec_helper'
1
+ require 'rails_helper'
2
2
 
3
3
  module OpenStax
4
4
  module Api
5
5
  describe RepresentableSchemaPrinter do
6
6
  it 'must print model schemas' do
7
- schema = RepresentableSchemaPrinter.json(DummyUserRepresenter)
7
+ schema = RepresentableSchemaPrinter.json(UserRepresenter)
8
8
  expect(schema).to include('## Schema')
9
9
  expect(schema).to include('{#')
10
10
  expect(schema).to include(' .schema}')
@@ -15,7 +15,9 @@ module OpenStax
15
15
  "type" => "object",
16
16
  "required" => [ "username" ],
17
17
  "properties" => {
18
- "username" => { "type" => "string" }
18
+ "username" => { "type" => "string" },
19
+ "name" => { "type" => "string" },
20
+ "email" => { "type" => "string" }
19
21
  },
20
22
  "additionalProperties" => false
21
23
  })
@@ -0,0 +1,22 @@
1
+ require 'rails_helper'
2
+
3
+ module OpenStax
4
+ module Api
5
+ module V1
6
+ describe Roar do
7
+ let!(:controller) { OpenStax::Api::V1::ApiController.new }
8
+
9
+ it 'adds methods to ApiController instance' do
10
+ expect(controller).to respond_to(:standard_create)
11
+ expect(controller).to respond_to(:standard_read)
12
+ expect(controller).to respond_to(:standard_update)
13
+ expect(controller).to respond_to(:standard_destroy)
14
+ expect(controller).to respond_to(:standard_nested_create)
15
+ expect(controller).to respond_to(:standard_index)
16
+ expect(controller).to respond_to(:standard_search)
17
+ expect(controller).to respond_to(:standard_sort)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require 'rails_helper'
2
2
 
3
3
  module OpenStax
4
4
  module Api
@@ -0,0 +1,19 @@
1
+ require 'rails_helper'
2
+
3
+ describe OpenStax::Api do
4
+ it 'is configurable' do
5
+ expect(OpenStax::Api.configuration).to respond_to(:user_class_name)
6
+ expect(OpenStax::Api.configuration).to respond_to(:current_user_method)
7
+ expect(OpenStax::Api.configuration).to respond_to(:routing_error_app)
8
+
9
+ expect(OpenStax::Api.configuration.user_class_name).to eq 'User'
10
+
11
+ OpenStax::Api.configure { |config| config.user_class_name = 'Test' }
12
+
13
+ expect(OpenStax::Api.configuration.user_class_name).to eq 'Test'
14
+
15
+ OpenStax::Api.configure { |config| config.user_class_name = 'User' }
16
+
17
+ expect(OpenStax::Api.configuration.user_class_name).to eq 'User'
18
+ end
19
+ end
@@ -1,9 +1,9 @@
1
- require 'spec_helper'
1
+ require 'rails_helper'
2
2
 
3
3
  module OpenStax
4
4
  module Api
5
5
  describe ApiUser do
6
- let(:user) { DummyUser.create }
6
+ let(:user) { FactoryGirl.create :user }
7
7
  let(:application) { double('Doorkeeper::Application') }
8
8
  let(:doorkeeper_token) { double('Doorkeeper::AccessToken') }
9
9
  let(:non_doorkeeper_user_proc) { lambda { user } }
@@ -0,0 +1,54 @@
1
+ ENV["RAILS_ENV"] ||= 'test'
2
+
3
+ require 'spec_helper'
4
+ require File.expand_path("../dummy/config/environment", __FILE__)
5
+ require 'rspec/rails'
6
+ require 'factory_girl_rails'
7
+ require 'faker'
8
+ require 'squeel'
9
+
10
+ # Add additional requires below this line. Rails is not loaded until this point!
11
+
12
+ # Requires supporting ruby files with custom matchers and macros, etc, in
13
+ # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
14
+ # run as spec files by default. This means that files in spec/support that end
15
+ # in _spec.rb will both be required and run as specs, causing the specs to be
16
+ # run twice. It is recommended that you do not name files matching this glob to
17
+ # end with _spec.rb. You can configure this pattern with the --pattern
18
+ # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
19
+ #
20
+ # The following line is provided for convenience purposes. It has the downside
21
+ # of increasing the boot-up time by auto-requiring all files in the support
22
+ # directory. Alternatively, in the individual `*_spec.rb` files, manually
23
+ # require only the support files necessary.
24
+ #
25
+ # Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
26
+
27
+ # Checks for pending migrations before tests are run.
28
+ # If you are not using ActiveRecord, you can remove this line.
29
+ ActiveRecord::Migration.maintain_test_schema!
30
+
31
+ RSpec.configure do |config|
32
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
33
+ # config.fixture_path = "#{::Rails.root}/spec/fixtures"
34
+
35
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
36
+ # examples within a transaction, remove the following line or assign false
37
+ # instead of true.
38
+ # config.use_transactional_fixtures = true
39
+
40
+ # RSpec Rails can automatically mix in different behaviours to your tests
41
+ # based on their file location, for example enabling you to call `get` and
42
+ # `post` in specs under `spec/controllers`.
43
+ #
44
+ # You can disable this behaviour by removing the line below, and instead
45
+ # explicitly tag your specs with their type, e.g.:
46
+ #
47
+ # RSpec.describe UsersController, :type => :controller do
48
+ # # ...
49
+ # end
50
+ #
51
+ # The different available types are documented in the features, such as in
52
+ # https://relishapp.com/rspec/rspec-rails/docs
53
+ config.infer_spec_type_from_file_location!
54
+ end
@@ -0,0 +1,127 @@
1
+ require 'rails_helper'
2
+
3
+ module OpenStax
4
+ module Api
5
+ module V1
6
+ describe AbstractSearchRepresenter do
7
+
8
+ before(:all) do
9
+ 100.times do
10
+ FactoryGirl.create(:user)
11
+ end
12
+
13
+ User.where{name.like "% doe%"}.delete_all
14
+
15
+ john_doe = FactoryGirl.create :user, name: "John Doe",
16
+ username: "doejohn",
17
+ email: "john@doe.com"
18
+ jane_doe = FactoryGirl.create :user, name: "Jane Doe",
19
+ username: "doejane",
20
+ email: "jane@doe.com"
21
+ jack_doe = FactoryGirl.create :user, name: "Jack Doe",
22
+ username: "doejack",
23
+ email: "jack@doe.com"
24
+
25
+ @john_hash = JSON.parse(UserRepresenter.new(john_doe).to_json)
26
+ @jane_hash = JSON.parse(UserRepresenter.new(jane_doe).to_json)
27
+ @jack_hash = JSON.parse(UserRepresenter.new(jack_doe).to_json)
28
+ end
29
+
30
+ it "represents search results" do
31
+ outputs = SearchUsers.call('last_name:dOe').outputs
32
+ response = JSON.parse(UserSearchRepresenter.new(outputs).to_json)
33
+ total_count = response['total_count']
34
+ items = response['items']
35
+
36
+ expect(total_count).to eq 3
37
+ expect(items).to include(@john_hash)
38
+ expect(items).to include(@jane_hash)
39
+ expect(items).to include(@jack_hash)
40
+
41
+ outputs = SearchUsers.call('first_name:jOhN last_name:DoE').outputs
42
+ response = JSON.parse(UserSearchRepresenter.new(outputs).to_json)
43
+ total_count = response['total_count']
44
+ items = response['items']
45
+
46
+ expect(total_count).to eq 1
47
+ expect(items).to include(@john_hash)
48
+ expect(items).not_to include(@jane_hash)
49
+ expect(items).not_to include(@jack_hash)
50
+
51
+ outputs = SearchUsers.call('first_name:JoHn,JaNe last_name:dOe').outputs
52
+ response = JSON.parse(UserSearchRepresenter.new(outputs).to_json)
53
+ total_count = response['total_count']
54
+ items = response['items']
55
+
56
+ expect(total_count).to eq 2
57
+ expect(items).to include(@john_hash)
58
+ expect(items).to include(@jane_hash)
59
+ expect(items).not_to include(@jack_hash)
60
+ end
61
+
62
+ it "represents ordered results" do
63
+ outputs = SearchUsers.call('username:DoE', order_by: 'cReAtEd_At AsC, iD')
64
+ .outputs
65
+ response = JSON.parse(UserSearchRepresenter.new(outputs).to_json)
66
+ total_count = response['total_count']
67
+ items = response['items']
68
+
69
+ expect(total_count).to eq 3
70
+ expect(items[0]).to eq @john_hash
71
+ expect(items[1]).to eq @jane_hash
72
+ expect(items[2]).to eq @jack_hash
73
+
74
+ outputs = SearchUsers.call('username:dOe', order_by: 'CrEaTeD_aT dEsC, Id DeSc')
75
+ .outputs
76
+ response = JSON.parse(UserSearchRepresenter.new(outputs).to_json)
77
+ total_count = response['total_count']
78
+ items = response['items']
79
+
80
+ expect(total_count).to eq 3
81
+ expect(items[0]).to eq @jack_hash
82
+ expect(items[1]).to eq @jane_hash
83
+ expect(items[2]).to eq @john_hash
84
+ end
85
+
86
+ it "represents paginated results" do
87
+ user_count = User.count
88
+
89
+ outputs = SearchUsers.call('').outputs
90
+ response = JSON.parse(UserSearchRepresenter.new(outputs).to_json)
91
+ total_count = response['total_count']
92
+ items = response['items']
93
+
94
+ expect(total_count).to eq user_count
95
+ expect(items.count).to eq user_count
96
+
97
+ outputs = SearchUsers.call('', per_page: 20).outputs
98
+ response = JSON.parse(UserSearchRepresenter.new(outputs).to_json)
99
+ total_count = response['total_count']
100
+ items = response['items']
101
+
102
+ expect(total_count).to eq user_count
103
+ expect(items.count).to eq 20
104
+
105
+ for page in 1..5
106
+ outputs = SearchUsers.call('', page: page, per_page: 20).outputs
107
+ response = JSON.parse(UserSearchRepresenter.new(outputs).to_json)
108
+ total_count = response['total_count']
109
+ items = response['items']
110
+
111
+ expect(total_count).to eq user_count
112
+ expect(items.count).to eq 20
113
+ end
114
+
115
+ outputs = SearchUsers.call('', page: 1000, per_page: 20).outputs
116
+ response = JSON.parse(UserSearchRepresenter.new(outputs).to_json)
117
+ total_count = response['total_count']
118
+ items = response['items']
119
+
120
+ expect(total_count).to eq user_count
121
+ expect(items.count).to eq 0
122
+ end
123
+
124
+ end
125
+ end
126
+ end
127
+ end