service_spec.rb 12.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# typed: false
# frozen_string_literal: true

require "formula"
require "service"

describe Homebrew::Service do
  let(:klass) do
    Class.new(Formula) do
      url "https://brew.sh/test-1.0.tbz"
    end
  end
  let(:name) { "formula_name" }
  let(:path) { Formulary.core_path(name) }
  let(:spec) { :stable }
  let(:f) { klass.new(name, path, spec) }

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  describe "#std_service_path_env" do
    it "returns valid std_service_path_env" do
      f.class.service do
        run opt_bin/"beanstalkd"
        run_type :immediate
        environment_variables PATH: std_service_path_env
        error_log_path var/"log/beanstalkd.error.log"
        log_path var/"log/beanstalkd.log"
        working_dir var
        keep_alive true
      end

      path = f.service.std_service_path_env
      expect(path).to eq("#{HOMEBREW_PREFIX}/bin:#{HOMEBREW_PREFIX}/sbin:/usr/bin:/bin:/usr/sbin:/sbin")
    end
  end

35
36
  describe "#process_type" do
    it "throws for unexpected type" do
Sean Molenaar's avatar
Sean Molenaar committed
37
38
      f.class.service do
        run opt_bin/"beanstalkd"
39
        process_type :cow
Sean Molenaar's avatar
Sean Molenaar committed
40
41
      end

42
43
44
      expect {
        f.service.manual_command
      }.to raise_error TypeError, "Service#process_type allows: 'background'/'standard'/'interactive'/'adaptive'"
Sean Molenaar's avatar
Sean Molenaar committed
45
    end
46
  end
Sean Molenaar's avatar
Sean Molenaar committed
47

48
  describe "#run_type" do
Sean Molenaar's avatar
Sean Molenaar committed
49
50
51
52
53
54
55
56
57
58
59
60
    it "throws for unexpected type" do
      f.class.service do
        run opt_bin/"beanstalkd"
        run_type :cow
      end

      expect {
        f.service.manual_command
      }.to raise_error TypeError, "Service#run_type allows: 'immediate'/'interval'/'cron'"
    end
  end

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
  describe "#manual_command" do
    it "returns valid manual_command" do
      f.class.service do
        run "#{HOMEBREW_PREFIX}/bin/beanstalkd"
        run_type :immediate
        environment_variables PATH: std_service_path_env, ETC_DIR: etc/"beanstalkd"
        error_log_path var/"log/beanstalkd.error.log"
        log_path var/"log/beanstalkd.log"
        working_dir var
        keep_alive true
      end

      path = f.service.manual_command
      expect(path).to eq("ETC_DIR=\"#{HOMEBREW_PREFIX}/etc/beanstalkd\" #{HOMEBREW_PREFIX}/bin/beanstalkd")
    end

    it "returns valid manual_command without variables" do
      f.class.service do
        run opt_bin/"beanstalkd"
        run_type :immediate
        environment_variables PATH: std_service_path_env
        error_log_path var/"log/beanstalkd.error.log"
        log_path var/"log/beanstalkd.log"
        working_dir var
        keep_alive true
      end

      path = f.service.manual_command
      expect(path).to eq("#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd")
    end
  end

93
94
  describe "#to_plist" do
    it "returns valid plist" do
Sean Molenaar's avatar
Sean Molenaar committed
95
      f.class.service do
96
        run [opt_bin/"beanstalkd", "test"]
97
        run_type :immediate
98
        environment_variables PATH: std_service_path_env, FOO: "BAR", ETC_DIR: etc/"beanstalkd"
99
100
        error_log_path var/"log/beanstalkd.error.log"
        log_path var/"log/beanstalkd.log"
101
102
        input_path var/"in/beanstalkd"
        root_dir var
103
104
        working_dir var
        keep_alive true
Sean Molenaar's avatar
Sean Molenaar committed
105
        process_type :interactive
106
        restart_delay 30
Sean Molenaar's avatar
Sean Molenaar committed
107
        interval 5
108
        macos_legacy_timers true
109
110
      end

Sean Molenaar's avatar
Sean Molenaar committed
111
      plist = f.service.to_plist
112
113
114
115
116
117
118
      plist_expect = <<~EOS
        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
        <plist version="1.0">
        <dict>
        \t<key>EnvironmentVariables</key>
        \t<dict>
119
120
        \t\t<key>ETC_DIR</key>
        \t\t<string>#{HOMEBREW_PREFIX}/etc/beanstalkd</string>
121
122
        \t\t<key>FOO</key>
        \t\t<string>BAR</string>
123
124
125
126
127
128
129
        \t\t<key>PATH</key>
        \t\t<string>#{HOMEBREW_PREFIX}/bin:#{HOMEBREW_PREFIX}/sbin:/usr/bin:/bin:/usr/sbin:/sbin</string>
        \t</dict>
        \t<key>KeepAlive</key>
        \t<true/>
        \t<key>Label</key>
        \t<string>homebrew.mxcl.formula_name</string>
