Normalize line endings to LF via .gitattributes

Репозиторий хранил фронтенд в CRLF, а часть бэкенда — вперемешку, хотя CI и Docker-сборка
работают под Linux. Прибиваем LF атрибутом `* text=auto eol=lf` и разово нормализуем дерево,
чтобы форматтеры не переписывали файлы целиком на каждом прогоне.

Коммит чисто механический: изменений содержимого нет, только концы строк.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Leonid Pershin
2026-07-27 01:37:33 +03:00
co-authored by Claude Opus 5
parent 9d1c6d2fc3
commit 0442056367
109 changed files with 13469 additions and 13459 deletions
@@ -1,69 +1,69 @@
using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Broadcast;
using TeleWave.Application.Broadcast.CreateChannel;
using TeleWave.Application.Broadcast.UpdateChannelSettings;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Broadcast;
using TeleWave.Domain.Library;
using TeleWave.Domain.Media;
using Xunit;
namespace TeleWave.Application.Tests.Broadcast;
public class ChannelHandlersTests
{
private static readonly DateTimeOffset T0 = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
[Fact]
public async Task CreateChannel_Succeeds_AndRejectsDuplicateSlug()
{
var fixture = new TestDb();
await using var db = fixture.New();
var ok = await new CreateChannelCommandHandler(db).Handle(
new CreateChannelCommand("News", "news"),
CancellationToken.None
);
Assert.True(ok.IsSuccess);
await db.SaveChangesAsync(CancellationToken.None);
var dup = await new CreateChannelCommandHandler(db).Handle(
new CreateChannelCommand("Other", "news"),
CancellationToken.None
);
Assert.False(dup.IsSuccess);
Assert.Equal(ChannelErrors.DuplicateSlug, dup.Error);
}
[Fact]
public async Task UpdateChannelSettings_UpdatesBumperChances()
{
var fixture = new TestDb();
var channel = Channel.Create("c", "c", T0);
await using (var seed = fixture.New())
{
seed.Channels.Add(channel);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var result = await new UpdateChannelSettingsCommandHandler(db).Handle(
new UpdateChannelSettingsCommand(
channel.Id,
"c",
true,
true,
new BumperSettingsInput(BumperFont.Sans, BumperSelection.WeightedRandom),
null
),
CancellationToken.None
);
Assert.True(result.IsSuccess);
await db.SaveChangesAsync(CancellationToken.None);
await using var verify = fixture.New();
var stored = await verify.Channels.FindAsync(channel.Id);
Assert.Equal(BumperFont.Sans, stored!.BumperFont);
Assert.Equal(BumperSelection.WeightedRandom, stored.BumperSelection);
}
}
using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Broadcast;
using TeleWave.Application.Broadcast.CreateChannel;
using TeleWave.Application.Broadcast.UpdateChannelSettings;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Broadcast;
using TeleWave.Domain.Library;
using TeleWave.Domain.Media;
using Xunit;
namespace TeleWave.Application.Tests.Broadcast;
public class ChannelHandlersTests
{
private static readonly DateTimeOffset T0 = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
[Fact]
public async Task CreateChannel_Succeeds_AndRejectsDuplicateSlug()
{
var fixture = new TestDb();
await using var db = fixture.New();
var ok = await new CreateChannelCommandHandler(db).Handle(
new CreateChannelCommand("News", "news"),
CancellationToken.None
);
Assert.True(ok.IsSuccess);
await db.SaveChangesAsync(CancellationToken.None);
var dup = await new CreateChannelCommandHandler(db).Handle(
new CreateChannelCommand("Other", "news"),
CancellationToken.None
);
Assert.False(dup.IsSuccess);
Assert.Equal(ChannelErrors.DuplicateSlug, dup.Error);
}
[Fact]
public async Task UpdateChannelSettings_UpdatesBumperChances()
{
var fixture = new TestDb();
var channel = Channel.Create("c", "c", T0);
await using (var seed = fixture.New())
{
seed.Channels.Add(channel);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var result = await new UpdateChannelSettingsCommandHandler(db).Handle(
new UpdateChannelSettingsCommand(
channel.Id,
"c",
true,
true,
new BumperSettingsInput(BumperFont.Sans, BumperSelection.WeightedRandom),
null
),
CancellationToken.None
);
Assert.True(result.IsSuccess);
await db.SaveChangesAsync(CancellationToken.None);
await using var verify = fixture.New();
var stored = await verify.Channels.FindAsync(channel.Id);
Assert.Equal(BumperFont.Sans, stored!.BumperFont);
Assert.Equal(BumperSelection.WeightedRandom, stored.BumperSelection);
}
}
@@ -1,201 +1,201 @@
using Microsoft.EntityFrameworkCore;
using NSubstitute;
using TeleWave.Application.Broadcast;
using TeleWave.Application.Broadcast.Bumpers;
using TeleWave.Application.Broadcast.GetChannel;
using TeleWave.Application.Broadcast.GetSchedule;
using TeleWave.Application.Broadcast.ListChannels;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Library.DeleteShow;
using TeleWave.Application.Library.GetShow;
using TeleWave.Application.Programming.Groups;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Broadcast;
using TeleWave.Domain.Library;
using TeleWave.Domain.Media;
using Xunit;
namespace TeleWave.Application.Tests.Broadcast;
public class QueryHandlersTests
{
private static readonly DateTimeOffset T0 = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
private static GroupMembershipCleaner GroupCleaner(IAppDbContext db) =>
new(db, new GroupStatsService(db, new GroupElementResolver(db)));
[Fact]
public async Task GetChannel_UnknownId_ReturnsNotFound()
{
var fixture = new TestDb();
await using var db = fixture.New();
var result = await new GetChannelQueryHandler(db).Handle(
new GetChannelQuery(Guid.NewGuid()),
CancellationToken.None
);
Assert.False(result.IsSuccess);
}
[Fact]
public async Task ListChannels_ReturnsSummaries()
{
var fixture = new TestDb();
await using (var seed = fixture.New())
{
seed.Channels.Add(Channel.Create("A", "a", T0));
seed.Channels.Add(Channel.Create("B", "b", T0));
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var result = await new ListChannelsQueryHandler(db).Handle(
new ListChannelsQuery(),
CancellationToken.None
);
Assert.Equal(2, result.Count);
}
[Fact]
public async Task GetChannelSchedule_ReturnsEntriesWithBumperLabel()
{
var fixture = new TestDb();
var channel = Channel.Create("c", "c", T0);
var show = Show.Create("Show A", ShowKind.Series);
var variant = channel.BumperTemplates[0].Variants[0];
var asset = MediaAsset.Register("Show.A.S01E01.mkv", ".mkv", MediaSource.Upload);
var program = ScheduleEntry.Program(
channel.Id,
asset.Id,
T0,
T0.AddMinutes(20),
show.Id,
0
);
var bumper = ScheduleEntry.Bumper(
channel.Id,
Guid.NewGuid(),
T0.AddMinutes(20),
T0.AddMinutes(20).AddSeconds(8),
show.Id,
variant.Id
);
await using (var seed = fixture.New())
{
seed.Shows.Add(show);
seed.Channels.Add(channel);
seed.MediaAssets.Add(asset);
seed.ScheduleEntries.Add(program);
seed.ScheduleEntries.Add(bumper);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var result = await new GetChannelScheduleQueryHandler(db).Handle(
new GetChannelScheduleQuery(channel.Id, T0.AddMinutes(-5), T0.AddHours(1)),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(2, result.Value.Count);
var bumperDto = result.Value.Single(e => e.Kind == ScheduleEntryKind.Bumper);
Assert.NotNull(bumperDto.BumperName);
}
[Fact]
public async Task GetShow_ReturnsDtoOrNotFound()
{
var fixture = new TestDb();
var show = Show.Create("A", ShowKind.Series);
await using (var seed = fixture.New())
{
seed.Shows.Add(show);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var ok = await new GetShowQueryHandler(db).Handle(
new GetShowQuery(show.Id),
CancellationToken.None
);
Assert.True(ok.IsSuccess);
var missing = await new GetShowQueryHandler(db).Handle(
new GetShowQuery(Guid.NewGuid()),
CancellationToken.None
);
Assert.False(missing.IsSuccess);
}
[Fact]
public async Task DeleteShow_RemovesOrNotFound()
{
var fixture = new TestDb();
var show = Show.Create("A", ShowKind.Series);
await using (var seed = fixture.New())
{
seed.Shows.Add(show);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var missing = await new DeleteShowCommandHandler(db, GroupCleaner(db)).Handle(
new DeleteShowCommand(Guid.NewGuid()),
CancellationToken.None
);
Assert.False(missing.IsSuccess);
var ok = await new DeleteShowCommandHandler(db, GroupCleaner(db)).Handle(
new DeleteShowCommand(show.Id),
CancellationToken.None
);
Assert.True(ok.IsSuccess);
}
[Fact]
public async Task AddAndRemoveBumperTemplate_WorkThroughStorage()
{
var fixture = new TestDb();
var channel = Channel.Create("c", "c", T0);
await using (var seed = fixture.New())
{
seed.Channels.Add(channel);
await seed.SaveChangesAsync(CancellationToken.None);
}
Guid templateId;
await using (var db = fixture.New())
{
var added = await new AddBumperTemplateCommandHandler(db).Handle(
new AddBumperTemplateCommand(channel.Id, ""),
CancellationToken.None
);
Assert.True(added.IsSuccess);
templateId = added.Value;
await db.SaveChangesAsync(CancellationToken.None);
}
var storage = Substitute.For<IBumperTemplateStorage>();
await using (var db = fixture.New())
{
var removed = await new RemoveBumperTemplateCommandHandler(db, storage).Handle(
new RemoveBumperTemplateCommand(channel.Id, templateId),
CancellationToken.None
);
Assert.True(removed.IsSuccess);
}
await storage.Received(1).DeleteTemplateAsync(templateId, Arg.Any<CancellationToken>());
// дефолтный блок удалить нельзя
await using (var db = fixture.New())
{
var def = await db
.Channels.Include(c => c.BumperTemplates)
.FirstAsync(c => c.Id == channel.Id);
var result = await new RemoveBumperTemplateCommandHandler(db, storage).Handle(
new RemoveBumperTemplateCommand(channel.Id, def.BumperTemplates[0].Id),
CancellationToken.None
);
Assert.Equal(ChannelErrors.CannotRemoveDefaultBumperTemplate, result.Error);
}
}
}
using Microsoft.EntityFrameworkCore;
using NSubstitute;
using TeleWave.Application.Broadcast;
using TeleWave.Application.Broadcast.Bumpers;
using TeleWave.Application.Broadcast.GetChannel;
using TeleWave.Application.Broadcast.GetSchedule;
using TeleWave.Application.Broadcast.ListChannels;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Library.DeleteShow;
using TeleWave.Application.Library.GetShow;
using TeleWave.Application.Programming.Groups;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Broadcast;
using TeleWave.Domain.Library;
using TeleWave.Domain.Media;
using Xunit;
namespace TeleWave.Application.Tests.Broadcast;
public class QueryHandlersTests
{
private static readonly DateTimeOffset T0 = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
private static GroupMembershipCleaner GroupCleaner(IAppDbContext db) =>
new(db, new GroupStatsService(db, new GroupElementResolver(db)));
[Fact]
public async Task GetChannel_UnknownId_ReturnsNotFound()
{
var fixture = new TestDb();
await using var db = fixture.New();
var result = await new GetChannelQueryHandler(db).Handle(
new GetChannelQuery(Guid.NewGuid()),
CancellationToken.None
);
Assert.False(result.IsSuccess);
}
[Fact]
public async Task ListChannels_ReturnsSummaries()
{
var fixture = new TestDb();
await using (var seed = fixture.New())
{
seed.Channels.Add(Channel.Create("A", "a", T0));
seed.Channels.Add(Channel.Create("B", "b", T0));
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var result = await new ListChannelsQueryHandler(db).Handle(
new ListChannelsQuery(),
CancellationToken.None
);
Assert.Equal(2, result.Count);
}
[Fact]
public async Task GetChannelSchedule_ReturnsEntriesWithBumperLabel()
{
var fixture = new TestDb();
var channel = Channel.Create("c", "c", T0);
var show = Show.Create("Show A", ShowKind.Series);
var variant = channel.BumperTemplates[0].Variants[0];
var asset = MediaAsset.Register("Show.A.S01E01.mkv", ".mkv", MediaSource.Upload);
var program = ScheduleEntry.Program(
channel.Id,
asset.Id,
T0,
T0.AddMinutes(20),
show.Id,
0
);
var bumper = ScheduleEntry.Bumper(
channel.Id,
Guid.NewGuid(),
T0.AddMinutes(20),
T0.AddMinutes(20).AddSeconds(8),
show.Id,
variant.Id
);
await using (var seed = fixture.New())
{
seed.Shows.Add(show);
seed.Channels.Add(channel);
seed.MediaAssets.Add(asset);
seed.ScheduleEntries.Add(program);
seed.ScheduleEntries.Add(bumper);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var result = await new GetChannelScheduleQueryHandler(db).Handle(
new GetChannelScheduleQuery(channel.Id, T0.AddMinutes(-5), T0.AddHours(1)),
CancellationToken.None
);
Assert.True(result.IsSuccess);
Assert.Equal(2, result.Value.Count);
var bumperDto = result.Value.Single(e => e.Kind == ScheduleEntryKind.Bumper);
Assert.NotNull(bumperDto.BumperName);
}
[Fact]
public async Task GetShow_ReturnsDtoOrNotFound()
{
var fixture = new TestDb();
var show = Show.Create("A", ShowKind.Series);
await using (var seed = fixture.New())
{
seed.Shows.Add(show);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var ok = await new GetShowQueryHandler(db).Handle(
new GetShowQuery(show.Id),
CancellationToken.None
);
Assert.True(ok.IsSuccess);
var missing = await new GetShowQueryHandler(db).Handle(
new GetShowQuery(Guid.NewGuid()),
CancellationToken.None
);
Assert.False(missing.IsSuccess);
}
[Fact]
public async Task DeleteShow_RemovesOrNotFound()
{
var fixture = new TestDb();
var show = Show.Create("A", ShowKind.Series);
await using (var seed = fixture.New())
{
seed.Shows.Add(show);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var missing = await new DeleteShowCommandHandler(db, GroupCleaner(db)).Handle(
new DeleteShowCommand(Guid.NewGuid()),
CancellationToken.None
);
Assert.False(missing.IsSuccess);
var ok = await new DeleteShowCommandHandler(db, GroupCleaner(db)).Handle(
new DeleteShowCommand(show.Id),
CancellationToken.None
);
Assert.True(ok.IsSuccess);
}
[Fact]
public async Task AddAndRemoveBumperTemplate_WorkThroughStorage()
{
var fixture = new TestDb();
var channel = Channel.Create("c", "c", T0);
await using (var seed = fixture.New())
{
seed.Channels.Add(channel);
await seed.SaveChangesAsync(CancellationToken.None);
}
Guid templateId;
await using (var db = fixture.New())
{
var added = await new AddBumperTemplateCommandHandler(db).Handle(
new AddBumperTemplateCommand(channel.Id, ""),
CancellationToken.None
);
Assert.True(added.IsSuccess);
templateId = added.Value;
await db.SaveChangesAsync(CancellationToken.None);
}
var storage = Substitute.For<IBumperTemplateStorage>();
await using (var db = fixture.New())
{
var removed = await new RemoveBumperTemplateCommandHandler(db, storage).Handle(
new RemoveBumperTemplateCommand(channel.Id, templateId),
CancellationToken.None
);
Assert.True(removed.IsSuccess);
}
await storage.Received(1).DeleteTemplateAsync(templateId, Arg.Any<CancellationToken>());
// дефолтный блок удалить нельзя
await using (var db = fixture.New())
{
var def = await db
.Channels.Include(c => c.BumperTemplates)
.FirstAsync(c => c.Id == channel.Id);
var result = await new RemoveBumperTemplateCommandHandler(db, storage).Handle(
new RemoveBumperTemplateCommand(channel.Id, def.BumperTemplates[0].Id),
CancellationToken.None
);
Assert.Equal(ChannelErrors.CannotRemoveDefaultBumperTemplate, result.Error);
}
}
}