baza.rb 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|