test: integration link spec

- spec_helper (spec-kemal setup)
- spec_helper (micreate migration)
- create_test_link + get_test_link + delete_test_link helpers
- fix test env variables
This commit is contained in:
Juan Rodriguez
2024-05-13 22:57:13 +02:00
parent 6b21bc0cd6
commit a8e2e971d6
6 changed files with 300 additions and 17 deletions
+3
View File
@@ -0,0 +1,3 @@
PORT=4001
DATABASE_URL=sqlite3://./sqlite/data.test.db?journal_mode=wal&synchronous=normal&foreign_keys=true
APP_URL=http://localhost:4001
+16 -4
View File
@@ -10,11 +10,11 @@ shards:
db:
git: https://github.com/crystal-lang/crystal-db.git
version: 0.13.1
version: 0.11.0
dotenv:
git: https://github.com/gdotdesign/cr-dotenv.git
version: 0.6.0
version: 1.0.0
exception_page:
git: https://github.com/crystal-loot/exception_page.git
@@ -24,15 +24,27 @@ shards:
git: https://github.com/kemalcr/kemal.git
version: 1.5.0
micrate:
git: https://github.com/amberframework/micrate.git
version: 0.15.1
mysql:
git: https://github.com/crystal-lang/crystal-mysql.git
version: 0.14.0
pg:
git: https://github.com/will/crystal-pg.git
version: 0.26.0
radix:
git: https://github.com/luislavena/radix.git
version: 0.4.1
spec-kemal:
git: https://github.com/kemalcr/spec-kemal.git
version: 0.5.0
version: 1.0.0
sqlite3:
git: https://github.com/crystal-lang/crystal-sqlite3.git
version: 0.21.0
version: 0.19.0
+7 -2
View File
@@ -5,8 +5,10 @@ authors:
- Juan Rodriguez <sjdonado@icloud.com>
targets:
pa:
main: src/url-shortener.cr
url-shortener:
main: url-shortener.cr
cli:
main: scripts/cli.cr
dependencies:
kemal:
@@ -21,6 +23,9 @@ development_dependencies:
github: gdotdesign/cr-dotenv
spec-kemal:
github: kemalcr/spec-kemal
micrate:
github: amberframework/micrate
version: 0.15.1
crystal: '>= 1.12.1'
+226
View File
@@ -0,0 +1,226 @@
require "../spec_helper"
require "../../app/models/*"
API_KEY = Random::Secure.urlsafe_base64()
describe App::Controllers::Link do
describe "Create" do
it "should create link" do
test_user = create_test_user()
payload = {"url" => "https://kagi.com"}
post(
"/api/links",
headers: HTTP::Headers{"Content-Type" => "application/json", "X-Api-Key" => test_user.api_key.to_s},
body: payload.to_json
)
parsed_response = Hash(String, Hash(String, String | Int64)).from_json(response.body)
parsed_response["data"]["origin"].should eq(payload["url"])
end
it "should return 400 - url required field" do
test_user = create_test_user()
payload = {"test" => "https://kagi.com"}
post(
"/api/links",
headers: HTTP::Headers{"Content-Type" => "application/json", "X-Api-Key" => test_user.api_key.to_s},
body: payload.to_json
)
expected = {"error" => "url: Required field"}.to_json
response.body.should eq(expected)
end
it "should return 400 - invalid url" do
test_user = create_test_user()
payload = {"url" => "test" }
post(
"/api/links",
headers: HTTP::Headers{"Content-Type" => "application/json", "X-Api-Key" => test_user.api_key.to_s},
body: payload.to_json
)
expected = {"errors" => {"url" => ["is invalid"]}}.to_json
response.body.should eq(expected)
end
it "should return 401 - missing api key" do
payload = {"url" => "https://kagi.com"}
post("/api/links", headers: HTTP::Headers{"Content-Type" => "application/json"}, body: payload.to_json)
expected = {"error" => "Unauthorized"}.to_json
response.status_code.should eq(401)
response.body.should eq(expected)
end
end
describe "Index" do
it "should redirect to origin domain" do
link = "https://kagi.com"
test_user = create_test_user()
test_link = create_test_link(test_user, link)
serialized_link = App::Serializers::Link.new(test_link)
get(serialized_link.refer, headers: HTTP::Headers{"X-Api-Key" => test_user.api_key.to_s})
response.headers["Location"].should eq(link)
end
it "should increase click counter after redirect" do
link = "https://kagi.com"
test_user = create_test_user()
test_link = create_test_link(test_user, link)
serialized_link = App::Serializers::Link.new(test_link)
get(serialized_link.refer, headers: HTTP::Headers{"X-Api-Key" => test_user.api_key.to_s})
updated_test_link = get_test_link(test_link.id)
response.headers["Location"].should eq(link)
updated_test_link.click_counter.should eq(1)
end
it "should return 404 - link does not exist" do
link = "https://kagi.com"
test_user = create_test_user()
test_link = create_test_link(test_user, link)
serialized_link = App::Serializers::Link.new(test_link)
delete_test_link(test_link.id)
get(serialized_link.refer, headers: HTTP::Headers{"X-Api-Key" => test_user.api_key.to_s})
expected = {"error" => "Not Found"}.to_json
response.status_code.should eq(404)
response.body.should eq(expected)
end
end
describe "All" do
it "should return all links" do
links = ["https://google.com", "google.com", "google.com.co"]
test_user = create_test_user()
links.each do |link|
create_test_link(test_user, link)
end
get("/api/links", headers: HTTP::Headers{"X-Api-Key" => test_user.api_key.to_s})
parsed_response = Hash(String, Array(Hash(String, String | Int64))).from_json(response.body)
parsed_response["data"][0]["origin"].should eq(links[0])
parsed_response["data"][1]["origin"].should eq(links[1])
parsed_response["data"][2]["origin"].should eq(links[2])
end
it "should return owned links only" do
links = ["https://google.com", "google.com", "google.com.co", "kagi.com"]
test_user = create_test_user()
links[0..2].each do |link|
create_test_link(test_user, link)
end
test_other_user = create_test_user()
create_test_link(test_other_user, links[3])
get("/api/links", headers: HTTP::Headers{"X-Api-Key" => test_user.api_key.to_s})
parsed_response = Hash(String, Array(Hash(String, String | Int64))).from_json(response.body)
parsed_response["data"].size.should eq(3)
parsed_response["data"][0]["origin"].should eq(links[0])
parsed_response["data"][1]["origin"].should eq(links[1])
parsed_response["data"][2]["origin"].should eq(links[2])
end
it "should return 401 - missing api key" do
get "/api/links"
expected = {"error" => "Unauthorized"}.to_json
response.status_code.should eq(401)
response.body.should eq(expected)
end
end
describe "Update" do
it "should update link url" do
link = "https://kagi.com"
test_user = create_test_user()
test_link = create_test_link(test_user, link)
payload = {"url" => "https://kagi.com.co"}
put(
"/api/links/#{test_link.id}",
headers: HTTP::Headers{"Content-Type" => "application/json", "X-Api-Key" => test_user.api_key.to_s},
body: payload.to_json
)
parsed_response = Hash(String, Hash(String, String | Int64)).from_json(response.body)
parsed_response["data"]["origin"].should eq(payload["url"])
end
it "should return 404 - link does not exist" do
test_user = create_test_user()
payload = {"url" => "https://kagi.com.co"}
put(
"/api/links/1",
headers: HTTP::Headers{"Content-Type" => "application/json", "X-Api-Key" => test_user.api_key.to_s},
body: payload.to_json
)
expected = {"error" => "Not Found"}.to_json
response.status_code.should eq(404)
response.body.should eq(expected)
end
it "should return 401 - missing api key" do
payload = {"url" => "https://kagi.com.co"}
put(
"/api/links/1",
headers: HTTP::Headers{"Content-Type" => "application/json"},
body: payload.to_json
)
expected = {"error" => "Unauthorized"}.to_json
response.status_code.should eq(401)
response.body.should eq(expected)
end
end
describe "Delete" do
it "should delete link url" do
link = "https://kagi.com"
test_user = create_test_user()
test_link = create_test_link(test_user, link)
delete("/api/links/#{test_link.id}", headers: HTTP::Headers{"X-Api-Key" => test_user.api_key.to_s})
response.status_code.should eq(204)
end
it "should return 404 - link does not exist" do
test_user = create_test_user()
delete("/api/links/1", headers: HTTP::Headers{"X-Api-Key" => test_user.api_key.to_s})
expected = {"error" => "Not Found"}.to_json
response.status_code.should eq(404)
response.body.should eq(expected)
end
it "should return 401 - missing api key" do
delete "/api/links/1"
expected = {"error" => "Unauthorized"}.to_json
response.status_code.should eq(401)
response.body.should eq(expected)
end
end
end
-9
View File
@@ -1,9 +0,0 @@
require "./spec_helper"
describe Url::Shortener do
# TODO: Write tests
it "works" do
false.should eq(true)
end
end
+48 -2
View File
@@ -1,2 +1,48 @@
require "spec"
require "../src/pa"
require "uuid"
require "spec-kemal"
require "micrate"
require "../url-shortener"
Spec.before_suite do
Micrate::DB.connection_url = ENV["DATABASE_URL"]
Micrate::Cli.run_up
end
def create_test_user
user = App::Models::User.new
user.id = UUID.v4.to_s
user.name = "Tester"
user.api_key = Random::Secure.urlsafe_base64()
changeset = App::Lib::Database.insert(user)
if !changeset.valid?
raise "Test user creation failed"
end
user
end
def create_test_link(user, url)
link = App::Models::Link.new
link.id = UUID.v4.to_s
link.url = url
link.slug = Random::Secure.urlsafe_base64(4)
link.user = user
changeset = App::Lib::Database.insert(link)
if !changeset.valid?
raise "Test link creation failed"
end
link
end
def get_test_link(link_id)
App::Lib::Database.get!(App::Models::Link, link_id)
end
def delete_test_link(link_id)
App::Lib::Database.raw_exec("DELETE FROM links WHERE id = (?)", link_id) # tempfix: Database.delete does not work
end