Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions GrowthBook.Tests/Api/GrowthBookFileBasedTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System.Collections.Generic;
using FluentAssertions;
using Xunit;

namespace GrowthBook.Tests.Api
{
/// <summary>
/// Tests demonstrating GrowthBook with file-based cache (Swift-like).
/// </summary>
public class GrowthBookFileBasedTests
{
[Fact]
public void GrowthBook_DefaultConstruction_UsesFileBased()
{
// Arrange & Act
var context = new Context();
using var growthBook = new GrowthBook(context);

// Assert
growthBook.Should().NotBeNull();
// File-based cache is now the default (no configuration needed)
}

[Fact]
public void GrowthBook_WithCustomCachePath_ShouldWork()
{
// Arrange
var context = new Context
{
CachePath = "/tmp/growthbook-test"
};

// Act & Assert
using var growthBook = new GrowthBook(context);
growthBook.Should().NotBeNull();
}

[Fact]
public void GrowthBook_WithFeatures_ShouldWork()
{
// Arrange
var context = new Context
{
Features = new Dictionary<string, Feature>
{
["test-feature"] = new Feature
{
DefaultValue = "file-cached-value"
}
}
};

// Act
using var growthBook = new GrowthBook(context);
var value = growthBook.GetFeatureValue("test-feature", "fallback");

// Assert
value.Should().Be("file-cached-value");
}
}
}
70 changes: 70 additions & 0 deletions GrowthBook.Tests/Api/InMemoryFeatureCacheTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FluentAssertions;
using GrowthBook.Api;
using Xunit;

namespace GrowthBook.Tests.Api
{
/// <summary>
/// Tests for file-based feature cache - similar to Swift CachingManager tests.
/// </summary>
public class InMemoryFeatureCacheTests : IDisposable
{
private readonly InMemoryFeatureCache _cache;

public InMemoryFeatureCacheTests()
{
_cache = new InMemoryFeatureCache();
}

[Fact]
public async Task TestCaching()
{
// Arrange
var features = new Dictionary<string, Feature>
{
["GrowthBook"] = new Feature { DefaultValue = "GrowthBook" }
};

// Act
await _cache.RefreshWith(features);
var retrievedFeatures = await _cache.GetFeatures();

// Assert
retrievedFeatures.Should().ContainKey("GrowthBook");
retrievedFeatures["GrowthBook"].DefaultValue.ToString().Should().Be("GrowthBook");
}

[Fact]
public async Task TestClearCache()
{
// Arrange
var features = new Dictionary<string, Feature>
{
["GrowthBook"] = new Feature { DefaultValue = "GrowthBook" }
};

// Act
await _cache.RefreshWith(features);
_cache.ClearCache();
var retrievedFeatures = await _cache.GetFeatures();

// Assert
retrievedFeatures.Should().BeEmpty();
}

[Fact]
public void TestSetCacheKey()
{
// Act & Assert
_cache.Invoking(c => c.SetCacheKey("test-key")).Should().NotThrow();
}

public void Dispose()
{
_cache?.ClearCache();
}
}
}
4 changes: 3 additions & 1 deletion GrowthBook.Tests/ApiTests/FeatureRepositoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public async Task GettingFeaturesWhenApiCallIsRequiredWithoutWaitingForRetrieval
.Returns(_availableFeatures);
_backgroundWorker
.RefreshCacheFromApi(Arg.Any<CancellationToken?>())
.Returns(_availableFeatures);
.Returns(Task.FromResult<IDictionary<string, Feature>>(_availableFeatures));

var options = isForcedRefresh switch
{
Expand All @@ -77,6 +77,8 @@ public async Task GettingFeaturesWhenApiCallIsRequiredWithoutWaitingForRetrieval
var features = await _featureRepository.GetFeatures(options);
await Task.Delay(50);

await Task.Delay(10);

_ = _cache.Received(2).IsCacheExpired;
_ = _cache.Received(2).FeatureCount;
// Remove this line - cache.GetFeatures is not called when WaitForCompletion = true
Expand Down
1 change: 1 addition & 0 deletions GrowthBook.Tests/ApiTests/InMemoryFeatureCacheTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class InMemoryFeatureCacheTests : UnitTest
public InMemoryFeatureCacheTests()
{
_cache = new(60);
_cache.ClearCache();

_firstFeature = new() { DefaultValue = 1 };
_secondFeature = new() { DefaultValue = 2 };
Expand Down
171 changes: 0 additions & 171 deletions GrowthBook.Tests/CustomTests/ThreadingIssueTests.cs

This file was deleted.

15 changes: 8 additions & 7 deletions GrowthBook/Api/FeatureRefreshWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,22 @@ public void Cancel()

public async Task<IDictionary<string, Feature>> RefreshCacheFromApi(CancellationToken? cancellationToken = null)
{
_logger.LogInformation("Making an HTTP request to the default Features API endpoint \'{FeaturesApiEndpoint}\'", _featuresApiEndpoint);
_logger.LogInformation("Making an HTTP request to the default Features API endpoint '{FeaturesApiEndpoint}'", _featuresApiEndpoint);

var httpClient = _httpClientFactory.CreateClient(ConfiguredClients.DefaultApiClient);

var response = await httpClient.GetFeaturesFrom(_featuresApiEndpoint, _logger, _config, cancellationToken ?? _refreshWorkerCancellation.Token);
var response = await httpClient
.GetFeaturesFrom(_featuresApiEndpoint, _logger, _config, cancellationToken ?? _refreshWorkerCancellation.Token)
.ConfigureAwait(false);

if (response.Features is null)
{
return null;
}

await _cache.RefreshWith(response.Features, cancellationToken);

// Now that the cache has been populated at least once, we need to see if we're allowed
// to kick off the server sent events listener and make sure we're in the intended mode
// of operating going forward.
await _cache
.RefreshWith(response.Features, cancellationToken)
.ConfigureAwait(false);

if (_config.PreferServerSentEvents)
{
Expand All @@ -87,6 +87,7 @@ public async Task<IDictionary<string, Feature>> RefreshCacheFromApi(Cancellation
return response.Features;
}


private void EnsureCorrectRefreshModeIsActive()
{
if (_isServerSentEventsEnabled)
Expand Down
Loading
Loading