Commit bd67a56d by John Doe

migrating to jbuilder for json, adding controller specs

parent 2e645090
Showing with 1328 additions and 69 deletions
...@@ -129,7 +129,7 @@ GEM ...@@ -129,7 +129,7 @@ GEM
guard (~> 2.0) guard (~> 2.0)
rubocop (~> 0.20) rubocop (~> 0.20)
hashdiff (0.3.2) hashdiff (0.3.2)
hashie (3.5.1) hashie (3.5.3)
httparty (0.14.0) httparty (0.14.0)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
i18n (0.8.0) i18n (0.8.0)
......
...@@ -3,45 +3,58 @@ ...@@ -3,45 +3,58 @@
# Controller for Database Objects # Controller for Database Objects
class DbsController < ApplicationController class DbsController < ApplicationController
before_action :authenticate_user! before_action :authenticate_user!
before_action :set_db, only: [:show, :update]
before_action :authorize_viewer, only: [:show]
before_action :authorize_owner, only: [:update]
# GET /dbs
# GET /dbs.json
def show def show
db = Db.find(params[:id]) db = Db.find(params[:id])
head(:unauthorized) && return unless current_user.views_nilm?(db.nilm)
render json: db render json: db
end end
# PATCH/PUT /dbs/1
# PATCH/PUT /dbs/1.json
def update def update
db = Db.find(params[:id]) @service = StubService.new
prev_url = db.url prev_url = @db.url
if db.update_attributes(db_params) if @db.update_attributes(db_params)
if(prev_url != db.url || params[:refresh]) if prev_url != @db.url || params[:refresh]
refresh(db) and return # refresh the database
@service = refresh
render status: @service.success? ? :ok : :unprocessable_entity
else
@service.add_notice('database updated')
render status: :ok
end end
stub = StubService.new()
stub.add_notice("database updated")
render json: {data: db,
messages: stub}
else else
render json: "adfs" @service.errors = @db.errors.full_messages
render status: :unprocessable_entity
end end
end end
private private
def refresh(db) def refresh
adapter = DbAdapter.new(db.url) adapter = DbAdapter.new(@db.url)
service = UpdateDb.new(db: db) service = UpdateDb.new(db: @db)
service.run(adapter.dbinfo, adapter.schema) return service.run(adapter.dbinfo, adapter.schema)
if(service.success?)
render json: {data: db, messages: service}
else
render json: {data: nil, messages: service},
status: :unprocessable_entity
end
end end
def db_params def db_params
params.permit(:url, :max_points_per_plot) params.permit(:url, :max_points_per_plot)
end end
def set_db
@db = Db.find(params[:id])
@nilm = @db.nilm
end
#authorization based on nilms
def authorize_owner
head :unauthorized unless current_user.owns_nilm?(@nilm)
end
def authorize_viewer
head :unauthorized unless current_user.views_nilm?(@nilm)
end
end end
...@@ -3,14 +3,52 @@ ...@@ -3,14 +3,52 @@
# controller for NILM objects # controller for NILM objects
class NilmsController < ApplicationController class NilmsController < ApplicationController
before_action :authenticate_user! before_action :authenticate_user!
before_action :set_nilm, only: [:show, :update]
before_action :authorize_viewer, only: [:show]
before_action :authorize_owner, only: [:update]
# GET /nilms
# GET /nilms.json
def index def index
nilms = Nilm.all @nilms = current_user.retrieve_nilms_by_permission
render json: nilms
end end
# GET /nilms/1
# GET /nilms/1.json
def show def show
nilm = Nilm.find(params[:id]) # renders nilms/show
render json: nilm end
# PATCH/PUT /nilms/1
# PATCH/PUT /nilms/1.json
def update
@service = StubService.new
if @nilm.update(nilm_params)
@service.add_notice('NILM Updated')
render status: :ok
else
@service.errors = @nilm.errors
render status: :unprocessable_entity
end
end end
private
# Use callbacks to share common setup or constraints between actions.
def set_nilm
@nilm = Nilm.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def nilm_params
params.permit(:name, :description,:url)
end
#authorization based on nilms
def authorize_owner
head :unauthorized unless current_user.owns_nilm?(@nilm)
end
def authorize_viewer
head :unauthorized unless current_user.views_nilm?(@nilm)
end
end end
class PermissionsController < ApplicationController
before_action :set_permission, only: [:show, :update, :destroy]
# GET /permissions
# GET /permissions.json
def index
@permissions = Permission.all
end
# GET /permissions/1
# GET /permissions/1.json
def show
end
# POST /permissions
# POST /permissions.json
def create
@permission = Permission.new(permission_params)
if @permission.save
render :show, status: :created, location: @permission
else
render json: @permission.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /permissions/1
# PATCH/PUT /permissions/1.json
def update
if @permission.update(permission_params)
render :show, status: :ok, location: @permission
else
render json: @permission.errors, status: :unprocessable_entity
end
end
# DELETE /permissions/1
# DELETE /permissions/1.json
def destroy
@permission.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_permission
@permission = Permission.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def permission_params
params.fetch(:permission, {})
end
end
class UserGroupsController < ApplicationController
before_action :set_user_group, only: [:show, :update, :destroy]
# GET /user_groups
# GET /user_groups.json
def index
@user_groups = UserGroup.all
end
# GET /user_groups/1
# GET /user_groups/1.json
def show
end
# POST /user_groups
# POST /user_groups.json
def create
@user_group = UserGroup.new(user_group_params)
if @user_group.save
render :show, status: :created, location: @user_group
else
render json: @user_group.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /user_groups/1
# PATCH/PUT /user_groups/1.json
def update
if @user_group.update(user_group_params)
render :show, status: :ok, location: @user_group
else
render json: @user_group.errors, status: :unprocessable_entity
end
end
# DELETE /user_groups/1
# DELETE /user_groups/1.json
def destroy
@user_group.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user_group
@user_group = UserGroup.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_group_params
params.fetch(:user_group, {})
end
end
...@@ -2,12 +2,17 @@ ...@@ -2,12 +2,17 @@
# Database object # Database object
class Db < ApplicationRecord class Db < ApplicationRecord
#---Associations----
belongs_to :root_folder, belongs_to :root_folder,
foreign_key: 'db_folder_id', foreign_key: 'db_folder_id',
class_name: 'DbFolder', class_name: 'DbFolder',
dependent: :destroy dependent: :destroy
belongs_to :nilm belongs_to :nilm
#---Validations
validates :max_points_per_plot, numericality: { only_integer: true }
def url def url
# return a custom URL if set # return a custom URL if set
return super unless super.nil? || super.empty? return super unless super.nil? || super.empty?
...@@ -17,9 +22,9 @@ class Db < ApplicationRecord ...@@ -17,9 +22,9 @@ class Db < ApplicationRecord
"#{nilm.url}/nilmdb" "#{nilm.url}/nilmdb"
end end
def as_json(options = {}) # def as_json(options = {})
db = super(except: [:created_at, :updated_at]) # db = super(except: [:created_at, :updated_at])
db[:contents] = root_folder.as_json({shallow: false}) # db[:contents] = root_folder.as_json({shallow: false})
db # db
end # end
end end
...@@ -2,12 +2,20 @@ ...@@ -2,12 +2,20 @@
# NILM object # NILM object
class Nilm < ApplicationRecord class Nilm < ApplicationRecord
#---Associations-----
has_one :db has_one :db
has_many :permissions, dependent: :destroy #viewer, owner, admin
has_many :users, through: :permissions
has_many :user_groups, through: :permissions
def as_json(_options = {}) #---Validations-----
nilm = super(except: [:created_at, :updated_at]) validates :name, presence: true
nilm[:available] = db.available
nilm[:db] = db.as_json() # def as_json(_options = {})
nilm # nilm = super(except: [:created_at, :updated_at])
end # nilm[:available] = db.available
# nilm[:db] = db.as_json()
# nilm
# end
end end
class Permission < ApplicationRecord
#---Associations----
belongs_to :user
belongs_to :user_group
belongs_to :nilm
end
class User < ActiveRecord::Base class User < ActiveRecord::Base
# Include default devise modules. #---Attributes------
devise :database_authenticatable, :registerable, devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :recoverable, :rememberable, :trackable, :validatable,
:confirmable, :omniauthable :confirmable, :omniauthable
include DeviseTokenAuth::Concerns::User include DeviseTokenAuth::Concerns::User
#---Associations----
has_many :permissions
has_many :nilms, through: :permissions
has_and_belongs_to_many :user_groups
#---Validations-----
validates :first_name, :last_name, :email, :presence => true
validates :email, :uniqueness => true
validates :password, :confirmation => true
# ----------------------------------------
# :section: Permission Checkers
# ----------------------------------------
# Returns a dict of nilms this user can access
# nilms are grouped by role (+admin+,+owner+,+viewer+). Nilms are unique
# and assigned to the highest permission bin. If a user has direct permissions as +owner+ on
# an nilm and also +viewer+ permissions through a group, the nilm will be in
# the +owner+ array only.
#
# ==== Example
# nilms = {
# :admin => [x, y, z],
# :owner => [m, n],
# :viewer => []
# }
#
def retrieve_nilms_by_permission
#return Nilm's categorized by permission level (viewer, admin, owner)
nilms = {}
roles=[:admin,:owner,:viewer]
#only account for each nilm once- use the highest permission level
allowed_nilms = []
roles.each do |role|
#first get NILM's explicitly related to this user
user_nilms = self.nilms.where(permissions:{role: role})
allowed_nilms+= user_nilms.pluck(:id)
#add NILM's related through a user_group
User.joins(:user_groups, :permissions, :nilms)
user_groups = self.user_groups
group_nilms = Nilm.joins(permissions: :user_group).where(permissions:{role: role}).
where(user_groups: {id: user_groups.pluck(:id)})
if(not allowed_nilms.empty?)
group_nilms = group_nilms.where("nilms.id NOT IN (?)", allowed_nilms)
end
allowed_nilms += group_nilms.pluck(:id)
nilms[role] = user_nilms + group_nilms
end
return nilms
end
#returns true if the user has +admin+ privileges either directly or through a group
def admins_nilm?(nilm)
return self.has_permission("admin",nilm)
end
#returns true if the user has <em>at least</em> +owner+ privileges either directly or through a group
def owns_nilm?(nilm)
return self.has_permission(["admin","owner"],nilm)
end
#returns true if the user has <em> at least</em> +viewer+ privileges either directly or through a group
def views_nilm?(nilm)
return self.has_permission(["admin","owner","viewer"],nilm)
end
protected
# :category: Permission Checkers
#
# checks if the user has either direct or group permissions of one or more specified +roles+
# on a specified +nilm+
#
# ===attributes
# * +roles+: array of roles [+admin+,+owner+,+viewer+]
# * +nilm+: nilm object to check
#
def has_permission(roles,nilm)
#check if user has [roles] on the nilm directly
answer = self.nilms.where(permissions:{role: roles}, id: nilm.id).exists?
if(answer == false) #if not, check if he has [roles] through a group
answer = Nilm.joins(permissions: :user_group).
where(permissions:{role: roles}).
where(user_groups: {id: self.user_groups.pluck(:id)}).
where(nilms: {id: nilm.id}).exists?
end
return answer
end
end end
# Logical groups of users for bulk permission assignment. The group has a single
# +owner+ who alone can add and remove members. Permissions inherited through a group
# assignment are merged with individually assigned permissions with the highest permission
# level taking precedence. That is you can't be demoted by being assigned to a group.
#
# ===Accessible Attributes
# * +attr+ - descrip
#
# ===Protected Attributes
# * +name+ - friendly name
# * +description+ - description of the group
# * +owner+ - User object
#
class UserGroup < ApplicationRecord
#---Associations----
has_and_belongs_to_many :users
belongs_to :owner, class_name: "User"
has_many :permissions
has_many :nilms, through: :permissions
#---Validations-----
validates :name, :presence => true, :uniqueness => true
validates :description, :presence => true
validates :owner_id, :presence => true
#---------------
#:section: Utility Methods
#---------------
# Returns a json model of the UserGroup.
# ===attributes
# * +options+: hash, pass <tt>{}</tt> for no options, or <tt>{:include_members=>true}</tt>
#
# ===examples
#
# #Just the group
# user_group.as_json({}) =
# {"name" => "Lab Group",
# "description" => "Users working on NILM in the lab",
# "id" => 3}
#
# #Include the members
# user_group.as_json({:include_members=>true}) =
# {"name" => "Lab Group",
# "description" => "Users working on NILM in the lab",
# "id" => 3}
# :members => [
# {"first_name" => "John",
# "last_name" => "Ledner",
# "id" => 3,
# :confirmed => true,
# }, ... ]
# }
def as_json(options)
group = super(only: [:name, :description, :id])
if(options[:include_members])
group[:members] = self.users.as_json(:abbreviated=>true)
end
return group
end
end
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
# #
module ServiceStatus module ServiceStatus
attr_reader :errors, :warnings, :notices attr_reader :errors, :warnings, :notices
attr_writer :errors #errors can be set in bulk
FAIL_ON_ERROR = 0 FAIL_ON_ERROR = 0
FAIL_ON_WARNING = 1 FAIL_ON_WARNING = 1
NEVER_FAIL = 2 NEVER_FAIL = 2
......
json.extract! db_folder, :id, :name, :description, :path, :hidden,
:start_time, :end_time, :size_on_disk
json.shallow shallow
unless(shallow)
json.subfolders do
json.array! db_folder.subfolders, partial: 'db_folders/db_folder',
as: :db_folder, shallow: true
end
json.streams do
json.array! db_folder.db_streams, partial: 'db_streams/db_stream',
as: :db_stream
end
end
json.partial! "db_folders/db_folder", db_folder: @db_folder, shallow: false
json.data do
json.partial! "db_folders/db_folder", db_folder: @db_folder, shallow: false
end
json.partial! "helpers/messages", service: @service
json.extract! db_element, :id, :name, :units, :column, :default_max,
:default_min, :scale_factor, :offset, :plottable,
:discrete
json.extract! db_stream, :id, :name, :description, :path, :start_time,
:end_time, :size_on_disk, :total_rows, :total_time,
:data_type, :name_abbrev, :delete_locked, :hidden
json.elements json.array! db_stream.elements,
partial: 'db_streams/db_element',
as: :db_element
json.partial! "db_folders/db_folder", db_folder: @db_folder, shallow: false
json.data do
json.partial! "db_folders/db_folder", db_folder: @db_folder, shallow: false
end
json.partial! "helpers/messages", service: @service
json.extract! db, :id, :url, :size_total, :size_db,
:size_other, :version, :max_points_per_plot,
:available
json.contents do
json.partial! "db_folders/db_folder", db_folder: @db.root_folder,
shallow: false
end
json.partial! "dbs/db", nilm: @db
json.data do
json.partial! "dbs/db", db: @db
end
json.partial! "helpers/messages", service: @service
# expects service to be a service object
json.messages do
json.notices service.notices
json.warnings service.warnings
json.errors service.errors
end
json.extract! nilm, :id, :name, :description, :url
json.available nilm.db.available
json.admin do
json.array! @nilms[:admin], partial: 'nilms/nilm', as: :nilm
end
json.owner do
json.array! @nilms[:owner], partial: 'nilms/nilm', as: :nilm
end
json.viewer do
json.array! @nilms[:viewer], partial: 'nilms/nilm', as: :nilm
end
json.partial! "nilms/nilm", nilm: @nilm
json.data do
json.partial! "nilms/nilm", nilm: @nilm
end
json.partial! "helpers/messages", service: @service
json.extract! permission, :id, :created_at, :updated_at
json.url permission_url(permission, format: :json)
\ No newline at end of file
json.array! @permissions, partial: 'permissions/permission', as: :permission
\ No newline at end of file
json.partial! "permissions/permission", permission: @permission
\ No newline at end of file
json.extract! user_group, :id, :created_at, :updated_at
json.url user_group_url(user_group, format: :json)
\ No newline at end of file
json.array! @user_groups, partial: 'user_groups/user_group', as: :user_group
\ No newline at end of file
json.partial! "user_groups/user_group", user_group: @user_group
\ No newline at end of file
#!/usr/bin/env ruby #!/usr/bin/env ruby
begin
load File.expand_path('../spring', __FILE__)
rescue LoadError => e
raise unless e.message.include?('spring')
end
# frozen_string_literal: true # frozen_string_literal: true
# #
# This file was generated by Bundler. # This file was generated by Bundler.
......
#!/usr/bin/env ruby #!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'spring' is installed as part of a gem, and
# this file is here to facilitate running it.
#
# This file loads spring without using Bundler, in order to be fast. require "pathname"
# It gets overwritten when you run the `spring binstub` command. ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
unless defined?(Spring) require "rubygems"
require 'rubygems' require "bundler/setup"
require 'bundler'
if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)) load Gem.bin_path("spring", "spring")
Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) }
gem 'spring', match[1]
require 'spring/binstub'
end
end
Rails.application.routes.draw do Rails.application.routes.draw do
resources :user_groups
resources :permissions
mount_devise_token_auth_for 'User', at: 'auth' mount_devise_token_auth_for 'User', at: 'auth'
resources :db_decimations resources :db_decimations
resources :nilms resources :nilms
......
class AddPermissions < ActiveRecord::Migration[5.0]
def change
create_table "permissions", :force => true do |t|
t.integer "installation_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "user_id"
t.integer "user_group_id"
t.string "role"
end
end
end
class CreatePermissions < ActiveRecord::Migration[5.0]
def change
create_table :permissions do |t|
t.integer "nilm_id"
t.integer "user_id"
t.string "role"
t.timestamps
end
end
end
class CreateUserGroups < ActiveRecord::Migration[5.0]
def change
create_table :user_groups do |t|
t.string "name"
t.string "description"
t.integer "owner_id"
t.timestamps
end
end
end
class FixPermissionsTable < ActiveRecord::Migration[5.0]
def change
rename_column :permissions, :installation_id, :nilm_id
end
end
class AddUserGroupsJoinTable < ActiveRecord::Migration[5.0]
def change
create_table :user_groups_users, id: false do |t|
t.integer :user_group_id
t.integer :user_id
end
add_index :user_groups_users, :user_group_id
add_index :user_groups_users, :user_id
end
end
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170204010933) do ActiveRecord::Schema.define(version: 20170214031515) do
create_table "db_decimations", force: :cascade do |t| create_table "db_decimations", force: :cascade do |t|
t.integer "start_time", limit: 8 t.integer "start_time", limit: 8
...@@ -94,6 +94,30 @@ ActiveRecord::Schema.define(version: 20170204010933) do ...@@ -94,6 +94,30 @@ ActiveRecord::Schema.define(version: 20170204010933) do
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
end end
create_table "permissions", force: :cascade do |t|
t.integer "nilm_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.integer "user_group_id"
t.string "role"
end
create_table "user_groups", force: :cascade do |t|
t.string "name"
t.string "description"
t.integer "owner_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "user_groups_users", id: false, force: :cascade do |t|
t.integer "user_group_id"
t.integer "user_id"
t.index ["user_group_id"], name: "index_user_groups_users_on_user_group_id"
t.index ["user_id"], name: "index_user_groups_users_on_user_id"
end
create_table "users", force: :cascade do |t| create_table "users", force: :cascade do |t|
t.string "provider", default: "email", null: false t.string "provider", default: "email", null: false
t.string "uid", default: "", null: false t.string "uid", default: "", null: false
......
...@@ -2,13 +2,106 @@ ...@@ -2,13 +2,106 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe DbsController, type: :controller do RSpec.describe DbsController, type: :request do
let(:john) { create(:user, first_name: 'John') }
let(:steve) { create(:user, first_name: 'Steve') }
let(:john_nilm) { create(:nilm, name: "John's NILM", admins: [john]) }
let(:lab_nilm) { create(:nilm, name: 'Lab NILM', owners: [john]) }
let(:pete_nilm) { create(:nilm, name: "Pete's NILM", viewers: [john])}
let(:hidden_nilm) { create(:nilm, name: "Private NILM", owners: [steve])}
# index action does not exist
describe 'GET show' do describe 'GET show' do
it 'lists the database contents' do before do
allow(Db).to receive(:find).and_return(Db.new) john.confirm
get :show, params: { id: 1} steve.confirm
expect(Db).to have_received(:find) end
expect(response.header['Content-Type']).to include('application/json') context 'with any permissions' do
it 'returns the db as json' do
# john has some permission on all 3 nilms
@auth_headers = john.create_new_auth_token
[pete_nilm.db, lab_nilm.db, john_nilm.db].each do |db|
get "/dbs/#{db.id}.json",
headers: @auth_headers
expect(response.status).to eq(200)
expect(response.header['Content-Type']).to include('application/json')
body = JSON.parse(response.body)
expect(body['url']).to eq(db.url)
end
end
end
context 'without permissions' do
it 'returns unauthorized' do
# steve does NOT have permissions on john_nilm
@auth_headers = steve.create_new_auth_token
get "/dbs/#{john_nilm.db.id}.json",
headers: @auth_headers
expect(response.status).to eq(401)
end
end
context 'without sign-in' do
it 'returns unauthorized' do
# no headers: nobody is signed in, deny all
get "/dbs/#{john_nilm.db.id}.json"
expect(response.status).to eq(401)
end
end
end
describe 'PUT update' do
before do
john.confirm
steve.confirm
end
context 'with owner permissions' do
it 'refreshes db if URL is changed or refresh paramter is set' do
end
it 'returns 422 on invalid parameters' do
# max points must be a positive number
@auth_headers = john.create_new_auth_token
put "/dbs/#{john_nilm.db.id}.json",
params: {max_points_per_plot: 'invalid'},
headers: @auth_headers
expect(response.status).to eq(422)
expect(response).to have_error_message(/not a number/)
end
it 'only allows configurable parameters to be changed' do
# should ignore size and accept max_points
@auth_headers = john.create_new_auth_token
size = john_nilm.db.size_db
num_points = pete_nilm.db.max_points_per_plot
put "/dbs/#{john_nilm.db.id}.json",
params: {max_points_per_plot: num_points+10, size_db: size+10},
headers: @auth_headers
expect(response.status).to eq(200)
expect(response).to have_notice_message()
expect(john_nilm.db.reload.size_db).to eq(size)
expect(john_nilm.db.max_points_per_plot).to eq(num_points+10)
end
end
context 'without admin permissions' do
it 'returns unauthorized' do
@auth_headers = john.create_new_auth_token
num_points = pete_nilm.db.max_points_per_plot
put "/dbs/#{pete_nilm.db.id}.json",
params: {max_points_per_plot: num_points+10},
headers: @auth_headers
expect(response).to have_http_status(:unauthorized)
expect(pete_nilm.db.max_points_per_plot).to eq(num_points)
end
end
context 'without sign-in' do
it 'returns unauthorized' do
num_points = pete_nilm.db.max_points_per_plot
put "/dbs/#{pete_nilm.db.id}.json",
params: {max_points_per_plot: num_points+10}
expect(response).to have_http_status(:unauthorized)
expect(pete_nilm.db.max_points_per_plot).to eq(num_points)
end
end end
end end
end end
...@@ -2,5 +2,121 @@ ...@@ -2,5 +2,121 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe NilmsController, type: :controller do RSpec.describe NilmsController, type: :request do
let(:john) { create(:user, first_name: 'John') }
let(:steve) { create(:user, first_name: 'Steve') }
let(:john_nilm) { create(:nilm, name: "John's NILM", admins: [john]) }
let(:lab_nilm) { create(:nilm, name: 'Lab NILM', owners: [john]) }
let(:pete_nilm) { create(:nilm, name: "Pete's NILM", viewers: [john])}
let(:hidden_nilm) { create(:nilm, name: "Private NILM", owners: [steve])}
describe 'GET index' do
before do
john.confirm
steve.confirm
end
context 'with authenticated user' do
it 'returns authorized nilms' do
# force lazy evaluation of let to build NILMs
john_nilm; pete_nilm; lab_nilm; hidden_nilm
@auth_headers = john.create_new_auth_token
get "/nilms.json", headers: @auth_headers
expect(response.header['Content-Type']).to include('application/json')
body = JSON.parse(response.body)
expect(body["admin"][0]["id"]).to eq(john_nilm.id)
expect(body["owner"][0]["id"]).to eq(lab_nilm.id)
expect(body["viewer"][0]["id"]).to eq(pete_nilm.id)
end
end
context 'without sign-in' do
it 'returns unauthorized' do
get "/nilms.json"
expect(response.status).to eq(401)
end
end
end
describe 'PUT update' do
before do
john.confirm
steve.confirm
end
context 'with owner permissions' do
it 'updates parameters' do
@auth_headers = john.create_new_auth_token
[john_nilm, lab_nilm].each do |nilm|
put "/nilms/#{nilm.id}.json",
params: {id: nilm.id, name: "changed:#{nilm.id}"},
headers: @auth_headers
expect(response.status).to eq(200)
expect(response).to have_notice_message
expect(nilm.reload.name).to eq("changed:#{nilm.id}")
end
end
it 'returns 422 on invalid parameters' do
@auth_headers = john.create_new_auth_token
put "/nilms/#{john_nilm.id}.json",
params: {id: john_nilm.id, name: ""},
headers: @auth_headers
expect(response).to have_http_status(:unprocessable_entity)
expect(john_nilm.reload.name).to eq("John's NILM")
end
end
context 'without admin permissions' do
it 'returns unauthorized' do
@auth_headers = john.create_new_auth_token
put "/nilms/#{pete_nilm.id}.json",
params: {id: pete_nilm.id, name: "test"},
headers: @auth_headers
expect(response).to have_http_status(:unauthorized)
expect(pete_nilm.reload.name).to eq("Pete's NILM")
end
end
context 'without sign-in' do
it 'returns unauthorized' do
put "/nilms/#{pete_nilm.id}.json",
params: {id: pete_nilm.id, name: "test"}
expect(response).to have_http_status(:unauthorized)
expect(pete_nilm.reload.name).to eq("Pete's NILM")
end
end
end
describe 'GET show' do
before do
john.confirm
steve.confirm
end
context 'with any permissions' do
it 'returns the nilm as json' do
# john has some permission on all 3 nilms
@auth_headers = john.create_new_auth_token
[pete_nilm, lab_nilm, john_nilm].each do |nilm|
get "/nilms/#{nilm.id}.json",
headers: @auth_headers
expect(response.status).to eq(200)
expect(response.header['Content-Type']).to include('application/json')
body = JSON.parse(response.body)
expect(body['name']).to eq(nilm.name)
end
end
end
context 'without permissions' do
it 'returns unauthorized' do
# steve does NOT have permissions on john_nilm
@auth_headers = steve.create_new_auth_token
get "/nilms/#{john_nilm.id}.json",
headers: @auth_headers
expect(response.status).to eq(401)
end
end
context 'without sign-in' do
it 'returns unauthorized' do
# no headers: nobody is signed in, deny all
get "/nilms/#{john_nilm.id}.json"
expect(response.status).to eq(401)
end
end
end
end end
require 'rails_helper'
# This spec was generated by rspec-rails when you ran the scaffold generator.
# It demonstrates how one might use RSpec to specify the controller code that
# was generated by Rails when you ran the scaffold generator.
#
# It assumes that the implementation code is generated by the rails scaffold
# generator. If you are using any extension libraries to generate different
# controller code, this generated spec may or may not pass.
#
# It only uses APIs available in rails and/or rspec-rails. There are a number
# of tools you can use to make these specs even more expressive, but we're
# sticking to rails and rspec-rails APIs to keep things simple and stable.
#
# Compared to earlier versions of this generator, there is very limited use of
# stubs and message expectations in this spec. Stubs are only used when there
# is no simpler way to get a handle on the object needed for the example.
# Message expectations are only used when there is no simpler way to specify
# that an instance is receiving a specific message.
RSpec.describe PermissionsController, type: :controller, broken: true do
# This should return the minimal set of attributes required to create a valid
# Permission. As you add validations to Permission, be sure to
# adjust the attributes here as well.
let(:valid_attributes) {
skip("Add a hash of attributes valid for your model")
}
let(:invalid_attributes) {
skip("Add a hash of attributes invalid for your model")
}
# This should return the minimal set of values that should be in the session
# in order to pass any filters (e.g. authentication) defined in
# PermissionsController. Be sure to keep this updated too.
let(:valid_session) { {} }
describe "GET #index" do
it "assigns all permissions as @permissions" do
permission = Permission.create! valid_attributes
get :index, params: {}, session: valid_session
expect(assigns(:permissions)).to eq([permission])
end
end
describe "GET #show" do
it "assigns the requested permission as @permission" do
permission = Permission.create! valid_attributes
get :show, params: {id: permission.to_param}, session: valid_session
expect(assigns(:permission)).to eq(permission)
end
end
describe "GET #new" do
it "assigns a new permission as @permission" do
get :new, params: {}, session: valid_session
expect(assigns(:permission)).to be_a_new(Permission)
end
end
describe "GET #edit" do
it "assigns the requested permission as @permission" do
permission = Permission.create! valid_attributes
get :edit, params: {id: permission.to_param}, session: valid_session
expect(assigns(:permission)).to eq(permission)
end
end
describe "POST #create" do
context "with valid params" do
it "creates a new Permission" do
expect {
post :create, params: {permission: valid_attributes}, session: valid_session
}.to change(Permission, :count).by(1)
end
it "assigns a newly created permission as @permission" do
post :create, params: {permission: valid_attributes}, session: valid_session
expect(assigns(:permission)).to be_a(Permission)
expect(assigns(:permission)).to be_persisted
end
it "redirects to the created permission" do
post :create, params: {permission: valid_attributes}, session: valid_session
expect(response).to redirect_to(Permission.last)
end
end
context "with invalid params" do
it "assigns a newly created but unsaved permission as @permission" do
post :create, params: {permission: invalid_attributes}, session: valid_session
expect(assigns(:permission)).to be_a_new(Permission)
end
it "re-renders the 'new' template" do
post :create, params: {permission: invalid_attributes}, session: valid_session
expect(response).to render_template("new")
end
end
end
describe "PUT #update" do
context "with valid params" do
let(:new_attributes) {
skip("Add a hash of attributes valid for your model")
}
it "updates the requested permission" do
permission = Permission.create! valid_attributes
put :update, params: {id: permission.to_param, permission: new_attributes}, session: valid_session
permission.reload
skip("Add assertions for updated state")
end
it "assigns the requested permission as @permission" do
permission = Permission.create! valid_attributes
put :update, params: {id: permission.to_param, permission: valid_attributes}, session: valid_session
expect(assigns(:permission)).to eq(permission)
end
it "redirects to the permission" do
permission = Permission.create! valid_attributes
put :update, params: {id: permission.to_param, permission: valid_attributes}, session: valid_session
expect(response).to redirect_to(permission)
end
end
context "with invalid params" do
it "assigns the permission as @permission" do
permission = Permission.create! valid_attributes
put :update, params: {id: permission.to_param, permission: invalid_attributes}, session: valid_session
expect(assigns(:permission)).to eq(permission)
end
it "re-renders the 'edit' template" do
permission = Permission.create! valid_attributes
put :update, params: {id: permission.to_param, permission: invalid_attributes}, session: valid_session
expect(response).to render_template("edit")
end
end
end
describe "DELETE #destroy" do
it "destroys the requested permission" do
permission = Permission.create! valid_attributes
expect {
delete :destroy, params: {id: permission.to_param}, session: valid_session
}.to change(Permission, :count).by(-1)
end
it "redirects to the permissions list" do
permission = Permission.create! valid_attributes
delete :destroy, params: {id: permission.to_param}, session: valid_session
expect(response).to redirect_to(permissions_url)
end
end
end
require 'rails_helper'
# This spec was generated by rspec-rails when you ran the scaffold generator.
# It demonstrates how one might use RSpec to specify the controller code that
# was generated by Rails when you ran the scaffold generator.
#
# It assumes that the implementation code is generated by the rails scaffold
# generator. If you are using any extension libraries to generate different
# controller code, this generated spec may or may not pass.
#
# It only uses APIs available in rails and/or rspec-rails. There are a number
# of tools you can use to make these specs even more expressive, but we're
# sticking to rails and rspec-rails APIs to keep things simple and stable.
#
# Compared to earlier versions of this generator, there is very limited use of
# stubs and message expectations in this spec. Stubs are only used when there
# is no simpler way to get a handle on the object needed for the example.
# Message expectations are only used when there is no simpler way to specify
# that an instance is receiving a specific message.
RSpec.describe UserGroupsController, type: :controller, broken: true do
# This should return the minimal set of attributes required to create a valid
# UserGroup. As you add validations to UserGroup, be sure to
# adjust the attributes here as well.
let(:valid_attributes) {
skip("Add a hash of attributes valid for your model")
}
let(:invalid_attributes) {
skip("Add a hash of attributes invalid for your model")
}
# This should return the minimal set of values that should be in the session
# in order to pass any filters (e.g. authentication) defined in
# UserGroupsController. Be sure to keep this updated too.
let(:valid_session) { {} }
describe "GET #index" do
it "assigns all user_groups as @user_groups" do
user_group = UserGroup.create! valid_attributes
get :index, params: {}, session: valid_session
expect(assigns(:user_groups)).to eq([user_group])
end
end
describe "GET #show" do
it "assigns the requested user_group as @user_group" do
user_group = UserGroup.create! valid_attributes
get :show, params: {id: user_group.to_param}, session: valid_session
expect(assigns(:user_group)).to eq(user_group)
end
end
describe "GET #new" do
it "assigns a new user_group as @user_group" do
get :new, params: {}, session: valid_session
expect(assigns(:user_group)).to be_a_new(UserGroup)
end
end
describe "GET #edit" do
it "assigns the requested user_group as @user_group" do
user_group = UserGroup.create! valid_attributes
get :edit, params: {id: user_group.to_param}, session: valid_session
expect(assigns(:user_group)).to eq(user_group)
end
end
describe "POST #create" do
context "with valid params" do
it "creates a new UserGroup" do
expect {
post :create, params: {user_group: valid_attributes}, session: valid_session
}.to change(UserGroup, :count).by(1)
end
it "assigns a newly created user_group as @user_group" do
post :create, params: {user_group: valid_attributes}, session: valid_session
expect(assigns(:user_group)).to be_a(UserGroup)
expect(assigns(:user_group)).to be_persisted
end
it "redirects to the created user_group" do
post :create, params: {user_group: valid_attributes}, session: valid_session
expect(response).to redirect_to(UserGroup.last)
end
end
context "with invalid params" do
it "assigns a newly created but unsaved user_group as @user_group" do
post :create, params: {user_group: invalid_attributes}, session: valid_session
expect(assigns(:user_group)).to be_a_new(UserGroup)
end
it "re-renders the 'new' template" do
post :create, params: {user_group: invalid_attributes}, session: valid_session
expect(response).to render_template("new")
end
end
end
describe "PUT #update" do
context "with valid params" do
let(:new_attributes) {
skip("Add a hash of attributes valid for your model")
}
it "updates the requested user_group" do
user_group = UserGroup.create! valid_attributes
put :update, params: {id: user_group.to_param, user_group: new_attributes}, session: valid_session
user_group.reload
skip("Add assertions for updated state")
end
it "assigns the requested user_group as @user_group" do
user_group = UserGroup.create! valid_attributes
put :update, params: {id: user_group.to_param, user_group: valid_attributes}, session: valid_session
expect(assigns(:user_group)).to eq(user_group)
end
it "redirects to the user_group" do
user_group = UserGroup.create! valid_attributes
put :update, params: {id: user_group.to_param, user_group: valid_attributes}, session: valid_session
expect(response).to redirect_to(user_group)
end
end
context "with invalid params" do
it "assigns the user_group as @user_group" do
user_group = UserGroup.create! valid_attributes
put :update, params: {id: user_group.to_param, user_group: invalid_attributes}, session: valid_session
expect(assigns(:user_group)).to eq(user_group)
end
it "re-renders the 'edit' template" do
user_group = UserGroup.create! valid_attributes
put :update, params: {id: user_group.to_param, user_group: invalid_attributes}, session: valid_session
expect(response).to render_template("edit")
end
end
end
describe "DELETE #destroy" do
it "destroys the requested user_group" do
user_group = UserGroup.create! valid_attributes
expect {
delete :destroy, params: {id: user_group.to_param}, session: valid_session
}.to change(UserGroup, :count).by(-1)
end
it "redirects to the user_groups list" do
user_group = UserGroup.create! valid_attributes
delete :destroy, params: {id: user_group.to_param}, session: valid_session
expect(response).to redirect_to(user_groups_url)
end
end
end
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# generic DbFolder # generic DbFolder
FactoryGirl.define do FactoryGirl.define do
factory :db_folder do factory :db_folder, aliases: [:root_folder] do
name { Faker::Lorem.word } name { Faker::Lorem.word }
end end
end end
...@@ -2,5 +2,12 @@ ...@@ -2,5 +2,12 @@
FactoryGirl.define do FactoryGirl.define do
factory :db do factory :db do
url ""
max_points_per_plot { Faker::Number.number(3) }
size_db { Faker::Number.number(5) }
size_other { Faker::Number.number(5) }
size_total { size_db + size_other }
available true
root_folder
end end
end end
# frozen_string_literal: true # frozen_string_literal: true
FactoryGirl.define do FactoryGirl.define do
factory :nilm do factory :nilm do
db
name "test_nilm"
description { Faker::Lorem.sentence }
transient do
admins []
owners []
viewers []
end
after(:create) do |nilm, evaluator|
evaluator.admins.each do |admin|
if admin.instance_of? User
create(:permission, user: admin, user_group: nil, nilm: nilm, role: "admin")
else
create(:permission, user_group: admin, user: nil, nilm: nilm, role: "admin")
end
end
evaluator.owners.each do |owner|
if owner.instance_of? User
create(:permission, user: owner, user_group: nil, nilm: nilm, role: "owner")
else
create(:permission, user_group: owner, user: nil, nilm: nilm, role: "owner")
end
end
evaluator.viewers.each do |viewer|
if viewer.instance_of? User
create(:permission, user: viewer, user_group: nil, nilm: nilm, role: "viewer")
else
create(:permission, user_group: viewer, user: nil, nilm: nilm, role: "viewer")
end
end
end
end end
end end
FactoryGirl.define do
factory :permission do
user
nilm
end
end
FactoryGirl.define do
factory :user_group do
transient do
members []
end
sequence :name do |n| "group#{n}" end
description { Faker::Lorem.sentence }
owner { users.first }
users { members.empty? ? [User.first] : members }
end
end
FactoryGirl.define do
factory :user do
first_name {Faker::Name.first_name}
last_name {Faker::Name.first_name}
email {Faker::Internet.unique.email}
password {Faker::Lorem.characters(10)}
end
end
require 'rails_helper'
RSpec.describe Permission, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
require 'rails_helper'
RSpec.describe UserGroup, type: :model do
describe "UserGroup Behavior" do
context "Given a group with four members" do
before(:each) do
#users
@john = create(:user, first_name: "John")
@nicky = create(:user, first_name: "Nicky")
@pete = create(:user, first_name: "Pete")
@leeb = create(:user, first_name: "Leeb")
#groups
@group = create(:user_group, name: "Group", owner: @john,
members: [@john, @nicky, @pete, @leeb])
end
it "has an owner and four members" do
expect(@group.users.count).to eq(4)
expect(@group.owner).to eq(@john)
expect(@group.users).to eq([@john,@nicky,@pete,@leeb])
end
end
end
end
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe User, type: :model do
describe 'user' do
let(:user) { User.new }
specify { expect(user).to respond_to(:first_name) }
specify { expect(user).to respond_to(:last_name) }
specify { expect(user).to respond_to(:email) }
end
describe "permission management" do
context "Given the Donnal's House and the Lab" do
before(:each) do
#users
@john = create(:user, first_name: "John")
@nicky = create(:user, first_name: "Nicky")
@pete = create(:user, first_name: "Pete")
@leeb = create(:user, first_name: "Leeb")
#groups
@donnals = create(:user_group, name: "Donnals", members: [@john, @nicky])
@labmates = create(:user_group, name: "Labmates", members: [@john, @pete, @leeb])
@public = create(:user_group, members:[@john, @nicky, @pete, @leeb])
#nilms
@donnal_house = create(:nilm, name: "Donnal House",
admins: [@john], owners: [@donnals])
@lab = create(:nilm, name: "LEES Lab", admins: [@john, @leeb],
owners: [@labmates], viewers: [@public])
end
it "lets John admin his house and the lab" do
expect(@john.admins_nilm?(@donnal_house)).to eq(true)
expect(@john.owns_nilm?(@donnal_house)).to eq(true)
expect(@john.views_nilm?(@donnal_house)).to eq(true)
expect(@john.admins_nilm?(@lab)).to eq(true)
expect(@john.owns_nilm?(@lab)).to eq(true)
expect(@john.views_nilm?(@lab)).to eq(true)
end
it "lets Nicky own her house and view the lab" do
expect(@nicky.admins_nilm?(@donnal_house)).to eq(false)
expect(@nicky.owns_nilm?(@donnal_house)).to eq(true)
expect(@nicky.views_nilm?(@donnal_house)).to eq(true)
expect(@nicky.admins_nilm?(@lab)).to eq(false)
expect(@nicky.owns_nilm?(@lab)).to eq(false)
expect(@nicky.views_nilm?(@lab)).to eq(true)
end
it "lets Pete own the lab but hides the Donnal's house" do
expect(@pete.admins_nilm?(@donnal_house)).to eq(false)
expect(@pete.owns_nilm?(@donnal_house)).to eq(false)
expect(@pete.views_nilm?(@donnal_house)).to eq(false)
expect(@pete.admins_nilm?(@lab)).to eq(false)
expect(@pete.owns_nilm?(@lab)).to eq(true)
expect(@pete.views_nilm?(@lab)).to eq(true)
end
it "lets Leeb admin the lab but hides the Donnal's house" do
expect(@leeb.admins_nilm?(@donnal_house)).to eq(false)
expect(@leeb.owns_nilm?(@donnal_house)).to eq(false)
expect(@leeb.views_nilm?(@donnal_house)).to eq(false)
expect(@leeb.admins_nilm?(@lab)).to eq(true)
expect(@leeb.owns_nilm?(@lab)).to eq(true)
expect(@leeb.views_nilm?(@lab)).to eq(true)
end
end
end
end
# frozen_string_literal: true
# This file is copied to spec/ when you run 'rails generate rspec:install' # This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test' ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__) require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production # Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production? abort('The Rails environment is running in production mode!') if Rails.env.production?
require 'spec_helper' require 'spec_helper'
require 'rspec/rails' require 'rspec/rails'
require 'support/factory_girl'
require 'support/api_messages'
# Add additional requires below this line. Rails is not loaded until this point! # Add additional requires below this line. Rails is not loaded until this point!
# Requires supporting ruby files with custom matchers and macros, etc, in # Requires supporting ruby files with custom matchers and macros, etc, in
...@@ -27,6 +31,9 @@ require 'rspec/rails' ...@@ -27,6 +31,9 @@ require 'rspec/rails'
ActiveRecord::Migration.maintain_test_schema! ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config| RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :view
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures" config.fixture_path = "#{::Rails.root}/spec/fixtures"
...@@ -61,4 +68,3 @@ VCR.configure do |c| ...@@ -61,4 +68,3 @@ VCR.configure do |c|
c.hook_into :webmock c.hook_into :webmock
c.configure_rspec_metadata! c.configure_rspec_metadata!
end end
require 'rails_helper'
RSpec.describe "Permissions", type: :request do
describe "GET /permissions" do
it "works! (now write some real specs)" do
get permissions_path
expect(response).to have_http_status(200)
end
end
end
require 'rails_helper'
RSpec.describe "UserGroups", type: :request do
describe "GET /user_groups" do
it "works! (now write some real specs)" do
get user_groups_path
expect(response).to have_http_status(200)
end
end
end
...@@ -23,6 +23,9 @@ SimpleCov.start ...@@ -23,6 +23,9 @@ SimpleCov.start
require 'webmock/rspec' require 'webmock/rspec'
RSpec.configure do |config| RSpec.configure do |config|
config.filter_run_excluding :broken => true
# rspec-expectations config goes here. You can use an alternate # rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest # assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer. # assertions if you prefer.
......
RSpec::Matchers.define :have_error_message do |regex|
match do |response|
body = JSON.parse(response.body)
# omit regex to test if there are any error messages
if(regex == nil)
return false if body["messages"]["errors"].empty?
return true
end
# specify regex to match a particular error message
body["messages"]["errors"].each do |error|
return true if(regex.match(error))
end
return false
end
failure_message do |str|
body = JSON.parse(response.body)
"Expected #{regex} to match in [ " +
body["messages"]["errors"].join(", ")+" ]"
end
failure_message_when_negated do |str|
body = JSON.parse(response.body)
"Expected #{regex} to not match in:\n" +
body["messages"]["errors"].join(", ")
end
end
RSpec::Matchers.define :have_notice_message do |regex|
match do |response|
body = JSON.parse(response.body)
# omit regex to test if there are any notice messages
if(regex == nil)
return false if body["messages"]["notices"].empty?
return true
end
# specify regex to match a particular notice message
body["messages"]["notices"].each do |notice|
return true if(regex.match(notice))
end
return false
end
failure_message do |str|
body = JSON.parse(response.body)
"Expected #{regex} to match in [ " +
body["messages"]["notices"].join(", ")+" ]"
end
failure_message_when_negated do |str|
body = JSON.parse(response.body)
"Expected #{regex} to not match in:\n" +
body["messages"]["notices"].join(", ")
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment