Merge branch 'repost'

This commit is contained in:
Raphael Sofaer 2011-07-22 18:00:02 -07:00
commit dfa6539ef2
57 changed files with 1052 additions and 151 deletions

View file

@ -44,7 +44,7 @@ class AspectsController < ApplicationController
@aspect_ids = @aspects.map { |a| a.id }
posts = current_user.visible_posts(:by_members_of => @aspect_ids,
:type => ['StatusMessage','ActivityStreams::Photo'],
:type => ['StatusMessage','Reshare', 'ActivityStreams::Photo'],
:order => session[:sort_order] + ' DESC',
:max_time => params[:max_time].to_i
).includes(:mentions => {:person => :profile})

View file

@ -96,12 +96,11 @@ class PeopleController < ApplicationController
else
@commenting_disabled = false
end
@posts = current_user.posts_from(@person).where(:type => ["StatusMessage", "ActivityStreams::Photo"]).includes(:comments).limit(15).where(StatusMessage.arel_table[:created_at].lt(max_time))
@posts = current_user.posts_from(@person).where(:type => ["StatusMessage", "Reshare", "ActivityStreams::Photo"]).includes(:comments).limit(15).where(StatusMessage.arel_table[:created_at].lt(max_time))
else
@commenting_disabled = true
@posts = @person.posts.where(:type => ["StatusMessage", "ActivityStreams::Photo"], :public => true).includes(:comments).limit(15).where(StatusMessage.arel_table[:created_at].lt(max_time)).order('posts.created_at DESC')
@posts = @person.posts.where(:type => ["StatusMessage", "Reshare", "ActivityStreams::Photo"], :public => true).includes(:comments).limit(15).where(StatusMessage.arel_table[:created_at].lt(max_time)).order('posts.created_at DESC')
end
@posts = PostsFake.new(@posts)
end

View file

@ -12,6 +12,9 @@ class PublicsController < ApplicationController
skip_before_filter :set_grammatical_gender
before_filter :allow_cross_origin, :only => [:hcard, :host_meta, :webfinger]
respond_to :html
respond_to :xml, :only => :post
def allow_cross_origin
headers["Access-Control-Allow-Origin"] = "*"
end
@ -66,23 +69,31 @@ class PublicsController < ApplicationController
end
def post
@post = Post.where(:id => params[:id], :public => true).includes(:author, :comments => :author).first
if params[:guid].to_s.length <= 8
@post = Post.where(:id => params[:guid], :public => true).includes(:author, :comments => :author).first
else
@post = Post.where(:guid => params[:guid], :public => true).includes(:author, :comments => :author).first
end
#hax to upgrade logged in users who can comment
if @post
if user_signed_in? && current_user.find_visible_post_by_id(@post.id)
redirect_to post_path(@post)
return
end
@landing_page = true
@person = @post.author
if @person.owner_id
I18n.locale = @person.owner.language
render "#{@post.class.to_s.underscore}", :layout => 'application'
else
flash[:error] = I18n.t('posts.show.not_found')
redirect_to root_url
@landing_page = true
@person = @post.author
if @person.owner_id
I18n.locale = @person.owner.language
respond_to do |format|
format.all{ render "#{@post.class.to_s.underscore}", :layout => 'application'}
format.xml{ render :xml => @post.to_diaspora_xml }
end
else
flash[:error] = I18n.t('posts.show.not_found')
redirect_to root_url
end
end
else
flash[:error] = I18n.t('posts.show.not_found')

View file

@ -0,0 +1,14 @@
class ResharesController < ApplicationController
before_filter :authenticate_user!
respond_to :js
def create
@reshare = current_user.build_post(:reshare, :root_guid => params[:root_guid])
if @reshare.save
current_user.add_to_streams(@reshare, current_user.aspects)
current_user.dispatch_post(@reshare, :url => post_url(@reshare), :additional_subscribers => @reshare.root.author)
end
respond_with @reshare
end
end

View file

@ -0,0 +1,13 @@
module ResharesHelper
def reshare_error_message(reshare)
if @reshare.errors[:root_guid].present?
escape_javascript(@reshare.errors[:root_guid].first)
else
escape_javascript(t('reshares.create.failure'))
end
end
def reshare_link post
link_to t("reshares.reshare.reshare", :count => post.reshares.size), reshares_path(:root_guid => post.guid), :method => :post, :remote => true, :confirm => t('reshares.reshare.reshare_confirmation', :author => post.author.name, :text => post.text)
end
end

View file

