Files
tours_monitor/tests/TourMonitor.Tests/LevelTravelIntegrationTests.cs

147 lines
5.4 KiB
C#

using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using TourMonitor.Camoufox;
using TourMonitor.Jobs;
using TourMonitor.LevelTravel;
using TourMonitor.Notifications;
using TourMonitor.Storage;
namespace TourMonitor.Tests;
/// <summary>
/// Живые проверки публичного API Level.Travel через браузер Camoufox (единственный транспорт).
/// Включаются только явно:
/// LT_LIVE_TESTS=1 dotnet test
/// Без переменной окружения тесты «проходят» без выполнения запросов.
/// </summary>
public class LevelTravelIntegrationTests
{
private const string SunriseTucana = "9099454";
private const string PoshClub = "9151153";
private static bool LiveEnabled => Environment.GetEnvironmentVariable("LT_LIVE_TESTS") == "1";
private static LevelTravelOptions BuildOptions() =>
new() { SecretBoxKeys = TestData.SecretBoxKeys.ToList() };
private static async Task<CamoufoxBrowserSession> BuildSessionAsync()
{
var installDir = Path.Combine(Path.GetTempPath(), $"camoufox_test_{Guid.NewGuid():N}");
var session = new CamoufoxBrowserSession(
new CamoufoxInstaller(new CamoufoxReleaseResolver(), NullLogger<CamoufoxInstaller>.Instance),
Options.Create(new CamoufoxOptions { InstallDirectory = installDir, Headless = true }),
NullLogger<CamoufoxBrowserSession>.Instance);
await session.GetPageAsync();
return session;
}
private static LevelTravelClient BuildClient(CamoufoxBrowserSession session)
{
var transport = new CamoufoxApiTransport(session, Options.Create(BuildOptions()), NullLogger<CamoufoxApiTransport>.Instance);
return new LevelTravelClient(transport, Options.Create(BuildOptions()), NullLogger<LevelTravelClient>.Instance);
}
private static List<SearchParam> BuildParams(string date)
{
var hotels = new[] { SunriseTucana, PoshClub };
return hotels.Select(h => new SearchParam
{
HotelIds = int.Parse(h),
FromCity = "Moscow",
FromCountry = "RU",
ToCity = "Makadi Bay",
ToCountry = "EG",
StartDate = date,
Nights = "9",
Adults = 2,
Kids = 1,
KidsAges = new[] { 5 },
FlexDates = false,
SearchType = "package",
}).ToList();
}
[Fact]
public async Task Live_Enqueue_Status_RoomRates_Flow()
{
if (!LiveEnabled)
return;
var session = await BuildSessionAsync();
try
{
var client = BuildClient(session);
var date = DateTime.Today.AddDays(3).ToString("yyyy-MM-dd");
var enqueue = await client.MultiEnqueueAsync(BuildParams(date));
Assert.True(enqueue.Success);
Assert.Equal(2, enqueue.SearchRequests.Count);
var hotelIds = new[] { SunriseTucana, PoshClub };
var totalOffers = 0;
for (var i = 0; i < enqueue.SearchRequests.Count; i++)
{
var status = await client.WaitForSearchAsync(enqueue.SearchRequests[i].RequestId, 4, 240);
Assert.True(status.Completeness >= 100 || status.Status?.Count > 0);
var rates = await client.GetRoomRatesAsync(hotelIds[i], enqueue.SearchRequests[i].RequestId);
if (rates.Success)
totalOffers += rates.Result.Sum(r => r.Offers.Values.Sum(o => o.Count));
}
Assert.True(totalOffers > 0, "API вернуло пустой результат по обоим отелям.");
}
finally
{
await session.CloseAsync();
}
}
[Fact]
public async Task Live_DailyScan_Persists_Offers()
{
if (!LiveEnabled)
return;
var dbPath = Path.Combine(Path.GetTempPath(), $"tour_monitor_scan_{Guid.NewGuid():N}.db");
var session = await BuildSessionAsync();
try
{
var store = new PriceStore(dbPath);
await store.EnsureCreatedAsync();
var monitor = new MonitorOptions
{
Hotels =
{
new() { Id = int.Parse(SunriseTucana), Name = "Sunrise Tucana Resort", ToCity = "Makadi Bay", ToCountry = "EG", Nights = 9 },
new() { Id = int.Parse(PoshClub), Name = "Posh Club By Sunrise Tucana Resort", ToCity = "Makadi Bay", ToCountry = "EG", Nights = 9 },
},
DateRangeDays = 1,
KidsAges = { 5 },
MaxParallelDates = 2,
SearchTimeoutSeconds = 240,
};
var job = new DailyScanJob(
BuildClient(session),
store,
new TelegramNotifier(new HttpClient(), Options.Create(new TelegramOptions()), NullLogger<TelegramNotifier>.Instance),
session,
Options.Create(monitor),
NullLogger<DailyScanJob>.Instance);
await job.ExecuteAsync();
var bests = await store.GetTodayBestsAsync(DateTime.Today.ToString("yyyy-MM-dd"));
Assert.NotEmpty(bests);
}
finally
{
await session.CloseAsync();
if (File.Exists(dbPath))
File.Delete(dbPath);
}
}
}