houston-core 0.7.0.beta3 → 0.7.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +56 -56
- data/app/assets/javascripts/houston/app/infinite_scroll.coffee +6 -2
- data/app/assets/javascripts/houston/app/models/ticket.coffee +0 -42
- data/app/assets/javascripts/houston/app/ticket_tracker_refresh.coffee +0 -2
- data/app/assets/javascripts/houston/app/views/keyboard_shortcuts_modal.coffee +0 -6
- data/app/assets/javascripts/houston/core/handlebars_helpers.coffee +0 -5
- data/app/assets/stylesheets/houston/application/actions.scss +15 -0
- data/app/assets/stylesheets/houston/application/layout.scss +3 -0
- data/app/assets/stylesheets/houston/application/markdown.scss +1 -1
- data/app/assets/stylesheets/houston/application/navigation.scss +3 -1
- data/app/assets/stylesheets/houston/application/project_banner_buttons.scss +2 -0
- data/app/assets/stylesheets/houston/application/tables.scss +0 -1
- data/app/assets/stylesheets/houston/application/timeline.scss +1 -1
- data/app/assets/stylesheets/houston/core/overrides.scss +2 -1
- data/app/concerns/houston/props.rb +11 -4
- data/app/concerns/project_adapter.rb +1 -1
- data/app/concerns/unique_add.rb +1 -1
- data/app/controllers/actions_controller.rb +39 -0
- data/app/controllers/commits_controller.rb +1 -1
- data/app/controllers/errors_controller.rb +10 -0
- data/app/controllers/hooks_controller.rb +1 -1
- data/app/controllers/omnibar_controller.rb +1 -11
- data/app/controllers/project_hooks_controller.rb +2 -2
- data/app/controllers/projects_controller.rb +2 -0
- data/app/controllers/test_runs_controller.rb +14 -3
- data/app/controllers/triggers_controller.rb +8 -0
- data/app/helpers/actions_helper.rb +7 -0
- data/app/helpers/application_helper.rb +1 -1
- data/app/interactors/cache_key_dependencies.rb +1 -1
- data/app/interactors/test_run_comparer.rb +1 -1
- data/app/mailers/project_notification.rb +0 -31
- data/app/models/ability.rb +0 -11
- data/app/models/{job.rb → action.rb} +19 -7
- data/app/models/commit.rb +2 -2
- data/app/models/deploy.rb +3 -3
- data/app/models/github/comment_event.rb +9 -3
- data/app/models/github/post_receive_event.rb +1 -1
- data/app/models/github/pull_request.rb +5 -5
- data/app/models/persistent_trigger.rb +46 -0
- data/app/models/project.rb +0 -1
- data/app/models/release.rb +1 -1
- data/app/models/run_tests_on_post_receive.rb +17 -17
- data/app/models/task.rb +4 -4
- data/app/models/test_run.rb +18 -5
- data/app/models/ticket.rb +2 -21
- data/app/models/ticket_antecedent.rb +3 -3
- data/app/models/user.rb +0 -1
- data/app/views/actions/index.html.erb +69 -0
- data/app/views/actions/show.html.erb +45 -0
- data/app/views/commits/show.html.erb +7 -8
- data/app/views/errors/_actions.html.erb +11 -0
- data/app/views/errors/index.html.erb +39 -0
- data/app/views/layouts/_mobile_navigation.html.erb +1 -9
- data/app/views/layouts/_navigation.html.erb +14 -8
- data/app/views/layouts/application.html.erb +1 -3
- data/app/views/project_notification/test_run.html.erb +13 -3
- data/app/views/projects/_form.html.erb +13 -7
- data/app/views/triggers/index.html.erb +39 -0
- data/config/initializers/add_navigation_renderers.rb +0 -6
- data/config/initializers/houston_async.rb +4 -2
- data/config/initializers/houston_scheduler_daemon.rb +6 -0
- data/config/initializers/load_persistent_triggers.rb +7 -0
- data/config/initializers/requirements.rb +2 -1
- data/config/initializers/sync_commits_on_post_receive.rb +2 -2
- data/config/routes.rb +17 -15
- data/db/migrate/20160711170921_rename_jobs_to_actions.rb +5 -0
- data/db/migrate/20160713204605_add_trigger_and_params_to_actions.rb +6 -0
- data/db/migrate/20160715173039_create_persistent_triggers.rb +10 -0
- data/db/structure.sql +197 -221
- data/houston-core.gemspec +1 -1
- data/lib/houston/boot/actions.rb +105 -0
- data/lib/houston/boot/active_record_serializer.rb +24 -0
- data/lib/houston/boot/configuration.rb +118 -49
- data/lib/houston/boot/events.rb +46 -0
- data/lib/houston/boot/extensions.rb +118 -14
- data/lib/houston/boot/observer.rb +122 -24
- data/lib/houston/boot/readonly_hash_serializer.rb +15 -0
- data/lib/houston/boot/serializer.rb +83 -0
- data/lib/houston/boot/ticket_antecedent_serializer.rb +21 -0
- data/lib/houston/boot/timer.rb +45 -0
- data/lib/houston/boot/triggers.rb +75 -0
- data/lib/houston/boot.rb +5 -0
- data/lib/houston/version.rb +1 -1
- data/lib/params_serializer.rb +18 -0
- data/lib/tasks/actions.rake +12 -0
- data/lib/tasks/events.rake +11 -0
- data/templates/new-instance/config/abilities.rb +0 -8
- data/templates/new-instance/config/{triggers → events}/alerts/slack_when_assigned.rb +1 -1
- data/templates/new-instance/config/{triggers → events}/alerts/slack_when_opened.rb +1 -1
- data/templates/new-instance/config/{triggers → events}/daemons/health.rb +6 -6
- data/templates/new-instance/config/{triggers → events}/deploy/autoresolve_errs.rb +1 -1
- data/templates/new-instance/config/{triggers → events}/deploy/checkout_mentioned_alerts.rb +1 -1
- data/templates/new-instance/config/{triggers → events}/deploy/notify_deployer_when_finished.rb +2 -2
- data/templates/new-instance/config/{triggers → events}/github/publish_comments_on_slack.rb +9 -9
- data/templates/new-instance/config/{triggers → events}/tests/slack_when_analyzed.rb +1 -1
- data/templates/new-instance/config/{triggers → events}/tests/slack_when_completed.rb +1 -1
- data/templates/new-instance/config/{triggers → events}/tickets/mark_tasks_completed_on_commit.rb +1 -1
- data/templates/new-instance/config/main.rb +8 -35
- data/templates/new-instance/config/{jobs → timers}/cache_key_dependencies.rb +0 -0
- data/templates/new-instance/config/{jobs → timers}/email_about_open_alerts.rb +0 -0
- data/templates/new-instance/config/{jobs → timers}/purge_jobs.rb +0 -0
- data/templates/new-instance/config/{jobs → timers}/slack_reminders_about_alerts.rb +0 -0
- data/templates/new-instance/config/{jobs → timers}/sync_commits.rb +0 -0
- data/templates/new-instance/config/{jobs → timers}/sync_pull_requests.rb +0 -0
- data/templates/new-instance/config/{jobs → timers}/sync_tickets.rb +0 -0
- data/templates/new-module/lib/houston/%name%.rb +13 -0
- data/test/integration/ci_integration_test.rb +5 -5
- data/test/integration/web_hook_test.rb +1 -1
- data/test/test_helper.rb +14 -0
- data/test/unit/controllers/hooks_controller_test.rb +2 -2
- data/test/unit/initializers/sync_commits_on_post_receive_test.rb +1 -1
- data/test/unit/models/actions_test.rb +107 -0
- data/test/unit/models/configuration_test.rb +108 -0
- data/test/unit/models/observer_test.rb +87 -3
- data/test/unit/models/persistent_trigger_test.rb +94 -0
- data/test/unit/models/serializer_test.rb +80 -0
- data/test/unit/models/triggers_test.rb +53 -0
- metadata +60 -60
- data/app/assets/javascripts/houston/app/models/testing_note.coffee +0 -18
- data/app/assets/javascripts/houston/app/views/commit_view.coffee +0 -13
- data/app/assets/javascripts/houston/app/views/testing_note_view.coffee +0 -85
- data/app/assets/javascripts/houston/app/views/testing_report_view.coffee +0 -29
- data/app/assets/javascripts/houston/app/views/testing_ticket_view.coffee +0 -203
- data/app/assets/stylesheets/houston/application/jobs.scss +0 -5
- data/app/assets/stylesheets/houston/application/testing_report.scss +0 -279
- data/app/assets/templates/commit.hbs +0 -9
- data/app/assets/templates/testing_notes/edit.hbs +0 -20
- data/app/assets/templates/testing_notes/new.hbs +0 -27
- data/app/assets/templates/testing_notes/show.hbs +0 -11
- data/app/assets/templates/testing_report/description.hbs +0 -12
- data/app/assets/templates/testing_report/ticket.hbs +0 -21
- data/app/assets/templates/testing_report/verdict.hbs +0 -4
- data/app/controllers/jobs_controller.rb +0 -42
- data/app/controllers/testing_notes_controller.rb +0 -50
- data/app/controllers/testing_report_controller.rb +0 -38
- data/app/models/testing_note.rb +0 -64
- data/app/presenters/testing_note_presenter.rb +0 -27
- data/app/presenters/testing_report_ticket_presenter.rb +0 -71
- data/app/views/jobs/index.html.erb +0 -72
- data/app/views/jobs/show.html.erb +0 -41
- data/app/views/project_notification/testing_note.html.erb +0 -9
- data/app/views/testing_report/_scripts.html.erb +0 -12
- data/app/views/testing_report/index.html.erb +0 -31
- data/app/views/testing_report/show.html.erb +0 -29
- data/config/initializers/houston_scheduler.rb +0 -23
- data/db/migrate/20120424212706_create_testing_notes.rb +0 -14
- data/db/migrate/20120501231817_add_expires_at_to_testing_notes.rb +0 -5
- data/db/migrate/20120501231948_add_unfuddle_id_to_testing_notes.rb +0 -5
- data/db/migrate/20120715230526_change_testing_notes_comment_to_text.rb +0 -9
- data/db/migrate/20130211015046_add_min_passing_verdicts_to_projects.rb +0 -5
- data/db/migrate/20130407220039_add_project_id_to_testing_notes.rb +0 -26
- data/db/migrate/20140511024021_rename_testing_notes_unfuddle_id_to_remote_id.rb +0 -5
- data/templates/new-instance/config/triggers/tickets/email_testing_notes.rb +0 -7
- data/templates/new-instance/log/development.log +0 -41253
- data/templates/new-instance/log/test.log +0 -545
- data/templates/new-module/config/initializers/add_navigation_renderer.rb +0 -3
@@ -249,45 +249,18 @@ Houston.config do
|
|
249
249
|
# ---------------------------------------------------------------------------
|
250
250
|
#
|
251
251
|
# Configure Houston to execute block of code when an event is triggered.
|
252
|
-
#
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
#
|
259
|
-
# * daemon:*:restart When a background thread (like the Scheduler or Slack) is restarted
|
260
|
-
# * daemon:*:stop When a background thread (like the Scheduler or Slack) dies
|
261
|
-
# * deploy:completed When a deploy has been recorded
|
262
|
-
# * hooks:* When a Web Hook as been triggered
|
263
|
-
# * release:create When a new Release has been created
|
264
|
-
# * test_run:start When the CI server has begun a build
|
265
|
-
# * test_run:complete When the CI server has completed a build
|
266
|
-
# * testing_note:create When a Testing Note has been created
|
267
|
-
# * testing_note:update When a Testing Note has been updated
|
268
|
-
# * testing_note:save When a Testing Note has been created or updated
|
269
|
-
# * ticket:release When a Ticket is mentioned in a Release
|
270
|
-
# * task:released When a commit mentioning a Task is released
|
271
|
-
# * task:committed When a commit mentioning a Task is pushed
|
272
|
-
# * task:completed When a Task is marked completed
|
273
|
-
# * task:reopened When a Task is marked reopened
|
274
|
-
# * alert:create When an Alert is created
|
275
|
-
# * alert:*:create When an Alert of a particular type is created
|
276
|
-
# * alert:assign When an Alert is assigned
|
277
|
-
# * alert:*:assign When an Alert of a particular type is assigned
|
278
|
-
# * alert:deployed When an Alert is deployed
|
279
|
-
# * alert:*:deployed When an Alert of a particular type is deployed
|
280
|
-
|
281
|
-
load "triggers/**/*"
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
# Background Jobs
|
252
|
+
# To see a complete list of events run `rake houston:events` at the command line.
|
253
|
+
|
254
|
+
load "events/**/*"
|
255
|
+
|
256
|
+
|
257
|
+
|
258
|
+
# Timers
|
286
259
|
# ---------------------------------------------------------------------------
|
287
260
|
#
|
288
261
|
# Houston can be configured to run jobs at a variety of intervals.
|
289
262
|
|
290
|
-
load "
|
263
|
+
load "timers/**/*"
|
291
264
|
|
292
265
|
|
293
266
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -12,4 +12,17 @@ module Houston
|
|
12
12
|
end
|
13
13
|
|
14
14
|
end
|
15
|
+
|
16
|
+
|
17
|
+
# Register events that will be raised by this module
|
18
|
+
# register_events {{
|
19
|
+
# "<%= name %>:create" => params("<%= name %>").desc("<%= camelized %> was created"),
|
20
|
+
# "<%= name %>:update" => params("<%= name %>").desc("<%= camelized %> was updated")
|
21
|
+
# }}
|
22
|
+
|
23
|
+
|
24
|
+
# Add a link to Houston's global navigation
|
25
|
+
# add_navigation_renderer :<%= name %> do
|
26
|
+
# render_nav_link "<%= camelized %>", Houston::<%= camelized %>::Engine.routes.url_helpers.<%= name %>_path, icon: "fa-thumbs-up"
|
27
|
+
# end
|
15
28
|
end
|
@@ -8,7 +8,7 @@ class CIIntegrationTest < ActionDispatch::IntegrationTest
|
|
8
8
|
|
9
9
|
|
10
10
|
context "Houston" do
|
11
|
-
should "trigger a build when the hooks:post_receive event is fired for a project that uses a CI server" do
|
11
|
+
should "trigger a build when the hooks:project:post_receive event is fired for a project that uses a CI server" do
|
12
12
|
@project = create(:project, ci_server_name: "Mock")
|
13
13
|
|
14
14
|
stub.instance_of(PostReceivePayload).commit { "63cd1ef" }
|
@@ -20,7 +20,7 @@ class CIIntegrationTest < ActionDispatch::IntegrationTest
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
should "do nothing when the hooks:post_receive event is fired for a project that does not use a CI server" do
|
23
|
+
should "do nothing when the hooks:project:post_receive event is fired for a project that does not use a CI server" do
|
24
24
|
@project = create(:project, ci_server_name: "None")
|
25
25
|
|
26
26
|
assert_no_difference "TestRun.count" do
|
@@ -127,7 +127,7 @@ class CIIntegrationTest < ActionDispatch::IntegrationTest
|
|
127
127
|
stub(Object.new).success? { true }
|
128
128
|
end
|
129
129
|
|
130
|
-
Houston.observer.fire "test_run:start", test_run
|
130
|
+
Houston.observer.fire "test_run:start", test_run: test_run
|
131
131
|
end
|
132
132
|
|
133
133
|
should "publish test results to GitHub" do
|
@@ -148,7 +148,7 @@ class CIIntegrationTest < ActionDispatch::IntegrationTest
|
|
148
148
|
stub(Object.new).success? { true }
|
149
149
|
end
|
150
150
|
|
151
|
-
Houston.observer.fire "test_run:complete", test_run
|
151
|
+
Houston.observer.fire "test_run:complete", test_run: test_run
|
152
152
|
end
|
153
153
|
end
|
154
154
|
|
@@ -162,7 +162,7 @@ class CIIntegrationTest < ActionDispatch::IntegrationTest
|
|
162
162
|
|
163
163
|
mock(CodeClimate::CoverageReport).publish!(test_run)
|
164
164
|
|
165
|
-
Houston.observer.fire "test_run:complete", test_run
|
165
|
+
Houston.observer.fire "test_run:complete", test_run: test_run
|
166
166
|
end
|
167
167
|
end
|
168
168
|
|
@@ -20,7 +20,7 @@ class WebHookTest < ActionDispatch::IntegrationTest
|
|
20
20
|
end
|
21
21
|
|
22
22
|
test "should trigger a project hook when it is defined" do
|
23
|
-
assert_triggered "hooks:whatever" do
|
23
|
+
assert_triggered "hooks:project:whatever" do
|
24
24
|
post "/projects/#{project.slug}/hooks/whatever"
|
25
25
|
assert_response :success
|
26
26
|
end
|
data/test/test_helper.rb
CHANGED
@@ -85,6 +85,20 @@ class ActiveSupport::TestCase
|
|
85
85
|
|
86
86
|
|
87
87
|
|
88
|
+
def refute_raises(*exp)
|
89
|
+
msg = "#{exp.pop}.\n" if String === exp.last
|
90
|
+
exp << StandardError if exp.empty?
|
91
|
+
|
92
|
+
yield
|
93
|
+
|
94
|
+
pass
|
95
|
+
rescue *exp => e
|
96
|
+
exp = exp.first if exp.size == 1
|
97
|
+
flunk "#{msg}#{mu_pp(exp)} was not supposed to be raised"
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
|
88
102
|
def assert_deep_equal(expected_value, actual_value)
|
89
103
|
differences = differences_between_values(expected_value, actual_value)
|
90
104
|
assert differences.none?, differences.join("\n")
|
@@ -62,10 +62,10 @@ class HooksControllerTest < ActionController::TestCase
|
|
62
62
|
assert_response :success
|
63
63
|
end
|
64
64
|
|
65
|
-
should "trigger a `hooks:post_receive` event for the project" do
|
65
|
+
should "trigger a `hooks:project:post_receive` event for the project" do
|
66
66
|
project = create(:project, slug: "public-repo")
|
67
67
|
expected_payload = hash_including(github_push_event_payload.slice("before", "after"))
|
68
|
-
mock(Houston.observer).fire("hooks:post_receive", project, expected_payload)
|
68
|
+
mock(Houston.observer).fire("hooks:project:post_receive", project: project, params: expected_payload)
|
69
69
|
post :github, hook: github_push_event_payload
|
70
70
|
end
|
71
71
|
end
|
@@ -7,7 +7,7 @@ class SyncCommitsOnPostReceiveTest < ActiveSupport::TestCase
|
|
7
7
|
should "sync commits for that project" do
|
8
8
|
project = create(:project, version_control_name: "Mock")
|
9
9
|
mock(project.commits).sync!
|
10
|
-
Houston.observer.fire "hooks:post_receive", project, {}
|
10
|
+
Houston.observer.fire "hooks:project:post_receive", project: project, params: {}
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ActionsTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
actions.define("test-action") { }
|
7
|
+
Action.delete_all
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
context "Actions#define" do
|
12
|
+
should "allow you to define the params an action requires" do
|
13
|
+
action = actions.redefine("test-action", %w{param1 param2}) { }
|
14
|
+
assert_equal "test-action", action.name
|
15
|
+
assert_equal %w{param1 param2}, action.required_params
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
context "Actions#run" do
|
21
|
+
should "raise if called with an argument that isn't a Hash" do
|
22
|
+
assert_raises ArgumentError do
|
23
|
+
run! 5
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
should "raise if any required params are absent" do
|
28
|
+
actions.redefine("test-action", %w{param1 param2}) { }
|
29
|
+
assert_raises Houston::Observer::MissingParamError do
|
30
|
+
run! param1: 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
should "allow unrequired params to be passed" do
|
35
|
+
actions.redefine("test-action", %w{param1}) { }
|
36
|
+
refute_raises Houston::Observer::MissingParamError do
|
37
|
+
run! param1: 1, param2: 2
|
38
|
+
end
|
39
|
+
|
40
|
+
assert_equal({param1: 1, param2: 2}, Action.last.params)
|
41
|
+
end
|
42
|
+
|
43
|
+
# We require that all params be serializable so that (1) actions can
|
44
|
+
# be recorded with all their state (and retried) and (2) the actions
|
45
|
+
# system can be "outsourced" to a background job system like Sidekiq
|
46
|
+
# if need be.
|
47
|
+
should "raise if any required param is unserializable" do
|
48
|
+
actions.redefine("test-action", %w{param1}) { }
|
49
|
+
unserializable_object = Class.new.new
|
50
|
+
assert_raises Houston::Serializer::UnserializableError do
|
51
|
+
run! param1: unserializable_object
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
should "record every invocation of the action" do
|
56
|
+
assert_difference "Action.count", +1 do
|
57
|
+
run!
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
should "record any exception raised by the action" do
|
62
|
+
actions.redefine("test-action") { raise "hell" }
|
63
|
+
run!
|
64
|
+
refute Action.last.succeeded?
|
65
|
+
error = Action.last.error
|
66
|
+
assert_kind_of Error, error
|
67
|
+
assert_equal "hell", error.message
|
68
|
+
end
|
69
|
+
|
70
|
+
should "record how an action was triggered" do
|
71
|
+
run!
|
72
|
+
assert_equal "manual", Action.first.trigger
|
73
|
+
|
74
|
+
run!({}, trigger: "at(5:00pm)")
|
75
|
+
assert_equal "at(5:00pm)", Action.first.trigger
|
76
|
+
end
|
77
|
+
|
78
|
+
should "record the params with which an action was triggered" do
|
79
|
+
run!
|
80
|
+
assert_equal({}, Action.first.params)
|
81
|
+
|
82
|
+
actions.redefine("test-action", [:example]) { }
|
83
|
+
run! example: 5
|
84
|
+
assert_equal({example: 5}, Action.first.params)
|
85
|
+
end
|
86
|
+
|
87
|
+
should "invoke actions in the context of their params" do
|
88
|
+
actions.redefine("test-action") do
|
89
|
+
assert respond_to?(:example)
|
90
|
+
assert_equal 5, example
|
91
|
+
end
|
92
|
+
run! example: 5
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def actions
|
100
|
+
@actions ||= Houston::Actions.new
|
101
|
+
end
|
102
|
+
|
103
|
+
def run!(params={}, options={})
|
104
|
+
actions.run "test-action", params, options.merge(async: false)
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ConfigurationTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
|
6
|
+
context "#action" do
|
7
|
+
should "define a new action" do
|
8
|
+
config.action("test-action") { }
|
9
|
+
assert config.actions.exists?("test-action")
|
10
|
+
end
|
11
|
+
|
12
|
+
should "raise if a block isn't given" do
|
13
|
+
assert_raises ArgumentError do
|
14
|
+
config.action("test-action")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
should "raise if the action has already been defined" do
|
19
|
+
config.action("test-action") { }
|
20
|
+
assert_raises ArgumentError do
|
21
|
+
config.action("test-action") { }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
context "#every" do
|
28
|
+
should "define an action and a timer that triggers it when you pass two arguments" do
|
29
|
+
assert_difference "config.triggers.count", +1 do
|
30
|
+
config.every("10m", "test-action") { }
|
31
|
+
assert config.actions.exists?("test-action")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
should "define an action and a timer that triggers it when you pass a hash" do
|
36
|
+
assert_difference "config.triggers.count", +1 do
|
37
|
+
config.every("10m" => "test-action") { }
|
38
|
+
assert config.actions.exists?("test-action")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
should "define an action with a made-up name (for now) when you don't specify an action" do
|
43
|
+
action = nil
|
44
|
+
assert_difference ["config.triggers.count", "config.actions.count"], +1 do
|
45
|
+
action = config.every("10m") { }
|
46
|
+
end
|
47
|
+
assert_match /10m:[a-f0-9]{8,}/, action.name
|
48
|
+
end
|
49
|
+
|
50
|
+
should "define a timer for an existing action" do
|
51
|
+
assert_difference "config.triggers.count", +1 do
|
52
|
+
config.action("test-action") { }
|
53
|
+
config.every "10m" => "test-action"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
should "raise if the action requires any params" do
|
58
|
+
config.action("test-action", ["required_param"]) { }
|
59
|
+
assert_raises Houston::Observer::MissingParamError do
|
60
|
+
config.every "10m" => "test-action"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
context "#on" do
|
67
|
+
should "define an action and an observer that triggers it when you pass two arguments" do
|
68
|
+
assert_difference "config.triggers.count", +1 do
|
69
|
+
config.on("hooks:example", "test-action") { }
|
70
|
+
assert config.actions.exists?("test-action")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
should "define the action to require the params passed by the event" do
|
75
|
+
action = config.on("hooks:example", "test-action") { }
|
76
|
+
assert_equal %w{params}, action.required_params
|
77
|
+
end
|
78
|
+
|
79
|
+
should "define an action and an observer that triggers it when you pass a hash" do
|
80
|
+
assert_difference "config.triggers.count", +1 do
|
81
|
+
config.on("hooks:example" => "test-action") { }
|
82
|
+
assert config.actions.exists?("test-action")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
should "define an observer for an existing action" do
|
87
|
+
assert_difference "config.triggers.count", +1 do
|
88
|
+
config.action("test-action") { }
|
89
|
+
config.on "hooks:example" => "test-action"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
should "raise if the event doesn't supply the action's required params" do
|
94
|
+
config.action("test-action", ["required_param"]) { }
|
95
|
+
assert_raises Houston::Observer::MissingParamError do
|
96
|
+
config.on "hooks:example" => "test-action"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def config
|
105
|
+
@config ||= Houston::Configuration.new
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -1,17 +1,101 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
|
+
Houston.register_events {{
|
4
|
+
"test" => params("example").desc("For testing the observer"),
|
5
|
+
"test0" => desc("For testing the observer, takes 0 params")
|
6
|
+
}}
|
7
|
+
|
3
8
|
class ObserverTest < ActiveSupport::TestCase
|
4
9
|
|
5
10
|
|
6
11
|
context "Houston.observer.once" do
|
7
12
|
should "trigger a callback and then unregister it" do
|
8
13
|
calls = 0
|
9
|
-
Houston.observer.once("
|
10
|
-
Houston.observer.fire "
|
11
|
-
Houston.observer.fire "
|
14
|
+
Houston.observer.once("test0") { calls += 1 }
|
15
|
+
Houston.observer.fire "test0"
|
16
|
+
Houston.observer.fire "test0"
|
12
17
|
assert_equal 1, calls, "Expected the callback to be called only once"
|
13
18
|
end
|
14
19
|
end
|
15
20
|
|
16
21
|
|
22
|
+
context "Houston.observer.fire" do
|
23
|
+
should "raise if called with an argument that isn't a Hash" do
|
24
|
+
assert_raises ArgumentError do
|
25
|
+
Houston.observer.fire "test", 5
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
should "raise if called with more than one argument" do
|
30
|
+
assert_raises ArgumentError do
|
31
|
+
Houston.observer.fire "test", {}, 5
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
should "raise if the event isn't registered" do
|
36
|
+
assert_raises Houston::Observer::UnregisteredEventError do
|
37
|
+
Houston.observer.fire "test2"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
should "raise if the event is triggered without a registered param" do
|
42
|
+
assert_raises Houston::Observer::MissingParamError do
|
43
|
+
Houston.observer.fire "test", {}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
should "raise if the event is triggered with an unregistered param" do
|
48
|
+
assert_raises Houston::Observer::UnregisteredParamError do
|
49
|
+
Houston.observer.fire "test", {example: 5, counterexample: 1}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# We require that all params be serializable so that (1) actions can
|
54
|
+
# be recorded with all their state (and retried) and (2) the actions
|
55
|
+
# system can be "outsourced" to a background job system like Sidekiq
|
56
|
+
# if need be.
|
57
|
+
should "raise if the event is triggered with an unserializable param" do
|
58
|
+
unserializable_object = Class.new.new
|
59
|
+
assert_raises Houston::Serializer::UnserializableError do
|
60
|
+
Houston.observer.fire "test", {example: unserializable_object}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
should "invoke callbacks with {} if called with no arguments" do
|
65
|
+
callback_args = :not_called
|
66
|
+
Houston.observer.on("test0") { |*args| callback_args = args }
|
67
|
+
Houston.observer.fire "test0"
|
68
|
+
assert_equal 1, callback_args.length
|
69
|
+
assert_equal [], callback_args[0].methods(false)
|
70
|
+
end
|
71
|
+
|
72
|
+
should "invoke callbacks with a copy of the params it was called with" do
|
73
|
+
params = {example: 5}
|
74
|
+
callback_params = :not_called
|
75
|
+
Houston.observer.on("test") { |params| callback_params = params }
|
76
|
+
Houston.observer.fire "test", params
|
77
|
+
assert_equal params, callback_params.to_h
|
78
|
+
refute_equal params.object_id, callback_params.to_h.object_id,
|
79
|
+
"Expected the observer to be invoked with a copy of the params; not the actual params itself"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
context "callback params" do
|
85
|
+
should "be retrievable by array-style access" do
|
86
|
+
callback_params = :not_called
|
87
|
+
Houston.observer.on("test") { |params| callback_params = params }
|
88
|
+
Houston.observer.fire "test", example: 5
|
89
|
+
assert_equal 5, callback_params[:example]
|
90
|
+
end
|
91
|
+
|
92
|
+
should "be retrievable by method-style access" do
|
93
|
+
callback_params = :not_called
|
94
|
+
Houston.observer.on("test") { |params| callback_params = params }
|
95
|
+
Houston.observer.fire "test", example: 5
|
96
|
+
assert_equal 5, callback_params.example
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
17
101
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class PersistentTriggerTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
|
6
|
+
context ".at" do
|
7
|
+
should "define a new trigger" do
|
8
|
+
trigger = PersistentTrigger.at("2:30pm", "test-action", example: 5)
|
9
|
+
assert_equal :at, trigger.type
|
10
|
+
assert_equal "2:30pm", trigger.value
|
11
|
+
assert_equal "test-action", trigger.action
|
12
|
+
assert_equal({example: 5}, trigger.params)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
context "Validations:" do
|
18
|
+
should "require the action to exist" do
|
19
|
+
trigger = PersistentTrigger.new(action: "undefined-action").tap(&:validate)
|
20
|
+
assert_match /is not defined/, trigger.errors[:action].join
|
21
|
+
end
|
22
|
+
|
23
|
+
should "require the type to be one of :at, :every, or :on" do
|
24
|
+
trigger = PersistentTrigger.new
|
25
|
+
|
26
|
+
trigger.type = :at
|
27
|
+
refute trigger.tap(&:validate).errors[:type].any?
|
28
|
+
|
29
|
+
trigger.type = :every
|
30
|
+
refute trigger.tap(&:validate).errors[:type].any?
|
31
|
+
|
32
|
+
trigger.type = :on
|
33
|
+
refute trigger.tap(&:validate).errors[:type].any?
|
34
|
+
|
35
|
+
trigger.type = :nope
|
36
|
+
assert trigger.tap(&:validate).errors[:type].any?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
context "#save!" do
|
42
|
+
setup do
|
43
|
+
Houston.config.actions.define("test-action") { }
|
44
|
+
end
|
45
|
+
|
46
|
+
teardown do
|
47
|
+
Houston.config.actions.undefine("test-action")
|
48
|
+
end
|
49
|
+
|
50
|
+
should "add the trigger to the database" do
|
51
|
+
assert_difference "PersistentTrigger.count", +1 do
|
52
|
+
PersistentTrigger.at("1:30pm", "test-action", example: 5).save!
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
should "register the trigger" do
|
57
|
+
assert_difference "Houston.config.triggers.count", +1 do
|
58
|
+
PersistentTrigger.at("2:30pm", "test-action", example: 5).save!
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
context ".load_all" do
|
65
|
+
setup do
|
66
|
+
PersistentTrigger.all.insert({
|
67
|
+
PersistentTrigger.column_for_attribute(:type) => "at",
|
68
|
+
PersistentTrigger.column_for_attribute(:value) => "9:00am",
|
69
|
+
PersistentTrigger.column_for_attribute(:action) => "test-action",
|
70
|
+
PersistentTrigger.column_for_attribute(:params) => {example: 5}
|
71
|
+
})
|
72
|
+
end
|
73
|
+
|
74
|
+
teardown do
|
75
|
+
Houston.config.triggers.clear
|
76
|
+
end
|
77
|
+
|
78
|
+
should "load all persisted triggers and register them" do
|
79
|
+
assert_equal 1, PersistentTrigger.count
|
80
|
+
assert_difference "Houston.config.triggers.count", +1 do
|
81
|
+
PersistentTrigger.load_all
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
should "be idempotent" do
|
86
|
+
PersistentTrigger.load_all
|
87
|
+
assert_no_difference "Houston.config.triggers.count" do
|
88
|
+
PersistentTrigger.load_all
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class SerializerTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
|
6
|
+
should "serialize JSON literals conventionally" do
|
7
|
+
assert_serializes(5, to: '5')
|
8
|
+
assert_serializes("five", to: '"five"')
|
9
|
+
assert_serializes(false, to: 'false')
|
10
|
+
assert_serializes(nil, to: 'null')
|
11
|
+
end
|
12
|
+
|
13
|
+
should "let Oj serialize dates via iso8601" do
|
14
|
+
assert_equal '{"^O":"Date","iso8601":"2016-07-15"}',
|
15
|
+
dump(Date.new(2016, 7, 15))
|
16
|
+
|
17
|
+
assert_equal '{"^O":"DateTime","iso8601":"2016-07-15T01:30:59+00:00"}',
|
18
|
+
dump(DateTime.new(2016, 7, 15, 1, 30, 59.457))
|
19
|
+
end
|
20
|
+
|
21
|
+
should "let Oj serialize times" do
|
22
|
+
assert_equal '{"^t":1468587630.542999999e59}',
|
23
|
+
dump(Time.new(2016, 7, 15, 13, 1, 30, 59.457))
|
24
|
+
end
|
25
|
+
|
26
|
+
should "serialize Houston::ReadonlyHash like a hash" do
|
27
|
+
assert_equal '{":x":5}', dump(Houston::ReadonlyHash.new(x: 5))
|
28
|
+
end
|
29
|
+
|
30
|
+
should "serialize ActiveSupport::TimeWithZone like a DateTime" do
|
31
|
+
time = ActiveSupport::TimeZone["Mountain Time (US & Canada)"].now
|
32
|
+
assert_equal '{"^O":"DateTime","iso8601":"' + time.iso8601 + '"}', dump(time)
|
33
|
+
end
|
34
|
+
|
35
|
+
should "serialize ActiveRecord objects by their raw attributes" do
|
36
|
+
project = Project.create!(name: "Test", slug: "test")
|
37
|
+
assert_serializes(project,
|
38
|
+
like: /"class":"Project","attributes":{.*},"\^S":"Houston::ActiveRecordSerializer"/)
|
39
|
+
end
|
40
|
+
|
41
|
+
should "serialize objects that refer to ActiveRecord objects" do
|
42
|
+
project = Project.create!(name: "Test", slug: "test")
|
43
|
+
commit = Commit.new(project: project, message: "Commit Message")
|
44
|
+
antecedent = TicketAntecedent.new(commit, "Err", 45)
|
45
|
+
assert_serializes(antecedent,
|
46
|
+
like: /"ticket_or_commit":{"class":"Commit".*},"kind":"Err","id":45,"\^S":"Houston::TicketAntecedentSerializer"/)
|
47
|
+
end
|
48
|
+
|
49
|
+
should "work even when you set a value" do
|
50
|
+
project = Project.create!(name: "Test", slug: "test")
|
51
|
+
project.updated_at = 1.week.after(project.updated_at) # ActiveSupport::TimeWithZone
|
52
|
+
refute_match /"^o":"ActiveSupport::TimeWithZone"/, dump(x: project)
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def assert_serializes(object, options={})
|
59
|
+
serialized = dump(object)
|
60
|
+
|
61
|
+
if expectation = options[:to]
|
62
|
+
assert_equal expectation, serialized, "The object wasn't serialized as expected"
|
63
|
+
end
|
64
|
+
|
65
|
+
if expectation = options[:like]
|
66
|
+
assert_match expectation, serialized, "The object wasn't serialized as expected"
|
67
|
+
end
|
68
|
+
|
69
|
+
assert_equal object, load(serialized), "The object wasn't deserialized as expected"
|
70
|
+
end
|
71
|
+
|
72
|
+
def load(string)
|
73
|
+
Houston::Serializer.new.load(string)
|
74
|
+
end
|
75
|
+
|
76
|
+
def dump(object)
|
77
|
+
Houston::Serializer.new.dump(object)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|