@ -28,6 +28,7 @@ module SocketsHelper
post_hash = {:post => object,
:author => object.author,
:photos => object.photos,
:reshare => nil,
:comments => object.comments.map{|c|
{:comment => c,
:author => c.author

View file

@ -28,4 +28,8 @@ module StreamHelper
def comments_expanded
false
end
def reshare?(post)
(defined?(post.model) && post.model.is_a?(Reshare)) || post.instance_of?(Reshare)
end
end

View file

@ -6,7 +6,7 @@ require 'uri'
class AppConfig < Settingslogic
def self.source_file_name
if Rails.env == 'test' || ENV["CI"]
if Rails.env == 'test' || ENV["CI"] || Rails.env.include?("integration")
File.join(Rails.root, "config", "application.yml.example")
else
File.join(Rails.root, "config", "application.yml")
@ -125,7 +125,7 @@ HELP
def self.pod_uri
if @@pod_uri.nil?
begin
@@pod_uri = URI.parse(self.pod_url)
@@pod_uri = Addressable::URI.parse(self.pod_url)
rescue
puts "WARNING: pod url " + self.pod_url + " is not a legal URI"
end

View file

@ -235,7 +235,6 @@ class Person < ActiveRecord::Base
protected
def clean_url
self.url ||= "http://localhost:3000/" if self.class == User
if self.url
self.url = 'http://' + self.url unless self.url.match(/https?:\/\//)
self.url = self.url + '/' if self.url[-1, 1] != '/'

View file

@ -24,6 +24,9 @@ class Post < ActiveRecord::Base
has_many :contacts, :through => :post_visibilities
has_many :mentions, :dependent => :destroy
has_many :reshares, :class_name => "Reshare", :foreign_key => :root_guid, :primary_key => :guid
has_many :resharers, :class_name => 'Person', :through => :reshares, :source => :author
belongs_to :author, :class_name => 'Person'
def diaspora_handle

View file

@ -2,81 +2,28 @@
# licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file.
class RelayableRetraction
include ROXML
include Diaspora::Webhooks
include Diaspora::Encryptable
class RelayableRetraction < SignedRetraction
xml_name :relayable_retraction
xml_attr :target_guid
xml_attr :target_type
xml_attr :sender_handle
xml_attr :parent_author_signature
xml_attr :target_author_signature
attr_accessor :target_guid,
:target_type,
:parent_author_signature,
:target_author_signature,
:sender
attr_accessor :parent_author_signature
def signable_accessors
accessors = self.class.roxml_attrs.collect do |definition|
definition.accessor
end
['target_author_signature', 'parent_author_signature'].each do |acc|
accessors.delete acc
end
accessors
end
def sender_handle= new_sender_handle
@sender = Person.where(:diaspora_handle => new_sender_handle).first
end
def sender_handle
@sender.diaspora_handle
end
def diaspora_handle
self.sender_handle
end
def subscribers(user)
self.target.subscribers(user)
super - ['parent_author_signature']
end
def self.build(sender, target)
retraction = self.new
retraction.sender = sender
retraction.target = target
retraction.target_author_signature = retraction.sign_with_key(sender.encryption_key) if sender.person == target.author
retraction.parent_author_signature = retraction.sign_with_key(sender.encryption_key) if sender.person == target.parent.author
retraction = super
retraction.parent_author_signature = retraction.sign_with_key(sender.encryption_key) if defined?(target.parent) && sender.person == target.parent.author
retraction
end
def target
@target ||= self.target_type.constantize.where(:guid => target_guid).first
end
def guid
target_guid
end
def target= new_target
@target = new_target
@target_type = new_target.class.to_s
@target_guid = new_target.guid
end
def parent
self.target.parent
end
def perform receiving_user
Rails.logger.debug "Performing retraction for #{target_guid}"
self.target.unsocket_from_user receiving_user if target.respond_to? :unsocket_from_user
self.target.destroy
Rails.logger.info(:event => :retraction, :status => :complete, :target_type => self.target_type, :guid => self.target_guid)
def diaspora_handle
self.sender_handle
end
def receive(recipient, sender)
@ -101,8 +48,4 @@ class RelayableRetraction
def parent_author_signature_valid?
verify_signature(self.parent_author_signature, self.parent.author)
end
def target_author_signature_valid?
verify_signature(self.target_author_signature, self.target.author)
end
end

67
app/models/reshare.rb Normal file
View file

@ -0,0 +1,67 @@
class Reshare < Post
belongs_to :root, :class_name => 'Post', :foreign_key => :root_guid, :primary_key => :guid
validate :root_must_be_public
attr_accessible :root_guid, :public
validates_presence_of :root, :on => :create
validates_uniqueness_of :root_guid, :scope => :author_id
xml_attr :root_diaspora_id
xml_attr :root_guid
before_validation do
self.public = true
end
def root_diaspora_id
self.root.author.diaspora_handle
end
def receive(recipient, sender)
local_reshare = Reshare.where(:guid => self.guid).first
if local_reshare && local_reshare.root.author_id == recipient.person.id
local_reshare.root.reshares << local_reshare
if recipient.contact_for(sender)
local_reshare.receive(recipient, sender)
end
else
super(recipient, sender)
end
end
private
def after_parse
root_author = Webfinger.new(@root_diaspora_id).fetch
root_author.save! unless root_author.persisted?
return if Post.exists?(:guid => self.root_guid)
fetched_post = self.class.fetch_post(root_author, self.root_guid)
#Why are we checking for this?
if root_author.diaspora_handle != fetched_post.diaspora_handle
raise "Diaspora ID (#{fetched_post.diaspora_handle}) in the root does not match the Diaspora ID (#{root_author.diaspora_handle}) specified in the reshare!"
end
fetched_post.save!
end
# Fetch a remote public post, used for receiving reshares of unknown posts
# @param [Person] author the remote post's author
# @param [String] guid the remote post's guid
# @return [Post] an unsaved remote post
def self.fetch_post author, guid
response = Faraday.get(author.url + "/p/#{guid}.xml")
Diaspora::Parser.from_xml(response.body)
end
def root_must_be_public
if self.root && !self.root.public
errors[:base] << "you must reshare public posts"
return false
end
end
end

View file

@ -15,6 +15,8 @@ class Retraction
def subscribers(user)
unless self.type == 'Person'
@subscribers ||= self.object.subscribers(user)
@subscribers -= self.object.resharers
@subscribers
else
raise 'HAX: you must set the subscribers manaully before unfriending' if @subscribers.nil?
@subscribers

View file

@ -0,0 +1,97 @@
# Copyright (c) 2010, Diaspora Inc. This file is
# licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file.
class SignedRetraction
include ROXML
include Diaspora::Webhooks
include Diaspora::Encryptable
xml_name :signed_retraction
xml_attr :target_guid
xml_attr :target_type
xml_attr :sender_handle
xml_attr :target_author_signature
attr_accessor :target_guid,
:target_type,
:target_author_signature,
:sender
def signable_accessors
accessors = self.class.roxml_attrs.collect do |definition|
definition.accessor
end
accessors - ['target_author_signature', 'sender_handle']
end
def sender_handle= new_sender_handle
@sender = Person.where(:diaspora_handle => new_sender_handle).first
end
def sender_handle
@sender.diaspora_handle
end
def diaspora_handle
self.sender_handle
end
def subscribers(user)
self.target.subscribers(user)
end
def self.build(sender, target)
retraction = self.new
retraction.sender = sender
retraction.target = target
retraction.target_author_signature = retraction.sign_with_key(sender.encryption_key) if sender.person == target.author
retraction
end
def target
@target ||= self.target_type.constantize.where(:guid => target_guid).first
end
def guid
target_guid
end
def target= new_target
@target = new_target
@target_type = new_target.class.to_s
@target_guid = new_target.guid
end
def perform receiving_user
Rails.logger.debug "Performing retraction for #{target_guid}"
if reshare = Reshare.where(:author_id => receiving_user.person.id, :root_guid => target_guid).first
onward_retraction = self.dup
onward_retraction.sender = receiving_user.person
Postzord::Dispatch.new(receiving_user, onward_retraction).post
end
if target
self.target.unsocket_from_user receiving_user if target.respond_to? :unsocket_from_user
self.target.destroy
end
Rails.logger.info(:event => :retraction, :status => :complete, :target_type => self.target_type, :guid => self.target_guid)
end
def receive(recipient, sender)
if self.target.nil?
Rails.logger.info("event=retraction status=abort reason='no post found' sender=#{sender.diaspora_handle} target_guid=#{target_guid}")
return
elsif self.target_author_signature_valid?
#this is a retraction from the upstream owner
self.perform(recipient)
else
Rails.logger.info("event=receive status=abort reason='object signature not valid' recipient=#{recipient.diaspora_handle} sender=#{self.sender_handle} payload_type=#{self.class}")
return
end
self
end
def target_author_signature_valid?
verify_signature(self.target_author_signature, self.target.author)
end
end

View file

@ -136,7 +136,8 @@ class User < ActiveRecord::Base
end
def dispatch_post(post, opts = {})
mailman = Postzord::Dispatch.new(self, post)
additional_people = opts.delete(:additional_subscribers)
mailman = Postzord::Dispatch.new(self, post, :additional_subscribers => additional_people)
mailman.post(opts)
end
@ -229,14 +230,20 @@ class User < ActiveRecord::Base
end
######### Posts and Such ###############
def retract(target)
def retract(target, opts={})
if target.respond_to?(:relayable?) && target.relayable?
retraction = RelayableRetraction.build(self, target)
elsif target.is_a? Post
retraction = SignedRetraction.build(self, target)
else
retraction = Retraction.for(target)
end
mailman = Postzord::Dispatch.new(self, retraction)
if target.is_a?(Post)
opts[:additional_subscribers] = target.resharers
end
mailman = Postzord::Dispatch.new(self, retraction, opts)
mailman.post
retraction.perform(self)
@ -326,7 +333,7 @@ class User < ActiveRecord::Base
end
self.person = Person.new(opts[:person])
self.person.diaspora_handle = "#{opts[:username]}@#{AppConfig[:pod_uri].host}"
self.person.diaspora_handle = "#{opts[:username]}@#{AppConfig[:pod_uri].authority}"
self.person.url = AppConfig[:pod_url]

View file

@ -4,7 +4,7 @@
.span-20.append-2.prepend-2.last
#main_stream.stream.status_message_show
= render 'shared/stream_element', :post => @post, :all_aspects => @post.aspects
%br
%br
%br
= render 'shared/stream_element', :post => @post, :commenting_disabled => defined?(@commenting_disabled)
%br
%br
%br

View file

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'
xmlns:hm='http://host-meta.net/xrd/1.0'>
<hm:Host><%= AppConfig[:pod_uri].host %></hm:Host>
<hm:Host><%= AppConfig[:pod_uri].authority %></hm:Host>
<Link rel='lrdd'
template='<%= AppConfig[:pod_url].chomp("/") %>/webfinger?q={uri}'>
<Title>Resource Descriptor</Title>

View file

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'
xmlns:hm='http://host-meta.net/xrd/1.0'>
<hm:Host><%= AppConfig[:pod_uri].host %></hm:Host>
<hm:Host><%= AppConfig[:pod_uri].authority %></hm:Host>
<Link rel='lrdd'
template='<%= AppConfig[:pod_url] %>webfinger?q={uri}'>
<Title>Resource Descriptor</Title>

View file

@ -0,0 +1,25 @@
-# Copyright (c) 2010, Diaspora Inc. This file is
-# licensed under the Affero General Public License version 3 or later. See
-# the COPYRIGHT file.
.reshare
- if post
= person_image_link(post.author, :size => :thumb_small)
.content
.right
= link_to t("show_original"), post_path(post)
%span.from
= person_link(post.author, :class => "hovercardable")
- if post.activity_streams?
= link_to image_tag(post.image_url, 'data-small-photo' => post.image_url, 'data-full-photo' => post.image_url, :class => 'stream-photo'), post.object_url, :class => "stream-photo-link"
- else
= render 'status_messages/status_message', :post => post, :photos => post.photos
- if defined?(current_user) && current_user && (post.author_id != current_user.person.id) && (post.public?) && !reshare?(post)
%span.reshare_action
= reshare_link(post)
- else
= t('.deleted')

View file

@ -0,0 +1,9 @@
<% if @reshare.persisted? %>
$('.stream_element#<%=params[:root_guid]%>').addClass('reshared');
ContentUpdater.addPostToStream(
"<%= escape_javascript(render(:partial => 'shared/stream_element', :locals => {:post => @reshare }))=%>"
);
<% else %>
Diaspora.widgets.flashes.render({success:false,
notice: "<%= reshare_error_message(@reshare) %>"});
<% end %>

View file

@ -6,9 +6,11 @@
- if current_user && post.author.owner_id == current_user.id
.right.controls
= link_to image_tag('deletelabel.png'), post_path(post), :confirm => t('are_you_sure'), :method => :delete, :remote => true, :class => "delete stream_element_delete", :title => t('delete')
- else
.right.controls
= link_to image_tag('deletelabel.png'), post_visibility_path(:id => "42", :post_id => post.id), :method => :put, :remote => true, :class => "delete stream_element_delete", :title => t('hide')
.undo_text.hidden
= t('post_visibilites.update.post_hidden', :name => post.author.name)
= link_to t('undo'), post_visibility_path(:id => "42", :post_id => post.id), :method => :put, :remote => true, :class => "delete stream_element_delete"
@ -20,6 +22,7 @@
%span.from
= person_link(post.author, :class => 'hovercardable')
%time.time.timeago{:datetime => post.created_at, :integer => time_for_sort(post).to_i}
%span.details
%span.timeago
@ -27,6 +30,8 @@
- if post.activity_streams?
= link_to image_tag(post.image_url, 'data-small-photo' => post.image_url, 'data-full-photo' => post.image_url, :class => 'stream-photo'), post.object_url, :class => "stream-photo-link"
- elsif reshare?(post)
= render 'reshares/reshare', :reshare => post, :post => post.root
- else
= render 'status_messages/status_message', :post => post, :photos => post.photos
@ -52,7 +57,13 @@
- unless (defined?(@commenting_disabled) && @commenting_disabled)
%span.like_action
= like_action(post, current_user)
- if (post.author_id != current_user.person.id) && (post.public?) && !reshare?(post)
·
%span.reshare_action
= reshare_link(post)
·
= link_to t('comments.new_comment.comment'), '#', :class => 'focus_comment_textarea'
.likes.on_post

View file

@ -5,7 +5,8 @@
:author => @status_message.author,
:photos => @status_message.photos,
:comments => [],
:all_aspects => current_user.aspects
:all_aspects => current_user.aspects,
:reshare => nil
}
),
:post_id => @status_message.guid}.to_json.html_safe%>

View file

@ -154,3 +154,12 @@ test:
open_invitations: false
integration_1:
<<: *defaults
pod_url: "http://localhost:45789"
enable_splunk_logging: false
integration_2:
<<: *defaults
pod_url: "http://localhost:34658"
enable_splunk_logging: false

View file

@ -15,13 +15,12 @@ Diaspora::Application.configure do
# Show full error reports and disable caching
config.consider_all_requests_local = true
config.action_view.debug_rjs = true
config.action_controller.perform_caching = false
# Don't care if the mailer can't send
config.action_mailer.raise_delivery_errors = false
config.active_support.deprecation = :log
#config.threadsafe!
config.threadsafe!
# Monkeypatch around the nasty "2.5MB exception page" issue, caused by very large environment vars
# This snippet via: http://stackoverflow.com/questions/3114993/exception-pages-in-development-mode-take-upwards-of-15-30-seconds-to-render-why

View file

@ -3,7 +3,7 @@
# the COPYRIGHT file.
Diaspora::Application.configure do
config.action_mailer.default_url_options = {:host => AppConfig[:pod_uri].host}
config.action_mailer.default_url_options = {:host => AppConfig[:pod_uri].authority }
unless Rails.env == 'test' || AppConfig[:mailer_on] != true
if AppConfig[:mailer_method] == "sendmail"
config.action_mailer.delivery_method = :sendmail

View file

@ -26,3 +26,4 @@
attributes:
from_id:
taken: "is a duplicate of a pre-existing request."

View file

@ -65,8 +65,10 @@ en:
attributes:
from_id:
taken: "is a duplicate of a pre-existing request."
reshare:
attributes:
root_guid:
taken: "You've already reshared that post!"
error_messages:
helper:
invalid_fields: "Invalid Fields"
@ -579,7 +581,19 @@ en:
few: "%{count} new requests!"
many: "%{count} new requests!"
other: "%{count} new requests!"
reshares:
reshare:
reshare:
zero: "Reshare"
one: "1 Reshare"
few: "%{count} Reshares"
many: "%{count} Reshares"
other: "%{count} Reshares"
show_original: "Show Original"
reshare_confirmation: "Reshare %{author} - %{text}?"
deleted: "Original post deleted by author."
create:
failure: "There was an error resharing this post."
services:
index:
logged_in_as: "logged in as"

View file

@ -47,3 +47,5 @@ en:
comments:
show: "show all comments"
hide: "hide comments"
reshares:
duplicate:"You've already reshared that post!"

View file

@ -6,6 +6,8 @@ Diaspora::Application.routes.draw do
# Posting and Reading
resources :reshares
resources :aspects do
put :toggle_contact_visibility
end
@ -16,7 +18,7 @@ Diaspora::Application.routes.draw do
resources :likes, :only => [:create, :destroy, :index]
resources :comments, :only => [:create, :destroy, :index]
end
get 'p/:id' => 'publics#post', :as => 'public_post'
get 'p/:guid' => 'publics#post', :as => 'public_post'
# roll up likes into a nested resource above
resources :comments, :only => [:create, :destroy] do

View file

@ -0,0 +1,9 @@
class AddRootIdToPosts < ActiveRecord::Migration
def self.up
add_column :posts, :root_guid, :string, :limit => 30
end
def self.down
remove_column :posts, :root_guid
end
end

View file

@ -268,6 +268,7 @@ ActiveRecord::Schema.define(:version => 20110707234802) do
t.string "provider_display_name"
t.string "actor_url"
t.integer "objectId"
t.string "root_guid", :limit => 30
t.string "status_message_guid"
t.integer "likes_count", :default => 0
end

View file

@ -1,6 +1,6 @@
@javascript
Feature: disconnecting users
In order to deal with life
In order to help users feel secure on Diaspora
As a User
I want to be able to disconnect from others

View file

@ -64,6 +64,7 @@ Feature: posting
And I am on "bob@bob.bob"'s page
And I hover over the ".stream_element"
And I preemptively confirm the alert
And I click to delete the first post
And I wait for the ajax to finish
And I go to "bob@bob.bob"'s page

98
features/repost.feature Normal file
View file

@ -0,0 +1,98 @@
@javascript
Feature: public repost
In order to make Diaspora more viral
As a User
I want to reshare my friends post
Background:
Given a user named "Bob Jones" with email "bob@bob.bob"
And a user named "Alice Smith" with email "alice@alice.alice"
And a user with email "bob@bob.bob" is connected with "alice@alice.alice"
Scenario: does not show the reshare button on my own posts
And "bob@bob.bob" has a non public post with text "reshare this!"
And I sign in as "bob@bob.bob"
Then I should not see "Reshare"
Scenario: does not show a reshare button on other private pots
And "bob@bob.bob" has a non public post with text "reshare this!"
And I sign in as "alice@alice.alice"
Then I should not see "Reshare"
Scenario: does shows the reshare button on my own posts
And "bob@bob.bob" has a public post with text "reshare this!"
And I sign in as "alice@alice.alice"
Then I should see "Reshare"
Scenario: shows up on the profile page
And "bob@bob.bob" has a public post with text "reshare this!"
And I sign in as "alice@alice.alice"
And I preemptively confirm the alert
And I follow "Reshare"
And I wait for the ajax to finish
And I wait for 2 seconds
And I am on "alice@alice.alice"'s page
Then I should see "reshare this!"
Then I should see a ".reshare"
And I should see "Bob"
Scenario: shows up on the aspects page
And "bob@bob.bob" has a public post with text "reshare this!"
And I sign in as "alice@alice.alice"
And I preemptively confirm the alert
And I follow "Reshare"
And I wait for the ajax to finish
And I go to the home page
Then I should see a ".reshare"
And I follow "Your Aspects"
Then I should see "reshare this!"
Then I should see a ".reshare"
And I should see "Bob"
Scenario: can be retracted
And "bob@bob.bob" has a public post with text "reshare this!"
And I sign in as "alice@alice.alice"
And I preemptively confirm the alert
And I follow "Reshare"
And I wait for the ajax to finish
And I go to the home page
Then I should see a ".reshare"
And I follow "Your Aspects"
Then I should see "reshare this!"
Then I should see a ".reshare"
And I should see "Bob"
And I go to the destroy user session page
And I sign in as "bob@bob.bob"
And The user deletes their first post
And I go to the destroy user session page
And I sign in as "alice@alice.alice"
And I go to the home page
Then I should see "Original post deleted by author"
Scenario: Keeps track of the number of reshares
And "bob@bob.bob" has a public post with text "reshare this!"
And I sign in as "alice@alice.alice"
And I preemptively confirm the alert
And I follow "Reshare"
And I wait for the ajax to finish
And I go to the home page
Then I should see a ".reshare"
And I follow "Your Aspects"
Then I should see "reshare this!"
Then I should see a ".reshare"
And I should see "Bob"
And I go to the home page
And I should see "1 Reshare"
Scenario: Can have text

View file

@ -44,6 +44,10 @@ When /^I click to delete the first post$/ do
page.execute_script('$(".stream_element").first().find(".stream_element_delete").first().click()')
end
When /^I click to delete the ([\d])(nd|rd|st|th) post$/ do |number, stuff|
page.execute_script('$(".stream_element:nth-child('+ number +'").first().find(".stream_element_delete").first().click()')
end
When /^I click to delete the first comment$/ do
page.execute_script('$(".comment.posted").first().find(".comment_delete").click()')
end

View file

@ -59,7 +59,7 @@ class Chubbies
def self.run
@pid = fork do
Process.exec "cd #{Rails.root}/spec/chubbies/ && bundle exec rackup -p #{PORT} 2> /dev/null 1> /dev/null"
Process.exec "cd #{Rails.root}/spec/chubbies/ && bundle exec #{run_command} #{nullify}"
end
at_exit do
@ -71,6 +71,10 @@ class Chubbies
end
end
def self.nullify
"2> /dev/null > /dev/null"
end
def self.kill
`kill -9 #{get_pid}`
end
@ -94,9 +98,13 @@ class Chubbies
end
end
def self.run_command
"rackup -p #{PORT}"
end
def self.get_pid
@pid ||= lambda {
processes = `ps ax -o pid,command | grep "rackup -p #{PORT}"`.split("\n")
processes = `ps ax -o pid,command | grep "#{run_command}"`.split("\n")
processes = processes.select{|p| !p.include?("grep") }
processes.first.split(" ").first
}.call

View file

@ -8,3 +8,17 @@ end
Then /^I should see an uploaded image within the photo drop zone$/ do
find("#photodropzone img")["src"].should include("uploads/images")
end
Given /^"([^"]*)" has a public post with text "([^"]*)"$/ do |email, text|
user = User.find_by_email(email)
user.post(:status_message, :text => text, :public => true, :to => user.aspects)
end
Given /^"([^"]*)" has a non public post with text "([^"]*)"$/ do |email, text|
user = User.find_by_email(email)
user.post(:status_message, :text => text, :public => false, :to => user.aspects)
end
When /^The user deletes their first post$/ do
@me.posts.first.destroy
end

View file

@ -3,7 +3,9 @@
# the COPYRIGHT file.
class Postzord::Dispatch
def initialize(user, object)
# @note Takes :additional_subscribers param to add to subscribers to dispatch to
def initialize(user, object, opts={})
unless object.respond_to? :to_diaspora_xml
raise 'this object does not respond_to? to_diaspora xml. try including Diaspora::Webhooks into your object'
end
@ -12,6 +14,7 @@ class Postzord::Dispatch
@object = object
@xml = @object.to_diaspora_xml
@subscribers = @object.subscribers(@sender)
@subscribers = @subscribers | [*opts[:additional_subscribers]] if opts[:additional_subscribers]
end
def salmon

View file

@ -4,7 +4,7 @@
namespace :db do
desc "rebuild and prepare test db"
task :rebuild => [:drop, :create, :migrate, :seed,'db:test:prepare']
task :rebuild => [:drop, :drop_integration, :create, :migrate, :seed, 'db:test:prepare']
namespace :integration do
# desc 'Check for pending migrations and load the integration schema'
@ -81,6 +81,14 @@ namespace :db do
end
task :add_user => :environment
task :drop_integration do
ActiveRecord::Base.configurations.keys.select{ |k|
k.include?("integration")
}.each{ |k|
drop_database ActiveRecord::Base.configurations[k] rescue Mysql2::Error
}
end
task :fix_diaspora_handle do
puts "fixing the people in this seed"
require File.join(File.dirname(__FILE__), '..', '..', 'config', 'environment')

View file

@ -17,11 +17,11 @@ var View = {
});
Diaspora.widgets.subscribe("stream/scrolled", function() {
$('#main_stream .comments label').inFieldLabels();
$('#main_stream label').inFieldLabels();
});
Diaspora.widgets.subscribe("stream/reloaded", function() {
$('#main_stream .comments label').inFieldLabels();
$('#main_stream label').inFieldLabels();
});

View file

@ -475,6 +475,7 @@ ul.as-selections
.from
a
:color $blue
.status_message_show
.stream_element
.content
@ -554,7 +555,8 @@ ul.as-selections
ul.comments,
ul.show_comments,
.likes_container
.likes_container,
.stream_element .reshare
.avatar
:width 30px
@ -589,18 +591,18 @@ ul.show_comments,
:height 250px
:width 400px
.content
:margin
:top 0px
:bottom -2px
:padding
:left 36px
:right 10px
.content
:margin
:top 0px
:bottom -2px
:padding
:left 36px
:right 10px
p
:margin
:bottom 0
:top 0
p
:margin
:bottom 0
:top 0
.right
:right 4px
@ -609,6 +611,10 @@ ul.show_comments,
:position absolute
:display inline
.stream_element .reshare
:padding 10px
:border 1px solid #eee
ul.show_comments
:padding
:bottom 6px

View file

@ -179,6 +179,12 @@ describe AspectsController do
assigns(:posts).models.length.should == 2
end
it "posts include reshares" do
reshare = alice.post(:reshare, :public => true, :root_guid => Factory(:status_message, :public => true).guid, :to => alice.aspects)
get :index
assigns[:posts].post_fakes.map{|x| x.id}.should include(reshare.id)
end
it "can filter to a single aspect" do
get :index, :a_ids => [@alices_aspect_2.id.to_s]
assigns(:posts).models.length.should == 1

View file

@ -137,6 +137,7 @@ describe PeopleController do
response.body.match(profile.first_name).should be_false
end
context "when the person is the current user" do
it "succeeds" do
get :show, :id => @user.person.to_param
@ -204,6 +205,12 @@ describe PeopleController do
@public_posts.first.save
end
it "posts include reshares" do
reshare = @user.post(:reshare, :public => true, :root_guid => Factory(:status_message, :public => true).guid, :to => alice.aspects)
get :show, :id => @user.person.id
assigns[:posts].post_fakes.map{|x| x.id}.should include(reshare.id)
end
it "assigns only public posts" do
get :show, :id => @person.id
assigns[:posts].models.should =~ @public_posts
@ -251,6 +258,12 @@ describe PeopleController do
assigns(:posts).models.should =~ posts_user_can_see
end
it "posts include reshares" do
reshare = @user.post(:reshare, :public => true, :root_guid => Factory(:status_message, :public => true).guid, :to => alice.aspects)
get :show, :id => @user.person.id
assigns[:posts].post_fakes.map{|x| x.id}.should include(reshare.id)
end
it 'sets @commenting_disabled to true' do
get :show, :id => @person.id
assigns(:commenting_disabled).should == false
@ -283,6 +296,12 @@ describe PeopleController do
assigns[:posts].models.should =~ [public_post]
end
it "posts include reshares" do
reshare = @user.post(:reshare, :public => true, :root_guid => Factory(:status_message, :public => true).guid, :to => alice.aspects)
get :show, :id => @user.person.id
assigns[:posts].post_fakes.map{|x| x.id}.should include(reshare.id)
end
it 'sets @commenting_disabled to true' do
get :show, :id => @person.id
assigns(:commenting_disabled).should == true

View file

@ -65,22 +65,49 @@ describe PublicsController do
it 'shows a public post' do
status = alice.post(:status_message, :text => "hello", :public => true, :to => 'all')
get :post, :id => status.id
get :post, :guid => status.id
response.status= 200
end
it 'does not show a private post' do
status = alice.post(:status_message, :text => "hello", :public => false, :to => 'all')
get :post, :id => status.id
get :post, :guid => status.id
response.status = 302
end
it 'redirects to the proper show page if the user has visibility of the post' do
status = alice.post(:status_message, :text => "hello", :public => true, :to => 'all')
sign_in bob
get :post, :id => status.id
get :post, :guid => status.id
response.should be_redirect
end
it 'responds with diaspora xml if format is xml' do
status = alice.post(:status_message, :text => "hello", :public => true, :to => 'all')
get :post, :guid => status.guid, :format => :xml
response.body.should == status.to_diaspora_xml
end
# We want to be using guids from now on for this post route, but do not want to break
# preexisiting permalinks. We can assume a guid is 8 characters long as we have
# guids set to hex(8) since we started using them.
context 'id/guid switch' do
before do
@status = alice.post(:status_message, :text => "hello", :public => true, :to => 'all')
end
it 'assumes guids less than 8 chars are ids and not guids' do
Post.should_receive(:where).with(hash_including(:id => @status.id)).and_return(Post)
get :post, :guid => @status.id
response.status= 200
end
it 'assumes guids more than (or equal to) 8 chars are actually guids' do
Post.should_receive(:where).with(hash_including(:guid => @status.guid)).and_return(Post)
get :post, :guid => @status.guid
response.status= 200
end
end
end
describe '#hcard' do

View file

@ -0,0 +1,39 @@
require 'spec_helper'
describe ResharesController do
describe '#create' do
it 'requires authentication' do
post :create, :format => :js
response.should_not be_success
end
context 'with an authenticated user' do
before do
sign_in :user, bob
@post_guid = Factory(:status_message, :public => true).guid
@controller.stub(:current_user).and_return(bob)
end
it 'succeeds' do
post :create, :format => :js, :root_guid => @post_guid
response.should be_success
end
it 'creates a reshare' do
expect{
post :create, :format => :js, :root_guid => @post_guid
}.should change(Reshare, :count).by(1)
end
it 'after save, calls add to streams' do
bob.should_receive(:add_to_streams)
post :create, :format => :js, :root_guid => @post_guid
end
it 'calls dispatch' do
bob.should_receive(:dispatch_post).with(anything, hash_including(:additional_subscribers))
post :create, :format => :js, :root_guid => @post_guid
end
end
end
end

View file

@ -19,7 +19,7 @@ end
Factory.define :person do |p|
p.sequence(:diaspora_handle) { |n| "bob-person-#{n}#{r_str}@aol.com" }
p.sequence(:url) { |n| "http://google-#{n}#{r_str}.com/" }
p.sequence(:url) { |n| AppConfig[:pod_url] }
p.serialized_public_key OpenSSL::PKey::RSA.generate(1024).public_key.export
p.after_build do |person|
person.profile ||= Factory.build(:profile, :person => person)
@ -41,6 +41,7 @@ Factory.define :like do |x|
end
Factory.define :user do |u|
u.getting_started false
u.sequence(:username) { |n| "bob#{n}#{r_str}" }
u.sequence(:email) { |n| "bob#{n}#{r_str}@pivotallabs.com" }
u.password "bluepin7"
@ -91,6 +92,11 @@ Factory.define(:photo) do |p|
end
end
Factory.define :reshare do |r|
r.association(:root, :public => true, :factory => :status_message)
r.association(:author, :factory => :person)
end
Factory.define :service do |service|
service.nickname "sirrobertking"
service.type "Services::Twitter"

View file

@ -0,0 +1,15 @@
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the ResharesHelper. For example:
#
# describe ResharesHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# helper.concat_strings("this","that").should == "this that"
# end
# end
# end
describe ResharesHelper do
pending "add some examples to (or delete) #{__FILE__}"
end

View file

@ -23,10 +23,20 @@ describe Postzord::Dispatch do
zord.instance_variable_get(:@object).should == @sm
end
it 'sets @subscribers from object' do
@sm.should_receive(:subscribers).and_return(@subscribers)
zord = Postzord::Dispatch.new(alice, @sm)
zord.instance_variable_get(:@subscribers).should == @subscribers
context 'setting @subscribers' do
it 'sets @subscribers from object' do
@sm.should_receive(:subscribers).and_return(@subscribers)
zord = Postzord::Dispatch.new(alice, @sm)
zord.instance_variable_get(:@subscribers).should == @subscribers
end
it 'accepts additional subscribers from opts' do
new_person = Factory(:person)
@sm.should_receive(:subscribers).and_return(@subscribers)
zord = Postzord::Dispatch.new(alice, @sm, :additional_subscribers => new_person)
zord.instance_variable_get(:@subscribers).should == @subscribers | [new_person]
end
end
it 'sets the @sender_person object' do

View file

@ -41,7 +41,7 @@ describe Person do
context 'local people' do
it 'uses the pod config url to set the diaspora_handle' do
new_person = User.build(:username => "foo123", :email => "foo123@example.com", :password => "password", :password_confirmation => "password").person
new_person.diaspora_handle.should == "foo123@#{AppConfig[:pod_uri].host}"
new_person.diaspora_handle.should == "foo123@#{AppConfig[:pod_uri].authority}"
end
end

151
spec/models/reshare_spec.rb Normal file
View file

@ -0,0 +1,151 @@
require 'spec_helper'
describe Reshare do
include ActionView::Helpers::UrlHelper
include Rails.application.routes.url_helpers
def controller
mock()
end
it 'has a valid Factory' do
Factory(:reshare).should be_valid
end
it 'requires root' do
reshare = Factory.build(:reshare, :root => nil)
reshare.should_not be_valid
end
it 'require public root' do
Factory.build(:reshare, :root => Factory.build(:status_message, :public => false)).should_not be_valid
end
it 'forces public' do
Factory(:reshare, :public => false).public.should be_true
end
describe "#receive" do
before do
@reshare = Factory.create(:reshare, :root => Factory(:status_message, :author => bob.person, :public => true))
@root = @reshare.root
@reshare.receive(@root.author.owner, @reshare.author)
end
it 'increments the reshare count' do
@root.resharers.count.should == 1
end
it 'adds the resharer to the re-sharers of the post' do
@root.resharers.should include(@reshare.author)
end
end
describe "XML" do
before do
@reshare = Factory(:reshare)
@xml = @reshare.to_xml.to_s
end
context 'serialization' do
it 'serializes root_diaspora_id' do
@xml.should include("root_diaspora_id")
@xml.should include(@reshare.author.diaspora_handle)
end
it 'serializes root_guid' do
@xml.should include("root_guid")
@xml.should include(@reshare.root.guid)
end
end
context 'marshalling' do
context 'local' do
before do
@original_author = @reshare.root.author
@root_object = @reshare.root
end
it 'marshals the guid' do
Reshare.from_xml(@xml).root_guid.should == @root_object.guid
end
it 'fetches the root post from root_guid' do
Reshare.from_xml(@xml).root.should == @root_object
end
it 'fetches the root author from root_diaspora_id' do
Reshare.from_xml(@xml).root.author.should == @original_author
end
end
context 'remote' do
before do
@root_object = @reshare.root
@root_object.delete
end
it 'fetches the root author from root_diaspora_id' do
@original_profile = @reshare.root.author.profile.dup
@reshare.root.author.profile.delete
@original_author = @reshare.root.author.dup
@reshare.root.author.delete
@original_author.profile = @original_profile
wf_prof_mock = mock
wf_prof_mock.should_receive(:fetch).and_return(@original_author)
Webfinger.should_receive(:new).and_return(wf_prof_mock)
response = mock
response.stub(:body).and_return(@root_object.to_diaspora_xml)
Faraday.default_connection.should_receive(:get).with(@original_author.url + public_post_path(:guid => @root_object.guid, :format => "xml")).and_return(response)
Reshare.from_xml(@xml)
end
context 'saving the post' do
before do
response = mock
response.stub(:body).and_return(@root_object.to_diaspora_xml)
Faraday.default_connection.stub(:get).with(@reshare.root.author.url + public_post_path(:guid => @root_object.guid, :format => "xml")).and_return(response)
end
it 'fetches the root post from root_guid' do
root = Reshare.from_xml(@xml).root
[:text, :guid, :diaspora_handle, :type, :public].each do |attr|
root.send(attr).should == @reshare.root.send(attr)
end
end
it 'correctly saves the type' do
Reshare.from_xml(@xml).root.reload.type.should == "StatusMessage"
end
it 'correctly sets the author' do
@original_author = @reshare.root.author
Reshare.from_xml(@xml).root.reload.author.reload.should == @original_author
end
it 'verifies that the author of the post received is the same as the author in the reshare xml' do
@original_author = @reshare.root.author.dup
@xml = @reshare.to_xml.to_s
different_person = Factory.create(:person)
wf_prof_mock = mock
wf_prof_mock.should_receive(:fetch).and_return(different_person)
Webfinger.should_receive(:new).and_return(wf_prof_mock)
different_person.stub(:url).and_return(@original_author.url)
lambda{
Reshare.from_xml(@xml)
}.should raise_error /^Diaspora ID \(.+\) in the root does not match the Diaspora ID \(.+\) specified in the reshare!$/
end
end
end
end
end
end

View file

@ -8,7 +8,7 @@ describe Retraction do
before do
@aspect = alice.aspects.first
alice.contacts.create(:person => eve.person, :aspects => [@aspect])
@post = alice.post :status_message, :text => "Destroy!", :to => @aspect.id
@post = alice.post(:status_message, :public => true, :text => "Destroy!", :to => @aspect.id)
end
describe 'serialization' do
@ -20,12 +20,24 @@ describe Retraction do
end
describe '#subscribers' do
it 'returns the subscribers to the post for all objects other than person' do
retraction = Retraction.for(@post)
obj = retraction.instance_variable_get(:@object)
wanted_subscribers = obj.subscribers(alice)
obj.should_receive(:subscribers).with(alice).and_return(wanted_subscribers)
retraction.subscribers(alice).map{|s| s.id}.should =~ wanted_subscribers.map{|s| s.id}
context 'posts' do
before do
@retraction = Retraction.for(@post)
@obj = @retraction.instance_variable_get(:@object)
@wanted_subscribers = @obj.subscribers(alice)
end
it 'returns the subscribers to the post for all objects other than person' do
@retraction.subscribers(alice).map(&:id).should =~ @wanted_subscribers.map(&:id)
end
it 'does not return the authors of reshares' do
@post.reshares << Factory.create(:reshare, :root => @post, :author => bob.person)
@post.save!
@wanted_subscribers -= [bob.person]
@retraction.subscribers(alice).map(&:id).should =~ @wanted_subscribers.map(&:id)
end
end
context 'setting subscribers' do

View file

@ -0,0 +1,47 @@
require 'spec_helper'
describe SignedRetraction do
before do
@post = Factory(:status_message, :author => bob.person, :public => true)
@resharer = Factory(:user)
@post.reshares << Factory.create(:reshare, :root => @post, :author => @resharer.person)
@post.save!
end
describe '#perform' do
it "dispatches the retraction onward to recipients of the recipient's reshare" do
retraction = SignedRetraction.build(bob, @post)
onward_retraction = retraction.dup
retraction.should_receive(:dup).and_return(onward_retraction)
dis = mock
Postzord::Dispatch.should_receive(:new).with(@resharer, onward_retraction).and_return(dis)
dis.should_receive(:post)
retraction.perform(@resharer)
end
it 'relays the retraction onward even if the post does not exist' do
remote_post = Factory(:status_message, :public => true)
bob.post(:reshare, :root_guid => remote_post.guid)
alice.post(:reshare, :root_guid => remote_post.guid)
remote_retraction = SignedRetraction.new.tap{|r|
r.target_type = remote_post.type
r.target_guid = remote_post.guid
r.sender = remote_post.author
r.stub!(:target_author_signature_valid?).and_return(true)
}
remote_retraction.dup.perform(bob)
Post.exists?(:id => remote_post.id).should be_false
dis = mock
Postzord::Dispatch.should_receive(:new){ |sender, retraction|
sender.should == alice
retraction.sender.should == alice.person
dis
}
dis.should_receive(:post)
remote_retraction.perform(alice)
end
end
end

View file

@ -114,13 +114,13 @@ describe User do
alice.email = eve.email
alice.should_not be_valid
end
it "requires a vaild email address" do
alice.email = "somebody@anywhere"
alice.should_not be_valid
end
end
describe "of unconfirmed_email" do
it "unconfirmed_email address can be nil/blank" do
alice.unconfirmed_email = nil
@ -134,7 +134,7 @@ describe User do
alice.unconfirmed_email = "new@email.com"
alice.should be_valid
end
it "requires a vaild unconfirmed_email address" do
alice.unconfirmed_email = "somebody@anywhere"
alice.should_not be_valid
@ -679,7 +679,7 @@ describe User do
user.confirm_email_token.size.should eql(30)
end
end
describe '#mail_confirm_email' do
it 'enqueues a mail job on user with unconfirmed email' do
user.update_attribute(:unconfirmed_email, "alice@newmail.com")
@ -712,14 +712,14 @@ describe User do
user.unconfirmed_email.should_not eql(nil)
user.confirm_email_token.should_not eql(nil)
end
it 'returns false and does not change anything on blank token' do
user.confirm_email("").should eql(false)
user.email.should_not eql("alice@newmail.com")
user.unconfirmed_email.should_not eql(nil)
user.confirm_email_token.should_not eql(nil)
end
it 'returns false and does not change anything on blank token' do
user.confirm_email(nil).should eql(false)
user.email.should_not eql("alice@newmail.com")
@ -752,4 +752,43 @@ describe User do
end
end
end
describe '#retract' do
before do
@retraction = mock
@post = Factory(:status_message, :author => bob.person, :public => true)
end
context "posts" do
before do
SignedRetraction.stub(:build).and_return(@retraction)
@retraction.stub(:perform)
end
it 'sends a retraction' do
dispatcher = mock
Postzord::Dispatch.should_receive(:new).with(bob, @retraction, anything()).and_return(dispatcher)
dispatcher.should_receive(:post)
bob.retract(@post)
end
it 'adds resharers of target post as additional subsctibers' do
person = Factory(:person)
reshare = Factory(:reshare, :root => @post, :author => person)
@post.reshares << reshare
dispatcher = mock
Postzord::Dispatch.should_receive(:new).with(bob, @retraction, {:additional_subscribers => [person]}).and_return(dispatcher)
dispatcher.should_receive(:post)
bob.retract(@post)
end
it 'performs the retraction' do
pending
end
end
end
end

View file

@ -0,0 +1,76 @@
require 'spec_helper'
unless Server.all.empty?
describe "reposting" do
before(:all) do
WebMock::Config.instance.allow_localhost = true
enable_typhoeus
#Server.all.each{|s| s.kill if s.running?}
#Server.all.each{|s| s.run}
end
after(:all) do
disable_typhoeus
#Server.all.each{|s| s.kill if s.running?}
#sleep(1)
#Server.all.each{|s| puts "Server at port #{s.port} still running." if s.running?}
WebMock::Config.instance.allow_localhost = false
end
before do
Server.all.each{|s| s.truncate_database; }
@original_post = nil
Server[0].in_scope do
original_poster = Factory.create(:user_with_aspect, :username => "original_poster")
resharer = Factory.create(:user_with_aspect, :username => "resharer")
connect_users_with_aspects(original_poster, resharer)
@original_post = original_poster.post(:status_message,
:public => true,
:text => "Awesome Sauce!",
:to => 'all')
end
Server[1].in_scope do
recipient = Factory.create(:user_with_aspect, :username => "recipient")
end
Server[0].in_scope do
r = User.find_by_username("resharer")
person = Webfinger.new("recipient@localhost:#{Server[1].port}").fetch
r.share_with(person, r.aspects.first)
end
Server[1].in_scope do
r = User.find_by_username("recipient")
person = Webfinger.new("resharer@localhost:#{Server[0].port}").fetch
r.share_with(person, r.aspects.first)
end
Server[0].in_scope do
r = User.find_by_username("resharer")
r.post(:reshare, :root_guid => @original_post.guid, :to => 'all')
end
end
it 'fetches the original post from the root server' do
Server[1].in_scope do
Post.exists?(:guid => @original_post.guid).should be_true
end
end
it 'relays the retraction for the root post to recipients of the reshare' do
Server[0].in_scope do
poster = User.find_by_username 'original_poster'
fantasy_resque do
poster.retract @original_post
end
end
Server[1].in_scope do
Post.exists?(:guid => @original_post.guid).should be_false
end
end
end
end

View file

@ -1,14 +1,18 @@
#This is a spec for the class that runs the servers used in the other multi-server specs
require 'spec_helper'
WebMock::Config.instance.allow_localhost = true
if Server.all && Server.all.first && Server.all.first.running?
unless Server.all.empty?
describe Server do
before(:all) do
WebMock::Config.instance.allow_localhost = true
#Server.all.each{|s| s.kill if s.running?}
#Server.all.each{|s| s.run}
end
after(:all) do
#Server.all.each{|s| s.kill if s.running?}
#sleep(1)
#Server.all.each{|s| puts "Server at port #{s.port} still running." if s.running?}
WebMock::Config.instance.allow_localhost = false
end
@ -25,12 +29,12 @@ if Server.all && Server.all.first && Server.all.first.running?
it 'takes an environment' do
server = Server.new("integration_1")
server.env.should == "integration_1"
server.port.should == ActiveRecord::Base.configurations["integration_1"]["app_server_port"]
end
end
describe "#running?" do
it "verifies that the server is running" do
Server.new("integration_1").running?.should be_true
server = Server.new("integration_1")
server.running?.should be_true
end
end
describe '#db' do
@ -52,4 +56,3 @@ if Server.all && Server.all.first && Server.all.first.running?
end
end
end
WebMock::Config.instance.allow_localhost = false

View file

@ -1,5 +1,10 @@
#This class is for running servers in the background during integration testing. This will not run on Windows.
class Server
def self.[] index
self.all[index]
end
def self.all
@servers ||= ActiveRecord::Base.configurations.keys.select{
|k| k.include?("integration")
@ -8,9 +13,46 @@ class Server
attr_reader :port, :env
def initialize(env)
@config = ActiveRecord::Base.configurations[env]
@port = @config["app_server_port"]
@db_config = ActiveRecord::Base.configurations[env]
@app_config = (YAML.load_file AppConfig.source)[env]
@port = URI::parse(@app_config["pod_url"]).port
@env = env
ensure_database_setup
end
def ensure_database_setup
`cd #{Rails.root} && RAILS_ENV=#{@env} bundle exec rake db:create`
tables_exist = self.db do
ActiveRecord::Base.connection.tables.include?("users")
end
if tables_exist
truncate_database
else
`cd #{Rails.root} && RAILS_ENV=#{@env} bundle exec rake db:schema:load`
end
end
def run
@pid = fork do
Process.exec "cd #{Rails.root} && RAILS_ENV=#{@env} bundle exec #{run_command}"# 2> /dev/null"
end
end
def kill
puts "I am trying to kill the server"
`kill -9 #{get_pid}`
end
def run_command
"rails s -p #{@port}"
end
def get_pid
@pid = lambda {
processes = `ps ax -o pid,command | grep "#{run_command}"`.split("\n")
processes = processes.select{|p| !p.include?("grep") }
processes.first.split(" ").first
}.call
end
def running?
@ -25,8 +67,12 @@ class Server
def db
former_env = Rails.env
ActiveRecord::Base.establish_connection(env)
yield
ActiveRecord::Base.establish_connection(former_env)
begin
result = yield
ensure
ActiveRecord::Base.establish_connection(former_env)
end
result
end
def truncate_database
@ -34,4 +80,18 @@ class Server
DatabaseCleaner.clean_with(:truncation)
end
end
def in_scope
pod_url = "http://localhost:#{@port}/"
old_pod_url = AppConfig[:pod_url]
AppConfig[:pod_url] = pod_url
begin
result = db do
yield
end
ensure
AppConfig[:pod_url] = old_pod_url
end
result
end
end

View file

@ -1,4 +1,8 @@
class User
include Rails.application.routes.url_helpers
def default_url_options
{:host => AppConfig[:pod_url]}
end
alias_method :share_with_original, :share_with
@ -16,7 +20,9 @@ class User
aspects = self.aspects_from_ids(opts[:to])
add_to_streams(p, aspects)
dispatch_post(p, :to => opts[:to])
dispatch_opts = {:url => post_url(p), :to => opts[:to]}
dispatch_opts.merge!(:additional_subscribers => p.root.author) if class_name == :reshare
dispatch_post(p, dispatch_opts)
end
unless opts[:created_at]
p.created_at = Time.now - 1