scales-monitor 0.0.1.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +6 -0
  3. data/LICENSE +22 -0
  4. data/Rakefile +8 -0
  5. data/bin/scales-monitor +8 -0
  6. data/lib/scales-monitor.rb +11 -0
  7. data/lib/scales-monitor/app/Gemfile +3 -0
  8. data/lib/scales-monitor/app/Rakefile +0 -0
  9. data/lib/scales-monitor/app/app/assets/images/.gitignore +0 -0
  10. data/lib/scales-monitor/app/app/assets/images/glyphicons-halflings-white.png +0 -0
  11. data/lib/scales-monitor/app/app/assets/images/glyphicons-halflings.png +0 -0
  12. data/lib/scales-monitor/app/app/assets/javascripts/app/config/config.js.coffee +8 -0
  13. data/lib/scales-monitor/app/app/assets/javascripts/app/config/routes.js.coffee +15 -0
  14. data/lib/scales-monitor/app/app/assets/javascripts/app/controllers/.gitignore +0 -0
  15. data/lib/scales-monitor/app/app/assets/javascripts/app/controllers/log.jst.coffee +58 -0
  16. data/lib/scales-monitor/app/app/assets/javascripts/app/controllers/machines.js.coffee +60 -0
  17. data/lib/scales-monitor/app/app/assets/javascripts/app/controllers/queues.js.coffee +55 -0
  18. data/lib/scales-monitor/app/app/assets/javascripts/app/controllers/resources.js.coffee +128 -0
  19. data/lib/scales-monitor/app/app/assets/javascripts/app/index.js.coffee +8 -0
  20. data/lib/scales-monitor/app/app/assets/javascripts/app/models/.gitignore +0 -0
  21. data/lib/scales-monitor/app/app/assets/javascripts/app/models/socket.js.coffee +9 -0
  22. data/lib/scales-monitor/app/app/assets/javascripts/app/views/.gitignore +0 -0
  23. data/lib/scales-monitor/app/app/assets/javascripts/app/views/_format_bar.jst.eco +10 -0
  24. data/lib/scales-monitor/app/app/assets/javascripts/app/views/_machine.jst.eco +11 -0
  25. data/lib/scales-monitor/app/app/assets/javascripts/app/views/_queue_item.jst.eco +10 -0
  26. data/lib/scales-monitor/app/app/assets/javascripts/app/views/_resource.jst.eco +5 -0
  27. data/lib/scales-monitor/app/app/assets/javascripts/app/views/_top.jst.eco +1 -0
  28. data/lib/scales-monitor/app/app/assets/javascripts/app/views/log.jst.eco +11 -0
  29. data/lib/scales-monitor/app/app/assets/javascripts/app/views/machines.jst.eco +33 -0
  30. data/lib/scales-monitor/app/app/assets/javascripts/app/views/queues.jst.eco +33 -0
  31. data/lib/scales-monitor/app/app/assets/javascripts/app/views/resources.jst.eco +31 -0
  32. data/lib/scales-monitor/app/app/assets/javascripts/application.js.coffee +16 -0
  33. data/lib/scales-monitor/app/app/assets/javascripts/lib/bootstrap.js +1825 -0
  34. data/lib/scales-monitor/app/app/assets/javascripts/lib/jquery.js +4 -0
  35. data/lib/scales-monitor/app/app/assets/javascripts/lib/jquery.timeago.js +152 -0
  36. data/lib/scales-monitor/app/app/assets/javascripts/lib/json2.js +485 -0
  37. data/lib/scales-monitor/app/app/assets/javascripts/lib/spine/ajax.coffee +208 -0
  38. data/lib/scales-monitor/app/app/assets/javascripts/lib/spine/list.coffee +43 -0
  39. data/lib/scales-monitor/app/app/assets/javascripts/lib/spine/local.coffee +16 -0
  40. data/lib/scales-monitor/app/app/assets/javascripts/lib/spine/manager.coffee +83 -0
  41. data/lib/scales-monitor/app/app/assets/javascripts/lib/spine/relation.coffee +144 -0
  42. data/lib/scales-monitor/app/app/assets/javascripts/lib/spine/route.coffee +145 -0
  43. data/lib/scales-monitor/app/app/assets/javascripts/lib/spine/spine.coffee +537 -0
  44. data/lib/scales-monitor/app/app/assets/stylesheets/application.css.scss +58 -0
  45. data/lib/scales-monitor/app/app/assets/stylesheets/bootstrap-docs.css +845 -0
  46. data/lib/scales-monitor/app/app/assets/stylesheets/bootstrap-responsive.css +815 -0
  47. data/lib/scales-monitor/app/app/assets/stylesheets/bootstrap.css +4983 -0
  48. data/lib/scales-monitor/app/app/views/index.html.erb +45 -0
  49. data/lib/scales-monitor/app/config.ru +3 -0
  50. data/lib/scales-monitor/app/config/config.rb +17 -0
  51. data/lib/scales-monitor/app/config/routes.rb +13 -0
  52. data/lib/scales-monitor/app/public/assets/.gitignore +0 -0
  53. data/lib/scales-monitor/app/public/assets/application.css +6709 -0
  54. data/lib/scales-monitor/app/public/assets/application.js +5563 -0
  55. data/lib/scales-monitor/app/public/assets/glyphicons-halflings-white.png +0 -0
  56. data/lib/scales-monitor/app/public/assets/glyphicons-halflings.png +0 -0
  57. data/lib/scales-monitor/app/public/index.html +45 -0
  58. data/lib/scales-monitor/base.rb +17 -0
  59. data/lib/scales-monitor/boot/autoload.rb +5 -0
  60. data/lib/scales-monitor/boot/initializers/goliath.rb +3 -0
  61. data/lib/scales-monitor/monitor.rb +33 -0
  62. data/lib/scales-monitor/version.rb +5 -0
  63. data/lib/scales-monitor/web_socket.rb +180 -0
  64. data/scales-monitor.gemspec +24 -0
  65. data/spec/gem_spec.rb +7 -0
  66. data/spec/helper.rb +50 -0
  67. data/spec/monitor_spec.rb +50 -0
  68. data/spec/web_socket_spec.rb +103 -0
  69. metadata +174 -0
