diff --git a/local/wiremock/__files/steam-global-percentages-1091500.json b/local/wiremock/__files/steam-global-percentages-1091500.json new file mode 100644 index 0000000..b94dc9d --- /dev/null +++ b/local/wiremock/__files/steam-global-percentages-1091500.json @@ -0,0 +1,30 @@ +{ + "achievementpercentages": { + "achievements": [ + { + "name": "ACH_B1", + "percent": 81.0 + }, + { + "name": "ACH_B2", + "percent": 57.3 + }, + { + "name": "ACH_B3", + "percent": 39.8 + }, + { + "name": "ACH_B4", + "percent": 26.5 + }, + { + "name": "ACH_B5", + "percent": 23.6 + }, + { + "name": "ACH_B6", + "percent": 18.2 + } + ] + } +} diff --git a/local/wiremock/__files/steam-global-percentages-1245620.json b/local/wiremock/__files/steam-global-percentages-1245620.json new file mode 100644 index 0000000..afeff7b --- /dev/null +++ b/local/wiremock/__files/steam-global-percentages-1245620.json @@ -0,0 +1,38 @@ +{ + "achievementpercentages": { + "achievements": [ + { + "name": "ACH_A1", + "percent": 88.2 + }, + { + "name": "ACH_A2", + "percent": 52.7 + }, + { + "name": "ACH_A3", + "percent": 74.1 + }, + { + "name": "ACH_A4", + "percent": 31.4 + }, + { + "name": "ACH_A5", + "percent": 44.9 + }, + { + "name": "ACH_A6", + "percent": 36.0 + }, + { + "name": "ACH_A7", + "percent": 28.3 + }, + { + "name": "ACH_A8", + "percent": 4.6 + } + ] + } +} diff --git a/local/wiremock/__files/steam-global-percentages-1332010.json b/local/wiremock/__files/steam-global-percentages-1332010.json new file mode 100644 index 0000000..c3af42d --- /dev/null +++ b/local/wiremock/__files/steam-global-percentages-1332010.json @@ -0,0 +1,30 @@ +{ + "achievementpercentages": { + "achievements": [ + { + "name": "ACH_F1", + "percent": 91.2 + }, + { + "name": "ACH_F2", + "percent": 64.8 + }, + { + "name": "ACH_F3", + "percent": 48.5 + }, + { + "name": "ACH_F4", + "percent": 37.1 + }, + { + "name": "ACH_F5", + "percent": 22.4 + }, + { + "name": "ACH_F6", + "percent": 11.0 + } + ] + } +} diff --git a/local/wiremock/__files/steam-global-percentages-268910.json b/local/wiremock/__files/steam-global-percentages-268910.json new file mode 100644 index 0000000..25bed36 --- /dev/null +++ b/local/wiremock/__files/steam-global-percentages-268910.json @@ -0,0 +1,30 @@ +{ + "achievementpercentages": { + "achievements": [ + { + "name": "ACH_E1", + "percent": 83.4 + }, + { + "name": "ACH_E2", + "percent": 62.0 + }, + { + "name": "ACH_E3", + "percent": 44.3 + }, + { + "name": "ACH_E4", + "percent": 31.9 + }, + { + "name": "ACH_E5", + "percent": 27.5 + }, + { + "name": "ACH_E6", + "percent": 9.7 + } + ] + } +} diff --git a/local/wiremock/__files/steam-global-percentages-632470.json b/local/wiremock/__files/steam-global-percentages-632470.json new file mode 100644 index 0000000..648b664 --- /dev/null +++ b/local/wiremock/__files/steam-global-percentages-632470.json @@ -0,0 +1,30 @@ +{ + "achievementpercentages": { + "achievements": [ + { + "name": "ACH_C1", + "percent": 78.5 + }, + { + "name": "ACH_C2", + "percent": 47.2 + }, + { + "name": "ACH_C3", + "percent": 33.6 + }, + { + "name": "ACH_C4", + "percent": 35.4 + }, + { + "name": "ACH_C5", + "percent": 41.0 + }, + { + "name": "ACH_C6", + "percent": 12.9 + } + ] + } +} diff --git a/local/wiremock/__files/steam-global-percentages-646570.json b/local/wiremock/__files/steam-global-percentages-646570.json new file mode 100644 index 0000000..f467a17 --- /dev/null +++ b/local/wiremock/__files/steam-global-percentages-646570.json @@ -0,0 +1,30 @@ +{ + "achievementpercentages": { + "achievements": [ + { + "name": "ACH_D1", + "percent": 70.3 + }, + { + "name": "ACH_D2", + "percent": 25.8 + }, + { + "name": "ACH_D3", + "percent": 30.2 + }, + { + "name": "ACH_D4", + "percent": 15.6 + }, + { + "name": "ACH_D5", + "percent": 12.1 + }, + { + "name": "ACH_D6", + "percent": 8.4 + } + ] + } +} diff --git a/local/wiremock/__files/steam-owned-games.json b/local/wiremock/__files/steam-owned-games.json index e8ed248..a874668 100644 --- a/local/wiremock/__files/steam-owned-games.json +++ b/local/wiremock/__files/steam-owned-games.json @@ -1,6 +1,6 @@ { "response": { - "game_count": 8, + "game_count": 30, "games": [ { "appid": 620, @@ -8,7 +8,8 @@ "playtime_forever": 1450, "playtime_2weeks": null, "img_icon_url": "", - "has_community_visible_stats": true + "has_community_visible_stats": true, + "rtime_last_played": 1787300000 }, { "appid": 1145360, @@ -16,7 +17,8 @@ "playtime_forever": 3200, "playtime_2weeks": 240, "img_icon_url": "", - "has_community_visible_stats": true + "has_community_visible_stats": true, + "rtime_last_played": 1790500000 }, { "appid": 413150, @@ -24,7 +26,8 @@ "playtime_forever": 5400, "playtime_2weeks": null, "img_icon_url": "", - "has_community_visible_stats": true + "has_community_visible_stats": true, + "rtime_last_played": 1788900000 }, { "appid": 427520, @@ -32,7 +35,8 @@ "playtime_forever": 12000, "playtime_2weeks": 600, "img_icon_url": "", - "has_community_visible_stats": true + "has_community_visible_stats": true, + "rtime_last_played": 1790900000 }, { "appid": 504230, @@ -40,7 +44,8 @@ "playtime_forever": 800, "playtime_2weeks": null, "img_icon_url": "", - "has_community_visible_stats": true + "has_community_visible_stats": true, + "rtime_last_played": 1789200000 }, { "appid": 367520, @@ -56,7 +61,8 @@ "playtime_forever": 780, "playtime_2weeks": null, "img_icon_url": "", - "has_community_visible_stats": true + "has_community_visible_stats": true, + "rtime_last_played": 1786900000 }, { "appid": 892970, @@ -64,6 +70,198 @@ "playtime_forever": 90, "playtime_2weeks": 90, "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1790700000 + }, + { + "appid": 1245620, + "name": "ELDEN RING", + "playtime_forever": 6100, + "playtime_2weeks": 900, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1790000000 + }, + { + "appid": 1091500, + "name": "Cyberpunk 2077", + "playtime_forever": 4300, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1787900000 + }, + { + "appid": 292030, + "name": "The Witcher 3: Wild Hunt", + "playtime_forever": 5200, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1789800000 + }, + { + "appid": 1086940, + "name": "Baldur's Gate 3", + "playtime_forever": 2800, + "playtime_2weeks": 300, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1790150000 + }, + { + "appid": 730, + "name": "Counter-Strike 2", + "playtime_forever": 1900, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1788600000 + }, + { + "appid": 570, + "name": "Dota 2", + "playtime_forever": 0, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true + }, + { + "appid": 1174180, + "name": "Red Dead Redemption 2", + "playtime_forever": 0, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true + }, + { + "appid": 105600, + "name": "Terraria", + "playtime_forever": 2600, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1789300000 + }, + { + "appid": 294100, + "name": "RimWorld", + "playtime_forever": 3900, + "playtime_2weeks": 120, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1790300000 + }, + { + "appid": 632470, + "name": "Disco Elysium - The Final Cut", + "playtime_forever": 1500, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1788100000 + }, + { + "appid": 548430, + "name": "Deep Rock Galactic", + "playtime_forever": 700, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1790000000 + }, + { + "appid": 646570, + "name": "Slay the Spire", + "playtime_forever": 2200, + "playtime_2weeks": 60, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1790450000 + }, + { + "appid": 2379780, + "name": "Balatro", + "playtime_forever": 1100, + "playtime_2weeks": 420, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1790600000 + }, + { + "appid": 1794680, + "name": "Vampire Survivors", + "playtime_forever": 450, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1790700000 + }, + { + "appid": 268910, + "name": "Cuphead", + "playtime_forever": 900, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1787600000 + }, + { + "appid": 814380, + "name": "Sekiro: Shadows Die Twice", + "playtime_forever": 0, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true + }, + { + "appid": 374320, + "name": "DARK SOULS III", + "playtime_forever": 0, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true + }, + { + "appid": 1057090, + "name": "Ori and the Will of the Wisps", + "playtime_forever": 0, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true + }, + { + "appid": 275850, + "name": "No Man's Sky", + "playtime_forever": 120, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1790200000 + }, + { + "appid": 526870, + "name": "Satisfactory", + "playtime_forever": 0, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true + }, + { + "appid": 1332010, + "name": "Stray", + "playtime_forever": 330, + "playtime_2weeks": null, + "img_icon_url": "", + "has_community_visible_stats": true, + "rtime_last_played": 1789000000 + }, + { + "appid": 108600, + "name": "Project Zomboid", + "playtime_forever": 0, + "playtime_2weeks": null, + "img_icon_url": "", "has_community_visible_stats": true } ] diff --git a/local/wiremock/__files/steam-player-achievements-1091500.json b/local/wiremock/__files/steam-player-achievements-1091500.json new file mode 100644 index 0000000..c5ecc36 --- /dev/null +++ b/local/wiremock/__files/steam-player-achievements-1091500.json @@ -0,0 +1,39 @@ +{ + "playerstats": { + "steamID": "76561198000000001", + "gameName": "Cyberpunk 2077", + "success": true, + "achievements": [ + { + "apiname": "ACH_B1", + "achieved": 1, + "unlocktime": 1786000000 + }, + { + "apiname": "ACH_B2", + "achieved": 1, + "unlocktime": 1786259200 + }, + { + "apiname": "ACH_B3", + "achieved": 1, + "unlocktime": 1786518400 + }, + { + "apiname": "ACH_B4", + "achieved": 1, + "unlocktime": 1786777600 + }, + { + "apiname": "ACH_B5", + "achieved": 1, + "unlocktime": 1787036800 + }, + { + "apiname": "ACH_B6", + "achieved": 0, + "unlocktime": 0 + } + ] + } +} diff --git a/local/wiremock/__files/steam-player-achievements-1245620.json b/local/wiremock/__files/steam-player-achievements-1245620.json new file mode 100644 index 0000000..6f34c2a --- /dev/null +++ b/local/wiremock/__files/steam-player-achievements-1245620.json @@ -0,0 +1,49 @@ +{ + "playerstats": { + "steamID": "76561198000000001", + "gameName": "ELDEN RING", + "success": true, + "achievements": [ + { + "apiname": "ACH_A1", + "achieved": 1, + "unlocktime": 1786000000 + }, + { + "apiname": "ACH_A2", + "achieved": 1, + "unlocktime": 1786259200 + }, + { + "apiname": "ACH_A3", + "achieved": 1, + "unlocktime": 1786518400 + }, + { + "apiname": "ACH_A4", + "achieved": 0, + "unlocktime": 0 + }, + { + "apiname": "ACH_A5", + "achieved": 0, + "unlocktime": 0 + }, + { + "apiname": "ACH_A6", + "achieved": 0, + "unlocktime": 0 + }, + { + "apiname": "ACH_A7", + "achieved": 0, + "unlocktime": 0 + }, + { + "apiname": "ACH_A8", + "achieved": 0, + "unlocktime": 0 + } + ] + } +} diff --git a/local/wiremock/__files/steam-player-achievements-1332010.json b/local/wiremock/__files/steam-player-achievements-1332010.json new file mode 100644 index 0000000..37e64e3 --- /dev/null +++ b/local/wiremock/__files/steam-player-achievements-1332010.json @@ -0,0 +1,39 @@ +{ + "playerstats": { + "steamID": "76561198000000001", + "gameName": "Stray", + "success": true, + "achievements": [ + { + "apiname": "ACH_F1", + "achieved": 1, + "unlocktime": 1786000000 + }, + { + "apiname": "ACH_F2", + "achieved": 1, + "unlocktime": 1786259200 + }, + { + "apiname": "ACH_F3", + "achieved": 0, + "unlocktime": 0 + }, + { + "apiname": "ACH_F4", + "achieved": 0, + "unlocktime": 0 + }, + { + "apiname": "ACH_F5", + "achieved": 0, + "unlocktime": 0 + }, + { + "apiname": "ACH_F6", + "achieved": 0, + "unlocktime": 0 + } + ] + } +} diff --git a/local/wiremock/__files/steam-player-achievements-268910.json b/local/wiremock/__files/steam-player-achievements-268910.json new file mode 100644 index 0000000..c1b6a4b --- /dev/null +++ b/local/wiremock/__files/steam-player-achievements-268910.json @@ -0,0 +1,39 @@ +{ + "playerstats": { + "steamID": "76561198000000001", + "gameName": "Cuphead", + "success": true, + "achievements": [ + { + "apiname": "ACH_E1", + "achieved": 1, + "unlocktime": 1786000000 + }, + { + "apiname": "ACH_E2", + "achieved": 1, + "unlocktime": 1786259200 + }, + { + "apiname": "ACH_E3", + "achieved": 1, + "unlocktime": 1786518400 + }, + { + "apiname": "ACH_E4", + "achieved": 1, + "unlocktime": 1786777600 + }, + { + "apiname": "ACH_E5", + "achieved": 1, + "unlocktime": 1787036800 + }, + { + "apiname": "ACH_E6", + "achieved": 0, + "unlocktime": 0 + } + ] + } +} diff --git a/local/wiremock/__files/steam-player-achievements-632470.json b/local/wiremock/__files/steam-player-achievements-632470.json new file mode 100644 index 0000000..3f44ace --- /dev/null +++ b/local/wiremock/__files/steam-player-achievements-632470.json @@ -0,0 +1,39 @@ +{ + "playerstats": { + "steamID": "76561198000000001", + "gameName": "Disco Elysium - The Final Cut", + "success": true, + "achievements": [ + { + "apiname": "ACH_C1", + "achieved": 1, + "unlocktime": 1786000000 + }, + { + "apiname": "ACH_C2", + "achieved": 1, + "unlocktime": 1786259200 + }, + { + "apiname": "ACH_C3", + "achieved": 1, + "unlocktime": 1786518400 + }, + { + "apiname": "ACH_C4", + "achieved": 1, + "unlocktime": 1786777600 + }, + { + "apiname": "ACH_C5", + "achieved": 1, + "unlocktime": 1787036800 + }, + { + "apiname": "ACH_C6", + "achieved": 0, + "unlocktime": 0 + } + ] + } +} diff --git a/local/wiremock/__files/steam-player-achievements-646570.json b/local/wiremock/__files/steam-player-achievements-646570.json new file mode 100644 index 0000000..79c8b2a --- /dev/null +++ b/local/wiremock/__files/steam-player-achievements-646570.json @@ -0,0 +1,39 @@ +{ + "playerstats": { + "steamID": "76561198000000001", + "gameName": "Slay the Spire", + "success": true, + "achievements": [ + { + "apiname": "ACH_D1", + "achieved": 1, + "unlocktime": 1786000000 + }, + { + "apiname": "ACH_D2", + "achieved": 1, + "unlocktime": 1786259200 + }, + { + "apiname": "ACH_D3", + "achieved": 1, + "unlocktime": 1786518400 + }, + { + "apiname": "ACH_D4", + "achieved": 1, + "unlocktime": 1786777600 + }, + { + "apiname": "ACH_D5", + "achieved": 1, + "unlocktime": 1787036800 + }, + { + "apiname": "ACH_D6", + "achieved": 1, + "unlocktime": 1787296000 + } + ] + } +} diff --git a/local/wiremock/__files/steam-schema-1091500.json b/local/wiremock/__files/steam-schema-1091500.json new file mode 100644 index 0000000..c5c1c00 --- /dev/null +++ b/local/wiremock/__files/steam-schema-1091500.json @@ -0,0 +1,58 @@ +{ + "game": { + "gameName": "Cyberpunk 2077", + "gameVersion": "1", + "availableGameStats": { + "achievements": [ + { + "name": "ACH_B1", + "displayName": "Street Kid", + "description": "Get through the opening job.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1091500/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1091500/capsule_184x69.jpg" + }, + { + "name": "ACH_B2", + "displayName": "Chrome Fan", + "description": "Install five implants.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1091500/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1091500/capsule_184x69.jpg" + }, + { + "name": "ACH_B3", + "displayName": "Fast Driver", + "description": "Win a street race.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1091500/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1091500/capsule_184x69.jpg" + }, + { + "name": "ACH_B4", + "displayName": "Netrunner", + "description": "Hack ten enemies in one fight.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1091500/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1091500/capsule_184x69.jpg" + }, + { + "name": "ACH_B5", + "displayName": "Final Run", + "description": "Complete the main story.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1091500/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1091500/capsule_184x69.jpg" + }, + { + "name": "ACH_B6", + "displayName": "Rich Merc", + "description": "Hold a big pile of eddies.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1091500/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1091500/capsule_184x69.jpg" + } + ] + } + } +} diff --git a/local/wiremock/__files/steam-schema-1245620.json b/local/wiremock/__files/steam-schema-1245620.json new file mode 100644 index 0000000..968e5d7 --- /dev/null +++ b/local/wiremock/__files/steam-schema-1245620.json @@ -0,0 +1,74 @@ +{ + "game": { + "gameName": "ELDEN RING", + "gameVersion": "1", + "availableGameStats": { + "achievements": [ + { + "name": "ACH_A1", + "displayName": "Rune Bearer", + "description": "Pick up your first rune from the grace.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg" + }, + { + "name": "ACH_A2", + "displayName": "Margit Down", + "description": "Defeat the warden of the gate.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg" + }, + { + "name": "ACH_A3", + "displayName": "Roundtable Guest", + "description": "Visit the hold between worlds.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg" + }, + { + "name": "ACH_A4", + "displayName": "Smith Friend", + "description": "Upgrade a weapon to a high level.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg" + }, + { + "name": "ACH_A5", + "displayName": "Mounted Hunter", + "description": "Slay a foe from horseback.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg" + }, + { + "name": "ACH_A6", + "displayName": "Great Rune", + "description": "Restore a great rune.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg" + }, + { + "name": "ACH_A7", + "displayName": "Deep Delver", + "description": "Explore a deep underground city.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg" + }, + { + "name": "ACH_A8", + "displayName": "Lord of Lands", + "description": "Gather every legendary talisman.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1245620/capsule_184x69.jpg" + } + ] + } + } +} diff --git a/local/wiremock/__files/steam-schema-1332010.json b/local/wiremock/__files/steam-schema-1332010.json new file mode 100644 index 0000000..3813197 --- /dev/null +++ b/local/wiremock/__files/steam-schema-1332010.json @@ -0,0 +1,58 @@ +{ + "game": { + "gameName": "Stray", + "gameVersion": "1", + "availableGameStats": { + "achievements": [ + { + "name": "ACH_F1", + "displayName": "Cat Nap", + "description": "Sleep in a sunny spot.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1332010/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1332010/capsule_184x69.jpg" + }, + { + "name": "ACH_F2", + "displayName": "Rooftop Walker", + "description": "Cross the roofs of the old slum.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1332010/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1332010/capsule_184x69.jpg" + }, + { + "name": "ACH_F3", + "displayName": "Robot Friend", + "description": "Make a friend of a small robot.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1332010/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1332010/capsule_184x69.jpg" + }, + { + "name": "ACH_F4", + "displayName": "Meow Choir", + "description": "Meow in front of ten robots.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1332010/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1332010/capsule_184x69.jpg" + }, + { + "name": "ACH_F5", + "displayName": "Fish Lover", + "description": "Find a cold bowl of fish.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1332010/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1332010/capsule_184x69.jpg" + }, + { + "name": "ACH_F6", + "displayName": "Secret Paw", + "description": "Open every hidden door.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/1332010/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/1332010/capsule_184x69.jpg" + } + ] + } + } +} diff --git a/local/wiremock/__files/steam-schema-268910.json b/local/wiremock/__files/steam-schema-268910.json new file mode 100644 index 0000000..742cbd8 --- /dev/null +++ b/local/wiremock/__files/steam-schema-268910.json @@ -0,0 +1,58 @@ +{ + "game": { + "gameName": "Cuphead", + "gameVersion": "1", + "availableGameStats": { + "achievements": [ + { + "name": "ACH_E1", + "displayName": "Contract Signed", + "description": "Take the deal at the casino.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/268910/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/268910/capsule_184x69.jpg" + }, + { + "name": "ACH_E2", + "displayName": "Run and Gun", + "description": "Pass a platform stage.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/268910/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/268910/capsule_184x69.jpg" + }, + { + "name": "ACH_E3", + "displayName": "Boss Rush", + "description": "Defeat five bosses.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/268910/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/268910/capsule_184x69.jpg" + }, + { + "name": "ACH_E4", + "displayName": "Devil Beaten", + "description": "Complete the main story.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/268910/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/268910/capsule_184x69.jpg" + }, + { + "name": "ACH_E5", + "displayName": "Coin Collector", + "description": "Find every coin of a world.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/268910/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/268910/capsule_184x69.jpg" + }, + { + "name": "ACH_E6", + "displayName": "Perfect Parry", + "description": "Parry twelve pink shots in a row.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/268910/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/268910/capsule_184x69.jpg" + } + ] + } + } +} diff --git a/local/wiremock/__files/steam-schema-632470.json b/local/wiremock/__files/steam-schema-632470.json new file mode 100644 index 0000000..a284461 --- /dev/null +++ b/local/wiremock/__files/steam-schema-632470.json @@ -0,0 +1,58 @@ +{ + "game": { + "gameName": "Disco Elysium - The Final Cut", + "gameVersion": "1", + "availableGameStats": { + "achievements": [ + { + "name": "ACH_C1", + "displayName": "Fresh Case", + "description": "Wake up in the hostel room.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/632470/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/632470/capsule_184x69.jpg" + }, + { + "name": "ACH_C2", + "displayName": "Good Listener", + "description": "Talk to every skill voice.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/632470/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/632470/capsule_184x69.jpg" + }, + { + "name": "ACH_C3", + "displayName": "Thought Cabinet", + "description": "Fill all slots of the cabinet.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/632470/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/632470/capsule_184x69.jpg" + }, + { + "name": "ACH_C4", + "displayName": "Case Closed", + "description": "Finish the main story.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/632470/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/632470/capsule_184x69.jpg" + }, + { + "name": "ACH_C5", + "displayName": "Best Friend", + "description": "Win the trust of your partner.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/632470/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/632470/capsule_184x69.jpg" + }, + { + "name": "ACH_C6", + "displayName": "Sweet Tunes", + "description": "Hear every song on the radio.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/632470/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/632470/capsule_184x69.jpg" + } + ] + } + } +} diff --git a/local/wiremock/__files/steam-schema-646570.json b/local/wiremock/__files/steam-schema-646570.json new file mode 100644 index 0000000..b4a2113 --- /dev/null +++ b/local/wiremock/__files/steam-schema-646570.json @@ -0,0 +1,58 @@ +{ + "game": { + "gameName": "Slay the Spire", + "gameVersion": "1", + "availableGameStats": { + "achievements": [ + { + "name": "ACH_D1", + "displayName": "Spire Climber", + "description": "Reach the top of the first act.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/646570/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/646570/capsule_184x69.jpg" + }, + { + "name": "ACH_D2", + "displayName": "Card Hoarder", + "description": "Own forty cards in one run.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/646570/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/646570/capsule_184x69.jpg" + }, + { + "name": "ACH_D3", + "displayName": "Relic Fan", + "description": "Hold ten relics in one run.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/646570/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/646570/capsule_184x69.jpg" + }, + { + "name": "ACH_D4", + "displayName": "Silent Hunter", + "description": "Win a run as the second hero.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/646570/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/646570/capsule_184x69.jpg" + }, + { + "name": "ACH_D5", + "displayName": "Cold Heart", + "description": "Win a run as the third hero.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/646570/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/646570/capsule_184x69.jpg" + }, + { + "name": "ACH_D6", + "displayName": "Pure Deck", + "description": "Win a run with a tiny deck.", + "hidden": 0, + "icon": "https://cdn.cloudflare.steamstatic.com/steam/apps/646570/capsule_184x69.jpg", + "icongray": "https://cdn.cloudflare.steamstatic.com/steam/apps/646570/capsule_184x69.jpg" + } + ] + } + } +} diff --git a/local/wiremock/mappings/steam-global-percentages-1091500.json b/local/wiremock/mappings/steam-global-percentages-1091500.json new file mode 100644 index 0000000..a20de57 --- /dev/null +++ b/local/wiremock/mappings/steam-global-percentages-1091500.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetGlobalAchievementPercentagesForApp/v2/", + "queryParameters": { + "gameid": { + "equalTo": "1091500" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-global-percentages-1091500.json" + } +} diff --git a/local/wiremock/mappings/steam-global-percentages-1245620.json b/local/wiremock/mappings/steam-global-percentages-1245620.json new file mode 100644 index 0000000..f65cbc2 --- /dev/null +++ b/local/wiremock/mappings/steam-global-percentages-1245620.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetGlobalAchievementPercentagesForApp/v2/", + "queryParameters": { + "gameid": { + "equalTo": "1245620" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-global-percentages-1245620.json" + } +} diff --git a/local/wiremock/mappings/steam-global-percentages-1332010.json b/local/wiremock/mappings/steam-global-percentages-1332010.json new file mode 100644 index 0000000..dcacdc2 --- /dev/null +++ b/local/wiremock/mappings/steam-global-percentages-1332010.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetGlobalAchievementPercentagesForApp/v2/", + "queryParameters": { + "gameid": { + "equalTo": "1332010" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-global-percentages-1332010.json" + } +} diff --git a/local/wiremock/mappings/steam-global-percentages-268910.json b/local/wiremock/mappings/steam-global-percentages-268910.json new file mode 100644 index 0000000..5a72c25 --- /dev/null +++ b/local/wiremock/mappings/steam-global-percentages-268910.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetGlobalAchievementPercentagesForApp/v2/", + "queryParameters": { + "gameid": { + "equalTo": "268910" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-global-percentages-268910.json" + } +} diff --git a/local/wiremock/mappings/steam-global-percentages-632470.json b/local/wiremock/mappings/steam-global-percentages-632470.json new file mode 100644 index 0000000..be01235 --- /dev/null +++ b/local/wiremock/mappings/steam-global-percentages-632470.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetGlobalAchievementPercentagesForApp/v2/", + "queryParameters": { + "gameid": { + "equalTo": "632470" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-global-percentages-632470.json" + } +} diff --git a/local/wiremock/mappings/steam-global-percentages-646570.json b/local/wiremock/mappings/steam-global-percentages-646570.json new file mode 100644 index 0000000..1f3591b --- /dev/null +++ b/local/wiremock/mappings/steam-global-percentages-646570.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetGlobalAchievementPercentagesForApp/v2/", + "queryParameters": { + "gameid": { + "equalTo": "646570" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-global-percentages-646570.json" + } +} diff --git a/local/wiremock/mappings/steam-player-achievements-1091500.json b/local/wiremock/mappings/steam-player-achievements-1091500.json new file mode 100644 index 0000000..b6a825a --- /dev/null +++ b/local/wiremock/mappings/steam-player-achievements-1091500.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetPlayerAchievements/v1/", + "queryParameters": { + "appid": { + "equalTo": "1091500" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-player-achievements-1091500.json" + } +} diff --git a/local/wiremock/mappings/steam-player-achievements-1245620.json b/local/wiremock/mappings/steam-player-achievements-1245620.json new file mode 100644 index 0000000..d531670 --- /dev/null +++ b/local/wiremock/mappings/steam-player-achievements-1245620.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetPlayerAchievements/v1/", + "queryParameters": { + "appid": { + "equalTo": "1245620" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-player-achievements-1245620.json" + } +} diff --git a/local/wiremock/mappings/steam-player-achievements-1332010.json b/local/wiremock/mappings/steam-player-achievements-1332010.json new file mode 100644 index 0000000..215a361 --- /dev/null +++ b/local/wiremock/mappings/steam-player-achievements-1332010.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetPlayerAchievements/v1/", + "queryParameters": { + "appid": { + "equalTo": "1332010" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-player-achievements-1332010.json" + } +} diff --git a/local/wiremock/mappings/steam-player-achievements-268910.json b/local/wiremock/mappings/steam-player-achievements-268910.json new file mode 100644 index 0000000..ff3000f --- /dev/null +++ b/local/wiremock/mappings/steam-player-achievements-268910.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetPlayerAchievements/v1/", + "queryParameters": { + "appid": { + "equalTo": "268910" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-player-achievements-268910.json" + } +} diff --git a/local/wiremock/mappings/steam-player-achievements-632470.json b/local/wiremock/mappings/steam-player-achievements-632470.json new file mode 100644 index 0000000..8897c64 --- /dev/null +++ b/local/wiremock/mappings/steam-player-achievements-632470.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetPlayerAchievements/v1/", + "queryParameters": { + "appid": { + "equalTo": "632470" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-player-achievements-632470.json" + } +} diff --git a/local/wiremock/mappings/steam-player-achievements-646570.json b/local/wiremock/mappings/steam-player-achievements-646570.json new file mode 100644 index 0000000..8fe8595 --- /dev/null +++ b/local/wiremock/mappings/steam-player-achievements-646570.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetPlayerAchievements/v1/", + "queryParameters": { + "appid": { + "equalTo": "646570" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-player-achievements-646570.json" + } +} diff --git a/local/wiremock/mappings/steam-schema-1091500.json b/local/wiremock/mappings/steam-schema-1091500.json new file mode 100644 index 0000000..6822b69 --- /dev/null +++ b/local/wiremock/mappings/steam-schema-1091500.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetSchemaForGame/v2/", + "queryParameters": { + "appid": { + "equalTo": "1091500" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-schema-1091500.json" + } +} diff --git a/local/wiremock/mappings/steam-schema-1245620.json b/local/wiremock/mappings/steam-schema-1245620.json new file mode 100644 index 0000000..318f24f --- /dev/null +++ b/local/wiremock/mappings/steam-schema-1245620.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetSchemaForGame/v2/", + "queryParameters": { + "appid": { + "equalTo": "1245620" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-schema-1245620.json" + } +} diff --git a/local/wiremock/mappings/steam-schema-1332010.json b/local/wiremock/mappings/steam-schema-1332010.json new file mode 100644 index 0000000..d58802c --- /dev/null +++ b/local/wiremock/mappings/steam-schema-1332010.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetSchemaForGame/v2/", + "queryParameters": { + "appid": { + "equalTo": "1332010" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-schema-1332010.json" + } +} diff --git a/local/wiremock/mappings/steam-schema-268910.json b/local/wiremock/mappings/steam-schema-268910.json new file mode 100644 index 0000000..2d3fcb9 --- /dev/null +++ b/local/wiremock/mappings/steam-schema-268910.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetSchemaForGame/v2/", + "queryParameters": { + "appid": { + "equalTo": "268910" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-schema-268910.json" + } +} diff --git a/local/wiremock/mappings/steam-schema-632470.json b/local/wiremock/mappings/steam-schema-632470.json new file mode 100644 index 0000000..a0b3fa2 --- /dev/null +++ b/local/wiremock/mappings/steam-schema-632470.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetSchemaForGame/v2/", + "queryParameters": { + "appid": { + "equalTo": "632470" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-schema-632470.json" + } +} diff --git a/local/wiremock/mappings/steam-schema-646570.json b/local/wiremock/mappings/steam-schema-646570.json new file mode 100644 index 0000000..f4230ce --- /dev/null +++ b/local/wiremock/mappings/steam-schema-646570.json @@ -0,0 +1,18 @@ +{ + "request": { + "method": "GET", + "urlPath": "/steam/ISteamUserStats/GetSchemaForGame/v2/", + "queryParameters": { + "appid": { + "equalTo": "646570" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "bodyFileName": "steam-schema-646570.json" + } +} diff --git a/src/test/java/core_service/configuration/LocalMocksParseTest.java b/src/test/java/core_service/configuration/LocalMocksParseTest.java index d873e34..07b7af9 100644 --- a/src/test/java/core_service/configuration/LocalMocksParseTest.java +++ b/src/test/java/core_service/configuration/LocalMocksParseTest.java @@ -68,14 +68,19 @@ class LocalMocksParseTest extends AbstractUnitTest { @Test void everyMapping_PointsToAnExistingAnswer() throws IOException { - List mappings; - try (Stream listed = Files.list(MAPPINGS)) { - mappings = listed.sorted().toList(); - } + List mappings = sortedFiles(MAPPINGS); assertThat(mappings).isNotEmpty(); for (Path mapping : mappings) { - JsonNode response = jsonMapper.readTree(mapping.toFile()).path("response"); + JsonNode root = jsonMapper.readTree(mapping.toFile()); + JsonNode response = root.path("response"); + // A copied achievement mapping must ask for the appid in its own name. + String name = mapping.getFileName().toString(); + if (name.matches("steam-(schema|player-achievements|global-percentages)-\\d+\\.json")) { + JsonNode query = root.path("request").path("queryParameters"); + String asked = query.path(query.has("appid") ? "appid" : "gameid").path("equalTo").asString(); + assertThat(name).as(mapping.toString()).isEqualTo(name.replaceAll("\\d+", asked)); + } // The PlayStation authorize answer is a redirect with no body. assertThat(response.has("bodyFileName") || response.has("jsonBody") || response.path("headers").has("Location")) .as(mapping.toString()).isTrue(); @@ -105,10 +110,7 @@ class LocalMocksParseTest extends AbstractUnitTest { @Test void everyRecordedAnswer_ParsesIntoItsResponseType() throws IOException, ReflectiveOperationException { - List files; - try (Stream listed = Files.list(FILES)) { - files = listed.sorted().toList(); - } + List files = sortedFiles(FILES); assertThat(files).isNotEmpty(); for (Path file : files) { @@ -121,4 +123,30 @@ class LocalMocksParseTest extends AbstractUnitTest { assertFilled(name, jsonMapper.readValue(file.toFile(), type)); } } + + // A copied stub with a typo in its appid is otherwise only a silent 400 in the sync log. + @Test + void everyAchievementStub_BelongsToAnOwnedGame() throws IOException { + List owned = jsonMapper.readValue(FILES.resolve("steam-owned-games.json").toFile(), SteamOwnedGamesResponse.class) + .response().games().stream() + .map(game -> game.appid().toString()) + .toList(); + List schemas = sortedFiles(FILES).stream() + .filter(file -> file.getFileName().toString().matches("steam-schema-\\d+\\.json")) + .toList(); + assertThat(schemas).isNotEmpty(); + + for (Path schema : schemas) { + String appid = schema.getFileName().toString().replaceAll("\\D", ""); + assertThat(owned).as(schema.toString()).contains(appid); + assertThat(FILES.resolve("steam-player-achievements-" + appid + ".json")).exists(); + assertThat(FILES.resolve("steam-global-percentages-" + appid + ".json")).exists(); + } + } + + private static List sortedFiles(Path dir) throws IOException { + try (Stream listed = Files.list(dir)) { + return listed.sorted().toList(); + } + } }