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.
- checksums.yaml +4 -4
- data/.github/workflows/e2e_tests.yml +3 -0
- data/README.md +52 -53
- data/RELEASE.md +14 -1
- data/lib/commands/schedule_commands.rb +84 -0
- data/lib/generators/templates/common/git_ignore.tt +1 -0
- data/lib/ruby_raider/runtime/retry_reset.rb +32 -0
- data/lib/ruby_raider/runtime/rspec.rb +1 -0
- data/lib/ruby_raider/runtime/show_browser.rb +53 -0
- data/lib/ruby_raider/runtime.rb +2 -0
- data/lib/ruby_raider.rb +22 -7
- data/lib/utilities/schedules/adapter.rb +22 -0
- data/lib/utilities/schedules/base.rb +27 -0
- data/lib/utilities/schedules/kind.rb +74 -0
- data/lib/utilities/schedules/legacy.rb +196 -0
- data/lib/utilities/schedules/legacy_stub.rb +62 -0
- data/lib/utilities/{flows → schedules}/next_run.rb +2 -2
- data/lib/utilities/{flows → schedules}/notifier.rb +6 -6
- data/lib/utilities/{flows → schedules}/os/command_runner.rb +1 -1
- data/lib/utilities/{flows → schedules}/os/cron.rb +27 -17
- data/lib/utilities/{flows → schedules}/os/launchd.rb +32 -14
- data/lib/utilities/{flows → schedules}/os/linux.rb +23 -5
- data/lib/utilities/{flows → schedules}/os/systemd.rb +33 -17
- data/lib/utilities/{flows → schedules}/os/task_scheduler.rb +36 -20
- data/lib/utilities/schedules/os_enumerator.rb +30 -0
- data/lib/utilities/schedules/project.rb +70 -0
- data/lib/utilities/schedules/rule.rb +162 -0
- data/lib/utilities/schedules/run_lock.rb +160 -0
- data/lib/utilities/schedules/run_log.rb +122 -0
- data/lib/utilities/schedules/run_report.rb +139 -0
- data/lib/utilities/schedules/run_wrapper.rb +216 -0
- data/lib/utilities/schedules/schedule.rb +89 -0
- data/lib/utilities/schedules/schedule_store.rb +53 -0
- data/lib/utilities/schedules/scheduler.rb +178 -0
- data/lib/utilities/schedules/scheduler_steps.rb +69 -0
- data/lib/utilities/schedules/selection.rb +133 -0
- data/lib/utilities/schedules/spawn.rb +68 -0
- data/lib/utilities/{flows/schedule_store.rb → schedules/store.rb} +17 -13
- data/lib/version +1 -1
- data/ruby_raider.gemspec +1 -0
- data/sig/commands/schedule_commands.rbs +12 -0
- data/sig/ruby_raider/runtime/retry_reset.rbs +7 -0
- data/sig/ruby_raider/runtime/show_browser.rbs +27 -0
- data/sig/ruby_raider.rbs +4 -0
- data/sig/utilities/schedules/adapter.rbs +5 -0
- data/sig/utilities/schedules/base.rbs +15 -0
- data/sig/utilities/schedules/kind.rbs +32 -0
- data/sig/utilities/schedules/legacy.rbs +45 -0
- data/sig/utilities/schedules/legacy_stub.rbs +15 -0
- data/sig/utilities/{flows → schedules}/next_run.rbs +3 -3
- data/sig/utilities/{flows → schedules}/notifier.rbs +1 -1
- data/sig/utilities/{flows → schedules}/os/command_runner.rbs +1 -1
- data/sig/utilities/{flows → schedules}/os/cron.rbs +7 -7
- data/sig/utilities/{flows → schedules}/os/launchd.rbs +7 -6
- data/sig/utilities/{flows → schedules}/os/linux.rbs +10 -4
- data/sig/utilities/{flows → schedules}/os/systemd.rbs +8 -7
- data/sig/utilities/{flows → schedules}/os/task_scheduler.rbs +13 -10
- data/sig/utilities/schedules/os_enumerator.rbs +12 -0
- data/sig/utilities/schedules/project.rbs +22 -0
- data/sig/utilities/{flows/schedule.rbs → schedules/rule.rbs} +23 -12
- data/sig/utilities/schedules/run_lock.rbs +29 -0
- data/sig/utilities/schedules/run_log.rbs +33 -0
- data/sig/utilities/schedules/run_report.rbs +34 -0
- data/sig/utilities/schedules/run_wrapper.rbs +45 -0
- data/sig/utilities/schedules/schedule.rbs +27 -0
- data/sig/utilities/schedules/schedule_store.rbs +21 -0
- data/sig/utilities/{flows → schedules}/scheduler.rbs +22 -15
- data/sig/utilities/schedules/scheduler_steps.rbs +22 -0
- data/sig/utilities/schedules/selection.rbs +36 -0
- data/sig/utilities/schedules/spawn.rbs +10 -0
- data/sig/utilities/{flows/schedule_store.rbs → schedules/store.rbs} +14 -9
- data/spec/commands/schedule_commands_spec.rb +284 -0
- data/spec/integration/content/common_content_spec.rb +8 -2
- data/spec/integration/content/gemfile_content_spec.rb +2 -2
- data/spec/integration/end_to_end_spec.rb +0 -63
- data/spec/runtime/retry_reset_spec.rb +112 -0
- data/spec/runtime/show_browser_project_spec.rb +70 -0
- data/spec/runtime/show_browser_spec.rb +101 -0
- data/spec/utilities/schedules/kind_spec.rb +81 -0
- data/spec/utilities/schedules/legacy_spec.rb +433 -0
- data/spec/utilities/schedules/legacy_stub_spec.rb +144 -0
- data/spec/utilities/{flows → schedules}/next_run_spec.rb +5 -5
- data/spec/utilities/{flows → schedules}/notifier_spec.rb +3 -4
- data/spec/utilities/{flows → schedules}/os/command_runner_spec.rb +2 -3
- data/spec/utilities/{flows → schedules}/os/cron_spec.rb +34 -29
- data/spec/utilities/{flows → schedules}/os/launchd_spec.rb +42 -29
- data/spec/utilities/{flows → schedules}/os/linux_spec.rb +27 -13
- data/spec/utilities/schedules/os/list_ids_spec.rb +202 -0
- data/spec/utilities/{flows → schedules}/os/systemd_spec.rb +35 -31
- data/spec/utilities/{flows → schedules}/os/task_scheduler_spec.rb +40 -34
- data/spec/utilities/schedules/os_entry_spec.rb +54 -0
- data/spec/utilities/schedules/os_enumerator_spec.rb +57 -0
- data/spec/utilities/schedules/project_spec.rb +125 -0
- data/spec/utilities/schedules/run_lock_spec.rb +143 -0
- data/spec/utilities/schedules/run_log_spec.rb +83 -0
- data/spec/utilities/schedules/run_report_spec.rb +150 -0
- data/spec/utilities/schedules/run_wrapper_integration_spec.rb +166 -0
- data/spec/utilities/schedules/run_wrapper_spec.rb +430 -0
- data/spec/utilities/schedules/schedule_spec.rb +120 -0
- data/spec/utilities/schedules/schedule_store_spec.rb +151 -0
- data/spec/utilities/schedules/scheduler_launchd_spec.rb +77 -0
- data/spec/utilities/schedules/scheduler_spec.rb +445 -0
- data/spec/utilities/schedules/selection_spec.rb +176 -0
- data/spec/utilities/schedules/spawn_spec.rb +104 -0
- metadata +106 -53
- data/lib/commands/flow_commands.rb +0 -104
- data/lib/generators/templates/flows/flow_helper.tt +0 -42
- data/lib/generators/templates/flows/run.tt +0 -332
- data/lib/utilities/flows/project.rb +0 -170
- data/lib/utilities/flows/run_log.rb +0 -61
- data/lib/utilities/flows/runner.rb +0 -206
- data/lib/utilities/flows/schedule.rb +0 -221
- data/lib/utilities/flows/scheduler.rb +0 -187
- data/sig/commands/flow_commands.rbs +0 -13
- data/sig/utilities/flows/project.rbs +0 -46
- data/sig/utilities/flows/run_log.rbs +0 -22
- data/sig/utilities/flows/runner.rbs +0 -35
- data/spec/commands/flow_commands_spec.rb +0 -128
- data/spec/generators/flow_helper_spec.rb +0 -40
- data/spec/integration/flows/flow_project.rb +0 -62
- data/spec/integration/flows/run_command_spec.rb +0 -91
- data/spec/integration/flows/run_lifecycle_spec.rb +0 -383
- data/spec/utilities/flows/project_spec.rb +0 -178
- data/spec/utilities/flows/run_log_spec.rb +0 -47
- data/spec/utilities/flows/runner_spec.rb +0 -313
- data/spec/utilities/flows/schedule_spec.rb +0 -233
- data/spec/utilities/flows/schedule_store_spec.rb +0 -137
- data/spec/utilities/flows/scheduler_spec.rb +0 -329
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'stringio'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
require 'yaml'
|
|
6
|
+
require_relative '../../../lib/utilities/schedules/legacy'
|
|
7
|
+
require_relative '../../../lib/utilities/schedules/project'
|
|
8
|
+
|
|
9
|
+
# Every adapter here is a recording double, or a real one on a command double and a temp home: nothing
|
|
10
|
+
# reaches launchctl, crontab, systemctl or the real ~/Library/LaunchAgents.
|
|
11
|
+
# rubocop:disable RSpec/MultipleMemoizedHelpers
|
|
12
|
+
RSpec.describe RubyRaider::Schedules::Legacy do # rubocop:disable RSpec/FilePath
|
|
13
|
+
# Answers list_ids from ids and remove from answers by name (true unless told: false, or an error to raise)
|
|
14
|
+
let(:fake_adapter_class) do
|
|
15
|
+
Class.new do
|
|
16
|
+
attr_reader :calls, :answers
|
|
17
|
+
attr_accessor :ids, :on_remove
|
|
18
|
+
|
|
19
|
+
def initialize(ids = [])
|
|
20
|
+
@calls = []
|
|
21
|
+
@ids = ids
|
|
22
|
+
@answers = {}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def list_ids(prefix)
|
|
26
|
+
@calls << [:list_ids, prefix]
|
|
27
|
+
ids.select { |id| id.start_with?(prefix) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def remove(os_id)
|
|
31
|
+
@calls << [:remove, os_id]
|
|
32
|
+
on_remove&.call(os_id)
|
|
33
|
+
answer = answers.fetch(os_id.split('.').last, true)
|
|
34
|
+
raise answer if answer.is_a?(Exception)
|
|
35
|
+
|
|
36
|
+
answer
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Plays launchctl, crontab, systemctl and schtasks: records argv, keeps the crontab, answers from a table by
|
|
42
|
+
# verb (outputs: the stdout of a verb other than crontab -l)
|
|
43
|
+
let(:fake_runner_class) do
|
|
44
|
+
Class.new do
|
|
45
|
+
attr_reader :calls, :answers, :outputs
|
|
46
|
+
attr_accessor :table
|
|
47
|
+
|
|
48
|
+
def initialize
|
|
49
|
+
@calls = []
|
|
50
|
+
@answers = {}
|
|
51
|
+
@outputs = {}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def call(*argv, stdin_data: nil)
|
|
55
|
+
@calls << argv
|
|
56
|
+
key = argv.first == '/bin/launchctl' ? argv[1] : argv.reject { |arg| arg == '--user' }.first(2).join(' ')
|
|
57
|
+
stderr, status = answers.fetch(key) { key == 'crontab -l' && table.nil? ? ['no crontab for raider', 1] : ['', 0] }
|
|
58
|
+
self.table = stdin_data if key == 'crontab -' && status.zero?
|
|
59
|
+
stdout = key == 'crontab -l' ? table.to_s : outputs.fetch(key, '')
|
|
60
|
+
RubyRaider::Schedules::Os::CommandRunner::Result.new(stdout: stdout, stderr: stderr, exitstatus: status)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
let(:tmp) { File.realpath(Dir.mktmpdir('schedules-legacy')) }
|
|
66
|
+
let(:home) { File.join(tmp, 'home') }
|
|
67
|
+
let(:agents) { File.join(home, 'Library', 'LaunchAgents') }
|
|
68
|
+
let(:a_dir) { project_dir('a') }
|
|
69
|
+
let(:b_dir) { project_dir('b') }
|
|
70
|
+
let(:err) { StringIO.new }
|
|
71
|
+
let(:runner) { fake_runner_class.new }
|
|
72
|
+
let(:kind) { RubyRaider::Schedules::Kind::FLOW_LEGACY }
|
|
73
|
+
let(:old_rule) { described_class::OldRule }
|
|
74
|
+
|
|
75
|
+
after { FileUtils.rm_rf(tmp) }
|
|
76
|
+
|
|
77
|
+
def project_dir(name)
|
|
78
|
+
dir = File.join(tmp, name)
|
|
79
|
+
FileUtils.mkdir_p(dir)
|
|
80
|
+
dir
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def project(dir) = RubyRaider::Schedules::Project.new(dir)
|
|
84
|
+
|
|
85
|
+
def os_id(dir, name) = old_rule.os_id_for(dir, name)
|
|
86
|
+
|
|
87
|
+
def yml(dir) = File.join(dir, '.raider', 'flows', 'schedules.yml')
|
|
88
|
+
|
|
89
|
+
# A 4.1 schedules.yml; each entry a name and its os_id for this project unless given
|
|
90
|
+
def write_yml(dir, *names, entries: names.map { |name| { 'name' => name, 'every' => 'day', 'at' => '08:00', 'os_id' => os_id(dir, name) } })
|
|
91
|
+
FileUtils.mkdir_p(File.dirname(yml(dir)))
|
|
92
|
+
File.write(yml(dir), YAML.dump({ 'schedules' => entries }))
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def cleanup(dir, adapter, **options) = described_class.new(project(dir), adapter: adapter, err: err).cleanup(**options)
|
|
96
|
+
|
|
97
|
+
def removed(*names) = names.map { |name| [:remove, os_id(a_dir, name)] }
|
|
98
|
+
|
|
99
|
+
def removed_notice(*names)
|
|
100
|
+
one = names.size == 1
|
|
101
|
+
"Removed #{one ? '1 schedule' : "#{names.size} schedules"} made with Ruby Raider 4.1 (#{names.join(', ')}); " \
|
|
102
|
+
"#{one ? 'it' : 'they'} can't run in 4.2. Add #{one ? 'it' : 'them'} again with raider schedule add."
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe 'with launchd' do
|
|
106
|
+
let(:launchd) { RubyRaider::Schedules::Os::Launchd.new(kind: kind, runner: runner, home: home, uid: 501) }
|
|
107
|
+
|
|
108
|
+
def plist(dir, name) = File.join(agents, "#{os_id(dir, name)}.plist")
|
|
109
|
+
|
|
110
|
+
def write_plist(path, text = 'x')
|
|
111
|
+
FileUtils.mkdir_p(agents)
|
|
112
|
+
File.write(path, text)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "removes only this project's entry when another project has one with the same name" do
|
|
116
|
+
write_yml(a_dir, 'report')
|
|
117
|
+
write_plist(plist(a_dir, 'report'))
|
|
118
|
+
write_plist(plist(b_dir, 'report'), "b's plist")
|
|
119
|
+
|
|
120
|
+
notice = cleanup(a_dir, launchd)
|
|
121
|
+
|
|
122
|
+
expect(notice).to eq(removed_notice('report'))
|
|
123
|
+
expect(err.string).to eq("#{removed_notice('report')}\n")
|
|
124
|
+
expect([File.exist?(plist(a_dir, 'report')), File.read(plist(b_dir, 'report')), File.exist?(yml(a_dir))]).to eq([false, "b's plist", false])
|
|
125
|
+
expect(runner.calls).to eq([['/bin/launchctl', 'bootout', "gui/501/#{os_id(a_dir, 'report')}"]])
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'never uses an os_id stored in the yml: an entry copied from elsewhere is skipped' do
|
|
129
|
+
foreign = 'dev.rubyraider.flow.1255a248a3.tredssd'
|
|
130
|
+
write_yml(a_dir, entries: [{ 'name' => 'tredssd', 'os_id' => foreign }])
|
|
131
|
+
write_plist(File.join(agents, "#{foreign}.plist"), 'live')
|
|
132
|
+
write_plist(plist(a_dir, 'tredssd'))
|
|
133
|
+
|
|
134
|
+
notice = cleanup(a_dir, launchd)
|
|
135
|
+
|
|
136
|
+
expect(notice).to eq(removed_notice('tredssd'))
|
|
137
|
+
expect(runner.calls.flatten.grep(/1255a248a3/)).to be_empty
|
|
138
|
+
expect(File.read(File.join(agents, "#{foreign}.plist"))).to eq('live')
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it 'never passes a stored os_id to remove' do
|
|
143
|
+
write_yml(a_dir, entries: [{ 'name' => 'tredssd', 'os_id' => 'dev.rubyraider.flow.1255a248a3.tredssd' }])
|
|
144
|
+
adapter = fake_adapter_class.new(['dev.rubyraider.flow.1255a248a3.tredssd'])
|
|
145
|
+
|
|
146
|
+
expect(cleanup(a_dir, adapter)).to be_nil
|
|
147
|
+
expect(adapter.calls.flatten.grep(/1255a248a3/)).to be_empty
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "keeps the yml of a moved project, whose entries carry the old path's os_ids" do
|
|
151
|
+
old_dir = project_dir('old')
|
|
152
|
+
write_yml(a_dir, entries: %w[alpha beta].map { |name| { 'name' => name, 'os_id' => os_id(old_dir, name) } })
|
|
153
|
+
adapter = fake_adapter_class.new([os_id(old_dir, 'alpha'), os_id(old_dir, 'beta')])
|
|
154
|
+
|
|
155
|
+
expect([cleanup(a_dir, adapter), err.string, File.exist?(yml(a_dir))]).to eq([nil, '', true])
|
|
156
|
+
expect(adapter.calls.map(&:first)).to eq([:list_ids])
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
describe 'with cron' do
|
|
160
|
+
let(:cron) { RubyRaider::Schedules::Os::Cron.new(kind: kind, runner: runner) }
|
|
161
|
+
|
|
162
|
+
def pair(dir, name)
|
|
163
|
+
"# raider-flow #{os_id(dir, name)}\n" \
|
|
164
|
+
"0 8 * * * cd #{dir} && /usr/bin/env /r /b exec raider flow run #{name} --project #{dir} --trigger schedule >> /l 2>&1\n"
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it "removes only this project's tagged pair; every other byte stays" do
|
|
168
|
+
runner.table = "#{pair(b_dir, 'report')}MAILTO=\"\"\n5 * * * * /usr/bin/backup --keep\n#{pair(a_dir, 'report')}"
|
|
169
|
+
|
|
170
|
+
expect(cleanup(a_dir, cron)).to eq(removed_notice('report'))
|
|
171
|
+
expect(runner.table).to eq("#{pair(b_dir, 'report')}MAILTO=\"\"\n5 * * * * /usr/bin/backup --keep\n")
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
describe 'on Linux, with systemd' do
|
|
176
|
+
let(:units) { File.join(home, '.config', 'systemd', 'user') }
|
|
177
|
+
let(:linux) do
|
|
178
|
+
systemd = RubyRaider::Schedules::Os::Systemd.new(kind: kind, runner: runner, home: home, config_home: nil)
|
|
179
|
+
RubyRaider::Schedules::Os::Linux.new(kind: kind, runner: runner, systemd: systemd)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def unit(dir, name, type) = File.join(units, "#{os_id(dir, name)}.#{type}")
|
|
183
|
+
|
|
184
|
+
it "removes only this project's units when another project has one with the same name" do
|
|
185
|
+
write_yml(a_dir, 'report')
|
|
186
|
+
FileUtils.mkdir_p(units)
|
|
187
|
+
[a_dir, b_dir].product(%w[timer service]).each { |dir, type| File.write(unit(dir, 'report', type), "#{dir} #{type}") }
|
|
188
|
+
|
|
189
|
+
expect(cleanup(a_dir, linux)).to eq(removed_notice('report'))
|
|
190
|
+
expect(runner.calls.flatten.grep(/dev\.rubyraider/)).to eq(["#{os_id(a_dir, 'report')}.timer"])
|
|
191
|
+
expect(%w[timer service].map { |type| File.exist?(unit(a_dir, 'report', type)) }).to eq([false, false])
|
|
192
|
+
expect(%w[timer service].map { |type| File.read(unit(b_dir, 'report', type)) }).to eq(["#{b_dir} timer", "#{b_dir} service"])
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
describe 'with Task Scheduler' do
|
|
197
|
+
let(:schtasks) { 'C:\\Windows\\System32\\schtasks.exe' }
|
|
198
|
+
let(:task_scheduler) { RubyRaider::Schedules::Os::TaskScheduler.new(kind: kind, runner: runner, schtasks: schtasks) }
|
|
199
|
+
|
|
200
|
+
def task(dir, name) = "\\RubyRaider\\#{os_id(dir, name)}"
|
|
201
|
+
|
|
202
|
+
it "deletes only this project's task when another project has one with the same name" do
|
|
203
|
+
write_yml(a_dir, 'report')
|
|
204
|
+
runner.outputs["#{schtasks} /Query"] = [task(b_dir, 'report'), task(a_dir, 'report')]
|
|
205
|
+
.map { |name| %("#{name}","11/10/2026 8:00:00 AM","Ready"\r\n) }.join
|
|
206
|
+
|
|
207
|
+
expect(cleanup(a_dir, task_scheduler)).to eq(removed_notice('report'))
|
|
208
|
+
expect(runner.calls.select { |argv| argv[1] == '/Delete' }).to eq([[schtasks, '/Delete', '/TN', task(a_dir, 'report'), '/F']])
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
describe 'on Linux, cron with systemd unit files left behind' do
|
|
213
|
+
let(:units) { File.join(home, '.config', 'systemd', 'user') }
|
|
214
|
+
let(:linux) do
|
|
215
|
+
systemd = RubyRaider::Schedules::Os::Systemd.new(kind: kind, runner: runner, home: home, config_home: nil)
|
|
216
|
+
RubyRaider::Schedules::Os::Linux.new(kind: kind, runner: runner, systemd: systemd)
|
|
217
|
+
end
|
|
218
|
+
let(:stranded) do
|
|
219
|
+
'Could not remove 1 schedule made with Ruby Raider 4.1 (report: This schedule is a systemd timer; run raider schedule list ' \
|
|
220
|
+
'from a session where systemctl --user works); it is tried again next time.'
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
before do
|
|
224
|
+
runner.answers['systemctl show-environment'] = ['Failed to connect to bus', 1]
|
|
225
|
+
FileUtils.mkdir_p(units)
|
|
226
|
+
File.write(File.join(units, "#{os_id(a_dir, 'report')}.timer"), 'x')
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it 'reports the failure, keeps the yml, and tries again next time' do
|
|
230
|
+
write_yml(a_dir, 'report')
|
|
231
|
+
|
|
232
|
+
first = cleanup(a_dir, linux)
|
|
233
|
+
FileUtils.rm_f(yml(a_dir))
|
|
234
|
+
second = cleanup(a_dir, linux)
|
|
235
|
+
|
|
236
|
+
expect([first, second]).to eq([stranded, stranded])
|
|
237
|
+
expect(err.string.lines).to eq(["#{stranded}\n", "#{stranded}\n"])
|
|
238
|
+
expect(File.exist?(File.join(units, "#{os_id(a_dir, 'report')}.timer"))).to be(true)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
it 'keeps the yml' do
|
|
242
|
+
write_yml(a_dir, 'report')
|
|
243
|
+
cleanup(a_dir, linux)
|
|
244
|
+
|
|
245
|
+
expect(File.exist?(yml(a_dir))).to be(true)
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
describe 'a failed removal' do
|
|
250
|
+
it 'keeps the yml; a later run deletes it and reports only what it removed then' do
|
|
251
|
+
write_yml(a_dir, 'alpha', 'beta')
|
|
252
|
+
adapter = fake_adapter_class.new
|
|
253
|
+
adapter.answers['beta'] = RubyRaider::Schedules::Error.new("busy\nnow")
|
|
254
|
+
|
|
255
|
+
first = cleanup(a_dir, adapter)
|
|
256
|
+
kept = File.exist?(yml(a_dir))
|
|
257
|
+
adapter.answers.replace('alpha' => false)
|
|
258
|
+
second = cleanup(a_dir, adapter)
|
|
259
|
+
|
|
260
|
+
expect(first).to eq("#{removed_notice('alpha')} Could not remove 1 schedule made with Ruby Raider 4.1 (beta: busy?now); it is tried again next time.")
|
|
261
|
+
expect([kept, second, File.exist?(yml(a_dir))]).to eq([true, removed_notice('beta'), false])
|
|
262
|
+
expect(err.string.lines.size).to eq(2)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
it 'counts in the plural' do
|
|
266
|
+
write_yml(a_dir, 'alpha', 'beta')
|
|
267
|
+
adapter = fake_adapter_class.new
|
|
268
|
+
adapter.answers.replace('alpha' => RubyRaider::Schedules::Error.new('x'), 'beta' => RubyRaider::Schedules::Error.new('y'))
|
|
269
|
+
|
|
270
|
+
expect(cleanup(a_dir, adapter))
|
|
271
|
+
.to eq('Could not remove 2 schedules made with Ruby Raider 4.1 (alpha: x, beta: y); they are tried again next time.')
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
it 'names every schedule it removed, from the yml and the OS, sorted' do
|
|
275
|
+
write_yml(a_dir, 'beta')
|
|
276
|
+
adapter = fake_adapter_class.new([os_id(a_dir, 'alpha'), os_id(a_dir, 'beta')])
|
|
277
|
+
|
|
278
|
+
expect(cleanup(a_dir, adapter)).to eq(removed_notice('alpha', 'beta'))
|
|
279
|
+
expect(adapter.calls.drop(1)).to eq(removed('alpha', 'beta'))
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
describe 'with nothing to clean' do
|
|
284
|
+
it 'is nil, prints nothing, creates or deletes nothing, and only enumerates' do
|
|
285
|
+
adapter = fake_adapter_class.new
|
|
286
|
+
FileUtils.mkdir_p(File.join(a_dir, 'flows'))
|
|
287
|
+
before = Dir.glob('**/*', File::FNM_DOTMATCH, base: tmp).sort
|
|
288
|
+
|
|
289
|
+
expect(cleanup(a_dir, adapter)).to be_nil
|
|
290
|
+
expect([err.string, Dir.glob('**/*', File::FNM_DOTMATCH, base: tmp).sort]).to eq(['', before])
|
|
291
|
+
expect(adapter.calls).to eq([[:list_ids, old_rule.os_id_prefix_for(a_dir)]])
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
it 'is nil when the entries are already gone, and deletes the yml' do
|
|
295
|
+
write_yml(a_dir, 'report')
|
|
296
|
+
adapter = fake_adapter_class.new
|
|
297
|
+
adapter.answers['report'] = false
|
|
298
|
+
|
|
299
|
+
expect([cleanup(a_dir, adapter), err.string, File.exist?(yml(a_dir))]).to eq([nil, '', false])
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
it 'deletes an empty schedules file' do
|
|
303
|
+
results = ['', "schedules: []\n"].map do |text|
|
|
304
|
+
FileUtils.mkdir_p(File.dirname(yml(a_dir)))
|
|
305
|
+
File.write(yml(a_dir), text)
|
|
306
|
+
[cleanup(a_dir, fake_adapter_class.new), File.exist?(yml(a_dir))]
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
expect([results, err.string]).to eq([[[nil, false], [nil, false]], ''])
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
it 'does nothing where there is no OS scheduler' do
|
|
313
|
+
write_yml(a_dir, 'report')
|
|
314
|
+
allow(RubyRaider::Schedules).to receive(:adapter).and_raise(RubyRaider::Schedules::Error, 'Scheduling 4.1 schedules is not supported on solaris')
|
|
315
|
+
|
|
316
|
+
expect([described_class.new(project(a_dir), err: err).cleanup, err.string, File.exist?(yml(a_dir))]).to eq([nil, '', true])
|
|
317
|
+
expect(RubyRaider::Schedules).to have_received(:adapter).with(kind: kind)
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
describe 'a yml it does not read' do
|
|
322
|
+
let(:adapter) { fake_adapter_class.new }
|
|
323
|
+
|
|
324
|
+
it 'skips .raider/flows when it is a link to another folder, and leaves what is there' do
|
|
325
|
+
outside = project_dir('outside')
|
|
326
|
+
write_yml(outside, entries: [{ 'name' => 'report', 'os_id' => os_id(a_dir, 'report') }])
|
|
327
|
+
FileUtils.mkdir_p(File.join(a_dir, '.raider'))
|
|
328
|
+
File.symlink(File.join(outside, '.raider', 'flows'), File.join(a_dir, '.raider', 'flows'))
|
|
329
|
+
|
|
330
|
+
expect(cleanup(a_dir, adapter)).to be_nil
|
|
331
|
+
expect([adapter.calls.map(&:first), File.exist?(yml(outside))]).to eq([[:list_ids], true])
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
it 'skips a schedules.yml that is a link, and leaves it' do
|
|
335
|
+
outside = project_dir('outside')
|
|
336
|
+
write_yml(outside, entries: [{ 'name' => 'report', 'os_id' => os_id(a_dir, 'report') }])
|
|
337
|
+
FileUtils.mkdir_p(File.dirname(yml(a_dir)))
|
|
338
|
+
File.symlink(yml(outside), yml(a_dir))
|
|
339
|
+
|
|
340
|
+
expect(cleanup(a_dir, adapter)).to be_nil
|
|
341
|
+
expect([adapter.calls.map(&:first), File.symlink?(yml(a_dir)), File.exist?(yml(outside))]).to eq([[:list_ids], true, true])
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
it 'skips entries that are not 4.1 ones, and a file that is too big or not YAML' do
|
|
345
|
+
write_yml(a_dir, entries: [{ 'name' => 123, 'os_id' => os_id(a_dir, '123') }, { 'name' => 'env', 'os_id' => os_id(a_dir, 'env') },
|
|
346
|
+
{ 'name' => 'Caps', 'os_id' => os_id(a_dir, 'Caps') }, { 'name' => 'nameless' }, 'report'])
|
|
347
|
+
results = [[cleanup(a_dir, adapter), File.exist?(yml(a_dir))]]
|
|
348
|
+
["schedules: [\n", "schedules:\n - name: #{'a' * 1_000_001}\n", "schedules: report\n", "- report\n"].each do |text|
|
|
349
|
+
File.write(yml(a_dir), text)
|
|
350
|
+
results << [cleanup(a_dir, adapter), File.exist?(yml(a_dir))]
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
expect(results).to all(eq([nil, true]))
|
|
354
|
+
expect(adapter.calls.map(&:first).uniq).to eq([:list_ids])
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
it "leaves the user's files" do
|
|
359
|
+
write_yml(a_dir, 'report')
|
|
360
|
+
kept = %w[flows/login.yml flow-results/run.json .raider/flows/env .raider/flows/report/scheduler.log .raider/tests/flows/x.rb]
|
|
361
|
+
kept.each do |path|
|
|
362
|
+
FileUtils.mkdir_p(File.dirname(File.join(a_dir, path)))
|
|
363
|
+
File.write(File.join(a_dir, path), path)
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
expect(cleanup(a_dir, fake_adapter_class.new)).to eq(removed_notice('report'))
|
|
367
|
+
expect(kept.map { |path| File.read(File.join(a_dir, path)) }).to eq(kept)
|
|
368
|
+
expect(File.exist?(yml(a_dir))).to be(false)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
it 'turns an unexpected error into one printed line, never raising' do
|
|
372
|
+
write_yml(a_dir, 'report')
|
|
373
|
+
adapter = fake_adapter_class.new
|
|
374
|
+
adapter.answers['report'] = RuntimeError.new("odd\e[2J")
|
|
375
|
+
|
|
376
|
+
expect(cleanup(a_dir, adapter)).to eq('Could not remove the schedules made with Ruby Raider 4.1: odd?[2J')
|
|
377
|
+
expect([err.string, File.exist?(yml(a_dir))]).to eq(["Could not remove the schedules made with Ruby Raider 4.1: odd?[2J\n", true])
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
describe 'its own entry (last)' do
|
|
381
|
+
let(:events) { [] }
|
|
382
|
+
let(:recording_err) do
|
|
383
|
+
log = events
|
|
384
|
+
Class.new do
|
|
385
|
+
define_method(:puts) { |text| log << [:puts, text] }
|
|
386
|
+
define_method(:flush) { log << [:flush] }
|
|
387
|
+
end.new
|
|
388
|
+
end
|
|
389
|
+
let(:adapter) do
|
|
390
|
+
fake_adapter_class.new([os_id(a_dir, 'report'), os_id(a_dir, 'other')]).tap do |double|
|
|
391
|
+
log = events
|
|
392
|
+
path = yml(a_dir)
|
|
393
|
+
double.on_remove = ->(id) { log << [:remove, id.split('.').last, File.exist?(path)] }
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def own_cleanup = described_class.new(project(a_dir), adapter: adapter, err: recording_err).cleanup(last: 'report')
|
|
398
|
+
|
|
399
|
+
it 'is counted, and removed after the notice is out and the yml deleted' do
|
|
400
|
+
write_yml(a_dir, 'report')
|
|
401
|
+
|
|
402
|
+
notice = own_cleanup
|
|
403
|
+
|
|
404
|
+
expect(notice).to eq(removed_notice('other', 'report'))
|
|
405
|
+
expect(events).to eq([[:remove, 'other', true], [:puts, notice], [:flush], [:remove, 'report', false]])
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
it 'says so on a second line when it can not be removed' do
|
|
409
|
+
adapter.answers['report'] = RubyRaider::Schedules::Error.new('denied')
|
|
410
|
+
|
|
411
|
+
own_cleanup
|
|
412
|
+
|
|
413
|
+
expect(events.select { |event| event.first == :puts }.map(&:last)).to eq([removed_notice('other', 'report'), 'Could not remove report: denied'])
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
it 'is not removed when it is not one of the names' do
|
|
417
|
+
adapter.ids = [os_id(a_dir, 'other')]
|
|
418
|
+
|
|
419
|
+
expect(described_class.new(project(a_dir), adapter: adapter, err: err).cleanup(last: 'report')).to eq(removed_notice('other'))
|
|
420
|
+
expect(adapter.calls.drop(1)).to eq(removed('other'))
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
it 'prints no flow word' do
|
|
425
|
+
write_yml(a_dir, 'alpha', 'beta')
|
|
426
|
+
adapter = fake_adapter_class.new
|
|
427
|
+
adapter.answers['beta'] = RubyRaider::Schedules::Error.new('busy')
|
|
428
|
+
cleanup(a_dir, adapter)
|
|
429
|
+
|
|
430
|
+
expect(err.string).not_to match(/\bflows?\b/i)
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
# rubocop:enable RSpec/MultipleMemoizedHelpers
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'stringio'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
require_relative '../../../lib/ruby_raider'
|
|
6
|
+
require_relative '../../../lib/utilities/schedules/legacy_stub'
|
|
7
|
+
require_relative '../../../lib/utilities/schedules/notifier'
|
|
8
|
+
|
|
9
|
+
# Through RubyRaider::Raider.start, as `raider flow ...` from a 4.1 OS entry reaches it. Legacy always gets a
|
|
10
|
+
# recording adapter: nothing reaches the OS scheduler.
|
|
11
|
+
# rubocop:disable RSpec/MultipleMemoizedHelpers
|
|
12
|
+
RSpec.describe RubyRaider::Schedules::LegacyStub do # rubocop:disable RSpec/FilePath
|
|
13
|
+
let(:fake_adapter_class) do
|
|
14
|
+
Class.new do
|
|
15
|
+
attr_reader :ids, :removed
|
|
16
|
+
|
|
17
|
+
def initialize(ids)
|
|
18
|
+
@ids = ids
|
|
19
|
+
@removed = []
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def list_ids(prefix) = ids.select { |id| id.start_with?(prefix) }
|
|
23
|
+
|
|
24
|
+
def remove(os_id)
|
|
25
|
+
@removed << os_id.split('.').last
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
let(:dir) { File.realpath(Dir.mktmpdir('legacy-stub')) }
|
|
31
|
+
let(:adapter) { fake_adapter_class.new(%w[other tredssd].map { |name| os_id(name) }) }
|
|
32
|
+
let(:removed_line) { 'This command was removed in Ruby Raider 4.2. To run tests on a schedule, use raider schedule add.' }
|
|
33
|
+
let(:notice) do
|
|
34
|
+
"Removed 2 schedules made with Ruby Raider 4.1 (other, tredssd); they can't run in 4.2. Add them again with raider schedule add."
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
before do
|
|
38
|
+
File.write(File.join(dir, 'Gemfile'), "source 'https://rubygems.org'\n")
|
|
39
|
+
allow(RubyRaider::Schedules::Legacy).to receive(:new).and_wrap_original do |original, project, **options|
|
|
40
|
+
original.call(project, adapter: adapter, **options)
|
|
41
|
+
end
|
|
42
|
+
expect(RubyRaider::Schedules::Notifier).not_to receive(:new) # rubocop:disable RSpec/ExpectInHook
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
after { FileUtils.rm_rf(dir) }
|
|
46
|
+
|
|
47
|
+
def os_id(name) = RubyRaider::Schedules::Legacy::OldRule.os_id_for(dir, name)
|
|
48
|
+
|
|
49
|
+
# [stdout, stderr, exit code or nil when it did not exit]
|
|
50
|
+
def raider(*args)
|
|
51
|
+
out = StringIO.new
|
|
52
|
+
err = StringIO.new
|
|
53
|
+
stdout = $stdout
|
|
54
|
+
stderr = $stderr
|
|
55
|
+
$stdout = out
|
|
56
|
+
$stderr = err
|
|
57
|
+
code = begin
|
|
58
|
+
RubyRaider::Raider.start(args)
|
|
59
|
+
nil
|
|
60
|
+
rescue SystemExit => e
|
|
61
|
+
e.status
|
|
62
|
+
ensure
|
|
63
|
+
$stdout = stdout
|
|
64
|
+
$stderr = stderr
|
|
65
|
+
end
|
|
66
|
+
[out.string, err.string, code]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "removes the project's 4.1 schedules, its own last, and exits 2 with the notice on stderr" do
|
|
70
|
+
result = raider('flow', 'run', 'tredssd', '--project', dir, '--trigger', 'schedule', '--retries', '1', '--notify', '--input', 'a=b')
|
|
71
|
+
|
|
72
|
+
expect(result).to eq(['', "#{notice}\n", 2])
|
|
73
|
+
expect(adapter.removed).to eq(%w[other tredssd])
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'takes --project=VALUE, and f' do
|
|
77
|
+
result = raider('f', 'run', 'tredssd', "--project=#{dir}", '--trigger', 'schedule')
|
|
78
|
+
|
|
79
|
+
expect(result).to eq(['', "#{notice}\n", 2])
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'takes the first --project only' do
|
|
83
|
+
result = raider('flow', 'run', 'tredssd', '--project', dir, '--project', '/nonexistent')
|
|
84
|
+
|
|
85
|
+
expect(result).to eq(['', "#{notice}\n", 2])
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'still cleans up with invalid UTF-8 in another argument' do
|
|
89
|
+
result = raider('flow', 'run', "a\xFF", '--project', dir, '--input', "x=\xFF")
|
|
90
|
+
|
|
91
|
+
expect(result).to eq(['', "#{notice}\n", 2])
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'says none was found for the project when there is nothing to remove' do
|
|
95
|
+
adapter.ids.clear
|
|
96
|
+
|
|
97
|
+
result = raider('flow', 'run', 'tredssd', '--project', dir, '--trigger', 'schedule')
|
|
98
|
+
|
|
99
|
+
expect(result).to eq(['', "This schedule was made with Ruby Raider 4.1 and can't run in 4.2, and none was found for #{dir} " \
|
|
100
|
+
"to remove: see Upgrading from 4.1 in the README.\n", 2])
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'exits 2 even when the cleanup misbehaves' do
|
|
104
|
+
allow(RubyRaider::Schedules::Legacy).to receive(:new).and_raise(RuntimeError, 'odd')
|
|
105
|
+
|
|
106
|
+
expect(raider('flow', 'run', 'tredssd', '--project', dir).last).to eq(2)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
describe 'without a usable project' do
|
|
110
|
+
let(:no_gemfile) { File.realpath(Dir.mktmpdir('legacy-stub-bare')) }
|
|
111
|
+
|
|
112
|
+
after { FileUtils.rm_rf(no_gemfile) }
|
|
113
|
+
|
|
114
|
+
it 'says the command was removed, exits 2 and cleans up nothing, whatever the argv' do
|
|
115
|
+
gemfile_dir = File.realpath(Dir.mktmpdir('legacy-stub-gemfile-dir'))
|
|
116
|
+
FileUtils.mkdir_p(File.join(gemfile_dir, 'Gemfile'))
|
|
117
|
+
argvs = [%w[flow], %w[f], %w[flow --help], %w[flow help], %w[flow run tredssd --project], ['flow', 'run', 'tredssd', '--project', "#{dir}\xFF"],
|
|
118
|
+
%w[flow run tredssd --project relative/dir], %w[flow run tredssd --project /nonexistent/raider],
|
|
119
|
+
['flow', 'run', 'tredssd', '--project', no_gemfile], ['flow', 'run', 'tredssd', '--project', gemfile_dir],
|
|
120
|
+
['flow', 'run', 'tredssd', '--project', "#{dir}\0"], %w[flow run tredssd --retries 1 --notify --input a=b]]
|
|
121
|
+
|
|
122
|
+
results = argvs.map { |argv| raider(*argv) }
|
|
123
|
+
|
|
124
|
+
expect(results).to all(eq(['', "#{removed_line}\n", 2]))
|
|
125
|
+
expect(RubyRaider::Schedules::Legacy).not_to have_received(:new)
|
|
126
|
+
ensure
|
|
127
|
+
FileUtils.rm_rf(gemfile_dir)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'never takes the current folder' do
|
|
131
|
+
result = Dir.chdir(dir) { raider('flow', 'run', 'tredssd', '--trigger', 'schedule') }
|
|
132
|
+
|
|
133
|
+
expect(result).to eq(['', "#{removed_line}\n", 2])
|
|
134
|
+
expect(adapter.removed).to be_empty
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'is not in raider help' do
|
|
139
|
+
out, = raider('help')
|
|
140
|
+
|
|
141
|
+
expect(out).not_to match(/\bflows?\b/)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
# rubocop:enable RSpec/MultipleMemoizedHelpers
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'tmpdir'
|
|
4
|
-
require_relative '../../../lib/utilities/
|
|
4
|
+
require_relative '../../../lib/utilities/schedules/next_run'
|
|
5
|
+
require_relative '../../../lib/utilities/schedules/schedule'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
RSpec.describe RubyRaider::Flows::NextRun do # rubocop:disable RSpec/FilePath
|
|
7
|
+
RSpec.describe RubyRaider::Schedules::NextRun do # rubocop:disable RSpec/FilePath
|
|
8
8
|
let(:dir) { File.realpath(Dir.mktmpdir('flows-next-run')) }
|
|
9
|
-
let(:project) { RubyRaider::
|
|
9
|
+
let(:project) { RubyRaider::Schedules::Project.new(dir) }
|
|
10
10
|
|
|
11
11
|
around do |example|
|
|
12
12
|
zone = ENV.fetch('TZ', nil)
|
|
@@ -19,7 +19,7 @@ RSpec.describe RubyRaider::Flows::NextRun do # rubocop:disable RSpec/FilePath
|
|
|
19
19
|
after { FileUtils.rm_rf(dir) }
|
|
20
20
|
|
|
21
21
|
def rule(every, at = '08:00', on: nil)
|
|
22
|
-
RubyRaider::
|
|
22
|
+
RubyRaider::Schedules::Schedule.build(project: project, name: 'report', every: every, at: at, on: on)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def next_after(schedule, *local)
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'stringio'
|
|
4
|
-
require_relative '../../../lib/utilities/
|
|
4
|
+
require_relative '../../../lib/utilities/schedules/notifier'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
RSpec.describe RubyRaider::Flows::Notifier do # rubocop:disable RSpec/FilePath
|
|
6
|
+
RSpec.describe RubyRaider::Schedules::Notifier do # rubocop:disable RSpec/FilePath
|
|
8
7
|
# Records each command instead of running it: no real osascript, notify-send or PowerShell
|
|
9
8
|
let(:runner) do
|
|
10
9
|
Class.new do
|
|
@@ -18,7 +17,7 @@ RSpec.describe RubyRaider::Flows::Notifier do # rubocop:disable RSpec/FilePath
|
|
|
18
17
|
|
|
19
18
|
def call(*argv, env: {}, timeout: nil, **)
|
|
20
19
|
@calls << { argv: argv, env: env, timeout: timeout }
|
|
21
|
-
RubyRaider::
|
|
20
|
+
RubyRaider::Schedules::Os::CommandRunner::Result.new(stdout: '', stderr: 'no notification server', exitstatus: exitstatus)
|
|
22
21
|
end
|
|
23
22
|
end.new
|
|
24
23
|
end
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'rbconfig'
|
|
4
|
-
require_relative '../../../../lib/utilities/
|
|
4
|
+
require_relative '../../../../lib/utilities/schedules/os/command_runner'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
RSpec.describe RubyRaider::Flows::Os::CommandRunner do # rubocop:disable RSpec/FilePath
|
|
6
|
+
RSpec.describe RubyRaider::Schedules::Os::CommandRunner do # rubocop:disable RSpec/FilePath
|
|
8
7
|
let(:runner) { described_class.new }
|
|
9
8
|
let(:ruby) { RbConfig.ruby }
|
|
10
9
|
|