PR Gates / CI image pin matches docker/ci (pull_request) Successful in 14s
PR Gates / Docs update reminder (pull_request) Successful in 16s
PR Gates / decisions lifecycle (pull_request) Successful in 25s
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 5m51s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 8s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 8s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 17m10s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 21m38s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Follow-up to the cold review of 615e00a. The record was corrected to say only that no NEW sentinel rows are created, but the test file's header still carried the disclaimed "the data is clean at rest for every provider" claim — sitting on the very test that supposedly proved it. It now states what the tests actually pin, and names what they cannot speak to (rows written before this change, which keep the sentinel and are covered only by the read coercion). Also corrects docs/testing.md's ErsatzTV.Core.Tests count (543 -> ~650 measured; the neighbouring Scanner.Tests cell is corrected in #602 instead, to keep the two open PRs off the same line).
110 lines
4.0 KiB
C#
110 lines
4.0 KiB
C#
using ErsatzTV.Core.Domain;
|
|
using ErsatzTV.Infrastructure.Data;
|
|
using ErsatzTV.Infrastructure.Data.Repositories;
|
|
using ErsatzTV.Tests.Support;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using NUnit.Framework;
|
|
using Shouldly;
|
|
|
|
namespace ErsatzTV.Tests.Integration;
|
|
|
|
// ersatztv#460: the disable-sync (remove-and-recreate) flows used to stamp the 0001-01-01 MinValue
|
|
// sentinel into Library.LastScan. #409 fixed the READ side (the API coerces the sentinel to null and a
|
|
// migration cleaned historical rows), so the wire contract was already correct — these tests pin the
|
|
// WRITE side for every provider, so no NEW sentinel rows are created. They do not (and cannot) speak to
|
|
// rows already written before this change: those keep the sentinel at rest and are covered only by the
|
|
// read-boundary coercion, since this ships no second cleanup migration.
|
|
[TestFixture]
|
|
public class MediaSourceRepositoryDisableSyncTests
|
|
{
|
|
private InMemoryTvContext _db = null!;
|
|
private MediaSourceRepository _repository = null!;
|
|
|
|
[SetUp]
|
|
public async Task SetUp()
|
|
{
|
|
_db = await InMemoryTvContext.CreateAsync();
|
|
_repository = new MediaSourceRepository(_db.Factory, NullLogger<MediaSourceRepository>.Instance);
|
|
}
|
|
|
|
[TearDown]
|
|
public async Task TearDown() => await _db.DisposeAsync();
|
|
|
|
[Test]
|
|
public async Task DisablePlexLibrarySync_Should_Null_LastScan_Not_Write_MinValue()
|
|
{
|
|
int libraryId = await Seed(new PlexLibrary
|
|
{
|
|
Name = "Movies",
|
|
MediaKind = LibraryMediaKind.Movies,
|
|
MediaSourceId = 1,
|
|
Key = "key1",
|
|
ShouldSyncItems = true,
|
|
LastScan = new DateTime(2026, 7, 1, 12, 0, 0, DateTimeKind.Utc),
|
|
Paths = [new LibraryPath { Path = "/data/movies" }]
|
|
});
|
|
|
|
await _repository.DisablePlexLibrarySync([libraryId]);
|
|
|
|
await using TvContext context = _db.CreateContext();
|
|
PlexLibrary library = await context.PlexLibraries.SingleAsync(l => l.Name == "Movies");
|
|
library.LastScan.ShouldBeNull();
|
|
library.ShouldSyncItems.ShouldBeFalse();
|
|
}
|
|
|
|
[Test]
|
|
public async Task DisableJellyfinLibrarySync_Should_Null_LastScan_Not_Write_MinValue()
|
|
{
|
|
int libraryId = await Seed(new JellyfinLibrary
|
|
{
|
|
Name = "Concerts",
|
|
MediaKind = LibraryMediaKind.MusicVideos,
|
|
MediaSourceId = 1,
|
|
ItemId = "item1",
|
|
ShouldSyncItems = true,
|
|
LastScan = new DateTime(2026, 7, 1, 12, 0, 0, DateTimeKind.Utc),
|
|
Paths = [new LibraryPath { Path = "/data/music" }],
|
|
PathInfos = []
|
|
});
|
|
|
|
await _repository.DisableJellyfinLibrarySync([libraryId]);
|
|
|
|
await using TvContext context = _db.CreateContext();
|
|
JellyfinLibrary library = await context.JellyfinLibraries.SingleAsync(l => l.Name == "Concerts");
|
|
library.LastScan.ShouldBeNull();
|
|
library.ShouldSyncItems.ShouldBeFalse();
|
|
}
|
|
|
|
[Test]
|
|
public async Task DisableEmbyLibrarySync_Should_Null_LastScan_Not_Write_MinValue()
|
|
{
|
|
int libraryId = await Seed(new EmbyLibrary
|
|
{
|
|
Name = "Shows",
|
|
MediaKind = LibraryMediaKind.Shows,
|
|
MediaSourceId = 1,
|
|
ItemId = "item1",
|
|
ShouldSyncItems = true,
|
|
LastScan = new DateTime(2026, 7, 1, 12, 0, 0, DateTimeKind.Utc),
|
|
Paths = [new LibraryPath { Path = "/data/shows" }],
|
|
PathInfos = []
|
|
});
|
|
|
|
await _repository.DisableEmbyLibrarySync([libraryId]);
|
|
|
|
await using TvContext context = _db.CreateContext();
|
|
EmbyLibrary library = await context.EmbyLibraries.SingleAsync(l => l.Name == "Shows");
|
|
library.LastScan.ShouldBeNull();
|
|
library.ShouldSyncItems.ShouldBeFalse();
|
|
}
|
|
|
|
private async Task<int> Seed(Library library)
|
|
{
|
|
await using TvContext context = _db.CreateContext();
|
|
await context.AddAsync(library);
|
|
await context.SaveChangesAsync();
|
|
return library.Id;
|
|
}
|
|
}
|