130
131
        \t<key>LegacyTimers</key>
        \t<true/>
valrus's avatar
valrus committed
132
133
        \t<key>ProcessType</key>
        \t<string>Interactive</string>
134
135
136
137
138
        \t<key>ProgramArguments</key>
        \t<array>
        \t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string>
        \t\t<string>test</string>
        \t</array>
139
140
        \t<key>RootDirectory</key>
        \t<string>#{HOMEBREW_PREFIX}/var</string>
141
142
143
144
        \t<key>RunAtLoad</key>
        \t<true/>
        \t<key>StandardErrorPath</key>
        \t<string>#{HOMEBREW_PREFIX}/var/log/beanstalkd.error.log</string>
145
146
        \t<key>StandardInPath</key>
        \t<string>#{HOMEBREW_PREFIX}/var/in/beanstalkd</string>
147
148
        \t<key>StandardOutPath</key>
        \t<string>#{HOMEBREW_PREFIX}/var/log/beanstalkd.log</string>
149
150
        \t<key>TimeOut</key>
        \t<integer>30</integer>
151
152
153
154
155
156
        \t<key>WorkingDirectory</key>
        \t<string>#{HOMEBREW_PREFIX}/var</string>
        </dict>
        </plist>
      EOS
      expect(plist).to eq(plist_expect)
157
158
159
    end

    it "returns valid partial plist" do
Sean Molenaar's avatar
Sean Molenaar committed
160
      f.class.service do
161
        run opt_bin/"beanstalkd"
162
163
164
        run_type :immediate
      end

Sean Molenaar's avatar
Sean Molenaar committed
165
      plist = f.service.to_plist
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
      plist_expect = <<~EOS
        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
        <plist version="1.0">
        <dict>
        \t<key>Label</key>
        \t<string>homebrew.mxcl.formula_name</string>
        \t<key>ProgramArguments</key>
        \t<array>
        \t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string>
        \t</array>
        \t<key>RunAtLoad</key>
        \t<true/>
        </dict>
        </plist>
      EOS
      expect(plist).to eq(plist_expect)
183
    end
Sean Molenaar's avatar
Sean Molenaar committed
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212

    it "returns valid interval plist" do
      f.class.service do
        run opt_bin/"beanstalkd"
        run_type :interval
        interval 5
      end

      plist = f.service.to_plist
      plist_expect = <<~EOS
        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
        <plist version="1.0">
        <dict>
        \t<key>Label</key>
        \t<string>homebrew.mxcl.formula_name</string>
        \t<key>ProgramArguments</key>
        \t<array>
        \t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string>
        \t</array>
        \t<key>RunAtLoad</key>
        \t<false/>
        \t<key>StartInterval</key>
        \t<integer>5</integer>
        </dict>
        </plist>
      EOS
      expect(plist).to eq(plist_expect)
    end
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

    it "returns valid cron plist" do
      f.class.service do
        run opt_bin/"beanstalkd"
        run_type :cron
        cron "@daily"
      end

      plist = f.service.to_plist
      plist_expect = <<~EOS
        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
        <plist version="1.0">
        <dict>
        \t<key>Label</key>
        \t<string>homebrew.mxcl.formula_name</string>
        \t<key>ProgramArguments</key>
        \t<array>
        \t\t<string>#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd</string>
        \t</array>
        \t<key>RunAtLoad</key>
        \t<false/>
        \t<key>StartCalendarInterval</key>
        \t<dict>
        \t\t<key>Hour</key>
        \t\t<integer>0</integer>
        \t\t<key>Minute</key>
        \t\t<integer>0</integer>
        \t</dict>
        </dict>
        </plist>
      EOS
      expect(plist).to eq(plist_expect)
    end
247
  end
248
249
250
251

  describe "#to_systemd_unit" do
    it "returns valid unit" do
      f.class.service do
252
        run [opt_bin/"beanstalkd", "test"]
253
        run_type :immediate
254
        environment_variables PATH: std_service_path_env, FOO: "BAR"
255
256
        error_log_path var/"log/beanstalkd.error.log"
        log_path var/"log/beanstalkd.log"
257
258
        input_path var/"in/beanstalkd"
        root_dir var
259
260
        working_dir var
        keep_alive true
Sean Molenaar's avatar
Sean Molenaar committed
261
        process_type :interactive
262
263
        restart_delay 30
        macos_legacy_timers true
264
265
266
267
      end

      unit = f.service.to_systemd_unit
      std_path = "#{HOMEBREW_PREFIX}/bin:#{HOMEBREW_PREFIX}/sbin:/usr/bin:/bin:/usr/sbin:/sbin"
