ruby_raider 4.1.0 → 4.2.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.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/e2e_tests.yml +3 -0
  3. data/README.md +52 -53
  4. data/RELEASE.md +14 -1
  5. data/lib/commands/schedule_commands.rb +84 -0
  6. data/lib/generators/templates/common/git_ignore.tt +1 -0
  7. data/lib/ruby_raider/runtime/retry_reset.rb +32 -0
  8. data/lib/ruby_raider/runtime/rspec.rb +1 -0
  9. data/lib/ruby_raider/runtime/show_browser.rb +53 -0
  10. data/lib/ruby_raider/runtime.rb +2 -0
  11. data/lib/ruby_raider.rb +22 -7
  12. data/lib/utilities/schedules/adapter.rb +22 -0
  13. data/lib/utilities/schedules/base.rb +27 -0
  14. data/lib/utilities/schedules/kind.rb +74 -0
  15. data/lib/utilities/schedules/legacy.rb +196 -0
  16. data/lib/utilities/schedules/legacy_stub.rb +62 -0
  17. data/lib/utilities/{flows → schedules}/next_run.rb +2 -2
  18. data/lib/utilities/{flows → schedules}/notifier.rb +6 -6
  19. data/lib/utilities/{flows → schedules}/os/command_runner.rb +1 -1
  20. data/lib/utilities/{flows → schedules}/os/cron.rb +27 -17
  21. data/lib/utilities/{flows → schedules}/os/launchd.rb +32 -14
  22. data/lib/utilities/{flows → schedules}/os/linux.rb +23 -5
  23. data/lib/utilities/{flows → schedules}/os/systemd.rb +33 -17
  24. data/lib/utilities/{flows → schedules}/os/task_scheduler.rb +36 -20
  25. data/lib/utilities/schedules/os_enumerator.rb +30 -0
  26. data/lib/utilities/schedules/project.rb +70 -0
  27. data/lib/utilities/schedules/rule.rb +162 -0
  28. data/lib/utilities/schedules/run_lock.rb +160 -0
  29. data/lib/utilities/schedules/run_log.rb +122 -0
  30. data/lib/utilities/schedules/run_report.rb +139 -0
  31. data/lib/utilities/schedules/run_wrapper.rb +216 -0
  32. data/lib/utilities/schedules/schedule.rb +89 -0
  33. data/lib/utilities/schedules/schedule_store.rb +53 -0
  34. data/lib/utilities/schedules/scheduler.rb +178 -0
  35. data/lib/utilities/schedules/scheduler_steps.rb +69 -0
  36. data/lib/utilities/schedules/selection.rb +133 -0
  37. data/lib/utilities/schedules/spawn.rb +68 -0
  38. data/lib/utilities/{flows/schedule_store.rb → schedules/store.rb} +17 -13
  39. data/lib/version +1 -1
  40. data/ruby_raider.gemspec +1 -0
  41. data/sig/commands/schedule_commands.rbs +12 -0
  42. data/sig/ruby_raider/runtime/retry_reset.rbs +7 -0
  43. data/sig/ruby_raider/runtime/show_browser.rbs +27 -0
  44. data/sig/ruby_raider.rbs +4 -0
  45. data/sig/utilities/schedules/adapter.rbs +5 -0
  46. data/sig/utilities/schedules/base.rbs +15 -0
  47. data/sig/utilities/schedules/kind.rbs +32 -0
  48. data/sig/utilities/schedules/legacy.rbs +45 -0
  49. data/sig/utilities/schedules/legacy_stub.rbs +15 -0
  50. data/sig/utilities/{flows → schedules}/next_run.rbs +3 -3
  51. data/sig/utilities/{flows → schedules}/notifier.rbs +1 -1
  52. data/sig/utilities/{flows → schedules}/os/command_runner.rbs +1 -1
  53. data/sig/utilities/{flows → schedules}/os/cron.rbs +7 -7
  54. data/sig/utilities/{flows → schedules}/os/launchd.rbs +7 -6
  55. data/sig/utilities/{flows → schedules}/os/linux.rbs +10 -4
  56. data/sig/utilities/{flows → schedules}/os/systemd.rbs +8 -7
  57. data/sig/utilities/{flows → schedules}/os/task_scheduler.rbs +13 -10
  58. data/sig/utilities/schedules/os_enumerator.rbs +12 -0
  59. data/sig/utilities/schedules/project.rbs +22 -0
  60. data/sig/utilities/{flows/schedule.rbs → schedules/rule.rbs} +23 -12
  61. data/sig/utilities/schedules/run_lock.rbs +29 -0
  62. data/sig/utilities/schedules/run_log.rbs +33 -0
  63. data/sig/utilities/schedules/run_report.rbs +34 -0
  64. data/sig/utilities/schedules/run_wrapper.rbs +45 -0
  65. data/sig/utilities/schedules/schedule.rbs +27 -0
  66. data/sig/utilities/schedules/schedule_store.rbs +21 -0
  67. data/sig/utilities/{flows → schedules}/scheduler.rbs +22 -15
  68. data/sig/utilities/schedules/scheduler_steps.rbs +22 -0
  69. data/sig/utilities/schedules/selection.rbs +36 -0
  70. data/sig/utilities/schedules/spawn.rbs +10 -0
  71. data/sig/utilities/{flows/schedule_store.rbs → schedules/store.rbs} +14 -9
  72. data/spec/commands/schedule_commands_spec.rb +284 -0
  73. data/spec/integration/content/common_content_spec.rb +8 -2
  74. data/spec/integration/content/gemfile_content_spec.rb +2 -2
  75. data/spec/integration/end_to_end_spec.rb +0 -63
  76. data/spec/runtime/retry_reset_spec.rb +112 -0
  77. data/spec/runtime/show_browser_project_spec.rb +70 -0
  78. data/spec/runtime/show_browser_spec.rb +101 -0
  79. data/spec/utilities/schedules/kind_spec.rb +81 -0
  80. data/spec/utilities/schedules/legacy_spec.rb +433 -0
  81. data/spec/utilities/schedules/legacy_stub_spec.rb +144 -0
  82. data/spec/utilities/{flows → schedules}/next_run_spec.rb +5 -5
  83. data/spec/utilities/{flows → schedules}/notifier_spec.rb +3 -4
  84. data/spec/utilities/{flows → schedules}/os/command_runner_spec.rb +2 -3
  85. data/spec/utilities/{flows → schedules}/os/cron_spec.rb +34 -29
  86. data/spec/utilities/{flows → schedules}/os/launchd_spec.rb +42 -29
  87. data/spec/utilities/{flows → schedules}/os/linux_spec.rb +27 -13
  88. data/spec/utilities/schedules/os/list_ids_spec.rb +202 -0
  89. data/spec/utilities/{flows → schedules}/os/systemd_spec.rb +35 -31
  90. data/spec/utilities/{flows → schedules}/os/task_scheduler_spec.rb +40 -34
  91. data/spec/utilities/schedules/os_entry_spec.rb +54 -0
  92. data/spec/utilities/schedules/os_enumerator_spec.rb +57 -0
  93. data/spec/utilities/schedules/project_spec.rb +125 -0
  94. data/spec/utilities/schedules/run_lock_spec.rb +143 -0
  95. data/spec/utilities/schedules/run_log_spec.rb +83 -0
  96. data/spec/utilities/schedules/run_report_spec.rb +150 -0
  97. data/spec/utilities/schedules/run_wrapper_integration_spec.rb +166 -0
  98. data/spec/utilities/schedules/run_wrapper_spec.rb +430 -0
  99. data/spec/utilities/schedules/schedule_spec.rb +120 -0
  100. data/spec/utilities/schedules/schedule_store_spec.rb +151 -0
  101. data/spec/utilities/schedules/scheduler_launchd_spec.rb +77 -0
  102. data/spec/utilities/schedules/scheduler_spec.rb +445 -0
  103. data/spec/utilities/schedules/selection_spec.rb +176 -0
  104. data/spec/utilities/schedules/spawn_spec.rb +104 -0
  105. metadata +106 -53
  106. data/lib/commands/flow_commands.rb +0 -104
  107. data/lib/generators/templates/flows/flow_helper.tt +0 -42
  108. data/lib/generators/templates/flows/run.tt +0 -332
  109. data/lib/utilities/flows/project.rb +0 -170
  110. data/lib/utilities/flows/run_log.rb +0 -61
  111. data/lib/utilities/flows/runner.rb +0 -206
  112. data/lib/utilities/flows/schedule.rb +0 -221
  113. data/lib/utilities/flows/scheduler.rb +0 -187
  114. data/sig/commands/flow_commands.rbs +0 -13
  115. data/sig/utilities/flows/project.rbs +0 -46
  116. data/sig/utilities/flows/run_log.rbs +0 -22
  117. data/sig/utilities/flows/runner.rbs +0 -35
  118. data/spec/commands/flow_commands_spec.rb +0 -128
  119. data/spec/generators/flow_helper_spec.rb +0 -40
  120. data/spec/integration/flows/flow_project.rb +0 -62
  121. data/spec/integration/flows/run_command_spec.rb +0 -91
  122. data/spec/integration/flows/run_lifecycle_spec.rb +0 -383
  123. data/spec/utilities/flows/project_spec.rb +0 -178
  124. data/spec/utilities/flows/run_log_spec.rb +0 -47
  125. data/spec/utilities/flows/runner_spec.rb +0 -313
  126. data/spec/utilities/flows/schedule_spec.rb +0 -233
  127. data/spec/utilities/flows/schedule_store_spec.rb +0 -137
  128. data/spec/utilities/flows/scheduler_spec.rb +0 -329
