nexus_mods 2.0.1 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/lib/nexus_mods/api/mod_updates.rb +49 -0
- data/lib/nexus_mods/api_client.rb +55 -16
- data/lib/nexus_mods/cacheable_with_expiry.rb +1 -1
- data/lib/nexus_mods/core_extensions/cacheable/cache_adapters/persistent_json_adapter.rb +2 -4
- data/lib/nexus_mods/version.rb +1 -1
- data/lib/nexus_mods.rb +92 -1
- data/spec/nexus_mods_test/factories/games.rb +10 -2
- data/spec/nexus_mods_test/factories/mod_files.rb +12 -4
- data/spec/nexus_mods_test/factories/mod_updates.rb +57 -0
- data/spec/nexus_mods_test/factories/mods.rb +10 -2
- data/spec/nexus_mods_test/helpers.rb +2 -0
- data/spec/nexus_mods_test/scenarios/nexus_mods/api/mod_updates_spec.rb +91 -0
- data/spec/nexus_mods_test/scenarios/nexus_mods/nexus_mods_caching_spec.rb +236 -183
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a25aa2568a5be1e1a8592196344fde1ca68bcf814b67a51cf6921f7ed64c0f70
|
4
|
+
data.tar.gz: cdb50c9b1394af620109ea82439a3254b96ad223286ae1f797fcbc37c32a1a58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7f49167041ee67f3855c0a474cdba90a744660f424356943516016628f1e65f37154c7c6b596eca2b7ac2430eae4b0726db6a45d3685b750bdf7a33e8674d2e
|
7
|
+
data.tar.gz: ce904391c82e93f029a41a47d3e7e391af8dac23625ac38eda01b2066ea9261ae3ff7bbea2aed23fbbba9b124846be80640bdbbf94ef98291b1b020824ec74df
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
# [v2.2.0](https://github.com/Muriel-Salvan/nexus_mods/compare/v2.1.0...v2.2.0) (2023-04-12 12:56:31)
|
2
|
+
|
3
|
+
### Features
|
4
|
+
|
5
|
+
* [[Feature] Add possibility to retrieve the cache timestamp of API resources](https://github.com/Muriel-Salvan/nexus_mods/commit/ba67323a32472deb1f063a51683ff9282ef96981)
|
6
|
+
|
7
|
+
# [v2.1.0](https://github.com/Muriel-Salvan/nexus_mods/compare/v2.0.1...v2.1.0) (2023-04-11 18:24:04)
|
8
|
+
|
9
|
+
### Features
|
10
|
+
|
11
|
+
* [[Feature] Add API entry point to retrieve last updated mods](https://github.com/Muriel-Salvan/nexus_mods/commit/cf702fc2c146cc7d61aadca005aa0e784b807c44)
|
12
|
+
|
1
13
|
# [v2.0.1](https://github.com/Muriel-Salvan/nexus_mods/compare/v2.0.0...v2.0.1) (2023-04-11 10:22:54)
|
2
14
|
|
3
15
|
### Patches
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class NexusMods
|
2
|
+
|
3
|
+
module Api
|
4
|
+
|
5
|
+
# A NexusMods mod updates.
|
6
|
+
class ModUpdates
|
7
|
+
|
8
|
+
attr_reader(
|
9
|
+
*%i[
|
10
|
+
mod_id
|
11
|
+
latest_file_update
|
12
|
+
latest_mod_activity
|
13
|
+
]
|
14
|
+
)
|
15
|
+
|
16
|
+
# Constructor
|
17
|
+
#
|
18
|
+
# Parameters::
|
19
|
+
# * *mod_id* (Integer): The mod's id
|
20
|
+
# * *latest_file_update* (Time): The mod's latest file update
|
21
|
+
# * *latest_mod_activity* (Time): The mod's latest activity
|
22
|
+
def initialize(
|
23
|
+
mod_id:,
|
24
|
+
latest_file_update:,
|
25
|
+
latest_mod_activity:
|
26
|
+
)
|
27
|
+
@mod_id = mod_id
|
28
|
+
@latest_file_update = latest_file_update
|
29
|
+
@latest_mod_activity = latest_mod_activity
|
30
|
+
end
|
31
|
+
|
32
|
+
# Equality operator
|
33
|
+
#
|
34
|
+
# Parameters::
|
35
|
+
# * *other* (Object): Other object to compare with
|
36
|
+
# Result::
|
37
|
+
# * Boolean: Are objects equal?
|
38
|
+
def ==(other)
|
39
|
+
other.is_a?(ModUpdates) &&
|
40
|
+
@mod_id == other.mod_id &&
|
41
|
+
@latest_file_update == other.latest_file_update &&
|
42
|
+
@latest_mod_activity == other.latest_mod_activity
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -50,25 +50,43 @@ class NexusMods
|
|
50
50
|
#
|
51
51
|
# Parameters::
|
52
52
|
# * *path* (String): API path to contact (from v1/ and without .json)
|
53
|
+
# * *parameters* (Hash<Symbol,Object>): Optional parameters to add to the path [default: {}]
|
53
54
|
# * *verb* (Symbol): Verb to be used (:get, :post...) [default: :get]
|
54
55
|
# * *clear_cache* (Boolean): Should we clear the API cache for this resource? [default: false]
|
55
56
|
# Result::
|
56
57
|
# * Object: The JSON response
|
57
|
-
def api(path, verb: :get, clear_cache: false)
|
58
|
-
clear_cached_api_cache(path, verb:) if clear_cache
|
59
|
-
cached_api(path, verb:)
|
58
|
+
def api(path, parameters: {}, verb: :get, clear_cache: false)
|
59
|
+
clear_cached_api_cache(path, parameters:, verb:) if clear_cache
|
60
|
+
cached_api(path, parameters:, verb:)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Get the timestamp of the cached data linked to a given API call
|
64
|
+
#
|
65
|
+
# Parameters::
|
66
|
+
# * *path* (String): API path to contact (from v1/ and without .json)
|
67
|
+
# * *parameters* (Hash<Symbol,Object>): Optional parameters to add to the path [default: {}]
|
68
|
+
# * *verb* (Symbol): Verb to be used (:get, :post...) [default: :get]
|
69
|
+
# Result::
|
70
|
+
# * Time or nil: The refresh time of the data, or nil if not part of the cache
|
71
|
+
def api_cache_timestamp(path, parameters: {}, verb: :get)
|
72
|
+
key = ApiClient.cache_key(path, parameters:, verb:)
|
73
|
+
return unless Cacheable.cache_adapter.exist?(key)
|
74
|
+
|
75
|
+
str_time = Cacheable.cache_adapter.context.dig(key, 'invalidate_time')
|
76
|
+
str_time.nil? ? nil : Time.parse(str_time)
|
60
77
|
end
|
61
78
|
|
62
79
|
# Send an HTTP request to the API and get back the HTTP response
|
63
80
|
#
|
64
81
|
# Parameters::
|
65
82
|
# * *path* (String): API path to contact (from v1/ and without .json)
|
83
|
+
# * *parameters* (Hash<Symbol,Object>): Optional parameters to add to the path [default: {}]
|
66
84
|
# * *verb* (Symbol): Verb to be used (:get, :post...) [default: :get]
|
67
85
|
# Result::
|
68
86
|
# * Faraday::Response: The HTTP response
|
69
|
-
def http(path, verb: :get)
|
87
|
+
def http(path, parameters: {}, verb: :get)
|
70
88
|
@http_client.send(verb) do |req|
|
71
|
-
req.url api_uri(path)
|
89
|
+
req.url api_uri(path, parameters:)
|
72
90
|
req.headers['apikey'] = @api_key
|
73
91
|
req.headers['User-Agent'] = "nexus_mods/#{NexusMods::VERSION} (#{RUBY_PLATFORM}) Ruby/#{RUBY_VERSION}"
|
74
92
|
end
|
@@ -97,6 +115,18 @@ class NexusMods
|
|
97
115
|
# ApiClient: The API client to be used by the cacheable adapter (singleton pattern)
|
98
116
|
attr_accessor :api_client
|
99
117
|
|
118
|
+
# Get the cache key to be used for a given API query
|
119
|
+
#
|
120
|
+
# Parameters::
|
121
|
+
# * *path* (String): API path to contact (from v1/ and without .json)
|
122
|
+
# * *parameters* (Hash<Symbol,Object>): Optional parameters to add to the path [default: {}]
|
123
|
+
# * *verb* (Symbol): Verb to be used (:get, :post...) [default: :get]
|
124
|
+
# Result::
|
125
|
+
# * String: The corresponding cache key
|
126
|
+
def cache_key(path, parameters:, verb:)
|
127
|
+
"#{verb}/#{path}#{parameters.empty? ? '' : "/#{parameters.map { |param, value| "#{param}=#{value}" }.sort.join('/')}"}"
|
128
|
+
end
|
129
|
+
|
100
130
|
end
|
101
131
|
|
102
132
|
@api_client = nil
|
@@ -106,11 +136,12 @@ class NexusMods
|
|
106
136
|
#
|
107
137
|
# Parameters::
|
108
138
|
# * *path* (String): API path to contact (from v1/ and without .json)
|
139
|
+
# * *parameters* (Hash<Symbol,Object>): Optional parameters to add to the path [default: {}]
|
109
140
|
# * *verb* (Symbol): Verb to be used (:get, :post...) [default: :get]
|
110
141
|
# Result::
|
111
142
|
# * Object: The JSON response
|
112
|
-
def cached_api(path, verb: :get)
|
113
|
-
res = http(path, verb:)
|
143
|
+
def cached_api(path, parameters: {}, verb: :get)
|
144
|
+
res = http(path, parameters:, verb:)
|
114
145
|
json = JSON.parse(res.body)
|
115
146
|
uri = api_uri(path)
|
116
147
|
@logger.debug "[API call] - #{verb} #{uri} => #{res.status}\n#{
|
@@ -139,11 +170,12 @@ class NexusMods
|
|
139
170
|
cacheable_api(
|
140
171
|
:cached_api,
|
141
172
|
key_format: proc do |_target, _method_name, method_args, method_kwargs|
|
142
|
-
|
173
|
+
cache_key(method_args.first, parameters: method_kwargs[:parameters], verb: method_kwargs[:verb])
|
143
174
|
end,
|
144
175
|
expiry_from_key: proc do |key|
|
145
176
|
# Example of keys:
|
146
177
|
# get/games
|
178
|
+
# get/games/skyrimspecialedition/mods/updated/period=1d
|
147
179
|
# get/games/skyrimspecialedition/mods/2014
|
148
180
|
# get/games/skyrimspecialedition/mods/2014/files
|
149
181
|
# get/users/validate
|
@@ -155,13 +187,19 @@ class NexusMods
|
|
155
187
|
else
|
156
188
|
case key_components[2]
|
157
189
|
when 'mods'
|
158
|
-
case key_components[
|
159
|
-
when
|
160
|
-
|
161
|
-
|
162
|
-
ApiClient.api_client.api_cache_expiry[:mod_files]
|
190
|
+
case key_components[3]
|
191
|
+
when 'updated'
|
192
|
+
# According to the API doc, this is updated every 5 minutes
|
193
|
+
5 * 60
|
163
194
|
else
|
164
|
-
|
195
|
+
case key_components[4]
|
196
|
+
when nil
|
197
|
+
ApiClient.api_client.api_cache_expiry[:mod]
|
198
|
+
when 'files'
|
199
|
+
ApiClient.api_client.api_cache_expiry[:mod_files]
|
200
|
+
else
|
201
|
+
raise "Unknown API path: #{key}"
|
202
|
+
end
|
165
203
|
end
|
166
204
|
else
|
167
205
|
raise "Unknown API path: #{key}"
|
@@ -183,10 +221,11 @@ class NexusMods
|
|
183
221
|
#
|
184
222
|
# Parameters::
|
185
223
|
# * *path* (String): API path to contact (from v1/ and without .json)
|
224
|
+
# * *parameters* (Hash<Symbol,Object>): Optional parameters to add to the path [default: {}]
|
186
225
|
# Result::
|
187
226
|
# * String: The URI
|
188
|
-
def api_uri(path)
|
189
|
-
"https://api.nexusmods.com/v1/#{path}.json"
|
227
|
+
def api_uri(path, parameters: {})
|
228
|
+
"https://api.nexusmods.com/v1/#{path}.json#{parameters.empty? ? '' : "?#{parameters.map { |param, value| "#{param}=#{value}" }.join('&')}"}"
|
190
229
|
end
|
191
230
|
|
192
231
|
end
|
@@ -45,7 +45,7 @@ class NexusMods
|
|
45
45
|
expiry_cache[key].nil? || (Time.now.utc - Time.parse(context['invalidate_time']).utc > expiry_cache[key])
|
46
46
|
end,
|
47
47
|
update_context_after_fetch: proc do |_key, _value, _options, context|
|
48
|
-
context['invalidate_time'] = Time.now.utc.strftime('%FT%
|
48
|
+
context['invalidate_time'] = Time.now.utc.strftime('%FT%T.%9NUTC')
|
49
49
|
end
|
50
50
|
}
|
51
51
|
)
|
@@ -13,6 +13,8 @@ module Cacheable
|
|
13
13
|
# * The context information is JSON serializable.
|
14
14
|
class PersistentJsonAdapter < MemoryAdapter
|
15
15
|
|
16
|
+
attr_reader :context
|
17
|
+
|
16
18
|
# Fetch a key with the givien cache options
|
17
19
|
#
|
18
20
|
# Parameters::
|
@@ -87,10 +89,6 @@ module Cacheable
|
|
87
89
|
@context = loaded_content['context']
|
88
90
|
end
|
89
91
|
|
90
|
-
private
|
91
|
-
|
92
|
-
attr_reader :context
|
93
|
-
|
94
92
|
end
|
95
93
|
|
96
94
|
end
|
data/lib/nexus_mods/version.rb
CHANGED
data/lib/nexus_mods.rb
CHANGED
@@ -8,6 +8,7 @@ require 'nexus_mods/api/game'
|
|
8
8
|
require 'nexus_mods/api/user'
|
9
9
|
require 'nexus_mods/api/mod'
|
10
10
|
require 'nexus_mods/api/mod_file'
|
11
|
+
require 'nexus_mods/api/mod_updates'
|
11
12
|
|
12
13
|
# Ruby API to access NexusMods REST API
|
13
14
|
class NexusMods
|
@@ -130,7 +131,7 @@ class NexusMods
|
|
130
131
|
nexusmods_url: game_json['nexusmods_url'],
|
131
132
|
genre: game_json['genre'],
|
132
133
|
domain_name: game_json['domain_name'],
|
133
|
-
approved_date: Time.at(game_json['approved_date']),
|
134
|
+
approved_date: Time.at(game_json['approved_date']).utc,
|
134
135
|
files_count: game_json['file_count'],
|
135
136
|
files_views: game_json['file_views'],
|
136
137
|
files_endorsements: game_json['file_endorsements'],
|
@@ -142,6 +143,14 @@ class NexusMods
|
|
142
143
|
end
|
143
144
|
end
|
144
145
|
|
146
|
+
# Get the cached timestamp of the list of games
|
147
|
+
#
|
148
|
+
# Result::
|
149
|
+
# * Time or nil: Freshness time of the data in the API cache, or nil if not present in the cache
|
150
|
+
def games_cache_timestamp
|
151
|
+
@api_client.api_cache_timestamp('games')
|
152
|
+
end
|
153
|
+
|
145
154
|
# Get information about a mod
|
146
155
|
#
|
147
156
|
# Parameters::
|
@@ -182,6 +191,17 @@ class NexusMods
|
|
182
191
|
)
|
183
192
|
end
|
184
193
|
|
194
|
+
# Get the cached timestamp of a mod information
|
195
|
+
#
|
196
|
+
# Parameters::
|
197
|
+
# * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
|
198
|
+
# * *mod_id* (Integer): The mod ID [default: @mod_id]
|
199
|
+
# Result::
|
200
|
+
# * Time or nil: Freshness time of the data in the API cache, or nil if not present in the cache
|
201
|
+
def mod_cache_timestamp(game_domain_name: @game_domain_name, mod_id: @mod_id)
|
202
|
+
@api_client.api_cache_timestamp("games/#{game_domain_name}/mods/#{mod_id}")
|
203
|
+
end
|
204
|
+
|
185
205
|
# Get files belonging to a mod
|
186
206
|
#
|
187
207
|
# Parameters::
|
@@ -213,4 +233,75 @@ class NexusMods
|
|
213
233
|
end
|
214
234
|
end
|
215
235
|
|
236
|
+
# Get the cached timestamp of a mod files information
|
237
|
+
#
|
238
|
+
# Parameters::
|
239
|
+
# * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
|
240
|
+
# * *mod_id* (Integer): The mod ID [default: @mod_id]
|
241
|
+
# Result::
|
242
|
+
# * Time or nil: Freshness time of the data in the API cache, or nil if not present in the cache
|
243
|
+
def mod_files_cache_timestamp(game_domain_name: @game_domain_name, mod_id: @mod_id)
|
244
|
+
@api_client.api_cache_timestamp("games/#{game_domain_name}/mods/#{mod_id}/files")
|
245
|
+
end
|
246
|
+
|
247
|
+
# Get a list of updated mod ids since a given time
|
248
|
+
#
|
249
|
+
# Parameters::
|
250
|
+
# * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
|
251
|
+
# * *since* (Symbol): The time from which we look for updated mods [default: :one_day]
|
252
|
+
# Possible values are:
|
253
|
+
# * *one_day*: Since 1 day
|
254
|
+
# * *one_week*: Since 1 week
|
255
|
+
# * *one_month*: Since 1 month
|
256
|
+
# * *clear_cache* (Boolean): Should we clear the API cache for this resource? [default: false]
|
257
|
+
# Result::
|
258
|
+
# * Array<ModUpdates>: Mod's updates information
|
259
|
+
def updated_mods(game_domain_name: @game_domain_name, since: :one_day, clear_cache: false)
|
260
|
+
@api_client.api("games/#{game_domain_name}/mods/updated", parameters: period_to_url_params(since), clear_cache:).map do |updated_mod_json|
|
261
|
+
Api::ModUpdates.new(
|
262
|
+
mod_id: updated_mod_json['mod_id'],
|
263
|
+
latest_file_update: Time.at(updated_mod_json['latest_file_update']).utc,
|
264
|
+
latest_mod_activity: Time.at(updated_mod_json['latest_mod_activity']).utc
|
265
|
+
)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# Get the cached timestamp of a mod files information
|
270
|
+
#
|
271
|
+
# Parameters::
|
272
|
+
# * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
|
273
|
+
# * *since* (Symbol): The time from which we look for updated mods [default: :one_day]
|
274
|
+
# Possible values are:
|
275
|
+
# * *one_day*: Since 1 day
|
276
|
+
# * *one_week*: Since 1 week
|
277
|
+
# * *one_month*: Since 1 month
|
278
|
+
# Result::
|
279
|
+
# * Time or nil: Freshness time of the data in the API cache, or nil if not present in the cache
|
280
|
+
def updated_mods_cache_timestamp(game_domain_name: @game_domain_name, since: :one_day)
|
281
|
+
@api_client.api_cache_timestamp("games/#{game_domain_name}/mods/updated", parameters: period_to_url_params(since))
|
282
|
+
end
|
283
|
+
|
284
|
+
private
|
285
|
+
|
286
|
+
# Get the URL parameters from the required period
|
287
|
+
#
|
288
|
+
# Parameters::
|
289
|
+
# * *since* (Symbol): The time from which we look for updated mods
|
290
|
+
# Possible values are:
|
291
|
+
# * *one_day*: Since 1 day
|
292
|
+
# * *one_week*: Since 1 week
|
293
|
+
# * *one_month*: Since 1 month
|
294
|
+
# Result::
|
295
|
+
# * Hash<Symbol,Object>: Corresponding URL parameters
|
296
|
+
def period_to_url_params(since)
|
297
|
+
nexus_mods_period = {
|
298
|
+
one_day: '1d',
|
299
|
+
one_week: '1w',
|
300
|
+
one_month: '1m'
|
301
|
+
}[since]
|
302
|
+
raise "Unknown time stamp: #{since}" if nexus_mods_period.nil?
|
303
|
+
|
304
|
+
{ period: nexus_mods_period }
|
305
|
+
end
|
306
|
+
|
216
307
|
end
|
@@ -5,7 +5,7 @@ module NexusModsTest
|
|
5
5
|
module Games
|
6
6
|
|
7
7
|
# Test game with id 100
|
8
|
-
def json_game100
|
8
|
+
def self.json_game100
|
9
9
|
{
|
10
10
|
'id' => 100,
|
11
11
|
'name' => 'Morrowind',
|
@@ -35,8 +35,12 @@ module NexusModsTest
|
|
35
35
|
}
|
36
36
|
end
|
37
37
|
|
38
|
+
def json_game100
|
39
|
+
Games.json_game100
|
40
|
+
end
|
41
|
+
|
38
42
|
# Test game with id 101
|
39
|
-
def json_game101
|
43
|
+
def self.json_game101
|
40
44
|
{
|
41
45
|
'id' => 101,
|
42
46
|
'name' => 'Oblivion',
|
@@ -66,6 +70,10 @@ module NexusModsTest
|
|
66
70
|
}
|
67
71
|
end
|
68
72
|
|
73
|
+
def json_game101
|
74
|
+
Games.json_game101
|
75
|
+
end
|
76
|
+
|
69
77
|
# Expect a game to be the test game of id 100
|
70
78
|
#
|
71
79
|
# Parameters::
|
@@ -5,7 +5,7 @@ module NexusModsTest
|
|
5
5
|
module ModFiles
|
6
6
|
|
7
7
|
# Test mod file with id 2472
|
8
|
-
def json_mod_file2472
|
8
|
+
def self.json_mod_file2472
|
9
9
|
{
|
10
10
|
'id' => [
|
11
11
|
2472,
|
@@ -32,8 +32,12 @@ module NexusModsTest
|
|
32
32
|
}
|
33
33
|
end
|
34
34
|
|
35
|
+
def json_mod_file2472
|
36
|
+
ModFiles.json_mod_file2472
|
37
|
+
end
|
38
|
+
|
35
39
|
# Test mod file with id 2487
|
36
|
-
def json_mod_file2487
|
40
|
+
def self.json_mod_file2487
|
37
41
|
{
|
38
42
|
'id' => [
|
39
43
|
2487,
|
@@ -60,10 +64,14 @@ module NexusModsTest
|
|
60
64
|
}
|
61
65
|
end
|
62
66
|
|
67
|
+
def json_mod_file2487
|
68
|
+
ModFiles.json_mod_file2487
|
69
|
+
end
|
70
|
+
|
63
71
|
# Expect a mod's file to be the example one with id 2472
|
64
72
|
#
|
65
73
|
# Parameters::
|
66
|
-
# * *mod_file* (NexusMods::Api::
|
74
|
+
# * *mod_file* (NexusMods::Api::ModFile): Mod file to validate
|
67
75
|
def expect_mod_file_to_be2472(mod_file)
|
68
76
|
expect(mod_file.ids).to eq [2472, 1704]
|
69
77
|
expect(mod_file.uid).to eq 7_318_624_274_856
|
@@ -87,7 +95,7 @@ module NexusModsTest
|
|
87
95
|
# Expect a mod's file to be the example one with id 2487
|
88
96
|
#
|
89
97
|
# Parameters::
|
90
|
-
# * *mod_file* (NexusMods::Api::
|
98
|
+
# * *mod_file* (NexusMods::Api::ModFile): Mod file to validate
|
91
99
|
def expect_mod_file_to_be2487(mod_file)
|
92
100
|
expect(mod_file.ids).to eq [2487, 1705]
|
93
101
|
expect(mod_file.uid).to eq 7_318_624_274_857
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module NexusModsTest
|
2
|
+
|
3
|
+
module Factories
|
4
|
+
|
5
|
+
module ModUpdates
|
6
|
+
|
7
|
+
# Test mod updates with id 2014
|
8
|
+
def self.json_mod_updates2014
|
9
|
+
{
|
10
|
+
'mod_id' => 2014,
|
11
|
+
'latest_file_update' => 1_655_813_855,
|
12
|
+
'latest_mod_activity' => 1_681_169_675
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def json_mod_updates2014
|
17
|
+
ModUpdates.json_mod_updates2014
|
18
|
+
end
|
19
|
+
|
20
|
+
# Test mod updates with id 100
|
21
|
+
def self.json_mod_updates100
|
22
|
+
{
|
23
|
+
'mod_id' => 100,
|
24
|
+
'latest_file_update' => 1_681_143_964,
|
25
|
+
'latest_mod_activity' => 1_681_143_964
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def json_mod_updates100
|
30
|
+
ModUpdates.json_mod_updates100
|
31
|
+
end
|
32
|
+
|
33
|
+
# Expect a mod's updates to be the example one with id 2014
|
34
|
+
#
|
35
|
+
# Parameters::
|
36
|
+
# * *mod_updates* (NexusMods::Api::ModUpdates): Mod updates to validate
|
37
|
+
def expect_mod_file_to_be2014(mod_updates)
|
38
|
+
expect(mod_updates.mod_id).to eq 2014
|
39
|
+
expect(mod_updates.latest_file_update).to eq Time.parse('2022-06-21 12:17:35 UTC')
|
40
|
+
expect(mod_updates.latest_mod_activity).to eq Time.parse('2023-04-10 23:34:35 UTC')
|
41
|
+
end
|
42
|
+
|
43
|
+
# Expect a mod's updates to be the example one with id 100
|
44
|
+
#
|
45
|
+
# Parameters::
|
46
|
+
# * *mod_updates* (NexusMods::Api::ModUpdates): Mod updates to validate
|
47
|
+
def expect_mod_file_to_be100(mod_updates)
|
48
|
+
expect(mod_updates.mod_id).to eq 100
|
49
|
+
expect(mod_updates.latest_file_update).to eq Time.parse('2023-04-10 16:26:04 UTC')
|
50
|
+
expect(mod_updates.latest_mod_activity).to eq Time.parse('2023-04-10 16:26:04 UTC')
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -5,7 +5,7 @@ module NexusModsTest
|
|
5
5
|
module Mods
|
6
6
|
|
7
7
|
# Example of JSON object returned by the API for a mod information, having all possible fields
|
8
|
-
def json_complete_mod
|
8
|
+
def self.json_complete_mod
|
9
9
|
{
|
10
10
|
'name' => 'ApachiiSkyHair SSE',
|
11
11
|
'summary' => 'New Female and Male Hairstyles for Humans, Elves and Orcs. Converted hair from Sims2 and Sims3.<br />Standalone version.',
|
@@ -44,8 +44,12 @@ module NexusModsTest
|
|
44
44
|
}
|
45
45
|
end
|
46
46
|
|
47
|
+
def json_complete_mod
|
48
|
+
Mods.json_complete_mod
|
49
|
+
end
|
50
|
+
|
47
51
|
# Example of JSON object returned by the API for a mod information, having minimum fields
|
48
|
-
def json_partial_mod
|
52
|
+
def self.json_partial_mod
|
49
53
|
{
|
50
54
|
'mod_downloads' => 13_634_545,
|
51
55
|
'mod_unique_downloads' => 4_052_221,
|
@@ -75,6 +79,10 @@ module NexusModsTest
|
|
75
79
|
}
|
76
80
|
end
|
77
81
|
|
82
|
+
def json_partial_mod
|
83
|
+
Mods.json_partial_mod
|
84
|
+
end
|
85
|
+
|
78
86
|
# Expect a mod to be the example complete one
|
79
87
|
#
|
80
88
|
# Parameters::
|
@@ -4,6 +4,7 @@ require 'rspec/support/object_formatter'
|
|
4
4
|
require 'nexus_mods_test/factories/games'
|
5
5
|
require 'nexus_mods_test/factories/mods'
|
6
6
|
require 'nexus_mods_test/factories/mod_files'
|
7
|
+
require 'nexus_mods_test/factories/mod_updates'
|
7
8
|
require 'nexus_mods'
|
8
9
|
|
9
10
|
module NexusModsTest
|
@@ -180,6 +181,7 @@ RSpec.configure do |config|
|
|
180
181
|
config.include NexusModsTest::Factories::Games
|
181
182
|
config.include NexusModsTest::Factories::Mods
|
182
183
|
config.include NexusModsTest::Factories::ModFiles
|
184
|
+
config.include NexusModsTest::Factories::ModUpdates
|
183
185
|
config.before do
|
184
186
|
@nexus_mods = nil
|
185
187
|
# Reload the ApiClient as it stores caches at class level
|
@@ -0,0 +1,91 @@
|
|
1
|
+
describe NexusMods::Api::ModUpdates do
|
2
|
+
|
3
|
+
context 'when testing mod updates' do
|
4
|
+
|
5
|
+
before do
|
6
|
+
expect_validate_user
|
7
|
+
end
|
8
|
+
|
9
|
+
# Expect the given array of mod updates to be the ones of examples (for mods 100 and 2014)
|
10
|
+
#
|
11
|
+
# Parameters::
|
12
|
+
# * *mod_updates* (Array<NexusMods::Api::ModUpdates>): The list of mod updates to validate
|
13
|
+
def expect_mod_updates_to_be_example(mod_updates)
|
14
|
+
sorted_mod_updates = mod_updates.sort_by(&:mod_id)
|
15
|
+
expect_mod_file_to_be100(sorted_mod_updates.first)
|
16
|
+
expect_mod_file_to_be2014(sorted_mod_updates[1])
|
17
|
+
end
|
18
|
+
|
19
|
+
{
|
20
|
+
'last day' => {
|
21
|
+
since: :one_day,
|
22
|
+
expected_url_params: 'period=1d'
|
23
|
+
},
|
24
|
+
'last week' => {
|
25
|
+
since: :one_week,
|
26
|
+
expected_url_params: 'period=1w'
|
27
|
+
},
|
28
|
+
'last month' => {
|
29
|
+
since: :one_month,
|
30
|
+
expected_url_params: 'period=1m'
|
31
|
+
}
|
32
|
+
}.each do |since, since_config|
|
33
|
+
|
34
|
+
context "when testing updated months since #{since}" do
|
35
|
+
|
36
|
+
it 'returns updated mods' do
|
37
|
+
expect_http_call_to(
|
38
|
+
path: "/v1/games/skyrimspecialedition/mods/updated.json?#{since_config[:expected_url_params]}",
|
39
|
+
json: [
|
40
|
+
json_mod_updates2014,
|
41
|
+
json_mod_updates100
|
42
|
+
]
|
43
|
+
)
|
44
|
+
expect_mod_updates_to_be_example(nexus_mods.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since]))
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'returns updated mods information for the default game' do
|
48
|
+
expect_http_call_to(
|
49
|
+
path: "/v1/games/skyrimspecialedition/mods/updated.json?#{since_config[:expected_url_params]}",
|
50
|
+
json: [
|
51
|
+
json_mod_updates2014,
|
52
|
+
json_mod_updates100
|
53
|
+
]
|
54
|
+
)
|
55
|
+
expect_mod_updates_to_be_example(nexus_mods(game_domain_name: 'skyrimspecialedition').updated_mods(since: since_config[:since]))
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'returns updated mods information for the default game set using accessor' do
|
59
|
+
expect_http_call_to(
|
60
|
+
path: "/v1/games/skyrimspecialedition/mods/updated.json?#{since_config[:expected_url_params]}",
|
61
|
+
json: [
|
62
|
+
json_mod_updates2014,
|
63
|
+
json_mod_updates100
|
64
|
+
]
|
65
|
+
)
|
66
|
+
nexus_mods.game_domain_name = 'skyrimspecialedition'
|
67
|
+
expect_mod_updates_to_be_example(nexus_mods.updated_mods(since: since_config[:since]))
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'compares objects for equality' do
|
71
|
+
expect_http_call_to(
|
72
|
+
path: "/v1/games/skyrimspecialedition/mods/updated.json?#{since_config[:expected_url_params]}",
|
73
|
+
json: [json_mod_updates2014]
|
74
|
+
)
|
75
|
+
mod_updates1 = nexus_mods.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since])
|
76
|
+
mod_updates2 = nexus_mods.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since])
|
77
|
+
expect(mod_updates1.object_id).not_to eq mod_updates2.object_id
|
78
|
+
expect(mod_updates1).to eq mod_updates2
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'fails to fetch updated mods for an unknown period' do
|
86
|
+
expect { nexus_mods.updated_mods(game_domain_name: 'skyrimspecialedition', since: :unknown_since) }.to raise_error 'Unknown time stamp: unknown_since'
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -10,115 +10,245 @@ describe NexusMods do
|
|
10
10
|
nexus_mods.api_limits
|
11
11
|
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
{
|
14
|
+
'games' => {
|
15
|
+
expected_api_path: '/v1/games.json',
|
16
|
+
mocked_api_json: [
|
17
|
+
NexusModsTest::Factories::Games.json_game100,
|
18
|
+
NexusModsTest::Factories::Games.json_game101
|
19
|
+
],
|
20
|
+
query: proc { |nm| nm.games },
|
21
|
+
query_without_cache: proc { |nm| nm.games(clear_cache: true) },
|
22
|
+
get_cache_timestamp: proc { |nm| nm.games_cache_timestamp },
|
23
|
+
expiry_cache_param: :games
|
24
|
+
},
|
25
|
+
'mods' => {
|
26
|
+
expected_api_path: '/v1/games/skyrimspecialedition/mods/2014.json',
|
27
|
+
mocked_api_json: NexusModsTest::Factories::Mods.json_complete_mod,
|
28
|
+
query: proc { |nm| nm.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014) },
|
29
|
+
query_without_cache: proc { |nm| nm.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014, clear_cache: true) },
|
30
|
+
get_cache_timestamp: proc { |nm| nm.mod_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014) },
|
31
|
+
expiry_cache_param: :mod
|
32
|
+
},
|
33
|
+
'mod files' => {
|
34
|
+
expected_api_path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
|
35
|
+
mocked_api_json: {
|
36
|
+
files: [
|
37
|
+
NexusModsTest::Factories::ModFiles.json_mod_file2472,
|
38
|
+
NexusModsTest::Factories::ModFiles.json_mod_file2487
|
39
|
+
]
|
40
|
+
},
|
41
|
+
query: proc { |nm| nm.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014) },
|
42
|
+
query_without_cache: proc { |nm| nm.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014, clear_cache: true) },
|
43
|
+
get_cache_timestamp: proc { |nm| nm.mod_files_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014) },
|
44
|
+
expiry_cache_param: :mod_files
|
45
|
+
}
|
46
|
+
}.merge(
|
47
|
+
{
|
48
|
+
'last day' => {
|
49
|
+
since: :one_day,
|
50
|
+
expected_url_params: 'period=1d'
|
51
|
+
},
|
52
|
+
'last week' => {
|
53
|
+
since: :one_week,
|
54
|
+
expected_url_params: 'period=1w'
|
55
|
+
},
|
56
|
+
'last month' => {
|
57
|
+
since: :one_month,
|
58
|
+
expected_url_params: 'period=1m'
|
59
|
+
}
|
60
|
+
}.to_h do |since, since_config|
|
61
|
+
[
|
62
|
+
"mod updates since #{since}",
|
63
|
+
{
|
64
|
+
expected_api_path: "/v1/games/skyrimspecialedition/mods/updated.json?#{since_config[:expected_url_params]}",
|
65
|
+
mocked_api_json: [
|
66
|
+
NexusModsTest::Factories::ModUpdates.json_mod_updates2014,
|
67
|
+
NexusModsTest::Factories::ModUpdates.json_mod_updates100
|
68
|
+
],
|
69
|
+
query: proc { |nm| nm.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since]) },
|
70
|
+
query_without_cache: proc { |nm| nm.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since], clear_cache: true) },
|
71
|
+
get_cache_timestamp: proc { |nm| nm.updated_mods_cache_timestamp(game_domain_name: 'skyrimspecialedition', since: since_config[:since]) }
|
72
|
+
}
|
20
73
|
]
|
21
|
-
|
22
|
-
|
23
|
-
expect(nexus_mods.games).to eq(games)
|
24
|
-
end
|
74
|
+
end
|
75
|
+
).each do |resource, resource_config|
|
25
76
|
|
26
|
-
|
27
|
-
expect_validate_user
|
28
|
-
expect_http_call_to(
|
29
|
-
path: '/v1/games.json',
|
30
|
-
json: [
|
31
|
-
json_game100,
|
32
|
-
json_game101
|
33
|
-
],
|
34
|
-
times: 2
|
35
|
-
)
|
36
|
-
games = nexus_mods.games
|
37
|
-
expect(nexus_mods.games(clear_cache: true)).to eq(games)
|
38
|
-
end
|
77
|
+
context "when testing #{resource}" do
|
39
78
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
79
|
+
it 'caches API queries' do
|
80
|
+
expect_validate_user
|
81
|
+
expect_http_call_to(
|
82
|
+
path: resource_config[:expected_api_path],
|
83
|
+
json: resource_config[:mocked_api_json]
|
84
|
+
)
|
85
|
+
resource = resource_config[:query].call(nexus_mods)
|
86
|
+
expect(resource_config[:query].call(nexus_mods)).to eq resource
|
87
|
+
end
|
49
88
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
89
|
+
it 'does not cache API queries if asked' do
|
90
|
+
expect_validate_user
|
91
|
+
expect_http_call_to(
|
92
|
+
path: resource_config[:expected_api_path],
|
93
|
+
json: resource_config[:mocked_api_json],
|
94
|
+
times: 2
|
95
|
+
)
|
96
|
+
resource = resource_config[:query].call(nexus_mods)
|
97
|
+
expect(resource_config[:query_without_cache].call(nexus_mods)).to eq resource
|
98
|
+
end
|
60
99
|
|
61
|
-
|
62
|
-
expect_validate_user
|
63
|
-
expect_http_call_to(
|
64
|
-
path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
|
65
|
-
json: { files: [json_mod_file2472, json_mod_file2487] }
|
66
|
-
)
|
67
|
-
mod_files = nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
|
68
|
-
expect(nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq(mod_files)
|
69
|
-
end
|
100
|
+
if resource_config[:expiry_cache_param]
|
70
101
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
102
|
+
it 'expires API queries cache' do
|
103
|
+
expect_validate_user
|
104
|
+
expect_http_call_to(
|
105
|
+
path: resource_config[:expected_api_path],
|
106
|
+
json: resource_config[:mocked_api_json],
|
107
|
+
times: 2
|
108
|
+
)
|
109
|
+
nexus_mods_instance = nexus_mods(api_cache_expiry: { resource_config[:expiry_cache_param] => 1 })
|
110
|
+
resource = resource_config[:query].call(nexus_mods_instance)
|
111
|
+
sleep 2
|
112
|
+
expect(resource_config[:query].call(nexus_mods_instance)).to eq resource
|
113
|
+
end
|
81
114
|
|
82
|
-
|
83
|
-
expect_validate_user
|
84
|
-
expect_http_call_to(
|
85
|
-
path: '/v1/games.json',
|
86
|
-
json: [
|
87
|
-
json_game100,
|
88
|
-
json_game101
|
89
|
-
],
|
90
|
-
times: 2
|
91
|
-
)
|
92
|
-
nexus_mods_instance = nexus_mods(api_cache_expiry: { games: 1 })
|
93
|
-
games = nexus_mods_instance.games
|
94
|
-
sleep 2
|
95
|
-
expect(nexus_mods_instance.games).to eq(games)
|
96
|
-
end
|
115
|
+
end
|
97
116
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
117
|
+
it 'stores no timestamp of the data stored in the API cache before fetching data' do
|
118
|
+
expect_validate_user
|
119
|
+
expect(resource_config[:get_cache_timestamp].call(nexus_mods)).to be_nil
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'retrieves the timestamp of the data stored in the API cache' do
|
123
|
+
expect_validate_user
|
124
|
+
expect_http_call_to(
|
125
|
+
path: resource_config[:expected_api_path],
|
126
|
+
json: resource_config[:mocked_api_json]
|
127
|
+
)
|
128
|
+
before = Time.now
|
129
|
+
resource_config[:query].call(nexus_mods)
|
130
|
+
after = Time.now
|
131
|
+
expect(resource_config[:get_cache_timestamp].call(nexus_mods)).to be_between(before, after)
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'retrieves the timestamp of the games data stored in the cache even after cache is used' do
|
135
|
+
expect_validate_user
|
136
|
+
expect_http_call_to(
|
137
|
+
path: resource_config[:expected_api_path],
|
138
|
+
json: resource_config[:mocked_api_json]
|
139
|
+
)
|
140
|
+
before = Time.now
|
141
|
+
resource_config[:query].call(nexus_mods)
|
142
|
+
after = Time.now
|
143
|
+
resource_config[:query].call(nexus_mods)
|
144
|
+
expect(resource_config[:get_cache_timestamp].call(nexus_mods)).to be_between(before, after)
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'retrieves the timestamp of the games data stored in the cache even after cache is persisted' do
|
148
|
+
with_api_cache_file do |api_cache_file|
|
149
|
+
expect_validate_user(times: 2)
|
150
|
+
expect_http_call_to(
|
151
|
+
path: resource_config[:expected_api_path],
|
152
|
+
json: resource_config[:mocked_api_json]
|
153
|
+
)
|
154
|
+
before = Time.now
|
155
|
+
resource_config[:query].call(nexus_mods(api_cache_file:))
|
156
|
+
after = Time.now
|
157
|
+
reset_nexus_mods
|
158
|
+
expect(resource_config[:get_cache_timestamp].call(nexus_mods(api_cache_file:))).to be_between(before, after)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'updates the timestamp of the data stored in the API cache' do
|
163
|
+
expect_validate_user
|
164
|
+
expect_http_call_to(
|
165
|
+
path: resource_config[:expected_api_path],
|
166
|
+
json: resource_config[:mocked_api_json],
|
167
|
+
times: 2
|
168
|
+
)
|
169
|
+
resource_config[:query].call(nexus_mods)
|
170
|
+
sleep 1
|
171
|
+
before = Time.now
|
172
|
+
resource_config[:query_without_cache].call(nexus_mods)
|
173
|
+
after = Time.now
|
174
|
+
expect(resource_config[:get_cache_timestamp].call(nexus_mods)).to be_between(before, after)
|
175
|
+
end
|
176
|
+
|
177
|
+
context 'when testing cache persistence in files' do
|
178
|
+
|
179
|
+
it 'persists API cache in a file' do
|
180
|
+
with_api_cache_file do |api_cache_file|
|
181
|
+
expect_validate_user
|
182
|
+
expect_http_call_to(
|
183
|
+
path: resource_config[:expected_api_path],
|
184
|
+
json: resource_config[:mocked_api_json]
|
185
|
+
)
|
186
|
+
resource_config[:query].call(nexus_mods(api_cache_file:))
|
187
|
+
expect(File.exist?(api_cache_file)).to be true
|
188
|
+
expect(File.size(api_cache_file)).to be > 0
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'uses API cache from a file' do
|
193
|
+
with_api_cache_file do |api_cache_file|
|
194
|
+
expect_validate_user(times: 2)
|
195
|
+
expect_http_call_to(
|
196
|
+
path: resource_config[:expected_api_path],
|
197
|
+
json: resource_config[:mocked_api_json]
|
198
|
+
)
|
199
|
+
# Generate the cache first
|
200
|
+
resource = resource_config[:query].call(nexus_mods(api_cache_file:))
|
201
|
+
# Force a new instance of NexusMods API to run
|
202
|
+
reset_nexus_mods
|
203
|
+
expect(resource_config[:query].call(nexus_mods(api_cache_file:))).to eq resource
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
if resource_config[:expiry_cache_param]
|
208
|
+
|
209
|
+
it 'uses API cache from a file, taking expiry time into account' do
|
210
|
+
with_api_cache_file do |api_cache_file|
|
211
|
+
expect_validate_user(times: 2)
|
212
|
+
expect_http_call_to(
|
213
|
+
path: resource_config[:expected_api_path],
|
214
|
+
json: resource_config[:mocked_api_json],
|
215
|
+
times: 2
|
216
|
+
)
|
217
|
+
# Generate the cache first
|
218
|
+
resource = resource_config[:query].call(nexus_mods(api_cache_file:, api_cache_expiry: { resource_config[:expiry_cache_param] => 1 }))
|
219
|
+
# Force a new instance of NexusMods API to run
|
220
|
+
reset_nexus_mods
|
221
|
+
sleep 2
|
222
|
+
# As the expiry time is 1 second, then the cache should still be invalidated
|
223
|
+
expect(resource_config[:query].call(nexus_mods(api_cache_file:, api_cache_expiry: { resource_config[:expiry_cache_param] => 1 }))).to eq resource
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'uses API cache from a file, taking expiry time of the new process into account' do
|
228
|
+
with_api_cache_file do |api_cache_file|
|
229
|
+
expect_validate_user(times: 2)
|
230
|
+
expect_http_call_to(
|
231
|
+
path: resource_config[:expected_api_path],
|
232
|
+
json: resource_config[:mocked_api_json],
|
233
|
+
times: 2
|
234
|
+
)
|
235
|
+
# Generate the cache first
|
236
|
+
resource = resource_config[:query].call(nexus_mods(api_cache_file:, api_cache_expiry: { resource_config[:expiry_cache_param] => 10 }))
|
237
|
+
# Force a new instance of NexusMods API to run
|
238
|
+
reset_nexus_mods
|
239
|
+
sleep 2
|
240
|
+
# Even if the expiry time was 10 seconds while fetching the resource,
|
241
|
+
# if we decide it has to be 1 second now then it has to be invalidated.
|
242
|
+
expect(resource_config[:query].call(nexus_mods(api_cache_file:, api_cache_expiry: { resource_config[:expiry_cache_param] => 1 }))).to eq resource
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
110
251
|
|
111
|
-
it 'expires mod files queries cache' do
|
112
|
-
expect_validate_user
|
113
|
-
expect_http_call_to(
|
114
|
-
path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
|
115
|
-
json: { files: [json_mod_file2472, json_mod_file2487] },
|
116
|
-
times: 2
|
117
|
-
)
|
118
|
-
nexus_mods_instance = nexus_mods(api_cache_expiry: { mod_files: 1 })
|
119
|
-
mod_files = nexus_mods_instance.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
|
120
|
-
sleep 2
|
121
|
-
expect(nexus_mods_instance.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq(mod_files)
|
122
252
|
end
|
123
253
|
|
124
254
|
it 'only clears the cache of the wanted resource' do
|
@@ -136,89 +266,12 @@ describe NexusMods do
|
|
136
266
|
mod_files20151 = nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2015)
|
137
267
|
mod_files20142 = nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014, clear_cache: true)
|
138
268
|
mod_files20152 = nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2015)
|
139
|
-
expect(mod_files20141).to eq
|
140
|
-
expect(mod_files20151).to eq
|
269
|
+
expect(mod_files20141).to eq mod_files20142
|
270
|
+
expect(mod_files20151).to eq mod_files20152
|
141
271
|
end
|
142
272
|
|
143
273
|
context 'with file persistence' do
|
144
274
|
|
145
|
-
it 'persists API cache in a file' do
|
146
|
-
with_api_cache_file do |api_cache_file|
|
147
|
-
expect_validate_user
|
148
|
-
expect_http_call_to(
|
149
|
-
path: '/v1/games.json',
|
150
|
-
json: [
|
151
|
-
json_game100,
|
152
|
-
json_game101
|
153
|
-
]
|
154
|
-
)
|
155
|
-
nexus_mods(api_cache_file:).games
|
156
|
-
expect(File.exist?(api_cache_file)).to be true
|
157
|
-
expect(File.size(api_cache_file)).to be > 0
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
it 'uses API cache from a file' do
|
162
|
-
with_api_cache_file do |api_cache_file|
|
163
|
-
expect_validate_user(times: 2)
|
164
|
-
expect_http_call_to(
|
165
|
-
path: '/v1/games.json',
|
166
|
-
json: [
|
167
|
-
json_game100,
|
168
|
-
json_game101
|
169
|
-
]
|
170
|
-
)
|
171
|
-
# Generate the cache first
|
172
|
-
games = nexus_mods(api_cache_file:).games
|
173
|
-
# Force a new instance of NexusMods API to run
|
174
|
-
reset_nexus_mods
|
175
|
-
expect(nexus_mods(api_cache_file:).games).to eq games
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
it 'uses API cache from a file, taking expiry time into account' do
|
180
|
-
with_api_cache_file do |api_cache_file|
|
181
|
-
expect_validate_user(times: 2)
|
182
|
-
expect_http_call_to(
|
183
|
-
path: '/v1/games.json',
|
184
|
-
json: [
|
185
|
-
json_game100,
|
186
|
-
json_game101
|
187
|
-
],
|
188
|
-
times: 2
|
189
|
-
)
|
190
|
-
# Generate the cache first
|
191
|
-
games = nexus_mods(api_cache_file:, api_cache_expiry: { games: 1 }).games
|
192
|
-
# Force a new instance of NexusMods API to run
|
193
|
-
reset_nexus_mods
|
194
|
-
sleep 2
|
195
|
-
# As the expiry time is 1 second, then the cache should still be invalidated
|
196
|
-
expect(nexus_mods(api_cache_file:, api_cache_expiry: { games: 1 }).games).to eq games
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
it 'uses API cache from a file, taking expiry time of the new process into account' do
|
201
|
-
with_api_cache_file do |api_cache_file|
|
202
|
-
expect_validate_user(times: 2)
|
203
|
-
expect_http_call_to(
|
204
|
-
path: '/v1/games.json',
|
205
|
-
json: [
|
206
|
-
json_game100,
|
207
|
-
json_game101
|
208
|
-
],
|
209
|
-
times: 2
|
210
|
-
)
|
211
|
-
# Generate the cache first
|
212
|
-
games = nexus_mods(api_cache_file:, api_cache_expiry: { games: 10 }).games
|
213
|
-
# Force a new instance of NexusMods API to run
|
214
|
-
reset_nexus_mods
|
215
|
-
sleep 2
|
216
|
-
# Even if the expiry time was 10 seconds while fetching the resource,
|
217
|
-
# if we decide it has to be 1 second now then it has to be invalidated.
|
218
|
-
expect(nexus_mods(api_cache_file:, api_cache_expiry: { games: 1 }).games).to eq games
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
275
|
it 'completes the API cache from a file' do
|
223
276
|
with_api_cache_file do |api_cache_file|
|
224
277
|
expect_validate_user(times: 3)
|
@@ -276,10 +329,10 @@ describe NexusMods do
|
|
276
329
|
nexus_mods_instance3 = nexus_mods(api_cache_file:)
|
277
330
|
mod_files20143 = nexus_mods_instance3.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
|
278
331
|
mod_files20153 = nexus_mods_instance3.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2015)
|
279
|
-
expect(mod_files20141).to eq
|
280
|
-
expect(mod_files20141).to eq
|
281
|
-
expect(mod_files20151).to eq
|
282
|
-
expect(mod_files20151).to eq
|
332
|
+
expect(mod_files20141).to eq mod_files20142
|
333
|
+
expect(mod_files20141).to eq mod_files20143
|
334
|
+
expect(mod_files20151).to eq mod_files20152
|
335
|
+
expect(mod_files20151).to eq mod_files20153
|
283
336
|
end
|
284
337
|
end
|
285
338
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nexus_mods
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Muriel Salvan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04-
|
11
|
+
date: 2023-04-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -137,6 +137,7 @@ files:
|
|
137
137
|
- lib/nexus_mods/api/game.rb
|
138
138
|
- lib/nexus_mods/api/mod.rb
|
139
139
|
- lib/nexus_mods/api/mod_file.rb
|
140
|
+
- lib/nexus_mods/api/mod_updates.rb
|
140
141
|
- lib/nexus_mods/api/user.rb
|
141
142
|
- lib/nexus_mods/api_client.rb
|
142
143
|
- lib/nexus_mods/cacheable_api.rb
|
@@ -146,12 +147,14 @@ files:
|
|
146
147
|
- lib/nexus_mods/version.rb
|
147
148
|
- spec/nexus_mods_test/factories/games.rb
|
148
149
|
- spec/nexus_mods_test/factories/mod_files.rb
|
150
|
+
- spec/nexus_mods_test/factories/mod_updates.rb
|
149
151
|
- spec/nexus_mods_test/factories/mods.rb
|
150
152
|
- spec/nexus_mods_test/helpers.rb
|
151
153
|
- spec/nexus_mods_test/scenarios/nexus_mods/api/api_limits_spec.rb
|
152
154
|
- spec/nexus_mods_test/scenarios/nexus_mods/api/game_spec.rb
|
153
155
|
- spec/nexus_mods_test/scenarios/nexus_mods/api/mod_file_spec.rb
|
154
156
|
- spec/nexus_mods_test/scenarios/nexus_mods/api/mod_spec.rb
|
157
|
+
- spec/nexus_mods_test/scenarios/nexus_mods/api/mod_updates_spec.rb
|
155
158
|
- spec/nexus_mods_test/scenarios/nexus_mods/nexus_mods_access_spec.rb
|
156
159
|
- spec/nexus_mods_test/scenarios/nexus_mods/nexus_mods_caching_spec.rb
|
157
160
|
- spec/nexus_mods_test/scenarios/nexus_mods/nexus_mods_common_spec.rb
|