scales-monitor 0.0.1.beta.1

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.
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