require "spec_helper" describe DataMapper::Adapters::ParseAdapter do let(:adapter) { DataMapper.setup(:default, options) } let(:options) { { adapter: :parse, app_id: app_id, api_key: api_key } } let(:app_id) { "xxx" } let(:api_key) { "yyy" } let(:app_id_header) { "X-Parse-Application-Id" } let(:api_key_header) { "X-Parse-REST-API-Key" } let(:master_key_header) { "X-Parse-Master-Key" } let(:model) { Article } describe "#parse_conditions_for" do subject { adapter.send :parse_conditions_for, query } context "when query is nil" do let(:query) { model.all.query } it { should be_nil } end context "when query assigns some exact values" do let(:query) { model.all(:id => "z", :title => "x", :body => "y").query } it { should eq("objectId" => "z", "title" => "x", "body" => "y") } end [:gt, :gte, :lt, :lte].each do |slug| context "when query has #{slug} comparison" do let(:query) { model.all(:id => "z", :rank.send(slug) => 5).query } it { should eq("objectId" => "z", "rank" => {"$#{slug}" => 5}) } end end context "when query has :not operation" do let(:query) { model.all(:rank.not => 5, :body.not => "x").query } it { should eq("rank" => {"$ne" => 5}, "body" => {"$ne" => "x"}) } end context "when query has multiple comparisons of one field" do let(:query) { model.all(:rank.lt => 5, :rank.gt => 3).query } it { should eq("rank" => {"$lt" => 5, "$gt" => 3}) } end context "when query has :in comparison" do let(:query) { model.all("rank" => 1..3).query } it { should eq("rank" => {"$in" => (1..3).to_a}) } end context "when query has :regexp comparison" do let(:query) { model.all(:body => regex).query } let(:regex) { /^[A-Z]\d/ } it { should eq("body" => {"$regex" => "^[A-Z]\\d"}) } context "when regular expersion has options" do let(:regex) { /bbq/mi } it { should eq("body" => {"$regex"=>"bbq", "$options"=>"im"}) } end end context "when query has :or operation" do let(:query) { (model.all(:rank => 3) + model.all(:rank => 4) + model.all(:rank => 5)).query } it { should eq("$or" => [{"rank" => 3}, {"rank" => 4}, {"rank" => 5}]) } end context "when query has union operator" do let(:query) { (model.all(:rank => 3) | model.all(:rank => 4) | model.all(:rank => 5)).query } it { should eq("$or" => [{"rank" => 3}, {"rank" => 4}, {"rank" => 5}]) } end context "when query has and operator" do let(:query) { (model.all("rank" => 5) & model.all("body" => "x")).query } it { should eq("rank" => 5, "body" => "x") } end context "when query has complex :not operation" do let(:query) { (model.all - model.all(:rank => 5, :body => "x")).query } it { should eq("rank" => {"$ne" => 5}, "body" => {"$ne" => "x"}) } context "when condition is not EqualToComparison" do let(:query) { model.all(:rank.not => [2, 3]).query } it { should eq("rank" => {"$nin" => [2, 3]}) } end end describe "exceptions" do subject { -> { adapter.send :parse_conditions_for, query } } context "when the key is same" do let(:query) { (model.all("rank" => 5) & model.all("rank" => 3)).query } it { should raise_error("can only use one EqualToComparison for a field") } end context "when query has :eql and others of one field" do let(:query) { model.all(:rank => 5, :rank.gt => 3).query } it { should raise_error } end end # exceptions end # #parse_conditions_for describe "#parse_orders_for" do subject { adapter.send :parse_orders_for, query } let(:query) { model.all("body" => "x").query } it { should be_nil } context "when orders are given" do let(:query) { model.all(:order => [:rank.asc, :title.desc]).query } it { should eq("rank,-title") } end end # #parse_orders_for describe "#parse_limit_for" do subject { adapter.send :parse_limit_for, query } let(:query) { model.all.query } it { should eq(1000) } context "when 0 is given" do let(:query) { model.all(:limit => 0).query } it { should eq(0) } end describe "exceptions" do subject { -> { adapter.send :parse_limit_for, query } } context "when 1001 is given" do let(:query) { model.all(:limit => 1001).query } it { should raise_error("Parse limit: only number from 0 to 1000 is valid") } end end end # #parse_limit_for describe "#parse_offset_for" do subject { adapter.send :parse_offset_for, query } let(:query) { model.all.query } it { should eq(0) } context "when a number is given" do let(:query) { model.all(:offset => number, :limit => 200).query } context "the number is positive" do let(:number) { 1 } it { should eq(number) } end context "the number is positive" do let(:number) { 0 } it { should eq(number) } end end describe "exceptions" do subject { -> { adapter.send :parse_offset_for, query } } let(:query) { model.all(:offset => number, :limit => 200).query } context "the number is negative" do let(:number) { -1 } it { should raise_error } end end end # #parse_offset_for describe "#parse_params_for" do subject { adapter.send :parse_params_for, query } let(:query) { model.all.query } it { should eq(:limit => 1000) } context "when limit is given" do let(:query) { model.all(:limit => 200).query } it { should eq(:limit => 200) } end context "when conditions is given" do let(:query) { model.all(:rank => 5).query } it { should eq(:limit => 1000, :where => {"rank" => 5}.to_json) } end context "when offset is given" do let(:query) { model.all(:limit => 200, :offset => 300).query } it { should eq(:limit => 200, :skip => 300) } end context "when orders are given" do let(:query) { model.all(:order => [:rank.desc]).query } it { should eq(:limit => 1000, :order => "-rank") } end end # #parse_params_for shared_examples_for DataMapper::Parse::Resource do let(:options) { { adapter: :parse, app_id: app_id, api_key: api_key} } it { should be_a(DataMapper::Parse::Resource) } its(:options) { should eq(format: :json, headers: {app_id_header => app_id, api_key_header => api_key}) } context "when master mode is on" do let(:options) { { adapter: :parse, app_id: app_id, api_key: api_key, master: true } } its(:options) { should eq(format: :json, headers: {app_id_header => app_id, master_key_header => api_key}) } end end describe "#classes" do subject { adapter.classes } its(:url) { should eq("https://api.parse.com/1/classes") } it_should_behave_like DataMapper::Parse::Resource end describe "#users" do subject { adapter.users } its(:url) { should eq("https://api.parse.com/1/users") } it_should_behave_like DataMapper::Parse::Resource end describe "#login" do subject { adapter.login } its(:url) { should eq("https://api.parse.com/1/login") } it_should_behave_like DataMapper::Parse::Resource end describe "#password_reset" do subject { adapter.password_reset } its(:url) { should eq("https://api.parse.com/1/requestPasswordReset") } it_should_behave_like DataMapper::Parse::Resource end describe "#parse_resources_for" do subject { adapter.parse_resources_for model } it { should eq(adapter.classes[model.storage_name]) } context "when storage_name of model is _User" do before(:each) { model.stub(storage_name: "_User") } it { should eq(adapter.users) } end end describe "#parse_resource_for" do subject { adapter.parse_resource_for resource } let(:resource) { model.new id: "xxx" } it { should eq(adapter.parse_resources_for(model)["xxx"]) } end describe "#create" do subject { adapter.create resources } let(:resources) { [resource] } let(:resource) { model.new attributes } let(:attributes) { { id: "fd", rank: 3, created_at: 1.day.ago, updated_at: 2.days.ago } } before(:each) do double_resources = double("resource") double_resources.should_receive(:post).with(params: {"rank" => 3}).once.and_return({"createdAt" => "2011-08-20T02:06:57.931Z", "objectId" => "Ed1nuqPvcm"}) adapter.stub(parse_resources_for: double_resources) end it { should eq(1) } end describe "#read" do subject { adapter.read query } let(:query) { model.all(:rank => 4).query } let(:results) { [{"objectId" => "anything"}] } before(:each) do double_resources = double("resource") double_resources.should_receive(:get).with(params: { limit: 1000, where: {"rank" => 4}.to_json }).once.and_return("results" => results) adapter.stub(parse_resources_for: double_resources) end it { should eq(results) } end describe "#delete" do subject { adapter.delete resources } let(:resources) { [resource] } let(:resource) { model.new id: id } let(:id) { "xxx" } before(:each) do double_resource = double("resource") double_resource.should_receive(:delete).with(no_args).once.and_return({}) adapter.stub(parse_resource_for: double_resource) end it { should eq(1) } end describe "#update" do subject { adapter.update attributes, resources } let(:resources) { [resource] } let(:resource) { model.new id: "xxx" } let(:attributes) { model.new(rank: 5, created_at: 1.day.ago, updated_at: 2.days.ago).attributes(:property) } before(:each) do double_resource = double("resource") double_resource.should_receive(:put).with(params: {"rank" => 5}).once.and_return("updatedAt" => "2011-08-21T18:02:52.248Z") adapter.stub(parse_resource_for: double_resource) end it { should eq(1) } end end