Split grab and parse logic, recheck cache after semaphore lock, timeouts, add URL validation check, add torrent file tests, replace test hash with valid SHA1 format, tracker URL validation, fix token disposal issue
271 lines
No EOL
9.2 KiB
C#
271 lines
No EOL
9.2 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using Moq;
|
|
using RdtClient.Service.Services;
|
|
using MonoTorrent.BEncoding;
|
|
|
|
namespace RdtClient.Service.Test.Services;
|
|
|
|
public class EnricherTest : IDisposable
|
|
{
|
|
private readonly MockRepository _mockRepository;
|
|
private readonly Mock<ILogger<Enricher>> _loggerMock;
|
|
private readonly Mock<ITrackerListGrabber> _trackerListGrabberMock;
|
|
|
|
public EnricherTest()
|
|
{
|
|
_mockRepository = new MockRepository(MockBehavior.Strict);
|
|
_loggerMock = _mockRepository.Create<ILogger<Enricher>>(MockBehavior.Loose);
|
|
_trackerListGrabberMock = _mockRepository.Create<ITrackerListGrabber>();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_mockRepository.VerifyAll();
|
|
}
|
|
|
|
private const String TestMagnetLink =
|
|
"magnet:?xt=urn:btih:0123456789abcdef0123456789abcdef01234567&dn=TestFile&tr=http%3A%2F%2Ftracker1.com%2Fannounce&tr=http%3A%2F%2Ftracker2.com%2Fannounce";
|
|
|
|
[Fact]
|
|
public async Task EnrichMagnetLink_AddsNoTrackers_WhenNoTrackersFromTrackerGrabber()
|
|
{
|
|
// Arrange
|
|
SetupTrackerListGrabber([]);
|
|
|
|
var enricher = new Enricher(_loggerMock.Object, _trackerListGrabberMock.Object);
|
|
|
|
// Act
|
|
var enriched = await enricher.EnrichMagnetLink(TestMagnetLink);
|
|
|
|
// Assert
|
|
Assert.Equal(TestMagnetLink, enriched);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task EnrichMagnetLink_AddsTrackers_WhenTrackersFromTrackerGrabber()
|
|
{
|
|
// Arrange
|
|
SetupTrackerListGrabber(["http://new-tracker.com/announce"]);
|
|
|
|
var enricher = new Enricher(_loggerMock.Object, _trackerListGrabberMock.Object);
|
|
|
|
// Act
|
|
var enriched = await enricher.EnrichMagnetLink(TestMagnetLink);
|
|
|
|
// Assert
|
|
Assert.Equal(TestMagnetLink + $"&tr={Uri.EscapeDataString("http://new-tracker.com/announce")}", enriched);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task EnrichMagnetLink_DoesNotAddDuplicateTrackers_WhenTrackersFromTrackerGrabberAlreadyPresent()
|
|
{
|
|
// Arrange
|
|
SetupTrackerListGrabber(["http://new-tracker.com/announce", "http://tracker1.com/announce"]);
|
|
|
|
var enricher = new Enricher(_loggerMock.Object, _trackerListGrabberMock.Object);
|
|
|
|
// Act
|
|
var enriched = await enricher.EnrichMagnetLink(TestMagnetLink);
|
|
|
|
// Assert
|
|
Assert.Equal(TestMagnetLink + $"&tr={Uri.EscapeDataString("http://new-tracker.com/announce")}", enriched);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task EnrichMagnetLink_Throws_WhenTrackerGrabberThrows()
|
|
{
|
|
// Arrange
|
|
_trackerListGrabberMock
|
|
.Setup(t => t.GetTrackers())
|
|
.ThrowsAsync(new InvalidOperationException("Unable to fetch tracker list for enrichment."));
|
|
|
|
var enricher = new Enricher(_loggerMock.Object, _trackerListGrabberMock.Object);
|
|
|
|
// Act & Assert
|
|
await Assert.ThrowsAsync<InvalidOperationException>(() => enricher.EnrichMagnetLink(TestMagnetLink));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task EnrichTorrentBytes_AddsTrackers_WhenTrackersFromTrackerGrabber()
|
|
{
|
|
// Arrange
|
|
var originalTracker = "http://tracker1.com/announce";
|
|
var newTracker = "http://new-tracker.com/announce";
|
|
|
|
var torrentDict = new BEncodedDictionary
|
|
{
|
|
["announce"] = new BEncodedString(originalTracker),
|
|
["announce-list"] = new BEncodedList
|
|
{
|
|
new BEncodedList
|
|
{
|
|
new BEncodedString(originalTracker)
|
|
}
|
|
}
|
|
};
|
|
|
|
var originalTorrentBytes = torrentDict.Encode();
|
|
|
|
SetupTrackerListGrabber([newTracker]);
|
|
var enricher = new Enricher(_loggerMock.Object, _trackerListGrabberMock.Object);
|
|
|
|
// Act
|
|
var enrichedBytes = await enricher.EnrichTorrentBytes(originalTorrentBytes);
|
|
var enrichedDict = BEncodedValue.Decode<BEncodedDictionary>(enrichedBytes);
|
|
|
|
// Assert
|
|
Assert.True(enrichedDict.ContainsKey("announce"));
|
|
Assert.True(enrichedDict.ContainsKey("announce-list"));
|
|
|
|
var announceList = (BEncodedList)enrichedDict["announce-list"];
|
|
var flattened = announceList.Cast<BEncodedList>().SelectMany(l => l.Cast<BEncodedString>().Select(s => s.Text)).ToList();
|
|
|
|
Assert.Contains(originalTracker, flattened);
|
|
Assert.Contains(newTracker, flattened);
|
|
|
|
var announce = ((BEncodedString)enrichedDict["announce"]).Text;
|
|
Assert.Equal(flattened.First(), announce);
|
|
}
|
|
|
|
private void SetupTrackerListGrabber(String[] trackerList)
|
|
{
|
|
_trackerListGrabberMock
|
|
.Setup(t => t.GetTrackers())
|
|
.ReturnsAsync(trackerList)
|
|
.Verifiable();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task EnrichTorrentBytes_DoesNotAddTrackers_WhenNoTrackersFromTrackerGrabber()
|
|
{
|
|
// Arrange
|
|
var originalTracker = "http://tracker1.com/announce";
|
|
|
|
var torrentDict = new BEncodedDictionary
|
|
{
|
|
["announce"] = new BEncodedString(originalTracker),
|
|
["announce-list"] = new BEncodedList
|
|
{
|
|
new BEncodedList
|
|
{
|
|
new BEncodedString(originalTracker)
|
|
}
|
|
}
|
|
};
|
|
|
|
var originalTorrentBytes = torrentDict.Encode();
|
|
|
|
SetupTrackerListGrabber([]);
|
|
var enricher = new Enricher(_loggerMock.Object, _trackerListGrabberMock.Object);
|
|
|
|
// Act
|
|
var enrichedBytes = await enricher.EnrichTorrentBytes(originalTorrentBytes);
|
|
var enrichedDict = BEncodedValue.Decode<BEncodedDictionary>(enrichedBytes);
|
|
|
|
// Assert
|
|
Assert.True(enrichedDict.ContainsKey("announce"));
|
|
Assert.True(enrichedDict.ContainsKey("announce-list"));
|
|
|
|
var announceList = (BEncodedList)enrichedDict["announce-list"];
|
|
var flattened = announceList.Cast<BEncodedList>().SelectMany(l => l.Cast<BEncodedString>().Select(s => s.Text)).ToList();
|
|
|
|
Assert.Single(flattened);
|
|
Assert.Contains(originalTracker, flattened);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task EnrichTorrentBytes_DoesNotAddDuplicateTrackers()
|
|
{
|
|
// Arrange
|
|
var originalTracker = "http://tracker1.com/announce";
|
|
var duplicateTracker = "http://tracker1.com/announce";
|
|
|
|
var torrentDict = new BEncodedDictionary
|
|
{
|
|
["announce"] = new BEncodedString(originalTracker),
|
|
["announce-list"] = new BEncodedList
|
|
{
|
|
new BEncodedList
|
|
{
|
|
new BEncodedString(originalTracker)
|
|
}
|
|
}
|
|
};
|
|
|
|
var originalTorrentBytes = torrentDict.Encode();
|
|
|
|
SetupTrackerListGrabber([duplicateTracker]);
|
|
var enricher = new Enricher(_loggerMock.Object, _trackerListGrabberMock.Object);
|
|
|
|
// Act
|
|
var enrichedBytes = await enricher.EnrichTorrentBytes(originalTorrentBytes);
|
|
var enrichedDict = BEncodedValue.Decode<BEncodedDictionary>(enrichedBytes);
|
|
|
|
// Assert
|
|
var announceList = (BEncodedList)enrichedDict["announce-list"];
|
|
var flattened = announceList.Cast<BEncodedList>().SelectMany(l => l.Cast<BEncodedString>().Select(s => s.Text)).ToList();
|
|
|
|
Assert.Single(flattened);
|
|
Assert.Contains(originalTracker, flattened);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task EnrichTorrentBytes_AddsTrackers_WhenNoAnnounceListPresent()
|
|
{
|
|
// Arrange
|
|
var originalTracker = "http://tracker1.com/announce";
|
|
var newTracker = "http://new-tracker.com/announce";
|
|
|
|
var torrentDict = new BEncodedDictionary
|
|
{
|
|
["announce"] = new BEncodedString(originalTracker)
|
|
|
|
// No "announce-list"
|
|
};
|
|
|
|
var originalTorrentBytes = torrentDict.Encode();
|
|
|
|
SetupTrackerListGrabber([newTracker]);
|
|
var enricher = new Enricher(_loggerMock.Object, _trackerListGrabberMock.Object);
|
|
|
|
// Act
|
|
var enrichedBytes = await enricher.EnrichTorrentBytes(originalTorrentBytes);
|
|
var enrichedDict = BEncodedValue.Decode<BEncodedDictionary>(enrichedBytes);
|
|
|
|
// Assert
|
|
Assert.True(enrichedDict.ContainsKey("announce-list"));
|
|
var announceList = (BEncodedList)enrichedDict["announce-list"];
|
|
var flattened = announceList.Cast<BEncodedList>().SelectMany(l => l.Cast<BEncodedString>().Select(s => s.Text)).ToList();
|
|
|
|
Assert.Contains(originalTracker, flattened);
|
|
Assert.Contains(newTracker, flattened);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task EnrichTorrentBytes_Throws_WhenTrackerGrabberThrows()
|
|
{
|
|
// Arrange
|
|
_trackerListGrabberMock
|
|
.Setup(t => t.GetTrackers())
|
|
.ThrowsAsync(new InvalidOperationException("Unable to fetch tracker list for enrichment."));
|
|
|
|
var torrentDict = new BEncodedDictionary
|
|
{
|
|
["announce"] = new BEncodedString("http://tracker1.com/announce"),
|
|
["announce-list"] = new BEncodedList
|
|
{
|
|
new BEncodedList
|
|
{
|
|
new BEncodedString("http://tracker1.com/announce")
|
|
}
|
|
}
|
|
};
|
|
|
|
var originalTorrentBytes = torrentDict.Encode();
|
|
|
|
var enricher = new Enricher(_loggerMock.Object, _trackerListGrabberMock.Object);
|
|
|
|
// Act & Assert
|
|
await Assert.ThrowsAsync<InvalidOperationException>(() => enricher.EnrichTorrentBytes(originalTorrentBytes));
|
|
}
|
|
} |