diff --git a/SimcProfileParser.Tests/DataSync/CacheServiceTests.cs b/SimcProfileParser.Tests/DataSync/CacheServiceTests.cs index 9d06bcc..b06c0ea 100644 --- a/SimcProfileParser.Tests/DataSync/CacheServiceTests.cs +++ b/SimcProfileParser.Tests/DataSync/CacheServiceTests.cs @@ -244,5 +244,39 @@ public void CS_Ptr_Defaults_Off() // Assert Assert.That(url, Is.EqualTo("https://raw.githubusercontent.com/simulationcraft/simc/test_branch/engine/dbc/generated/test.inc")); } + + [Test] + public async Task CS_ClearCache_Deletes_Files_And_Recovers() + { + // Arrange + CacheService cache = new CacheService(null, _loggerFactory.CreateLogger()); + + var configuration = new CacheFileConfiguration() + { + LocalParsedFile = "CombatRatingMultipliers.json", + ParsedFileType = SimcParsedFileType.CombatRatingMultipliers, + RawFiles = new Dictionary() + { + { "ScaleData.raw", "sc_scale_data" } + } + }; + var filePath = Path.Combine(cache.BaseFileDirectory, "ScaleData.raw"); + + // Ensure a file exists by downloading it + _ = await cache.GetRawFileContentsAsync(configuration, "ScaleData.raw"); + FileAssert.Exists(filePath); + + // Act: clear the cache + await cache.ClearCacheAsync(); + + // Assert: file was deleted + Assert.That(File.Exists(filePath), Is.False, "Cache file should be removed after clear."); + + // Act again: accessing should re-download + _ = await cache.GetRawFileContentsAsync(configuration, "ScaleData.raw"); + + // Assert: file is back + FileAssert.Exists(filePath); + } } } diff --git a/SimcProfileParser.Tests/SimcGenerationServiceIntegrationTests.cs b/SimcProfileParser.Tests/SimcGenerationServiceIntegrationTests.cs index ce18d10..d61e788 100644 --- a/SimcProfileParser.Tests/SimcGenerationServiceIntegrationTests.cs +++ b/SimcProfileParser.Tests/SimcGenerationServiceIntegrationTests.cs @@ -135,5 +135,27 @@ public async Task SGS_Gets_Game_Version() ClassicAssert.IsNotNull(version); ClassicAssert.AreEqual("12.", version.Substring(0, 3)); } + + [Test] + public async Task SGS_ClearsCache() + { + // Arrange: Warm up by generating something that touches the cache + var spellOptions = new SimcSpellOptions() + { + ItemLevel =226, + SpellId =343538, + ItemQuality = ItemQuality.ITEM_QUALITY_EPIC, + ItemInventoryType = InventoryType.INVTYPE_TRINKET + }; + var spell = await _sgs.GenerateSpellAsync(spellOptions); + ClassicAssert.IsNotNull(spell); + + // Act: clear the cache + await _sgs.ClearCacheAsync(); + + // Assert: call again should still work (re-hydrates cache) + var spell2 = await _sgs.GenerateSpellAsync(spellOptions); + ClassicAssert.IsNotNull(spell2); + } } } diff --git a/SimcProfileParser/DataSync/CacheService.cs b/SimcProfileParser/DataSync/CacheService.cs index 0ded226..fad6587 100644 --- a/SimcProfileParser/DataSync/CacheService.cs +++ b/SimcProfileParser/DataSync/CacheService.cs @@ -501,5 +501,34 @@ public void SetUseBranchName(string branchName) { _useBranchName = branchName; } + + public async Task ClearCacheAsync() + { + // Clear in-memory caches + _cachedFileData.Clear(); + _eTagCacheData.Clear(); + + // Clear on-disk cache + try + { + if (Directory.Exists(BaseFileDirectory)) + { + foreach (var file in Directory.GetFiles(BaseFileDirectory)) + { + try { File.Delete(file); } + catch (Exception ex) + { + _logger?.LogWarning(ex, "Failed to delete cache file {file}", file); + } + } + } + } + catch (Exception ex) + { + _logger?.LogError(ex, "Error clearing cache directory {BaseFileDirectory}", BaseFileDirectory); + } + + await Task.CompletedTask; + } } } diff --git a/SimcProfileParser/Interfaces/DataSync/ICacheService.cs b/SimcProfileParser/Interfaces/DataSync/ICacheService.cs index a5ce353..1895034 100644 --- a/SimcProfileParser/Interfaces/DataSync/ICacheService.cs +++ b/SimcProfileParser/Interfaces/DataSync/ICacheService.cs @@ -56,5 +56,10 @@ public interface ICacheService /// /// e.g. thewarwithin void SetUseBranchName(string branchName); + + /// + /// Clears all cached data from memory and disk. + /// + Task ClearCacheAsync(); } } diff --git a/SimcProfileParser/Interfaces/ISimcGenerationService.cs b/SimcProfileParser/Interfaces/ISimcGenerationService.cs index b12b9cf..44e177e 100644 --- a/SimcProfileParser/Interfaces/ISimcGenerationService.cs +++ b/SimcProfileParser/Interfaces/ISimcGenerationService.cs @@ -27,6 +27,11 @@ public interface ISimcGenerationService Task GenerateSpellAsync(SimcSpellOptions options); Task GetGameDataVersionAsync(); + + /// + /// Clears all cached data used by the generator (both memory and disk cache). + /// + Task ClearCacheAsync(); // TODO: Make this public once implemented. //Task> GetAvailableTalentsAsync(int classId, int specId); } diff --git a/SimcProfileParser/SimcGenerationService.cs b/SimcProfileParser/SimcGenerationService.cs index e13ef7d..121b0dd 100644 --- a/SimcProfileParser/SimcGenerationService.cs +++ b/SimcProfileParser/SimcGenerationService.cs @@ -477,5 +477,24 @@ public string UseBranchName get => _cacheService.UseBranchName; set => _cacheService.SetUseBranchName(value); } + + /// + /// Clears the cached data for profiles, items, spells, and talents. + /// + /// + /// + /// This method will remove all cached entries associated with the current character or profile. + /// It is useful for refreshing data after game updates, profile changes, or debugging. + /// + /// + /// After clearing the cache, the next profile generation will + /// re-download and re-process all required data from SimulationCraft's data files. + /// + /// + /// + public async Task ClearCacheAsync() + { + await _cacheService.ClearCacheAsync(); + } } } diff --git a/SimcProfileParser/SimcProfileParser.csproj b/SimcProfileParser/SimcProfileParser.csproj index aae83db..41716d6 100644 --- a/SimcProfileParser/SimcProfileParser.csproj +++ b/SimcProfileParser/SimcProfileParser.csproj @@ -3,9 +3,9 @@ net9.0 true - 3.1.0 - 3.1.0 - 3.1.0 + 3.1.1 + 3.1.1 + 3.1.1 Mechanical Priest Mechanical Priest GPL-3.0-only