chore(deps): update jetbrains/qodana-action action to v2024 (#3195)
[ArchiSteamFarm.git] / ArchiSteamFarm.Tests / Bot.cs
blob640c24bcac0b9c23122eae9709be5852512d88df
1 // ----------------------------------------------------------------------------------------------
2 // _ _ _ ____ _ _____
3 // / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
4 // / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
5 // / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
6 // /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
7 // ----------------------------------------------------------------------------------------------
8 // |
9 // Copyright 2015-2024 Ɓukasz "JustArchi" Domeradzki
10 // Contact: JustArchi@JustArchi.net
11 // |
12 // Licensed under the Apache License, Version 2.0 (the "License");
13 // you may not use this file except in compliance with the License.
14 // You may obtain a copy of the License at
15 // |
16 // http://www.apache.org/licenses/LICENSE-2.0
17 // |
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the License is distributed on an "AS IS" BASIS,
20 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 // See the License for the specific language governing permissions and
22 // limitations under the License.
24 using System;
25 using System.Collections.Generic;
26 using System.Linq;
27 using ArchiSteamFarm.Steam.Data;
28 using Microsoft.VisualStudio.TestTools.UnitTesting;
29 using static ArchiSteamFarm.Steam.Bot;
31 namespace ArchiSteamFarm.Tests;
33 [TestClass]
34 public sealed class Bot {
35 [TestMethod]
36 public void MaxItemsBarelyEnoughForOneSet() {
37 const uint relevantAppID = 42;
39 Dictionary<uint, byte> itemsPerSet = new() {
40 { relevantAppID, MinCardsPerBadge },
41 { 43, MinCardsPerBadge + 1 }
44 HashSet<Asset> items = [];
46 foreach ((uint appID, byte cards) in itemsPerSet) {
47 for (byte i = 1; i <= cards; i++) {
48 items.Add(CreateCard(i, realAppID: appID));
52 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, itemsPerSet, MinCardsPerBadge);
54 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = items.Where(static item => item.RealAppID == relevantAppID).GroupBy(static item => (item.RealAppID, item.ContextID, item.ClassID)).ToDictionary(static group => group.Key, static group => (uint) group.Sum(static item => item.Amount));
56 AssertResultMatchesExpectation(expectedResult, itemsToSend);
59 [TestMethod]
60 public void MaxItemsTooSmall() {
61 const uint appID = 42;
63 HashSet<Asset> items = [
64 CreateCard(1, realAppID: appID),
65 CreateCard(2, realAppID: appID)
68 Assert.ThrowsException<ArgumentOutOfRangeException>(() => GetItemsForFullBadge(items, 2, appID, MinCardsPerBadge - 1));
71 [TestMethod]
72 public void MoreCardsThanNeeded() {
73 const uint appID = 42;
75 HashSet<Asset> items = [
76 CreateCard(1, realAppID: appID),
77 CreateCard(1, realAppID: appID),
78 CreateCard(2, realAppID: appID),
79 CreateCard(3, realAppID: appID)
82 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 3, appID);
84 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() {
85 { (appID, Asset.SteamCommunityContextID, 1), 1 },
86 { (appID, Asset.SteamCommunityContextID, 2), 1 },
87 { (appID, Asset.SteamCommunityContextID, 3), 1 }
90 AssertResultMatchesExpectation(expectedResult, itemsToSend);
93 [TestMethod]
94 public void MultipleSets() {
95 const uint appID = 42;
97 HashSet<Asset> items = [
98 CreateCard(1, realAppID: appID),
99 CreateCard(1, realAppID: appID),
100 CreateCard(2, realAppID: appID),
101 CreateCard(2, realAppID: appID)
104 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 2, appID);
106 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() {
107 { (appID, Asset.SteamCommunityContextID, 1), 2 },
108 { (appID, Asset.SteamCommunityContextID, 2), 2 }
111 AssertResultMatchesExpectation(expectedResult, itemsToSend);
114 [TestMethod]
115 public void MultipleSetsDifferentAmount() {
116 const uint appID = 42;
118 HashSet<Asset> items = [
119 CreateCard(1, amount: 2, realAppID: appID),
120 CreateCard(2, realAppID: appID),
121 CreateCard(2, realAppID: appID)
124 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 2, appID);
126 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() {
127 { (appID, Asset.SteamCommunityContextID, 1), 2 },
128 { (appID, Asset.SteamCommunityContextID, 2), 2 }
131 AssertResultMatchesExpectation(expectedResult, itemsToSend);
134 [TestMethod]
135 public void MutliRarityAndType() {
136 const uint appID = 42;
138 HashSet<Asset> items = [
139 CreateCard(1, realAppID: appID, type: EAssetType.TradingCard, rarity: EAssetRarity.Common),
140 CreateCard(2, realAppID: appID, type: EAssetType.TradingCard, rarity: EAssetRarity.Common),
142 CreateCard(1, realAppID: appID, type: EAssetType.FoilTradingCard, rarity: EAssetRarity.Uncommon),
143 CreateCard(2, realAppID: appID, type: EAssetType.FoilTradingCard, rarity: EAssetRarity.Uncommon),
145 CreateCard(1, realAppID: appID, type: EAssetType.FoilTradingCard, rarity: EAssetRarity.Rare),
146 CreateCard(2, realAppID: appID, type: EAssetType.FoilTradingCard, rarity: EAssetRarity.Rare),
148 // for better readability and easier verification when thinking about this test the items that shall be selected for sending are the ones below this comment
149 CreateCard(1, realAppID: appID, type: EAssetType.TradingCard, rarity: EAssetRarity.Uncommon),
150 CreateCard(2, realAppID: appID, type: EAssetType.TradingCard, rarity: EAssetRarity.Uncommon),
151 CreateCard(3, realAppID: appID, type: EAssetType.TradingCard, rarity: EAssetRarity.Uncommon),
153 CreateCard(1, realAppID: appID, type: EAssetType.FoilTradingCard, rarity: EAssetRarity.Common),
154 CreateCard(3, realAppID: appID, type: EAssetType.FoilTradingCard, rarity: EAssetRarity.Common),
155 CreateCard(7, realAppID: appID, type: EAssetType.FoilTradingCard, rarity: EAssetRarity.Common),
157 CreateCard(2, realAppID: appID, type: EAssetType.Unknown, rarity: EAssetRarity.Rare),
158 CreateCard(3, realAppID: appID, type: EAssetType.Unknown, rarity: EAssetRarity.Rare),
159 CreateCard(4, realAppID: appID, type: EAssetType.Unknown, rarity: EAssetRarity.Rare)
162 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 3, appID);
164 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() {
165 { (appID, Asset.SteamCommunityContextID, 1), 2 },
166 { (appID, Asset.SteamCommunityContextID, 2), 2 },
167 { (appID, Asset.SteamCommunityContextID, 3), 3 },
168 { (appID, Asset.SteamCommunityContextID, 4), 1 },
169 { (appID, Asset.SteamCommunityContextID, 7), 1 }
172 AssertResultMatchesExpectation(expectedResult, itemsToSend);
175 [TestMethod]
176 public void NotAllCardsPresent() {
177 const uint appID = 42;
179 HashSet<Asset> items = [
180 CreateCard(1, realAppID: appID),
181 CreateCard(2, realAppID: appID)
184 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 3, appID);
186 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new(0);
187 AssertResultMatchesExpectation(expectedResult, itemsToSend);
190 [TestMethod]
191 public void OneSet() {
192 const uint appID = 42;
194 HashSet<Asset> items = [
195 CreateCard(1, realAppID: appID),
196 CreateCard(2, realAppID: appID)
199 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 2, appID);
201 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() {
202 { (appID, Asset.SteamCommunityContextID, 1), 1 },
203 { (appID, Asset.SteamCommunityContextID, 2), 1 }
206 AssertResultMatchesExpectation(expectedResult, itemsToSend);
209 [TestMethod]
210 public void OtherAppIDFullSets() {
211 const uint appID0 = 42;
212 const uint appID1 = 43;
214 HashSet<Asset> items = [
215 CreateCard(1, realAppID: appID0),
216 CreateCard(1, realAppID: appID1)
219 HashSet<Asset> itemsToSend = GetItemsForFullBadge(
220 items, new Dictionary<uint, byte> {
221 { appID0, 1 },
222 { appID1, 1 }
226 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() {
227 { (appID0, Asset.SteamCommunityContextID, 1), 1 },
228 { (appID1, Asset.SteamCommunityContextID, 1), 1 }
231 AssertResultMatchesExpectation(expectedResult, itemsToSend);
234 [TestMethod]
235 public void OtherAppIDNoSets() {
236 const uint appID0 = 42;
237 const uint appID1 = 43;
239 HashSet<Asset> items = [
240 CreateCard(1, realAppID: appID0),
241 CreateCard(1, realAppID: appID1)
244 HashSet<Asset> itemsToSend = GetItemsForFullBadge(
245 items, new Dictionary<uint, byte> {
246 { appID0, 2 },
247 { appID1, 2 }
251 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new(0);
253 AssertResultMatchesExpectation(expectedResult, itemsToSend);
256 [TestMethod]
257 public void OtherAppIDOneSet() {
258 const uint appID0 = 42;
259 const uint appID1 = 43;
260 const uint appID2 = 44;
262 HashSet<Asset> items = [
263 CreateCard(1, realAppID: appID0),
264 CreateCard(2, realAppID: appID0),
266 CreateCard(1, realAppID: appID1),
267 CreateCard(2, realAppID: appID1),
268 CreateCard(3, realAppID: appID1)
271 HashSet<Asset> itemsToSend = GetItemsForFullBadge(
272 items, new Dictionary<uint, byte> {
273 { appID0, 3 },
274 { appID1, 3 },
275 { appID2, 3 }
279 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() {
280 { (appID1, Asset.SteamCommunityContextID, 1), 1 },
281 { (appID1, Asset.SteamCommunityContextID, 2), 1 },
282 { (appID1, Asset.SteamCommunityContextID, 3), 1 }
285 AssertResultMatchesExpectation(expectedResult, itemsToSend);
288 [TestMethod]
289 public void OtherRarityFullSets() {
290 const uint appID = 42;
292 HashSet<Asset> items = [
293 CreateCard(1, realAppID: appID, rarity: EAssetRarity.Common),
294 CreateCard(1, realAppID: appID, rarity: EAssetRarity.Rare)
297 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 1, appID);
299 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() {
300 { (appID, Asset.SteamCommunityContextID, 1), 2 }
303 AssertResultMatchesExpectation(expectedResult, itemsToSend);
306 [TestMethod]
307 public void OtherRarityNoSets() {
308 const uint appID = 42;
310 HashSet<Asset> items = [
311 CreateCard(1, realAppID: appID, rarity: EAssetRarity.Common),
312 CreateCard(1, realAppID: appID, rarity: EAssetRarity.Rare)
315 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 2, appID);
317 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new(0);
319 AssertResultMatchesExpectation(expectedResult, itemsToSend);
322 [TestMethod]
323 public void OtherRarityOneSet() {
324 const uint appID = 42;
326 HashSet<Asset> items = [
327 CreateCard(1, realAppID: appID, rarity: EAssetRarity.Common),
328 CreateCard(2, realAppID: appID, rarity: EAssetRarity.Common),
329 CreateCard(1, realAppID: appID, rarity: EAssetRarity.Uncommon),
330 CreateCard(2, realAppID: appID, rarity: EAssetRarity.Uncommon),
331 CreateCard(3, realAppID: appID, rarity: EAssetRarity.Uncommon)
334 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 3, appID);
336 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() {
337 { (appID, Asset.SteamCommunityContextID, 1), 1 },
338 { (appID, Asset.SteamCommunityContextID, 2), 1 },
339 { (appID, Asset.SteamCommunityContextID, 3), 1 }
342 AssertResultMatchesExpectation(expectedResult, itemsToSend);
345 [TestMethod]
346 public void OtherTypeFullSets() {
347 const uint appID = 42;
349 HashSet<Asset> items = [
350 CreateCard(1, realAppID: appID, type: EAssetType.TradingCard),
351 CreateCard(1, realAppID: appID, type: EAssetType.FoilTradingCard)
354 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 1, appID);
356 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() {
357 { (appID, Asset.SteamCommunityContextID, 1), 2 }
360 AssertResultMatchesExpectation(expectedResult, itemsToSend);
363 [TestMethod]
364 public void OtherTypeNoSets() {
365 const uint appID = 42;
367 HashSet<Asset> items = [
368 CreateCard(1, realAppID: appID, type: EAssetType.TradingCard),
369 CreateCard(1, realAppID: appID, type: EAssetType.FoilTradingCard)
372 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 2, appID);
374 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new(0);
376 AssertResultMatchesExpectation(expectedResult, itemsToSend);
379 [TestMethod]
380 public void OtherTypeOneSet() {
381 const uint appID = 42;
383 HashSet<Asset> items = [
384 CreateCard(1, realAppID: appID, type: EAssetType.TradingCard),
385 CreateCard(2, realAppID: appID, type: EAssetType.TradingCard),
386 CreateCard(1, realAppID: appID, type: EAssetType.FoilTradingCard),
387 CreateCard(2, realAppID: appID, type: EAssetType.FoilTradingCard),
388 CreateCard(3, realAppID: appID, type: EAssetType.FoilTradingCard)
391 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 3, appID);
393 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() {
394 { (appID, Asset.SteamCommunityContextID, 1), 1 },
395 { (appID, Asset.SteamCommunityContextID, 2), 1 },
396 { (appID, Asset.SteamCommunityContextID, 3), 1 }
399 AssertResultMatchesExpectation(expectedResult, itemsToSend);
402 [TestMethod]
403 public void TooHighAmount() {
404 const uint appID0 = 42;
406 HashSet<Asset> items = [
407 CreateCard(1, amount: 2, realAppID: appID0),
408 CreateCard(2, realAppID: appID0)
411 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 2, appID0);
413 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() {
414 { (appID0, Asset.SteamCommunityContextID, 1), 1 },
415 { (appID0, Asset.SteamCommunityContextID, 2), 1 }
418 AssertResultMatchesExpectation(expectedResult, itemsToSend);
421 [TestMethod]
422 public void TooManyCardsForSingleTrade() {
423 const uint appID = 42;
425 HashSet<Asset> items = [];
427 for (byte i = 0; i < Steam.Exchange.Trading.MaxItemsPerTrade; i++) {
428 items.Add(CreateCard(1, realAppID: appID));
429 items.Add(CreateCard(2, realAppID: appID));
432 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 2, appID);
434 Assert.IsTrue(itemsToSend.Count <= Steam.Exchange.Trading.MaxItemsPerTrade);
437 [TestMethod]
438 public void TooManyCardsForSingleTradeMultipleAppIDs() {
439 const uint appID0 = 42;
440 const uint appID1 = 43;
442 HashSet<Asset> items = [];
444 for (byte i = 0; i < 100; i++) {
445 items.Add(CreateCard(1, realAppID: appID0));
446 items.Add(CreateCard(2, realAppID: appID0));
447 items.Add(CreateCard(1, realAppID: appID1));
448 items.Add(CreateCard(2, realAppID: appID1));
451 Dictionary<uint, byte> itemsPerSet = new() {
452 { appID0, 2 },
453 { appID1, 2 }
456 HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, itemsPerSet);
458 Assert.IsTrue(itemsToSend.Count <= Steam.Exchange.Trading.MaxItemsPerTrade);
461 [TestMethod]
462 public void TooManyCardsPerSet() {
463 const uint appID0 = 42;
464 const uint appID1 = 43;
465 const uint appID2 = 44;
467 HashSet<Asset> items = [
468 CreateCard(1, realAppID: appID0),
469 CreateCard(2, realAppID: appID0),
470 CreateCard(3, realAppID: appID0),
471 CreateCard(4, realAppID: appID0)
474 Assert.ThrowsException<InvalidOperationException>(
475 () => GetItemsForFullBadge(
476 items, new Dictionary<uint, byte> {
477 { appID0, 3 },
478 { appID1, 3 },
479 { appID2, 3 }
485 private static void AssertResultMatchesExpectation(IReadOnlyDictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult, IReadOnlyCollection<Asset> itemsToSend) {
486 ArgumentNullException.ThrowIfNull(expectedResult);
487 ArgumentNullException.ThrowIfNull(itemsToSend);
489 Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), long> realResult = itemsToSend.GroupBy(static asset => (asset.RealAppID, asset.ContextID, asset.ClassID)).ToDictionary(static group => group.Key, static group => group.Sum(static asset => asset.Amount));
490 Assert.AreEqual(expectedResult.Count, realResult.Count);
491 Assert.IsTrue(expectedResult.All(expectation => realResult.TryGetValue(expectation.Key, out long reality) && (expectation.Value == reality)));
494 private static Asset CreateCard(ulong classID, ulong instanceID = 0, uint amount = 1, bool marketable = false, bool tradable = false, uint realAppID = Asset.SteamAppID, EAssetType type = EAssetType.TradingCard, EAssetRarity rarity = EAssetRarity.Common) => new(Asset.SteamAppID, Asset.SteamCommunityContextID, classID, amount, new InventoryDescription(Asset.SteamAppID, classID, instanceID, marketable, tradable, realAppID, type, rarity));
496 private static HashSet<Asset> GetItemsForFullBadge(IReadOnlyCollection<Asset> inventory, byte cardsPerSet, uint appID, ushort maxItems = Steam.Exchange.Trading.MaxItemsPerTrade) => GetItemsForFullBadge(inventory, new Dictionary<uint, byte> { { appID, cardsPerSet } }, maxItems);
498 private static HashSet<Asset> GetItemsForFullBadge(IReadOnlyCollection<Asset> inventory, IDictionary<uint, byte> cardsPerSet, ushort maxItems = Steam.Exchange.Trading.MaxItemsPerTrade) {
499 Dictionary<(uint RealAppID, EAssetType Type, EAssetRarity Rarity), List<uint>> inventorySets = Steam.Exchange.Trading.GetInventorySets(inventory);
501 return GetItemsForFullSets(inventory, inventorySets.ToDictionary(static kv => kv.Key, kv => (SetsToExtract: inventorySets[kv.Key][0], cardsPerSet[kv.Key.RealAppID])), maxItems).ToHashSet();