wheneverd 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/FEATURE_SUMMARY.md +10 -1
- data/Gemfile.lock +2 -2
- data/README.md +70 -5
- data/lib/wheneverd/cli/diff.rb +98 -0
- data/lib/wheneverd/cli/init.rb +5 -1
- data/lib/wheneverd/cli/status.rb +37 -0
- data/lib/wheneverd/cli/validate.rb +50 -0
- data/lib/wheneverd/cli.rb +6 -0
- data/lib/wheneverd/dsl/context.rb +51 -8
- data/lib/wheneverd/dsl/period_parser.rb +8 -107
- data/lib/wheneverd/dsl/period_strategy/array_strategy.rb +29 -0
- data/lib/wheneverd/dsl/period_strategy/base.rb +65 -0
- data/lib/wheneverd/dsl/period_strategy/duration_strategy.rb +33 -0
- data/lib/wheneverd/dsl/period_strategy/string_strategy.rb +51 -0
- data/lib/wheneverd/dsl/period_strategy/symbol_strategy.rb +31 -0
- data/lib/wheneverd/dsl/period_strategy.rb +43 -0
- data/lib/wheneverd/duration.rb +1 -7
- data/lib/wheneverd/errors.rb +3 -0
- data/lib/wheneverd/interval.rb +22 -7
- data/lib/wheneverd/job/command.rb +88 -8
- data/lib/wheneverd/systemd/analyze.rb +56 -0
- data/lib/wheneverd/systemd/cron_parser/dow_parser.rb +208 -0
- data/lib/wheneverd/systemd/cron_parser/field_parser.rb +163 -0
- data/lib/wheneverd/systemd/cron_parser.rb +56 -303
- data/lib/wheneverd/systemd/errors.rb +3 -0
- data/lib/wheneverd/systemd/renderer.rb +6 -64
- data/lib/wheneverd/systemd/unit_content_builder.rb +76 -0
- data/lib/wheneverd/systemd/unit_deleter.rb +2 -28
- data/lib/wheneverd/systemd/unit_lister.rb +2 -28
- data/lib/wheneverd/systemd/unit_namer.rb +6 -14
- data/lib/wheneverd/systemd/unit_path_utils.rb +54 -0
- data/lib/wheneverd/systemd/unit_writer.rb +2 -28
- data/lib/wheneverd/trigger/base.rb +22 -0
- data/lib/wheneverd/trigger/boot.rb +8 -6
- data/lib/wheneverd/trigger/calendar.rb +7 -0
- data/lib/wheneverd/trigger/interval.rb +8 -6
- data/lib/wheneverd/validation.rb +89 -0
- data/lib/wheneverd/version.rb +1 -1
- data/lib/wheneverd.rb +5 -1
- data/test/cli_diff_test.rb +118 -0
- data/test/cli_init_test.rb +49 -0
- data/test/cli_status_test.rb +76 -0
- data/test/cli_validate_test.rb +81 -0
- data/test/domain_model_test.rb +106 -1
- data/test/dsl_context_shell_test.rb +23 -0
- data/test/dsl_loader_test.rb +23 -0
- data/test/job_command_test.rb +29 -0
- data/test/systemd_analyze_test.rb +55 -0
- data/test/systemd_cron_parser_test.rb +41 -25
- data/test/systemd_renderer_errors_test.rb +1 -1
- data/test/systemd_renderer_test.rb +6 -0
- metadata +25 -2
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
require_relative "support/cli_test_helpers"
|
|
5
|
+
|
|
6
|
+
class CLIValidateTest < Minitest::Test
|
|
7
|
+
include CLITestHelpers
|
|
8
|
+
|
|
9
|
+
DUP_HOURLY_SCHEDULE = <<~RUBY
|
|
10
|
+
# frozen_string_literal: true
|
|
11
|
+
|
|
12
|
+
every :hour do
|
|
13
|
+
command "echo a"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
every :hour do
|
|
17
|
+
command "echo b"
|
|
18
|
+
end
|
|
19
|
+
RUBY
|
|
20
|
+
|
|
21
|
+
HOURLY_SCHEDULE = <<~RUBY
|
|
22
|
+
# frozen_string_literal: true
|
|
23
|
+
|
|
24
|
+
every :hour do
|
|
25
|
+
command "echo a"
|
|
26
|
+
end
|
|
27
|
+
RUBY
|
|
28
|
+
|
|
29
|
+
def test_validate_runs_systemd_analyze_calendar_for_each_unique_on_calendar
|
|
30
|
+
with_project_dir do
|
|
31
|
+
write_schedule(DUP_HOURLY_SCHEDULE)
|
|
32
|
+
status, out, err, calls = run_validate("--verbose")
|
|
33
|
+
assert_cli_success(status, err)
|
|
34
|
+
assert_includes out, "OK OnCalendar=hourly"
|
|
35
|
+
assert_equal 1, calls.length
|
|
36
|
+
assert_equal %w[systemd-analyze calendar hourly], calls.fetch(0).fetch(0)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_validate_prints_message_when_no_on_calendar_and_verbose
|
|
41
|
+
with_project_dir do
|
|
42
|
+
write_empty_schedule
|
|
43
|
+
status, out, err, calls = run_validate("--verbose")
|
|
44
|
+
assert_cli_success(status, err)
|
|
45
|
+
assert_includes out, "No OnCalendar= values found"
|
|
46
|
+
assert_equal [], calls
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_validate_verify_runs_systemd_analyze_verify
|
|
51
|
+
with_project_dir do
|
|
52
|
+
write_schedule(HOURLY_SCHEDULE)
|
|
53
|
+
status, out, err, calls = run_validate("--verify", "--verbose")
|
|
54
|
+
assert_cli_success(status, err)
|
|
55
|
+
assert_includes out, "OK systemd-analyze --user verify"
|
|
56
|
+
assert_equal %w[systemd-analyze --user verify], calls.fetch(1).fetch(0).take(3)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def test_validate_returns_nonzero_when_systemd_analyze_calendar_fails
|
|
61
|
+
with_project_dir do
|
|
62
|
+
write_schedule(HOURLY_SCHEDULE)
|
|
63
|
+
status, _out, err, calls = run_validate(exitstatus: 1, stderr: "boom\n")
|
|
64
|
+
assert_equal 1, status
|
|
65
|
+
assert_includes err, "systemd-analyze failed"
|
|
66
|
+
assert_includes err, "boom"
|
|
67
|
+
assert_equal %w[systemd-analyze calendar hourly], calls.fetch(0).fetch(0)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def run_validate(*args, **kwargs)
|
|
74
|
+
run_cli_with_capture3_stub(["validate", *args], **kwargs)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def write_schedule(contents)
|
|
78
|
+
FileUtils.mkdir_p("config")
|
|
79
|
+
File.write(File.join("config", "schedule.rb"), contents)
|
|
80
|
+
end
|
|
81
|
+
end
|
data/test/domain_model_test.rb
CHANGED
|
@@ -61,7 +61,7 @@ class DomainModelTest < Minitest::Test
|
|
|
61
61
|
assert_raises(Wheneverd::InvalidCommandError) { Wheneverd::Job::Command.new(command: " ") }
|
|
62
62
|
|
|
63
63
|
error = assert_raises(Wheneverd::InvalidCommandError) { Wheneverd::Job::Command.new(command: 123) }
|
|
64
|
-
assert_includes error.message, "String"
|
|
64
|
+
assert_includes error.message, "String or an Array"
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def test_triggers_render_timer_lines
|
|
@@ -105,4 +105,109 @@ class DomainModelTest < Minitest::Test
|
|
|
105
105
|
def test_entry_requires_a_trigger
|
|
106
106
|
assert_raises(ArgumentError) { Wheneverd::Entry.new(trigger: nil) }
|
|
107
107
|
end
|
|
108
|
+
|
|
109
|
+
def test_trigger_base_module_raises_not_implemented
|
|
110
|
+
# Create a class that includes Base but doesn't implement the methods
|
|
111
|
+
test_trigger_class = Class.new do
|
|
112
|
+
include Wheneverd::Trigger::Base
|
|
113
|
+
end
|
|
114
|
+
trigger = test_trigger_class.new
|
|
115
|
+
|
|
116
|
+
error = assert_raises(NotImplementedError) { trigger.systemd_timer_lines }
|
|
117
|
+
assert_includes error.message, "must implement #systemd_timer_lines"
|
|
118
|
+
|
|
119
|
+
error = assert_raises(NotImplementedError) { trigger.signature }
|
|
120
|
+
assert_includes error.message, "must implement #signature"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def test_trigger_signatures
|
|
124
|
+
interval = Wheneverd::Trigger::Interval.new(seconds: 60)
|
|
125
|
+
assert_equal "interval:60", interval.signature
|
|
126
|
+
|
|
127
|
+
boot = Wheneverd::Trigger::Boot.new(seconds: 5)
|
|
128
|
+
assert_equal "boot:5", boot.signature
|
|
129
|
+
|
|
130
|
+
calendar = Wheneverd::Trigger::Calendar.new(on_calendar: %w[daily hourly])
|
|
131
|
+
assert_equal "calendar:daily|hourly", calendar.signature
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def test_period_strategy_base_raises_not_implemented
|
|
135
|
+
# Create a class that inherits from Base but doesn't implement the methods
|
|
136
|
+
test_strategy_class = Class.new(Wheneverd::DSL::PeriodStrategy::Base)
|
|
137
|
+
strategy = test_strategy_class.new(path: "test")
|
|
138
|
+
|
|
139
|
+
error = assert_raises(NotImplementedError) { strategy.handles?(:anything) }
|
|
140
|
+
assert_includes error.message, "must implement #handles?"
|
|
141
|
+
|
|
142
|
+
error = assert_raises(NotImplementedError) { strategy.parse(:anything, at_times: []) }
|
|
143
|
+
assert_includes error.message, "must implement #parse"
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def test_validation_type
|
|
147
|
+
# Valid type
|
|
148
|
+
assert_equal 42, Wheneverd::Validation.type(42, Integer, name: "value")
|
|
149
|
+
|
|
150
|
+
# Invalid type
|
|
151
|
+
error = assert_raises(ArgumentError) do
|
|
152
|
+
Wheneverd::Validation.type("string", Integer, name: "value")
|
|
153
|
+
end
|
|
154
|
+
assert_includes error.message, "value must be a Integer"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def test_validation_positive_integer
|
|
158
|
+
# Valid positive integer
|
|
159
|
+
assert_equal 5, Wheneverd::Validation.positive_integer(5, name: "count")
|
|
160
|
+
|
|
161
|
+
# Non-positive
|
|
162
|
+
error = assert_raises(ArgumentError) do
|
|
163
|
+
Wheneverd::Validation.positive_integer(0, name: "count")
|
|
164
|
+
end
|
|
165
|
+
assert_includes error.message, "must be positive"
|
|
166
|
+
|
|
167
|
+
# Non-integer
|
|
168
|
+
error = assert_raises(ArgumentError) do
|
|
169
|
+
Wheneverd::Validation.positive_integer("5", name: "count")
|
|
170
|
+
end
|
|
171
|
+
assert_includes error.message, "must be a Integer"
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def test_validation_non_empty_string
|
|
175
|
+
# Valid non-empty string
|
|
176
|
+
assert_equal "hello", Wheneverd::Validation.non_empty_string(" hello ", name: "text")
|
|
177
|
+
|
|
178
|
+
# Empty string
|
|
179
|
+
error = assert_raises(ArgumentError) do
|
|
180
|
+
Wheneverd::Validation.non_empty_string(" ", name: "text")
|
|
181
|
+
end
|
|
182
|
+
assert_includes error.message, "must not be empty"
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def test_validation_non_empty_array
|
|
186
|
+
# Valid non-empty array
|
|
187
|
+
arr = [1, 2, 3]
|
|
188
|
+
assert_equal arr, Wheneverd::Validation.non_empty_array(arr, name: "items")
|
|
189
|
+
|
|
190
|
+
# Empty array
|
|
191
|
+
error = assert_raises(ArgumentError) do
|
|
192
|
+
Wheneverd::Validation.non_empty_array([], name: "items")
|
|
193
|
+
end
|
|
194
|
+
assert_includes error.message, "must not be empty"
|
|
195
|
+
|
|
196
|
+
# Non-array
|
|
197
|
+
error = assert_raises(ArgumentError) do
|
|
198
|
+
Wheneverd::Validation.non_empty_array("not an array", name: "items")
|
|
199
|
+
end
|
|
200
|
+
assert_includes error.message, "must be a Array"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def test_validation_in_range
|
|
204
|
+
# Valid in range
|
|
205
|
+
assert_equal 5, Wheneverd::Validation.in_range(5, 1..10, name: "value")
|
|
206
|
+
|
|
207
|
+
# Out of range
|
|
208
|
+
error = assert_raises(ArgumentError) do
|
|
209
|
+
Wheneverd::Validation.in_range(15, 1..10, name: "value")
|
|
210
|
+
end
|
|
211
|
+
assert_includes error.message, "must be in 1..10"
|
|
212
|
+
end
|
|
108
213
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
|
|
5
|
+
class DSLContextShellTest < Minitest::Test
|
|
6
|
+
def test_shell_requires_every_block
|
|
7
|
+
ctx = Wheneverd::DSL::Context.new(path: "/tmp/config/schedule.rb")
|
|
8
|
+
error = assert_raises(Wheneverd::DSL::LoadError) { ctx.shell("echo hi") }
|
|
9
|
+
assert_includes error.message, "shell() must be called inside every() block"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_shell_requires_string_script
|
|
13
|
+
ctx = Wheneverd::DSL::Context.new(path: "/tmp/config/schedule.rb")
|
|
14
|
+
error = assert_raises(Wheneverd::DSL::LoadError) { ctx.every("1m") { shell(123) } }
|
|
15
|
+
assert_includes error.message, "shell() script must be a String"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_shell_rejects_empty_script
|
|
19
|
+
ctx = Wheneverd::DSL::Context.new(path: "/tmp/config/schedule.rb")
|
|
20
|
+
error = assert_raises(Wheneverd::DSL::LoadError) { ctx.every("1m") { shell(" ") } }
|
|
21
|
+
assert_includes error.message, "shell() script must not be empty"
|
|
22
|
+
end
|
|
23
|
+
end
|
data/test/dsl_loader_test.rb
CHANGED
|
@@ -45,6 +45,29 @@ class DSLLoaderIntervalAndDurationTest < Minitest::Test
|
|
|
45
45
|
assert_equal ["echo hello"], entry.jobs.map(&:command)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
def test_loads_argv_command
|
|
49
|
+
schedule = load_schedule(<<~RUBY)
|
|
50
|
+
every "5m" do
|
|
51
|
+
command ["echo", "hello world"]
|
|
52
|
+
end
|
|
53
|
+
RUBY
|
|
54
|
+
|
|
55
|
+
job = schedule.entries.fetch(0).jobs.fetch(0)
|
|
56
|
+
assert_equal ["echo", "hello world"], job.argv
|
|
57
|
+
assert_equal "echo \"hello world\"", job.command
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def test_loads_shell_helper
|
|
61
|
+
schedule = load_schedule(<<~RUBY)
|
|
62
|
+
every "5m" do
|
|
63
|
+
shell "echo hello | sed -e s/hello/hi/"
|
|
64
|
+
end
|
|
65
|
+
RUBY
|
|
66
|
+
|
|
67
|
+
job = schedule.entries.fetch(0).jobs.fetch(0)
|
|
68
|
+
assert_equal ["/bin/bash", "-lc", "echo hello | sed -e s/hello/hi/"], job.argv
|
|
69
|
+
end
|
|
70
|
+
|
|
48
71
|
def test_loads_duration_with_at_as_calendar
|
|
49
72
|
schedule = load_schedule(<<~RUBY)
|
|
50
73
|
every 1.day, at: "4:30 am" do
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
|
|
5
|
+
class JobCommandTest < Minitest::Test
|
|
6
|
+
def test_accepts_argv_and_formats_execstart
|
|
7
|
+
command = Wheneverd::Job::Command.new(command: ["echo", "hello world"])
|
|
8
|
+
assert_equal ["echo", "hello world"], command.argv
|
|
9
|
+
assert_equal "echo \"hello world\"", command.command
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_accepts_empty_argv_argument
|
|
13
|
+
command = Wheneverd::Job::Command.new(command: ["printf", "%s", ""])
|
|
14
|
+
assert_equal "printf %s \"\"", command.command
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_rejects_invalid_argv
|
|
18
|
+
assert_raises(Wheneverd::InvalidCommandError) { Wheneverd::Job::Command.new(command: []) }
|
|
19
|
+
assert_raises(Wheneverd::InvalidCommandError) { Wheneverd::Job::Command.new(command: [" "]) }
|
|
20
|
+
assert_raises(Wheneverd::InvalidCommandError) { Wheneverd::Job::Command.new(command: ["echo", 1]) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_rejects_argv_with_newlines
|
|
24
|
+
error = assert_raises(Wheneverd::InvalidCommandError) do
|
|
25
|
+
Wheneverd::Job::Command.new(command: %W[echo hi\nthere])
|
|
26
|
+
end
|
|
27
|
+
assert_includes error.message, "must not include NUL or newlines"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
|
|
5
|
+
class SystemdAnalyzeTest < Minitest::Test
|
|
6
|
+
def with_capture3_stub(exitstatus:, stdout: "", stderr: "")
|
|
7
|
+
calls = []
|
|
8
|
+
Thread.current[:open3_capture3_stub] = {
|
|
9
|
+
calls: calls,
|
|
10
|
+
stdout: stdout,
|
|
11
|
+
stderr: stderr,
|
|
12
|
+
exitstatus: exitstatus
|
|
13
|
+
}
|
|
14
|
+
yield calls
|
|
15
|
+
ensure
|
|
16
|
+
Thread.current[:open3_capture3_stub] = nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_calendar_builds_command_and_returns_output
|
|
20
|
+
with_capture3_stub(exitstatus: 0, stdout: "ok\n", stderr: "") do |calls|
|
|
21
|
+
out, err = Wheneverd::Systemd::Analyze.calendar("hourly")
|
|
22
|
+
assert_equal "ok\n", out
|
|
23
|
+
assert_equal "", err
|
|
24
|
+
assert_equal [%w[systemd-analyze calendar hourly], {}], calls.fetch(0)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_verify_builds_user_verify_command
|
|
29
|
+
with_capture3_stub(exitstatus: 0) do |calls|
|
|
30
|
+
Wheneverd::Systemd::Analyze.verify(["/tmp/a.timer", "/tmp/a.service"], user: true)
|
|
31
|
+
assert_equal(
|
|
32
|
+
[["systemd-analyze", "--user", "verify", "/tmp/a.timer", "/tmp/a.service"], {}],
|
|
33
|
+
calls.fetch(0)
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_calendar_raises_systemd_analyze_error_on_failure_with_details
|
|
39
|
+
with_capture3_stub(exitstatus: 1, stdout: "oops\n", stderr: "nope\n") do
|
|
40
|
+
error = assert_raises(Wheneverd::Systemd::SystemdAnalyzeError) do
|
|
41
|
+
Wheneverd::Systemd::Analyze.calendar("hourly")
|
|
42
|
+
end
|
|
43
|
+
assert_includes error.message, "status: 1"
|
|
44
|
+
assert_includes error.message, "stdout: oops"
|
|
45
|
+
assert_includes error.message, "stderr: nope"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def test_calendar_raises_systemd_analyze_error_when_missing
|
|
50
|
+
error = assert_raises(Wheneverd::Systemd::SystemdAnalyzeError) do
|
|
51
|
+
Wheneverd::Systemd::Analyze.calendar("hourly", systemd_analyze: "/no/such/systemd-analyze")
|
|
52
|
+
end
|
|
53
|
+
assert_includes error.message, "systemd-analyze not found"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -38,28 +38,51 @@ class SystemdCronParserTest < Minitest::Test
|
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
def test_field_parser_rejects_empty_numeric_field
|
|
42
|
+
assert_raises(Wheneverd::Systemd::UnsupportedCronError) do
|
|
43
|
+
Wheneverd::Systemd::CronParser::FieldParser.parse_mapped(
|
|
44
|
+
"",
|
|
45
|
+
0..59,
|
|
46
|
+
field: "minute",
|
|
47
|
+
input: "x",
|
|
48
|
+
names: {}
|
|
49
|
+
)
|
|
50
|
+
end
|
|
50
51
|
end
|
|
51
52
|
|
|
52
|
-
def
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
def test_field_parser_rejects_invalid_numeric_tokens
|
|
54
|
+
# Empty token (just whitespace)
|
|
55
|
+
assert_raises(Wheneverd::Systemd::UnsupportedCronError) do
|
|
56
|
+
Wheneverd::Systemd::CronParser::FieldParser.parse_mapped(
|
|
57
|
+
" ",
|
|
58
|
+
0..59,
|
|
59
|
+
field: "minute",
|
|
60
|
+
input: "x",
|
|
61
|
+
names: {}
|
|
62
|
+
)
|
|
63
|
+
end
|
|
56
64
|
end
|
|
57
65
|
|
|
58
|
-
def
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
def test_dow_parser_rejects_empty_and_invalid_tokens
|
|
67
|
+
# Empty day-of-week field
|
|
68
|
+
assert_raises(Wheneverd::Systemd::UnsupportedCronError) do
|
|
69
|
+
Wheneverd::Systemd::CronParser::DowParser.parse("", input: "x")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Out of range day-of-week value (8)
|
|
73
|
+
assert_raises(Wheneverd::Systemd::UnsupportedCronError) do
|
|
74
|
+
Wheneverd::Systemd::CronParser::DowParser.parse("8", input: "x")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Invalid step (non-numeric)
|
|
78
|
+
assert_raises(Wheneverd::Systemd::UnsupportedCronError) do
|
|
79
|
+
Wheneverd::Systemd::CronParser::DowParser.parse("*/x", input: "x")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Invalid step (zero)
|
|
83
|
+
assert_raises(Wheneverd::Systemd::UnsupportedCronError) do
|
|
84
|
+
Wheneverd::Systemd::CronParser::DowParser.parse("*/0", input: "x")
|
|
85
|
+
end
|
|
63
86
|
end
|
|
64
87
|
|
|
65
88
|
def test_rejects_unsupported_cron_patterns
|
|
@@ -97,13 +120,6 @@ class SystemdCronParserTest < Minitest::Test
|
|
|
97
120
|
assert_equal expected, Wheneverd::Systemd::CronParser.to_on_calendar_values(cron)
|
|
98
121
|
end
|
|
99
122
|
|
|
100
|
-
def assert_private_unsupported(method, *args, **kwargs)
|
|
101
|
-
parser = Wheneverd::Systemd::CronParser
|
|
102
|
-
assert_raises(Wheneverd::Systemd::UnsupportedCronError) do
|
|
103
|
-
parser.send(method, *args, **kwargs)
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
123
|
def assert_unsupported(*crons)
|
|
108
124
|
crons.each do |cron|
|
|
109
125
|
assert_raises(Wheneverd::Systemd::UnsupportedCronError) do
|
|
@@ -44,7 +44,7 @@ class SystemdRendererErrorsTest < Minitest::Test
|
|
|
44
44
|
|
|
45
45
|
def test_timer_lines_for_rejects_unknown_trigger
|
|
46
46
|
assert_raises(ArgumentError) do
|
|
47
|
-
Wheneverd::Systemd::
|
|
47
|
+
Wheneverd::Systemd::UnitContentBuilder.timer_lines_for(Object.new)
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
50
|
|
|
@@ -62,6 +62,12 @@ class SystemdRendererIntervalTest < Minitest::Test
|
|
|
62
62
|
assert_includes service.contents, "Type=oneshot"
|
|
63
63
|
assert_includes service.contents, "ExecStart=echo hello"
|
|
64
64
|
end
|
|
65
|
+
|
|
66
|
+
def test_interval_service_contains_execstart_for_argv
|
|
67
|
+
entry = interval_entry(seconds: 60, command: ["echo", "hello world"])
|
|
68
|
+
service = service_for(entry)
|
|
69
|
+
assert_includes service.contents, "ExecStart=echo \"hello world\""
|
|
70
|
+
end
|
|
65
71
|
end
|
|
66
72
|
|
|
67
73
|
class SystemdRendererCalendarTest < Minitest::Test
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: wheneverd
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- bigcurl
|
|
@@ -50,11 +50,14 @@ files:
|
|
|
50
50
|
- lib/wheneverd/cli/current.rb
|
|
51
51
|
- lib/wheneverd/cli/deactivate.rb
|
|
52
52
|
- lib/wheneverd/cli/delete.rb
|
|
53
|
+
- lib/wheneverd/cli/diff.rb
|
|
53
54
|
- lib/wheneverd/cli/help.rb
|
|
54
55
|
- lib/wheneverd/cli/init.rb
|
|
55
56
|
- lib/wheneverd/cli/linger.rb
|
|
56
57
|
- lib/wheneverd/cli/reload.rb
|
|
57
58
|
- lib/wheneverd/cli/show.rb
|
|
59
|
+
- lib/wheneverd/cli/status.rb
|
|
60
|
+
- lib/wheneverd/cli/validate.rb
|
|
58
61
|
- lib/wheneverd/cli/write.rb
|
|
59
62
|
- lib/wheneverd/core_ext/numeric_duration.rb
|
|
60
63
|
- lib/wheneverd/dsl/at_normalizer.rb
|
|
@@ -63,41 +66,61 @@ files:
|
|
|
63
66
|
- lib/wheneverd/dsl/errors.rb
|
|
64
67
|
- lib/wheneverd/dsl/loader.rb
|
|
65
68
|
- lib/wheneverd/dsl/period_parser.rb
|
|
69
|
+
- lib/wheneverd/dsl/period_strategy.rb
|
|
70
|
+
- lib/wheneverd/dsl/period_strategy/array_strategy.rb
|
|
71
|
+
- lib/wheneverd/dsl/period_strategy/base.rb
|
|
72
|
+
- lib/wheneverd/dsl/period_strategy/duration_strategy.rb
|
|
73
|
+
- lib/wheneverd/dsl/period_strategy/string_strategy.rb
|
|
74
|
+
- lib/wheneverd/dsl/period_strategy/symbol_strategy.rb
|
|
66
75
|
- lib/wheneverd/duration.rb
|
|
67
76
|
- lib/wheneverd/entry.rb
|
|
68
77
|
- lib/wheneverd/errors.rb
|
|
69
78
|
- lib/wheneverd/interval.rb
|
|
70
79
|
- lib/wheneverd/job/command.rb
|
|
71
80
|
- lib/wheneverd/schedule.rb
|
|
81
|
+
- lib/wheneverd/systemd/analyze.rb
|
|
72
82
|
- lib/wheneverd/systemd/calendar_spec.rb
|
|
73
83
|
- lib/wheneverd/systemd/cron_parser.rb
|
|
84
|
+
- lib/wheneverd/systemd/cron_parser/dow_parser.rb
|
|
85
|
+
- lib/wheneverd/systemd/cron_parser/field_parser.rb
|
|
74
86
|
- lib/wheneverd/systemd/errors.rb
|
|
75
87
|
- lib/wheneverd/systemd/loginctl.rb
|
|
76
88
|
- lib/wheneverd/systemd/renderer.rb
|
|
77
89
|
- lib/wheneverd/systemd/systemctl.rb
|
|
78
90
|
- lib/wheneverd/systemd/time_parser.rb
|
|
91
|
+
- lib/wheneverd/systemd/unit_content_builder.rb
|
|
79
92
|
- lib/wheneverd/systemd/unit_deleter.rb
|
|
80
93
|
- lib/wheneverd/systemd/unit_lister.rb
|
|
81
94
|
- lib/wheneverd/systemd/unit_namer.rb
|
|
95
|
+
- lib/wheneverd/systemd/unit_path_utils.rb
|
|
82
96
|
- lib/wheneverd/systemd/unit_writer.rb
|
|
97
|
+
- lib/wheneverd/trigger/base.rb
|
|
83
98
|
- lib/wheneverd/trigger/boot.rb
|
|
84
99
|
- lib/wheneverd/trigger/calendar.rb
|
|
85
100
|
- lib/wheneverd/trigger/interval.rb
|
|
101
|
+
- lib/wheneverd/validation.rb
|
|
86
102
|
- lib/wheneverd/version.rb
|
|
87
103
|
- test/cli_activate_test.rb
|
|
88
104
|
- test/cli_current_test.rb
|
|
89
105
|
- test/cli_deactivate_test.rb
|
|
106
|
+
- test/cli_diff_test.rb
|
|
90
107
|
- test/cli_end_to_end_test.rb
|
|
108
|
+
- test/cli_init_test.rb
|
|
91
109
|
- test/cli_linger_test.rb
|
|
92
110
|
- test/cli_reload_test.rb
|
|
111
|
+
- test/cli_status_test.rb
|
|
93
112
|
- test/cli_systemctl_integration_test.rb
|
|
94
113
|
- test/cli_systemd_analyze_test.rb
|
|
95
114
|
- test/cli_test.rb
|
|
115
|
+
- test/cli_validate_test.rb
|
|
96
116
|
- test/domain_model_test.rb
|
|
97
117
|
- test/dsl_calendar_symbol_period_list_test.rb
|
|
118
|
+
- test/dsl_context_shell_test.rb
|
|
98
119
|
- test/dsl_loader_test.rb
|
|
120
|
+
- test/job_command_test.rb
|
|
99
121
|
- test/support/cli_subprocess_test_helpers.rb
|
|
100
122
|
- test/support/cli_test_helpers.rb
|
|
123
|
+
- test/systemd_analyze_test.rb
|
|
101
124
|
- test/systemd_calendar_spec_test.rb
|
|
102
125
|
- test/systemd_cron_parser_test.rb
|
|
103
126
|
- test/systemd_renderer_errors_test.rb
|
|
@@ -131,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
131
154
|
- !ruby/object:Gem::Version
|
|
132
155
|
version: '0'
|
|
133
156
|
requirements: []
|
|
134
|
-
rubygems_version: 4.0.
|
|
157
|
+
rubygems_version: 4.0.4
|
|
135
158
|
specification_version: 4
|
|
136
159
|
summary: Wheneverd is to systemd timers what whenever is to cron.
|
|
137
160
|
test_files: []
|