using System;
using System.IO;
using System.Threading.Tasks;
using Emby.Server.Implementations.MediaEncoding;
using Jellyfin.Server;
using Jellyfin.Server.Extensions;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.MediaEncoding;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using StackExchange.Redis;
using Testcontainers.Redis;
using Xunit;
namespace Jellyfin.Server.Implementations.Tests.MediaEncoding;
///
/// Drives the whole configuration path a deployment uses: a bare Jellyfin__TranscodeStore__*
/// environment variable, the server's own configuration builder, the store registration, and a
/// session round-trip against a real Valkey server.
///
[Trait("Category", "RequiresDocker")]
public sealed class TranscodeStoreWiringTests : IAsyncLifetime
{
private const string RedisConnectionStringVariable = "Jellyfin__TranscodeStore__RedisConnectionString";
private readonly RedisContainer _container;
private string _configDirectory = string.Empty;
///
/// Initializes a new instance of the class.
///
public TranscodeStoreWiringTests()
{
_container = new RedisBuilder("valkey/valkey:8-alpine").Build();
}
///
/// Starts Valkey and lays out the configuration directory the server reads at startup.
///
/// A representing the asynchronous operation.
public async ValueTask InitializeAsync()
{
await _container.StartAsync().ConfigureAwait(false);
_configDirectory = Directory.CreateTempSubdirectory("jellyfin-wiring-test").FullName;
await File.WriteAllTextAsync(Path.Combine(_configDirectory, "logging.default.json"), "{}").ConfigureAwait(false);
}
///
/// Removes the environment variable, configuration directory and container.
///
/// A representing the asynchronous operation.
public async ValueTask DisposeAsync()
{
Environment.SetEnvironmentVariable(RedisConnectionStringVariable, null);
if (_configDirectory.Length > 0)
{
Directory.Delete(_configDirectory, true);
}
await _container.DisposeAsync().ConfigureAwait(false);
}
///
/// The variable form deployments set selects the Redis store and that store really talks to Valkey.
///
/// A representing the asynchronous operation.
[Fact]
public async Task ManifestStyleEnvironmentVariable_Should_Reach_Valkey()
{
Environment.SetEnvironmentVariable(
RedisConnectionStringVariable,
_container.GetConnectionString() + ",abortConnect=false");
var appPaths = new Mock();
appPaths.Setup(paths => paths.ConfigurationDirectoryPath).Returns(_configDirectory);
var configuration = Program.CreateAppConfiguration(new StartupOptions(), appPaths.Object);
var services = new ServiceCollection();
services.AddLogging();
services.AddTranscodeSessionStore(configuration, NullLogger.Instance);
await using var provider = services.BuildServiceProvider();
var store = provider.GetRequiredService();
Assert.IsType(store);
var playSessionId = Guid.NewGuid().ToString("N");
await store.SetAsync(
TranscodeSession.CreateForPlaylist(playSessionId, "media-1", "pod-a", "/transcodes/abc.m3u8", TimeSpan.FromSeconds(30)),
TestContext.Current.CancellationToken);
var stored = await store.TryGetAsync(playSessionId, TestContext.Current.CancellationToken);
Assert.NotNull(stored);
Assert.Equal("pod-a", stored.OwnerPod);
var redis = provider.GetRequiredService();
Assert.True(await redis.GetDatabase().KeyExistsAsync("jellyfin:transcode:" + playSessionId));
}
}