end
@@ -68,9 +68,9 @@ describe Agents::TwitterActionAgent do
context 'when set up to favorite' do
before do
@agent = build_agent(
- 'expected_receive_period_in_days' => '2',
'favorite' => 'true',
'retweet' => 'false',
+ 'emit_error_events' => 'true'
)
@agent.save!
end
@@ -107,13 +107,48 @@ describe Agents::TwitterActionAgent do
end
end
end
+
+ context 'with emit_error_events set to false' do
+ it 'does re-raises the exception on failure' do
+ agent = build_agent
+
+ stub(agent.twitter).retweet(anything) {
+ raise Twitter::Error.new('uh oh')
+ }
+
+ expect { agent.receive([@event1]) }.to raise_error(StandardError, /uh oh/)
+
+ end
+ end
end
describe "#validate_options" do
+ it 'the default options are valid' do
+ agent = build_agent(described_class.new.default_options)
+
+ expect(agent).to be_valid
+ end
+
+ context 'emit_error_events' do
+ it 'can be set to true' do
+ agent = build_agent(described_class.new.default_options.merge('emit_error_events' => 'true'))
+ expect(agent).to be_valid
+ end
+
+ it 'must be a boolean' do
+ agent = build_agent(described_class.new.default_options.merge('emit_error_events' => 'notbolean'))
+ expect(agent).not_to be_valid
+ end
+ end
+
+ it 'expected_receive_period_in_days must be set' do
+ agent = build_agent(described_class.new.default_options.merge('expected_receive_period_in_days' => ''))
+ expect(agent).not_to be_valid
+ end
+
context 'when set up to neither favorite or retweet' do
it 'is invalid' do
agent = build_agent(
- 'expected_receive_period_in_days' => '2',
'favorite' => 'false',
'retweet' => 'false',
)
@@ -129,11 +164,7 @@ describe Agents::TwitterActionAgent do
end
it 'checks if events have been received within the expected time period' do
- agent = build_agent(
- 'expected_receive_period_in_days' => '2',
- 'favorite' => 'false',
- 'retweet' => 'true',
- )
+ agent = build_agent
agent.save!
expect(agent).not_to be_working # No events received
@@ -147,10 +178,10 @@ describe Agents::TwitterActionAgent do
end
end
- def build_agent(options)
+ def build_agent(options = {})
described_class.new do |agent|
agent.name = 'twitter stuff'
- agent.options = options
+ agent.options = agent.default_options.merge(options)
agent.service = services(:generic)
agent.user = users(:bob)
end
@@ -19,6 +19,16 @@ describe Agents::WeatherAgent do |
||
19 | 19 |
it "creates a valid agent" do |
20 | 20 |
expect(agent).to be_valid |
21 | 21 |
end |
22 |
+ |
|
23 |
+ it "is valid with put-your-key-here or your-key" do |
|
24 |
+ agent.options['api_key'] = 'put-your-key-here' |
|
25 |
+ expect(agent).to be_valid |
|
26 |
+ expect(agent.working?).to be_falsey |
|
27 |
+ |
|
28 |
+ agent.options['api_key'] = 'your-key' |
|
29 |
+ expect(agent).to be_valid |
|
30 |
+ expect(agent.working?).to be_falsey |
|
31 |
+ end |
|
22 | 32 |
|
23 | 33 |
describe "#service" do |
24 | 34 |
it "doesn't have a Service object attached" do |
@@ -49,6 +49,12 @@ describe Agents::WebhookAgent do |
||
49 | 49 |
expect(out).to eq(['', 201]) |
50 | 50 |
end |
51 | 51 |
|
52 |
+ it 'should respond with interpolated response message if configured with `response` option' do |
|
53 |
+ agent.options['response'] = '{{some_key.people[1].name}}' |
|
54 |
+ out = agent.receive_web_request({ 'secret' => 'foobar', 'some_key' => payload }, "post", "text/html") |
|
55 |
+ expect(out).to eq(['jon', 201]) |
|
56 |
+ end |
|
57 |
+ |
|
52 | 58 |
it 'should respond with `Event Created` if the response option is nil or missing' do |
53 | 59 |
agent.options['response'] = nil |
54 | 60 |
out = agent.receive_web_request({ 'secret' => 'foobar', 'some_key' => payload }, "post", "text/html") |
@@ -59,6 +65,26 @@ describe Agents::WebhookAgent do |
||
59 | 65 |
expect(out).to eq(['Event Created', 201]) |
60 | 66 |
end |
61 | 67 |
|
68 |
+ it 'should respond with customized response code if configured with `code` option' do |
|
69 |
+ agent.options['code'] = '200' |
|
70 |
+ out = agent.receive_web_request({ 'secret' => 'foobar', 'some_key' => payload }, "post", "text/html") |
|
71 |
+ expect(out).to eq(['Event Created', 200]) |
|
72 |
+ end |
|
73 |
+ |
|
74 |
+ it 'should respond with `201` if the code option is empty, nil or missing' do |
|
75 |
+ agent.options['code'] = '' |
|
76 |
+ out = agent.receive_web_request({ 'secret' => 'foobar', 'some_key' => payload }, "post", "text/html") |
|
77 |
+ expect(out).to eq(['Event Created', 201]) |
|
78 |
+ |
|
79 |
+ agent.options['code'] = nil |
|
80 |
+ out = agent.receive_web_request({ 'secret' => 'foobar', 'some_key' => payload }, "post", "text/html") |
|
81 |
+ expect(out).to eq(['Event Created', 201]) |
|
82 |
+ |
|
83 |
+ agent.options.delete('code') |
|
84 |
+ out = agent.receive_web_request({ 'secret' => 'foobar', 'some_key' => payload }, "post", "text/html") |
|
85 |
+ expect(out).to eq(['Event Created', 201]) |
|
86 |
+ end |
|
87 |
+ |
|
62 | 88 |
describe "receiving events" do |
63 | 89 |
|
64 | 90 |
context "default settings" do |
@@ -1105,8 +1105,8 @@ fire: hot |
||
1105 | 1105 |
|
1106 | 1106 |
describe "#check" do |
1107 | 1107 |
before do |
1108 |
- expect { @checker.check }.to change { Event.count }.by(7) |
|
1109 |
- @events = Event.last(7) |
|
1108 |
+ expect { @checker.check }.to change { Event.count }.by(8) |
|
1109 |
+ @events = Event.last(8) |
|
1110 | 1110 |
end |
1111 | 1111 |
|
1112 | 1112 |
it "should check hostname" do |
@@ -1143,6 +1143,11 @@ fire: hot |
||
1143 | 1143 |
event = @events[6] |
1144 | 1144 |
expect(event.payload['url']).to eq("https://www.google.ca/search?q=%EC%9C%84%ED%82%A4%EB%B0%B1%EA%B3%BC:%EB%8C%80%EB%AC%B8") |
1145 | 1145 |
end |
1146 |
+ |
|
1147 |
+ it "should check url with unescaped brackets in the path component" do |
|
1148 |
+ event = @events[7] |
|
1149 |
+ expect(event.payload['url']).to eq("http://[::1]/path%5B%5D?query[]=foo") |
|
1150 |
+ end |
|
1146 | 1151 |
end |
1147 | 1152 |
end |
1148 | 1153 |
end |
@@ -98,6 +98,7 @@ describe Service do |
||
98 | 98 |
expect(service.token).to eq('a1b2c3d4...') |
99 | 99 |
expect(service.secret).to eq('abcdef1234') |
100 | 100 |
end |
101 |
+ |
|
101 | 102 |
it "should work with 37signals services" do |
102 | 103 |
signals = JSON.parse(File.read(Rails.root.join('spec/data_fixtures/services/37signals.json'))) |
103 | 104 |
expect { |
@@ -113,6 +114,7 @@ describe Service do |
||
113 | 114 |
expect(service.options[:user_id]).to eq(12345) |
114 | 115 |
service.expires_at = Time.at(1401554352) |
115 | 116 |
end |
117 |
+ |
|
116 | 118 |
it "should work with github services" do |
117 | 119 |
signals = JSON.parse(File.read(Rails.root.join('spec/data_fixtures/services/github.json'))) |
118 | 120 |
expect { |
@@ -126,4 +128,27 @@ describe Service do |
||
126 | 128 |
expect(service.token).to eq('agithubtoken') |
127 | 129 |
end |
128 | 130 |
end |
131 |
+ |
|
132 |
+ describe 'omniauth options provider registry for non-conforming omniauth responses' do |
|
133 |
+ describe '.register_options_provider' do |
|
134 |
+ before do |
|
135 |
+ Service.register_options_provider('test-omniauth-provider') do |omniauth| |
|
136 |
+ { name: omniauth['special_field'] } |
|
137 |
+ end |
|
138 |
+ end |
|
139 |
+ |
|
140 |
+ after do |
|
141 |
+ Service.option_providers.delete('test-omniauth-provider') |
|
142 |
+ end |
|
143 |
+ |
|
144 |
+ it 'allows gem developers to add their own options provider to the registry' do |
|
145 |
+ actual_options = Service.get_options({ |
|
146 |
+ 'provider' => 'test-omniauth-provider', |
|
147 |
+ 'special_field' => 'A Great Name' |
|
148 |
+ }) |
|
149 |
+ |
|
150 |
+ expect(actual_options[:name]).to eq('A Great Name') |
|
151 |
+ end |
|
152 |
+ end |
|
153 |
+ end |
|
129 | 154 |
end |
@@ -8,14 +8,6 @@ describe UserCredential do |
||
8 | 8 |
it { should validate_presence_of(:user_id) } |
9 | 9 |
end |
10 | 10 |
|
11 |
- describe "mass assignment" do |
|
12 |
- it { should allow_mass_assignment_of :credential_name } |
|
13 |
- |
|
14 |
- it { should allow_mass_assignment_of :credential_value } |
|
15 |
- |
|
16 |
- it { should_not allow_mass_assignment_of :user_id } |
|
17 |
- end |
|
18 |
- |
|
19 | 11 |
describe "cleaning fields" do |
20 | 12 |
it "should trim whitespace" do |
21 | 13 |
user_credential = user_credentials(:bob_aws_key) |
@@ -1,6 +1,8 @@ |
||
1 | 1 |
require 'rails_helper' |
2 | 2 |
|
3 | 3 |
describe User do |
4 |
+ let(:bob) { users(:bob) } |
|
5 |
+ |
|
4 | 6 |
describe "validations" do |
5 | 7 |
describe "invitation_code" do |
6 | 8 |
context "when configured to use invitation codes" do |
@@ -64,4 +66,29 @@ describe User do |
||
64 | 66 |
expect(users(:bob).deactivated_at).to be_nil |
65 | 67 |
end |
66 | 68 |
end |
69 |
+ |
|
70 |
+ context '#undefined_agent_types' do |
|
71 |
+ it 'returns an empty array when no agents are undefined' do |
|
72 |
+ expect(bob.undefined_agent_types).to be_empty |
|
73 |
+ end |
|
74 |
+ |
|
75 |
+ it 'returns the undefined agent types' do |
|
76 |
+ agent = agents(:bob_website_agent) |
|
77 |
+ agent.update_attribute(:type, 'Agents::UndefinedAgent') |
|
78 |
+ expect(bob.undefined_agent_types).to match_array(['Agents::UndefinedAgent']) |
|
79 |
+ end |
|
80 |
+ end |
|
81 |
+ |
|
82 |
+ context '#undefined_agents' do |
|
83 |
+ it 'returns an empty array when no agents are undefined' do |
|
84 |
+ expect(bob.undefined_agents).to be_empty |
|
85 |
+ end |
|
86 |
+ |
|
87 |
+ it 'returns the undefined agent types' do |
|
88 |
+ agent = agents(:bob_website_agent) |
|
89 |
+ agent.update_attribute(:type, 'Agents::UndefinedAgent') |
|
90 |
+ expect(bob.undefined_agents).not_to be_empty |
|
91 |
+ expect(bob.undefined_agents.first).to be_a(Agent) |
|
92 |
+ end |
|
93 |
+ end |
|
67 | 94 |
end |
@@ -13,7 +13,7 @@ require 'rspec/rails' |
||
13 | 13 |
require 'rr' |
14 | 14 |
require 'webmock/rspec' |
15 | 15 |
|
16 |
-WebMock.disable_net_connect! |
|
16 |
+WebMock.disable_net_connect!(allow_localhost: true) |
|
17 | 17 |
|
18 | 18 |
# Requires supporting ruby files with custom matchers and macros, etc, |
19 | 19 |
# in spec/support/ and its subdirectories. |
@@ -66,7 +66,7 @@ RSpec.configure do |config| |
||
66 | 66 |
|
67 | 67 |
config.render_views |
68 | 68 |
|
69 |
- config.include Devise::TestHelpers, type: :controller |
|
69 |
+ config.include Devise::Test::ControllerHelpers, type: :controller |
|
70 | 70 |
config.include SpecHelpers |
71 | 71 |
config.include Delorean |
72 | 72 |
end |
@@ -1,6 +1,8 @@ |
||
1 | 1 |
require 'rails_helper' |
2 | 2 |
|
3 | 3 |
shared_examples_for 'FileHandlingConsumer' do |
4 |
+ let(:event) { Event.new(user: @checker.user, payload: {'file_pointer' => {'file' => 'text.txt', 'agent_id' => @checker.id}}) } |
|
5 |
+ |
|
4 | 6 |
it 'returns a file pointer' do |
5 | 7 |
expect(@checker.get_file_pointer('testfile')).to eq(file_pointer: { file: "testfile", agent_id: @checker.id}) |
6 | 8 |
end |
@@ -9,8 +11,26 @@ shared_examples_for 'FileHandlingConsumer' do |
||
9 | 11 |
@checker2 = @checker.dup |
10 | 12 |
@checker2.user = users(:bob) |
11 | 13 |
@checker2.save! |
12 |
- expect(@checker2.user.id).not_to eq(@checker.user.id) |
|
13 |
- event = Event.new(user: @checker.user, payload: {'file_pointer' => {'file' => 'test', 'agent_id' => @checker2.id}}) |
|
14 |
+ event.payload['file_pointer']['agent_id'] = @checker2.id |
|
14 | 15 |
expect { @checker.get_io(event) }.to raise_error(ActiveRecord::RecordNotFound) |
15 | 16 |
end |
16 |
-end |
|
17 |
+ |
|
18 |
+ context '#has_file_pointer?' do |
|
19 |
+ it 'returns true if the event contains a file pointer' do |
|
20 |
+ expect(@checker.has_file_pointer?(event)).to be_truthy |
|
21 |
+ end |
|
22 |
+ |
|
23 |
+ it 'returns false if the event does not contain a file pointer' do |
|
24 |
+ expect(@checker.has_file_pointer?(Event.new)).to be_falsy |
|
25 |
+ end |
|
26 |
+ end |
|
27 |
+ |
|
28 |
+ it '#get_upload_io returns a Faraday::UploadIO instance' do |
|
29 |
+ io_mock = mock() |
|
30 |
+ mock(@checker).get_io(event) { StringIO.new("testdata") } |
|
31 |
+ |
|
32 |
+ upload_io = @checker.get_upload_io(event) |
|
33 |
+ expect(upload_io).to be_a(Faraday::UploadIO) |
|
34 |
+ expect(upload_io.content_type).to eq('text/plain') |
|
35 |
+ end |
|
36 |
+end |
@@ -1,40 +1,146 @@ |
||
1 |
-// |
|
2 |
-// Use internal $.serializeArray to get list of form elements which is |
|
3 |
-// consistent with $.serialize |
|
4 |
-// |
|
5 |
-// From version 2.0.0, $.serializeObject will stop converting [name] values |
|
6 |
-// to camelCase format. This is *consistent* with other serialize methods: |
|
7 |
-// |
|
8 |
-// - $.serialize |
|
9 |
-// - $.serializeArray |
|
10 |
-// |
|
11 |
-// If you require camel casing, you can either download version 1.0.4 or map |
|
12 |
-// them yourself. |
|
13 |
-// |
|
14 |
- |
|
15 |
-(function($){ |
|
16 |
- $.fn.serializeObject = function () { |
|
17 |
- "use strict"; |
|
18 |
- |
|
19 |
- var result = {}; |
|
20 |
- var extend = function (i, element) { |
|
21 |
- var node = result[element.name]; |
|
22 |
- |
|
23 |
- // If node with same name exists already, need to convert it to an array as it |
|
24 |
- // is a multi-value field (i.e., checkboxes) |
|
25 |
- |
|
26 |
- if ('undefined' !== typeof node && node !== null) { |
|
27 |
- if ($.isArray(node)) { |
|
28 |
- node.push(element.value); |
|
29 |
- } else { |
|
30 |
- result[element.name] = [node, element.value]; |
|
1 |
+/** |
|
2 |
+ * jQuery serializeObject |
|
3 |
+ * @copyright 2014, macek <paulmacek@gmail.com> |
|
4 |
+ * @link https://github.com/macek/jquery-serialize-object |
|
5 |
+ * @license BSD |
|
6 |
+ * @version 2.5.0 |
|
7 |
+ */ |
|
8 |
+(function(root, factory) { |
|
9 |
+ |
|
10 |
+ // AMD |
|
11 |
+ if (typeof define === "function" && define.amd) { |
|
12 |
+ define(["exports", "jquery"], function(exports, $) { |
|
13 |
+ return factory(exports, $); |
|
14 |
+ }); |
|
15 |
+ } |
|
16 |
+ |
|
17 |
+ // CommonJS |
|
18 |
+ else if (typeof exports !== "undefined") { |
|
19 |
+ var $ = require("jquery"); |
|
20 |
+ factory(exports, $); |
|
21 |
+ } |
|
22 |
+ |
|
23 |
+ // Browser |
|
24 |
+ else { |
|
25 |
+ factory(root, (root.jQuery || root.Zepto || root.ender || root.$)); |
|
26 |
+ } |
|
27 |
+ |
|
28 |
+}(this, function(exports, $) { |
|
29 |
+ |
|
30 |
+ var patterns = { |
|
31 |
+ validate: /^[a-z_][a-z0-9_]*(?:\[(?:\d*|[a-z0-9_]+)\])*$/i, |
|
32 |
+ key: /[a-z0-9_]+|(?=\[\])/gi, |
|
33 |
+ push: /^$/, |
|
34 |
+ fixed: /^\d+$/, |
|
35 |
+ named: /^[a-z0-9_]+$/i |
|
36 |
+ }; |
|
37 |
+ |
|
38 |
+ function FormSerializer(helper, $form) { |
|
39 |
+ |
|
40 |
+ // private variables |
|
41 |
+ var data = {}, |
|
42 |
+ pushes = {}; |
|
43 |
+ |
|
44 |
+ // private API |
|
45 |
+ function build(base, key, value) { |
|
46 |
+ base[key] = value; |
|
47 |
+ return base; |
|
48 |
+ } |
|
49 |
+ |
|
50 |
+ function makeObject(root, value) { |
|
51 |
+ |
|
52 |
+ var keys = root.match(patterns.key), k; |
|
53 |
+ |
|
54 |
+ // nest, nest, ..., nest |
|
55 |
+ while ((k = keys.pop()) !== undefined) { |
|
56 |
+ // foo[] |
|
57 |
+ if (patterns.push.test(k)) { |
|
58 |
+ var idx = incrementPush(root.replace(/\[\]$/, '')); |
|
59 |
+ value = build([], idx, value); |
|
60 |
+ } |
|
61 |
+ |
|
62 |
+ // foo[n] |
|
63 |
+ else if (patterns.fixed.test(k)) { |
|
64 |
+ value = build([], k, value); |
|
65 |
+ } |
|
66 |
+ |
|
67 |
+ // foo; foo[bar] |
|
68 |
+ else if (patterns.named.test(k)) { |
|
69 |
+ value = build({}, k, value); |
|
31 | 70 |
} |
32 |
- } else { |
|
33 |
- result[element.name] = element.value; |
|
34 | 71 |
} |
35 |
- }; |
|
36 | 72 |
|
37 |
- $.each(this.serializeArray(), extend); |
|
38 |
- return result; |
|
73 |
+ return value; |
|
74 |
+ } |
|
75 |
+ |
|
76 |
+ function incrementPush(key) { |
|
77 |
+ if (pushes[key] === undefined) { |
|
78 |
+ pushes[key] = 0; |
|
79 |
+ } |
|
80 |
+ return pushes[key]++; |
|
81 |
+ } |
|
82 |
+ |
|
83 |
+ function encode(pair) { |
|
84 |
+ switch ($('[name="' + pair.name + '"]', $form).attr("type")) { |
|
85 |
+ case "checkbox": |
|
86 |
+ return pair.value === "on" ? true : pair.value; |
|
87 |
+ default: |
|
88 |
+ return pair.value; |
|
89 |
+ } |
|
90 |
+ } |
|
91 |
+ |
|
92 |
+ function addPair(pair) { |
|
93 |
+ if (!patterns.validate.test(pair.name)) return this; |
|
94 |
+ var obj = makeObject(pair.name, encode(pair)); |
|
95 |
+ data = helper.extend(true, data, obj); |
|
96 |
+ return this; |
|
97 |
+ } |
|
98 |
+ |
|
99 |
+ function addPairs(pairs) { |
|
100 |
+ if (!helper.isArray(pairs)) { |
|
101 |
+ throw new Error("formSerializer.addPairs expects an Array"); |
|
102 |
+ } |
|
103 |
+ for (var i=0, len=pairs.length; i<len; i++) { |
|
104 |
+ this.addPair(pairs[i]); |
|
105 |
+ } |
|
106 |
+ return this; |
|
107 |
+ } |
|
108 |
+ |
|
109 |
+ function serialize() { |
|
110 |
+ return data; |
|
111 |
+ } |
|
112 |
+ |
|
113 |
+ function serializeJSON() { |
|
114 |
+ return JSON.stringify(serialize()); |
|
115 |
+ } |
|
116 |
+ |
|
117 |
+ // public API |
|
118 |
+ this.addPair = addPair; |
|
119 |
+ this.addPairs = addPairs; |
|
120 |
+ this.serialize = serialize; |
|
121 |
+ this.serializeJSON = serializeJSON; |
|
122 |
+ } |
|
123 |
+ |
|
124 |
+ FormSerializer.patterns = patterns; |
|
125 |
+ |
|
126 |
+ FormSerializer.serializeObject = function serializeObject() { |
|
127 |
+ return new FormSerializer($, this). |
|
128 |
+ addPairs(this.serializeArray()). |
|
129 |
+ serialize(); |
|
130 |
+ }; |
|
131 |
+ |
|
132 |
+ FormSerializer.serializeJSON = function serializeJSON() { |
|
133 |
+ return new FormSerializer($, this). |
|
134 |
+ addPairs(this.serializeArray()). |
|
135 |
+ serializeJSON(); |
|
39 | 136 |
}; |
40 |
-})(jQuery); |
|
137 |
+ |
|
138 |
+ if (typeof $.fn !== "undefined") { |
|
139 |
+ $.fn.serializeObject = FormSerializer.serializeObject; |
|
140 |
+ $.fn.serializeJSON = FormSerializer.serializeJSON; |
|
141 |
+ } |
|
142 |
+ |
|
143 |
+ exports.FormSerializer = FormSerializer; |
|
144 |
+ |
|
145 |
+ return FormSerializer; |
|
146 |
+})); |