fbe 0.24.2 → 0.24.4
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/.rubocop.yml +2 -0
- data/Gemfile.lock +1 -1
- data/Rakefile +1 -1
- data/lib/fbe/conclude.rb +17 -14
- data/lib/fbe/fb.rb +2 -2
- data/lib/fbe/just_one.rb +1 -1
- data/lib/fbe/middleware/formatter.rb +16 -0
- data/lib/fbe/middleware/sqlite_store.rb +37 -5
- data/lib/fbe/octo.rb +13 -3
- data/lib/fbe.rb +1 -1
- data/test/fbe/middleware/test_formatter.rb +33 -12
- data/test/fbe/middleware/test_rate_limit.rb +1 -1
- data/test/fbe/middleware/test_sqlite_store.rb +95 -2
- data/test/fbe/middleware/test_trace.rb +1 -1
- data/test/fbe/test_conclude.rb +44 -0
- data/test/fbe/test_github_graph.rb +2 -2
- data/test/fbe/test_if_absent.rb +2 -2
- data/test/fbe/test_just_one.rb +1 -1
- data/test/fbe/test_octo.rb +47 -0
- data/test/fbe/test_unmask_repos.rb +1 -1
- data/test/test__helper.rb +1 -1
- data/test/test_fbe.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92baa5e54a0df9b2e57f2d6619cfad4d4ee1b4a4150b1e30b82780062dd9bd34
|
4
|
+
data.tar.gz: ac08a108536716ff130271018211616e02b58a55dcef303bed0c0d01808387eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4b87c1fefa9477f19588b01eeb7a7d593ca38f1eed068fe7d27a84a66b4cc35214f83b8f6128a169a007df962063f4ae5373442a17c61d12f1c7e876c1099ef6
|
7
|
+
data.tar.gz: ae01c566bca05de64432b34b6f27bbf82497d5d72817c5bbb0ef9f80e0f62f742681f1ae820d30bc081d891b31f926369ee1f0efe42e4b778a0223b4be26959e
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
data/Rakefile
CHANGED
data/lib/fbe/conclude.rb
CHANGED
@@ -6,8 +6,8 @@
|
|
6
6
|
require 'tago'
|
7
7
|
require_relative '../fbe'
|
8
8
|
require_relative 'fb'
|
9
|
-
require_relative 'octo'
|
10
9
|
require_relative 'if_absent'
|
10
|
+
require_relative 'octo'
|
11
11
|
|
12
12
|
# Creates an instance of {Fbe::Conclude} and evals it with the block provided.
|
13
13
|
#
|
@@ -17,13 +17,13 @@ require_relative 'if_absent'
|
|
17
17
|
# @param [Judges::Options] options The options coming from the +judges+ tool
|
18
18
|
# @param [Loog] loog The logging facility
|
19
19
|
# @yield [Factbase::Fact] The fact
|
20
|
-
def Fbe.conclude(fb: Fbe.fb, judge: $judge, loog: $loog, options: $options, global: $global, &)
|
20
|
+
def Fbe.conclude(fb: Fbe.fb, judge: $judge, loog: $loog, options: $options, global: $global, time: Time, &)
|
21
21
|
raise 'The fb is nil' if fb.nil?
|
22
22
|
raise 'The $judge is not set' if judge.nil?
|
23
23
|
raise 'The $global is not set' if global.nil?
|
24
24
|
raise 'The $options is not set' if options.nil?
|
25
25
|
raise 'The $loog is not set' if loog.nil?
|
26
|
-
c = Fbe::Conclude.new(fb:, judge:, loog:, options:, global:)
|
26
|
+
c = Fbe::Conclude.new(fb:, judge:, loog:, options:, global:, time:)
|
27
27
|
c.instance_eval(&)
|
28
28
|
end
|
29
29
|
|
@@ -58,7 +58,8 @@ class Fbe::Conclude
|
|
58
58
|
# @param [Hash] global The hash for global caching
|
59
59
|
# @param [Judges::Options] options The options coming from the +judges+ tool
|
60
60
|
# @param [Loog] loog The logging facility
|
61
|
-
|
61
|
+
# @param [Time] time The time
|
62
|
+
def initialize(fb:, judge:, global:, options:, loog:, time: Time)
|
62
63
|
@fb = fb
|
63
64
|
@judge = judge
|
64
65
|
@loog = loog
|
@@ -68,6 +69,7 @@ class Fbe::Conclude
|
|
68
69
|
@follows = []
|
69
70
|
@quota_aware = false
|
70
71
|
@timeout = 60
|
72
|
+
@time = time
|
71
73
|
end
|
72
74
|
|
73
75
|
# Make this block aware of GitHub API quota.
|
@@ -180,22 +182,23 @@ class Fbe::Conclude
|
|
180
182
|
# end
|
181
183
|
def roll(&)
|
182
184
|
passed = 0
|
183
|
-
start =
|
185
|
+
start = @time.now
|
184
186
|
oct = Fbe.octo(loog: @loog, options: @options, global: @global)
|
185
187
|
@fb.query(@query).each do |a|
|
188
|
+
if @quota_aware && oct.off_quota?
|
189
|
+
@loog.debug('We ran out of GitHub quota, must stop here')
|
190
|
+
break
|
191
|
+
end
|
192
|
+
now = @time.now
|
193
|
+
if now > start + @timeout
|
194
|
+
@loog.debug("We've spent more than #{start.ago}, must stop here")
|
195
|
+
break
|
196
|
+
end
|
186
197
|
@fb.txn do |fbt|
|
187
|
-
if @quota_aware && oct.off_quota?
|
188
|
-
@loog.debug('We ran out of GitHub quota, must stop here')
|
189
|
-
throw :commit
|
190
|
-
end
|
191
|
-
if Time.now > start + @timeout
|
192
|
-
@loog.debug("We've spent more than #{start.ago}, must stop here")
|
193
|
-
throw :commit
|
194
|
-
end
|
195
198
|
n = yield fbt, a
|
196
199
|
@loog.info("#{n.what}: #{n.details}") unless n.nil?
|
197
|
-
passed += 1
|
198
200
|
end
|
201
|
+
passed += 1
|
199
202
|
end
|
200
203
|
@loog.debug("Found and processed #{passed} facts by: #{@query}")
|
201
204
|
passed
|
data/lib/fbe/fb.rb
CHANGED
@@ -4,11 +4,11 @@
|
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
6
|
require 'factbase'
|
7
|
+
require 'factbase/cached/cached_factbase'
|
8
|
+
require 'factbase/indexed/indexed_factbase'
|
7
9
|
require 'factbase/logged'
|
8
10
|
require 'factbase/pre'
|
9
11
|
require 'factbase/rules'
|
10
|
-
require 'factbase/cached/cached_factbase'
|
11
|
-
require 'factbase/indexed/indexed_factbase'
|
12
12
|
require 'factbase/sync/sync_factbase'
|
13
13
|
require 'judges'
|
14
14
|
require 'loog'
|
data/lib/fbe/just_one.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Zerocracy
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
|
+
require 'ellipsized'
|
6
7
|
require 'faraday'
|
7
8
|
require 'faraday/logging/formatter'
|
8
9
|
require_relative '../../fbe'
|
@@ -66,6 +67,21 @@ class Fbe::Middleware::Formatter < Faraday::Logging::Formatter
|
|
66
67
|
)
|
67
68
|
return
|
68
69
|
end
|
70
|
+
if http.status >= 500 && http.response_headers['content-type']&.start_with?('text')
|
71
|
+
error(
|
72
|
+
[
|
73
|
+
"#{@req.method.upcase} #{apply_filters(@req.url.to_s)} HTTP/1.1",
|
74
|
+
shifted(apply_filters(dump_headers(@req.request_headers))),
|
75
|
+
'',
|
76
|
+
shifted(apply_filters(@req.request_body)),
|
77
|
+
"HTTP/1.1 #{http.status}",
|
78
|
+
shifted(apply_filters(dump_headers(http.response_headers))),
|
79
|
+
'',
|
80
|
+
shifted(apply_filters(http.response_body&.ellipsized(100, :right)))
|
81
|
+
].join("\n")
|
82
|
+
)
|
83
|
+
return
|
84
|
+
end
|
69
85
|
error(
|
70
86
|
[
|
71
87
|
"#{@req.method.upcase} #{apply_filters(@req.url.to_s)} HTTP/1.1",
|
@@ -3,12 +3,12 @@
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Zerocracy
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
|
-
require 'zlib'
|
7
6
|
require 'filesize'
|
8
7
|
require 'json'
|
9
8
|
require 'loog'
|
10
9
|
require 'sqlite3'
|
11
10
|
require 'time'
|
11
|
+
require 'zlib'
|
12
12
|
require_relative '../../fbe'
|
13
13
|
require_relative '../../fbe/middleware'
|
14
14
|
|
@@ -53,8 +53,10 @@ class Fbe::Middleware::SqliteStore
|
|
53
53
|
# @param loog [Loog] Logger instance (optional, defaults to Loog::NULL)
|
54
54
|
# @param maxsize [Integer] Maximum database size in bytes (optional, defaults to 10MB)
|
55
55
|
# @param maxvsize [Integer] Maximum size in bytes of a single value (optional, defaults to 10Kb)
|
56
|
-
# @
|
57
|
-
|
56
|
+
# @param ttl [Integer, nil] lifetime of keys in hours
|
57
|
+
# @raise [ArgumentError] If path is nil/empty, directory doesn't exist, version is nil/empty,
|
58
|
+
# or ttl is not nil or not Integer or not positive
|
59
|
+
def initialize(path, version, loog: Loog::NULL, maxsize: '10Mb', maxvsize: '10Kb', ttl: nil)
|
58
60
|
raise ArgumentError, 'Database path cannot be nil or empty' if path.nil? || path.empty?
|
59
61
|
dir = File.dirname(path)
|
60
62
|
raise ArgumentError, "Directory #{dir} does not exist" unless File.directory?(dir)
|
@@ -64,6 +66,8 @@ class Fbe::Middleware::SqliteStore
|
|
64
66
|
@loog = loog
|
65
67
|
@maxsize = Filesize.from(maxsize.to_s).to_i
|
66
68
|
@maxvsize = Filesize.from(maxvsize.to_s).to_i
|
69
|
+
raise ArgumentError, 'TTL can be nil or Integer > 0' if !ttl.nil? && !(ttl.is_a?(Integer) && ttl.positive?)
|
70
|
+
@ttl = ttl
|
67
71
|
end
|
68
72
|
|
69
73
|
# Read a value from the cache.
|
@@ -106,8 +110,8 @@ class Fbe::Middleware::SqliteStore
|
|
106
110
|
return if value.bytesize > @maxvsize
|
107
111
|
perform do |t|
|
108
112
|
t.execute(<<~SQL, [key, value, Time.now.utc.iso8601])
|
109
|
-
INSERT INTO cache(key, value, touched_at) VALUES(?1, ?2, ?3)
|
110
|
-
ON CONFLICT(key) DO UPDATE SET value = ?2, touched_at = ?3
|
113
|
+
INSERT INTO cache(key, value, touched_at, created_at) VALUES(?1, ?2, ?3, ?3)
|
114
|
+
ON CONFLICT(key) DO UPDATE SET value = ?2, touched_at = ?3, created_at = ?3
|
111
115
|
SQL
|
112
116
|
end
|
113
117
|
nil
|
@@ -153,10 +157,29 @@ class Fbe::Middleware::SqliteStore
|
|
153
157
|
SQL
|
154
158
|
t.execute 'INSERT INTO cache SELECT * FROM cache_old;'
|
155
159
|
t.execute 'DROP TABLE cache_old;'
|
160
|
+
t.execute 'CREATE INDEX IF NOT EXISTS cache_key_idx ON cache(key);'
|
156
161
|
t.execute 'CREATE INDEX IF NOT EXISTS cache_touched_at_idx ON cache(touched_at);'
|
157
162
|
end
|
158
163
|
d.execute 'VACUUM;'
|
159
164
|
end
|
165
|
+
if d.execute("SELECT 1 FROM pragma_table_info('cache') WHERE name = 'created_at';").dig(0, 0) != 1
|
166
|
+
d.transaction do |t|
|
167
|
+
t.execute 'ALTER TABLE cache ADD COLUMN created_at TEXT;'
|
168
|
+
t.execute 'UPDATE cache set created_at = ?;', [Time.now.utc.iso8601]
|
169
|
+
t.execute 'ALTER TABLE cache RENAME TO cache_old;'
|
170
|
+
t.execute <<~SQL
|
171
|
+
CREATE TABLE IF NOT EXISTS cache(
|
172
|
+
key TEXT UNIQUE NOT NULL, value TEXT, touched_at TEXT NOT NULL, created_at TEXT NOT NULL
|
173
|
+
);
|
174
|
+
SQL
|
175
|
+
t.execute 'INSERT INTO cache SELECT * FROM cache_old;'
|
176
|
+
t.execute 'DROP TABLE cache_old;'
|
177
|
+
t.execute 'CREATE INDEX IF NOT EXISTS cache_key_idx ON cache(key);'
|
178
|
+
t.execute 'CREATE INDEX IF NOT EXISTS cache_touched_at_idx ON cache(touched_at);'
|
179
|
+
t.execute 'CREATE INDEX IF NOT EXISTS cache_created_at_idx ON cache(created_at);'
|
180
|
+
end
|
181
|
+
d.execute 'VACUUM;'
|
182
|
+
end
|
160
183
|
found = d.execute("SELECT value FROM meta WHERE key = 'version' LIMIT 1;").dig(0, 0)
|
161
184
|
if found != @version
|
162
185
|
@loog.info("Version mismatch in SQLite cache: stored '#{found}' != current '#{@version}', cleaning up")
|
@@ -166,6 +189,15 @@ class Fbe::Middleware::SqliteStore
|
|
166
189
|
end
|
167
190
|
d.execute 'VACUUM;'
|
168
191
|
end
|
192
|
+
unless @ttl.nil?
|
193
|
+
d.transaction do |t|
|
194
|
+
t.execute <<~SQL, [(Time.now.utc - (@ttl * 60 * 60)).iso8601]
|
195
|
+
DELETE FROM cache
|
196
|
+
WHERE key IN (SELECT key FROM cache WHERE (created_at < ?));
|
197
|
+
SQL
|
198
|
+
end
|
199
|
+
d.execute 'VACUUM;'
|
200
|
+
end
|
169
201
|
if File.size(@path) > @maxsize
|
170
202
|
@loog.info(
|
171
203
|
"SQLite cache file size (#{Filesize.from(File.size(@path).to_s).pretty} bytes) exceeds " \
|
data/lib/fbe/octo.rb
CHANGED
@@ -93,7 +93,9 @@ def Fbe.octo(options: $options, global: $global, loog: $loog)
|
|
93
93
|
if options.sqlite_cache
|
94
94
|
maxsize = Filesize.from(options.sqlite_cache_maxsize || '100M').to_i
|
95
95
|
maxvsize = Filesize.from(options.sqlite_cache_maxvsize || '100K').to_i
|
96
|
-
store = Fbe::Middleware::SqliteStore.new(
|
96
|
+
store = Fbe::Middleware::SqliteStore.new(
|
97
|
+
options.sqlite_cache, Fbe::VERSION, loog:, maxsize:, maxvsize:, ttl: 24
|
98
|
+
)
|
97
99
|
loog.info(
|
98
100
|
"Using HTTP cache in SQLite file: #{store.path} (" \
|
99
101
|
"#{File.exist?(store.path) ? Filesize.from(File.size(store.path).to_s).pretty : 'file is absent'}, " \
|
@@ -665,7 +667,8 @@ class Fbe::FakeOctokit
|
|
665
667
|
user: { login: 'yegor256', id: 526_301, type: 'User' },
|
666
668
|
created_at: Time.parse('2025-06-01 12:00:55 UTC'),
|
667
669
|
updated_at: Time.parse('2025-06-01 15:47:18 UTC'),
|
668
|
-
closed_at: Time.parse('2025-06-02 15:00:00 UTC')
|
670
|
+
closed_at: Time.parse('2025-06-02 15:00:00 UTC'),
|
671
|
+
closed_by: { id: 526_301, login: 'yegor256' }
|
669
672
|
}
|
670
673
|
elsif number == 143
|
671
674
|
{
|
@@ -676,7 +679,8 @@ class Fbe::FakeOctokit
|
|
676
679
|
pull_request: { merged_at: nil },
|
677
680
|
created_at: Time.parse('2025-05-29 17:00:55 UTC'),
|
678
681
|
updated_at: Time.parse('2025-05-29 19:00:00 UTC'),
|
679
|
-
closed_at: Time.parse('2025-06-01 18:20:00 UTC')
|
682
|
+
closed_at: Time.parse('2025-06-01 18:20:00 UTC'),
|
683
|
+
closed_by: { id: 526_301, login: 'yegor256' }
|
680
684
|
}
|
681
685
|
else
|
682
686
|
{
|
@@ -708,6 +712,11 @@ class Fbe::FakeOctokit
|
|
708
712
|
repo: {
|
709
713
|
full_name: repo
|
710
714
|
},
|
715
|
+
base: {
|
716
|
+
repo: {
|
717
|
+
full_name: repo
|
718
|
+
}
|
719
|
+
},
|
711
720
|
state: 'closed',
|
712
721
|
user: { login: 'yegor256', id: 526_301, type: 'User' },
|
713
722
|
head: { ref: 'master', sha: '6dcb09b5b57875f334f61aebed695e2e4193db5e' },
|
@@ -716,6 +725,7 @@ class Fbe::FakeOctokit
|
|
716
725
|
changed_files: 3,
|
717
726
|
comments: 2,
|
718
727
|
review_comments: 2,
|
728
|
+
closed_at: Time.parse('2024-12-20'),
|
719
729
|
merged_at: Time.parse('2024-12-20'),
|
720
730
|
created_at: Time.parse('2024-09-20')
|
721
731
|
}
|
data/lib/fbe.rb
CHANGED
@@ -5,10 +5,11 @@
|
|
5
5
|
|
6
6
|
require 'faraday'
|
7
7
|
require 'loog'
|
8
|
-
|
8
|
+
require 'securerandom'
|
9
9
|
require_relative '../../../lib/fbe'
|
10
10
|
require_relative '../../../lib/fbe/middleware'
|
11
11
|
require_relative '../../../lib/fbe/middleware/formatter'
|
12
|
+
require_relative '../../test__helper'
|
12
13
|
|
13
14
|
# Test.
|
14
15
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -49,9 +50,38 @@ class LoggingFormatterTest < Fbe::Test
|
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
53
|
+
def test_truncate_body_for_error_text_response
|
54
|
+
body = SecureRandom.alphanumeric(120)
|
55
|
+
log_it(
|
56
|
+
status: 502,
|
57
|
+
response_body: body,
|
58
|
+
response_headers: {
|
59
|
+
'content-type' => 'text/html; charset=utf-8',
|
60
|
+
'x-github-api-version-selected' => '2022-11-28'
|
61
|
+
}
|
62
|
+
) do |loog|
|
63
|
+
str = loog.to_s
|
64
|
+
refute_empty(str)
|
65
|
+
[
|
66
|
+
%r{http://example.com},
|
67
|
+
/Authorization:/,
|
68
|
+
/some request body/,
|
69
|
+
%r{HTTP/1.1 502},
|
70
|
+
/x-github-api-version-selected: "2022-11-28"/,
|
71
|
+
%r{content-type: "text/html; charset=utf-8"},
|
72
|
+
"#{body.slice(0, 97)}..."
|
73
|
+
].each { |ptn| assert_match(ptn, str) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
52
77
|
private
|
53
78
|
|
54
|
-
def log_it(
|
79
|
+
def log_it(
|
80
|
+
status:,
|
81
|
+
method: :get,
|
82
|
+
response_body: '{"message": "hello, world!"}',
|
83
|
+
response_headers: { 'content-type' => 'application/json', 'x-github-api-version-selected' => '2022-11-28' }
|
84
|
+
)
|
55
85
|
loog = Loog::Buffer.new
|
56
86
|
formatter = Fbe::Middleware::Formatter.new(logger: loog, options: {})
|
57
87
|
formatter.request(
|
@@ -67,16 +97,7 @@ class LoggingFormatterTest < Fbe::Test
|
|
67
97
|
)
|
68
98
|
)
|
69
99
|
formatter.response(
|
70
|
-
Faraday::Env.from(
|
71
|
-
{
|
72
|
-
status:,
|
73
|
-
response_body: '{"message": "hello, world!"}',
|
74
|
-
response_headers: {
|
75
|
-
'content-type' => 'application/json',
|
76
|
-
'x-github-api-version-selected' => '2022-11-28'
|
77
|
-
}
|
78
|
-
}
|
79
|
-
)
|
100
|
+
Faraday::Env.from({ status:, response_body:, response_headers: })
|
80
101
|
)
|
81
102
|
yield loog
|
82
103
|
end
|
@@ -5,10 +5,10 @@
|
|
5
5
|
|
6
6
|
require 'faraday'
|
7
7
|
require 'webmock'
|
8
|
-
require_relative '../../test__helper'
|
9
8
|
require_relative '../../../lib/fbe'
|
10
9
|
require_relative '../../../lib/fbe/middleware'
|
11
10
|
require_relative '../../../lib/fbe/middleware/rate_limit'
|
11
|
+
require_relative '../../test__helper'
|
12
12
|
|
13
13
|
# Test.
|
14
14
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -3,11 +3,11 @@
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Zerocracy
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
|
-
require 'securerandom'
|
7
6
|
require 'qbash'
|
8
|
-
|
7
|
+
require 'securerandom'
|
9
8
|
require_relative '../../../lib/fbe/middleware'
|
10
9
|
require_relative '../../../lib/fbe/middleware/sqlite_store'
|
10
|
+
require_relative '../../test__helper'
|
11
11
|
|
12
12
|
# Test.
|
13
13
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -237,6 +237,99 @@ class SqliteStoreTest < Fbe::Test
|
|
237
237
|
end
|
238
238
|
end
|
239
239
|
|
240
|
+
def test_upgrade_sqlite_schema_for_add_created_at_column
|
241
|
+
with_tmpfile('a.db') do |f|
|
242
|
+
SQLite3::Database.new(f).tap do |d|
|
243
|
+
d.execute 'CREATE TABLE IF NOT EXISTS cache(key TEXT UNIQUE NOT NULL, value TEXT, touched_at TEXT NOT NULL);'
|
244
|
+
[
|
245
|
+
['key1', Zlib::Deflate.deflate(JSON.dump('value1')), Time.now.utc.iso8601],
|
246
|
+
['key2', Zlib::Deflate.deflate(JSON.dump('value2')), Time.now.utc.iso8601]
|
247
|
+
].each { d.execute 'INSERT INTO cache(key, value, touched_at) VALUES(?1, ?2, ?3);', _1 }
|
248
|
+
d.execute 'CREATE TABLE IF NOT EXISTS meta(key TEXT UNIQUE NOT NULL, value TEXT);'
|
249
|
+
d.execute "INSERT INTO meta(key, value) VALUES('version', ?);", ['0.0.1']
|
250
|
+
end
|
251
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog).then do |store|
|
252
|
+
assert_equal('value1', store.read('key1'))
|
253
|
+
assert_equal('value2', store.read('key2'))
|
254
|
+
rescue SQLite3::SQLException => e
|
255
|
+
assert_nil(e)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def test_set_correct_ttl
|
261
|
+
with_tmpfile('c.db') do |f|
|
262
|
+
s = Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, ttl: nil)
|
263
|
+
refute_nil(s)
|
264
|
+
s = Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, ttl: 24)
|
265
|
+
refute_nil(s)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def test_set_incorrect_ttl
|
270
|
+
with_tmpfile('c.db') do |f|
|
271
|
+
ex =
|
272
|
+
assert_raises(ArgumentError) do
|
273
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, ttl: 0)
|
274
|
+
end
|
275
|
+
assert_equal('TTL can be nil or Integer > 0', ex.message)
|
276
|
+
ex =
|
277
|
+
assert_raises(ArgumentError) do
|
278
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, ttl: -10)
|
279
|
+
end
|
280
|
+
assert_equal('TTL can be nil or Integer > 0', ex.message)
|
281
|
+
ex =
|
282
|
+
assert_raises(ArgumentError) do
|
283
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, ttl: 10.0)
|
284
|
+
end
|
285
|
+
assert_equal('TTL can be nil or Integer > 0', ex.message)
|
286
|
+
ex =
|
287
|
+
assert_raises(ArgumentError) do
|
288
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, ttl: '10')
|
289
|
+
end
|
290
|
+
assert_equal('TTL can be nil or Integer > 0', ex.message)
|
291
|
+
ex =
|
292
|
+
assert_raises(ArgumentError) do
|
293
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, ttl: Object.new)
|
294
|
+
end
|
295
|
+
assert_equal('TTL can be nil or Integer > 0', ex.message)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def test_delete_keys_if_ttl_expired
|
300
|
+
with_tmpfile('c.db') do |f|
|
301
|
+
now = Time.now
|
302
|
+
Time.stub(:now, now) do
|
303
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, ttl: 24).then do |s|
|
304
|
+
s.write('test1', 'value1')
|
305
|
+
s.write('test2', 'value2')
|
306
|
+
end
|
307
|
+
end
|
308
|
+
Time.stub(:now, now + (12 * 60 * 60)) do
|
309
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, ttl: 24).then do |s|
|
310
|
+
s.write('test3', 'value3')
|
311
|
+
s.write('test4', 'value4')
|
312
|
+
end
|
313
|
+
end
|
314
|
+
Time.stub(:now, now + (24 * 60 * 60)) do
|
315
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, ttl: 24).then do |s|
|
316
|
+
assert_equal('value1', s.read('test1'))
|
317
|
+
assert_equal('value2', s.read('test2'))
|
318
|
+
assert_equal('value3', s.read('test3'))
|
319
|
+
assert_equal('value4', s.read('test4'))
|
320
|
+
end
|
321
|
+
end
|
322
|
+
Time.stub(:now, now + (24 * 60 * 60) + 1) do
|
323
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, ttl: 24).then do |s|
|
324
|
+
assert_nil(s.read('test1'))
|
325
|
+
assert_nil(s.read('test2'))
|
326
|
+
assert_equal('value3', s.read('test3'))
|
327
|
+
assert_equal('value4', s.read('test4'))
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
240
333
|
private
|
241
334
|
|
242
335
|
def with_tmpfile(name = 'test.db', &)
|
@@ -6,10 +6,10 @@
|
|
6
6
|
require 'faraday'
|
7
7
|
require 'faraday/http_cache'
|
8
8
|
require 'webmock'
|
9
|
-
require_relative '../../test__helper'
|
10
9
|
require_relative '../../../lib/fbe'
|
11
10
|
require_relative '../../../lib/fbe/middleware'
|
12
11
|
require_relative '../../../lib/fbe/middleware/trace'
|
12
|
+
require_relative '../../test__helper'
|
13
13
|
|
14
14
|
# Test.
|
15
15
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
data/test/fbe/test_conclude.rb
CHANGED
@@ -125,4 +125,48 @@ class TestConclude < Fbe::Test
|
|
125
125
|
end
|
126
126
|
assert_equal(2, fb.size)
|
127
127
|
end
|
128
|
+
|
129
|
+
def test_stop_if_timeout_exceeded
|
130
|
+
$fb = Factbase.new
|
131
|
+
$fb.insert.then do |f|
|
132
|
+
f._id = 1
|
133
|
+
f.foo = 5
|
134
|
+
end
|
135
|
+
$fb.insert.then do |f|
|
136
|
+
f._id = 2
|
137
|
+
f.foo = 4
|
138
|
+
end
|
139
|
+
$fb.insert.then do |f|
|
140
|
+
f._id = 3
|
141
|
+
f.bar = 3
|
142
|
+
end
|
143
|
+
$fb.insert.then do |f|
|
144
|
+
f._id = 4
|
145
|
+
f.foo = 2
|
146
|
+
end
|
147
|
+
$fb.insert.then do |f|
|
148
|
+
f._id = 5
|
149
|
+
f.foo = 1
|
150
|
+
end
|
151
|
+
$global = {}
|
152
|
+
$options = Judges::Options.new({ 'testing' => true })
|
153
|
+
$loog = Loog::NULL
|
154
|
+
$judge = ''
|
155
|
+
total = 0
|
156
|
+
now = Time.now
|
157
|
+
time = Minitest::Mock.new
|
158
|
+
time.expect(:now, now)
|
159
|
+
time.expect(:now, now + 4)
|
160
|
+
time.expect(:now, now + 8)
|
161
|
+
time.expect(:now, now + 12)
|
162
|
+
Fbe.conclude(time: time) do
|
163
|
+
on '(exists foo)'
|
164
|
+
timeout 10
|
165
|
+
consider do |f|
|
166
|
+
total += f.foo
|
167
|
+
end
|
168
|
+
end
|
169
|
+
assert_equal(9, total)
|
170
|
+
time.verify
|
171
|
+
end
|
128
172
|
end
|
@@ -4,10 +4,10 @@
|
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
6
|
require 'judges/options'
|
7
|
-
require 'webmock/minitest'
|
8
7
|
require 'loog'
|
9
|
-
|
8
|
+
require 'webmock/minitest'
|
10
9
|
require_relative '../../lib/fbe/github_graph'
|
10
|
+
require_relative '../test__helper'
|
11
11
|
|
12
12
|
# Test.
|
13
13
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
data/test/fbe/test_if_absent.rb
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Zerocracy
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
|
-
require 'tmpdir'
|
7
6
|
require 'factbase'
|
8
7
|
require 'loog'
|
9
|
-
|
8
|
+
require 'tmpdir'
|
10
9
|
require_relative '../../lib/fbe/if_absent'
|
10
|
+
require_relative '../test__helper'
|
11
11
|
|
12
12
|
# Test.
|
13
13
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
data/test/fbe/test_just_one.rb
CHANGED
data/test/fbe/test_octo.rb
CHANGED
@@ -662,4 +662,51 @@ class TestOcto < Fbe::Test
|
|
662
662
|
https://github.com/octokit/octokit.rb/blob/ea3413c3174571e87c83d358fc893cc7613091fa/lib/octokit/connection.rb#L109-L119
|
663
663
|
MSG
|
664
664
|
end
|
665
|
+
|
666
|
+
def test_octo_cache_still_available_on_duration_of_age
|
667
|
+
WebMock.disable_net_connect!
|
668
|
+
now = Time.now
|
669
|
+
age = 60
|
670
|
+
stub_request(:get, 'https://api.github.com/rate_limit')
|
671
|
+
.to_return(
|
672
|
+
status: 200, headers: { 'Content-Type' => 'application/json', 'X-RateLimit-Remaining' => '5000' },
|
673
|
+
body: { 'rate' => { 'limit' => 5000, 'remaining' => 5000, 'reset' => 1_672_531_200 } }.to_json
|
674
|
+
)
|
675
|
+
Dir.mktmpdir do |dir|
|
676
|
+
sqlite_cache = File.expand_path('t.db', dir)
|
677
|
+
o = Fbe.octo(loog: fake_loog, global: {}, options: Judges::Options.new({ 'sqlite_cache' => sqlite_cache }))
|
678
|
+
stub_request(:get, 'https://api.github.com/repositories/798641472').to_return(
|
679
|
+
status: 200,
|
680
|
+
body: { id: 798_641_472, name: 'factbase' }.to_json,
|
681
|
+
headers: {
|
682
|
+
'Date' => now.httpdate,
|
683
|
+
'Content-Type' => 'application/json; charset=utf-8',
|
684
|
+
'Cache-Control' => "public, max-age=#{age}, s-maxage=#{age}",
|
685
|
+
'Etag' => 'W/"f5f1ea995fd7266816f681aca5a81f539420c469070a47568bebdaa3055487bc"',
|
686
|
+
'Last-Modified' => 'Fri, 04 Jul 2025 13:39:42 GMT'
|
687
|
+
}
|
688
|
+
).times(1).then.to_raise('no more request to /repositories/798641472')
|
689
|
+
assert_equal('factbase', o.repo(798_641_472)['name'])
|
690
|
+
Time.stub(:now, now + age - 1) do
|
691
|
+
assert_equal('factbase', o.repo(798_641_472)['name'])
|
692
|
+
end
|
693
|
+
stub_request(:get, 'https://api.github.com/repositories/798641472').to_return(
|
694
|
+
status: 200,
|
695
|
+
body: { id: 798_641_472, name: 'factbase_changed' }.to_json,
|
696
|
+
headers: {
|
697
|
+
'Date' => (now + age).httpdate,
|
698
|
+
'Content-Type' => 'application/json; charset=utf-8',
|
699
|
+
'Cache-Control' => "public, max-age=#{age}, s-maxage=#{age}",
|
700
|
+
'Etag' => 'W/"f5f1ea995fd7266816f681aca5a81f539420c469070a47568bebdaa3055487be"',
|
701
|
+
'Last-Modified' => 'Fri, 04 Jul 2025 13:39:42 GMT'
|
702
|
+
}
|
703
|
+
).times(1).then.to_raise('no more request to /repositories/798641472')
|
704
|
+
Time.stub(:now, now + age) do
|
705
|
+
assert_equal('factbase_changed', o.repo(798_641_472)['name'])
|
706
|
+
end
|
707
|
+
Time.stub(:now, now + (2 * age) - 1) do
|
708
|
+
assert_equal('factbase_changed', o.repo(798_641_472)['name'])
|
709
|
+
end
|
710
|
+
end
|
711
|
+
end
|
665
712
|
end
|
data/test/test__helper.rb
CHANGED
@@ -32,8 +32,8 @@ require 'minitest/reporters'
|
|
32
32
|
Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]
|
33
33
|
|
34
34
|
require 'loog'
|
35
|
-
require 'minitest/stub_const'
|
36
35
|
require 'minitest/autorun'
|
36
|
+
require 'minitest/stub_const'
|
37
37
|
require 'webmock/minitest'
|
38
38
|
require_relative '../lib/fbe'
|
39
39
|
|
data/test/test_fbe.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 Zerocracy
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
|
-
require_relative 'test__helper'
|
7
6
|
require_relative '../lib/fbe'
|
7
|
+
require_relative 'test__helper'
|
8
8
|
|
9
9
|
# Main module test.
|
10
10
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|