simplepub 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|