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,22 +0,0 @@
1
- module RubyRaider
2
- module Flows
3
- class RunLog
4
- MAX_BYTES: Integer
5
- NOFOLLOW: Integer
6
- NONBLOCK: Integer
7
- SUMMARY: Array[String]
8
-
9
- def self.summaries: (Project project) -> Array[Hash[String, untyped]]
10
-
11
- def initialize: (Project project, String name) -> void
12
- def lines: () -> Array[Hash[String, untyped]]
13
- def last: () -> Hash[String, untyped]?
14
- def last_result: () -> Hash[String, untyped]?
15
- def find: (String run_id) -> Hash[String, untyped]?
16
-
17
- private
18
-
19
- def tail: () -> String
20
- end
21
- end
22
- end
@@ -1,35 +0,0 @@
1
- module RubyRaider
2
- module Flows
3
- class Runner
4
- EXIT_CODES: Hash[String, Integer]
5
- INPUT: Regexp
6
- INPUT_RULE: String
7
- TRIGGERS: Array[String]
8
- RETRY_WAIT: Integer
9
-
10
- def self.spawn: (Hash[String, String?] env, Array[String] argv, String chdir) -> Integer?
11
- def self.stopped: () -> bool?
12
- def self.stopped=: (bool) -> bool
13
- def self.interrupt: (Integer pid) -> void
14
-
15
- def initialize: (Project project, ?out: IO | StringIO, ?spawner: untyped, ?sleeper: untyped, ?notifier: untyped) -> void
16
- def run: (String name, ?headed: bool?, ?retries: untyped, ?inputs: Array[String]?, ?trigger: String, ?notify: bool) -> Integer
17
-
18
- private
19
-
20
- def attempt_runs: (String name, Integer attempts, Hash[Symbol, untyped] options) -> String
21
- def notify_failure: (String name, String status, String trigger) -> bool
22
- def start_output: (String name, String trigger) -> String
23
- def stopped: (String name, String status) -> String
24
- def attempt_run: (String name, Integer attempt, Hash[Symbol, untyped] options) -> String
25
- def run_env: (String name, Integer attempt, String run_id, Hash[Symbol, untyped] options) -> Hash[String, String?]
26
- def input_env: (Array[String]? inputs) -> Hash[String, String]
27
- def checked_trigger: (String trigger) -> String
28
- def checked_retries: (untyped retries) -> Integer
29
- def report: (String name, Hash[String, untyped]? line, Integer? code) -> void
30
- def outcome: (Hash[String, untyped] line) -> String
31
- def failed_step: (untyped step) -> String
32
- def duration: (Hash[String, untyped] line) -> (Float | String)
33
- end
34
- end
35
- end
@@ -1,128 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'open3'
4
- require 'tmpdir'
5
- require_relative '../../lib/ruby_raider'
6
-
7
- RSpec.describe FlowCommands do
8
- let(:raider_bin) { File.expand_path('../../bin/raider', __dir__) }
9
-
10
- def run_raider(*args)
11
- Open3.capture3(RbConfig.ruby, raider_bin, *args)
12
- end
13
-
14
- it 'lists init, run and list in raider flow help' do
15
- out, _err, status = run_raider('flow', 'help')
16
-
17
- expect(status.exitstatus).to eq(0)
18
- expect(out).to include('flow init', 'flow run NAME', 'flow list')
19
- end
20
-
21
- it 'maps raider f to the flow commands' do
22
- out, _err, status = run_raider('f', 'help')
23
-
24
- expect(status.exitstatus).to eq(0)
25
- expect(out).to include('flow run NAME')
26
- end
27
-
28
- it 'is listed in raider help' do
29
- out, = run_raider('help')
30
-
31
- expect(out).to include('raider flow')
32
- end
33
-
34
- describe 'init' do
35
- it 'sets up flows in the --project folder from any working directory' do
36
- Dir.mktmpdir('flow-init') do |dir|
37
- File.write(File.join(dir, 'Gemfile'), "gem 'ruby_raider', '~> 4.0'\ngem 'selenium-webdriver'\n")
38
- FileUtils.mkdir_p(File.join(dir, 'helpers'))
39
- File.write(File.join(dir, 'helpers/driver_helper.rb'), "require 'ruby_raider/runtime'\n")
40
-
41
- out, _err, status = run_raider('flow', 'init', '--project', dir)
42
-
43
- expect(status.exitstatus).to eq(0)
44
- expect(out).to include('Created flows/flow_helper.rb', 'Run bundle install')
45
- end
46
- end
47
-
48
- it 'exits 2 for a project that cannot hold flows' do
49
- Dir.mktmpdir('flow-init') do |dir|
50
- _out, err, status = run_raider('flow', 'init', '--project', dir)
51
-
52
- expect(status.exitstatus).to eq(2)
53
- expect(err).to include('Flows need a Selenium web project made with Ruby Raider 4.0 or later')
54
- end
55
- end
56
-
57
- it 'exits 2 for a missing project folder' do
58
- _out, err, status = run_raider('flow', 'init', '--project', '/nonexistent/raider')
59
-
60
- expect(status.exitstatus).to eq(2)
61
- expect(err).to include('Project folder not found')
62
- end
63
- end
64
-
65
- it 'offers --notify on flow run and flow schedule' do
66
- run_help, = run_raider('flow', 'help', 'run')
67
- schedule_help, = run_raider('flow', 'help', 'schedule')
68
-
69
- expect([run_help, schedule_help]).to all(include('--notify'))
70
- end
71
-
72
- describe 'schedules' do
73
- # Only paths refused before the OS scheduler is asked: this machine's launchd is real
74
- it 'lists schedule, unschedule and schedules in raider flow help' do
75
- out, _err, status = run_raider('flow', 'help')
76
-
77
- expect(status.exitstatus).to eq(0)
78
- expect(out).to include('flow schedule NAME', 'flow unschedule NAME', 'flow schedules')
79
- end
80
-
81
- it 'dispatches raider flow schedule NAME to schedule, not schedules' do
82
- Dir.mktmpdir('flow-schedule') do |dir|
83
- _out, err, status = run_raider('flow', 'schedule', 'x', '--every', 'day', '--at', '08:00', '--project', dir)
84
-
85
- expect(status.exitstatus).to eq(2)
86
- expect(err).to include('No flow x: flows/x_flow.rb not found')
87
- end
88
- end
89
-
90
- it 'exits 2 through the rule check when --every is left out' do
91
- Dir.mktmpdir('flow-schedule') do |dir|
92
- %w[flows/flow_helper.rb flows/support/run.rb flows/report_flow.rb].each do |file|
93
- FileUtils.mkdir_p(File.dirname(File.join(dir, file)))
94
- File.write(File.join(dir, file), '')
95
- end
96
- _out, err, status = run_raider('flow', 'schedule', 'report', '--at', '08:00', '--project', dir)
97
-
98
- expect(status.exitstatus).to eq(2)
99
- expect(err).to include('Invalid every nil: use hour, day, weekday or week')
100
- end
101
- end
102
-
103
- it 'exits 2 for an invalid name to unschedule' do
104
- Dir.mktmpdir('flow-schedule') do |dir|
105
- _out, err, status = run_raider('flow', 'unschedule', 'Bad-Name', '--project', dir)
106
-
107
- expect(status.exitstatus).to eq(2)
108
- expect(err).to include('Invalid flow name "Bad-Name"')
109
- end
110
- end
111
-
112
- it 'exits 2 with a message for an argument that is not valid UTF-8' do
113
- _out, err, status = run_raider('flow', 'schedule', "a\xFF".b, '--every', 'day')
114
-
115
- expect(status.exitstatus).to eq(2)
116
- expect(err).to eq("Invalid argument \"a\\xFF\": use valid UTF-8\n")
117
- end
118
-
119
- it 'says there are no schedules, as text and as JSON' do
120
- Dir.mktmpdir('flow-schedule') do |dir|
121
- text, = run_raider('flow', 'schedules', '--project', dir)
122
- json, = run_raider('flow', 'schedules', '--json', '--project', dir)
123
-
124
- expect([text, json]).to eq(["No schedules yet: raider flow schedule <name> --every day --at 08:00\n", "[]\n"])
125
- end
126
- end
127
- end
128
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'erb'
4
-
5
- # The flow templates are copied into projects by `raider flow init`
6
- RSpec.describe 'flow templates' do
7
- let(:templates) { File.expand_path('../../lib/generators/templates', __dir__) }
8
- let(:helper) { render('flows/flow_helper.tt') }
9
-
10
- def render(relative) = ERB.new(File.read(File.join(templates, relative)), trim_mode: '-').result
11
-
12
- it 'starts the run before loading spec_helper, so the lock and HEADLESS come before the driver' do
13
- expect(helper.index('RaiderFlows::Run.start!')).to be < helper.index("require_relative '../helpers/spec_helper'")
14
- end
15
-
16
- it 'sends the report to the flow results folder without cleaning it, even when start! refuses the run' do
17
- expect(helper).to match(/begin\n RaiderFlows::Run.start!\nensure\n RaiderFlows::Run.configure_reporter\nend/)
18
- expect(render('flows/run.tt')).to include('reporter.output_dir = results_dir ||', 'reporter.clean_output_dir = false')
19
- end
20
-
21
- it 'never retries a flow inside one run' do
22
- expect(helper).to include('config.default_retry_count = 0')
23
- end
24
-
25
- it 'marks the start of the steps after spec_helper set up the driver' do
26
- expect(helper).to include('config.append_before(:each) { RaiderFlows::Run.steps_started! }')
27
- end
28
-
29
- it 'never sets the reporter output folder from spec_helper, so a test run cannot wipe flow results' do
30
- spec_helper = File.read(File.join(templates, 'helpers/spec_helper.tt'))
31
-
32
- expect(spec_helper).not_to match(/output_dir|clean_output_dir/)
33
- end
34
-
35
- it 'renders Ruby that parses' do
36
- [helper, render('flows/run.tt')].each do |source|
37
- expect { RubyVM::InstructionSequence.compile(source) }.not_to raise_error
38
- end
39
- end
40
- end
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'erb'
4
- require 'fileutils'
5
-
6
- # A project with the flow files `raider flow init` writes and a stand-in helpers/spec_helper.rb:
7
- # no browser and no raider_reporter. FAKE_DRIVER_FAIL makes the driver fail in before(:each);
8
- # FAKE_STEPS writes a run.json whose desktop step map names step 2 of flows/recorded_flow.rb.
9
- module FlowProject
10
- TEMPLATES = File.expand_path('../../../lib/generators/templates/flows', __dir__)
11
-
12
- STUB_SPEC_HELPER = <<~RUBY
13
- # frozen_string_literal: true
14
-
15
- require 'json'
16
- require 'rspec'
17
-
18
- module RaiderReporter
19
- Config = Struct.new(:output_dir, :clean_output_dir)
20
- def self.configuration = (@configuration ||= Config.new(output_dir: 'raider-results', clean_output_dir: true))
21
- def self.configure = yield(configuration)
22
- end
23
-
24
- # What the real reporter would write to and clean, for the specs to read
25
- at_exit { File.write('reporter.json', JSON.generate(RaiderReporter.configuration.to_h)) }
26
-
27
- module DriverHelper
28
- def driver
29
- raise 'the driver could not start' if ENV['FAKE_DRIVER_FAIL']
30
-
31
- @driver ||= Object.new
32
- end
33
- end
34
-
35
- RSpec.configure do |config|
36
- config.include(DriverHelper)
37
- config.before(:each) { driver }
38
- config.after(:each) { File.write('driver_quit', '') }
39
- # The real reporter writes run.json when it closes; the desktop sidecar names the failed step there
40
- config.after(:suite) do
41
- dir = RaiderReporter.configuration.output_dir
42
- steps = { 'items' => [{ 'id' => 's1', 'title' => 'open' }, { 'id' => 's2', 'title' => 'click "Export"' }], 'failed' => 1 }
43
- results = [{ 'file_path' => 'flows/recorded_flow.rb', 'status' => 'failed', 'steps' => steps }]
44
- File.write(File.join(dir, 'run.json'), JSON.generate('results' => results)) if dir && ENV['FAKE_STEPS']
45
- end
46
- end
47
- RUBY
48
-
49
- module_function
50
-
51
- def build(dir, flows)
52
- FileUtils.mkdir_p(File.join(dir, 'flows/support'))
53
- FileUtils.mkdir_p(File.join(dir, 'helpers'))
54
- File.write(File.join(dir, 'flows/flow_helper.rb'), render('flow_helper.tt'))
55
- File.write(File.join(dir, 'flows/support/run.rb'), render('run.tt'))
56
- File.write(File.join(dir, 'helpers/spec_helper.rb'), STUB_SPEC_HELPER)
57
- File.write(File.join(dir, '.rspec'), "-I .\n--require helpers/spec_helper\n")
58
- flows.each { |name, source| File.write(File.join(dir, "flows/#{name}_flow.rb"), source) }
59
- end
60
-
61
- def render(name) = ERB.new(File.read(File.join(TEMPLATES, name)), trim_mode: '-').result
62
- end
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
- require 'open3'
5
- require 'tmpdir'
6
- require_relative 'flow_project'
7
-
8
- # `raider flow run` and `raider flow list` against a project with its own Gemfile, from another folder.
9
- RSpec.describe 'raider flow run and list' do
10
- raider = File.expand_path('../../../bin/raider', __dir__)
11
-
12
- flows = {
13
- 'ok' => <<~RUBY,
14
- require_relative 'flow_helper'
15
-
16
- RSpec.describe 'ok' do
17
- it('runs every step') { puts "who=\#{flow_input('who', 'nobody')} headless=\#{ENV.fetch('HEADLESS', 'unset')}" }
18
- end
19
- RUBY
20
- 'broken' => <<~RUBY
21
- require_relative 'flow_helper'
22
-
23
- RSpec.describe 'broken' do
24
- it 'downloads the report' do
25
- raise 'no such element'
26
- end
27
- end
28
- RUBY
29
- }
30
-
31
- # Built once: bundling the project is the slow part
32
- dir = nil
33
-
34
- before(:context) do # rubocop:disable RSpec/BeforeAfterAll
35
- dir = File.realpath(Dir.mktmpdir('flow-command'))
36
- FlowProject.build(dir, flows)
37
- File.write(File.join(dir, 'Gemfile'), "source 'https://rubygems.org'\ngem 'rspec'\n")
38
- _out, err, status = Bundler.with_unbundled_env do
39
- Open3.capture3('bundle', 'install', '--prefer-local', '--quiet', chdir: dir)
40
- end
41
- raise "bundle install failed: #{err}" unless status.success?
42
- end
43
-
44
- after(:context) { FileUtils.rm_rf(dir) } # rubocop:disable RSpec/BeforeAfterAll
45
-
46
- define_method(:raider_flow) do |*args|
47
- out, err, status = Open3.capture3(RbConfig.ruby, raider, 'flow', *args, '--project', dir, chdir: Dir.tmpdir)
48
- [out, err, status.exitstatus]
49
- end
50
-
51
- it 'runs a completed flow, passing inputs and headless through, and exits 0' do
52
- out, _err, code = raider_flow('run', 'ok', '--input', 'who=ada')
53
-
54
- expect(code).to eq(0)
55
- expect(out).to include('who=ada headless=true', 'Flow ok completed in ')
56
- expect(out).to match(%r{^Results: flow-results/ok/\d{8}T\d{6}Z(-\d+)?$})
57
- end
58
-
59
- it 'exits 1 and names the failed line' do
60
- out, _err, code = raider_flow('run', 'broken')
61
-
62
- expect(code).to eq(1)
63
- expect(out).to include('Flow broken failed at line 5 (downloads the report)')
64
- end
65
-
66
- it 'exits 2 for a flow that does not exist' do
67
- _out, err, code = raider_flow('run', 'missing')
68
-
69
- expect(code).to eq(2)
70
- expect(err).to include('No flow missing: flows/missing_flow.rb not found')
71
- end
72
-
73
- it 'lists every flow with its last result as JSON for the desktop' do
74
- raider_flow('run', 'ok')
75
- out, _err, code = raider_flow('list', '--json')
76
- listed = JSON.parse(out)
77
-
78
- expect(code).to eq(0)
79
- expect(listed.map { |flow| flow['name'] }).to eq(%w[broken ok])
80
- expect(listed.last).to include('file' => 'flows/ok_flow.rb')
81
- expect(listed.last['last'].keys).to eq(%w[status finished_at failed_step results_dir])
82
- expect(listed.last['last']['status']).to eq('completed')
83
- end
84
-
85
- it 'lists flows as text' do
86
- raider_flow('run', 'ok')
87
- out, = raider_flow('list')
88
-
89
- expect(out).to match(/^ok\s+completed\s+\d{4}-\d\d-\d\dT/)
90
- end
91
- end