@@ -0,0 +1,45 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Scales | Monitor</title>
5
+ <link href='assets/application.css' media='all' rel='stylesheet' type='text/css'>
6
+ <script src='assets/application.js' type='text/javascript'></script>
7
+ </head>
8
+ <body>
9
+
10
+ <div class="navbar navbar-fixed-top">
11
+ <div class="navbar-inner">
12
+ <div class="container">
13
+ <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
14
+ <span class="icon-bar"></span>
15
+ <span class="icon-bar"></span>
16
+ <span class="icon-bar"></span>
17
+ </button>
18
+ <a class="brand" href="/">Scales Monitor</a>
19
+ <div class="nav-collapse collapse">
20
+ <ul class="nav">
21
+ <li class="" id="nav_machines">
22
+ <a href="#/machines">Machines</a>
23
+ </li>
24
+ <li class="" id="nav_queues">
25
+ <a href="#/queues">Queues</a>
26
+ </li>
27
+ <li class="" id="nav_resources">
28
+ <a href="#/resources">Resources</a>
29
+ </li>
30
+ <li class="" id="nav_log">
31
+ <a href="#/log">Log</a>
32
+ </li>
33
+ </ul>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </div>
38
+
39
+ <div class="container">
40
+ <div id="app"></div>
41
+ </div>
42
+
43
+
44
+ </body>
45
+ </html>
@@ -0,0 +1,17 @@
1
+ module Scales
2
+ module Monitor
3
+ class << self
4
+
5
+ def run!
6
+ ARGV << "--stdout" << "--environment" << "#{Scales.env}"
7
+
8
+ monitor = WebSocket.new
9
+ runner = Goliath::Runner.new(ARGV, monitor)
10
+ runner.app = Goliath::Rack::Builder.build(WebSocket, monitor)
11
+ runner.load_plugins(WebSocket.plugins)
12
+ runner.run
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ require 'scales-core'
2
+
3
+ autoload :Goliath, "scales-monitor/boot/initializers/goliath"
4
+
5
+ require 'scales-server/content_type'
@@ -0,0 +1,3 @@
1
+ require 'goliath'
2
+ require 'goliath/websocket'
3
+ require 'goliath/test_helper'
@@ -0,0 +1,33 @@
1
+ module Scales
2
+ module Monitor
3
+
4
+ PUBLIC_APP_DIR = File.expand_path("../app/public", __FILE__)
5
+
6
+ module Monitor
7
+
8
+ @@cache = nil
9
+
10
+ class << self
11
+
12
+ def serve(path)
13
+ cache_files! if @@cache.nil?
14
+ @@cache[path]
15
+ end
16
+
17
+ private
18
+
19
+ def cache_files!
20
+ @@cache = {}
21
+
22
+ Dir.chdir(PUBLIC_APP_DIR)
23
+ Dir["*.html", "assets/*"].each{ |file| @@cache["/#{file}"] = File.read(file) }
24
+ Dir.chdir(Scales.pwd)
25
+
26
+ @@cache["/"] = @@cache["/index.html"]
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ module Scales
2
+ module Monitor
3
+ VERSION = "0.0.1.beta.1"
4
+ end
5
+ end
@@ -0,0 +1,180 @@
1
+ module Scales
2
+ module Monitor
3
+
4
+ class WebSocket < Goliath::WebSocket
5
+ use Server::ContentType, 'html'
6
+
7
+ def on_open(env)
8
+ send_initial_statuses(env)
9
+ setup_subscription
10
+ add_to_subscribers(env)
11
+ end
12
+
13
+ def on_error(env, error)
14
+ env.logger.error error
15
+ end
16
+
17
+ def on_message(env, msg)
18
+ env.stream_send(msg)
19
+ end
20
+
21
+ def on_close(env)
22
+ remove_from_subscribers(env) if env['REQUEST_PATH'] == "/socket"
23
+ end
24
+
25
+ def response(env)
26
+ path = env['REQUEST_PATH']
27
+
28
+ if path == '/socket'
29
+ super(env)
30
+ else
31
+ [200, {}, Monitor.serve(path)]
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def setup_subscription
38
+ return if @subscribed
39
+ @subscribers = []
40
+
41
+ events = Storage::Async.new_connection!
42
+ events.subscribe("scales_monitor_events")
43
+ events.on(:message) do |channel, message|
44
+ @subscribers.each { |subscriber| subscriber.stream_send(message) }
45
+ end
46
+ @subscribed = true
47
+ end
48
+
49
+ def add_to_subscribers(env)
50
+ @subscribers << env
51
+ end
52
+
53
+ def remove_from_subscribers(env)
54
+ @subscribers.delete(env)
55
+ end
56
+
57
+ def send_initial_statuses(env)
58
+ server_statuses.each{ |server| env.stream_send(server) }
59
+ cache_statuses.each{ |cache| env.stream_send(cache) }
60
+ worker_statuses.each{ |worker| env.stream_send(worker) }
61
+
62
+ request_queue.each{ |request| env.stream_send(request) }
63
+ response_queue.each{ |request| env.stream_send(response) }
64
+
65
+ push_resources.each{ |resource| env.stream_send(resource) }
66
+ push_partials.each{ |partial| env.stream_send(partial) }
67
+ end
68
+
69
+ def server_statuses
70
+ servers = Storage::Async.connection.keys("scales_server_*")
71
+ return [] if servers.empty?
72
+
73
+ Storage::Async.connection.mget(*servers)
74
+ end
75
+
76
+ def cache_statuses
77
+ info = Storage::Async.connection.info
78
+ data = {
79
+ :id => redis_value("run_id", info)[0..16],
80
+ :key => "",
81
+ :type => "cache_started",
82
+ :spawned_at => Time.now.to_i - redis_value("uptime_in_seconds", info).to_i,
83
+ :env => Scales.env,
84
+ :ip => Scales.config.host,
85
+ :port => Scales.config.port
86
+ }
87
+ [JSON.generate(data)]
88
+ end
89
+
90
+ def worker_statuses
91
+ workers = Storage::Async.connection.keys("scales_worker_*")
92
+ return [] if workers.empty?
93
+
94
+ Storage::Async.connection.mget(*workers)
95
+ end
96
+
97
+ def request_queue
98
+ requests = Storage::Async.connection.llen("scales_request_queue")
99
+ return [] if requests == 0
100
+
101
+ data = []
102
+ Storage::Async.connection.lrange("scales_request_queue", 0, requests).each do |request|
103
+ job = JSON.parse(request)
104
+ data << {
105
+ :id => job['scales.id'],
106
+ :server_id => nil,
107
+ :type => "server_put_request_in_queue",
108
+ :path => job['PATH_INFO'],
109
+ :method => job['REQUEST_METHOD']
110
+ }.to_json
111
+ end
112
+ data
113
+ end
114
+
115
+ def response_queue
116
+ responses = Storage::Async.connection.keys("scales_response_*")
117
+ return [] if responses.empty?
118
+
119
+ data = []
120
+ responses.each do |response_key|
121
+ response = Storage::Async.connection.lindex(response_key, 0)
122
+ response = JSON.parse(response)
123
+ data << {
124
+ :id => response[1]['scales.id'],
125
+ :worker_id => nil,
126
+ :type => "worker_put_response_in_queue",
127
+ :path => response[1]['PATH_INFO'],
128
+ :method => response[1]['REQUEST_METHOD'],
129
+ :status => response[0]
130
+ }.to_json
131
+ end
132
+ data
133
+ end
134
+
135
+ def push_resources
136
+ resources = Storage::Async.connection.keys("scales_resource_/*")
137
+ return [] if resources.empty?
138
+
139
+ data = []
140
+ resources.each do |resource|
141
+ data << {
142
+ :path => resource.gsub("scales_resource_", ""),
143
+ :format => format(resource),
144
+ :type => "push_resource"
145
+ }.to_json
146
+ end
147
+ data
148
+ end
149
+
150
+ def push_partials
151
+ partials = Storage::Async.connection.keys("scales_partial_*")
152
+ return [] if partials.empty?
153
+
154
+ data = []
155
+ partials.each do |partial|
156
+ data << {
157
+ :path => partial.gsub("scales_partial_", ""),
158
+ :format => format(partial),
159
+ :type => "push_partial"
160
+ }.to_json
161
+ end
162
+ data
163
+ end
164
+
165
+ private
166
+
167
+ def redis_value(value, info)
168
+ info.scan(/^#{value}:.*/).first.split(":").last
169
+ end
170
+
171
+ def format(path)
172
+ format = "HTML"
173
+ Scales::Helper::ContentTypes::TYPES.each { |aformat, type| format = aformat.to_s.upcase and break if path =~ /\.#{aformat}(\?|$)/ }
174
+ format
175
+ end
176
+
177
+ end
178
+
179
+ end
180
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/scales-monitor/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Thomas Fankhauser"]
6
+ gem.email = ["tommylefunk@googlemail.com"]
7
+ gem.description = %q{Super Scale Caching Framework - Monitor Server}
8
+ gem.summary = %q{Monitoring in the form of a website that updates in real-time using a websocket connected to the main system.}
9
+ gem.homepage = "http://itscales.org"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "scales-monitor"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Scales::Monitor::VERSION
17
+
18
+ # Dependencies
19
+ gem.add_dependency "rake", ">= 0.9.2.2"
20
+ gem.add_dependency "rspec", ">= 2.11"
21
+ gem.add_dependency "scales-core", Scales::Monitor::VERSION
22
+ gem.add_dependency "scales-server", Scales::Monitor::VERSION
23
+ gem.add_dependency "goliath", ">= 1.0.0.beta.1"
24
+ end
data/spec/gem_spec.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ describe Scales::Monitor do
4
+ it "should have a version" do
5
+ Scales::Monitor::VERSION.should_not be_nil
6
+ end
7
+ end
data/spec/helper.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'scales-monitor'
2
+ require "net/http"
3
+ require "uri"
4
+
5
+ module Helpers
6
+
7
+ def async
8
+ if EM.reactor_running?
9
+ yield
10
+ else
11
+ out = nil
12
+ EM.synchrony do
13
+ out = yield
14
+ EM.stop
15
+ end
16
+ out
17
+ end
18
+ end
19
+
20
+ def get url
21
+ uri = URI.parse(url)
22
+ http = Net::HTTP.new(uri.host, uri.port)
23
+ request = Net::HTTP::Get.new(uri.request_uri)
24
+
25
+ http.request(request)
26
+ end
27
+
28
+ def post url, data = {}
29
+ uri = URI.parse(url)
30
+ Net::HTTP.post_form(uri, data)
31
+ end
32
+
33
+ def fixture(file)
34
+ File.read(File.expand_path("../fixtures/#{file}", __FILE__))
35
+ end
36
+
37
+ def squeeze string
38
+ string.gsub(/(\n|\t|\r)/, ' ').gsub(/>\s*</, '><').squeeze(' ').strip
39
+ end
40
+
41
+ end
42
+
43
+ RSpec.configure do |config|
44
+ config.include Helpers
45
+ config.include Goliath::TestHelper
46
+ config.treat_symbols_as_metadata_keys_with_true_values = true
47
+ config.before(:suite) do
48
+ Scales::Storage::Sync.flushall!
49
+ end
50
+ end
@@ -0,0 +1,50 @@
1
+ require 'helper'
2
+
3
+ describe Scales::Monitor::Monitor do
4
+
5
+ it "serves paths from cache" do
6
+ described_class.serve("/").should have_at_least(100).characters
7
+ end
8
+
9
+ end
10
+
11
+ describe Scales::Monitor do
12
+
13
+ before :all do
14
+ @server_pid = fork do
15
+ trap('INT') do
16
+ exit 0
17
+ end
18
+
19
+ Scales::Storage::Sync.force_reconnect!
20
+
21
+ ARGV << "-p" << "9000"
22
+ Scales::Monitor.run!
23
+ end
24
+
25
+ puts "Waiting for monitor server to spawn ..."
26
+ sleep 2
27
+ end
28
+
29
+ after :all do
30
+ Process.kill("INT", @server_pid)
31
+ Process.wait
32
+ end
33
+
34
+ it "should answer requests" do
35
+ EventMachine.run {
36
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:9000/').aget
37
+
38
+ http.errback {
39
+ EventMachine.stop
40
+ fail "Request did not work!"
41
+ }
42
+ http.callback {
43
+ http.response_header.status.should == 200
44
+ http.response.should have_at_least(100).characters
45
+ EventMachine.stop
46
+ }
47
+ }
48
+ end
49
+
50
+ end
@@ -0,0 +1,103 @@
1
+ require 'helper'
2
+
3
+ describe Scales::Monitor::WebSocket do
4
+
5
+ context "static files" do
6
+
7
+ it "serves cached html files" do
8
+ with_api(described_class, {:verbose => true, :log_stdout => true}) do |server|
9
+ get_request(:path => "/") do |client|
10
+ client.response_header['CONTENT_TYPE'].should =~ %r{^text/html}
11
+ end
12
+ end
13
+ end
14
+
15
+ it "serves cached json files" do
16
+ with_api(described_class, {:verbose => true, :log_stdout => true}) do |server|
17
+ get_request(:path => "/assets/application.js") do |client|
18
+ client.response_header['CONTENT_TYPE'].should =~ %r{^application/javascript}
19
+ end
20
+ end
21
+ end
22
+
23
+ end
24
+
25
+ context "initial statuses" do
26
+
27
+ it "sends servers" do
28
+ with_api(described_class, {:verbose => true, :log_stdout => true}) do |server|
29
+ Scales::Storage::Async.connection.set("scales_server_725634", "server1")
30
+ Scales::Storage::Async.connection.set("scales_server_8768fk", "server2")
31
+
32
+ servers = described_class.new.instance_eval{ server_statuses }
33
+ servers.include?("server1").should be_true
34
+ servers.include?("server2").should be_true
35
+ EM.stop
36
+ end
37
+ end
38
+
39
+ it "sends workers" do
40
+ with_api(described_class, {:verbose => true, :log_stdout => true}) do |server|
41
+ Scales::Storage::Async.connection.set("scales_worker_725634", "worker1")
42
+ Scales::Storage::Async.connection.set("scales_worker_8768fk", "worker2")
43
+
44
+ workers = described_class.new.instance_eval{ worker_statuses }
45
+ workers.include?("worker1").should be_true
46
+ workers.include?("worker2").should be_true
47
+ EM.stop
48
+ end
49
+ end
50
+
51
+ it "sends requests" do
52
+ with_api(described_class, {:verbose => true, :log_stdout => true}) do |server|
53
+ json = "{\"scales.id\":\"4f57fcec795b6a6158796a1958e781f0\",\"SERVER_NAME\":\"0.0.0.0\",\"SERVER_PORT\":\"3005\",\"REQUEST_METHOD\":\"POST\",\"QUERY_STRING\":\"\",\"PATH_INFO\":\"/tracks\",\"HTTP_HOST\":\"0.0.0.0:3005\",\"HTTP_USER_AGENT\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/536.19 (KHTML, like Gecko) Version/6.0 Safari/536.19\",\"HTTP_ACCEPT\":\"text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8\",\"HTTP_ORIGIN\":\"http://0.0.0.0:3005\",\"HTTP_REFERER\":\"http://0.0.0.0:3005/tracks/new\",\"HTTP_ACCEPT_LANGUAGE\":\"en-us\",\"HTTP_ACCEPT_ENCODING\":\"gzip, deflate\",\"HTTP_CONNECTION\":\"keep-alive\",\"HTTP_VERSION\":\"1.1\",\"rack.version\":[1,0],\"rack.url_scheme\":null,\"rack.input\":\"utf8=%E2%9C%93&authenticity_token=gniWYAZpl67rSI0VYfrpoBXJ3Ipv9ZEv9VY9FSbyoDM%3D&track%5Bname%5D=Next+one&track%5Bartist%5D=Okay+here&commit=Create+Track\"}"
54
+ Scales::Storage::Async.connection.lpush("scales_request_queue", json)
55
+ Scales::Storage::Async.connection.lpush("scales_request_queue", json)
56
+
57
+ requests = described_class.new.instance_eval{ request_queue }
58
+ requests.should have_at_least(2).requests
59
+ EM.stop
60
+ end
61
+ end
62
+
63
+ it "sends responds" do
64
+ with_api(described_class, {:verbose => true, :log_stdout => true}) do |server|
65
+ json = "[302,{\"Location\":\"http://0.0.0.0:3005/tracks/170\",\"Content-Type\":\"text/html; charset=utf-8\",\"Set-Cookie\":\"_app_session=BAh7B0kiCmZsYXNoBjoGRUZvOiVBY3Rpb25EaXNwYXRjaDo6Rmxhc2g6OkZsYXNoSGFzaAk6CkB1c2VkbzoIU2V0BjoKQGhhc2h7ADoMQGNsb3NlZEY6DUBmbGFzaGVzewY6C25vdGljZUkiJFRyYWNrIHdhcyBzdWNjZXNzZnVsbHkgY3JlYXRlZC4GOwBGOglAbm93MEkiD3Nlc3Npb25faWQGOwBGSSIlZDkxNDA4OTFmMWVhZTJlNGU2MmM3MmRkOTA4NDZjODUGOwBU--fd4912d39bd51eedcd5bf0519a939e5b38317b34; path=/; HttpOnly\",\"scales.id\":\"155a255dd3604fa2e39469e30aef3206\"},\"<html><body>You are being <a href=\\\"http://0.0.0.0:3005/tracks/170\\\">redirected</a>.</body></html>\"]"
66
+ Scales::Storage::Async.connection.lpush("scales_response_155a255dd3604fa2e39469e30aef3206", json)
67
+ Scales::Storage::Async.connection.lpush("scales_response_155a255dd3604fa2e39469e30aef3207", json)
68
+
69
+ responses = described_class.new.instance_eval{ response_queue }
70
+ responses.should have_at_least(2).responses
71
+ EM.stop
72
+ end
73
+ end
74
+
75
+ it "sends push resources" do
76
+ with_api(described_class, {:verbose => true, :log_stdout => true}) do |server|
77
+ Scales::Storage::Async.flushall!
78
+ Scales::Storage::Async.set_content "/tracks", "test 1"
79
+ Scales::Storage::Async.set_content "/tracks/2", "test 2"
80
+
81
+ resources = described_class.new.instance_eval{ push_resources }
82
+ resources.should have_at_least(2).resources
83
+ EM.stop
84
+ end
85
+ end
86
+
87
+ it "sends push partials" do
88
+ with_api(described_class, {:verbose => true, :log_stdout => true}) do |server|
89
+ Scales::Storage::Async.flushall!
90
+ Scales::Storage::Async.set_content "tracks", "test 1"
91
+ Scales::Storage::Async.set_content "tracks/2", "test 2"
92
+
93
+ partials = described_class.new.instance_eval{ push_partials }
94
+ partials.should have_at_least(2).partials
95
+ EM.stop
96
+ end
97
+ end
98
+
99
+ end
100
+
101
+
102
+
103
+ end