diff --git a/server/.rspec b/server/.rspec new file mode 100644 index 00000000..c99d2e73 --- /dev/null +++ b/server/.rspec @@ -0,0 +1 @@ +--require spec_helper diff --git a/server/Gemfile b/server/Gemfile index 9425d740..11f7f585 100644 --- a/server/Gemfile +++ b/server/Gemfile @@ -30,8 +30,8 @@ gem 'jbuilder', '~> 2.9' # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 4.0' # Use ActiveModel has_secure_password -# gem 'bcrypt', '~> 3.1.7' - +gem 'bcrypt', '~> 3.1.7' +gem 'jwt' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development @@ -44,6 +44,7 @@ group :development, :test do # Adds support for Capybara system testing and selenium driver gem 'capybara', '~> 3.29' gem 'selenium-webdriver' + gem 'rspec-rails', '~> 3.8' end group :development do @@ -55,5 +56,11 @@ group :development do gem 'spring-watcher-listen', '~> 2.0.0' end +group :test do + gem 'factory_bot_rails', '~> 4.0' + gem 'shoulda-matchers', '~> 3.1' + gem 'faker' + gem 'database_cleaner' +end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/server/Gemfile.lock b/server/Gemfile.lock index 95c53a9a..cac3fab2 100644 --- a/server/Gemfile.lock +++ b/server/Gemfile.lock @@ -45,6 +45,7 @@ GEM addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) arel (9.0.0) + bcrypt (3.1.13) bindex (0.5.0) bootsnap (1.4.5) msgpack (~> 1.0) @@ -69,15 +70,31 @@ GEM coffee-script-source (1.12.2) concurrent-ruby (1.1.5) crass (1.0.4) + database_cleaner (1.7.0) + diff-lcs (1.3) + equatable (0.6.0) erubi (1.8.0) execjs (2.7.0) - ffi (1.11.1) + factory_bot (4.11.1) + activesupport (>= 3.0.0) + factory_bot_rails (4.11.1) + factory_bot (~> 4.11.1) + railties (>= 3.0.0) + faker (1.9.4) + i18n (>= 0.7) + pastel (~> 0.7.2) + thor (~> 0.20.0) + tty-pager (~> 0.12.0) + tty-screen (~> 0.6.5) + tty-tree (~> 0.3.0) + ffi (1.10.0) globalid (0.4.2) activesupport (>= 4.2.0) i18n (1.6.0) concurrent-ruby (~> 1.0) jbuilder (2.9.1) activesupport (>= 4.2.0) + jwt (2.2.1) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -98,6 +115,9 @@ GEM nio4r (2.3.1) nokogiri (1.10.4) mini_portile2 (~> 2.4.0) + pastel (0.7.3) + equatable (~> 0.6) + tty-color (~> 0.5) pg (1.1.4) public_suffix (4.0.1) puma (4.2.0) @@ -133,23 +153,42 @@ GEM rb-fsevent (0.10.3) rb-inotify (0.10.0) ffi (~> 1.0) - regexp_parser (1.6.0) + regexp_parser (1.5.1) + rspec-core (3.8.1) + rspec-support (~> 3.8.0) + rspec-expectations (3.8.4) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.8.0) + rspec-mocks (3.8.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.8.0) + rspec-rails (3.8.2) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 3.8.0) + rspec-expectations (~> 3.8.0) + rspec-mocks (~> 3.8.0) + rspec-support (~> 3.8.0) + rspec-support (3.8.2) ruby_dep (1.5.0) - rubyzip (2.0.0) - sass-rails (6.0.0) - sassc-rails (~> 2.1, >= 2.1.1) - sassc (2.0.1) - ffi (~> 1.9) - rake - sassc-rails (2.1.2) - railties (>= 4.0.0) - sassc (>= 2.0) - sprockets (> 3.0) - sprockets-rails - tilt - selenium-webdriver (3.142.5) - childprocess (>= 0.5, < 3.0) - rubyzip (>= 1.2.2) + rubyzip (1.2.2) + sass (3.7.3) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sass-rails (5.0.7) + railties (>= 4.0.0, < 6) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (>= 1.1, < 3) + selenium-webdriver (3.142.3) + childprocess (>= 0.5, < 2.0) + rubyzip (~> 1.2, >= 1.2.2) + shoulda-matchers (3.1.3) + activesupport (>= 4.0.0) spring (2.1.0) spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) @@ -162,16 +201,31 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) sqlite3 (1.4.1) + strings (0.1.5) + strings-ansi (~> 0.1) + unicode-display_width (~> 1.5) + unicode_utils (~> 1.4) + strings-ansi (0.1.0) thor (0.20.3) thread_safe (0.3.6) tilt (2.0.9) - turbolinks (5.2.1) + tty-color (0.5.0) + tty-pager (0.12.1) + strings (~> 0.1.4) + tty-screen (~> 0.6) + tty-which (~> 0.4) + tty-screen (0.6.5) + tty-tree (0.3.0) + tty-which (0.4.1) + turbolinks (5.2.0) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) tzinfo (1.2.5) thread_safe (~> 0.1) uglifier (4.2.0) execjs (>= 0.3.0, < 3) + unicode-display_width (1.6.0) + unicode_utils (1.4.0) web-console (3.7.0) actionview (>= 5.0) activemodel (>= 5.0) @@ -187,17 +241,24 @@ PLATFORMS ruby DEPENDENCIES + bcrypt (~> 3.1.7) bootsnap (>= 1.1.0) byebug capybara (~> 3.29) coffee-rails (~> 5.0) + database_cleaner + factory_bot_rails (~> 4.0) + faker jbuilder (~> 2.9) + jwt listen (>= 3.0.5, < 3.2) pg puma (~> 4.2) rails (~> 5.2) - sass-rails (~> 6.0) + rspec-rails (~> 3.8) + sass-rails (~> 5.0) selenium-webdriver + shoulda-matchers (~> 3.1) spring spring-watcher-listen (~> 2.0.0) sqlite3 (~> 1.4.1) diff --git a/server/app/assets/javascripts/welcome.coffee b/server/app/assets/javascripts/welcome.coffee deleted file mode 100644 index 24f83d18..00000000 --- a/server/app/assets/javascripts/welcome.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/server/app/assets/stylesheets/welcome.scss b/server/app/assets/stylesheets/welcome.scss deleted file mode 100644 index f931ca65..00000000 --- a/server/app/assets/stylesheets/welcome.scss +++ /dev/null @@ -1,84 +0,0 @@ -//base css -* { - margin: 0; - padding: 0; - box-sizing: inherit; -} -body -{ - box-sizing: border-box; -} -html{ - font-size: 62.5%; - font-family: Arial, Helvetica, sans-serif; -} -// base css ends here - -//color variables -$color-primary: #DC143C; -$color-ligt-grey: #eeeeee; -$color-black: #0000; -$color-white: #ffffff; -$color-btn-gradient-dark: #ec008c; -$color-btn-gradient-light: #fc6767; -//color variables end here - -//welcome page css starts here -h1{ - letter-spacing: 0.1em; - font-size: 3.5rem; - margin: 5rem 0; - color: $color-primary; - text-shadow: 0 0.2rem 0.8rem $color-ligt-grey; -} - -.homeContainer{ - text-align: center; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; -} -.btn { - &, - &:link, - &:visited{ - text-transform: uppercase; - text-decoration: none; - padding: 15px 40px; - font-size: 2rem; - cursor: pointer; - text-shadow: 0 0.1rem 0.2rem $color-ligt-grey; - letter-spacing: 0.2rem; - transition: all 0.2s; - } - &:hover { - transform: translateY(-3px); - box-shadow: 0 10px 20px rgba($color-black, 0.2); - &:after { - transform: scaleX(1.4) scaleY(1.6); - opacity: 0; - } - } - &:active, - &:focus - { - outline: none; - transform: translateY(-2px); - box-shadow: 0 5px 10px rgba($color-black, 0.2); - } - &__signup{ - font-size: 2rem; - background: #ec008c; /* fallback for old browsers */ - background: -webkit-linear-gradient(to right, $color-btn-gradient-dark, $color-btn-gradient-light); - background: linear-gradient(to right, $color-btn-gradient-dark, $color-btn-gradient-light); - border-radius: 100px; - color: $color-white; - } -} - -.avatar{ - margin: 4rem 0; - max-width: 100%; - max-height: 100%; -} \ No newline at end of file diff --git a/server/app/controllers/api/v1/api_users_controller.rb b/server/app/controllers/api/v1/api_users_controller.rb new file mode 100644 index 00000000..dee4a184 --- /dev/null +++ b/server/app/controllers/api/v1/api_users_controller.rb @@ -0,0 +1,44 @@ +class Api::V1::ApiUsersController < ApplicationController + before_action :set_api_user, only: [:show, :update, :destroy] + + #GET /api_users + def index + @api_users = ApiUser.all + json_response(@api_users) + end + + # Post /api_users + def create + @api_user = ApiUser.create!(api_user_params) + json_response(@api_user, :created) + end + + # GET /api_users/:id + def show + json_response(@api_user) + end + + # PUT /api_users/:id + def update + @api_user.update(api_user_params) + head :no_content + end + + # DELETE /api_users/:id + def destroy + @api_user.destroy + head :no_content + end + + private + + def api_user_params + #whitelist params + #params.permit(:email, :password_digest) + params.require(:api_user).permit(:first_name, :last_name, :city, :email, :password_digest, :mentor, :mentee) + end + + def set_api_user + @api_user = ApiUser.find(params[:id]) + end +end diff --git a/server/app/controllers/application_controller.rb b/server/app/controllers/application_controller.rb index 1c07694e..02642bbd 100644 --- a/server/app/controllers/application_controller.rb +++ b/server/app/controllers/application_controller.rb @@ -1,3 +1,6 @@ -class ApplicationController < ActionController::Base - protect_from_forgery with: :exception +class ApplicationController < ActionController::API + #protect_from_forgery with: :exception + include Response + include ExceptionHandler end + diff --git a/server/app/controllers/concerns/exception_handler.rb b/server/app/controllers/concerns/exception_handler.rb new file mode 100644 index 00000000..639b83d1 --- /dev/null +++ b/server/app/controllers/concerns/exception_handler.rb @@ -0,0 +1,34 @@ +module ExceptionHandler + extend ActiveSupport::Concern + + # Define custom error subclasses - rescue catches `StandardErrors` + class AuthenticationError < StandardError; end + class MissingToken < StandardError; end + class InvalidToken < StandardError; end + + included do + rescue_from ActiveRecord::RecordInvalid, with: :four_two_two + rescue_from ActiveRecord::RecordNotFound, with: :four_o_four + rescue_from ActionController::ParameterMissing, with: :four_two_two + #Following handlers not required until authentication set up + #rescue_from ExceptionHandler::AuthenticationError, with: :unauthorized_request + #rescue_from ExceptionHandler::MissingToken, with: :four_twenty_two + #rescue_from ExceptionHandler::InvalidToken, with: :four_twenty_two + end + + private + + def four_o_four(e) + json_response({message: e.message}, :not_found) + end + + def four_two_two(e) + json_response({message: e.message}, :unprocessable_entity) + end + + # JSON response with message; Status code 401 - Unauthorized + def unauthorized_request(e) + json_response({ message: e.message }, :unauthorized) + end + +end diff --git a/server/app/controllers/concerns/response.rb b/server/app/controllers/concerns/response.rb new file mode 100644 index 00000000..cfb0caa4 --- /dev/null +++ b/server/app/controllers/concerns/response.rb @@ -0,0 +1,5 @@ +module Response + def json_response(object, status = :ok) + render json: object, status: status + end +end diff --git a/server/app/controllers/welcome_controller.rb b/server/app/controllers/welcome_controller.rb deleted file mode 100644 index f9b859b9..00000000 --- a/server/app/controllers/welcome_controller.rb +++ /dev/null @@ -1,4 +0,0 @@ -class WelcomeController < ApplicationController - def index - end -end diff --git a/server/app/helpers/api_users_helper.rb b/server/app/helpers/api_users_helper.rb new file mode 100644 index 00000000..2b56598f --- /dev/null +++ b/server/app/helpers/api_users_helper.rb @@ -0,0 +1,2 @@ +module ApiUsersHelper +end diff --git a/server/app/lib/message.rb b/server/app/lib/message.rb new file mode 100644 index 00000000..e47526c2 --- /dev/null +++ b/server/app/lib/message.rb @@ -0,0 +1,33 @@ +class Message + def self.not_found(record = 'record') + "Sorry, #{record} not found." + end + + def self.invalid_credentials + 'Invalid credentials' + end + + def self.invalid_token + 'Invalid token' + end + + def self.missing_token + 'Missing token' + end + + def self.unauthorized + 'Unauthorized request' + end + + def self.account_created + 'Account created successfully' + end + + def self.account_not_created + 'Account could not be created' + end + + def self.expired_token + 'Sorry, your token has expired. Please login to continue.' + end +end diff --git a/server/app/models/api_user.rb b/server/app/models/api_user.rb new file mode 100644 index 00000000..9cecadbc --- /dev/null +++ b/server/app/models/api_user.rb @@ -0,0 +1,12 @@ +class ApiUser < ApplicationRecord + # encrypt password + #has_secure_password + + + # Can write model association here for example + #h has_many :***, dependent: :destroy + + # validation + validates_presence_of :email, :password_digest +end + diff --git a/server/app/views/welcome/index.html.erb b/server/app/views/welcome/index.html.erb deleted file mode 100644 index 2f622239..00000000 --- a/server/app/views/welcome/index.html.erb +++ /dev/null @@ -1,10 +0,0 @@ -
-

