diff --git a/fever_api.rb b/fever_api.rb index d08357df2..5df59c9d0 100644 --- a/fever_api.rb +++ b/fever_api.rb @@ -117,11 +117,11 @@ def unread_stories(since_id = nil) end def all_starred_stories - Story.where(is_starred: true) + Story.where(is_starred: true) end def stories_by_ids(ids) - StoryRepository.fetch_by_ids(ids) + StoryRepository.fetch_by_ids(ids) end def feeds diff --git a/spec/factories/feed_factory.rb b/spec/factories/feed_factory.rb index b3b8f4570..203cf5df6 100644 --- a/spec/factories/feed_factory.rb +++ b/spec/factories/feed_factory.rb @@ -1,11 +1,24 @@ class FeedFactory - class FakeFeed < OpenStruct; end; + class FakeFeed < OpenStruct + def as_fever_json + { + id: self.id, + favicon_id: 0, + title: self.name, + url: self.url, + site_url: self.url, + is_spark: 0, + last_updated_on_time: self.last_fetched.to_i + } + end + end def self.build(params = {}) FakeFeed.new( + id: rand(100), name: params[:name] || Faker::Name.name + " on Software", url: params[:url] || Faker::Internet.url, last_fetched: params[:last_fetched] || Time.now, stories: params[:stories] || []) end -end \ No newline at end of file +end diff --git a/spec/factories/story_factory.rb b/spec/factories/story_factory.rb index d294f252d..f9d2d8598 100644 --- a/spec/factories/story_factory.rb +++ b/spec/factories/story_factory.rb @@ -9,6 +9,20 @@ def headline def source self.feed.name end + + def as_fever_json + { + id: self.id, + feed_id: self.feed_id, + title: self.title, + author: source, + html: body, + url: self.permalink, + is_saved: self.is_starred ? 1 : 0, + is_read: self.is_read ? 1 : 0, + created_on_time: self.published.to_i + } + end end def self.build(params = {}) diff --git a/spec/fever_api_spec.rb b/spec/fever_api_spec.rb new file mode 100644 index 000000000..e63fcde53 --- /dev/null +++ b/spec/fever_api_spec.rb @@ -0,0 +1,165 @@ +require 'spec_helper' +require './fever_api' + +describe FeverAPI do + include Rack::Test::Methods + + def app + FeverAPI + end + + let(:api_key) { 'apisecretkey' } + let(:story_one) { StoryFactory.build } + let(:story_two) { StoryFactory.build } + let(:feed) { FeedFactory.build } + let(:stories) { [story_one, story_two] } + let(:answer) { { api_version: 3, auth: 1, last_refreshed_on_time: Time.now.to_i } } + let(:headers) { { api_key: api_key } } + + before do + user = stub(api_key: api_key) + User.stub(:first).and_return(user) + end + + describe "authentication" do + it "authenticates request with correct api_key" do + get "/", headers + last_response.should be_ok + end + + it "does not authenticate request with incorrect api_key" do + get "/", api_key: 'foo' + last_response.should_not be_ok + end + + it "does not authenticate request when api_key is not provided" do + get "/" + last_response.should_not be_ok + end + end + + describe "#get" do + def make_request(extra_headers = {}) + get "/", headers.merge(extra_headers) + end + + it "returns standart answer" do + make_request + last_response.should be_ok + last_response.body.should == answer.to_json + end + + it "returns groups and feeds by groups when 'groups' header is provided" do + Feed.stub(:all).and_return([feed]) + make_request({ groups: nil }) + answer.merge!({ groups: [{ id: 1, title: "All items" }], feeds_groups: [{ group_id: 1, feed_ids: feed.id.to_s }] }) + last_response.should be_ok + last_response.body.should == answer.to_json + end + + it "returns feeds and feeds by groups when 'feeds' header is provided" do + Feed.stub(:all).and_return([feed]) + FeedRepository.stub(:list).and_return([feed]) + make_request({ feeds: nil }) + answer.merge!({ feeds: [feed.as_fever_json], feeds_groups: [{ group_id: 1, feed_ids: feed.id.to_s }] }) + last_response.should be_ok + last_response.body.should == answer.to_json + end + + it "returns favicons hash when 'favicons' header provided" do + make_request({ favicons: nil }) + answer.merge!({ favicons: [{ id: 0, data: "image/gif;base64,R0lGODlhAQABAIAAAObm5gAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" }] }) + last_response.should be_ok + last_response.body.should == answer.to_json + end + + it "returns stories when 'items' header is provided along with 'since_id'" do + StoryRepository.should_receive(:unread_since_id).with('5').and_return([story_one]) + StoryRepository.should_receive(:unread).and_return([story_one, story_two]) + make_request({ items: nil, since_id: 5 }) + answer.merge!({ items: [story_one.as_fever_json], total_items: 2 }) + last_response.should be_ok + last_response.body.should == answer.to_json + end + + it "returns stories when 'items' header is provided without 'since_id'" do + StoryRepository.should_receive(:unread).twice.and_return([story_one, story_two]) + make_request({ items: nil }) + answer.merge!({ items: [story_one.as_fever_json, story_two.as_fever_json], total_items: 2 }) + last_response.should be_ok + last_response.body.should == answer.to_json + end + + it "returns stories ids when 'items' header is provided along with 'with_ids'" do + StoryRepository.should_receive(:fetch_by_ids).twice.with(['5']).and_return([story_one]) + make_request({ items: nil, with_ids: 5 }) + answer.merge!({ items: [story_one.as_fever_json], total_items: 1 }) + last_response.should be_ok + last_response.body.should == answer.to_json + end + + it "returns links as empty array when 'links' header is provided" do + make_request({ links: nil }) + answer.merge!({ links: [] }) + last_response.should be_ok + last_response.body.should == answer.to_json + end + + it "returns unread items ids when 'unread_item_ids' header is provided" do + StoryRepository.should_receive(:unread).and_return([story_one, story_two]) + make_request({ unread_item_ids: nil }) + answer.merge!({ unread_item_ids: [story_one.id,story_two.id].join(',') }) + last_response.should be_ok + last_response.body.should == answer.to_json + end + + it "returns starred items when 'saved_item_ids' header is provided" do + Story.should_receive(:where).with({ is_starred: true }).and_return([story_one, story_two]) + make_request({ saved_item_ids: nil }) + answer.merge!({ saved_item_ids: [story_one.id,story_two.id].join(',') }) + last_response.should be_ok + last_response.body.should == answer.to_json + end + end + + describe "#post" do + def make_request(extra_headers = {}) + post "/", headers.merge(extra_headers) + end + + it "commands to mark story as read" do + MarkAsRead.should_receive(:new).with('10').and_return(stub(mark_as_read: true)) + make_request({ mark: 'item', as: 'read', id: 10 }) + last_response.should be_ok + last_response.body.should == answer.to_json + end + + it "commands to mark story as unread" do + MarkAsUnread.should_receive(:new).with('10').and_return(stub(mark_as_unread: true)) + make_request({ mark: 'item', as: 'unread', id: 10 }) + last_response.should be_ok + last_response.body.should == answer.to_json + end + + it "commands to save story" do + MarkAsStarred.should_receive(:new).with('10').and_return(stub(mark_as_starred: true)) + make_request({ mark: 'item', as: 'saved', id: 10 }) + last_response.should be_ok + last_response.body.should == answer.to_json + end + + it "commands to unsave story" do + MarkAsUnstarred.should_receive(:new).with('10').and_return(stub(mark_as_unstarred: true)) + make_request({ mark: 'item', as: 'unsaved', id: 10 }) + last_response.should be_ok + last_response.body.should == answer.to_json + end + + it "commands to mark group as read" do + MarkGroupAsRead.should_receive(:new).with('10', '1375080946').and_return(stub(mark_group_as_read: true)) + make_request({ mark: 'group', as: 'read', id: 10, before: 1375080946 }) + last_response.should be_ok + last_response.body.should == answer.to_json + end + end +end