cloud-crowd 0.3.3 → 0.4.0
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.
- 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
|