Created
June 25, 2019 12:53
-
-
Save fernandes/0dc8b96bbcae715d7390472eeaadb9e2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "../spec_helper.cr" | |
describe AppServer do | |
visitor = AppVisitor.new # <<<--- check this! | |
it "signs in valid user" do | |
# create a user | |
user = UserBox.new.create | |
# visit sign in endpoint | |
visitor.post("/api/v1/sign_in", ({ | |
"sign_in:email" => user.email, | |
"sign_in:password" => "pass1234", | |
})) | |
# check response has status: 200 and authorization header with "Bearer" | |
visitor.response.status_code.should eq 200 | |
visitor.response.headers["Authorization"].should_not be_nil | |
end | |
it "creates user on sign up" do | |
visitor.post("/api/v1/sign_up", ({ | |
"sign_up:name" => "New User", | |
"sign_up:email" => "[email protected]", | |
"sign_up:password" => "password", | |
"sign_up:password_confirmation" => "password", | |
})) | |
visitor.response.status_code.should eq 200 | |
visitor.response.headers["Authorization"].should_not be_nil | |
UserQuery.new.email("[email protected]").first.should_not be_nil | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "http/client" | |
class AppVisitor | |
CSRF_TOKEN = "statictesttoken" | |
getter! response | |
@response : HTTP::Client::Response? = nil | |
def visit(path : String, headers : HTTP::Headers? = nil) | |
request = HTTP::Request.new("GET", path, headers) | |
@response = process_request(request) | |
end | |
def put(path : String, body : Hash(String, String)) | |
request_with_body("PUT", path, with_csrf_token(body)) | |
end | |
def post(path : String, body : Hash(String, String)) | |
request_with_body("POST", path, with_csrf_token(body)) | |
end | |
def response_body | |
body = @response.not_nil!.body | |
if body[0, 5] == "ERROR" | |
raise Exception.new(body) | |
else | |
JSON.parse(body) | |
end | |
end | |
private def with_csrf_token(body : Hash(String, String)) | |
body[Lucky::ProtectFromForgery::PARAM_KEY] = CSRF_TOKEN | |
body | |
end | |
private def request_with_body(method : String, path : String, body : Hash(String, String)) | |
body_strings = [] of String | |
body.each do |key, value| | |
body_strings << "#{URI.escape(key)}=#{URI.escape(value)}" | |
end | |
request = HTTP::Request.new(method, path, nil, body_strings.join("&")) | |
@response = process_request(request) | |
end | |
private def process_request(request) | |
io = IO::Memory.new | |
response = HTTP::Server::Response.new(io) | |
context = HTTP::Server::Context.new(request, response) | |
# context.session[Lucky::ProtectFromForgery::SESSION_KEY] = CSRF_TOKEN | |
middlewares.call context | |
response.close | |
io.rewind | |
HTTP::Client::Response.from_io(io, decompress: false) | |
end | |
def middlewares | |
HTTP::Server.build_middleware([ | |
# Lucky::ForceSSLHandler.new, | |
Lucky::HttpMethodOverrideHandler.new, | |
Lucky::LogHandler.new, | |
# Disabled in API mode, but can be enabled if you need them: | |
# Lucky::SessionHandler.new, | |
# Lucky::FlashHandler.new, | |
Lucky::ErrorHandler.new(action: Errors::Show), | |
Lucky::RouteHandler.new, | |
# Disabled in API mode: | |
# Lucky::StaticFileHandler.new("./public", false), | |
Lucky::RouteNotFoundHandler.new, | |
]) | |
end | |
module Matchers | |
def redirect_to(path : String) | |
RedirectToExpectation.new(path) | |
end | |
end | |
struct RedirectToExpectation | |
def initialize(@expected_path : String) | |
end | |
def match(visitor : AppVisitor) | |
visitor.response.status_code == 302 && | |
visitor.response.headers.fetch("Location") == @expected_path | |
end | |
def failure_message(visitor : AppVisitor) | |
if visitor.response.headers.["Location"]? | |
"Expected to redirect to \"#{@expected_path}\", but redirected to #{visitor.response.headers.fetch("Location")}" | |
else | |
"Expected to redirect to \"#{@expected_path}\", but did not redirected at all" | |
end | |
end | |
end | |
def includes?(expected_value) | |
response.body.includes? expected_value | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "../../../../spec_helper" | |
include ContextHelper | |
private def params(merged = {} of String => String) | |
{ | |
"task" => { | |
"id" => Cuid.generate, | |
"title" => "This is my first task", | |
}.merge!(merged), | |
} | |
end | |
def execute_action(params, user : User) | |
context = post(user: user, params: params) | |
response = Api::V1::Tasks::Create.new(context, {} of String => String).call | |
end | |
describe "Api::V1::Tasks::Create" do | |
it "creates a record" do | |
post_params = params | |
user = UserBox.create | |
response = execute_action(post_params, user: user) | |
json = JSON.parse(response.body) | |
json["id"].should eq(post_params["task"]["id"]) | |
json["completed"].should eq(false) | |
end | |
it "acceptes completed value" do | |
post_params = params({"completed" => "true"}) | |
post_params["task"].delete("id") | |
user = UserBox.create | |
response = execute_action(post_params, user: user) | |
json = JSON.parse(response.body) | |
Cuid.validate(json["id"].to_s).should be_truthy | |
json["completed"].should eq(true) | |
end | |
it "creates a tasklist then reuse it" do | |
post_params = params | |
post_params["task"].delete("id") | |
user = UserBox.create | |
# Create First Task | |
response = execute_action(post_params, user: user) | |
tasklist = TasklistQuery.new.user_id(user.id).first | |
TasklistQuery.new.user_id(user.id).size.should eq(1) | |
TaskQuery.new.tasklist_id(tasklist.id).size.should eq(1) | |
# Create Second Task | |
response = execute_action(post_params, user: user) | |
tasklist = TasklistQuery.new.user_id(user.id).first | |
TasklistQuery.new.user_id(user.id).size.should eq(1) | |
TaskQuery.new.tasklist_id(tasklist.id).size.should eq(2) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module ContextHelper | |
include Auth::GenerateToken | |
# My Methods | |
private def post(user : User, params = {} of String => String) | |
headers = build_headers({"Authorization" => generate_token(user)}, content_type: :json) | |
request = build_request body: params.to_json, headers: headers | |
build_context(request: request) | |
end | |
private def post(params = {} of String => String) | |
headers = build_headers(content_type: :json) | |
request = build_request body: params.to_json, headers: headers | |
build_context(request: request) | |
end | |
private def build_headers(headers = {} of String => String, content_type = nil) | |
building_headers = HTTP::Headers.new | |
headers.each do |k, v| | |
building_headers.add(k, v) | |
end | |
building_headers.add("Content-Type", "application/json") if content_type == :json | |
building_headers | |
end | |
# Lucky Methods | |
private def build_request(method = "GET", body = "", content_type = "") | |
headers = HTTP::Headers.new | |
headers.add("Content-Type", content_type) | |
HTTP::Request.new(method, "/", body: body, headers: headers) | |
end | |
private def build_request(method = "GET", body = "", content_type = "", headers = {} of String => String) | |
HTTP::Request.new(method, "/", body: body, headers: headers) | |
end | |
private def build_context(path = "/", request = nil) : HTTP::Server::Context | |
build_context_with_io(IO::Memory.new, path: path, request: request) | |
end | |
private def build_context(user : User, method = "GET", path = "/", request = nil) : HTTP::Server::Context | |
headers = HTTP::Headers.new | |
headers.add("Content-Type", "") | |
headers.add("Authorization", generate_token(user)) | |
build_context_with_io( | |
IO::Memory.new, | |
path: "/", | |
request: build_request("GET", headers: headers), | |
) | |
end | |
private def build_context(method : String) : HTTP::Server::Context | |
build_context_with_io( | |
IO::Memory.new, | |
path: "/", | |
request: build_request(method) | |
) | |
end | |
private def build_context_with_io(io : IO, path = "/", request = nil) : HTTP::Server::Context | |
request = request || HTTP::Request.new("GET", path) | |
response = HTTP::Server::Response.new(io) | |
HTTP::Server::Context.new request, response | |
end | |
private def build_context_with_flash(flash : String) | |
build_context.tap do |context| | |
context.session.set(Lucky::FlashStore::SESSION_KEY, flash) | |
end | |
end | |
private def params | |
{} of String => String | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "jwt" | |
module Auth | |
module GenerateToken | |
def generate_token(user) | |
exp = (Time.utc + 14.days).to_unix | |
data = ({id: user.id, name: user.name, email: user.email}).to_s | |
payload = {"sub" => user.id, "user" => Base64.encode(data), "exp" => exp} | |
JWT.encode(payload, Lucky::Server.settings.secret_key_base, JWT::Algorithm::HS256) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment