Add LibraryDebugCollector and enhance ChannelDebugCollector for drift analysis
Introduced the LibraryDebugCollector to gather library-related data for channel exports. Updated the ChannelDebugCollector to calculate and include drift information, comparing scheduled and actual slot times. Enhanced the ExportChannelDebugCommandHandler to integrate library data into the export process. Added tests to verify the correct functionality of the new drift analysis and library export features, ensuring comprehensive debug data for channel analysis.
This commit is contained in:
@@ -124,6 +124,7 @@ public class ChannelDebugExportTests
|
||||
generator,
|
||||
Options.Create(new SchedulerOptions { HorizonDays = 1, RetentionDays = 90 })
|
||||
),
|
||||
new LibraryDebugCollector(db, GroupServices.Dynamic(db), new GroupElementResolver(db)),
|
||||
store
|
||||
);
|
||||
}
|
||||
@@ -165,10 +166,17 @@ public class ChannelDebugExportTests
|
||||
Assert.Equal(
|
||||
[
|
||||
"README.md",
|
||||
"bumpers.json",
|
||||
"channel.json",
|
||||
"drift.json",
|
||||
"effective-grid.json",
|
||||
"groups-composition.json",
|
||||
"groups.json",
|
||||
"junctions.json",
|
||||
"library-collections.json",
|
||||
"library-interstitials.json",
|
||||
"library-media.json",
|
||||
"library-shows.json",
|
||||
"plan-preview.json",
|
||||
"schedule.json",
|
||||
"slot-states.json",
|
||||
@@ -195,6 +203,80 @@ public class ChannelDebugExportTests
|
||||
Assert.Equal("Симпсоны", units[0].GetProperty("show").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Export_DescribesTheLibraryItWasBuiltFrom()
|
||||
{
|
||||
// Вторая половина разбора: когда в эфир вышло не то, смотрят не на ленту, а на то,
|
||||
// из чего её собирали — сколько у шоу серий, готовы ли ассеты, что дало правило группы.
|
||||
var (fixture, channelId) = await SeedAsync();
|
||||
|
||||
await using var db = fixture.New();
|
||||
var result = await Handler(db, Substitute.For<IDebugExportStore>())
|
||||
.Handle(new ExportChannelDebugCommand(channelId), CancellationToken.None);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
var files = Unpack(result.Value.Content);
|
||||
|
||||
using var shows = JsonDocument.Parse(files["library-shows.json"]);
|
||||
var show = shows.RootElement.EnumerateArray().Single();
|
||||
Assert.Equal("Симпсоны", show.GetProperty("name").GetString());
|
||||
Assert.Equal(2, show.GetProperty("episodes").GetInt32());
|
||||
Assert.Equal(2, show.GetProperty("readyEpisodes").GetInt32());
|
||||
// Серии перечислены по порядку показа — по нему планировщик их и ставит.
|
||||
var items = show.GetProperty("items").EnumerateArray().ToList();
|
||||
Assert.Equal(2, items.Count);
|
||||
Assert.Equal("s01e01.mkv", items[0].GetProperty("file").GetString());
|
||||
Assert.Equal("Ready", items[0].GetProperty("status").GetString());
|
||||
|
||||
using var groups = JsonDocument.Parse(files["groups-composition.json"]);
|
||||
var group = groups.RootElement.EnumerateArray().Single();
|
||||
Assert.Equal("Мультсериалы", group.GetProperty("name").GetString());
|
||||
// Состав вычислен, а не переписан из позиций: у динамической группы он только так и виден.
|
||||
var composition = group.GetProperty("composition").EnumerateArray().Single();
|
||||
Assert.Equal("Симпсоны", composition.GetProperty("name").GetString());
|
||||
Assert.Equal(2, composition.GetProperty("unitCount").GetInt32());
|
||||
|
||||
using var media = JsonDocument.Parse(files["library-media.json"]);
|
||||
Assert.Equal(2, media.RootElement.GetProperty("total").GetInt32());
|
||||
Assert.Empty(media.RootElement.GetProperty("problems").EnumerateArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Export_MeasuresDriftAgainstTheGrid()
|
||||
{
|
||||
// «Слот уехал» — первый вопрос к любой сетке, и сводить целевые времена с фактическими
|
||||
// вручную по двум файлам приходилось каждый раз.
|
||||
var (fixture, channelId) = await SeedAsync();
|
||||
var store = Substitute.For<IDebugExportStore>();
|
||||
|
||||
await using var db = fixture.New();
|
||||
var result = await Handler(db, store)
|
||||
.Handle(new ExportChannelDebugCommand(channelId), CancellationToken.None);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
var files = Unpack(result.Value.Content);
|
||||
|
||||
using var drift = JsonDocument.Parse(files["drift.json"]);
|
||||
var summary = drift.RootElement.GetProperty("summary");
|
||||
var instances = drift.RootElement.GetProperty("instances").EnumerateArray().ToList();
|
||||
|
||||
Assert.NotEmpty(instances);
|
||||
Assert.Equal(instances.Count, summary.GetProperty("instances").GetInt32());
|
||||
|
||||
// Ленты ещё нет — значит и дрейф мерить не по чему: это «без записей», а не нулевой сдвиг.
|
||||
Assert.Equal(instances.Count, summary.GetProperty("withoutEntries").GetInt32());
|
||||
Assert.All(
|
||||
instances,
|
||||
i =>
|
||||
{
|
||||
Assert.Equal(JsonValueKind.Null, i.GetProperty("actualStartUtc").ValueKind);
|
||||
Assert.Equal(JsonValueKind.Null, i.GetProperty("driftMinutes").ValueKind);
|
||||
Assert.False(i.GetProperty("exceedsTolerance").GetBoolean());
|
||||
Assert.True(i.GetProperty("maxDriftMinutes").GetInt32() > 0);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Export_DryRunGoesIntoTheDump()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user