simplepub 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/LICENSE.txt +22 -0
- data/README.md +146 -0
- data/Rakefile +8 -0
- data/app/assets/javascripts/simplepub.js +97 -0
- data/bin/simplepub +5 -0
- data/lib/generators/simplepub/install_generator.rb +17 -0
- data/lib/simplepub/cli/app.rb +13 -0
- data/lib/simplepub/cli/config.rb +33 -0
- data/lib/simplepub/cli/server.rb +64 -0
- data/lib/simplepub/cli.rb +7 -0
- data/lib/simplepub/configuration.rb +48 -0
- data/lib/simplepub/engine.rb +16 -0
- data/lib/simplepub/error.rb +7 -0
- data/lib/simplepub/faye_extension.rb +43 -0
- data/lib/simplepub/interface_methods.rb +61 -0
- data/lib/simplepub/rack_config.ru +10 -0
- data/lib/simplepub/version.rb +3 -0
- data/lib/simplepub/view_helpers.rb +22 -0
- data/lib/simplepub.rb +24 -0
- data/lib/templates/simplepub.ru +10 -0
- data/lib/templates/simplepub.yml +10 -0
- data/simplepub.gemspec +29 -0
- data/spec/fixtures/simplepub.yml +8 -0
- data/spec/javascripts/helpers/SpecHelper.js +15 -0
- data/spec/javascripts/simplepub_spec.js +165 -0
- data/spec/javascripts/support/jasmine.yml +76 -0
- data/spec/javascripts/support/jasmine_config.rb +23 -0
- data/spec/javascripts/support/jasmine_helper.rb +11 -0
- data/spec/javascripts/support/jasmine_runner.rb +32 -0
- data/spec/simplepub/faye_extension_spec.rb +67 -0
- data/spec/simplepub_spec.rb +148 -0
- data/spec/spec_helper.rb +7 -0
- metadata +208 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
module Simplepub
|
2
|
+
module InterfaceMethods
|
3
|
+
# Publish the given data to a specific channel. This ends up sending
|
4
|
+
# a Net::HTTP POST request to the Faye server.
|
5
|
+
def publish_to(channel, data)
|
6
|
+
publish_message(message(channel, data))
|
7
|
+
end
|
8
|
+
|
9
|
+
# Sends the given message hash to the Faye server using Net::HTTP.
|
10
|
+
def publish_message(message)
|
11
|
+
raise Error, "No server specified, ensure simplepub.yml was loaded properly." unless config[:server]
|
12
|
+
url = URI.parse(config[:server])
|
13
|
+
|
14
|
+
form = Net::HTTP::Post.new(url.path.empty? ? '/' : url.path)
|
15
|
+
form.set_form_data(:message => message.to_json)
|
16
|
+
|
17
|
+
http = Net::HTTP.new(url.host, url.port)
|
18
|
+
http.use_ssl = url.scheme == "https"
|
19
|
+
|
20
|
+
http.ca_path = config.ca_path if config.ca_path?
|
21
|
+
http.ca_file = config.ca_file if config.ca_file?
|
22
|
+
|
23
|
+
http.start {|h| h.request(form)}
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns a message hash for sending to Faye
|
27
|
+
def message(channel, data)
|
28
|
+
message = {:channel => channel, :data => {:channel => channel}, :ext => {:simplepub_token => config[:secret_token]}}
|
29
|
+
if data.kind_of? String
|
30
|
+
message[:data][:eval] = data
|
31
|
+
else
|
32
|
+
message[:data][:data] = data
|
33
|
+
end
|
34
|
+
message
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns a subscription hash to pass to the Simplepub.sign call in JavaScript.
|
38
|
+
# Any options passed are merged to the hash.
|
39
|
+
def subscription(options = {})
|
40
|
+
sub = {:server => config[:server], :timestamp => (Time.now.to_f * 1000).round}.merge(options)
|
41
|
+
sub[:signature] = Digest::SHA1.hexdigest([config[:secret_token], sub[:channel], sub[:timestamp]].join)
|
42
|
+
sub
|
43
|
+
end
|
44
|
+
|
45
|
+
# Determine if the signature has expired given a timestamp.
|
46
|
+
def signature_expired?(timestamp)
|
47
|
+
timestamp < ((Time.now.to_f - config[:signature_expiration])*1000).round if config[:signature_expiration]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the Faye Rack application.
|
51
|
+
# Any options given are passed to the Faye::RackAdapter.
|
52
|
+
def faye_app(options = {})
|
53
|
+
options = {:mount => "/faye", :timeout => 45, :extensions => [FayeExtension.new]}.merge(options)
|
54
|
+
Faye::RackAdapter.new(options)
|
55
|
+
end
|
56
|
+
|
57
|
+
def rackup_file
|
58
|
+
File.expand_path('../rack_config.ru', __FILE__)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Simplepub
|
2
|
+
module ViewHelpers
|
3
|
+
# Publish the given data or block to the client by sending
|
4
|
+
# a Net::HTTP POST request to the Faye server. If a block
|
5
|
+
# or string is passed in, it is evaluated as JavaScript
|
6
|
+
# on the client. Otherwise it will be converted to JSON
|
7
|
+
# for use in a JavaScript callback.
|
8
|
+
def publish_to(channel, data = nil, &block)
|
9
|
+
Simplepub.publish_to(channel, data || capture(&block))
|
10
|
+
end
|
11
|
+
|
12
|
+
# Subscribe the client to the given channel. This generates
|
13
|
+
# some JavaScript calling Simplepub.sign with the subscription
|
14
|
+
# options.
|
15
|
+
def subscribe_to(channel)
|
16
|
+
subscription = Simplepub.subscription(:channel => channel)
|
17
|
+
content_tag "script", :type => "text/javascript" do
|
18
|
+
raw("Simplepub.sign(#{subscription.to_json});")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/simplepub.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "simplepub/version"
|
2
|
+
require 'json'
|
3
|
+
require "digest/sha1"
|
4
|
+
require "net/http"
|
5
|
+
require "net/https"
|
6
|
+
|
7
|
+
require "simplepub/error"
|
8
|
+
require "simplepub/configuration"
|
9
|
+
require "simplepub/interface_methods"
|
10
|
+
require "simplepub/faye_extension"
|
11
|
+
require "simplepub/engine" if defined? Rails
|
12
|
+
|
13
|
+
module Simplepub
|
14
|
+
extend Configuration
|
15
|
+
extend InterfaceMethods
|
16
|
+
|
17
|
+
reset_config
|
18
|
+
|
19
|
+
def self.templates_root
|
20
|
+
File.dirname(__FILE__) + "/templates"
|
21
|
+
end
|
22
|
+
|
23
|
+
autoload :Cli, 'simplepub/cli'
|
24
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Run with: rackup simplepub.ru -s thin -E production
|
2
|
+
require "bundler/setup"
|
3
|
+
require "yaml"
|
4
|
+
require "faye"
|
5
|
+
require "simplepub"
|
6
|
+
|
7
|
+
Faye::WebSocket.load_adapter('thin')
|
8
|
+
|
9
|
+
Simplepub.load_config(File.expand_path("../config/simplepub.yml", __FILE__), ENV["RAILS_ENV"] || "development")
|
10
|
+
run Simplepub.faye_app
|
@@ -0,0 +1,10 @@
|
|
1
|
+
development:
|
2
|
+
server: "http://localhost:9292/faye"
|
3
|
+
secret_token: "secret"
|
4
|
+
test:
|
5
|
+
server: "http://localhost:9292/faye"
|
6
|
+
secret_token: "secret"
|
7
|
+
production:
|
8
|
+
server: "http://example.com/faye"
|
9
|
+
secret_token: "<%= defined?(SecureRandom) ? SecureRandom.hex(32) : ActiveSupport::SecureRandom.hex(32) %>"
|
10
|
+
signature_expiration: 3600 # one hour
|
data/simplepub.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'simplepub/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "simplepub"
|
8
|
+
spec.version = Simplepub::VERSION
|
9
|
+
spec.authors = ["Jorge Calás Lozano", "Ryan Bates"]
|
10
|
+
spec.email = ["calas@qvitta.net", "ryan@railscasts.com"]
|
11
|
+
spec.description = "Simple private pub/sub messaging for Ruby using Faye."
|
12
|
+
spec.summary = "Simple private pub/sub messaging for Ruby."
|
13
|
+
spec.homepage = "https://speedyrails.com"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_dependency 'faye'
|
21
|
+
spec.add_dependency 'thor'
|
22
|
+
spec.add_dependency 'hashie'
|
23
|
+
spec.add_dependency 'thin'
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "rspec"
|
28
|
+
spec.add_development_dependency "jasmine"
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
beforeEach(function() {
|
2
|
+
this.addMatchers({
|
3
|
+
/**
|
4
|
+
// Example matcher:
|
5
|
+
//
|
6
|
+
// expect(player).not.toBePlaying(song);
|
7
|
+
|
8
|
+
toBePlaying: function(expectedSong) {
|
9
|
+
var player = this.actual;
|
10
|
+
return player.currentlyPlayingSong === expectedSong &&
|
11
|
+
player.isPlaying;
|
12
|
+
}
|
13
|
+
**/
|
14
|
+
});
|
15
|
+
});
|
@@ -0,0 +1,165 @@
|
|
1
|
+
describe("Simplepub", function() {
|
2
|
+
var pub, doc;
|
3
|
+
beforeEach(function() {
|
4
|
+
Faye = {}; // To simulate global Faye object
|
5
|
+
doc = {};
|
6
|
+
pub = buildSimplepub(doc);
|
7
|
+
});
|
8
|
+
|
9
|
+
it("adds a subscription callback", function() {
|
10
|
+
pub.subscribe("hello", "callback");
|
11
|
+
expect(pub.subscriptionCallbacks["hello"]).toEqual("callback");
|
12
|
+
});
|
13
|
+
|
14
|
+
it("has a fayeExtension which adds matching subscription signature and timestamp to outgoing message", function() {
|
15
|
+
var called = false;
|
16
|
+
var message = {channel: "/meta/subscribe", subscription: "hello"}
|
17
|
+
pub.subscriptions["hello"] = {signature: "abcd", timestamp: "1234"}
|
18
|
+
pub.fayeExtension.outgoing(message, function(message) {
|
19
|
+
expect(message.ext.simplepub_signature).toEqual("abcd");
|
20
|
+
expect(message.ext.simplepub_timestamp).toEqual("1234");
|
21
|
+
called = true;
|
22
|
+
});
|
23
|
+
expect(called).toBeTruthy();
|
24
|
+
});
|
25
|
+
|
26
|
+
it("evaluates javascript in message response", function() {
|
27
|
+
pub.handleResponse({eval: 'self.subscriptions.foo = "bar"'});
|
28
|
+
expect(pub.subscriptions.foo).toEqual("bar");
|
29
|
+
});
|
30
|
+
|
31
|
+
it("triggers callback matching message channel in response", function() {
|
32
|
+
var called = false;
|
33
|
+
pub.subscribe("test", function(data, channel) {
|
34
|
+
expect(data).toEqual("abcd");
|
35
|
+
expect(channel).toEqual("test");
|
36
|
+
called = true;
|
37
|
+
});
|
38
|
+
pub.handleResponse({channel: "test", data: "abcd"});
|
39
|
+
expect(called).toBeTruthy();
|
40
|
+
});
|
41
|
+
|
42
|
+
it("adds a faye subscription with response handler when signing", function() {
|
43
|
+
var faye = {subscribe: jasmine.createSpy()};
|
44
|
+
spyOn(pub, 'faye').andCallFake(function(callback) {
|
45
|
+
callback(faye);
|
46
|
+
});
|
47
|
+
var options = {server: "server", channel: "somechannel"};
|
48
|
+
pub.sign(options);
|
49
|
+
expect(faye.subscribe).toHaveBeenCalledWith("somechannel", pub.handleResponse);
|
50
|
+
expect(pub.subscriptions.server).toEqual("server");
|
51
|
+
expect(pub.subscriptions.somechannel).toEqual(options);
|
52
|
+
});
|
53
|
+
|
54
|
+
it("adds a faye subscription with response handler when signing", function() {
|
55
|
+
var faye = {subscribe: jasmine.createSpy()};
|
56
|
+
spyOn(pub, 'faye').andCallFake(function(callback) {
|
57
|
+
callback(faye);
|
58
|
+
});
|
59
|
+
var options = {server: "server", channel: "somechannel"};
|
60
|
+
pub.sign(options);
|
61
|
+
expect(faye.subscribe).toHaveBeenCalledWith("somechannel", pub.handleResponse);
|
62
|
+
expect(pub.subscriptions.server).toEqual("server");
|
63
|
+
expect(pub.subscriptions.somechannel).toEqual(options);
|
64
|
+
});
|
65
|
+
|
66
|
+
it("takes a callback for subscription object when signing", function(){
|
67
|
+
var faye = {subscribe: function(){ return "subscription"; }};
|
68
|
+
spyOn(pub, 'faye').andCallFake(function(callback) {
|
69
|
+
callback(faye);
|
70
|
+
});
|
71
|
+
var options = { server: "server", channel: "somechannel" };
|
72
|
+
options.subscription = jasmine.createSpy();
|
73
|
+
pub.sign(options);
|
74
|
+
expect(options.subscription).toHaveBeenCalledWith("subscription");
|
75
|
+
});
|
76
|
+
|
77
|
+
it("returns the subscription object for a subscribed channel", function(){
|
78
|
+
var faye = {subscribe: function(){ return "subscription"; }};
|
79
|
+
spyOn(pub, 'faye').andCallFake(function(callback) {
|
80
|
+
callback(faye);
|
81
|
+
});
|
82
|
+
var options = { server: "server", channel: "somechannel" };
|
83
|
+
pub.sign(options);
|
84
|
+
expect(pub.subscription("somechannel")).toEqual("subscription")
|
85
|
+
});
|
86
|
+
|
87
|
+
it("unsubscribes a channel by name", function(){
|
88
|
+
var sub = { cancel: jasmine.createSpy() };
|
89
|
+
var faye = {subscribe: function(){ return sub; }};
|
90
|
+
spyOn(pub, 'faye').andCallFake(function(callback) {
|
91
|
+
callback(faye);
|
92
|
+
});
|
93
|
+
var options = { server: "server", channel: "somechannel" };
|
94
|
+
pub.sign(options);
|
95
|
+
expect(pub.subscription("somechannel")).toEqual(sub);
|
96
|
+
pub.unsubscribe("somechannel");
|
97
|
+
expect(sub.cancel).toHaveBeenCalled();
|
98
|
+
expect(pub.subscription("somechannel")).toBeFalsy();
|
99
|
+
});
|
100
|
+
|
101
|
+
it("unsubscribes all channels", function(){
|
102
|
+
var created = 0;
|
103
|
+
var sub = function() {
|
104
|
+
created ++;
|
105
|
+
var sub = { cancel: function(){ created --; } };
|
106
|
+
return sub;
|
107
|
+
};
|
108
|
+
var faye = { subscribe: function(){ return sub(); }};
|
109
|
+
spyOn(pub, 'faye').andCallFake(function(callback) {
|
110
|
+
callback(faye);
|
111
|
+
});
|
112
|
+
pub.sign({server: "server", channel: "firstchannel"});
|
113
|
+
pub.sign({server: "server", channel: "secondchannel"});
|
114
|
+
expect(created).toEqual(2);
|
115
|
+
expect(pub.subscription("firstchannel")).toBeTruthy();
|
116
|
+
expect(pub.subscription("secondchannel")).toBeTruthy();
|
117
|
+
pub.unsubscribeAll()
|
118
|
+
expect(created).toEqual(0);
|
119
|
+
expect(pub.subscription("firstchannel")).toBeFalsy();
|
120
|
+
expect(pub.subscription("secondchannel")).toBeFalsy();
|
121
|
+
});
|
122
|
+
|
123
|
+
it("triggers faye callback function immediately when fayeClient is available", function() {
|
124
|
+
var called = false;
|
125
|
+
pub.fayeClient = "faye";
|
126
|
+
pub.faye(function(faye) {
|
127
|
+
expect(faye).toEqual("faye");
|
128
|
+
called = true;
|
129
|
+
});
|
130
|
+
expect(called).toBeTruthy();
|
131
|
+
});
|
132
|
+
|
133
|
+
it("adds fayeCallback when client and server aren't available", function() {
|
134
|
+
pub.faye("callback");
|
135
|
+
expect(pub.fayeCallbacks[0]).toEqual("callback");
|
136
|
+
});
|
137
|
+
|
138
|
+
it("adds a script tag loading faye js when the server is present", function() {
|
139
|
+
script = {};
|
140
|
+
doc.createElement = function() { return script; };
|
141
|
+
doc.documentElement = {appendChild: jasmine.createSpy()};
|
142
|
+
pub.subscriptions.server = "path/to/faye";
|
143
|
+
pub.faye("callback");
|
144
|
+
expect(pub.fayeCallbacks[0]).toEqual("callback");
|
145
|
+
expect(script.type).toEqual("text/javascript");
|
146
|
+
expect(script.src).toEqual("path/to/faye.js");
|
147
|
+
expect(script.onload).toEqual(pub.connectToFaye);
|
148
|
+
expect(doc.documentElement.appendChild).toHaveBeenCalledWith(script);
|
149
|
+
});
|
150
|
+
|
151
|
+
it("connects to faye server, adds extension, and executes callbacks", function() {
|
152
|
+
callback = jasmine.createSpy();
|
153
|
+
client = {addExtension: jasmine.createSpy()};
|
154
|
+
Faye.Client = function(server) {
|
155
|
+
expect(server).toEqual("server")
|
156
|
+
return client;
|
157
|
+
};
|
158
|
+
pub.subscriptions.server = "server";
|
159
|
+
pub.fayeCallbacks.push(callback);
|
160
|
+
pub.connectToFaye();
|
161
|
+
expect(pub.fayeClient).toEqual(client);
|
162
|
+
expect(client.addExtension).toHaveBeenCalledWith(pub.fayeExtension);
|
163
|
+
expect(callback).toHaveBeenCalledWith(client);
|
164
|
+
});
|
165
|
+
});
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# src_files
|
2
|
+
#
|
3
|
+
# Return an array of filepaths relative to src_dir to include before jasmine specs.
|
4
|
+
# Default: []
|
5
|
+
#
|
6
|
+
# EXAMPLE:
|
7
|
+
#
|
8
|
+
# src_files:
|
9
|
+
# - lib/source1.js
|
10
|
+
# - lib/source2.js
|
11
|
+
# - dist/**/*.js
|
12
|
+
#
|
13
|
+
src_files:
|
14
|
+
- app/assets/javascripts/simplepub.js
|
15
|
+
|
16
|
+
# stylesheets
|
17
|
+
#
|
18
|
+
# Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs.
|
19
|
+
# Default: []
|
20
|
+
#
|
21
|
+
# EXAMPLE:
|
22
|
+
#
|
23
|
+
# stylesheets:
|
24
|
+
# - css/style.css
|
25
|
+
# - stylesheets/*.css
|
26
|
+
#
|
27
|
+
stylesheets:
|
28
|
+
|
29
|
+
# helpers
|
30
|
+
#
|
31
|
+
# Return an array of filepaths relative to spec_dir to include before jasmine specs.
|
32
|
+
# Default: ["helpers/**/*.js"]
|
33
|
+
#
|
34
|
+
# EXAMPLE:
|
35
|
+
#
|
36
|
+
# helpers:
|
37
|
+
# - helpers/**/*.js
|
38
|
+
#
|
39
|
+
helpers:
|
40
|
+
|
41
|
+
# spec_files
|
42
|
+
#
|
43
|
+
# Return an array of filepaths relative to spec_dir to include.
|
44
|
+
# Default: ["**/*[sS]pec.js"]
|
45
|
+
#
|
46
|
+
# EXAMPLE:
|
47
|
+
#
|
48
|
+
# spec_files:
|
49
|
+
# - **/*[sS]pec.js
|
50
|
+
#
|
51
|
+
spec_files:
|
52
|
+
- "**/*[sS]pec.js"
|
53
|
+
- "**/*[sS]pec.coffee"
|
54
|
+
- "**/*[sS]pec.js.coffee"
|
55
|
+
|
56
|
+
# src_dir
|
57
|
+
#
|
58
|
+
# Source directory path. Your src_files must be returned relative to this path. Will use root if left blank.
|
59
|
+
# Default: project root
|
60
|
+
#
|
61
|
+
# EXAMPLE:
|
62
|
+
#
|
63
|
+
# src_dir: public
|
64
|
+
#
|
65
|
+
src_dir:
|
66
|
+
|
67
|
+
# spec_dir
|
68
|
+
#
|
69
|
+
# Spec directory path. Your spec_files must be returned relative to this path.
|
70
|
+
# Default: spec/javascripts
|
71
|
+
#
|
72
|
+
# EXAMPLE:
|
73
|
+
#
|
74
|
+
# spec_dir: spec/javascripts
|
75
|
+
#
|
76
|
+
spec_dir:
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Jasmine
|
2
|
+
class Config
|
3
|
+
|
4
|
+
# Add your overrides or custom config code here
|
5
|
+
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
# Note - this is necessary for rspec2, which has removed the backtrace
|
11
|
+
module Jasmine
|
12
|
+
class SpecBuilder
|
13
|
+
def declare_spec(parent, spec)
|
14
|
+
me = self
|
15
|
+
example_name = spec["name"]
|
16
|
+
@spec_ids << spec["id"]
|
17
|
+
backtrace = @example_locations[parent.description + " " + example_name]
|
18
|
+
parent.it example_name, {} do
|
19
|
+
me.report_spec(spec["id"])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#Use this file to set/override Jasmine configuration options
|
2
|
+
#You can remove it if you don't need it.
|
3
|
+
#This file is loaded *after* jasmine.yml is interpreted.
|
4
|
+
#
|
5
|
+
#Example: using a different boot file.
|
6
|
+
#Jasmine.configure do |config|
|
7
|
+
# config.boot_dir = '/absolute/path/to/boot_dir'
|
8
|
+
# config.boot_files = lambda { ['/absolute/path/to/boot_dir/file.js'] }
|
9
|
+
#end
|
10
|
+
#
|
11
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
$:.unshift(ENV['JASMINE_GEM_PATH']) if ENV['JASMINE_GEM_PATH'] # for gem testing purposes
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'jasmine'
|
5
|
+
jasmine_config_overrides = File.expand_path(File.join(File.dirname(__FILE__), 'jasmine_config.rb'))
|
6
|
+
require jasmine_config_overrides if File.exist?(jasmine_config_overrides)
|
7
|
+
if Jasmine::Dependencies.rspec2?
|
8
|
+
require 'rspec'
|
9
|
+
else
|
10
|
+
require 'spec'
|
11
|
+
end
|
12
|
+
|
13
|
+
jasmine_config = Jasmine::Config.new
|
14
|
+
spec_builder = Jasmine::SpecBuilder.new(jasmine_config)
|
15
|
+
|
16
|
+
should_stop = false
|
17
|
+
|
18
|
+
if Jasmine::Dependencies.rspec2?
|
19
|
+
RSpec.configuration.after(:suite) do
|
20
|
+
spec_builder.stop if should_stop
|
21
|
+
end
|
22
|
+
else
|
23
|
+
Spec::Runner.configure do |config|
|
24
|
+
config.after(:suite) do
|
25
|
+
spec_builder.stop if should_stop
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
spec_builder.start
|
31
|
+
should_stop = true
|
32
|
+
spec_builder.declare_suites
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Simplepub::FayeExtension do
|
4
|
+
before(:each) do
|
5
|
+
Simplepub.reset_config
|
6
|
+
@faye = Simplepub::FayeExtension.new
|
7
|
+
@message = {"channel" => "/meta/subscribe", "ext" => {}}
|
8
|
+
end
|
9
|
+
|
10
|
+
it "adds an error on an incoming subscription with a bad signature" do
|
11
|
+
@message["subscription"] = "hello"
|
12
|
+
@message["ext"]["simplepub_signature"] = "bad"
|
13
|
+
@message["ext"]["simplepub_timestamp"] = "123"
|
14
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
15
|
+
message["error"].should eq("Incorrect signature.")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "has no error when the signature matches the subscription" do
|
19
|
+
sub = Simplepub.subscription(:channel => "hello")
|
20
|
+
@message["subscription"] = sub[:channel]
|
21
|
+
@message["ext"]["simplepub_signature"] = sub[:signature]
|
22
|
+
@message["ext"]["simplepub_timestamp"] = sub[:timestamp]
|
23
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
24
|
+
message["error"].should be_nil
|
25
|
+
end
|
26
|
+
|
27
|
+
it "has an error when signature just expired" do
|
28
|
+
Simplepub.config[:signature_expiration] = 1
|
29
|
+
sub = Simplepub.subscription(:timestamp => 123, :channel => "hello")
|
30
|
+
@message["subscription"] = sub[:channel]
|
31
|
+
@message["ext"]["simplepub_signature"] = sub[:signature]
|
32
|
+
@message["ext"]["simplepub_timestamp"] = sub[:timestamp]
|
33
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
34
|
+
message["error"].should eq("Signature has expired.")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "has an error when trying to publish to a custom channel with a bad token" do
|
38
|
+
Simplepub.config[:secret_token] = "good"
|
39
|
+
@message["channel"] = "/custom/channel"
|
40
|
+
@message["ext"]["simplepub_token"] = "bad"
|
41
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
42
|
+
message["error"].should eq("Incorrect token.")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "raises an exception when attempting to call a custom channel without a secret_token set" do
|
46
|
+
@message["channel"] = "/custom/channel"
|
47
|
+
@message["ext"]["simplepub_token"] = "bad"
|
48
|
+
lambda {
|
49
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
50
|
+
}.should raise_error("No secret_token config set, ensure simplepub.yml is loaded properly.")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "has no error on other meta calls" do
|
54
|
+
@message["channel"] = "/meta/connect"
|
55
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
56
|
+
message["error"].should be_nil
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should not let message carry the private pub token after server's validation" do
|
60
|
+
Simplepub.config[:secret_token] = "good"
|
61
|
+
@message["channel"] = "/custom/channel"
|
62
|
+
@message["ext"]["simplepub_token"] = Simplepub.config[:secret_token]
|
63
|
+
message = @faye.incoming(@message, lambda { |m| m })
|
64
|
+
message['ext']["simplepub_token"].should be_nil
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|