plan_my_stuff 0.3.0 → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +569 -38
- data/app/controllers/plan_my_stuff/comments_controller.rb +5 -1
- data/app/controllers/plan_my_stuff/issues/approvals_controller.rb +102 -0
- data/app/controllers/plan_my_stuff/issues/closures_controller.rb +37 -0
- data/app/controllers/plan_my_stuff/issues/links_controller.rb +127 -0
- data/app/controllers/plan_my_stuff/issues/takes_controller.rb +88 -0
- data/app/controllers/plan_my_stuff/issues/viewers_controller.rb +48 -0
- data/app/controllers/plan_my_stuff/issues/waitings_controller.rb +47 -0
- data/app/controllers/plan_my_stuff/issues_controller.rb +22 -55
- data/app/controllers/plan_my_stuff/labels_controller.rb +4 -4
- data/app/controllers/plan_my_stuff/project_items/assignments_controller.rb +75 -0
- data/app/controllers/plan_my_stuff/project_items/statuses_controller.rb +40 -0
- data/app/controllers/plan_my_stuff/project_items_controller.rb +0 -75
- data/app/controllers/plan_my_stuff/projects_controller.rb +11 -1
- data/app/controllers/plan_my_stuff/testing_project_items/results_controller.rb +54 -0
- data/app/controllers/plan_my_stuff/testing_project_items_controller.rb +39 -0
- data/app/controllers/plan_my_stuff/testing_projects_controller.rb +93 -0
- data/app/controllers/plan_my_stuff/webhooks/aws_controller.rb +148 -0
- data/app/controllers/plan_my_stuff/webhooks/github_controller.rb +284 -0
- data/app/jobs/plan_my_stuff/application_job.rb +9 -0
- data/app/jobs/plan_my_stuff/reminders_sweep_job.rb +81 -0
- data/app/views/plan_my_stuff/comments/partials/_form.html.erb +7 -0
- data/app/views/plan_my_stuff/issues/partials/_approvals.html.erb +87 -0
- data/app/views/plan_my_stuff/issues/partials/_labels.html.erb +2 -2
- data/app/views/plan_my_stuff/issues/partials/_links.html.erb +70 -0
- data/app/views/plan_my_stuff/issues/partials/_viewers.html.erb +2 -2
- data/app/views/plan_my_stuff/issues/show.html.erb +46 -3
- data/app/views/plan_my_stuff/projects/index.html.erb +15 -1
- data/app/views/plan_my_stuff/projects/show.html.erb +10 -5
- data/app/views/plan_my_stuff/testing_project_items/new.html.erb +12 -0
- data/app/views/plan_my_stuff/testing_project_items/results/new.html.erb +22 -0
- data/app/views/plan_my_stuff/testing_projects/edit.html.erb +7 -0
- data/app/views/plan_my_stuff/testing_projects/new.html.erb +7 -0
- data/app/views/plan_my_stuff/testing_projects/partials/_form.html.erb +39 -0
- data/app/views/plan_my_stuff/testing_projects/partials/_item.html.erb +51 -0
- data/app/views/plan_my_stuff/testing_projects/partials/items/_form.html.erb +35 -0
- data/app/views/plan_my_stuff/testing_projects/show.html.erb +65 -0
- data/config/routes.rb +38 -15
- data/lib/generators/plan_my_stuff/install/templates/initializer.rb +172 -5
- data/lib/plan_my_stuff/application_record.rb +121 -0
- data/lib/plan_my_stuff/approval.rb +80 -0
- data/lib/plan_my_stuff/archive/sweep.rb +85 -0
- data/lib/plan_my_stuff/archive.rb +14 -0
- data/lib/plan_my_stuff/aws_sns_simulator.rb +110 -0
- data/lib/plan_my_stuff/base_project.rb +661 -0
- data/lib/plan_my_stuff/base_project_item.rb +562 -0
- data/lib/plan_my_stuff/base_project_metadata.rb +16 -0
- data/lib/plan_my_stuff/cache.rb +197 -0
- data/lib/plan_my_stuff/client.rb +7 -0
- data/lib/plan_my_stuff/comment.rb +171 -50
- data/lib/plan_my_stuff/configuration.rb +210 -10
- data/lib/plan_my_stuff/custom_fields.rb +31 -17
- data/lib/plan_my_stuff/engine.rb +0 -4
- data/lib/plan_my_stuff/errors.rb +49 -0
- data/lib/plan_my_stuff/graphql/queries.rb +392 -0
- data/lib/plan_my_stuff/issue.rb +1476 -175
- data/lib/plan_my_stuff/issue_metadata.rb +122 -0
- data/lib/plan_my_stuff/label.rb +82 -11
- data/lib/plan_my_stuff/link.rb +144 -0
- data/lib/plan_my_stuff/notifications.rb +142 -0
- data/lib/plan_my_stuff/pipeline/issue_linker.rb +62 -0
- data/lib/plan_my_stuff/pipeline/status.rb +44 -0
- data/lib/plan_my_stuff/pipeline.rb +293 -0
- data/lib/plan_my_stuff/project.rb +30 -693
- data/lib/plan_my_stuff/project_item.rb +3 -417
- data/lib/plan_my_stuff/project_item_metadata.rb +55 -0
- data/lib/plan_my_stuff/project_metadata.rb +9 -3
- data/lib/plan_my_stuff/reminders/closer.rb +70 -0
- data/lib/plan_my_stuff/reminders/fire.rb +129 -0
- data/lib/plan_my_stuff/reminders/sweep.rb +54 -0
- data/lib/plan_my_stuff/reminders.rb +16 -0
- data/lib/plan_my_stuff/test_helpers.rb +260 -15
- data/lib/plan_my_stuff/testing_project.rb +291 -0
- data/lib/plan_my_stuff/testing_project_item.rb +216 -0
- data/lib/plan_my_stuff/testing_project_metadata.rb +94 -0
- data/lib/plan_my_stuff/user_resolver.rb +8 -3
- data/lib/plan_my_stuff/version.rb +1 -1
- data/lib/plan_my_stuff/webhook_replayer.rb +280 -0
- data/lib/plan_my_stuff.rb +15 -0
- data/lib/tasks/plan_my_stuff.rake +179 -0
- metadata +77 -3
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlanMyStuff
|
|
4
|
+
module Reminders
|
|
5
|
+
# Walks a single repo's waiting issues, dispatching each to +Closer+
|
|
6
|
+
# when past the inactivity ceiling or +Fire+ when a reminder is due.
|
|
7
|
+
# Called from +RemindersSweepJob+; no ActiveJob dependency here so
|
|
8
|
+
# the logic stays unit-testable and callable from a plain rake task.
|
|
9
|
+
class Sweep
|
|
10
|
+
# @param repo [Symbol, String] repo key or full name
|
|
11
|
+
# @param now [Time] clock reference
|
|
12
|
+
#
|
|
13
|
+
def initialize(repo:, now: Time.now.utc)
|
|
14
|
+
@repo = repo
|
|
15
|
+
@now = now.utc
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Runs the sweep. No-op when +config.reminders_enabled+ is false.
|
|
19
|
+
#
|
|
20
|
+
# @return [void]
|
|
21
|
+
#
|
|
22
|
+
def call
|
|
23
|
+
return unless PlanMyStuff.configuration.reminders_enabled
|
|
24
|
+
|
|
25
|
+
candidates.each do |issue|
|
|
26
|
+
if Closer.should_close?(issue, now: @now)
|
|
27
|
+
Closer.new(issue, now: @now).call
|
|
28
|
+
elsif Fire.ready?(issue, now: @now)
|
|
29
|
+
Fire.new(issue, now: @now).call
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
# Issues carrying either waiting label, deduplicated by number.
|
|
37
|
+
# GitHub's labels filter is AND across multiple labels, so we
|
|
38
|
+
# query each label separately and merge.
|
|
39
|
+
#
|
|
40
|
+
# @return [Array<PlanMyStuff::Issue>]
|
|
41
|
+
#
|
|
42
|
+
def candidates
|
|
43
|
+
user_label = PlanMyStuff.configuration.waiting_on_user_label
|
|
44
|
+
approval_label = PlanMyStuff.configuration.waiting_on_approval_label
|
|
45
|
+
|
|
46
|
+
by_label = [user_label, approval_label].flat_map do |label|
|
|
47
|
+
PlanMyStuff::Issue.list(repo: @repo, labels: [label], state: :open)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
by_label.uniq(&:number)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PlanMyStuff
|
|
4
|
+
# Follow-up reminder engine. The +Sweep+ class walks the waiting issues
|
|
5
|
+
# in a repo and dispatches to +Fire+ for reminders that are due or
|
|
6
|
+
# +Closer+ for issues that have exceeded the inactivity threshold.
|
|
7
|
+
#
|
|
8
|
+
# Entry point for the sweep lives in +RemindersSweepJob+; this module
|
|
9
|
+
# holds the POROs so they can be unit-tested without ActiveJob.
|
|
10
|
+
module Reminders
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
require_relative 'reminders/closer'
|
|
15
|
+
require_relative 'reminders/fire'
|
|
16
|
+
require_relative 'reminders/sweep'
|
|
@@ -58,8 +58,8 @@ module PlanMyStuff
|
|
|
58
58
|
custom_fields: {},
|
|
59
59
|
}.merge(metadata))
|
|
60
60
|
|
|
61
|
-
issue.
|
|
62
|
-
issue.
|
|
61
|
+
issue.metadata = issue_metadata
|
|
62
|
+
issue.__send__(:persisted!)
|
|
63
63
|
|
|
64
64
|
body_comment = build_comment(
|
|
65
65
|
body: body,
|
|
@@ -98,8 +98,8 @@ module PlanMyStuff
|
|
|
98
98
|
custom_fields: {},
|
|
99
99
|
}.merge(metadata))
|
|
100
100
|
|
|
101
|
-
comment.
|
|
102
|
-
comment.
|
|
101
|
+
comment.metadata = comment_metadata
|
|
102
|
+
comment.__send__(:persisted!)
|
|
103
103
|
comment
|
|
104
104
|
end
|
|
105
105
|
|
|
@@ -124,7 +124,7 @@ module PlanMyStuff
|
|
|
124
124
|
statuses: status_options,
|
|
125
125
|
fields: [{ id: 'field_status', name: 'Status', options: status_options }],
|
|
126
126
|
)
|
|
127
|
-
project.
|
|
127
|
+
project.__send__(:persisted!)
|
|
128
128
|
|
|
129
129
|
project.items = items.map do |item_hash|
|
|
130
130
|
PlanMyStuff::ProjectItem.build(
|
|
@@ -143,6 +143,54 @@ module PlanMyStuff
|
|
|
143
143
|
|
|
144
144
|
project
|
|
145
145
|
end
|
|
146
|
+
|
|
147
|
+
# Sets approvals on an in-memory +Issue+ without hitting the API.
|
|
148
|
+
# Accepts user objects or integer user_ids.
|
|
149
|
+
#
|
|
150
|
+
# @param issue [PlanMyStuff::Issue]
|
|
151
|
+
# @param approved [Array<Object, Integer>] approvers who have approved
|
|
152
|
+
# @param pending [Array<Object, Integer>] approvers who have not yet approved
|
|
153
|
+
#
|
|
154
|
+
# @return [Array<PlanMyStuff::Approval>] the records written to +issue.metadata.approvals+
|
|
155
|
+
#
|
|
156
|
+
def stub_approvals(issue, approved: [], pending: [])
|
|
157
|
+
records =
|
|
158
|
+
pending.map { |u| PlanMyStuff::Approval.new(user_id: extract_user_id(u), status: 'pending') } +
|
|
159
|
+
approved.map do |u|
|
|
160
|
+
PlanMyStuff::Approval.new(user_id: extract_user_id(u), status: 'approved', approved_at: Time.current)
|
|
161
|
+
end
|
|
162
|
+
issue.metadata.approvals = records
|
|
163
|
+
records
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# @return [Integer]
|
|
167
|
+
def extract_user_id(user)
|
|
168
|
+
user.is_a?(Integer) ? user : PlanMyStuff::UserResolver.user_id(user)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Captures +plan_my_stuff.*+ +ActiveSupport::Notifications+ events fired
|
|
173
|
+
# inside the given block. Lets consuming apps assert that their code
|
|
174
|
+
# triggers gem events without threading subscriptions through every spec.
|
|
175
|
+
#
|
|
176
|
+
# Usage:
|
|
177
|
+
#
|
|
178
|
+
# events = PlanMyStuff::TestHelpers::Notifications.capture do
|
|
179
|
+
# PlanMyStuff::Issue.create!(...)
|
|
180
|
+
# end
|
|
181
|
+
# events.first[:name] # => "plan_my_stuff.issue.created"
|
|
182
|
+
# events.first[:payload] # => { issue:, user:, timestamp:, ... }
|
|
183
|
+
#
|
|
184
|
+
module Notifications
|
|
185
|
+
module_function
|
|
186
|
+
|
|
187
|
+
# @return [Array<Hash>] events as +{ name: String, payload: Hash }+
|
|
188
|
+
def capture(&)
|
|
189
|
+
events = []
|
|
190
|
+
callback = -> (name, _start, _finish, _id, payload) { events << { name: name, payload: payload } }
|
|
191
|
+
ActiveSupport::Notifications.subscribed(callback, /^plan_my_stuff\./, &)
|
|
192
|
+
events
|
|
193
|
+
end
|
|
146
194
|
end
|
|
147
195
|
|
|
148
196
|
# RSpec matchers included when the module is included in a test config.
|
|
@@ -218,6 +266,41 @@ module PlanMyStuff
|
|
|
218
266
|
expect_pms_action(:item_assigned, 'item to be assigned', **filters)
|
|
219
267
|
end
|
|
220
268
|
|
|
269
|
+
# @param filters [Hash] attribute filters (e.g. assignee:, project_number:)
|
|
270
|
+
def expect_pms_pipeline_submitted(**filters)
|
|
271
|
+
expect_pms_action(:pipeline_submitted, 'pipeline submission', **filters)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# @param filters [Hash] attribute filters (e.g. item_id:)
|
|
275
|
+
def expect_pms_pipeline_taken(**filters)
|
|
276
|
+
expect_pms_action(:pipeline_started, 'pipeline item to be taken', **filters)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# @param filters [Hash] attribute filters (e.g. item_id:)
|
|
280
|
+
def expect_pms_pipeline_in_review(**filters)
|
|
281
|
+
expect_pms_action(:pipeline_in_review, 'pipeline item marked in review', **filters)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# @param filters [Hash] attribute filters (e.g. item_id:)
|
|
285
|
+
def expect_pms_pipeline_testing(**filters)
|
|
286
|
+
expect_pms_action(:pipeline_testing, 'pipeline item moved to testing', **filters)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# @param filters [Hash] attribute filters (e.g. item_id:)
|
|
290
|
+
def expect_pms_pipeline_ready_for_release(**filters)
|
|
291
|
+
expect_pms_action(:pipeline_ready_for_release, 'pipeline item marked ready for release', **filters)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# @param filters [Hash] attribute filters (e.g. project_number:, commit_sha:)
|
|
295
|
+
def expect_pms_pipeline_deployment_started(**filters)
|
|
296
|
+
expect_pms_action(:pipeline_deployment_started, 'pipeline deployment started', **filters)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# @param filters [Hash] attribute filters (e.g. item_id:, deployment_id:)
|
|
300
|
+
def expect_pms_pipeline_deployment_completed(**filters)
|
|
301
|
+
expect_pms_action(:pipeline_deployment_completed, 'pipeline deployment completed', **filters)
|
|
302
|
+
end
|
|
303
|
+
|
|
221
304
|
private
|
|
222
305
|
|
|
223
306
|
# @return [void]
|
|
@@ -265,10 +348,11 @@ module PlanMyStuff
|
|
|
265
348
|
stub_comment_class_methods!
|
|
266
349
|
stub_project_class_methods!
|
|
267
350
|
stub_project_item_class_methods!
|
|
351
|
+
stub_pipeline_methods!
|
|
268
352
|
@_test_mode = true
|
|
269
353
|
end
|
|
270
354
|
|
|
271
|
-
# Restores original class methods overwritten by test_mode!
|
|
355
|
+
# Restores original class / instance methods overwritten by test_mode!
|
|
272
356
|
#
|
|
273
357
|
# @return [void]
|
|
274
358
|
#
|
|
@@ -276,8 +360,13 @@ module PlanMyStuff
|
|
|
276
360
|
return unless @_test_mode
|
|
277
361
|
|
|
278
362
|
(@_test_mode_originals || {}).each do |klass, methods|
|
|
279
|
-
methods.each do |
|
|
280
|
-
|
|
363
|
+
methods.each do |key, original|
|
|
364
|
+
kind, name = key
|
|
365
|
+
if kind == :instance
|
|
366
|
+
klass.define_method(name, original)
|
|
367
|
+
else
|
|
368
|
+
klass.define_singleton_method(name, original)
|
|
369
|
+
end
|
|
281
370
|
end
|
|
282
371
|
end
|
|
283
372
|
|
|
@@ -288,7 +377,7 @@ module PlanMyStuff
|
|
|
288
377
|
|
|
289
378
|
private
|
|
290
379
|
|
|
291
|
-
# Saves the original method so it can be restored by exit_test_mode!
|
|
380
|
+
# Saves the original class method so it can be restored by exit_test_mode!
|
|
292
381
|
#
|
|
293
382
|
# @param klass [Class]
|
|
294
383
|
# @param method_name [Symbol]
|
|
@@ -297,13 +386,26 @@ module PlanMyStuff
|
|
|
297
386
|
#
|
|
298
387
|
def save_original(klass, method_name)
|
|
299
388
|
@_test_mode_originals[klass] ||= {}
|
|
300
|
-
@_test_mode_originals[klass][method_name] = klass.method(method_name)
|
|
389
|
+
@_test_mode_originals[klass][[:class, method_name]] = klass.method(method_name)
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# Saves the original instance method so it can be restored by exit_test_mode!
|
|
393
|
+
#
|
|
394
|
+
# @param klass [Class]
|
|
395
|
+
# @param method_name [Symbol]
|
|
396
|
+
#
|
|
397
|
+
# @return [void]
|
|
398
|
+
#
|
|
399
|
+
def save_instance_original(klass, method_name)
|
|
400
|
+
@_test_mode_originals[klass] ||= {}
|
|
401
|
+
@_test_mode_originals[klass][[:instance, method_name]] = klass.instance_method(method_name)
|
|
301
402
|
end
|
|
302
403
|
|
|
303
404
|
# @return [void]
|
|
304
405
|
def stub_issue_class_methods!
|
|
305
406
|
issue_mod = PlanMyStuff::Issue
|
|
306
|
-
%i[create! find list update!
|
|
407
|
+
%i[create! find list update!].each { |m| save_original(issue_mod, m) }
|
|
408
|
+
%i[add_viewers remove_viewers].each { |m| save_instance_original(issue_mod, m) }
|
|
307
409
|
|
|
308
410
|
issue_mod.define_singleton_method(:create!) do |**params|
|
|
309
411
|
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
@@ -359,19 +461,19 @@ module PlanMyStuff
|
|
|
359
461
|
nil
|
|
360
462
|
end
|
|
361
463
|
|
|
362
|
-
issue_mod.
|
|
464
|
+
issue_mod.define_method(:add_viewers) do |user_ids:, user: nil|
|
|
363
465
|
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
364
466
|
type: :viewers_added,
|
|
365
|
-
params:
|
|
467
|
+
params: { number: number, repo: repo&.to_s, user_ids: Array.wrap(user_ids), user: user },
|
|
366
468
|
}
|
|
367
469
|
|
|
368
470
|
nil
|
|
369
471
|
end
|
|
370
472
|
|
|
371
|
-
issue_mod.
|
|
473
|
+
issue_mod.define_method(:remove_viewers) do |user_ids:, user: nil|
|
|
372
474
|
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
373
475
|
type: :viewers_removed,
|
|
374
|
-
params:
|
|
476
|
+
params: { number: number, repo: repo&.to_s, user_ids: Array.wrap(user_ids), user: user },
|
|
375
477
|
}
|
|
376
478
|
|
|
377
479
|
nil
|
|
@@ -505,5 +607,148 @@ module PlanMyStuff
|
|
|
505
607
|
nil
|
|
506
608
|
end
|
|
507
609
|
end
|
|
610
|
+
|
|
611
|
+
# @return [void]
|
|
612
|
+
def stub_pipeline_methods!
|
|
613
|
+
pipeline_mod = PlanMyStuff::Pipeline
|
|
614
|
+
%i[
|
|
615
|
+
submit! take! mark_in_review! request_testing! mark_ready_for_release! start_deployment! complete_deployment!
|
|
616
|
+
].each { |m| save_original(pipeline_mod, m) }
|
|
617
|
+
|
|
618
|
+
pipeline_mod.define_singleton_method(:submit!) do |issue, assignee:, project_number: nil|
|
|
619
|
+
number =
|
|
620
|
+
project_number || PlanMyStuff.configuration.pipeline_project_number ||
|
|
621
|
+
PlanMyStuff.configuration.default_project_number
|
|
622
|
+
|
|
623
|
+
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
624
|
+
type: :pipeline_submitted,
|
|
625
|
+
params: {
|
|
626
|
+
issue_number: issue.respond_to?(:number) ? issue.number : nil,
|
|
627
|
+
assignee: assignee,
|
|
628
|
+
project_number: number,
|
|
629
|
+
},
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
project = PlanMyStuff::TestHelpers.build_project(number: number || 1)
|
|
633
|
+
PlanMyStuff::ProjectItem.build(
|
|
634
|
+
{
|
|
635
|
+
id: "PVTI_fake_#{rand(10_000)}",
|
|
636
|
+
title: issue.respond_to?(:title) ? issue.title : 'Untitled',
|
|
637
|
+
number: issue.respond_to?(:number) ? issue.number : nil,
|
|
638
|
+
status: PlanMyStuff::Pipeline::Status::SUBMITTED,
|
|
639
|
+
field_values: {},
|
|
640
|
+
},
|
|
641
|
+
project: project,
|
|
642
|
+
)
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
pipeline_mod.define_singleton_method(:take!) do |project_item|
|
|
646
|
+
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
647
|
+
type: :pipeline_started,
|
|
648
|
+
params: {
|
|
649
|
+
item_id: project_item.respond_to?(:id) ? project_item.id : nil,
|
|
650
|
+
issue_number: project_item.respond_to?(:number) ? project_item.number : nil,
|
|
651
|
+
},
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
nil
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
pipeline_mod.define_singleton_method(:mark_in_review!) do |project_item|
|
|
658
|
+
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
659
|
+
type: :pipeline_in_review,
|
|
660
|
+
params: {
|
|
661
|
+
item_id: project_item.respond_to?(:id) ? project_item.id : nil,
|
|
662
|
+
issue_number: project_item.respond_to?(:number) ? project_item.number : nil,
|
|
663
|
+
},
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
nil
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
pipeline_mod.define_singleton_method(:request_testing!) do |project_item|
|
|
670
|
+
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
671
|
+
type: :pipeline_testing,
|
|
672
|
+
params: {
|
|
673
|
+
item_id: project_item.respond_to?(:id) ? project_item.id : nil,
|
|
674
|
+
issue_number: project_item.respond_to?(:number) ? project_item.number : nil,
|
|
675
|
+
},
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
nil
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
pipeline_mod.define_singleton_method(:mark_ready_for_release!) do |project_item|
|
|
682
|
+
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
683
|
+
type: :pipeline_ready_for_release,
|
|
684
|
+
params: {
|
|
685
|
+
item_id: project_item.respond_to?(:id) ? project_item.id : nil,
|
|
686
|
+
issue_number: project_item.respond_to?(:number) ? project_item.number : nil,
|
|
687
|
+
},
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
nil
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
pipeline_mod.define_singleton_method(:start_deployment!) do |project_number: nil, commit_sha: nil|
|
|
694
|
+
number =
|
|
695
|
+
project_number || PlanMyStuff.configuration.pipeline_project_number ||
|
|
696
|
+
PlanMyStuff.configuration.default_project_number
|
|
697
|
+
|
|
698
|
+
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
699
|
+
type: :pipeline_deployment_started,
|
|
700
|
+
params: {
|
|
701
|
+
project_number: number,
|
|
702
|
+
commit_sha: commit_sha,
|
|
703
|
+
},
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
[]
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
pipeline_mod.define_singleton_method(:complete_deployment!) do |project_item, deployment_id: nil|
|
|
710
|
+
PlanMyStuff::TestHelpers.recorded_actions << {
|
|
711
|
+
type: :pipeline_deployment_completed,
|
|
712
|
+
params: {
|
|
713
|
+
item_id: project_item.respond_to?(:id) ? project_item.id : nil,
|
|
714
|
+
issue_number: project_item.respond_to?(:number) ? project_item.number : nil,
|
|
715
|
+
deployment_id: deployment_id,
|
|
716
|
+
},
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
nil
|
|
720
|
+
end
|
|
721
|
+
end
|
|
722
|
+
end
|
|
723
|
+
end
|
|
724
|
+
|
|
725
|
+
if defined?(RSpec::Matchers)
|
|
726
|
+
# Asserts that a +plan_my_stuff.<name>+ event was fired inside the block.
|
|
727
|
+
# Chain +.with(key: value, ...)+ to match on a payload subset.
|
|
728
|
+
#
|
|
729
|
+
# expect { Issue.create!(...) }.to(have_fired_event('plan_my_stuff.issue.created'))
|
|
730
|
+
# expect { Issue.create!(...) }.to(have_fired_event('plan_my_stuff.issue.created').with(user: alice))
|
|
731
|
+
#
|
|
732
|
+
RSpec::Matchers.define(:have_fired_event) do |event_name|
|
|
733
|
+
supports_block_expectations
|
|
734
|
+
|
|
735
|
+
chain(:with) { |expected_payload| @expected_payload = expected_payload }
|
|
736
|
+
|
|
737
|
+
match do |block|
|
|
738
|
+
events = PlanMyStuff::TestHelpers::Notifications.capture(&block)
|
|
739
|
+
matching = events.select { |e| e[:name] == event_name }
|
|
740
|
+
@fired_events = events
|
|
741
|
+
next false if matching.empty?
|
|
742
|
+
next true if @expected_payload.nil?
|
|
743
|
+
|
|
744
|
+
matching.any? { |e| @expected_payload.all? { |k, v| e[:payload][k] == v } }
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
failure_message do
|
|
748
|
+
fired_names = @fired_events.pluck(:name)
|
|
749
|
+
base = "expected block to fire #{event_name.inspect}"
|
|
750
|
+
base += " with payload including #{@expected_payload.inspect}" if @expected_payload
|
|
751
|
+
"#{base}, but fired: #{fired_names.empty? ? '(none)' : fired_names.inspect}"
|
|
752
|
+
end
|
|
508
753
|
end
|
|
509
754
|
end
|