diff --git a/Gemfile b/Gemfile index 97df7455..45513eed 100644 --- a/Gemfile +++ b/Gemfile @@ -58,6 +58,9 @@ gem 'tabler-rubygem' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development +# Ruby linting. +gem 'rubocop', require: false + group :production do # Use a postgres database in production. gem 'pg', '~> 0.18' @@ -69,9 +72,6 @@ group :development, :test do # Environment configuration. gem 'dotenv-rails' - - # Ruby linting. - gem 'rubocop' end group :test do diff --git a/app/controllers/rooms_controller.rb b/app/controllers/rooms_controller.rb index 5319d41d..218eea7e 100644 --- a/app/controllers/rooms_controller.rb +++ b/app/controllers/rooms_controller.rb @@ -155,7 +155,7 @@ class RoomsController < ApplicationController def bring_to_room if current_user # Redirect authenticated users to their room. - redirect_to room_path(current_user.room) + redirect_to room_path(current_user.main_room) else # Redirect unauthenticated users to root. redirect_to root_path diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 585a1a88..af6f531b 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,13 +1,8 @@ # frozen_string_literal: true class SessionsController < ApplicationController - LOGIN_FAILED = "Login failed due to invalid credentials. Are you sure you typed them correctly?" - # GET /users/login - def new - end - # GET /users/logout def destroy logout @@ -40,6 +35,9 @@ class SessionsController < ApplicationController def omniauth user = User.from_omniauth(request.env['omniauth.auth']) login(user) + rescue => e + logger.error "Error authenticating via omniauth: #{e}" + redirect_to root_path end # POST /auth/failure diff --git a/app/models/room.rb b/app/models/room.rb index a83f4eed..197fd631 100644 --- a/app/models/room.rb +++ b/app/models/room.rb @@ -97,7 +97,6 @@ class Room < ApplicationRecord # Fetches all recordings for a room. def recordings res = bbb.get_recordings(meetingID: bbb_id) - # Format playbacks in a more pleasant way. res[:recordings].each do |r| next if r.key?(:error) diff --git a/spec/controllers/errors_controller_spec.rb b/spec/controllers/errors_controller_spec.rb new file mode 100644 index 00000000..a64976de --- /dev/null +++ b/spec/controllers/errors_controller_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe ErrorsController, type: :controller do + describe "GET #not_found" do + it "returns not_found" do + get :not_found + expect(response).to have_http_status(404) + end + end + + describe "GET #unprocessable" do + it "returns unprocessable" do + get :unprocessable + expect(response).to have_http_status(422) + end + end + + describe "GET #internal_error" do + it "returns internal_error" do + get :internal_error + expect(response).to have_http_status(500) + end + end +end diff --git a/spec/controllers/main_controller_spec.rb b/spec/controllers/main_controller_spec.rb new file mode 100644 index 00000000..722cf796 --- /dev/null +++ b/spec/controllers/main_controller_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe MainController, type: :controller do + describe "GET #index" do + it "returns success" do + get :index + expect(response).to be_successful + end + end +end diff --git a/spec/controllers/rooms_controller_spec.rb b/spec/controllers/rooms_controller_spec.rb new file mode 100644 index 00000000..90fc9893 --- /dev/null +++ b/spec/controllers/rooms_controller_spec.rb @@ -0,0 +1,189 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe RoomsController, type: :controller do + describe "GET #show" do + before do + @user = create(:user) + @owner = create(:user) + end + + it "should fetch recordings and room state if user is owner" do + @request.session[:user_id] = @owner.id + + get :show, params: { room_uid: @owner.main_room } + + expect(assigns(:recordings)).to eql(@owner.main_room.recordings) + expect(assigns(:is_running)).to eql(@owner.main_room.running?) + end + + it "should render join if user is not owner" do + @request.session[:user_id] = @user.id + + get :show, params: { room_uid: @owner.main_room } + + expect(response).to render_template(:join) + end + + it "should raise if room is not valid" do + expect do + get :show, params: { room_uid: "non_existent" } + end.to raise_error(ActiveRecord::RecordNotFound) + end + end + + describe "POST #create" do + before do + @owner = create(:user) + end + + it "should create room with name" do + @request.session[:user_id] = @owner.id + name = Faker::Pokemon.name + post :create, params: { room: { name: name } } + + r = @owner.secondary_rooms.last + expect(r.name).to eql(name) + expect(r.owner).to eql(@owner) + expect(response).to redirect_to(r) + end + + it "it should redirect to root if not logged in" do + expect do + name = Faker::Pokemon.name + post :create, params: { room: { name: name } } + end.to change { Room.count }.by(0) + + expect(response).to redirect_to(root_path) + end + end + + describe "POST #join" do + before do + @user = create(:user) + @owner = create(:user) + @room = @owner.main_room + + allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:get_meeting_info).and_return( + moderatorPW: "modpass", + attendeePW: "attpass", + ) + end + + it "should use account name if user is logged in and meeting running" do + allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:is_meeting_running?).and_return(true) + + @request.session[:user_id] = @user.id + post :join, params: { room_uid: @room, join_name: @user.name } + + expect(response).to redirect_to(@user.main_room.join_path(@user.name, {}, @user.uid)) + end + + it "should use join name if user is not logged in and meeting running" do + allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:is_meeting_running?).and_return(true) + + post :join, params: { room_uid: @room, join_name: "Join Name" } + + expect(response).to redirect_to(@user.main_room.join_path("Join Name", {})) + end + + it "should render wait if meeting isn't running" do + allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:is_meeting_running?).and_return(false) + + @request.session[:user_id] = @user.id + post :join, params: { room_uid: @room, join_name: @user.name } + + expect(response).to render_template(:wait) + end + + it "should join owner as moderator if meeting running" do + allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:is_meeting_running?).and_return(true) + + @request.session[:user_id] = @owner.id + post :join, params: { room_uid: @room, join_name: @owner.name } + + expect(response).to redirect_to(@user.main_room.join_path(@owner.name, { user_is_moderator: true }, @owner.uid)) + end + end + + describe "DELETE #destroy" do + before do + @user = create(:user) + @secondary_room = create(:room, owner: @user) + end + + it "should delete room and redirect to main room" do + @request.session[:user_id] = @user.id + + expect do + delete :destroy, params: { room_uid: @secondary_room } + end.to change { Room.count }.by(-1) + + expect(response).to redirect_to(@user.main_room) + end + + it "should not delete room if not owner" do + random_user = create(:user) + @request.session[:user_id] = random_user.id + + expect do + delete :destroy, params: { room_uid: @user.main_room } + end.to change { Room.count }.by(0) + end + + it "should not delete room not logged in" do + expect do + delete :destroy, params: { room_uid: @user.main_room } + end.to change { Room.count }.by(0) + end + end + + describe "POST #start" do + before do + @user = create(:user) + @other_room = create(:room) + + allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:get_meeting_info).and_return( + moderatorPW: "modpass", + attendeePW: "attpass", + ) + end + + it "should redirect to join path if owner" do + @request.session[:user_id] = @user.id + post :start, params: { room_uid: @user.main_room } + + expect(response).to redirect_to(@user.main_room.join_path(@user.name, { user_is_moderator: true }, @user.uid)) + end + + it "should bring to room if not owner" do + @request.session[:user_id] = @user.id + post :start, params: { room_uid: @other_room } + + expect(response).to redirect_to(@user.main_room) + end + + it "should bring to root if not authenticated" do + post :start, params: { room_uid: @other_room } + + expect(response).to redirect_to(root_path) + end + end + + describe "POST #home" do + before do + @user = create(:user) + @secondary_room = create(:room, owner: @user) + end + + it "should change users home room" do + @request.session[:user_id] = @user.id + post :home, params: { room_uid: @secondary_room } + @user.reload + + expect(@user.main_room).to eql(@secondary_room) + expect(response).to redirect_to(@secondary_room) + end + end +end diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb new file mode 100644 index 00000000..8713b215 --- /dev/null +++ b/spec/controllers/sessions_controller_spec.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe SessionsController, type: :controller do + before(:all) do + @user = create(:user, password: "example", password_confirmation: "example") + end + + describe "GET #destroy" do + before(:each) do + @request.session[:user_id] = @user.id + get :destroy + end + + it "should logout user" do + expect(@request.session[:user_id]).to be_nil + end + + it "should redirect to root" do + expect(response).to redirect_to(root_path) + end + end + + describe "POST #create" do + it "should login user in if credentials valid" do + post :create, params: { + session: { + email: @user.email, + password: "example", + }, + } + + expect(@request.session[:user_id]).to eql(@user.id) + end + + it "should not login user in if credentials invalid" do + post :create, params: { + session: { + email: @user.email, + password: "invalid", + }, + } + + expect(@request.session[:user_id]).to be_nil + end + end + + describe "GET/POST #omniauth" do + before(:all) do + OmniAuth.config.test_mode = true + + OmniAuth.config.mock_auth[:twitter] = OmniAuth::AuthHash.new( + provider: "twitter", + uid: "twitter-user", + info: { + email: "user@twitter.com", + name: "Twitter User", + nickname: "username", + image: "example.png", + }, + ) + + OmniAuth.config.on_failure = proc { |env| + OmniAuth::FailureEndpoint.new(env).redirect_to_failure + } + end + + it "should create and login user with omniauth" do + request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:twitter] + get :omniauth, params: { provider: :twitter } + + u = User.last + expect(u.provider).to eql("twitter") + expect(u.email).to eql("user@twitter.com") + expect(@request.session[:user_id]).to eql(u.id) + end + + it "should redirect to root on invalid omniauth login" do + request.env["omniauth.auth"] = :invalid_credentials + get :omniauth, params: { provider: :twitter } + + expect(response).to redirect_to(root_path) + end + + it "should not create session without omniauth env set" do + get :omniauth, params: { provider: 'google' } + + expect(response).to redirect_to(root_path) + end + end +end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb new file mode 100644 index 00000000..6a4b8c10 --- /dev/null +++ b/spec/controllers/users_controller_spec.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +require "rails_helper" + +def random_valid_user_params + pass = Faker::Internet.password(8) + { + user: { + name: Faker::Name.first_name, + email: Faker::Internet.email, + password: pass, + password_confirmation: pass, + }, + } +end + +describe UsersController, type: :controller do + let(:invalid_params) do + { + user: { + name: "Invalid", + email: "example.com", + password: "pass", + passwrd_confirmation: "invalid", + }, + } + end + + describe "GET #new" do + it "assigns a blank user to the view" do + get :new + expect(assigns(:user)).to be_a_new(User) + end + end + + describe "POST #create" do + it "redirects to user room on succesful create" do + params = random_valid_user_params + post :create, params: params + + u = User.find_by(name: params[:user][:name], email: params[:user][:email]) + + expect(u).to_not be_nil + expect(u.name).to eql(params[:user][:name]) + expect(response).to redirect_to(room_path(u.main_room)) + end + + it "redirects to main room if already authenticated" do + user = create(:user) + @request.session[:user_id] = user.id + + post :create, params: random_valid_user_params + expect(response).to redirect_to(room_path(user.main_room)) + end + + it "user saves with greenlight provider" do + params = random_valid_user_params + post :create, params: params + + u = User.find_by(name: params[:user][:name], email: params[:user][:email]) + + expect(u.provider).to eql("greenlight") + end + + it "renders #new on unsuccessful save" do + post :create, params: invalid_params + + expect(response).to render_template(:new) + end + end + + describe "PATCH #update" do + it "properly updates user attributes" do + user = create(:user) + + params = random_valid_user_params + patch :update, params: params.merge!(user_uid: user) + user.reload + + expect(user.name).to eql(params[:user][:name]) + expect(user.email).to eql(params[:user][:email]) + end + + it "renders #edit on unsuccessful save" do + @user = create(:user) + + patch :update, params: invalid_params.merge!(user_uid: @user) + expect(response).to render_template(:edit) + end + end +end diff --git a/spec/factories.rb b/spec/factories.rb index d99ff9d0..69a03c04 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -4,7 +4,7 @@ FactoryBot.define do factory :user do password = Faker::Internet.password(8) - provider { %w(greenlight google twitter).sample } + provider { %w(google twitter).sample } uid { rand(10**8) } name { Faker::Name.first_name } username { Faker::Internet.user_name } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index cb0b497c..af3c9aed 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -59,8 +59,8 @@ describe User, type: :model do end context '#from_omniauth' do - it "should create user from omniauth" do - auth = { + let(:auth) do + { "uid" => "123456789", "provider" => "twitter", "info" => { @@ -70,7 +70,9 @@ describe User, type: :model do "image" => "example.png", }, } + end + it "should create user from omniauth" do expect do user = User.from_omniauth(auth)