Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ logs:
build:
@docker build --build-arg RAILS_MASTER_KEY=$(cat config/master.key) -f Dockerfile -t simplesave-api:latest .
test:
@rails test
@bin/rails test
run:
@rails server
@bin/rails server
migrate:
@rails db:migrate
@bin/rails db:migrate
30 changes: 30 additions & 0 deletions app/controllers/api/v1/auth_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Api
module V1
class AuthController < ApplicationController
def register
user = User.new(user_params)

if user.save
session = Session.create!(user_id: user.id)

render json: {
token: session.token,
user: {
id: user.id,
email: user.email
}
}, status: :created
else
render json: { errors: user.errors.full_messages }, status: :unprocessable_entity
end
end

private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
rescue ActionController::ParameterMissing
{}
end
end
end
end
6 changes: 6 additions & 0 deletions app/mailers/user_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class UserMailer < ApplicationMailer
def account_activation(user)
@user = user
mail to: @user.email, subject: "Account activation"
end
end
1 change: 1 addition & 0 deletions app/models/session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Session < ApplicationRecord
validates :token, presence: true, uniqueness: true
validates :expires_at, presence: true

before_validation :generate_token, on: :create
before_validation :set_expiration, on: :create

def revoked?
Expand Down
4 changes: 3 additions & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
class User < ApplicationRecord
has_secure_password

validates :email, presence: true, uniqueness: true
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, uniqueness: true, format: { with: VALID_EMAIL_REGEX }
validates :password, presence: { on: :create }, length: { minimum: 8 }, if: -> { password.present? }
end
5 changes: 5 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
# Application
resources :application

# V1 Namespace
namespace :auth do
post :register
end

# Root
root to: "root#index"
end
Expand Down
5 changes: 5 additions & 0 deletions test/controllers/api/v1/auth_controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require "test_helper"

class Api::V1::AuthControllerTest < ActionDispatch::IntegrationTest
# TODO: Add unit tests for registration endpoint here.
end
99 changes: 99 additions & 0 deletions test/integration/api/v1/api_v1_auth_register_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
require "test_helper"

class ApiV1AuthRegisterTest < ActionDispatch::IntegrationTest
def setup
# Start from a clean state before each test, each test does its setup.
Session.delete_all
User.delete_all
end

test "register creates a user and a session" do
assert_difference [ "User.count", "Session.count" ], +1 do
post "/api/v1/auth/register", params: {
user: {
email: "alice@example.com",
password: "securepassword",
password_confirmation: "securepassword"
}
}
end

assert_response :created

body = JSON.parse(response.body)

assert body["token"].present?, "expected response to include a session token"
assert_equal "alice@example.com", body.dig("user", "email")
end

test "register with existing email returns error" do
existing_user = User.create!(
email: "alice@example.com",
password: "securepassword",
password_confirmation: "securepassword"
)

assert_no_difference [ "User.count", "Session.count" ] do
post "/api/v1/auth/register", params: {
user: {
email: existing_user.email,
password: "anotherpassword",
password_confirmation: "anotherpassword"
}
}
end

assert_response :unprocessable_entity
assert JSON.parse(response.body)["errors"].present?, "expected response to include errors"
end

test "register with mismatched password confirmation returns error" do
post "/api/v1/auth/register", params: {
user: {
email: "alice@example.com",
password: "securepassword",
password_confirmation: "differentpassword"
}
}

assert_response :unprocessable_entity
assert JSON.parse(response.body)["errors"].present?, "expected response to include errors"
end

test "register with invalid email returns error" do
post "/api/v1/auth/register", params: {
user: {
email: "invalid-email",
password: "securepassword",
password_confirmation: "securepassword"
}
}

assert_response :unprocessable_entity
assert JSON.parse(response.body)["errors"].present?, "expected response to include errors"
end

test "register with short password returns error" do
post "/api/v1/auth/register", params: {
user: {
email: "alice@example.com",
password: "short",
password_confirmation: "short"
}
}

assert_response :unprocessable_entity
assert JSON.parse(response.body)["errors"].present?, "expected response to include errors"
end

test "register with missing parameters returns error" do
post "/api/v1/auth/register", params: {}
assert_response :unprocessable_entity
assert JSON.parse(response.body)["errors"].present?, "expected response to include errors"
end

test "register with non-POST method returns method not allowed" do
get "/api/v1/auth/register"
assert_response :not_found
end
end