wheneverd 0.1.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 +7 -0
- data/.gitignore +26 -0
- data/.rubocop.yml +41 -0
- data/.yardopts +8 -0
- data/AGENTS.md +42 -0
- data/CHANGELOG.md +28 -0
- data/FEATURE_SUMMARY.md +38 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +129 -0
- data/LICENSE +21 -0
- data/README.md +204 -0
- data/Rakefile +196 -0
- data/bin/console +8 -0
- data/bin/setup +5 -0
- data/exe/wheneverd +9 -0
- data/lib/wheneverd/cli/activate.rb +19 -0
- data/lib/wheneverd/cli/current.rb +22 -0
- data/lib/wheneverd/cli/deactivate.rb +19 -0
- data/lib/wheneverd/cli/delete.rb +20 -0
- data/lib/wheneverd/cli/help.rb +18 -0
- data/lib/wheneverd/cli/init.rb +78 -0
- data/lib/wheneverd/cli/reload.rb +40 -0
- data/lib/wheneverd/cli/show.rb +23 -0
- data/lib/wheneverd/cli/write.rb +32 -0
- data/lib/wheneverd/cli.rb +87 -0
- data/lib/wheneverd/core_ext/numeric_duration.rb +56 -0
- data/lib/wheneverd/dsl/at_normalizer.rb +48 -0
- data/lib/wheneverd/dsl/calendar_symbol_period_list.rb +42 -0
- data/lib/wheneverd/dsl/context.rb +72 -0
- data/lib/wheneverd/dsl/errors.rb +29 -0
- data/lib/wheneverd/dsl/loader.rb +49 -0
- data/lib/wheneverd/dsl/period_parser.rb +135 -0
- data/lib/wheneverd/duration.rb +27 -0
- data/lib/wheneverd/entry.rb +31 -0
- data/lib/wheneverd/errors.rb +9 -0
- data/lib/wheneverd/interval.rb +37 -0
- data/lib/wheneverd/job/command.rb +29 -0
- data/lib/wheneverd/schedule.rb +25 -0
- data/lib/wheneverd/systemd/calendar_spec.rb +109 -0
- data/lib/wheneverd/systemd/cron_parser.rb +352 -0
- data/lib/wheneverd/systemd/errors.rb +23 -0
- data/lib/wheneverd/systemd/renderer.rb +153 -0
- data/lib/wheneverd/systemd/systemctl.rb +38 -0
- data/lib/wheneverd/systemd/time_parser.rb +75 -0
- data/lib/wheneverd/systemd/unit_deleter.rb +64 -0
- data/lib/wheneverd/systemd/unit_lister.rb +59 -0
- data/lib/wheneverd/systemd/unit_namer.rb +69 -0
- data/lib/wheneverd/systemd/unit_writer.rb +132 -0
- data/lib/wheneverd/trigger/boot.rb +26 -0
- data/lib/wheneverd/trigger/calendar.rb +26 -0
- data/lib/wheneverd/trigger/interval.rb +30 -0
- data/lib/wheneverd/version.rb +6 -0
- data/lib/wheneverd.rb +41 -0
- data/test/cli_activate_test.rb +110 -0
- data/test/cli_current_test.rb +94 -0
- data/test/cli_deactivate_test.rb +111 -0
- data/test/cli_end_to_end_test.rb +98 -0
- data/test/cli_reload_test.rb +132 -0
- data/test/cli_systemctl_integration_test.rb +76 -0
- data/test/cli_systemd_analyze_test.rb +64 -0
- data/test/cli_test.rb +332 -0
- data/test/domain_model_test.rb +108 -0
- data/test/dsl_calendar_symbol_period_list_test.rb +53 -0
- data/test/dsl_loader_test.rb +384 -0
- data/test/support/cli_subprocess_test_helpers.rb +38 -0
- data/test/support/cli_test_helpers.rb +114 -0
- data/test/systemd_calendar_spec_test.rb +45 -0
- data/test/systemd_cron_parser_test.rb +114 -0
- data/test/systemd_renderer_errors_test.rb +85 -0
- data/test/systemd_renderer_test.rb +161 -0
- data/test/systemd_systemctl_test.rb +46 -0
- data/test/systemd_time_parser_test.rb +25 -0
- data/test/systemd_unit_deleter_test.rb +83 -0
- data/test/systemd_unit_writer_prune_test.rb +85 -0
- data/test/systemd_unit_writer_test.rb +71 -0
- data/test/test_helper.rb +34 -0
- data/test/wheneverd_test.rb +9 -0
- data/wheneverd.gemspec +35 -0
- metadata +136 -0
data/test/cli_test.rb
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
require_relative "support/cli_test_helpers"
|
|
5
|
+
|
|
6
|
+
class CLIHelpAndVersionTest < Minitest::Test
|
|
7
|
+
include CLITestHelpers
|
|
8
|
+
|
|
9
|
+
def test_help_prints_usage_and_exits_zero
|
|
10
|
+
out, err = capture_io { Wheneverd::CLI.run("wheneverd", ["--help"]) }
|
|
11
|
+
assert_includes out, "Usage:"
|
|
12
|
+
assert_includes out, "wheneverd [OPTIONS]"
|
|
13
|
+
assert_equal "", err
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_version_prints_version_and_exits_zero
|
|
17
|
+
status, out, err = run_cli(["--version"])
|
|
18
|
+
assert_equal 0, status
|
|
19
|
+
assert_includes out, Wheneverd::VERSION
|
|
20
|
+
assert_equal "", err
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_no_args_exits_one_and_prints_help_to_stderr
|
|
24
|
+
status, out, err = run_cli([])
|
|
25
|
+
assert_equal 1, status
|
|
26
|
+
assert_equal "", out
|
|
27
|
+
assert_includes err, "Usage:"
|
|
28
|
+
assert_includes err, "wheneverd [OPTIONS]"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_invalid_option_exits_one_and_prints_error
|
|
32
|
+
out, err = capture_io do
|
|
33
|
+
exit_error = assert_raises(SystemExit) do
|
|
34
|
+
Wheneverd::CLI.run("wheneverd", ["--definitely-not-a-real-flag"])
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
assert_equal 1, exit_error.status
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
assert_equal "", out
|
|
41
|
+
assert_includes err, "ERROR:"
|
|
42
|
+
assert_includes err, "--definitely-not-a-real-flag"
|
|
43
|
+
assert_includes err, "See:"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class CLIInitTest < Minitest::Test
|
|
48
|
+
include CLITestHelpers
|
|
49
|
+
|
|
50
|
+
def test_init_creates_schedule_file
|
|
51
|
+
with_project_dir do
|
|
52
|
+
status, out, err = run_cli(["init"])
|
|
53
|
+
assert_equal 0, status
|
|
54
|
+
assert_includes out, "Wrote schedule template"
|
|
55
|
+
assert_equal "", err
|
|
56
|
+
assert File.file?(File.join("config", "schedule.rb"))
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def test_init_refuses_overwrite_without_force
|
|
61
|
+
with_project_dir do
|
|
62
|
+
path = File.join("config", "schedule.rb")
|
|
63
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
64
|
+
File.write(path, "# existing\n")
|
|
65
|
+
|
|
66
|
+
status, out, err = run_cli(["init"])
|
|
67
|
+
assert_equal 1, status
|
|
68
|
+
assert_equal "", out
|
|
69
|
+
assert_includes err, "already exists"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def test_init_overwrites_with_force
|
|
74
|
+
with_project_dir do
|
|
75
|
+
path = File.join("config", "schedule.rb")
|
|
76
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
77
|
+
File.write(path, "# existing\n")
|
|
78
|
+
|
|
79
|
+
status, out, err = run_cli(["init", "--force"])
|
|
80
|
+
assert_equal 0, status
|
|
81
|
+
assert_includes out, "Overwrote schedule template"
|
|
82
|
+
assert_equal "", err
|
|
83
|
+
assert_includes File.read(path), "every \"5m\""
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_init_handles_filesystem_errors
|
|
88
|
+
with_project_dir do
|
|
89
|
+
FileUtils.mkdir_p("config")
|
|
90
|
+
status, out, err = run_cli(["init", "--schedule", "config", "--force"])
|
|
91
|
+
assert_equal 1, status
|
|
92
|
+
assert_equal "", out
|
|
93
|
+
assert_includes err, "Is a directory"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def test_init_verbose_includes_error_details
|
|
98
|
+
with_project_dir do
|
|
99
|
+
FileUtils.mkdir_p("config")
|
|
100
|
+
status, out, err = run_cli(["init", "--schedule", "config", "--force", "--verbose"])
|
|
101
|
+
assert_equal 1, status
|
|
102
|
+
assert_equal "", out
|
|
103
|
+
assert_includes err, "Is a directory"
|
|
104
|
+
assert_includes err, "lib/wheneverd/cli/init.rb"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
class CLIShowTest < Minitest::Test
|
|
110
|
+
include CLITestHelpers
|
|
111
|
+
|
|
112
|
+
EXPECTED_SHOW_OUTPUT_SNIPPETS = [
|
|
113
|
+
"OnActiveSec=300",
|
|
114
|
+
"OnUnitActiveSec=300",
|
|
115
|
+
"OnCalendar=hourly",
|
|
116
|
+
"OnCalendar=*-*-27..31 00:00:00",
|
|
117
|
+
"ExecStart=echo hello"
|
|
118
|
+
].freeze
|
|
119
|
+
|
|
120
|
+
def test_show_renders_units_to_stdout
|
|
121
|
+
with_project_dir do
|
|
122
|
+
assert_equal 0, run_cli(["init"]).first
|
|
123
|
+
status, out, err = run_cli(["show", "--identifier", "demo"])
|
|
124
|
+
|
|
125
|
+
assert_equal 0, status
|
|
126
|
+
assert_equal "", err
|
|
127
|
+
EXPECTED_SHOW_OUTPUT_SNIPPETS.each { |expected| assert_includes out, expected }
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def test_show_reports_missing_schedule_file
|
|
132
|
+
with_project_dir do
|
|
133
|
+
status, out, err = run_cli(["show"])
|
|
134
|
+
assert_equal 1, status
|
|
135
|
+
assert_equal "", out
|
|
136
|
+
assert_includes err, "Schedule file not found"
|
|
137
|
+
assert_includes err, "config/schedule.rb"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
class CLIWriteTest < Minitest::Test
|
|
143
|
+
include CLITestHelpers
|
|
144
|
+
|
|
145
|
+
def test_write_creates_unit_files_in_unit_dir
|
|
146
|
+
with_project_dir do |project_dir|
|
|
147
|
+
unit_dir = File.join(project_dir, "tmp_units")
|
|
148
|
+
assert_equal 0, run_cli(["init"]).first
|
|
149
|
+
status, out, err = run_write(unit_dir)
|
|
150
|
+
|
|
151
|
+
assert_cli_success(status, err)
|
|
152
|
+
assert_first_job_written(unit_dir, out)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def test_write_dry_run_does_not_create_files
|
|
157
|
+
with_project_dir do |project_dir|
|
|
158
|
+
unit_dir = File.join(project_dir, "tmp_units")
|
|
159
|
+
assert_equal 0, run_cli(["init"]).first
|
|
160
|
+
status, out, err = run_write(unit_dir, "--dry-run")
|
|
161
|
+
|
|
162
|
+
assert_equal 0, status
|
|
163
|
+
assert_equal "", err
|
|
164
|
+
assert_includes out, File.join(unit_dir, expected_timer_basenames.fetch(0))
|
|
165
|
+
refute Dir.exist?(unit_dir)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def test_write_prunes_old_units_by_default
|
|
170
|
+
with_project_dir do |project_dir|
|
|
171
|
+
unit_dir = File.join(project_dir, "tmp_units")
|
|
172
|
+
units1 = write_schedule_and_expected_units(schedule_with_two_jobs)
|
|
173
|
+
write_and_assert_success(unit_dir)
|
|
174
|
+
assert_unit_files_present(unit_dir, units1)
|
|
175
|
+
units2 = write_schedule_and_expected_units(schedule_with_one_job)
|
|
176
|
+
write_and_assert_success(unit_dir)
|
|
177
|
+
assert_unit_files_pruned(unit_dir, units_before: units1, units_after: units2)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def test_write_no_prune_keeps_old_units
|
|
182
|
+
with_project_dir do |project_dir|
|
|
183
|
+
unit_dir = File.join(project_dir, "tmp_units")
|
|
184
|
+
units1 = write_schedule_and_expected_units(schedule_with_two_jobs)
|
|
185
|
+
|
|
186
|
+
status, _out, err = run_write(unit_dir)
|
|
187
|
+
assert_cli_success(status, err)
|
|
188
|
+
|
|
189
|
+
write_schedule(schedule_with_one_job)
|
|
190
|
+
status, _out, err = run_write(unit_dir, "--no-prune")
|
|
191
|
+
assert_cli_success(status, err)
|
|
192
|
+
|
|
193
|
+
assert_unit_files_present(unit_dir, units1)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def test_write_reports_missing_schedule_file
|
|
198
|
+
with_project_dir do |project_dir|
|
|
199
|
+
unit_dir = File.join(project_dir, "tmp_units")
|
|
200
|
+
status, out, err = run_cli(["write", "--unit-dir", unit_dir])
|
|
201
|
+
assert_equal 1, status
|
|
202
|
+
assert_equal "", out
|
|
203
|
+
assert_includes err, "Schedule file not found"
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def write_schedule(contents)
|
|
208
|
+
path = File.join("config", "schedule.rb")
|
|
209
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
210
|
+
File.write(path, contents)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def schedule_with_two_jobs
|
|
214
|
+
<<~RUBY
|
|
215
|
+
# frozen_string_literal: true
|
|
216
|
+
|
|
217
|
+
every "1m" do
|
|
218
|
+
command "echo a"
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
every "2m" do
|
|
222
|
+
command "echo b"
|
|
223
|
+
end
|
|
224
|
+
RUBY
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def schedule_with_one_job
|
|
228
|
+
<<~RUBY
|
|
229
|
+
# frozen_string_literal: true
|
|
230
|
+
|
|
231
|
+
every "1m" do
|
|
232
|
+
command "echo a"
|
|
233
|
+
end
|
|
234
|
+
RUBY
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
private
|
|
238
|
+
|
|
239
|
+
def run_write(unit_dir, *extra_args)
|
|
240
|
+
run_cli(["write", "--identifier", "demo", "--unit-dir", unit_dir, *extra_args])
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def assert_first_job_written(unit_dir, out)
|
|
244
|
+
assert_includes out, File.join(unit_dir, expected_service_basenames.fetch(0))
|
|
245
|
+
assert File.exist?(File.join(unit_dir, expected_timer_basenames.fetch(0)))
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def write_schedule_and_expected_units(contents)
|
|
249
|
+
write_schedule(contents)
|
|
250
|
+
expected_units(identifier: "demo")
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def assert_unit_files_present(unit_dir, units)
|
|
254
|
+
units.each { |unit| assert File.exist?(File.join(unit_dir, unit.path_basename)) }
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def assert_unit_files_pruned(unit_dir, units_before:, units_after:)
|
|
258
|
+
keep = units_after.map(&:path_basename)
|
|
259
|
+
stale = units_before.map(&:path_basename) - keep
|
|
260
|
+
|
|
261
|
+
keep.each { |basename| assert File.exist?(File.join(unit_dir, basename)) }
|
|
262
|
+
stale.each { |basename| refute File.exist?(File.join(unit_dir, basename)) }
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def write_and_assert_success(unit_dir, *extra_args)
|
|
266
|
+
status, _out, err = run_write(unit_dir, *extra_args)
|
|
267
|
+
assert_cli_success(status, err)
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
class CLIDeleteTest < Minitest::Test
|
|
272
|
+
include CLITestHelpers
|
|
273
|
+
|
|
274
|
+
def test_delete_removes_units_for_identifier
|
|
275
|
+
with_project_dir do |project_dir|
|
|
276
|
+
unit_dir = File.join(project_dir, "tmp_units")
|
|
277
|
+
init_template_schedule
|
|
278
|
+
write_demo_units(unit_dir)
|
|
279
|
+
|
|
280
|
+
status, out, err = delete_demo_units(unit_dir)
|
|
281
|
+
assert_cli_success(status, err)
|
|
282
|
+
assert_demo_timer_deleted(unit_dir, out)
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def test_delete_reports_invalid_identifier
|
|
287
|
+
with_project_dir do |project_dir|
|
|
288
|
+
unit_dir = File.join(project_dir, "tmp_units")
|
|
289
|
+
FileUtils.mkdir_p(unit_dir)
|
|
290
|
+
|
|
291
|
+
status, out, err = run_cli(["delete", "--identifier", "!!!", "--unit-dir", unit_dir])
|
|
292
|
+
assert_equal 1, status
|
|
293
|
+
assert_equal "", out
|
|
294
|
+
assert_includes err, "identifier must include at least one alphanumeric character"
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
private
|
|
299
|
+
|
|
300
|
+
def init_template_schedule
|
|
301
|
+
assert_equal 0, run_cli(["init"]).first
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def write_demo_units(unit_dir)
|
|
305
|
+
assert_equal 0, run_cli(["write", "--identifier", "demo", "--unit-dir", unit_dir]).first
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def delete_demo_units(unit_dir)
|
|
309
|
+
run_cli(["delete", "--identifier", "demo", "--unit-dir", unit_dir])
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def assert_demo_timer_deleted(unit_dir, out)
|
|
313
|
+
expected_timer = expected_timer_basenames.fetch(0)
|
|
314
|
+
expected_timer_path = File.join(unit_dir, expected_timer)
|
|
315
|
+
assert_includes out, expected_timer_path
|
|
316
|
+
refute File.exist?(expected_timer_path)
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
class CLIVerboseTest < Minitest::Test
|
|
321
|
+
include CLITestHelpers
|
|
322
|
+
|
|
323
|
+
def test_verbose_prints_error_details
|
|
324
|
+
with_project_dir do
|
|
325
|
+
status, out, err = run_cli(["show", "--verbose"])
|
|
326
|
+
assert_equal 1, status
|
|
327
|
+
assert_equal "", out
|
|
328
|
+
assert_includes err, "Schedule file not found"
|
|
329
|
+
assert_includes err, "lib/wheneverd/cli.rb"
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
|
|
5
|
+
class DomainModelTest < Minitest::Test
|
|
6
|
+
def test_interval_parses_supported_units
|
|
7
|
+
assert_equal 30, Wheneverd::Interval.parse("30s")
|
|
8
|
+
assert_equal 300, Wheneverd::Interval.parse("5m")
|
|
9
|
+
assert_equal 3600, Wheneverd::Interval.parse("1h")
|
|
10
|
+
assert_equal 86_400, Wheneverd::Interval.parse("1d")
|
|
11
|
+
assert_equal 604_800, Wheneverd::Interval.parse("1w")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_interval_rejects_invalid_inputs
|
|
15
|
+
error = assert_raises(Wheneverd::InvalidIntervalError) { Wheneverd::Interval.parse("abc") }
|
|
16
|
+
assert_includes error.message, "Invalid interval"
|
|
17
|
+
|
|
18
|
+
assert_raises(Wheneverd::InvalidIntervalError) { Wheneverd::Interval.parse("0m") }
|
|
19
|
+
assert_raises(Wheneverd::InvalidIntervalError) { Wheneverd::Interval.parse("-5m") }
|
|
20
|
+
assert_raises(Wheneverd::InvalidIntervalError) { Wheneverd::Interval.parse("5") }
|
|
21
|
+
assert_raises(Wheneverd::InvalidIntervalError) { Wheneverd::Interval.parse("5mm") }
|
|
22
|
+
assert_raises(Wheneverd::InvalidIntervalError) { Wheneverd::Interval.parse(nil) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_duration_wraps_seconds
|
|
26
|
+
duration = Wheneverd::Duration.new(42)
|
|
27
|
+
assert_equal 42, duration.seconds
|
|
28
|
+
assert_equal 42, duration.to_i
|
|
29
|
+
|
|
30
|
+
assert_raises(ArgumentError) { Wheneverd::Duration.new(1.0) }
|
|
31
|
+
assert_raises(ArgumentError) { Wheneverd::Duration.new(0) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_numeric_duration_helpers_singular
|
|
35
|
+
assert_equal 1, 1.second.to_i
|
|
36
|
+
assert_equal 60, 1.minute.to_i
|
|
37
|
+
assert_equal 3600, 1.hour.to_i
|
|
38
|
+
assert_equal 86_400, 1.day.to_i
|
|
39
|
+
assert_equal 604_800, 1.week.to_i
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_numeric_duration_helpers_plural_seconds_and_minutes
|
|
43
|
+
assert_equal 2, 2.seconds.to_i
|
|
44
|
+
assert_equal 120, 2.minutes.to_i
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def test_numeric_duration_helpers_plural_hours_days_weeks
|
|
48
|
+
assert_equal 7200, 2.hours.to_i
|
|
49
|
+
assert_equal 172_800, 2.days.to_i
|
|
50
|
+
assert_equal 1_209_600, 2.weeks.to_i
|
|
51
|
+
|
|
52
|
+
error = assert_raises(ArgumentError) { 1.5.hours }
|
|
53
|
+
assert_includes error.message, "Integer receiver"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_job_command_validates_command
|
|
57
|
+
command = Wheneverd::Job::Command.new(command: " echo hello ")
|
|
58
|
+
assert_equal "echo hello", command.command
|
|
59
|
+
|
|
60
|
+
assert_raises(Wheneverd::InvalidCommandError) { Wheneverd::Job::Command.new(command: "") }
|
|
61
|
+
assert_raises(Wheneverd::InvalidCommandError) { Wheneverd::Job::Command.new(command: " ") }
|
|
62
|
+
|
|
63
|
+
error = assert_raises(Wheneverd::InvalidCommandError) { Wheneverd::Job::Command.new(command: 123) }
|
|
64
|
+
assert_includes error.message, "String"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def test_triggers_render_timer_lines
|
|
68
|
+
interval = Wheneverd::Trigger::Interval.new(seconds: 60)
|
|
69
|
+
assert_equal ["OnActiveSec=60", "OnUnitActiveSec=60"], interval.systemd_timer_lines
|
|
70
|
+
|
|
71
|
+
boot = Wheneverd::Trigger::Boot.new(seconds: 5)
|
|
72
|
+
assert_equal ["OnBootSec=5"], boot.systemd_timer_lines
|
|
73
|
+
|
|
74
|
+
calendar = Wheneverd::Trigger::Calendar.new(on_calendar: %w[hourly daily])
|
|
75
|
+
assert_equal ["OnCalendar=hourly", "OnCalendar=daily"], calendar.systemd_timer_lines
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def test_trigger_validations
|
|
79
|
+
assert_raises(ArgumentError) { Wheneverd::Trigger::Interval.new(seconds: "60") }
|
|
80
|
+
assert_raises(ArgumentError) { Wheneverd::Trigger::Interval.new(seconds: 0) }
|
|
81
|
+
|
|
82
|
+
assert_raises(ArgumentError) { Wheneverd::Trigger::Boot.new(seconds: "1") }
|
|
83
|
+
assert_raises(ArgumentError) { Wheneverd::Trigger::Boot.new(seconds: 0) }
|
|
84
|
+
|
|
85
|
+
assert_raises(ArgumentError) { Wheneverd::Trigger::Calendar.new(on_calendar: []) }
|
|
86
|
+
assert_raises(ArgumentError) { Wheneverd::Trigger::Calendar.new(on_calendar: [" "]) }
|
|
87
|
+
assert_raises(ArgumentError) { Wheneverd::Trigger::Calendar.new(on_calendar: "hourly") }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def test_entry_is_an_ordered_container
|
|
91
|
+
entry = Wheneverd::Entry.new(trigger: Wheneverd::Trigger::Interval.new(seconds: 60))
|
|
92
|
+
assert_equal [], entry.jobs
|
|
93
|
+
entry.add_job(Wheneverd::Job::Command.new(command: "echo hi"))
|
|
94
|
+
assert_equal 1, entry.jobs.length
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def test_schedule_is_an_ordered_container
|
|
98
|
+
schedule = Wheneverd::Schedule.new
|
|
99
|
+
assert_equal [], schedule.entries
|
|
100
|
+
entry = Wheneverd::Entry.new(trigger: Wheneverd::Trigger::Interval.new(seconds: 60))
|
|
101
|
+
schedule.add_entry(entry)
|
|
102
|
+
assert_equal [entry], schedule.entries
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def test_entry_requires_a_trigger
|
|
106
|
+
assert_raises(ArgumentError) { Wheneverd::Entry.new(trigger: nil) }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
|
|
5
|
+
class DSLCalendarSymbolPeriodListTest < Minitest::Test
|
|
6
|
+
def test_validate_raises_when_not_array
|
|
7
|
+
error = assert_raises(Wheneverd::DSL::InvalidPeriodError) do
|
|
8
|
+
Wheneverd::DSL::CalendarSymbolPeriodList.validate(
|
|
9
|
+
"day",
|
|
10
|
+
allowed_symbols: [:day],
|
|
11
|
+
path: "x.rb"
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
assert_includes error.message, "non-empty Array"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_validate_raises_when_empty_array
|
|
18
|
+
error = assert_raises(Wheneverd::DSL::InvalidPeriodError) do
|
|
19
|
+
Wheneverd::DSL::CalendarSymbolPeriodList.validate(
|
|
20
|
+
[],
|
|
21
|
+
allowed_symbols: [:day],
|
|
22
|
+
path: "x.rb"
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
assert_includes error.message, "non-empty Array"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_validate_raises_when_array_contains_non_symbols
|
|
29
|
+
error = assert_raises(Wheneverd::DSL::InvalidPeriodError) do
|
|
30
|
+
Wheneverd::DSL::CalendarSymbolPeriodList.validate([:day, "hour"], allowed_symbols: [:day],
|
|
31
|
+
path: "x.rb")
|
|
32
|
+
end
|
|
33
|
+
assert_includes error.message, "must be Symbols"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_validate_raises_when_symbols_are_unknown
|
|
37
|
+
error = assert_raises(Wheneverd::DSL::InvalidPeriodError) do
|
|
38
|
+
Wheneverd::DSL::CalendarSymbolPeriodList.validate(%i[day nope nope], allowed_symbols: [:day],
|
|
39
|
+
path: "x.rb")
|
|
40
|
+
end
|
|
41
|
+
assert_includes error.message, "Unknown period symbol"
|
|
42
|
+
assert_includes error.message, ":nope"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_validate_returns_periods_when_valid
|
|
46
|
+
result = Wheneverd::DSL::CalendarSymbolPeriodList.validate(
|
|
47
|
+
%i[day day],
|
|
48
|
+
allowed_symbols: [:day],
|
|
49
|
+
path: "x.rb"
|
|
50
|
+
)
|
|
51
|
+
assert_equal %i[day day], result
|
|
52
|
+
end
|
|
53
|
+
end
|