nexus_mods 2.0.1 → 2.2.0
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/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
|