cloud-crowd 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/cloud-crowd.gemspec +6 -6
- data/config/config.example.yml +23 -10
- data/lib/cloud-crowd.rb +4 -4
- data/lib/cloud_crowd/action.rb +24 -23
- data/lib/cloud_crowd/asset_store.rb +3 -1
- data/lib/cloud_crowd/asset_store/cloudfiles_store.rb +41 -0
- data/lib/cloud_crowd/asset_store/s3_store.rb +9 -7
- data/lib/cloud_crowd/models/node_record.rb +27 -26
- data/lib/cloud_crowd/models/work_unit.rb +35 -28
- data/lib/cloud_crowd/node.rb +43 -43
- data/lib/cloud_crowd/schema.rb +7 -7
- data/lib/cloud_crowd/server.rb +35 -30
- data/public/css/admin_console.css +25 -62
- data/public/js/admin_console.js +53 -70
- data/test/acceptance/test_server.rb +14 -16
- data/test/unit/test_action.rb +17 -15
- data/views/operations_center.erb +26 -13
- metadata +94 -59
@@ -22,7 +22,7 @@ body {
|
|
22
22
|
width: 236px; height: 91px;
|
23
23
|
background: url(../images/logo.png);
|
24
24
|
}
|
25
|
-
|
25
|
+
|
26
26
|
#disconnected {
|
27
27
|
position: absolute;
|
28
28
|
top: 122px; right: 15px;
|
@@ -42,70 +42,27 @@ body {
|
|
42
42
|
margin-right: 3px;
|
43
43
|
}
|
44
44
|
|
45
|
-
#
|
45
|
+
#stats {
|
46
46
|
position: absolute;
|
47
|
-
top: 16px; left: 327px; right:
|
47
|
+
top: 16px; left: 327px; right: 22px;
|
48
48
|
height: 77px;
|
49
49
|
overflow: hidden;
|
50
|
+
color: #454545;
|
50
51
|
}
|
51
|
-
#
|
52
|
-
|
53
|
-
|
54
|
-
bottom: 8px; right: 8px;
|
55
|
-
color: #999;
|
56
|
-
display: none;
|
52
|
+
#stats tr.data td {
|
53
|
+
padding: 0 50px 0 0;
|
54
|
+
font-size: 50px;
|
57
55
|
}
|
58
|
-
#
|
59
|
-
|
56
|
+
#stats tr.data td.last {
|
57
|
+
padding-right: 0;
|
60
58
|
}
|
61
|
-
|
62
|
-
|
63
|
-
left: 0; right: 0; top: 0;
|
64
|
-
height: 75px;
|
65
|
-
border: 1px solid #5c5c5c;
|
66
|
-
-moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px;
|
67
|
-
background: transparent url(../images/queue_fill.png) repeat-x 0px -1px;
|
68
|
-
}
|
69
|
-
#queue.no_jobs #queue_fill {
|
70
|
-
opacity: 0.3;
|
59
|
+
#stats tr.data td div {
|
60
|
+
border-bottom: 1px solid #777;
|
71
61
|
}
|
72
|
-
#
|
73
|
-
|
74
|
-
|
75
|
-
height: 75px;
|
76
|
-
background: blue;
|
77
|
-
float: left;
|
78
|
-
overflow: hidden;
|
79
|
-
-moz-border-radius: 10px;
|
80
|
-
-webkit-border-radius: 10px;
|
62
|
+
#stats tr.labels td {
|
63
|
+
font-size: 10px;
|
64
|
+
text-transform: uppercase;
|
81
65
|
}
|
82
|
-
#queue .completion {
|
83
|
-
position: absolute;
|
84
|
-
bottom: -1px;
|
85
|
-
height: 30px;
|
86
|
-
background: black;
|
87
|
-
border: 1px solid white;
|
88
|
-
-moz-border-radius: 10px; -webkit-border-radius: 10px;
|
89
|
-
opacity: 0.5;
|
90
|
-
overflow: hidden;
|
91
|
-
}
|
92
|
-
#queue .completion.zero {
|
93
|
-
border: 0;
|
94
|
-
}
|
95
|
-
#queue .percent_complete {
|
96
|
-
position: absolute;
|
97
|
-
bottom: 8px; left: 8px;
|
98
|
-
color: #c7c7c7;
|
99
|
-
z-index: 10;
|
100
|
-
}
|
101
|
-
#queue .job_id {
|
102
|
-
color: #333;
|
103
|
-
font-size: 14px;
|
104
|
-
position: absolute;
|
105
|
-
top: 8px; left: 8px;
|
106
|
-
z-index: 10;
|
107
|
-
}
|
108
|
-
|
109
66
|
#sidebar {
|
110
67
|
position: absolute;
|
111
68
|
top: 120px; left: 10px; bottom: 10px;
|
@@ -134,11 +91,17 @@ body {
|
|
134
91
|
}
|
135
92
|
#sidebar_header {
|
136
93
|
position: absolute;
|
137
|
-
width:
|
94
|
+
width: 283px;
|
138
95
|
top: 5px; left: 8px;
|
139
96
|
color: #404040;
|
140
97
|
text-shadow: 0px 1px 1px #eee;
|
141
98
|
}
|
99
|
+
#tail_log {
|
100
|
+
text-transform: none;
|
101
|
+
text-decoration: underline;
|
102
|
+
cursor: pointer;
|
103
|
+
float: right;
|
104
|
+
}
|
142
105
|
#sidebar_header.no_nodes .no_nodes,
|
143
106
|
#sidebar_header .has_nodes {
|
144
107
|
display: block;
|
@@ -185,7 +148,7 @@ body {
|
|
185
148
|
border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px;
|
186
149
|
background-color: #ccc;
|
187
150
|
}
|
188
|
-
|
151
|
+
|
189
152
|
#worker_info {
|
190
153
|
position: absolute;
|
191
154
|
width: 231px; height: 79px;
|
@@ -204,15 +167,15 @@ body {
|
|
204
167
|
background: url(../images/worker_info_loading.gif) no-repeat right bottom;
|
205
168
|
width: 45px; height: 9px;
|
206
169
|
}
|
207
|
-
#worker_info.awake #worker_details,
|
170
|
+
#worker_info.awake #worker_details,
|
208
171
|
#worker_sleeping {
|
209
172
|
display: block;
|
210
173
|
}
|
211
|
-
#worker_details, #worker_info.loading #worker_details,
|
174
|
+
#worker_details, #worker_info.loading #worker_details,
|
212
175
|
#worker_info.loading #worker_sleeping, #worker_info.awake #worker_sleeping {
|
213
176
|
display: none;
|
214
177
|
}
|
215
|
-
|
178
|
+
|
216
179
|
#graphs {
|
217
180
|
position: absolute;
|
218
181
|
padding: 17px 15px 15px 17px;
|
data/public/js/admin_console.js
CHANGED
@@ -4,22 +4,22 @@
|
|
4
4
|
// Think about pulling in the DCJS framework, instead of just raw jQuery here.
|
5
5
|
// Leaving it hacked together like this just cries out for templates, dunnit?
|
6
6
|
window.Console = {
|
7
|
-
|
7
|
+
|
8
8
|
// Maximum number of data points to record and graph.
|
9
9
|
MAX_DATA_POINTS : 100,
|
10
|
-
|
10
|
+
|
11
11
|
// Milliseconds between polling the central server for updates to Job progress.
|
12
|
-
POLL_INTERVAL :
|
13
|
-
|
12
|
+
POLL_INTERVAL : 6000,
|
13
|
+
|
14
14
|
// Default speed for all animations.
|
15
15
|
ANIMATION_SPEED : 300,
|
16
|
-
|
16
|
+
|
17
17
|
// Keep this in sync with the map in cloud-crowd.rb
|
18
|
-
DISPLAY_STATUS_MAP : ['unknown', 'processing', 'succeeded', 'failed', 'splitting', 'merging'],
|
19
|
-
|
18
|
+
DISPLAY_STATUS_MAP : ['unknown', 'processing', 'succeeded', 'failed', 'splitting', 'merging'],
|
19
|
+
|
20
20
|
// Images to preload
|
21
21
|
PRELOAD_IMAGES : ['images/server_error.png'],
|
22
|
-
|
22
|
+
|
23
23
|
// All options for drawing the system graphs.
|
24
24
|
GRAPH_OPTIONS : {
|
25
25
|
xaxis : {mode : 'time', timeformat : '%M:%S'},
|
@@ -31,37 +31,40 @@ window.Console = {
|
|
31
31
|
NODES_COLOR : '#1870ab',
|
32
32
|
WORKERS_COLOR : '#45a4e5',
|
33
33
|
WORK_UNITS_COLOR : '#ffba14',
|
34
|
-
|
34
|
+
|
35
35
|
// Starting the console begins polling the server.
|
36
36
|
initialize : function() {
|
37
|
-
this._jobsHistory
|
38
|
-
this._nodesHistory
|
39
|
-
this._workersHistory
|
40
|
-
this._workUnitsHistory
|
41
|
-
this._histories
|
42
|
-
this.
|
43
|
-
this.
|
37
|
+
this._jobsHistory = [];
|
38
|
+
this._nodesHistory = [];
|
39
|
+
this._workersHistory = [];
|
40
|
+
this._workUnitsHistory = [];
|
41
|
+
this._histories = [this._jobsHistory, this._nodesHistory, this._workersHistory, this._workUnitsHistory];
|
42
|
+
this._workerInfo = $('#worker_info');
|
43
|
+
this._jobCountEl = $('#job_count');
|
44
|
+
this._workUnitCountEl = $('#work_unit_count');
|
45
|
+
this._nodeCountEl = $('#node_count');
|
46
|
+
this._workerCountEl = $('#worker_count');
|
44
47
|
this._disconnected = $('#disconnected');
|
45
48
|
$(window).bind('resize', Console.renderGraphs);
|
46
49
|
$('#nodes .worker').live('click', Console.getWorkerInfo);
|
50
|
+
$('#tail_log').bind('click', Console.tailLog);
|
47
51
|
$('#workers_legend').css({background : this.WORKERS_COLOR});
|
48
52
|
$('#nodes_legend').css({background : this.NODES_COLOR});
|
49
53
|
this.getStatus();
|
50
54
|
$.each(this.PRELOAD_IMAGES, function(){ var i = new Image(); i.src = this; });
|
51
55
|
},
|
52
|
-
|
56
|
+
|
53
57
|
// Request the lastest status of all jobs and workers, re-render or update
|
54
58
|
// the DOM to reflect.
|
55
59
|
getStatus : function() {
|
56
60
|
$.ajax({url : 'status', dataType : 'json', success : function(resp) {
|
57
|
-
Console.
|
61
|
+
Console._jobCount = resp.job_count;
|
58
62
|
Console._nodes = resp.nodes;
|
59
63
|
Console._workUnitCount = resp.work_unit_count;
|
60
64
|
Console._workerCount = Console.countWorkers();
|
61
65
|
Console.recordDataPoint();
|
62
66
|
if (Console._disconnected.is(':visible')) Console._disconnected.fadeOut(Console.ANIMATION_SPEED);
|
63
|
-
|
64
|
-
Console.renderJobs();
|
67
|
+
Console.renderStats();
|
65
68
|
Console.renderNodes();
|
66
69
|
Console.renderGraphs();
|
67
70
|
setTimeout(Console.getStatus, Console.POLL_INTERVAL);
|
@@ -70,52 +73,32 @@ window.Console = {
|
|
70
73
|
setTimeout(Console.getStatus, Console.POLL_INTERVAL);
|
71
74
|
}});
|
72
75
|
},
|
73
|
-
|
76
|
+
|
77
|
+
// Fetch the last 100 lines of log from the server.
|
78
|
+
tailLog : function() {
|
79
|
+
$.ajax({url : 'log', success : function(resp) {
|
80
|
+
var win = window.open('');
|
81
|
+
win.document.open();
|
82
|
+
win.document.write('<pre>' + resp + '</pre>');
|
83
|
+
win.document.close();
|
84
|
+
}});
|
85
|
+
},
|
86
|
+
|
74
87
|
// Count the total number of workers in the current list of nodes.
|
75
88
|
countWorkers : function() {
|
76
89
|
var sum = 0;
|
77
90
|
for (var i=0; i < this._nodes.length; i++) sum += this._nodes[i].workers.length;
|
78
91
|
return sum;
|
79
92
|
},
|
80
|
-
|
81
|
-
// Render
|
82
|
-
|
83
|
-
this.
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
updateJob : function(job, jobEl) {
|
88
|
-
jobEl.animate({width : job.width + '%'}, this.ANIMATION_SPEED);
|
89
|
-
var completion = $('.completion', jobEl);
|
90
|
-
if (job.percent_complete > 0) completion.removeClass('zero');
|
91
|
-
completion.animate({width : job.percent_complete + '%'}, this.ANIMATION_SPEED);
|
92
|
-
$('.percent_complete', jobEl).html(job.percent_complete + '%');
|
93
|
-
},
|
94
|
-
|
95
|
-
// Render all jobs, calculating relative widths and completions.
|
96
|
-
renderJobs : function() {
|
97
|
-
var totalUnits = 0;
|
98
|
-
var totalWidth = this._queue.width();
|
99
|
-
var jobIds = [];
|
100
|
-
$.each(this._jobs, function() {
|
101
|
-
jobIds.push(this.id);
|
102
|
-
totalUnits += this.work_units;
|
103
|
-
});
|
104
|
-
$.each($('.job'), function() {
|
105
|
-
var el = this;
|
106
|
-
if (jobIds.indexOf(parseInt(el.id.replace(/\D/g, ''), 10)) < 0) {
|
107
|
-
$(el).animate({width : '0%'}, Console.ANIMATION_SPEED - 50, 'linear', function() {
|
108
|
-
$(el).remove();
|
109
|
-
});
|
110
|
-
}
|
111
|
-
});
|
112
|
-
$.each(this._jobs, function() {
|
113
|
-
this.width = (this.work_units / totalUnits) * 100;
|
114
|
-
var jobEl = $('#job_' + this.id);
|
115
|
-
jobEl[0] ? Console.updateJob(this, jobEl) : Console.renderJob(this);
|
116
|
-
});
|
93
|
+
|
94
|
+
// Render the numeric statistic counts.
|
95
|
+
renderStats : function() {
|
96
|
+
this._jobCountEl.text(this._jobCount);
|
97
|
+
this._workUnitCountEl.text(this._workUnitCount);
|
98
|
+
this._nodeCountEl.text(this._nodes.length);
|
99
|
+
this._workerCountEl.text(this._workerCount);
|
117
100
|
},
|
118
|
-
|
101
|
+
|
119
102
|
// Re-render all workers from scratch each time.
|
120
103
|
// This method is desperately in need of Javascript templates...
|
121
104
|
renderNodes : function() {
|
@@ -123,7 +106,7 @@ window.Console = {
|
|
123
106
|
var nc = this._nodes.length, wc = this._workerCount;
|
124
107
|
$('.has_nodes', header).html(nc + " Node" + (nc != 1 ? 's' : '') + " / " + wc + " Worker" + (wc != 1 ? 's' : ''));
|
125
108
|
header.toggleClass('no_nodes', this._nodes.length <= 0);
|
126
|
-
$('#nodes').html($.map(this._nodes, function(node) {
|
109
|
+
$('#nodes').html($.map(this._nodes, function(node) {
|
127
110
|
var html = "";
|
128
111
|
var extra = node.status == 'busy' ? ' <span class="busy">[busy]</span>' : '';
|
129
112
|
html += '<div class="node ' + node.status + '">' + node.host + extra + '</div>';
|
@@ -134,19 +117,19 @@ window.Console = {
|
|
134
117
|
return html;
|
135
118
|
}).join(''));
|
136
119
|
},
|
137
|
-
|
120
|
+
|
138
121
|
// Record the current state and re-render all graphs.
|
139
122
|
recordDataPoint : function() {
|
140
123
|
var timestamp = (new Date()).getTime();
|
141
|
-
this._jobsHistory.push([timestamp, this.
|
124
|
+
this._jobsHistory.push([timestamp, this._jobCount]);
|
142
125
|
this._nodesHistory.push([timestamp, this._nodes.length]);
|
143
126
|
this._workersHistory.push([timestamp, this._workerCount]);
|
144
127
|
this._workUnitsHistory.push([timestamp, this._workUnitCount]);
|
145
|
-
$.each(this._histories, function() {
|
146
|
-
if (this.length > Console.MAX_DATA_POINTS) this.shift();
|
128
|
+
$.each(this._histories, function() {
|
129
|
+
if (this.length > Console.MAX_DATA_POINTS) this.shift();
|
147
130
|
});
|
148
131
|
},
|
149
|
-
|
132
|
+
|
150
133
|
// Convert our recorded data points into a format Flot can understand.
|
151
134
|
renderGraphs : function() {
|
152
135
|
$.plot($('#work_units_graph'), [
|
@@ -160,7 +143,7 @@ window.Console = {
|
|
160
143
|
{label : 'Workers', color : Console.WORKERS_COLOR, data : Console._workersHistory}
|
161
144
|
], Console.GRAPH_OPTIONS);
|
162
145
|
},
|
163
|
-
|
146
|
+
|
164
147
|
// Request the Worker info from the central server.
|
165
148
|
getWorkerInfo : function(e) {
|
166
149
|
e.stopImmediatePropagation();
|
@@ -173,7 +156,7 @@ window.Console = {
|
|
173
156
|
$(document).bind('click', Console.hideWorkerInfo);
|
174
157
|
return false;
|
175
158
|
},
|
176
|
-
|
159
|
+
|
177
160
|
// When we receieve worker info, update the bubble.
|
178
161
|
renderWorkerInfo : function(resp) {
|
179
162
|
var info = Console._workerInfo;
|
@@ -185,13 +168,13 @@ window.Console = {
|
|
185
168
|
$('.job_id', info).html(resp.job_id);
|
186
169
|
$('.work_unit_id', info).html(resp.id);
|
187
170
|
},
|
188
|
-
|
171
|
+
|
189
172
|
// Hide worker info and unbind the global hide handler.
|
190
173
|
hideWorkerInfo : function() {
|
191
174
|
$(document).unbind('click', Console.hideWorkerInfo);
|
192
|
-
Console._workerInfo.fadeOut(Console.ANIMATION_SPEED);
|
175
|
+
Console._workerInfo.fadeOut(Console.ANIMATION_SPEED);
|
193
176
|
}
|
194
|
-
|
177
|
+
|
195
178
|
};
|
196
179
|
|
197
180
|
$(document).ready(function() { Console.initialize(); });
|
@@ -1,43 +1,41 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
class ServerTest < Test::Unit::TestCase
|
4
|
-
|
4
|
+
|
5
5
|
include Rack::Test::Methods
|
6
|
-
|
6
|
+
|
7
7
|
def app
|
8
8
|
CloudCrowd::Server
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
context "The CloudCrowd::Server (Sinatra)" do
|
12
|
-
|
12
|
+
|
13
13
|
setup do
|
14
14
|
Job.destroy_all
|
15
15
|
2.times { Job.make }
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
should "set the identity of the Ruby instance" do
|
19
19
|
app.new
|
20
20
|
assert CloudCrowd.server?
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
should "be able to render the Operations Center (GET /)" do
|
24
24
|
get '/'
|
25
25
|
assert last_response.body.include? '<div id="nodes">'
|
26
26
|
assert last_response.body.include? '<div id="graphs">'
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
should "be able to get the current status for all jobs (GET /status)" do
|
30
30
|
resp = JSON.parse(get('/status').body)
|
31
|
-
assert resp['
|
32
|
-
assert resp['jobs'][0]['status'] == 'processing'
|
33
|
-
assert resp['jobs'][0]['percent_complete'] == 0
|
31
|
+
assert resp['job_count'] == 2
|
34
32
|
assert resp['work_unit_count'] == 2
|
35
33
|
end
|
36
|
-
|
34
|
+
|
37
35
|
should "have a heartbeat" do
|
38
36
|
assert get('/heartbeat').body == 'buh-bump'
|
39
37
|
end
|
40
|
-
|
38
|
+
|
41
39
|
should "be able to create a job" do
|
42
40
|
WorkUnit.expects(:distribute_to_nodes).returns(true)
|
43
41
|
post('/jobs', :job => '{"action":"graphics_magick","inputs":["http://www.google.com/"]}')
|
@@ -47,20 +45,20 @@ class ServerTest < Test::Unit::TestCase
|
|
47
45
|
assert job_info['work_units'] == 1
|
48
46
|
assert Job.last.id == job_info['id']
|
49
47
|
end
|
50
|
-
|
48
|
+
|
51
49
|
should "be able to check in on the status of a job" do
|
52
50
|
get("/jobs/#{Job.last.id}")
|
53
51
|
assert last_response.ok?
|
54
52
|
assert JSON.parse(last_response.body)['percent_complete'] == 0
|
55
53
|
end
|
56
|
-
|
54
|
+
|
57
55
|
should "be able to clean up a job when we're done with it" do
|
58
56
|
id = Job.last.id
|
59
57
|
delete("/jobs/#{id}")
|
60
58
|
assert last_response.successful? && last_response.empty?
|
61
59
|
assert !Job.find_by_id(id)
|
62
60
|
end
|
63
|
-
|
61
|
+
|
64
62
|
end
|
65
|
-
|
63
|
+
|
66
64
|
end
|
data/test/unit/test_action.rb
CHANGED
@@ -8,63 +8,65 @@ class EmptyAction < CloudCrowd::Action
|
|
8
8
|
end
|
9
9
|
|
10
10
|
class ActionTest < Test::Unit::TestCase
|
11
|
-
|
11
|
+
|
12
12
|
context "A CloudCrowd::Action" do
|
13
|
-
|
13
|
+
|
14
14
|
setup do
|
15
15
|
@store = CloudCrowd::AssetStore.new
|
16
16
|
@args = [CloudCrowd::PROCESSING, 'file://' + File.expand_path(__FILE__), {'job_id' => 1, 'work_unit_id' => 1}, @store]
|
17
17
|
@action = CloudCrowd.actions['word_count'].new(*@args)
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
should "throw an exception if the 'process' method isn't implemented" do
|
21
21
|
assert_raise(NotImplementedError) { EmptyAction.new(*@args).process }
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
should "have downloaded the input URL to local storage" do
|
25
25
|
assert @action.input_path
|
26
26
|
assert File.read(@action.input_path) == File.read(File.expand_path(__FILE__))
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
should "be able to save (to the filesystem while testing)" do
|
30
30
|
assert @action.save(@action.input_path) == "file://#{@store.local_storage_path}/word_count/job_1/unit_1/test_action.rb"
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
should "be able to clean up after itself" do
|
34
34
|
@action.cleanup_work_directory
|
35
35
|
assert !File.exists?(@action.work_directory)
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
should "be able to generate a safe filename for a URL to write to disk" do
|
39
39
|
name = @action.safe_filename("http://example.com/Some%20(Crazy'Kinda%7E)'Filename.txt")
|
40
40
|
assert name == 'Some-Crazy-Kinda-Filename.txt'
|
41
|
+
name = @action.safe_filename("http://example.com/file.pdf?one=two&three=four")
|
42
|
+
assert name == 'file.pdf'
|
41
43
|
end
|
42
|
-
|
44
|
+
|
43
45
|
should "be able to count the number of words in this file" do
|
44
|
-
assert @action.process ==
|
46
|
+
assert @action.process == 219
|
45
47
|
end
|
46
|
-
|
48
|
+
|
47
49
|
should "raise an exception when backticks fail" do
|
48
50
|
def @action.process; `utter failure 2>&1`; end
|
49
51
|
assert_raise(CloudCrowd::Error::CommandFailed) { @action.process }
|
50
52
|
end
|
51
|
-
|
53
|
+
|
52
54
|
end
|
53
55
|
|
54
56
|
|
55
57
|
context "A CloudCrowd::Action without URL input" do
|
56
|
-
|
58
|
+
|
57
59
|
setup do
|
58
60
|
@store = CloudCrowd::AssetStore.new
|
59
61
|
@args = [CloudCrowd::PROCESSING, 'inputstring', {'job_id' => 1, 'work_unit_id' => 1}, @store]
|
60
62
|
@action = CloudCrowd.actions['word_count'].new(*@args)
|
61
63
|
end
|
62
|
-
|
64
|
+
|
63
65
|
should "should not interpret the input data as an url" do
|
64
66
|
assert_equal 'inputstring', @action.input
|
65
67
|
assert_nil @action.input_path
|
66
68
|
end
|
67
|
-
|
69
|
+
|
68
70
|
end
|
69
|
-
|
71
|
+
|
70
72
|
end
|