@@ -1,329 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
- require 'stringio'
5
- require 'tmpdir'
6
- require_relative '../../../lib/utilities/flows/scheduler'
7
-
8
- # Flow specs live under spec/utilities/flows, next to the code in lib/utilities/flows
9
- # The fake adapter, store and streams sit next to the project and scheduler
10
- # rubocop:disable RSpec/MultipleMemoizedHelpers
11
- RSpec.describe RubyRaider::Flows::Scheduler do # rubocop:disable RSpec/FilePath
12
- # Stands in for an OS scheduler: records every call, keeps the installed os_ids
13
- let(:fake_adapter) do
14
- Class.new do
15
- attr_reader :calls, :installed
16
- attr_accessor :fail_present
17
-
18
- def initialize
19
- @calls = []
20
- @installed = []
21
- end
22
-
23
- def label = 'fakeos'
24
-
25
- def install(schedule)
26
- @calls << [:install, schedule.os_id]
27
- @installed |= [schedule.os_id]
28
- true
29
- end
30
-
31
- def remove(os_id)
32
- @calls << [:remove, os_id]
33
- !@installed.delete(os_id).nil?
34
- end
35
-
36
- def present?(os_id)
37
- raise RubyRaider::Flows::Error, 'task list unreadable' if fail_present
38
-
39
- @installed.include?(os_id)
40
- end
41
- end
42
- end
43
-
44
- let(:dir) { File.realpath(Dir.mktmpdir('flows-scheduler')) }
45
- let(:project) { RubyRaider::Flows::Project.new(dir) }
46
- let(:adapter) { fake_adapter.new }
47
- let(:store) { RubyRaider::Flows::ScheduleStore.new(project) }
48
- let(:out) { StringIO.new }
49
- let(:err) { StringIO.new }
50
- # Sun 8 Nov 2026 12:00 local
51
- let(:now) { Time.local(2026, 11, 8, 12, 0) }
52
- let(:scheduler) { described_class.new(project, adapter: adapter, store: store, out: out, err: err, now: -> { now }) }
53
- let(:os_id) { RubyRaider::Flows::Schedule.os_id_for(dir, 'report') }
54
-
55
- before do
56
- %w[flows/flow_helper.rb flows/support/run.rb flows/report_flow.rb].each do |file|
57
- FileUtils.mkdir_p(File.dirname(File.join(dir, file)))
58
- File.write(File.join(dir, file), '')
59
- end
60
- end
61
-
62
- after { FileUtils.rm_rf(dir) }
63
-
64
- def log_run(name, line)
65
- file = File.join(dir, ".raider/flows/#{name}/runs.jsonl")
66
- FileUtils.mkdir_p(File.dirname(file))
67
- File.write(file, "#{JSON.generate(line)}\n", mode: 'a')
68
- end
69
-
70
- describe '.adapter' do
71
- let(:runner) { instance_double(RubyRaider::Flows::Os::CommandRunner) }
72
-
73
- def result(success)
74
- RubyRaider::Flows::Os::CommandRunner::Result.new(stdout: '', stderr: '', exitstatus: success ? 0 : 1)
75
- end
76
-
77
- it 'picks launchd on macOS and Task Scheduler on Windows without calling them' do
78
- picked = %w[darwin25 mingw32 mswin64 cygwin].map { |host| described_class.adapter(runner: runner, host_os: host).class }
79
-
80
- expect(picked).to eq([RubyRaider::Flows::Os::Launchd] + ([RubyRaider::Flows::Os::TaskScheduler] * 3))
81
- end
82
-
83
- it 'picks the Linux adapter, which labels itself by the scheduler that answers' do
84
- allow(runner).to receive(:call).and_return(result(true), result(false))
85
-
86
- picked = 2.times.map { described_class.adapter(runner: runner, host_os: 'linux-gnu') }
87
-
88
- expect(picked.map { |adapter| [adapter.class, adapter.label] })
89
- .to eq([[RubyRaider::Flows::Os::Linux, 'systemd'], [RubyRaider::Flows::Os::Linux, 'cron']])
90
- end
91
-
92
- it 'refuses another OS' do
93
- expect { described_class.adapter(runner: runner, host_os: 'solaris2') }
94
- .to raise_error(RubyRaider::Flows::Error, 'Scheduling flows is not supported on solaris2')
95
- end
96
- end
97
-
98
- describe '#schedule' do
99
- it 'installs the OS entry, then writes the yml, and says when it next runs' do
100
- allow(store).to receive(:save).and_wrap_original do |original, schedule|
101
- expect(adapter.installed).to include(schedule.os_id)
102
- original.call(schedule)
103
- end
104
-
105
- scheduler.schedule('report', every: 'weekday', at: '08:00')
106
-
107
- expect(out.string).to eq("Scheduled report: every weekday at 08:00. Next run Mon 9 Nov 08:00.\n")
108
- expect([adapter.calls, store.find('report').rule_text]).to eq([[[:install, os_id]], 'every weekday at 08:00'])
109
- end
110
-
111
- it 'stores notify and adds --notify to the OS entry' do
112
- allow(adapter).to receive(:install).and_wrap_original do |original, schedule|
113
- expect(schedule.argv).to include('--notify')
114
- original.call(schedule)
115
- end
116
- scheduler.schedule('report', every: 'day', at: '08:00', notify: true)
117
-
118
- expect([store.find('report').notify, out.string]).to eq([true, "Scheduled report: every day at 08:00 (notify). Next run Mon 9 Nov 08:00.\n"])
119
- end
120
-
121
- it 'says Replaced when the flow already had a schedule' do
122
- scheduler.schedule('report', every: 'day', at: '08:00')
123
- scheduler.schedule('report', every: 'week', at: '09:30', on: 'wed')
124
-
125
- expect(out.string.lines.last).to eq("Replaced the schedule for report: every week on wed at 09:30. Next run Wed 11 Nov 09:30.\n")
126
- expect(store.all.map(&:rule_text)).to eq(['every week on wed at 09:30'])
127
- end
128
-
129
- it 'removes the OS entry again when the yml cannot be written' do
130
- allow(store).to receive(:save).and_raise(RubyRaider::Flows::Error, 'disk full')
131
-
132
- expect { scheduler.schedule('report', every: 'day', at: '08:00') }
133
- .to raise_error(RubyRaider::Flows::Error, 'disk full: the fakeos entry was removed too, so report is not scheduled')
134
- expect([adapter.calls, adapter.installed]).to eq([[[:install, os_id], [:remove, os_id]], []])
135
- end
136
-
137
- it 'needs the flow file and flows set up, and checks the rule, before touching the OS' do
138
- expect { scheduler.schedule('nope', every: 'day', at: '08:00') }.to raise_error(RubyRaider::Flows::Error, /No flow nope/)
139
- expect { scheduler.schedule('report', every: 'day') }.to raise_error(RubyRaider::Flows::Error, /needs at/)
140
- File.delete(File.join(dir, 'flows/support/run.rb'))
141
- expect { scheduler.schedule('report', every: 'day', at: '08:00') }
142
- .to raise_error(RubyRaider::Flows::Error, 'Flows are not set up in this project: run raider flow init')
143
- expect(adapter.calls).to be_empty
144
- end
145
-
146
- it 'checks schedules.yml can be written before installing the OS entry' do
147
- FileUtils.mkdir_p(File.join(dir, '.raider/flows'))
148
- File.write(File.join(dir, '.raider/flows/schedules.yml'), "schedules: [\n")
149
-
150
- expect { scheduler.schedule('report', every: 'day', at: '08:00') }
151
- .to raise_error(RubyRaider::Flows::Error, /schedules.yml is not a schedules file/)
152
- expect(adapter.calls).to be_empty
153
- end
154
-
155
- it 'refuses with a Flows::Error when the flow folder cannot be made' do
156
- File.write(File.join(dir, '.raider'), '')
157
-
158
- expect { scheduler.schedule('report', every: 'day', at: '08:00') }
159
- .to raise_error(RubyRaider::Flows::Error, %r{\ACould not make \.raider/flows: })
160
- expect(adapter.calls).to be_empty
161
- end
162
-
163
- it 'makes .raider/flows/<name> a 0700 folder and refuses a link there' do
164
- scheduler.schedule('report', every: 'day', at: '08:00')
165
- expect(File.stat(File.join(dir, '.raider/flows/report')).mode & 0o777).to eq(0o700)
166
-
167
- FileUtils.rm_rf(File.join(dir, '.raider/flows/report'))
168
- File.symlink(FileUtils.mkdir_p(File.join(dir, 'elsewhere')).first, File.join(dir, '.raider/flows/report'))
169
- expect { scheduler.schedule('report', every: 'day', at: '08:00') }
170
- .to raise_error(RubyRaider::Flows::Error, '.raider/flows/report must be a folder, not a link')
171
- expect(adapter.calls).to eq([[:install, os_id]])
172
- end
173
- end
174
-
175
- describe '#unschedule' do
176
- it 'keeps the yml entry when the OS scheduler refuses the removal' do
177
- scheduler.schedule('report', every: 'day', at: '08:00')
178
- allow(adapter).to receive(:remove).and_raise(RubyRaider::Flows::Error, 'systemd units found')
179
-
180
- expect { scheduler.unschedule('report') }.to raise_error(RubyRaider::Flows::Error, 'systemd units found')
181
- expect(store.find('report')).not_to be_nil
182
- end
183
-
184
- it 'removes the yml entry and the OS entry' do
185
- scheduler.schedule('report', every: 'day', at: '08:00')
186
- scheduler.unschedule('report')
187
-
188
- expect([out.string.lines.last, store.all, adapter.installed]).to eq(["Unscheduled report\n", [], []])
189
- end
190
-
191
- it 'says when the OS entry was already gone' do
192
- scheduler.schedule('report', every: 'day', at: '08:00')
193
- adapter.installed.clear
194
- scheduler.unschedule('report')
195
-
196
- expect([out.string.lines.last, store.all]).to eq(["Unscheduled report (it was already gone from fakeos)\n", []])
197
- end
198
-
199
- it 'removes an OS entry with no yml entry, even for a deleted flow' do
200
- adapter.installed << RubyRaider::Flows::Schedule.os_id_for(dir, 'gone')
201
- scheduler.unschedule('gone')
202
-
203
- expect([out.string, adapter.installed]).to eq(["Unscheduled gone\n", []])
204
- end
205
-
206
- it 'refuses a flow with no schedule anywhere, and an invalid name' do
207
- expect { scheduler.unschedule('report') }.to raise_error(RubyRaider::Flows::Error, 'No schedule for report')
208
- expect { scheduler.unschedule('Bad-Name') }.to raise_error(RubyRaider::Flows::Error, /Invalid flow name/)
209
- end
210
-
211
- it 'says the OS entry was already removed when schedules.yml can not be written' do
212
- scheduler.schedule('report', every: 'day', at: '08:00')
213
- allow(store).to receive(:delete).and_raise(Errno::EACCES, 'schedules.yml')
214
-
215
- expect { scheduler.unschedule('report') }.to raise_error(RubyRaider::Flows::Error) do |error|
216
- expect([error.message, error.exit_code]).to eq(
217
- ['Permission denied - schedules.yml: the fakeos entry was already removed, but schedules.yml still lists report', 2]
218
- )
219
- end
220
- expect(adapter.installed).to eq([])
221
- end
222
-
223
- it 'warns that an OS entry from another project path may remain when the yml entry was unreadable' do
224
- FileUtils.mkdir_p(File.join(dir, '.raider/flows'))
225
- File.write(File.join(dir, '.raider/flows/schedules.yml'), { 'schedules' => [{ 'name' => 'report' }] }.to_yaml)
226
- scheduler.unschedule('report')
227
-
228
- expect(out.string).to eq("Unscheduled report (it was already gone from fakeos)\n")
229
- expect(err.string).to include('could not be read', 'fakeos entry made from a different project path may remain')
230
- end
231
- end
232
-
233
- describe '#schedules' do
234
- it 'lists each schedule with its rule, next run, last result and OS state' do
235
- scheduler.schedule('report', every: 'hour', at: ':15', retries: 2, inputs: ['who=me'], notify: true)
236
- log_run('report', 'status' => 'failed', 'finished_at' => "2026-11-09T08:00:12Z\e[2J", 'trigger' => 'schedule',
237
- 'failed_step' => 'Open', 'results_dir' => 'flow-results/report/x', 'run_id' => 'abc')
238
- out.truncate(0)
239
- out.rewind
240
- scheduler.schedules
241
-
242
- expect(out.string).to eq("#{'report'.ljust(30)} every hour at :15 (notify) next Sun 8 Nov 12:15 failed 2026-11-09T08:00:12Z?[2J ok\n")
243
- end
244
-
245
- it 'prints the JSON the desktop app reads' do
246
- scheduler.schedule('report', every: 'week', at: '08:00', on: 'mon', inputs: ['who=me'], notify: true)
247
- log_run('report', 'status' => 'completed', 'finished_at' => 'f', 'trigger' => 't', 'failed_step' => nil, 'results_dir' => 'r', 'run_id' => 'x')
248
- out.truncate(0)
249
- out.rewind
250
- scheduler.schedules(json: true)
251
-
252
- expect(JSON.parse(out.string)).to eq(
253
- [{ 'name' => 'report', 'every' => 'week', 'at' => '08:00', 'on' => 'mon', 'retries' => 0, 'inputs' => ['who=me'], 'notify' => true,
254
- 'next_run_at' => Time.local(2026, 11, 9, 8, 0).iso8601, 'os_state' => 'ok',
255
- 'last' => { 'status' => 'completed', 'finished_at' => 'f', 'trigger' => 't', 'failed_step' => nil, 'results_dir' => 'r' } }]
256
- )
257
- end
258
-
259
- it 'says Ruby not found when the stored ruby is gone, and missing when the OS entry is gone' do
260
- scheduler.schedule('report', every: 'day', at: '08:00')
261
- FileUtils.touch(File.join(dir, 'flows/other_flow.rb'))
262
- fields = store.find('report').to_h.transform_keys(&:to_sym)
263
- other = { name: 'other', os_id: RubyRaider::Flows::Schedule.os_id_for(dir, 'other'), ruby: '/nonexistent/ruby' }
264
- store.save(RubyRaider::Flows::Schedule.new(**fields, **other, project_root: dir))
265
- adapter.installed.clear
266
- out.truncate(0)
267
- out.rewind
268
- scheduler.schedules(json: true)
269
-
270
- expect(JSON.parse(out.string).map { |row| [row['name'], row['os_state'], row['last']] })
271
- .to eq([['other', 'Ruby not found: re-schedule', nil], ['report', 'missing from fakeos', nil]])
272
- end
273
-
274
- it 'says flow not found when the flow file is gone, after Ruby not found and before the OS state' do
275
- scheduler.schedule('report', every: 'day', at: '08:00')
276
- FileUtils.touch(File.join(dir, 'flows/other_flow.rb'))
277
- fields = store.find('report').to_h.transform_keys(&:to_sym)
278
- other = { name: 'other', os_id: RubyRaider::Flows::Schedule.os_id_for(dir, 'other'), ruby: '/nonexistent/ruby' }
279
- store.save(RubyRaider::Flows::Schedule.new(**fields, **other, project_root: dir))
280
- %w[report other].each { |name| File.delete(File.join(dir, "flows/#{name}_flow.rb")) }
281
- adapter.fail_present = true
282
- scheduler.schedules(json: true)
283
-
284
- expect(JSON.parse(out.string.lines.last).map { |row| [row['name'], row['os_state']] })
285
- .to eq([['other', 'Ruby not found: re-schedule'], ['report', 'flow not found: unschedule']])
286
- end
287
-
288
- it 'adds a row for a flow the OS still schedules with no yml entry' do
289
- adapter.installed << os_id
290
- scheduler.schedules(json: true)
291
-
292
- expect(JSON.parse(out.string)).to eq(
293
- [{ 'name' => 'report', 'every' => nil, 'at' => nil, 'on' => nil, 'retries' => nil, 'inputs' => nil, 'notify' => nil,
294
- 'next_run_at' => nil, 'os_state' => 'missing from schedules.yml', 'last' => nil }]
295
- )
296
- end
297
-
298
- it 'shows unknown when the OS state cannot be read' do
299
- scheduler.schedule('report', every: 'day', at: '08:00')
300
- adapter.fail_present = true
301
- out.truncate(0)
302
- out.rewind
303
- scheduler.schedules
304
-
305
- expect(out.string).to end_with(" never run unknown (task list unreadable)\n")
306
- end
307
-
308
- it 'prints the missed runs note of an adapter that has one, once' do
309
- adapter.define_singleton_method(:missed_runs_note) { '(fakeos: missed runs are skipped)' }
310
- %w[report other].each do |name|
311
- FileUtils.touch(File.join(dir, "flows/#{name}_flow.rb"))
312
- scheduler.schedule(name, every: 'day', at: '08:00')
313
- end
314
- out.truncate(0)
315
- out.rewind
316
- scheduler.schedules
317
-
318
- expect(out.string.lines.size).to eq(3)
319
- expect(out.string.lines.last).to eq("(fakeos: missed runs are skipped)\n")
320
- end
321
-
322
- it 'says how to add one when there are none' do
323
- scheduler.schedules
324
-
325
- expect(out.string).to eq("No schedules yet: raider flow schedule <name> --every day --at 08:00\n")
326
- end
327
- end
328
- end
329
- # rubocop:enable RSpec/MultipleMemoizedHelpers