Welcome to Mentor Mentee Platform

- - <%= image_tag("group.png", :alt => "group avatar", :class => "avatar") %> -
- - - - - diff --git a/server/config/routes.rb b/server/config/routes.rb index d91ad920..3bb9069b 100644 --- a/server/config/routes.rb +++ b/server/config/routes.rb @@ -1,6 +1,12 @@ Rails.application.routes.draw do get 'welcome/index' + namespace :api do + namespace :v1 do + resources :api_users + end + end root 'welcome#index' # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end + diff --git a/server/db/migrate/20190623093635_create_api_users.rb b/server/db/migrate/20190623093635_create_api_users.rb new file mode 100644 index 00000000..ae065c6d --- /dev/null +++ b/server/db/migrate/20190623093635_create_api_users.rb @@ -0,0 +1,16 @@ +class CreateApiUsers < ActiveRecord::Migration[5.2] + def change + create_table :api_users do |t| + + t.string :first_name + t.string :last_name + t.string :city + t.string :email + t.string :password_digest + + t.boolean :mentor + t.boolean :mentee + t.timestamps + end + end +end diff --git a/server/db/schema.rb b/server/db/schema.rb new file mode 100644 index 00000000..13876ee2 --- /dev/null +++ b/server/db/schema.rb @@ -0,0 +1,27 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 2019_06_23_093635) do + + create_table "api_users", force: :cascade do |t| + t.string "first_name" + t.string "last_name" + t.string "city" + t.string "email" + t.string "password_digest" + t.boolean "mentor" + t.boolean "mentee" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + +end diff --git a/server/db/seeds.rb b/server/db/seeds.rb deleted file mode 100644 index 1beea2ac..00000000 --- a/server/db/seeds.rb +++ /dev/null @@ -1,7 +0,0 @@ -# This file should contain all the record creation needed to seed the database with its default values. -# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). -# -# Examples: -# -# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) -# Character.create(name: 'Luke', movie: movies.first) diff --git a/server/spec/controllers/api_users_controller_spec.rb b/server/spec/controllers/api_users_controller_spec.rb new file mode 100644 index 00000000..de265e86 --- /dev/null +++ b/server/spec/controllers/api_users_controller_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Api::V1::ApiUsersController, type: :controller do + +end diff --git a/server/spec/factories/api_users.rb b/server/spec/factories/api_users.rb new file mode 100644 index 00000000..fe613662 --- /dev/null +++ b/server/spec/factories/api_users.rb @@ -0,0 +1,11 @@ +FactoryBot.define do + factory :api_user do + email {Faker::Internet.safe_email} + password_digest {Faker::Lorem.word} #Misc doesn't seem to work {Faker::Misc.password} + first_name {Faker::Name.first_name} + last_name {Faker::Name.last_name} + city {Faker::Address.city} + mentor {Faker::Boolean.boolean(0.5)} + mentee {Faker::Boolean.boolean(0.5)} + end +end diff --git a/server/spec/helpers/api_users_helper_spec.rb b/server/spec/helpers/api_users_helper_spec.rb new file mode 100644 index 00000000..2eb820c9 --- /dev/null +++ b/server/spec/helpers/api_users_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the ApiUsersHelper. For example: +# +# describe ApiUsersHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe ApiUsersHelper, type: :helper do + pending "Have not used this helper file function yet #{__FILE__}" +end diff --git a/server/spec/models/api_user_spec.rb b/server/spec/models/api_user_spec.rb new file mode 100644 index 00000000..09a307d9 --- /dev/null +++ b/server/spec/models/api_user_spec.rb @@ -0,0 +1,16 @@ +require 'rails_helper' + +RSpec.describe ApiUser, type: :model do + # Association test + # ensure an item record has 1:m relationship with the *** model + + # it { should have_many(:***).dependent(destroy)} + + # Validation test + # ensure columns 'email' and 'password' are present before + + it {should validate_presence_of(:email)} + it {should validate_presence_of(:password_digest)} + + pending "At some stage add more contraints and invoke more resource relationships in this file #{__FILE__}" +end diff --git a/server/spec/rails_helper.rb b/server/spec/rails_helper.rb new file mode 100644 index 00000000..230d682c --- /dev/null +++ b/server/spec/rails_helper.rb @@ -0,0 +1,86 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +require 'spec_helper' +ENV['RAILS_ENV'] ||= 'test' +require File.expand_path('../../config/environment', __FILE__) +# Prevent database truncation if the environment is production +abort("The Rails environment is running in production mode!") if Rails.env.production? +require 'rspec/rails' + +# Add additional requires below this line. Rails is not loaded until this point! +require 'database_cleaner' +Shoulda::Matchers.configure do |config| + config.integrate do |with| + with.test_framework :rspec + with.library :rails + end +end + +Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } +# Requires supporting ruby files with custom matchers and macros, etc, in +# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are +# run as spec files by default. This means that files in spec/support that end +# in _spec.rb will both be required and run as specs, causing the specs to be +# run twice. It is recommended that you do not name files matching this glob to +# end with _spec.rb. You can configure this pattern with the --pattern +# option on the command line or in ~/.rspec, .rspec or `.rspec-local`. +# +# The following line is provided for convenience purposes. It has the downside +# of increasing the boot-up time by auto-requiring all files in the support +# directory. Alternatively, in the individual `*_spec.rb` files, manually +# require only the support files necessary. +# +# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } + +# Checks for pending migrations and applies them before tests are run. +# If you are not using ActiveRecord, you can remove these lines. +begin + ActiveRecord::Migration.maintain_test_schema! +rescue ActiveRecord::PendingMigrationError => e + puts e.to_s.strip + exit 1 +end +RSpec.configure do |config| + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true + + # RSpec Rails can automatically mix in different behaviours to your tests + # based on their file location, for example enabling you to call `get` and + # `post` in specs under `spec/controllers`. + # + # You can disable this behaviour by removing the line below, and instead + # explicitly tag your specs with their type, e.g.: + # + # RSpec.describe UsersController, :type => :controller do + # # ... + # end + # + # The different available types are documented in the features, such as in + # https://relishapp.com/rspec/rspec-rails/docs + config.infer_spec_type_from_file_location! + + # Filter lines from Rails gems in backtraces. + config.filter_rails_from_backtrace! + # arbitrary gems may also be filtered via: + # config.filter_gems_from_backtrace("gem name") + + config.include RequestSpecHelper + config.include ControllerSpecHelper + # Add FactoryBot methods + config.include FactoryBot::Syntax::Methods + + config.before(:suite) do + DatabaseCleaner.clean_with(:truncation) + DatabaseCleaner.strategy = :transaction + end + + config.around(:each) do |example| + DatabaseCleaner.cleaning do + example.run + end + end +end diff --git a/server/spec/requests/api/v1/api_users_spec.rb b/server/spec/requests/api/v1/api_users_spec.rb new file mode 100644 index 00000000..28687465 --- /dev/null +++ b/server/spec/requests/api/v1/api_users_spec.rb @@ -0,0 +1,170 @@ +require 'rails_helper' + +RSpec.describe Api::V1::ApiUsersController, type: :request do +#RSpec.describe 'api_users API', type: :request do + + # initialize test data + let!(:api_users){create_list(:api_user, 10)} + let(:api_user_id) {api_users.first.id} + + # Test suite for GET /api/v1/api_users + describe 'GET /api/v1/api_users' do + before {get '/api/v1/api_users'} + + it 'returns api_users' do + expect(json).not_to be_empty + expect(json.size).to eq(10) + end + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + end + + # Test suite for GET /api/v1/api_userss/:id + describe 'GET /api/v1/api_users/:id' do + before { get "/api/v1/api_users/#{api_user_id}/" } + + context 'when api_user exists' do + it 'returns status code 200' do + expect(response).to have_http_status(200) + end + + it 'returns the item' do + expect(json['id']).to eq(api_user_id) + end + end + + context 'when api_user does not exist' do + let(:api_user_id) {0} + before { get "/api/v1/api_users/#{api_user_id}/" } + + it 'returns status code 404' do + expect(response).to have_http_status(404) + end + + it 'returns message informing no user with that id' do + expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) + end + end + + end + + # Test suite POST /api/v1/api_user + describe 'POST /api/v1/api_users' do + let(:valid_attributes) do + # send json payload + { "email": "Test_email@email.com", "password_digest": "password1"}.to_json + + context 'when request is valid' do + before { post '/api/v1/api_users', params: valid_attributes} + + it 'returns status code 201' do + expect(response).to have_http_status(201) + end + + it 'returns same params as entered' do + expect(json['email'], json['password_digest']).to eq("Test_email@email.com","password1") + end + end + end + + context 'when the request is invalid as no params' do + let(:invalid_attributes) { { api_user: { email: nil } }.to_json } + before { post '/api/v1/api_users', params: invalid_attributes } + + it 'returns status code 422' do + expect(response).to have_http_status(422) + end + + it 'returns a validation failure message' do + expect(json['message']) + .to match(/param is missing or the value is empty: api_user/) + end + end + + context 'when the request is invalid as only some requird params' do + let(:invalid_attributes) { { "api_user": { "email": "email@email.com" } }.to_json } + before { post '/api/v1/api_users', params: invalid_attributes } + + it 'returns status code 422' do + expect(response).to have_http_status(422) + end + it 'returns a validation failure message' do + expect(json['message']) + .to match(/param is missing or the value is empty: api_user/) + end + end + + end + + # Test suite for Patch /api/v1/api_userss/:id + describe 'PATCH /api/v1/api_users/:id' do + let(:valid_attributes) do + # send json payload + { "first_name": "Bobby","last_name": "Dylan", "city": "Mexico", "email": "Test_email@email.com", "password_digest": "password1", "mentor": True, "mentee": False}.to_json + let(:api_user_id) {api_users.first.id} + + context 'when request is valid' do + before { patch "/api/v1/api_users/#{api_user_id}/", params: valid_attributes} + + it 'returns status code 204' do + expect(response).to have_http_status(204) + end + end + + context 'check that parameters have updated correctly' do + before { get "/api/v1/api_users/#{api_user_id}/", params: valid_attributes} + end + + it 'returns same params as entered' do + expect(json['first_name'],json['last_name'],json['city'], json['email'], json['password_digest'],json['mentor'],json['mentee']).to eq("Bobby", "Dylan","Mexico", "Test_email@email.com","password1", True, False) + end + + end + + context 'when api_user does not exist' do + let(:api_user_id) {0} + let(:valid_attributes) do + # send json payload + { "email": "Test_email@email.com", "password_digest": "password1"}.to_json + before { patch "/api/v1/api_users/#{api_user_id}/", params: valid_attributes} + + it 'returns status code 404' do + expect(response).to have_http_status(404) + end + + it 'returns message informing no user with that id' do + expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) + end + end + end + end + + + # Test suite for Delete /api/v1/api_userss/:id + describe 'DELETE /api/v1/api_users/:id' do + + context 'when request made to delete a user' do + let(:api_user_id) {api_users.first.id} + before { delete "/api/v1/api_users/#{api_user_id}/" } + it 'returns status code 204' do + expect(response).to have_http_status(204) + end + end + + context 'when api_user does not exist' do + let(:api_user_id) {0} + before { delete "/api/v1/api_users/#{api_user_id}/" } + + it 'returns status code 404' do + expect(response).to have_http_status(404) + end + + it 'returns message informing no user with that id' do + expect(json["message"]).to match(/Couldn't find ApiUser with 'id'=#{api_user_id}/) + end + end + end + +end diff --git a/server/spec/spec_helper.rb b/server/spec/spec_helper.rb new file mode 100644 index 00000000..ce33d66d --- /dev/null +++ b/server/spec/spec_helper.rb @@ -0,0 +1,96 @@ +# This file was generated by the `rails generate rspec:install` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + config.disable_monkey_patching! + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = "doc" + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end diff --git a/server/spec/support/controller_spec_helper.rb b/server/spec/support/controller_spec_helper.rb new file mode 100644 index 00000000..9af5f7ce --- /dev/null +++ b/server/spec/support/controller_spec_helper.rb @@ -0,0 +1,27 @@ +module ControllerSpecHelper + # generate tokens from user id + def token_generator(api_user_id) + JsonWebToken.encode(api_user_id: api_user_id) + end + + # generate expired tokens from user id + def expired_token_generator(api_user_id) + JsonWebToken.encode({ api_user_id: api_user_id }, (Time.now.to_i - 10)) + end + + # return valid headers + def valid_headers + { + "Authorization" => token_generator(api_user.id), + "Content-Type" => "application/json" + } + end + + # return invalid headers + def invalid_headers + { + "Authorization" => nil, + "Content-Type" => "application/json" + } + end +end diff --git a/server/spec/support/request_spec_helper.rb b/server/spec/support/request_spec_helper.rb new file mode 100644 index 00000000..06a2d3ea --- /dev/null +++ b/server/spec/support/request_spec_helper.rb @@ -0,0 +1,6 @@ +module RequestSpecHelper + # Parse JSON reponse to ruby hash + def json + JSON.parse(response.body) + end +end