baza.rb 0.0.1
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/.0pdd.yml +25 -0
- data/.gitattributes +7 -0
- data/.github/workflows/actionlint.yml +41 -0
- data/.github/workflows/codecov.yml +39 -0
- data/.github/workflows/copyrights.yml +30 -0
- data/.github/workflows/markdown-lint.yml +38 -0
- data/.github/workflows/pdd.yml +34 -0
- data/.github/workflows/rake.yml +44 -0
- data/.github/workflows/xcop.yml +30 -0
- data/.github/workflows/yamllint.yml +36 -0
- data/.gitignore +8 -0
- data/.pdd +7 -0
- data/.rubocop.yml +64 -0
- data/.rultor.yml +42 -0
- data/.simplecov +41 -0
- data/.yamllint.yml +24 -0
- data/Gemfile +39 -0
- data/Gemfile.lock +255 -0
- data/LICENSE.txt +21 -0
- data/README.md +29 -0
- data/Rakefile +68 -0
- data/baza.rb.gemspec +51 -0
- data/lib/baza/elapsed.rb +34 -0
- data/lib/baza/version.rb +30 -0
- data/lib/baza.rb +332 -0
- data/renovate.json +6 -0
- data/test/test__helper.rb +34 -0
- data/test/test_baza.rb +258 -0
- metadata +200 -0
data/lib/baza.rb
ADDED
@@ -0,0 +1,332 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2024 Zerocracy
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the 'Software'), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
13
|
+
# copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
require 'typhoeus'
|
24
|
+
require 'retries'
|
25
|
+
require 'iri'
|
26
|
+
require 'loog'
|
27
|
+
require 'base64'
|
28
|
+
require_relative 'baza/elapsed'
|
29
|
+
|
30
|
+
# Interface to the API of zerocracy.com.
|
31
|
+
#
|
32
|
+
# You make an instance of this class and then call one of its methods.
|
33
|
+
# The object will make HTTP request to api.zerocracy.com and interpret the
|
34
|
+
# results returned.
|
35
|
+
#
|
36
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
37
|
+
# Copyright:: Copyright (c) 2024 Yegor Bugayenko
|
38
|
+
# License:: MIT
|
39
|
+
class Baza
|
40
|
+
VERSION = '0.0.0'
|
41
|
+
|
42
|
+
def initialize(host, port, token, ssl: true, timeout: 30, retries: 3, loog: Loog::NULL, compression: true)
|
43
|
+
@host = host
|
44
|
+
@port = port
|
45
|
+
@ssl = ssl
|
46
|
+
@token = token
|
47
|
+
@timeout = timeout
|
48
|
+
@loog = loog
|
49
|
+
@retries = retries
|
50
|
+
@compression = compression
|
51
|
+
end
|
52
|
+
|
53
|
+
# Push factbase to the server.
|
54
|
+
# @param [String] name The name of the job on the server
|
55
|
+
# @param [Bytes] data The data to push to the server (binary)
|
56
|
+
# @param [Array<String>] meta List of metas, possibly empty
|
57
|
+
# @return [Integer] Job ID on the server
|
58
|
+
def push(name, data, meta)
|
59
|
+
id = 0
|
60
|
+
hdrs = headers.merge(
|
61
|
+
'Content-Type' => 'application/octet-stream',
|
62
|
+
'Content-Length' => data.size
|
63
|
+
)
|
64
|
+
unless meta.empty?
|
65
|
+
hdrs = hdrs.merge('X-Zerocracy-Meta' => meta.map { |v| Base64.encode64(v).gsub("\n", '') }.join(' '))
|
66
|
+
end
|
67
|
+
params = {
|
68
|
+
connecttimeout: @timeout,
|
69
|
+
timeout: @timeout,
|
70
|
+
body: data,
|
71
|
+
headers: hdrs
|
72
|
+
}
|
73
|
+
elapsed(@loog) do
|
74
|
+
ret =
|
75
|
+
with_retries(max_tries: @retries) do
|
76
|
+
checked(
|
77
|
+
Typhoeus::Request.put(
|
78
|
+
home.append('push').append(name).to_s,
|
79
|
+
@compression ? zipped(params) : params
|
80
|
+
)
|
81
|
+
)
|
82
|
+
end
|
83
|
+
id = ret.body.to_i
|
84
|
+
throw :"Pushed #{data.size} bytes to #{@host}, job ID is ##{id}"
|
85
|
+
end
|
86
|
+
id
|
87
|
+
end
|
88
|
+
|
89
|
+
# Pull factbase from the server.
|
90
|
+
# @param [Integer] id The ID of the job on the server
|
91
|
+
# @return [Bytes] Binary data pulled
|
92
|
+
def pull(id)
|
93
|
+
data = 0
|
94
|
+
elapsed(@loog) do
|
95
|
+
Tempfile.open do |file|
|
96
|
+
File.open(file, 'wb') do |f|
|
97
|
+
request = Typhoeus::Request.new(
|
98
|
+
home.append('pull').append("#{id}.fb").to_s,
|
99
|
+
headers: headers.merge(
|
100
|
+
'Accept' => 'application/octet-stream'
|
101
|
+
),
|
102
|
+
connecttimeout: @timeout,
|
103
|
+
timeout: @timeout
|
104
|
+
)
|
105
|
+
request.on_body do |chunk|
|
106
|
+
f.write(chunk)
|
107
|
+
end
|
108
|
+
with_retries(max_tries: @retries) do
|
109
|
+
request.run
|
110
|
+
end
|
111
|
+
checked(request.response)
|
112
|
+
end
|
113
|
+
data = File.binread(file)
|
114
|
+
throw :"Pulled #{data.size} bytes of job ##{id} factbase at #{@host}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
data
|
118
|
+
end
|
119
|
+
|
120
|
+
# The job with this ID is finished already?
|
121
|
+
# @param [Integer] id The ID of the job on the server
|
122
|
+
# @return [Boolean] TRUE if the job is already finished
|
123
|
+
def finished?(id)
|
124
|
+
finished = false
|
125
|
+
elapsed(@loog) do
|
126
|
+
ret =
|
127
|
+
with_retries(max_tries: @retries) do
|
128
|
+
checked(
|
129
|
+
Typhoeus::Request.get(
|
130
|
+
home.append('finished').append(id).to_s,
|
131
|
+
headers:
|
132
|
+
)
|
133
|
+
)
|
134
|
+
end
|
135
|
+
finished = ret.body == 'yes'
|
136
|
+
throw :"The job ##{id} is #{finished ? '' : 'not yet '}finished at #{@host}"
|
137
|
+
end
|
138
|
+
finished
|
139
|
+
end
|
140
|
+
|
141
|
+
# Read and return the stdout of the job.
|
142
|
+
# @param [Integer] id The ID of the job on the server
|
143
|
+
# @return [String] The stdout, as a text
|
144
|
+
def stdout(id)
|
145
|
+
stdout = ''
|
146
|
+
elapsed(@loog) do
|
147
|
+
ret =
|
148
|
+
with_retries(max_tries: @retries) do
|
149
|
+
checked(
|
150
|
+
Typhoeus::Request.get(
|
151
|
+
home.append('stdout').append("#{id}.txt").to_s,
|
152
|
+
headers:
|
153
|
+
)
|
154
|
+
)
|
155
|
+
end
|
156
|
+
stdout = ret.body
|
157
|
+
throw :"The stdout of the job ##{id} has #{stdout.split("\n")} lines"
|
158
|
+
end
|
159
|
+
stdout
|
160
|
+
end
|
161
|
+
|
162
|
+
# Read and return the exit code of the job.
|
163
|
+
# @param [Integer] id The ID of the job on the server
|
164
|
+
# @return [Integer] The exit code
|
165
|
+
def exit_code(id)
|
166
|
+
code = 0
|
167
|
+
elapsed(@loog) do
|
168
|
+
ret =
|
169
|
+
with_retries(max_tries: @retries) do
|
170
|
+
checked(
|
171
|
+
Typhoeus::Request.get(
|
172
|
+
home.append('exit').append("#{id}.txt").to_s,
|
173
|
+
headers:
|
174
|
+
)
|
175
|
+
)
|
176
|
+
end
|
177
|
+
code = ret.body.to_i
|
178
|
+
throw :"The exit code of the job ##{id} is #{code}"
|
179
|
+
end
|
180
|
+
code
|
181
|
+
end
|
182
|
+
|
183
|
+
# Lock the name.
|
184
|
+
# @param [String] name The name of the job on the server
|
185
|
+
# @param [String] owner The owner of the lock (any string)
|
186
|
+
def lock(name, owner)
|
187
|
+
elapsed(@loog) do
|
188
|
+
with_retries(max_tries: @retries) do
|
189
|
+
checked(
|
190
|
+
Typhoeus::Request.get(
|
191
|
+
home.append('lock').append(name).add(owner:).to_s,
|
192
|
+
headers:
|
193
|
+
),
|
194
|
+
302
|
195
|
+
)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Unlock the name.
|
201
|
+
# @param [String] name The name of the job on the server
|
202
|
+
# @param [String] owner The owner of the lock (any string)
|
203
|
+
def unlock(name, owner)
|
204
|
+
elapsed(@loog) do
|
205
|
+
with_retries(max_tries: @retries) do
|
206
|
+
checked(
|
207
|
+
Typhoeus::Request.get(
|
208
|
+
home.append('unlock').append(name).add(owner:).to_s,
|
209
|
+
headers:
|
210
|
+
),
|
211
|
+
302
|
212
|
+
)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Get the ID of the job by the name.
|
218
|
+
# @param [String] name The name of the job on the server
|
219
|
+
# @return [Integer] The ID of the job on the server
|
220
|
+
def recent(name)
|
221
|
+
job = 0
|
222
|
+
elapsed(@loog) do
|
223
|
+
ret =
|
224
|
+
with_retries(max_tries: @retries) do
|
225
|
+
checked(
|
226
|
+
Typhoeus::Request.get(
|
227
|
+
home.append('recent').append("#{name}.txt").to_s,
|
228
|
+
headers:
|
229
|
+
)
|
230
|
+
)
|
231
|
+
end
|
232
|
+
job = ret.body.to_i
|
233
|
+
throw :"The recent \"#{name}\" job's ID is ##{job} at #{@host}"
|
234
|
+
end
|
235
|
+
job
|
236
|
+
end
|
237
|
+
|
238
|
+
# Check whether the name of the job exists on the server.
|
239
|
+
# @param [String] name The name of the job on the server
|
240
|
+
# @return [Boolean] TRUE if such name exists
|
241
|
+
def name_exists?(name)
|
242
|
+
exists = 0
|
243
|
+
elapsed(@loog) do
|
244
|
+
ret =
|
245
|
+
with_retries(max_tries: @retries) do
|
246
|
+
checked(
|
247
|
+
Typhoeus::Request.get(
|
248
|
+
home.append('exists').append(name).to_s,
|
249
|
+
headers:
|
250
|
+
)
|
251
|
+
)
|
252
|
+
end
|
253
|
+
exists = ret.body == 'yes'
|
254
|
+
throw :"The name \"#{name}\" #{exists ? 'exists' : "doesn't exist"} at #{@host}"
|
255
|
+
end
|
256
|
+
exists
|
257
|
+
end
|
258
|
+
|
259
|
+
private
|
260
|
+
|
261
|
+
def headers
|
262
|
+
{
|
263
|
+
'User-Agent' => "baza.rb #{Baza::VERSION}",
|
264
|
+
'Connection' => 'close',
|
265
|
+
'X-Zerocracy-Token' => @token
|
266
|
+
}
|
267
|
+
end
|
268
|
+
|
269
|
+
def zipped(params)
|
270
|
+
body = gzip(params.fetch(:body))
|
271
|
+
headers = params
|
272
|
+
.fetch(:headers)
|
273
|
+
.merge(
|
274
|
+
{
|
275
|
+
'Content-Type' => 'application/zip',
|
276
|
+
'Content-Encoding' => 'gzip',
|
277
|
+
'Content-Length' => body.size
|
278
|
+
}
|
279
|
+
)
|
280
|
+
params.merge(body:, headers:)
|
281
|
+
end
|
282
|
+
|
283
|
+
def gzip(data)
|
284
|
+
''.dup.tap do |result|
|
285
|
+
io = StringIO.new(result)
|
286
|
+
gz = Zlib::GzipWriter.new(io)
|
287
|
+
gz.write(data)
|
288
|
+
gz.close
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def home
|
293
|
+
Iri.new('')
|
294
|
+
.host(@host)
|
295
|
+
.port(@port)
|
296
|
+
.scheme(@ssl ? 'https' : 'http')
|
297
|
+
end
|
298
|
+
|
299
|
+
def checked(ret, allowed = [200])
|
300
|
+
allowed = [allowed] unless allowed.is_a?(Array)
|
301
|
+
mtd = (ret.request.original_options[:method] || '???').upcase
|
302
|
+
url = ret.effective_url
|
303
|
+
log = "#{mtd} #{url} -> #{ret.code}"
|
304
|
+
if allowed.include?(ret.code)
|
305
|
+
@loog.debug(log)
|
306
|
+
return ret
|
307
|
+
end
|
308
|
+
@loog.debug("#{log}\n #{(ret.headers || {}).map { |k, v| "#{k}: #{v}" }.join("\n ")}")
|
309
|
+
msg = [
|
310
|
+
"Invalid response code ##{ret.code} ",
|
311
|
+
"at #{mtd} #{url}",
|
312
|
+
ret.headers['X-Zerocracy-Flash'] ? " (#{ret.headers['X-Zerocracy-Flash'].inspect})" : ''
|
313
|
+
].join
|
314
|
+
case ret.code
|
315
|
+
when 500
|
316
|
+
msg +=
|
317
|
+
', most probably it\'s an internal error on the server, ' \
|
318
|
+
'please report this to https://github.com/zerocracy/baza'
|
319
|
+
when 503
|
320
|
+
msg +=
|
321
|
+
", most probably it's an internal error on the server (#{ret.headers['X-Zerocracy-Failure'].inspect}), " \
|
322
|
+
'please report this to https://github.com/zerocracy/baza.rb'
|
323
|
+
when 404
|
324
|
+
msg +=
|
325
|
+
', most probably you are trying to reach a wrong server, which doesn\'t ' \
|
326
|
+
'have the URL that it is expected to have'
|
327
|
+
when 0
|
328
|
+
msg += ', most likely an internal error'
|
329
|
+
end
|
330
|
+
raise msg
|
331
|
+
end
|
332
|
+
end
|
data/renovate.json
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2024 Zerocracy
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the 'Software'), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
13
|
+
# copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
$stdout.sync = true
|
24
|
+
|
25
|
+
require 'minitest/reporters'
|
26
|
+
Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]
|
27
|
+
|
28
|
+
require 'simplecov'
|
29
|
+
SimpleCov.start
|
30
|
+
|
31
|
+
require 'simplecov-cobertura'
|
32
|
+
SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter
|
33
|
+
|
34
|
+
require 'minitest/autorun'
|
data/test/test_baza.rb
ADDED
@@ -0,0 +1,258 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2024 Zerocracy
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the 'Software'), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
13
|
+
# copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
require 'minitest/autorun'
|
24
|
+
require 'webmock/minitest'
|
25
|
+
require 'webrick'
|
26
|
+
require 'loog'
|
27
|
+
require 'socket'
|
28
|
+
require 'stringio'
|
29
|
+
require 'random-port'
|
30
|
+
require 'factbase'
|
31
|
+
require 'securerandom'
|
32
|
+
require 'net/ping'
|
33
|
+
require_relative '../lib/baza'
|
34
|
+
|
35
|
+
# Test.
|
36
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
37
|
+
# Copyright:: Copyright (c) 2024 Yegor Bugayenko
|
38
|
+
# License:: MIT
|
39
|
+
class TestBaza < Minitest::Test
|
40
|
+
TOKEN = '00000000-0000-0000-0000-000000000000'
|
41
|
+
HOST = 'api.zerocracy.com'
|
42
|
+
PORT = 443
|
43
|
+
LIVE = Baza.new(HOST, PORT, TOKEN)
|
44
|
+
|
45
|
+
def test_live_recent_check
|
46
|
+
WebMock.enable_net_connect!
|
47
|
+
skip unless we_are_online
|
48
|
+
assert(LIVE.recent('zerocracy').positive?)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_live_name_exists_check
|
52
|
+
WebMock.enable_net_connect!
|
53
|
+
skip unless we_are_online
|
54
|
+
assert(LIVE.name_exists?('zerocracy'))
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_live_push
|
58
|
+
WebMock.enable_net_connect!
|
59
|
+
skip unless we_are_online
|
60
|
+
fb = Factbase.new
|
61
|
+
fb.insert.foo = 'test-' * 10_000
|
62
|
+
fb.insert
|
63
|
+
assert(LIVE.push(fake_name, fb.export, []).positive?)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_live_push_no_compression
|
67
|
+
WebMock.enable_net_connect!
|
68
|
+
skip unless we_are_online
|
69
|
+
fb = Factbase.new
|
70
|
+
fb.insert.foo = 'test-' * 10_000
|
71
|
+
fb.insert
|
72
|
+
baza = Baza.new(HOST, PORT, TOKEN, compression: false)
|
73
|
+
assert(baza.push(fake_name, fb.export, []).positive?)
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_live_pull
|
77
|
+
WebMock.enable_net_connect!
|
78
|
+
skip unless we_are_online
|
79
|
+
id = LIVE.recent('zerocracy')
|
80
|
+
assert(!LIVE.pull(id).nil?)
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_live_check_finished
|
84
|
+
WebMock.enable_net_connect!
|
85
|
+
skip unless we_are_online
|
86
|
+
id = LIVE.recent('zerocracy')
|
87
|
+
assert(!LIVE.finished?(id).nil?)
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_live_read_stdout
|
91
|
+
WebMock.enable_net_connect!
|
92
|
+
skip unless we_are_online
|
93
|
+
id = LIVE.recent('zerocracy')
|
94
|
+
assert(!LIVE.stdout(id).nil?)
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_live_read_exit_code
|
98
|
+
WebMock.enable_net_connect!
|
99
|
+
skip unless we_are_online
|
100
|
+
id = LIVE.recent('zerocracy')
|
101
|
+
assert(!LIVE.exit_code(id).nil?)
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_live_lock_unlock
|
105
|
+
WebMock.enable_net_connect!
|
106
|
+
skip unless we_are_online
|
107
|
+
n = fake_name
|
108
|
+
owner = 'judges teesting'
|
109
|
+
assert(!LIVE.lock(n, owner).nil?)
|
110
|
+
assert(!LIVE.unlock(n, owner).nil?)
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_simple_push
|
114
|
+
WebMock.disable_net_connect!
|
115
|
+
stub_request(:put, 'https://example.org/push/simple').to_return(
|
116
|
+
status: 200, body: '42'
|
117
|
+
)
|
118
|
+
assert_equal(
|
119
|
+
42,
|
120
|
+
Baza.new('example.org', 443, '000').push('simple', 'hello, world!', [])
|
121
|
+
)
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_simple_recent_check
|
125
|
+
WebMock.disable_net_connect!
|
126
|
+
stub_request(:get, 'https://example.org/recent/simple.txt')
|
127
|
+
.with(body: '', headers: { 'User-Agent' => /^baza.rb .*$/ })
|
128
|
+
.to_return(status: 200, body: '42')
|
129
|
+
assert_equal(
|
130
|
+
42,
|
131
|
+
Baza.new('example.org', 443, '000').recent('simple')
|
132
|
+
)
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_simple_exists_check
|
136
|
+
WebMock.disable_net_connect!
|
137
|
+
stub_request(:get, 'https://example.org/exists/simple').to_return(
|
138
|
+
status: 200, body: 'yes'
|
139
|
+
)
|
140
|
+
assert(
|
141
|
+
Baza.new('example.org', 443, '000').name_exists?('simple')
|
142
|
+
)
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_exit_code_check
|
146
|
+
WebMock.disable_net_connect!
|
147
|
+
stub_request(:get, 'https://example.org/exit/42.txt').to_return(
|
148
|
+
status: 200, body: '0'
|
149
|
+
)
|
150
|
+
assert(
|
151
|
+
Baza.new('example.org', 443, '000').exit_code(42).zero?
|
152
|
+
)
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_stdout_read
|
156
|
+
WebMock.disable_net_connect!
|
157
|
+
stub_request(:get, 'https://example.org/stdout/42.txt').to_return(
|
158
|
+
status: 200, body: 'hello!'
|
159
|
+
)
|
160
|
+
assert(
|
161
|
+
!Baza.new('example.org', 443, '000').stdout(42).empty?
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_simple_pull
|
166
|
+
WebMock.disable_net_connect!
|
167
|
+
stub_request(:get, 'https://example.org/pull/333.fb').to_return(
|
168
|
+
status: 200, body: 'hello, world!'
|
169
|
+
)
|
170
|
+
assert(
|
171
|
+
Baza.new('example.org', 443, '000').pull(333).start_with?('hello')
|
172
|
+
)
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_real_http
|
176
|
+
req =
|
177
|
+
with_http_server(200, 'yes') do |baza|
|
178
|
+
baza.name_exists?('simple')
|
179
|
+
end
|
180
|
+
assert_equal("baza.rb #{Baza::VERSION}", req['user-agent'])
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_push_with_meta
|
184
|
+
req =
|
185
|
+
with_http_server(200, 'yes') do |baza|
|
186
|
+
baza.push('simple', 'hello, world!', ['boom!', 'хей!'])
|
187
|
+
end
|
188
|
+
assert_equal('Ym9vbSE= 0YXQtdC5IQ==', req['x-zerocracy-meta'])
|
189
|
+
end
|
190
|
+
|
191
|
+
def test_push_with_big_meta
|
192
|
+
req =
|
193
|
+
with_http_server(200, 'yes') do |baza|
|
194
|
+
baza.push(
|
195
|
+
'simple',
|
196
|
+
'hello, world!',
|
197
|
+
[
|
198
|
+
'pages_url:https://zerocracy.github.io/zerocracy.html',
|
199
|
+
'others:https://zerocracy.github.io/zerocracy.html',
|
200
|
+
'duration:59595'
|
201
|
+
]
|
202
|
+
)
|
203
|
+
end
|
204
|
+
assert(req['x-zerocracy-meta'])
|
205
|
+
end
|
206
|
+
|
207
|
+
def test_push_compressed_content
|
208
|
+
skip # this test is not stable, see https://github.com/yegor256/judges/issues/105
|
209
|
+
req =
|
210
|
+
with_http_server(200, 'yes') do |baza|
|
211
|
+
baza.push('simple', 'hello, world!', %w[meta1 meta2 meta3])
|
212
|
+
end
|
213
|
+
assert_equal('application/zip', req.content_type)
|
214
|
+
assert_equal('gzip', req['content-encoding'])
|
215
|
+
body = Zlib::GzipReader.zcat(StringIO.new(req.body))
|
216
|
+
assert_equal('hello, world!', body)
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_push_compression_disabled
|
220
|
+
req =
|
221
|
+
with_http_server(200, 'yes', compression: false) do |baza|
|
222
|
+
baza.push('simple', 'hello, world!', %w[meta1 meta2 meta3])
|
223
|
+
end
|
224
|
+
assert_equal('application/octet-stream', req.content_type)
|
225
|
+
assert_equal('hello, world!', req.body)
|
226
|
+
end
|
227
|
+
|
228
|
+
private
|
229
|
+
|
230
|
+
def with_http_server(code, response, opts = {})
|
231
|
+
opts = { ssl: false, timeout: 1 }.merge(opts)
|
232
|
+
WebMock.enable_net_connect!
|
233
|
+
req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
|
234
|
+
host = '127.0.0.1'
|
235
|
+
RandomPort::Pool::SINGLETON.acquire do |port|
|
236
|
+
server = TCPServer.new(host, port)
|
237
|
+
t =
|
238
|
+
Thread.new do
|
239
|
+
socket = server.accept
|
240
|
+
req.parse(socket)
|
241
|
+
req.body
|
242
|
+
socket.puts "HTTP/1.1 #{code} OK\r\nContent-Length: #{response.length}\r\n\r\n#{response}"
|
243
|
+
socket.close
|
244
|
+
end
|
245
|
+
yield Baza.new(host, port, '0000', **opts)
|
246
|
+
t.join
|
247
|
+
end
|
248
|
+
req
|
249
|
+
end
|
250
|
+
|
251
|
+
def fake_name
|
252
|
+
"fake-#{SecureRandom.hex(8)}"
|
253
|
+
end
|
254
|
+
|
255
|
+
def we_are_online
|
256
|
+
Net::Ping::External.new('8.8.8.8').ping?
|
257
|
+
end
|
258
|
+
end
|