aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorShadowghost <Ghost_of_Stone@web.de>2026-08-12 07:42:29 +0200
committerShadowghost <Ghost_of_Stone@web.de>2026-08-12 08:15:55 +0200
commit9ae6ffe441586764554ea0e347b540365f069a00 (patch)
treea11513b8b4f459559efce23e09760f9d3c7081a1 /tests
parentfa7fdf58840567c07e85ffb00be4318e12fd021e (diff)
Share the SQLite fixture across the item tests
Diffstat (limited to 'tests')
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Item/AlternateVersionQueryTranslationTests.cs35
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryByNameTotalCountTests.cs54
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryGroupingTests.cs54
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryStreamFilterTests.cs51
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Item/DescendantQueryHelperTests.cs45
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Item/ItemPersistenceOwnedRowTests.cs41
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Item/PeopleRepositoryUpdatePeopleTests.cs38
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Item/SqliteDbTestFixture.cs85
8 files changed, 115 insertions, 288 deletions
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Item/AlternateVersionQueryTranslationTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Item/AlternateVersionQueryTranslationTests.cs
index b7fca74310..2dbcd41a41 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Item/AlternateVersionQueryTranslationTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Item/AlternateVersionQueryTranslationTests.cs
@@ -1,14 +1,9 @@
-#pragma warning disable RS0030 // Do not use banned APIs: Guid == is required inside EF expression trees to mirror the production query shapes.
+#pragma warning disable RS0030 // Do not use banned APIs: Guid == is required inside EF expression trees to mirror the production query shapes.
using System;
using System.Linq;
using Jellyfin.Database.Implementations;
using Jellyfin.Database.Implementations.Entities;
-using Jellyfin.Database.Implementations.Locking;
-using Jellyfin.Database.Providers.Sqlite;
-using Microsoft.Data.Sqlite;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Jellyfin.Server.Implementations.Tests.Item;
@@ -18,22 +13,10 @@ namespace Jellyfin.Server.Implementations.Tests.Item;
/// (BaseItemRepository.TranslateQuery) and the DatePlayed ordering (OrderMapper) translate
/// and evaluate correctly on the SQLite provider.
/// </summary>
-public sealed class AlternateVersionQueryTranslationTests : IDisposable
+public sealed class AlternateVersionQueryTranslationTests : SqliteDbTestFixture
{
- private readonly SqliteConnection _connection;
- private readonly DbContextOptions<JellyfinDbContext> _dbOptions;
-
public AlternateVersionQueryTranslationTests()
{
- _connection = new SqliteConnection("Data Source=:memory:");
- _connection.Open();
-
- _dbOptions = new DbContextOptionsBuilder<JellyfinDbContext>()
- .UseSqlite(_connection)
- .Options;
-
- using var ctx = CreateDbContext();
- ctx.Database.EnsureCreated();
}
[Fact]
@@ -220,18 +203,4 @@ public sealed class AlternateVersionQueryTranslationTests : IDisposable
ctx.SaveChanges();
return (user.Id, primary.Id, versionA.Id, versionB.Id);
}
-
- private JellyfinDbContext CreateDbContext()
- {
- return new JellyfinDbContext(
- _dbOptions,
- NullLogger<JellyfinDbContext>.Instance,
- new SqliteDatabaseProvider(null!, NullLogger<SqliteDatabaseProvider>.Instance),
- new NoLockBehavior(NullLogger<NoLockBehavior>.Instance));
- }
-
- public void Dispose()
- {
- _connection.Dispose();
- }
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryByNameTotalCountTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryByNameTotalCountTests.cs
index f675621e21..7dbaea2fb5 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryByNameTotalCountTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryByNameTotalCountTests.cs
@@ -1,19 +1,10 @@
-using System;
+using System;
using System.Linq;
using Emby.Server.Implementations.Data;
using Jellyfin.Database.Implementations;
using Jellyfin.Database.Implementations.Entities;
-using Jellyfin.Database.Implementations.Locking;
-using Jellyfin.Database.Providers.Sqlite;
using Jellyfin.Server.Implementations.Item;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Configuration;
-using Microsoft.Data.Sqlite;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging.Abstractions;
-using Moq;
using Xunit;
using BaseItemKind = Jellyfin.Data.Enums.BaseItemKind;
@@ -24,46 +15,16 @@ namespace Jellyfin.Server.Implementations.Tests.Item;
/// <c>GetItemValues</c>. A query without a <c>Limit</c> used to have its total record count
/// silently disabled, so callers got a populated <c>Items</c> array next to a zero total.
/// </summary>
-public sealed class BaseItemRepositoryByNameTotalCountTests : IDisposable
+public sealed class BaseItemRepositoryByNameTotalCountTests : SqliteDbTestFixture
{
- private readonly SqliteConnection _connection;
- private readonly DbContextOptions<JellyfinDbContext> _dbOptions;
private readonly BaseItemRepository _repository;
private readonly ItemTypeLookup _itemTypeLookup;
public BaseItemRepositoryByNameTotalCountTests()
{
- _connection = new SqliteConnection("Data Source=:memory:");
- _connection.Open();
-
- _dbOptions = new DbContextOptionsBuilder<JellyfinDbContext>()
- .UseSqlite(_connection)
- .Options;
-
- using (var ctx = CreateDbContext())
- {
- ctx.Database.EnsureCreated();
- }
-
- var factory = new Mock<IDbContextFactory<JellyfinDbContext>>();
- factory.Setup(f => f.CreateDbContext()).Returns(CreateDbContext);
-
_itemTypeLookup = new ItemTypeLookup();
- var serverConfigurationManager = new Mock<IServerConfigurationManager>();
- serverConfigurationManager.Setup(c => c.Configuration).Returns(new ServerConfiguration());
-
- _repository = new BaseItemRepository(
- factory.Object,
- new Mock<IServerApplicationHost>().Object,
- _itemTypeLookup,
- serverConfigurationManager.Object,
- NullLogger<BaseItemRepository>.Instance);
- }
-
- public void Dispose()
- {
- _connection.Dispose();
+ _repository = CreateBaseItemRepository(_itemTypeLookup);
}
[Fact]
@@ -187,13 +148,4 @@ public sealed class BaseItemRepositoryByNameTotalCountTests : IDisposable
ctx.SaveChanges();
}
-
- private JellyfinDbContext CreateDbContext()
- {
- return new JellyfinDbContext(
- _dbOptions,
- NullLogger<JellyfinDbContext>.Instance,
- new SqliteDatabaseProvider(null!, NullLogger<SqliteDatabaseProvider>.Instance),
- new NoLockBehavior(NullLogger<NoLockBehavior>.Instance));
- }
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryGroupingTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryGroupingTests.cs
index 083f725db9..5dd648a2b8 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryGroupingTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryGroupingTests.cs
@@ -1,66 +1,27 @@
-using System;
+using System;
using System.Linq;
using Emby.Server.Implementations.Data;
using Jellyfin.Database.Implementations;
using Jellyfin.Database.Implementations.Entities;
-using Jellyfin.Database.Implementations.Locking;
-using Jellyfin.Database.Providers.Sqlite;
using Jellyfin.Server.Implementations.Item;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
-using Microsoft.Data.Sqlite;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging.Abstractions;
-using Moq;
using Xunit;
using BaseItemKind = Jellyfin.Data.Enums.BaseItemKind;
namespace Jellyfin.Server.Implementations.Tests.Item;
-public sealed class BaseItemRepositoryGroupingTests : IDisposable
+public sealed class BaseItemRepositoryGroupingTests : SqliteDbTestFixture
{
- private readonly SqliteConnection _connection;
- private readonly DbContextOptions<JellyfinDbContext> _dbOptions;
private readonly BaseItemRepository _repository;
private readonly string _movieTypeName;
public BaseItemRepositoryGroupingTests()
{
- _connection = new SqliteConnection("Data Source=:memory:");
- _connection.Open();
-
- _dbOptions = new DbContextOptionsBuilder<JellyfinDbContext>()
- .UseSqlite(_connection)
- .Options;
-
- using (var ctx = CreateDbContext())
- {
- ctx.Database.EnsureCreated();
- }
-
- var factory = new Mock<IDbContextFactory<JellyfinDbContext>>();
- factory.Setup(f => f.CreateDbContext()).Returns(CreateDbContext);
-
var itemTypeLookup = new ItemTypeLookup();
_movieTypeName = itemTypeLookup.BaseItemKindNames[BaseItemKind.Movie];
- var serverConfigurationManager = new Mock<IServerConfigurationManager>();
- serverConfigurationManager.Setup(c => c.Configuration).Returns(new ServerConfiguration());
-
- _repository = new BaseItemRepository(
- factory.Object,
- new Mock<IServerApplicationHost>().Object,
- itemTypeLookup,
- serverConfigurationManager.Object,
- NullLogger<BaseItemRepository>.Instance);
- }
-
- public void Dispose()
- {
- _connection.Dispose();
+ _repository = CreateBaseItemRepository(itemTypeLookup);
}
[Fact]
@@ -132,13 +93,4 @@ public sealed class BaseItemRepositoryGroupingTests : IDisposable
IsVirtualItem = false
};
}
-
- private JellyfinDbContext CreateDbContext()
- {
- return new JellyfinDbContext(
- _dbOptions,
- NullLogger<JellyfinDbContext>.Instance,
- new SqliteDatabaseProvider(null!, NullLogger<SqliteDatabaseProvider>.Instance),
- new NoLockBehavior(NullLogger<NoLockBehavior>.Instance));
- }
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryStreamFilterTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryStreamFilterTests.cs
index 717085b440..3407e2130b 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryStreamFilterTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Item/BaseItemRepositoryStreamFilterTests.cs
@@ -1,37 +1,25 @@
-using System;
+using System;
using System.Linq;
using Emby.Server.Implementations.Data;
using Jellyfin.Database.Implementations;
using Jellyfin.Database.Implementations.Entities;
-using Jellyfin.Database.Implementations.Locking;
-using Jellyfin.Database.Providers.Sqlite;
using Jellyfin.Server.Implementations.Item;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Configuration;
-using Microsoft.Data.Sqlite;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging.Abstractions;
-using Moq;
using Xunit;
using LinkedChildType = Jellyfin.Database.Implementations.Entities.LinkedChildType;
namespace Jellyfin.Server.Implementations.Tests.Item;
/// <summary>
-/// Covers the filters that resolve "folders with a matching descendant" through
-/// <see cref="DescendantQueryHelper.GetFolderIdsMatching"/>, both in their positive and their
-/// negated form, so the sub-selects they build stay translatable on the SQLite provider.
+/// Covers the filters resolving "folders with a matching descendant" through
+/// <see cref="DescendantQueryHelper.GetFolderIdsMatching"/>, positive and negated.
/// </summary>
-public sealed class BaseItemRepositoryStreamFilterTests : IDisposable
+public sealed class BaseItemRepositoryStreamFilterTests : SqliteDbTestFixture
{
private const string FolderType = "MediaBrowser.Controller.Entities.Folder";
private const string BoxSetType = "MediaBrowser.Controller.Entities.Movies.BoxSet";
private const string MovieType = "MediaBrowser.Controller.Entities.Movies.Movie";
- private readonly SqliteConnection _connection;
- private readonly DbContextOptions<JellyfinDbContext> _dbOptions;
private readonly BaseItemRepository _repository;
private readonly Guid _library = Guid.NewGuid();
@@ -43,35 +31,14 @@ public sealed class BaseItemRepositoryStreamFilterTests : IDisposable
public BaseItemRepositoryStreamFilterTests()
{
- _connection = new SqliteConnection("Data Source=:memory:");
- _connection.Open();
-
- _dbOptions = new DbContextOptionsBuilder<JellyfinDbContext>()
- .UseSqlite(_connection)
- .Options;
-
using (var ctx = CreateDbContext())
{
- ctx.Database.EnsureCreated();
Seed(ctx);
}
- var factory = new Mock<IDbContextFactory<JellyfinDbContext>>();
- factory.Setup(f => f.CreateDbContext()).Returns(CreateDbContext);
-
- var serverConfigurationManager = new Mock<IServerConfigurationManager>();
- serverConfigurationManager.Setup(c => c.Configuration).Returns(new ServerConfiguration());
-
- _repository = new BaseItemRepository(
- factory.Object,
- new Mock<IServerApplicationHost>().Object,
- new ItemTypeLookup(),
- serverConfigurationManager.Object,
- NullLogger<BaseItemRepository>.Instance);
+ _repository = CreateBaseItemRepository(new ItemTypeLookup());
}
- public void Dispose() => _connection.Dispose();
-
[Fact]
public void HasSubtitles_MatchesTheItemAndItsParentFolder()
{
@@ -115,7 +82,6 @@ public sealed class BaseItemRepositoryStreamFilterTests : IDisposable
{
var ids = _repository.GetItemIdsList(new InternalItemsQuery { HasSubtitles = true });
- // The collection links the series, and the subtitles hang off the series' episode.
Assert.Contains(_linkedSeries, ids);
Assert.Contains(_collection, ids);
}
@@ -214,11 +180,4 @@ public sealed class BaseItemRepositoryStreamFilterTests : IDisposable
context.SaveChanges();
}
-
- private JellyfinDbContext CreateDbContext()
- => new JellyfinDbContext(
- _dbOptions,
- NullLogger<JellyfinDbContext>.Instance,
- new SqliteDatabaseProvider(null!, NullLogger<SqliteDatabaseProvider>.Instance),
- new NoLockBehavior(NullLogger<NoLockBehavior>.Instance));
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Item/DescendantQueryHelperTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Item/DescendantQueryHelperTests.cs
index fae2dd0628..6cd31d9243 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Item/DescendantQueryHelperTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Item/DescendantQueryHelperTests.cs
@@ -1,14 +1,10 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using Jellyfin.Database.Implementations;
using Jellyfin.Database.Implementations.Entities;
-using Jellyfin.Database.Implementations.Locking;
using Jellyfin.Database.Implementations.MatchCriteria;
-using Jellyfin.Database.Providers.Sqlite;
-using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Jellyfin.Server.Implementations.Tests.Item;
@@ -17,31 +13,18 @@ namespace Jellyfin.Server.Implementations.Tests.Item;
/// Verifies the descendant traversals against the SQLite provider: the sets they resolve, and that
/// they stay sub-selects instead of inlining every descendant id into the statement.
/// </summary>
-public sealed class DescendantQueryHelperTests : IDisposable
+public sealed class DescendantQueryHelperTests : SqliteDbTestFixture
{
private const string FolderType = "MediaBrowser.Controller.Entities.Folder";
private const string BoxSetType = "MediaBrowser.Controller.Entities.Movies.BoxSet";
private const string MovieType = "MediaBrowser.Controller.Entities.Movies.Movie";
private readonly Dictionary<Guid, int> _linkCounters = new();
- private readonly SqliteConnection _connection;
- private readonly DbContextOptions<JellyfinDbContext> _dbOptions;
public DescendantQueryHelperTests()
{
- _connection = new SqliteConnection("Data Source=:memory:");
- _connection.Open();
-
- _dbOptions = new DbContextOptionsBuilder<JellyfinDbContext>()
- .UseSqlite(_connection)
- .Options;
-
- using var ctx = CreateDbContext();
- ctx.Database.EnsureCreated();
}
- public void Dispose() => _connection.Dispose();
-
[Fact]
public void GetAllDescendantIds_Hierarchy_ReturnsEveryLevelWithoutTheParent()
{
@@ -95,7 +78,6 @@ public sealed class DescendantQueryHelperTests : IDisposable
{
var descendants = DescendantQueryHelper.GetAllDescendantIds(ctx, boxSet).ToHashSet();
- // The link reaches the series, and the series' own closure reaches the episode.
Assert.Contains(series, descendants);
Assert.Contains(episode, descendants);
}
@@ -117,7 +99,7 @@ public sealed class DescendantQueryHelperTests : IDisposable
AddLink(ctx, outer, inner);
AddLink(ctx, inner, movie);
- // Cycle back to the outer set: the traversal must not spin on it.
+ // The traversal must not spin on this cycle.
AddLink(ctx, inner, outer);
ctx.SaveChanges();
}
@@ -179,8 +161,8 @@ public sealed class DescendantQueryHelperTests : IDisposable
AddItem(ctx, boxSet, BoxSetType, isFolder: true);
AddItem(ctx, linkedMovie, MovieType);
- // How production writes it: an item carries its own chain plus its collection folder, but
- // not the user root above that folder - so one hop from the user root stops there.
+ // An item carries its own chain plus its collection folder, but not the user root above
+ // it, so one hop from the user root stops at the collection folder.
AddAncestors(ctx, collectionFolder, userRoot);
AddAncestors(ctx, series, collectionFolder);
AddAncestors(ctx, episode, series, collectionFolder);
@@ -264,7 +246,6 @@ public sealed class DescendantQueryHelperTests : IDisposable
AddLink(ctx, boxSet, series);
AddStream(ctx, episode, MediaStreamTypeEntity.Subtitle);
- // A second collection, over an item without subtitles, must not be picked up.
AddFolder(ctx, otherLibrary);
AddItem(ctx, otherBoxSet, BoxSetType, isFolder: true);
AddItem(ctx, silentMovie, MovieType);
@@ -302,7 +283,7 @@ public sealed class DescendantQueryHelperTests : IDisposable
AddLink(ctx, outer, inner);
AddLink(ctx, inner, movie);
- // Cycle back to the outer set: resolving the link parents must not spin on it.
+ // Resolving the link parents must not spin on this cycle.
AddLink(ctx, inner, outer);
AddStream(ctx, movie, MediaStreamTypeEntity.Subtitle);
@@ -337,8 +318,7 @@ public sealed class DescendantQueryHelperTests : IDisposable
AddFolder(ctx, series);
AddItem(ctx, episode, MovieType);
- // How production writes it: an item carries its own chain plus its collection folder, but
- // not the user root above that folder - so the closure is not transitive at this seam.
+ // The closure is not transitive at this seam: no item records the user root.
AddAncestors(ctx, episode, series, collectionFolder);
AddAncestors(ctx, series, collectionFolder);
AddAncestors(ctx, collectionFolder, userRoot);
@@ -419,8 +399,8 @@ public sealed class DescendantQueryHelperTests : IDisposable
var smallSql = CountingQuery(ctx, small).ToQueryString();
var largeSql = CountingQuery(ctx, large).ToQueryString();
- // Reading the ids into memory and passing them back as AsQueryable() makes EF inline one
- // literal per descendant, which is what made a large library allocate megabytes per call.
+ // Reading the ids into memory and handing them back as AsQueryable() makes EF inline one
+ // literal per descendant, which is what allocated megabytes per call.
Assert.Equal(smallSql.Length, largeSql.Length);
Assert.Contains("AncestorIds", smallSql, StringComparison.Ordinal);
Assert.Equal(10, CountingQuery(ctx, small).Count());
@@ -505,11 +485,4 @@ public sealed class DescendantQueryHelperTests : IDisposable
SortOrder = sortOrder
});
}
-
- private JellyfinDbContext CreateDbContext()
- => new JellyfinDbContext(
- _dbOptions,
- NullLogger<JellyfinDbContext>.Instance,
- new SqliteDatabaseProvider(null!, NullLogger<SqliteDatabaseProvider>.Instance),
- new NoLockBehavior(NullLogger<NoLockBehavior>.Instance));
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Item/ItemPersistenceOwnedRowTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Item/ItemPersistenceOwnedRowTests.cs
index 6324706452..9b78a609ab 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Item/ItemPersistenceOwnedRowTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Item/ItemPersistenceOwnedRowTests.cs
@@ -1,51 +1,29 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Jellyfin.Database.Implementations;
-using Jellyfin.Database.Implementations.Locking;
-using Jellyfin.Database.Providers.Sqlite;
using Jellyfin.Server.Implementations.Item;
-using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
-using Microsoft.Data.Sqlite;
-using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;
namespace Jellyfin.Server.Implementations.Tests.Item;
-public sealed class ItemPersistenceOwnedRowTests : IDisposable
+public sealed class ItemPersistenceOwnedRowTests : SqliteDbTestFixture
{
- private readonly SqliteConnection _connection;
- private readonly DbContextOptions<JellyfinDbContext> _dbOptions;
private readonly ItemPersistenceService _service;
- private readonly IApplicationPaths _applicationPaths;
private readonly ILibraryManager? _previousLibraryManager;
private readonly IServerConfigurationManager? _previousConfigurationManager;
public ItemPersistenceOwnedRowTests()
{
- _applicationPaths = new Mock<IApplicationPaths>().Object;
-
- _connection = new SqliteConnection("Data Source=:memory:");
- _connection.Open();
-
- _dbOptions = new DbContextOptionsBuilder<JellyfinDbContext>()
- .UseSqlite(_connection)
- .Options;
-
- using (var ctx = CreateDbContext())
- {
- ctx.Database.EnsureCreated();
- }
-
// BaseItem resolves these through process-wide statics; restored in Dispose.
_previousLibraryManager = BaseItem.LibraryManager;
_previousConfigurationManager = BaseItem.ConfigurationManager;
@@ -59,20 +37,17 @@ public sealed class ItemPersistenceOwnedRowTests : IDisposable
configurationManager.Setup(c => c.Configuration).Returns(new ServerConfiguration());
BaseItem.ConfigurationManager = configurationManager.Object;
- var factory = new Mock<IDbContextFactory<JellyfinDbContext>>();
- factory.Setup(f => f.CreateDbContext()).Returns(CreateDbContext);
-
_service = new ItemPersistenceService(
- factory.Object,
+ CreateDbContextFactory(),
new Mock<IServerApplicationHost>().Object,
NullLogger<ItemPersistenceService>.Instance);
}
- public void Dispose()
+ protected override void Dispose(bool disposing)
{
BaseItem.LibraryManager = _previousLibraryManager!;
BaseItem.ConfigurationManager = _previousConfigurationManager!;
- _connection.Dispose();
+ base.Dispose(disposing);
}
[Fact]
@@ -140,10 +115,4 @@ public sealed class ItemPersistenceOwnedRowTests : IDisposable
book.SetImage(new ItemImageInfo { Path = "/img/primary.jpg", Type = ImageType.Primary }, 0);
return book;
}
-
- private JellyfinDbContext CreateDbContext() => new(
- _dbOptions,
- NullLogger<JellyfinDbContext>.Instance,
- new SqliteDatabaseProvider(_applicationPaths, NullLogger<SqliteDatabaseProvider>.Instance),
- new NoLockBehavior(NullLogger<NoLockBehavior>.Instance));
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Item/PeopleRepositoryUpdatePeopleTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Item/PeopleRepositoryUpdatePeopleTests.cs
index 70d8e1f833..83465245fa 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Item/PeopleRepositoryUpdatePeopleTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Item/PeopleRepositoryUpdatePeopleTests.cs
@@ -1,45 +1,30 @@
-using System;
+using System;
using System.Linq;
using Emby.Server.Implementations.Data;
using Jellyfin.Data.Enums;
using Jellyfin.Database.Implementations;
using Jellyfin.Database.Implementations.Entities;
-using Jellyfin.Database.Implementations.Locking;
-using Jellyfin.Database.Providers.Sqlite;
using Jellyfin.Server.Implementations.Item;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Persistence;
-using Microsoft.Data.Sqlite;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;
using BaseItemKind = Jellyfin.Data.Enums.BaseItemKind;
namespace Jellyfin.Server.Implementations.Tests.Item;
-public sealed class PeopleRepositoryUpdatePeopleTests : IDisposable
+public sealed class PeopleRepositoryUpdatePeopleTests : SqliteDbTestFixture
{
private static readonly Guid _itemId = Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
- private readonly SqliteConnection _connection;
- private readonly DbContextOptions<JellyfinDbContext> _dbOptions;
private readonly PeopleRepository _repository;
public PeopleRepositoryUpdatePeopleTests()
{
- _connection = new SqliteConnection("Data Source=:memory:");
- _connection.Open();
-
- _dbOptions = new DbContextOptionsBuilder<JellyfinDbContext>()
- .UseSqlite(_connection)
- .Options;
-
var itemTypeLookup = new ItemTypeLookup();
using (var ctx = CreateDbContext())
{
- ctx.Database.EnsureCreated();
ctx.BaseItems.Add(new BaseItemEntity
{
Id = _itemId,
@@ -53,20 +38,12 @@ public sealed class PeopleRepositoryUpdatePeopleTests : IDisposable
ctx.SaveChanges();
}
- var factory = new Mock<IDbContextFactory<JellyfinDbContext>>();
- factory.Setup(f => f.CreateDbContext()).Returns(CreateDbContext);
-
_repository = new PeopleRepository(
- factory.Object,
+ CreateDbContextFactory(),
itemTypeLookup,
new Mock<IItemQueryHelpers>().Object);
}
- public void Dispose()
- {
- _connection.Dispose();
- }
-
[Fact]
public void UpdatePeople_SamePersonAndTypeWithDifferentRoles_KeepsEveryCredit()
{
@@ -174,13 +151,4 @@ public sealed class PeopleRepositoryUpdatePeopleTests : IDisposable
Role = role
};
}
-
- private JellyfinDbContext CreateDbContext()
- {
- return new JellyfinDbContext(
- _dbOptions,
- NullLogger<JellyfinDbContext>.Instance,
- new SqliteDatabaseProvider(null!, NullLogger<SqliteDatabaseProvider>.Instance),
- new NoLockBehavior(NullLogger<NoLockBehavior>.Instance));
- }
}
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Item/SqliteDbTestFixture.cs b/tests/Jellyfin.Server.Implementations.Tests/Item/SqliteDbTestFixture.cs
new file mode 100644
index 0000000000..6be8244c1e
--- /dev/null
+++ b/tests/Jellyfin.Server.Implementations.Tests/Item/SqliteDbTestFixture.cs
@@ -0,0 +1,85 @@
+using System;
+using Emby.Server.Implementations.Data;
+using Jellyfin.Database.Implementations;
+using Jellyfin.Database.Implementations.Locking;
+using Jellyfin.Database.Providers.Sqlite;
+using Jellyfin.Server.Implementations.Item;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Configuration;
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging.Abstractions;
+using Moq;
+
+namespace Jellyfin.Server.Implementations.Tests.Item;
+
+/// <summary>
+/// Base fixture for the item tests that run against the SQLite provider: one in-memory database per
+/// test class, plus the wiring the repositories under test need. The connection owns the database, so
+/// it stays open for the lifetime of the fixture. Derived classes seed in their own constructor.
+/// </summary>
+public abstract class SqliteDbTestFixture : IDisposable
+{
+ private readonly SqliteConnection _connection;
+ private readonly DbContextOptions<JellyfinDbContext> _dbOptions;
+
+ protected SqliteDbTestFixture()
+ {
+ ApplicationPaths = new Mock<IApplicationPaths>().Object;
+
+ _connection = new SqliteConnection("Data Source=:memory:");
+ _connection.Open();
+
+ _dbOptions = new DbContextOptionsBuilder<JellyfinDbContext>()
+ .UseSqlite(_connection)
+ .Options;
+
+ using var context = CreateDbContext();
+ context.Database.EnsureCreated();
+ }
+
+ protected IApplicationPaths ApplicationPaths { get; }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected JellyfinDbContext CreateDbContext() => new(
+ _dbOptions,
+ NullLogger<JellyfinDbContext>.Instance,
+ new SqliteDatabaseProvider(ApplicationPaths, NullLogger<SqliteDatabaseProvider>.Instance),
+ new NoLockBehavior(NullLogger<NoLockBehavior>.Instance));
+
+ protected IDbContextFactory<JellyfinDbContext> CreateDbContextFactory()
+ {
+ var factory = new Mock<IDbContextFactory<JellyfinDbContext>>();
+ factory.Setup(f => f.CreateDbContext()).Returns(CreateDbContext);
+
+ return factory.Object;
+ }
+
+ protected BaseItemRepository CreateBaseItemRepository(ItemTypeLookup itemTypeLookup)
+ {
+ var serverConfigurationManager = new Mock<IServerConfigurationManager>();
+ serverConfigurationManager.Setup(c => c.Configuration).Returns(new ServerConfiguration());
+
+ return new BaseItemRepository(
+ CreateDbContextFactory(),
+ new Mock<IServerApplicationHost>().Object,
+ itemTypeLookup,
+ serverConfigurationManager.Object,
+ NullLogger<BaseItemRepository>.Instance);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _connection.Dispose();
+ }
+ }
+}