Strengthened URL Validation.
Improved HTTP Client Configuration.
Logged total rejected tracker count at the information level.
Logged individual rejected tracker details at the debug level.
Debug logs for bytes and enriched bytes are now conditional.
Converted some tests from Fact to Theory.
Used Assert.Collection in EnricherTest.cs.
Standardized URL Encoding in Enricher.cs.
`DownloadableFileFilter` is an abstraction of the filtering we do in most of the `TorrentClient`s.
It operates on a single file at a time, not the entire torrent.
Tests:
- `GetDownloadLinks`
- `IncludeRegex` filter works
- `ExcludeRegex` filter works
- `DownloadMinSize` filter works
- only `IncludeRegex` is used when both `IncludeRegex` and `ExcludeRegex` are set
- gets links for all files if none of the above filters are set
- returns null if `torrent.RdId` is null
- `GetTorrents`
- [Both full/partial sync] returns empty list when no torrents present in api response
- [full sync] overrides any previous torrents in cache, deleting those not present in the api response
- [partial sync] updates torrents in cache according to the api response
- [partial sync] adds torrents to cache according to the api response
- `Map` (private so tested via `GetTorrents`)
- Computes progress correctly from `Downloaded / Size`
- `UpdateData`
- [`torrentClientTorrent != null`] does not get from api, updates the `torrent` accordingly
- [`torrentClientTorrent == null`] gets from api, updates the `torrent` accordingly
- [`torrentClientTorrent == null`] when torrent deleted from rd, updates `RdStatusRaw = "deleted""`
- when `torrent.RdId == null`, returns the `torrent` unmodified
Tests:
- [Both overloads] uses the `download.Link` to guess the filenamem if it's not present
- [Both overloads] returns null if `download.Link` is null
- [Both overloads] returns nuyll if `download.RdName` is null
- [Both overloads] works for a file in a deeply nested subdirectory
- [overload with `downloadPath` argument only] creates the correct directory
Tests:
- Downloads successfully when everything goes right first time
- Downloads successfully when the 5th api call is successful after 4 failures
- Fails to download when 5 api calls fail in a row
- Fails to download if the file has already been added to `Download Station` (when `GetInfoAsync` does not error)
- FAils to download if `remotePath` cannot be parsed by `Path.GetDirectoryName()`