# encoding: utf-8 require File.join(File.dirname(__FILE__), "../spec_helper.rb") describe Her::Model::Paths do context "building request paths" do context "simple model" do before do spawn_model "Foo::User" end describe "#request_path" do it "builds paths with defaults" do expect(Foo::User.new(:id => "foo").request_path).to eq("users/foo") expect(Foo::User.new(:id => nil).request_path).to eq("users") expect(Foo::User.new().request_path).to eq("users") end it "builds paths with custom collection path" do Foo::User.collection_path "/utilisateurs" expect(Foo::User.new(:id => "foo").request_path).to eq("/utilisateurs/foo") expect(Foo::User.new().request_path).to eq("/utilisateurs") end it "builds paths with custom relative collection path" do Foo::User.collection_path "utilisateurs" expect(Foo::User.new(:id => "foo").request_path).to eq("utilisateurs/foo") expect(Foo::User.new().request_path).to eq("utilisateurs") end it "builds paths with custom collection path with multiple variables" do Foo::User.collection_path "/organizations/:organization_id/utilisateurs" expect(Foo::User.new(:id => "foo").request_path(:_organization_id => "acme")).to eq("/organizations/acme/utilisateurs/foo") expect(Foo::User.new().request_path(:_organization_id => "acme")).to eq("/organizations/acme/utilisateurs") expect(Foo::User.new(:id => "foo", :organization_id => "acme").request_path).to eq("/organizations/acme/utilisateurs/foo") expect(Foo::User.new(:organization_id => "acme").request_path).to eq("/organizations/acme/utilisateurs") end it "builds paths with custom relative collection path with multiple variables" do Foo::User.collection_path "organizations/:organization_id/utilisateurs" expect(Foo::User.new(:id => "foo").request_path(:_organization_id => "acme")).to eq("organizations/acme/utilisateurs/foo") expect(Foo::User.new().request_path(:_organization_id => "acme")).to eq("organizations/acme/utilisateurs") expect(Foo::User.new(:id => "foo", :organization_id => "acme").request_path).to eq("organizations/acme/utilisateurs/foo") expect(Foo::User.new(:organization_id => "acme").request_path).to eq("organizations/acme/utilisateurs") end it "builds paths with custom item path" do Foo::User.resource_path "/utilisateurs/:id" expect(Foo::User.new(:id => "foo").request_path).to eq("/utilisateurs/foo") expect(Foo::User.new().request_path).to eq("users") end it "builds paths with custom relative item path" do Foo::User.resource_path "utilisateurs/:id" expect(Foo::User.new(:id => "foo").request_path).to eq("utilisateurs/foo") expect(Foo::User.new().request_path).to eq("users") end it "raises exceptions when building a path without required custom variables" do Foo::User.collection_path "/organizations/:organization_id/utilisateurs" expect { Foo::User.new(:id => "foo").request_path }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `/organizations/:organization_id/utilisateurs/:id`. Parameters are `{:id=>\"foo\"}`.") end it "escapes the variable values" do Foo::User.collection_path "organizations/:organization_id/utilisateurs" expect(Foo::User.new(:id => "Привет").request_path(:_organization_id => 'лол')).to eq("organizations/%D0%BB%D0%BE%D0%BB/utilisateurs/%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82") expect(Foo::User.new(:organization_id => 'лол', :id => "Привет").request_path).to eq("organizations/%D0%BB%D0%BE%D0%BB/utilisateurs/%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82") end end end context "simple model with multiple words" do before do spawn_model "Foo::AdminUser" end describe "#request_path" do it "builds paths with defaults" do expect(Foo::AdminUser.new(:id => "foo").request_path).to eq("admin_users/foo") expect(Foo::AdminUser.new().request_path).to eq("admin_users") end it "builds paths with custom collection path" do Foo::AdminUser.collection_path "/users" expect(Foo::AdminUser.new(:id => "foo").request_path).to eq("/users/foo") expect(Foo::AdminUser.new().request_path).to eq("/users") end it "builds paths with custom relative collection path" do Foo::AdminUser.collection_path "users" expect(Foo::AdminUser.new(:id => "foo").request_path).to eq("users/foo") expect(Foo::AdminUser.new().request_path).to eq("users") end it "builds paths with custom collection path with multiple variables" do Foo::AdminUser.collection_path "/organizations/:organization_id/users" expect(Foo::AdminUser.new(:id => "foo").request_path(:_organization_id => "acme")).to eq("/organizations/acme/users/foo") expect(Foo::AdminUser.new().request_path(:_organization_id => "acme")).to eq("/organizations/acme/users") end it "builds paths with custom relative collection path with multiple variables" do Foo::AdminUser.collection_path "organizations/:organization_id/users" expect(Foo::AdminUser.new(:id => "foo").request_path(:_organization_id => "acme")).to eq("organizations/acme/users/foo") expect(Foo::AdminUser.new().request_path(:_organization_id => "acme")).to eq("organizations/acme/users") end it "builds paths with custom item path" do Foo::AdminUser.resource_path "/users/:id" expect(Foo::AdminUser.new(:id => "foo").request_path).to eq("/users/foo") expect(Foo::AdminUser.new().request_path).to eq("admin_users") end it "builds paths with custom relative item path" do Foo::AdminUser.resource_path "users/:id" expect(Foo::AdminUser.new(:id => "foo").request_path).to eq("users/foo") expect(Foo::AdminUser.new().request_path).to eq("admin_users") end it "raises exceptions when building a path without required custom variables" do Foo::AdminUser.collection_path "/organizations/:organization_id/users" expect { Foo::AdminUser.new(:id => "foo").request_path }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `/organizations/:organization_id/users/:id`. Parameters are `{:id=>\"foo\"}`.") end it "raises exceptions when building a relative path without required custom variables" do Foo::AdminUser.collection_path "organizations/:organization_id/users" expect { Foo::AdminUser.new(:id => "foo").request_path }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `organizations/:organization_id/users/:id`. Parameters are `{:id=>\"foo\"}`.") end end end context "children model" do before do Her::API.setup :url => "https://api.example.com" do |builder| builder.use Her::Middleware::FirstLevelParseJSON builder.use Faraday::Request::UrlEncoded builder.adapter :test do |stub| stub.get("/users/foo") { |env| [200, {}, { :id => 'foo' }.to_json] } end end spawn_model("Foo::Model") { include_root_in_json true } class User < Foo::Model; end @spawned_models << :User end it "builds path using the children model name" do expect(User.find('foo').id).to eq('foo') expect(User.find('foo').id).to eq('foo') end end context "nested model" do before do spawn_model "Foo::User" end describe "#request_path" do it "builds paths with defaults" do expect(Foo::User.new(:id => "foo").request_path).to eq("users/foo") expect(Foo::User.new.request_path).to eq("users") end end end context 'custom primary key' do before do spawn_model 'User' do primary_key 'UserId' resource_path 'users/:UserId' end spawn_model 'Customer' do primary_key :customer_id resource_path 'customers/:id' end end describe '#request_path' do it 'uses the correct primary key attribute' do expect(User.new(:UserId => 'foo').request_path).to eq('users/foo') expect(User.new(:id => 'foo').request_path).to eq('users') end it 'replaces :id with the appropriate primary key' do expect(Customer.new(:customer_id => 'joe').request_path).to eq('customers/joe') expect(Customer.new(:id => 'joe').request_path).to eq('customers') end end end end context "making subdomain HTTP requests" do before do Her::API.setup :url => "https://api.example.com/" do |builder| builder.use Her::Middleware::FirstLevelParseJSON builder.use Faraday::Request::UrlEncoded builder.adapter :test do |stub| stub.get("organizations/2/users") { |env| [200, {}, [{ :id => 1, :fullname => "Tobias Fünke", :organization_id => 2 }, { :id => 2, :fullname => "Lindsay Fünke", :organization_id => 2 }].to_json] } stub.post("organizations/2/users") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :organization_id => 2 }.to_json] } stub.put("organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke", :organization_id => 2 }.to_json] } stub.get("organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :organization_id => 2, :active => true }.to_json] } stub.delete("organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke", :organization_id => 2, :active => false }.to_json] } end end spawn_model "Foo::User" do collection_path "organizations/:organization_id/users" end end describe "fetching a resource" do it "maps a single resource to a Ruby object" do @user = Foo::User.find(1, :_organization_id => 2) expect(@user.id).to eq(1) expect(@user.fullname).to eq("Tobias Fünke") end it "maps a single resource using a scope to a Ruby object" do Foo::User.scope :for_organization, lambda { |o| where(:organization_id => o) } @user = Foo::User.for_organization(2).find(1) expect(@user.id).to eq(1) expect(@user.fullname).to eq("Tobias Fünke") end end describe "fetching a collection" do it "maps a collection of resources to an array of Ruby objects" do @users = Foo::User.where(:_organization_id => 2).all expect(@users.length).to eq(2) expect(@users.first.fullname).to eq("Tobias Fünke") end end describe "handling new resource" do it "handles new resource" do @new_user = Foo::User.new(:fullname => "Tobias Fünke", :organization_id => 2) expect(@new_user.new?).to be_truthy @existing_user = Foo::User.find(1, :_organization_id => 2) expect(@existing_user.new?).to be_falsey end end describe "creating resources" do it "handle one-line resource creation" do @user = Foo::User.create(:fullname => "Tobias Fünke", :organization_id => 2) expect(@user.id).to eq(1) expect(@user.fullname).to eq("Tobias Fünke") end it "handle resource creation through Model.new + #save" do @user = Foo::User.new(:fullname => "Tobias Fünke", :organization_id => 2) @user.save expect(@user.fullname).to eq("Tobias Fünke") end end context "updating resources" do it "handle resource data update without saving it" do @user = Foo::User.find(1, :_organization_id => 2) expect(@user.fullname).to eq("Tobias Fünke") @user.fullname = "Kittie Sanchez" expect(@user.fullname).to eq("Kittie Sanchez") end it "handle resource update through the .update class method" do @user = Foo::User.save_existing(1, { :fullname => "Lindsay Fünke", :organization_id => 2 }) expect(@user.fullname).to eq("Lindsay Fünke") end it "handle resource update through #save on an existing resource" do @user = Foo::User.find(1, :_organization_id => 2) @user.fullname = "Lindsay Fünke" @user.save expect(@user.fullname).to eq("Lindsay Fünke") end end context "deleting resources" do it "handle resource deletion through the .destroy class method" do @user = Foo::User.destroy_existing(1, :_organization_id => 2) expect(@user.active).to be_falsey end it "handle resource deletion through #destroy on an existing resource" do @user = Foo::User.find(1, :_organization_id => 2) @user.destroy expect(@user.active).to be_falsey end end end context "making path HTTP requests" do before do Her::API.setup :url => "https://example.com/api/" do |builder| builder.use Her::Middleware::FirstLevelParseJSON builder.use Faraday::Request::UrlEncoded builder.adapter :test do |stub| stub.get("/api/organizations/2/users") { |env| [200, {}, [{ :id => 1, :fullname => "Tobias Fünke", :organization_id => 2 }, { :id => 2, :fullname => "Lindsay Fünke", :organization_id => 2 }].to_json] } stub.get("/api/organizations/2/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :organization_id => 2, :active => true }.to_json] } end end spawn_model "Foo::User" do collection_path "organizations/:organization_id/users" end end describe "fetching a resource" do it "maps a single resource to a Ruby object" do @user = Foo::User.find(1, :_organization_id => 2) expect(@user.id).to eq(1) expect(@user.fullname).to eq("Tobias Fünke") end end describe "fetching a collection" do it "maps a collection of resources to an array of Ruby objects" do @users = Foo::User.where(:_organization_id => 2).all expect(@users.length).to eq(2) expect(@users.first.fullname).to eq("Tobias Fünke") end end describe "fetching a resource with absolute path" do it "maps a single resource to a Ruby object" do Foo::User.resource_path '/api/' + Foo::User.resource_path @user = Foo::User.find(1, :_organization_id => 2) expect(@user.id).to eq(1) expect(@user.fullname).to eq("Tobias Fünke") end end describe "fetching a collection with absolute path" do it "maps a collection of resources to an array of Ruby objects" do Foo::User.collection_path '/api/' + Foo::User.collection_path @users = Foo::User.where(:_organization_id => 2).all expect(@users.length).to eq(2) expect(@users.first.fullname).to eq("Tobias Fünke") end end end end