268
269
270
271
      unit_expect = <<~EOS
        [Unit]
        Description=Homebrew generated unit for formula_name

272
273
274
        [Install]
        WantedBy=multi-user.target

275
276
277
278
        [Service]
        Type=simple
        ExecStart=#{HOMEBREW_PREFIX}/opt/#{name}/bin/beanstalkd test
        Restart=always
279
        RestartSec=30
280
        WorkingDirectory=#{HOMEBREW_PREFIX}/var
281
282
        RootDirectory=#{HOMEBREW_PREFIX}/var
        StandardInput=file:#{HOMEBREW_PREFIX}/var/in/beanstalkd
283
284
285
        StandardOutput=append:#{HOMEBREW_PREFIX}/var/log/beanstalkd.log
        StandardError=append:#{HOMEBREW_PREFIX}/var/log/beanstalkd.error.log
        Environment=\"PATH=#{std_path}\"
286
        Environment=\"FOO=BAR\"
287
288
      EOS
      expect(unit).to eq(unit_expect.strip)
289
290
291
292
293
294
295
296
297
    end

    it "returns valid partial unit" do
      f.class.service do
        run opt_bin/"beanstalkd"
        run_type :immediate
      end

      unit = f.service.to_systemd_unit
298
299
300
301
      unit_expect = <<~EOS
        [Unit]
        Description=Homebrew generated unit for formula_name

302
303
304
        [Install]
        WantedBy=multi-user.target

305
306
307
308
309
        [Service]
        Type=simple
        ExecStart=#{HOMEBREW_PREFIX}/opt/#{name}/bin/beanstalkd
      EOS
      expect(unit).to eq(unit_expect)
310
311
    end
  end
312

313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
  describe "#to_systemd_timer" do
    it "returns valid timer" do
      f.class.service do
        run [opt_bin/"beanstalkd", "test"]
        run_type :interval
        interval 5
      end

      unit = f.service.to_systemd_timer
      unit_expect = <<~EOS
        [Unit]
        Description=Homebrew generated timer for formula_name

        [Install]
        WantedBy=timers.target

        [Timer]
        Unit=homebrew.formula_name
        OnUnitActiveSec=5
      EOS
      expect(unit).to eq(unit_expect.strip)
    end

    it "returns valid partial timer" do
      f.class.service do
        run opt_bin/"beanstalkd"
        run_type :immediate
      end

      unit = f.service.to_systemd_timer
      unit_expect = <<~EOS
        [Unit]
        Description=Homebrew generated timer for formula_name

        [Install]
        WantedBy=timers.target

        [Timer]
        Unit=homebrew.formula_name
      EOS
      expect(unit).to eq(unit_expect)
    end
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401

    it "throws on incomplete cron" do
      f.class.service do
        run opt_bin/"beanstalkd"
        run_type :cron
        cron "1 2 3 4"
      end

      expect {
        f.service.to_systemd_timer
      }.to raise_error TypeError, "Service#parse_cron expects a valid cron syntax"
    end

    it "returns valid cron timers" do
      styles = {
        "@hourly":   "*-*-*-* *:00:00",
        "@daily":    "*-*-*-* 00:00:00",
        "@weekly":   "0-*-*-* 00:00:00",
        "@monthly":  "*-*-*-1 00:00:00",
        "@yearly":   "*-*-1-1 00:00:00",
        "@annually": "*-*-1-1 00:00:00",
        "5 5 5 5 5": "5-*-5-5 05:05:00",
      }

      styles.each do |cron, calendar|
        f.class.service do
          run opt_bin/"beanstalkd"
          run_type :cron
          cron cron.to_s
        end

        unit = f.service.to_systemd_timer
        unit_expect = <<~EOS
          [Unit]
          Description=Homebrew generated timer for formula_name

          [Install]
          WantedBy=timers.target

          [Timer]
          Unit=homebrew.formula_name
          Persistent=true
          OnCalendar=#{calendar}
        EOS
        expect(unit).to eq(unit_expect.chomp)
      end
    end
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
  end

  describe "#timed?" do
    it "returns false for immediate" do
      f.class.service do
        run [opt_bin/"beanstalkd", "test"]
        run_type :immediate
      end

      expect(f.service.timed?).to eq(false)
    end

    it "returns true for interval" do
      f.class.service do
        run [opt_bin/"beanstalkd", "test"]
        run_type :interval
      end

      expect(f.service.timed?).to eq(true)
    end
  end

424
425
426
  describe "#command" do
    it "returns @run data" do
      f.class.service do
427
        run [opt_bin/"beanstalkd", "test"]
428
429
430
431
        run_type :immediate
      end

      command = f.service.command
432
      expect(command).to eq(["#{HOMEBREW_PREFIX}/opt/#{name}/bin/beanstalkd", "test"])
433
434
    end
  end
435
end