1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-03-05 13:20:20 -05:00

Compare commits

...

108 Commits

Author SHA1 Message Date
Taloth Saldono
4c460f6836 WIP: Added testcases for a bunch of Parsed titles. Added support for At & Percent conversion. 2017-07-02 21:29:01 +02:00
Taloth Saldono
b03f434329 Disable Nyaa forcibly. 2017-07-02 21:27:59 +02:00
Chris Dyson
68290b0385 New: Added 'Series Title, The' renaming option
Closes #371
2017-07-02 21:21:46 +02:00
Taloth Saldono
773274f3cc Update Nyaa Pantsu apiPath to the actual /feed/torznab url. 2017-07-01 12:43:30 +02:00
Taloth Saldono
a33d8968ed New: Added Nyaa Pantsu as Torznab preset for Anime. 2017-07-01 00:08:01 +02:00
Taloth Saldono
e41baccd02 New: Added AnimeTosho as Newznab and Torznab presets. 2017-06-30 23:40:16 +02:00
Taloth Saldono
fef3423019 Fixed error in NzbGet KeepHistory check and updated tests. 2017-06-30 22:01:31 +02:00
Taloth Saldono
11926d8b2d Fixed: Support for Mono 5.x with the newer BoringTLS provider. 2017-06-30 21:33:41 +02:00
Mark McDowall
4189bc6f76 Webhook improvements
New: Include Path/Relative Path for on download Webhooks
New: IsUpgrade flag for on download Webhooks
2017-06-28 16:11:05 -07:00
Mark McDowall
601b54c244 Reorder HttpMethods to match RestSharp 2017-06-28 16:11:05 -07:00
Taloth Saldono
905ab18336 Fixed: Subtitle extensions should be case-insensitive. 2017-06-27 16:22:07 +02:00
Taloth Saldono
f46a576df5 Check if NzbGet KeepHistory value is set too high instead of only checking for 0. 2017-06-27 16:22:07 +02:00
Mark McDowall
60231fbcae Fixed: Show rounded age in minimum age rejection message
Fixes #2003
2017-06-26 22:41:22 -07:00
Mark McDowall
63860e783e Fixed: Background logo when URL base is used
Fixes #2002
2017-06-26 22:29:52 -07:00
Mark McDowall
cf8b9df5ad Fixed: Ignore case when importing extra files 2017-06-26 22:25:10 -07:00
Taloth Saldono
ff6841e410 Fixed: Added permanent Health Check warning to ensure Drone Factory is no longer used. 2017-06-21 19:35:32 +02:00
Taloth Saldono
ab49afe116 Don't log error on the shutdown the command execution pipeline.
closes #1961
2017-06-20 17:35:37 +02:00
Taloth Saldono
afcf136c4f Fixed regression in pending icon. 2017-06-20 17:23:01 +02:00
Taloth Saldono
d1b85444d0 Fixed DetectSampleFixture. 2017-06-19 23:48:47 +02:00
Taloth Saldono
63e0eba02f Fixed: releases with unknown seeders show on the UI as - instead of 0 to be easier to distinguish. 2017-06-19 23:30:43 +02:00
Taloth Saldono
475c99d492 Tweaked Newznab/Torznab handling of attr without value. 2017-06-19 23:30:43 +02:00
Taloth Saldono
23552c3267 Tweaked SingleInstancePolicy not to cancel startup if AppData is overridden is set. 2017-06-19 23:30:43 +02:00
Mark McDowall
a49e37239e Log responses from qbit 2017-06-18 22:21:33 -07:00
Mark McDowall
65b936ed94 Fixed: Time left cell for pending items in queue 2017-06-18 22:15:44 -07:00
Mark McDowall
359ef04861 Fixed: Grab/Delete buttons for pending releases in queue 2017-06-18 22:10:52 -07:00
Mark McDowall
8cf028e071 Fixed: Improve sample rejection message when MediaInfo is not available
Closes #1967
2017-06-18 21:26:18 -07:00
Mark McDowall
eea3419849 New: Download client and ID for custom scripts 2017-06-18 21:17:33 -07:00
Taloth Saldono
62bc63312d Fixed: Changed Authentication cookie to prevent conflicts with other apps. (invalidates existing logins)
Closes #1962
2017-06-18 00:18:23 +02:00
Taloth Saldono
6a6d415625 Fixed: Pending releases from blocked indexers should not be grabbed.
ref #1961
2017-06-18 00:07:16 +02:00
Taloth Saldono
1fbe82ae47 Prevent back-off escalation during grace period. 2017-06-17 23:42:04 +02:00
Taloth Saldono
87f3cc9014 Fixed: Regression prevented indexers from being re-enabled after a successful Test.
ref #1961
2017-06-17 23:41:38 +02:00
Taloth Saldono
ab07a40931 New: Added Omgwtfnzbs UHD category.
closes #1977
2017-06-17 20:55:18 +02:00
Taloth Saldono
10f292b225 Removed superfluous try catches so that DownloadClient backoff logic gets triggered. 2017-06-17 20:53:26 +02:00
Mark McDowall
de5ce23989 Fixed: Redirect calls missing URL Base 2017-06-10 16:47:12 -07:00
Mark McDowall
d0e226e269 Fixed: Logging full error message to database 2017-06-10 16:25:14 -07:00
Mark McDowall
d7cb5090fc Fixed: Twitter oAuth callback URL 2017-06-05 20:22:37 -07:00
Mark McDowall
416e9abca5 Fixed: Error message when adding a Plex server without a TV library 2017-06-04 21:10:56 -07:00
Mark McDowall
6dcb7768a9 Fixed broken test and add a couple more for ProcessDownloadDecisions 2017-06-04 00:52:34 -07:00
Mark McDowall
baf83b4c71 Fixed: Error when processing manual import decisions
Fixes #1590
2017-06-03 21:10:56 -07:00
Mark McDowall
285288db1a Additional logging when an import decision cannot be made 2017-06-03 21:10:56 -07:00
Jeremy Ryan
ec3dd982f6 Update EpisodeFileEditorLayoutTemplate.hbs 2017-06-03 21:10:20 -07:00
Drew Freyling
410aa467b8 include css files in minification 2017-06-03 21:08:35 -07:00
Mark McDowall
0c89a4ae8f Store releases when download client is unavailable
New: Retry releases when download client was unavailable
Closes #949
2017-06-03 20:41:32 -07:00
Taloth Saldono
a1edbafa8a Removed ugly UUID= VolumeLabel from mounts. 2017-05-27 22:26:41 +02:00
Taloth Saldono
ef5a400c68 Added missing ACC audio format. 2017-05-27 22:19:55 +02:00
Taloth Saldono
8cc02a9d9c Fixed: Minimum seeding check causing exception when release was pushed via api instead of by indexer. 2017-05-27 22:02:30 +02:00
Taloth Saldono
e83e852e0d Added ability for HealthChecks to run on specific events. 2017-05-27 20:45:01 +02:00
Taloth Saldono
4e10d30cf6 Added Status refreshes to Download Monitoring Service and allow DownloadService to report success (but not failure). 2017-05-27 20:44:58 +02:00
Taloth Saldono
f335cc1af8 Fixed: Prevent Download Client from being queried every minute if it failed repeatedly similar to Indexer temporarily disabled logic. 2017-05-27 20:44:55 +02:00
Taloth Saldono
f4bea5512c Refactored IndexerStatusService into Thingy Provider architecture. 2017-05-27 14:59:37 +02:00
Taloth Saldono
9f8091e4d7 Fixed: Added wildcard to BTN season searches to pick up 'Season x - Episodes 1-10' formats.
Closes #1946
2017-05-26 15:50:34 +02:00
Mark McDowall
5aa02eb15c Cleanup/fix EpisodeMonitoredService
Fixed: Unmonitor episodes when the season is unmonitored when adding the series
Fixes #1852
2017-05-23 22:04:56 -07:00
Mark McDowall
6bbe4ce066 Fixed: Ensure an API Key is set when starting Sonarr
Closes #1514
2017-05-22 21:54:18 -07:00
Mark McDowall
563b5ef017 Fixed: Don't use invalid scene mappings. Fixes #1627 2017-05-22 21:39:13 -07:00
Mark McDowall
b9df5634bf New: Link to more information on RSS sync interval
Closes #1918
2017-05-22 20:55:30 -07:00
Mark McDowall
755575d107 Fixed: Follow 301 redirects when fetching torrents
Closes #1929
2017-05-22 18:09:59 -07:00
Taloth Saldono
8eaab46488 Fixed up some errors and do the guid cache fix on the module instead of backend coz that would cause other issues. 2017-05-21 22:01:03 +02:00
Taloth Saldono
4fbc481780 Fixed: Processing of mixed newznab/torznab api such as the experimental animetosho api.
Ref #1384
2017-05-21 21:10:54 +02:00
Mark McDowall
a41b5723d4 New: Ability to set minimum seeders on a per indexer basis 2017-05-19 12:08:30 -07:00
Mark McDowall
c2b66cf524 Fixed: Deleting an episode file from the UI that was already deleted from disk
Fixes #1782
2017-05-19 12:07:57 -07:00
Mark McDowall
0d782e1cac Consistent formatting for MediaInfo in various locations
Fixed: Stream details for MP3 and EAC3 in Kodi metadata
Closes #1534
2017-05-19 11:18:50 -07:00
Mark McDowall
edf549d0fd Fixed: Improved message when a conflicting slug is added 2017-05-17 21:42:57 -07:00
Mark McDowall
cf7ce9804f Fixed: Ignore file quality matching release quality for unknown quality releases 2017-05-17 21:15:21 -07:00
Mark McDowall
e8d01daf03 Fixed: Ignore file quality matching release quality for season packs 2017-05-15 23:58:07 -07:00
Taloth Saldono
766520b851 Renamed DownloadClientStatus to DownloadClientInfo to avoid conflict. 2017-05-13 00:16:53 +02:00
Taloth Saldono
14144bd4d9 Renamed IndexerStatus.IndexerId to ProviderId. 2017-05-13 00:16:53 +02:00
Taloth Saldono
7b0e40d5d0 Replaced Url with BaseUrl in most indexers. 2017-05-13 00:16:53 +02:00
Taloth Saldono
2dbf095fd5 Fixed: Regression in Quality fallback by extension. 2017-05-13 00:13:12 +02:00
Taloth Saldono
2a86f8c241 Fixed: UI Series lookup autocomplete with diacritics.
Closes #1915
2017-05-11 20:37:07 +02:00
Taloth Saldono
c184e7ddcc Fixed: Multiple Scene Mapping exception even when the mappings pointed to the same tvdbid.
Closes #1917
2017-05-11 06:32:09 +02:00
Taloth Saldono
95c81f8905 Fixed exception in MountCheck if RootDirectory cannot be found. 2017-05-10 23:08:17 +02:00
Mark McDowall
db15949704 Fixed: Better error message when searching for episode without an absolute episode number 2017-05-09 20:00:21 -07:00
Mark McDowall
a63248401e Fixed: Width in Kodi Metadata 2017-05-09 20:00:21 -07:00
Kyse
1b32411219 New: Health Check warning if series folder is mounted with 'ro' option on linux
Closes #1867
2017-05-09 21:07:24 +02:00
Taloth Saldono
cd7368512d Fixed Scene Mapping error message. 2017-05-09 17:26:45 +02:00
Taloth Saldono
a5bc4a8f11 Added ability to filter scene mappings by regex via services. 2017-05-06 14:05:49 +02:00
Drew Freyling
2ae41a3404 remove redundant IE meta tag as we use http header instead 2017-05-05 22:26:28 -07:00
Drew Freyling
312136a57c use cleancss for minification 2017-05-05 22:26:10 -07:00
Mark McDowall
53e51af9c7 Fixed: Don't import the same file again
Closes #929
2017-05-02 22:52:59 -07:00
Mark McDowall
f852ca91c0 Clean up GrabbedReleaseQualityFixture 2017-05-02 22:52:11 -07:00
Mark McDowall
413dd51db1 Fix unit test 2017-05-02 22:06:18 -07:00
Mark McDowall
9d93fc1092 New: Prevent automatic import if file quality differs from grabbed release quality 2017-05-02 18:44:42 -07:00
Lloyd Sparkes
0bbb82c67f Update SocksWebProxy to fix #1641 2017-04-29 17:11:55 +02:00
Mark McDowall
cd8ae0d036 Added test to validate season pack being grabbed when only one episode is monitored
Closes #1862
2017-04-28 17:37:29 -07:00
Taloth Saldono
d726a7acb2 Fixed: Sonarr UI Authentication cookie should be placed on path (UrlBase) instead of domain alone.
fixes #1874
2017-04-27 23:33:04 +02:00
Mark McDowall
c94636e2b3 Placeholders for language profile migrations 2017-04-26 11:45:07 -07:00
Taloth Saldono
3749f3e2e5 Fixed missing icon preventing detailed explanation validation errors explanations from appearing. 2017-04-25 19:16:43 +02:00
Taloth Saldono
1f93bec055 Updated Transmission tests. 2017-04-24 21:53:40 +02:00
Taloth Saldono
a003a89b14 Fixed: Sonarr not importing torrents in Vuze if the torrent already finished seeding and was stopped. 2017-04-24 21:08:50 +02:00
Taloth Saldono
35fca89dad Fixed: Incorrect imports with Vuze when torrent contains a single file.
fixes #1805
2017-04-23 17:24:07 +02:00
Mark McDowall
e97e13e897 Fixed: Smarter application update completed message
Closes #1864
2017-04-21 10:52:31 -07:00
Taloth Saldono
f8d5f1fc94 Added -Scrambled to the ReleaseGroup cleanup list. 2017-04-14 20:47:17 +02:00
Taloth Saldono
46a1ff3e2d Tweaked parser to handle S01.Ep01.
closes #1849
2017-04-14 20:44:14 +02:00
Mark McDowall
f36d5dc881 Moving and Removing of downloads in usenet clients
Fixed: Moving items triggered via post-processing scripts
Fixed: Removing failed downloads fromusenet clients
2017-04-12 17:41:22 -07:00
Taloth Saldono
f8b8fcfb8d Fixed: Handling of priority setting when queueing is disabled in qBittorrent.
fixes #1841
2017-04-12 20:49:28 +02:00
Taloth Saldono
de7f68570e Fixed: Regression causing nzbToMedia imports to be copied instead of moved. 2017-04-12 20:49:28 +02:00
Taloth Saldono
fa006d85fd New: Check whether an existing episode file was deleted before grabbing an upgrade, to avoid timing issues in combination with Ignore Deleted Episodes. 2017-04-12 20:46:36 +02:00
Mark McDowall
413ce1d9a7 Fixed: Double periods in extra file names after rename 2017-04-11 20:32:34 -07:00
Mark McDowall
41f769790d Fix issue adding a series when TitleSlug for another series is null
Fixed: Adding a series when an existing series is has a null slug
Closes #1840
2017-04-11 17:57:29 -07:00
Taloth Saldono
b63bcd16a7 Fixed: Sample check has too little margin for 2 min anime with 1 minute files. Lowered to 15 sec. 2017-04-10 17:46:08 +02:00
Mark McDowall
b485bdaeec Fixed: Unable to execute custom scripts if IMDB ID is null
Fixes #1825
2017-04-08 08:20:57 -07:00
Taloth Saldono
b70d167911 Apply Cleanse to Exception Data as well. 2017-04-08 12:38:39 +02:00
Taloth Saldono
924fe80997 Fixed RssParser test. 2017-04-07 22:36:39 +02:00
Taloth Saldono
cd450a44bf Should not empty install folder, MirrorFolder will take care of it. 2017-04-07 22:09:49 +02:00
Taloth Saldono
35741b9cae Added a few more files to ignore during file copy. 2017-04-07 22:07:42 +02:00
Taloth Saldono
c9d1807670 Sentry should use CleanseLogMessage. 2017-04-07 20:42:39 +02:00
Taloth Saldono
94886e767b Fixed: UnsupportedFeedException should log error for each item 2017-04-07 19:32:12 +02:00
Taloth Saldono
e4c3418987 Fixed: Failing Newznab capabilities request should trigger automatic indexer backoff logic. 2017-04-07 19:10:08 +02:00
346 changed files with 6472 additions and 2041 deletions

View File

@@ -25,7 +25,7 @@ gulp.task('copyHtml', function () {
});
gulp.task('copyContent', function () {
return gulp.src([paths.src.content + '**/*.*', '!**/*.less'])
return gulp.src([paths.src.content + '**/*.*', '!**/*.less', '!**/*.css'])
.pipe(gulp.dest(paths.dest.content))
.pipe(livereload());
});

View File

@@ -5,7 +5,7 @@ var postcss = require('gulp-postcss');
var sourcemaps = require('gulp-sourcemaps');
var autoprefixer = require('autoprefixer-core');
var livereload = require('gulp-livereload');
var cleancss = require('gulp-clean-css');
var print = require('gulp-print');
var paths = require('./paths');
var errorHandler = require('./errorHandler');
@@ -16,6 +16,10 @@ gulp.task('less', function() {
paths.src.content + 'bootstrap.less',
paths.src.content + 'theme.less',
paths.src.content + 'overrides.less',
paths.src.content + 'bootstrap.toggle-switch.css',
paths.src.content + 'fullcalendar.css',
paths.src.content + 'Messenger/messenger.css',
paths.src.content + 'Messenger/messenger.flat.css',
paths.src.root + 'Series/series.less',
paths.src.root + 'Activity/activity.less',
paths.src.root + 'AddSeries/addSeries.less',
@@ -33,12 +37,13 @@ gulp.task('less', function() {
.pipe(sourcemaps.init())
.pipe(less({
dumpLineNumbers : 'false',
compress : true,
yuicompress : true,
compress : false,
yuicompress : false,
ieCompat : true,
strictImports : true
}))
.pipe(postcss([ autoprefixer({ browsers: ['last 2 versions'] }) ]))
.pipe(cleancss())
.on('error', errorHandler.onError)
.pipe(sourcemaps.write(paths.dest.content))
.pipe(gulp.dest(paths.dest.content))

View File

@@ -20,6 +20,7 @@
"del": "1.2.0",
"gulp": "3.9.0",
"gulp-cached": "1.1.0",
"gulp-clean-css": "^3.0.4",
"gulp-concat": "2.6.0",
"gulp-declare": "0.3.0",
"gulp-handlebars": "3.0.1",

View File

@@ -33,12 +33,12 @@ namespace NzbDrone.Api.Authentication
{
if (_configFileProvider.AuthenticationMethod == AuthenticationType.Forms)
{
RegisterFormsAuth(pipelines);
RegisterFormsAuth(pipelines);
}
else if (_configFileProvider.AuthenticationMethod == AuthenticationType.Basic)
{
pipelines.EnableBasicAuthentication(new BasicAuthenticationConfiguration(_authenticationService, "Sonarr"));
pipelines.EnableBasicAuthentication(new BasicAuthenticationConfiguration(_authenticationService, "Sonarr"));
}
pipelines.BeforeRequest.AddItemToEndOfPipeline((Func<NancyContext, Response>) RequiresAuthentication);
@@ -64,10 +64,13 @@ namespace NzbDrone.Api.Authentication
new DefaultHmacProvider(new PassphraseKeyGenerator(_configService.HmacPassphrase, Encoding.ASCII.GetBytes(_configService.HmacSalt)))
);
FormsAuthentication.FormsAuthenticationCookieName = "_ncfa_sonarr";
FormsAuthentication.Enable(pipelines, new FormsAuthenticationConfiguration
{
RedirectUrl = _configFileProvider.UrlBase + "/login",
UserMapper = _authenticationService,
Path = _configFileProvider.UrlBase,
CryptographyConfiguration = cryptographyConfiguration
});
}

View File

@@ -1,16 +1,13 @@
using System.Collections.Generic;
using System.IO;
using NLog;
using NzbDrone.Api.REST;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Exceptions;
using NzbDrone.SignalR;
using HttpStatusCode = System.Net.HttpStatusCode;
namespace NzbDrone.Api.EpisodeFiles
{
@@ -18,27 +15,21 @@ namespace NzbDrone.Api.EpisodeFiles
IHandle<EpisodeFileAddedEvent>
{
private readonly IMediaFileService _mediaFileService;
private readonly IDiskProvider _diskProvider;
private readonly IRecycleBinProvider _recycleBinProvider;
private readonly IDeleteMediaFiles _mediaFileDeletionService;
private readonly ISeriesService _seriesService;
private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification;
private readonly Logger _logger;
public EpisodeFileModule(IBroadcastSignalRMessage signalRBroadcaster,
IMediaFileService mediaFileService,
IDiskProvider diskProvider,
IRecycleBinProvider recycleBinProvider,
IDeleteMediaFiles mediaFileDeletionService,
ISeriesService seriesService,
IQualityUpgradableSpecification qualityUpgradableSpecification,
Logger logger)
IQualityUpgradableSpecification qualityUpgradableSpecification)
: base(signalRBroadcaster)
{
_mediaFileService = mediaFileService;
_diskProvider = diskProvider;
_recycleBinProvider = recycleBinProvider;
_mediaFileDeletionService = mediaFileDeletionService;
_seriesService = seriesService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
_logger = logger;
GetResourceById = GetEpisodeFile;
GetResourceAll = GetEpisodeFiles;
UpdateResource = SetQuality;
@@ -77,13 +68,15 @@ namespace NzbDrone.Api.EpisodeFiles
private void DeleteEpisodeFile(int id)
{
var episodeFile = _mediaFileService.Get(id);
var series = _seriesService.GetSeries(episodeFile.SeriesId);
var fullPath = Path.Combine(series.Path, episodeFile.RelativePath);
var subfolder = _diskProvider.GetParentFolder(series.Path).GetRelativePath(_diskProvider.GetParentFolder(fullPath));
_logger.Info("Deleting episode file: {0}", fullPath);
_recycleBinProvider.DeleteFile(fullPath, subfolder);
_mediaFileService.Delete(episodeFile, DeleteMediaFileReason.Manual);
if (episodeFile == null)
{
throw new NzbDroneClientException(HttpStatusCode.NotFound, "Episode file not found");
}
var series = _seriesService.GetSeries(episodeFile.SeriesId);
_mediaFileDeletionService.DeleteEpisodeFile(series, episodeFile);
}
public void Handle(EpisodeFileAddedEvent message)

View File

@@ -14,7 +14,7 @@ namespace NzbDrone.Api.Episodes
{
public abstract class EpisodeModuleWithSignalR : NzbDroneRestModuleWithSignalR<EpisodeResource, Episode>,
IHandle<EpisodeGrabbedEvent>,
IHandle<EpisodeDownloadedEvent>
IHandle<EpisodeImportedEvent>
{
protected readonly IEpisodeService _episodeService;
protected readonly ISeriesService _seriesService;
@@ -115,9 +115,14 @@ namespace NzbDrone.Api.Episodes
}
}
public void Handle(EpisodeDownloadedEvent message)
public void Handle(EpisodeImportedEvent message)
{
foreach (var episode in message.Episode.Episodes)
if (!message.NewDownload)
{
return;
}
foreach (var episode in message.EpisodeInfo.Episodes)
{
BroadcastResourceChange(ModelAction.Updated, episode.Id);
}

View File

@@ -0,0 +1,46 @@
using System;
using Nancy;
using Nancy.Bootstrapper;
using Nancy.Responses;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Api.Extensions.Pipelines
{
public class UrlBasePipeline : IRegisterNancyPipeline
{
private readonly string _urlBase;
public UrlBasePipeline(IConfigFileProvider configFileProvider)
{
_urlBase = configFileProvider.UrlBase;
}
public int Order => 99;
public void Register(IPipelines pipelines)
{
if (_urlBase.IsNotNullOrWhiteSpace())
{
pipelines.BeforeRequest.AddItemToStartOfPipeline((Func<NancyContext, Response>) Handle);
}
}
private Response Handle(NancyContext context)
{
var basePath = context.Request.Url.BasePath;
if (basePath.IsNullOrWhiteSpace())
{
return new RedirectResponse($"{_urlBase}{context.Request.Path}{context.Request.Url.Query}");
}
if (_urlBase != basePath)
{
return new NotFoundResponse();
}
return null;
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Text.RegularExpressions;
using Nancy;
@@ -17,7 +17,7 @@ namespace NzbDrone.Api.Frontend.Mappers
private readonly IAnalyticsService _analyticsService;
private readonly Func<ICacheBreakerProvider> _cacheBreakProviderFactory;
private readonly string _indexPath;
private static readonly Regex ReplaceRegex = new Regex(@"(?:(?<attribute>href|src)=\"")(?<path>.*?(?<extension>css|js|png|ico|ics))(?:\"")(?:\s(?<nohash>data-no-hash))?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex ReplaceRegex = new Regex(@"(?:(?<attribute>href|src)=\"")(?<path>.*?(?<extension>css|js|png|ico|ics|svg))(?:\"")(?:\s(?<nohash>data-no-hash))?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static string API_KEY;
private static string URL_BASE;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Nancy.Responses;
@@ -38,20 +38,6 @@ namespace NzbDrone.Api.Frontend
return new NotFoundResponse();
}
//Redirect to the subfolder if the request went to the base URL
if (path.Equals("/"))
{
var urlBase = _configFileProvider.UrlBase;
if (!string.IsNullOrEmpty(urlBase))
{
if (Request.Url.BasePath != urlBase)
{
return new RedirectResponse(urlBase + "/");
}
}
}
var mapper = _requestMappers.SingleOrDefault(m => m.CanHandle(path));
if (mapper != null)

View File

@@ -53,7 +53,7 @@ namespace NzbDrone.Api.Indexers
private Response DownloadRelease(ReleaseResource release)
{
var remoteEpisode = _remoteEpisodeCache.Find(release.Guid);
var remoteEpisode = _remoteEpisodeCache.Find(GetCacheKey(release));
if (remoteEpisode == null)
{
@@ -68,7 +68,7 @@ namespace NzbDrone.Api.Indexers
}
catch (ReleaseDownloadException ex)
{
_logger.Error(ex);
_logger.Error(ex, ex.Message);
throw new NzbDroneClientException(HttpStatusCode.Conflict, "Getting release from indexer failed");
}
@@ -113,8 +113,14 @@ namespace NzbDrone.Api.Indexers
protected override ReleaseResource MapDecision(DownloadDecision decision, int initialWeight)
{
_remoteEpisodeCache.Set(decision.RemoteEpisode.Release.Guid, decision.RemoteEpisode, TimeSpan.FromMinutes(30));
return base.MapDecision(decision, initialWeight);
var resource = base.MapDecision(decision, initialWeight);
_remoteEpisodeCache.Set(GetCacheKey(resource), decision.RemoteEpisode, TimeSpan.FromMinutes(30));
return resource;
}
private string GetCacheKey(ReleaseResource resource)
{
return string.Concat(resource.IndexerId, "_", resource.Guid);
}
}
}

View File

@@ -106,6 +106,7 @@
<Compile Include="Commands\CommandResource.cs" />
<Compile Include="Extensions\AccessControlHeaders.cs" />
<Compile Include="Extensions\Pipelines\CorsPipeline.cs" />
<Compile Include="Extensions\Pipelines\UrlBasePipeline.cs" />
<Compile Include="Extensions\Pipelines\RequestLoggingPipeline.cs" />
<Compile Include="Frontend\Mappers\LoginHtmlMapper.cs" />
<Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" />

View File

@@ -86,7 +86,7 @@ namespace NzbDrone.Common.Test
{
first.AsOsAgnostic().PathEquals(second.AsOsAgnostic()).Should().BeFalse();
}
[Test]
public void should_return_false_when_not_a_child()
{
@@ -113,6 +113,7 @@ namespace NzbDrone.Common.Test
[TestCase(@"C:\Test\", @"C:\Test\mydir")]
[TestCase(@"C:\Test\", @"C:\Test\mydir\")]
[TestCase(@"C:\Test", @"C:\Test\30.Rock.S01E01.Pilot.avi")]
[TestCase(@"C:\", @"C:\Test\30.Rock.S01E01.Pilot.avi")]
public void path_should_be_parent(string parentPath, string childPath)
{
parentPath.AsOsAgnostic().IsParentPath(childPath.AsOsAgnostic()).Should().BeTrue();

View File

@@ -590,7 +590,7 @@ namespace NzbDrone.Common.Disk
private bool ShouldIgnore(FileInfo file)
{
if (file.Name.StartsWith(".nfs"))
if (file.Name.StartsWith(".nfs") || file.Name == "debug.log" || file.Name.EndsWith(".socket"))
{
_logger.Trace("Ignoring file {0}", file.FullName);
return true;

View File

@@ -1,4 +1,5 @@
using System.IO;
using System.Collections.Generic;
using System.IO;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Common.Disk
@@ -8,10 +9,11 @@ namespace NzbDrone.Common.Disk
private readonly DriveInfo _driveInfo;
private readonly DriveType _driveType;
public DriveInfoMount(DriveInfo driveInfo, DriveType driveType = DriveType.Unknown)
public DriveInfoMount(DriveInfo driveInfo, DriveType driveType = DriveType.Unknown, MountOptions mountOptions = null)
{
_driveInfo = driveInfo;
_driveType = driveType;
MountOptions = mountOptions;
}
public long AvailableFreeSpace => _driveInfo.AvailableFreeSpace;
@@ -33,6 +35,8 @@ namespace NzbDrone.Common.Disk
public bool IsReady => _driveInfo.IsReady;
public MountOptions MountOptions { get; private set; }
public string Name => _driveInfo.Name;
public string RootDirectory => _driveInfo.RootDirectory.FullName;

View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.IO;
namespace NzbDrone.Common.Disk
@@ -8,6 +9,7 @@ namespace NzbDrone.Common.Disk
string DriveFormat { get; }
DriveType DriveType { get; }
bool IsReady { get; }
MountOptions MountOptions { get; }
string Name { get; }
string RootDirectory { get; }
long TotalFreeSpace { get; }

View File

@@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace NzbDrone.Common.Disk
{
public class MountOptions
{
private readonly Dictionary<string, string> _options;
public MountOptions(Dictionary<string, string> options)
{
_options = options;
}
public bool IsReadOnly => _options.ContainsKey("ro");
}
}

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Common.Extensions
{
public static class ExceptionExtensions
{
public static T WithData<T>(this T ex, string key, string value) where T : Exception
{
ex.AddData(key, value);
return ex;
}
public static T WithData<T>(this T ex, string key, int value) where T : Exception
{
ex.AddData(key, value.ToString());
return ex;
}
public static T WithData<T>(this T ex, string key, Http.HttpUri value) where T : Exception
{
ex.AddData(key, value.ToString());
return ex;
}
public static T WithData<T>(this T ex, Http.HttpResponse response, int maxSampleLength = 512) where T : Exception
{
if (response == null || response.Content == null) return ex;
var contentSample = response.Content.Substring(0, Math.Min(response.Content.Length, 512));
if (response.Headers != null)
{
ex.AddData("ContentType", response.Headers.ContentType ?? string.Empty);
}
ex.AddData("ContentLength", response.Content.Length.ToString());
ex.AddData("ContentSample", contentSample);
return ex;
}
private static void AddData(this Exception ex, string key, string value)
{
if (value.IsNullOrWhiteSpace()) return;
ex.Data[key] = value;
}
}
}

View File

@@ -80,11 +80,11 @@ namespace NzbDrone.Common.Extensions
public static bool IsParentPath(this string parentPath, string childPath)
{
if (parentPath != "/")
if (parentPath != "/" && !parentPath.EndsWith(":\\"))
{
parentPath = parentPath.TrimEnd(Path.DirectorySeparatorChar);
}
if (childPath != "/")
if (childPath != "/" && !parentPath.EndsWith(":\\"))
{
childPath = childPath.TrimEnd(Path.DirectorySeparatorChar);
}
@@ -276,4 +276,4 @@ namespace NzbDrone.Common.Extensions
return Path.Combine(appFolderInfo.StartUpFolder, NLOG_CONFIG_FILE);
}
}
}
}

View File

@@ -1,13 +1,14 @@
namespace NzbDrone.Common.Http
namespace NzbDrone.Common.Http
{
public enum HttpMethod
{
GET,
PUT,
POST,
HEAD,
PUT,
DELETE,
HEAD,
OPTIONS,
PATCH,
OPTIONS
MERGE
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json.Linq;
using NzbDrone.Common.Serializer;
namespace NzbDrone.Common.Instrumentation
{
public class CleansingJsonVisitor : JsonVisitor
{
public override void Visit(JArray json)
{
for (var i = 0; i < json.Count; i++)
{
if (json[i].Type == JTokenType.String)
{
var text = json[i].Value<string>();
json[i] = new JValue(CleanseLogMessage.Cleanse(text));
}
}
foreach (JToken token in json)
{
Visit(token);
}
}
public override void Visit(JProperty property)
{
if (property.Value.Type == JTokenType.String)
{
property.Value = CleanseValue(property.Value as JValue);
}
else
{
base.Visit(property);
}
}
private JValue CleanseValue(JValue value)
{
var text = value.Value<string>();
var cleansed = CleanseLogMessage.Cleanse(text);
return new JValue(cleansed);
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json.Linq;
namespace NzbDrone.Common.Instrumentation.Sentry
{
public class SentryPacketCleanser
{
public void CleansePacket(SonarrSentryPacket packet)
{
packet.Message = CleanseLogMessage.Cleanse(packet.Message);
if (packet.Fingerprint != null)
{
for (var i = 0; i < packet.Fingerprint.Length; i++)
{
packet.Fingerprint[i] = CleanseLogMessage.Cleanse(packet.Fingerprint[i]);
}
}
if (packet.Extra != null)
{
var target = JObject.FromObject(packet.Extra);
new CleansingJsonVisitor().Visit(target);
packet.Extra = target;
}
}
}
}

View File

@@ -114,7 +114,6 @@ namespace NzbDrone.Common.Instrumentation.Sentry
var extras = logEvent.Properties.ToDictionary(x => x.Key.ToString(), x => x.Value.ToString());
_client.Logger = logEvent.LoggerName;
var sentryMessage = new SentryMessage(logEvent.Message, logEvent.Parameters);
var sentryEvent = new SentryEvent(logEvent.Exception)

View File

@@ -6,6 +6,13 @@ namespace NzbDrone.Common.Instrumentation.Sentry
{
public class SonarrJsonPacketFactory : IJsonPacketFactory
{
private readonly SentryPacketCleanser _cleanser;
public SonarrJsonPacketFactory()
{
_cleanser = new SentryPacketCleanser();
}
private static string ShortenPath(string path)
{
@@ -37,6 +44,8 @@ namespace NzbDrone.Common.Instrumentation.Sentry
frame.Filename = ShortenPath(frame.Filename);
}
}
_cleanser.CleansePacket(packet);
}
catch (Exception)
{
@@ -46,7 +55,6 @@ namespace NzbDrone.Common.Instrumentation.Sentry
return packet;
}
[Obsolete]
public JsonPacket Create(string project, SentryMessage message, ErrorLevel level = ErrorLevel.Info, IDictionary<string, string> tags = null,
string[] fingerprint = null, object extra = null)
@@ -61,4 +69,4 @@ namespace NzbDrone.Common.Instrumentation.Sentry
throw new NotImplementedException();
}
}
}
}

View File

@@ -46,16 +46,14 @@
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.4.3\lib\net40\NLog.dll</HintPath>
</Reference>
<Reference Include="Org.Mentalis, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DotNet4.SocksProxy.1.3.2.0\lib\net40\Org.Mentalis.dll</HintPath>
<Private>True</Private>
<Reference Include="Org.Mentalis, Version=1.0.0.1, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DotNet4.SocksProxy.1.3.4.0\lib\net40\Org.Mentalis.dll</HintPath>
</Reference>
<Reference Include="SharpRaven, Version=2.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\SharpRaven.2.2.0\lib\net40\SharpRaven.dll</HintPath>
</Reference>
<Reference Include="SocksWebProxy, Version=1.3.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DotNet4.SocksProxy.1.3.2.0\lib\net40\SocksWebProxy.dll</HintPath>
<Private>True</Private>
<Reference Include="SocksWebProxy, Version=1.3.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\DotNet4.SocksProxy.1.3.4.0\lib\net40\SocksWebProxy.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
@@ -87,6 +85,7 @@
<Compile Include="Disk\FileSystemLookupService.cs" />
<Compile Include="Disk\DriveInfoMount.cs" />
<Compile Include="Disk\IMount.cs" />
<Compile Include="Disk\MountOptions.cs" />
<Compile Include="Disk\RelativeFileSystemModel.cs" />
<Compile Include="Disk\FileSystemModel.cs" />
<Compile Include="Disk\FileSystemResult.cs" />
@@ -136,6 +135,7 @@
<Compile Include="Extensions\Base64Extensions.cs" />
<Compile Include="Extensions\DateTimeExtensions.cs" />
<Compile Include="Crypto\HashConverter.cs" />
<Compile Include="Extensions\ExceptionExtensions.cs" />
<Compile Include="Extensions\Int64Extensions.cs" />
<Compile Include="Extensions\ObjectExtensions.cs" />
<Compile Include="Extensions\StreamExtensions.cs" />
@@ -175,12 +175,14 @@
<Compile Include="Extensions\IEnumerableExtensions.cs" />
<Compile Include="Http\UserAgentBuilder.cs" />
<Compile Include="Instrumentation\CleanseLogMessage.cs" />
<Compile Include="Instrumentation\CleansingJsonVisitor.cs" />
<Compile Include="Instrumentation\Extensions\LoggerProgressExtensions.cs" />
<Compile Include="Instrumentation\GlobalExceptionHandlers.cs" />
<Compile Include="Instrumentation\LogEventExtensions.cs" />
<Compile Include="Instrumentation\NzbDroneFileTarget.cs" />
<Compile Include="Instrumentation\NzbDroneLogger.cs" />
<Compile Include="Instrumentation\Sentry\SentryDebounce.cs" />
<Compile Include="Instrumentation\Sentry\SentryPacketCleanser.cs" />
<Compile Include="Instrumentation\Sentry\SentryTarget.cs" />
<Compile Include="Instrumentation\Sentry\MachineNameUserFactory.cs" />
<Compile Include="Instrumentation\Sentry\SonarrJsonPacketFactory.cs" />
@@ -205,6 +207,7 @@
<Compile Include="Serializer\HttpUriConverter.cs" />
<Compile Include="Serializer\IntConverter.cs" />
<Compile Include="Serializer\Json.cs" />
<Compile Include="Serializer\JsonVisitor.cs" />
<Compile Include="ServiceFactory.cs" />
<Compile Include="ServiceProvider.cs" />
<Compile Include="Extensions\StringExtensions.cs" />

View File

@@ -8,6 +8,7 @@ using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Model;
namespace NzbDrone.Common.Processes
@@ -129,7 +130,25 @@ namespace NzbDrone.Common.Processes
{
foreach (DictionaryEntry environmentVariable in environmentVariables)
{
startInfo.EnvironmentVariables.Add(environmentVariable.Key.ToString(), environmentVariable.Value.ToString());
try
{
_logger.Trace("Setting environment variable '{0}' to '{1}'", environmentVariable.Key, environmentVariable.Value);
startInfo.EnvironmentVariables.Add(environmentVariable.Key.ToString(), environmentVariable.Value.ToString());
}
catch (Exception e)
{
if (environmentVariable.Value == null)
{
_logger.Error(e, "Unable to set environment variable '{0}', value is null", environmentVariable.Key);
}
else
{
_logger.Error(e, "Unable to set environment variable '{0}'", environmentVariable.Key);
}
throw;
}
}
}

View File

@@ -60,6 +60,11 @@ namespace NzbDrone.Common.Reflection
return (T)attribute;
}
public static T[] GetAttributes<T>(this MemberInfo member) where T : Attribute
{
return member.GetCustomAttributes(typeof(T), false).OfType<T>().ToArray();
}
public static Type FindTypeByName(this Assembly assembly, string name)
{
return assembly.GetTypes().SingleOrDefault(c => c.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
@@ -70,4 +75,4 @@ namespace NzbDrone.Common.Reflection
return type.GetCustomAttributes(typeof(TAttribute), true).Any();
}
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Net;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation;
namespace NzbDrone.Common.Security
@@ -14,6 +15,12 @@ namespace NzbDrone.Common.Security
public static void Register()
{
if (OsInfo.IsNotWindows)
{
// This was never meant to be used on mono, and will cause issues with mono 5 and higher if btls is enabled.
return;
}
try
{
// TODO: In v3 we should drop support for SSL3 because its very insecure. Only leaving it enabled because some people might rely on it.

View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json.Linq;
namespace NzbDrone.Common.Serializer
{
public class JsonVisitor
{
protected void Dispatch(JToken json)
{
switch (json.Type)
{
case JTokenType.Object:
Visit(json as JObject);
break;
case JTokenType.Array:
Visit(json as JArray);
break;
case JTokenType.Raw:
Visit(json as JRaw);
break;
case JTokenType.Constructor:
Visit(json as JConstructor);
break;
case JTokenType.Property:
Visit(json as JProperty);
break;
case JTokenType.Comment:
case JTokenType.Integer:
case JTokenType.Float:
case JTokenType.String:
case JTokenType.Boolean:
case JTokenType.Null:
case JTokenType.Undefined:
case JTokenType.Date:
case JTokenType.Bytes:
case JTokenType.Guid:
case JTokenType.Uri:
case JTokenType.TimeSpan:
Visit(json as JValue);
break;
default:
break;
}
}
public virtual void Visit(JToken json)
{
Dispatch(json);
}
public virtual void Visit(JContainer json)
{
Dispatch(json);
}
public virtual void Visit(JArray json)
{
foreach (JToken token in json)
{
Visit(token);
}
}
public virtual void Visit(JConstructor json)
{
}
public virtual void Visit(JObject json)
{
foreach (JProperty property in json.Properties())
{
Visit(property);
}
}
public virtual void Visit(JProperty property)
{
Visit(property.Value);
}
public virtual void Visit(JValue value)
{
}
}
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DotNet4.SocksProxy" version="1.3.2.0" targetFramework="net40" />
<package id="DotNet4.SocksProxy" version="1.3.4.0" targetFramework="net40" />
<package id="ICSharpCode.SharpZipLib.Patched" version="0.86.5" targetFramework="net40" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" />
<package id="NLog" version="4.4.3" targetFramework="net40" />

View File

@@ -20,11 +20,14 @@ namespace NzbDrone.Core.Test.DataAugmentation.Scene
private Mock<ISceneMappingProvider> _provider1;
private Mock<ISceneMappingProvider> _provider2;
[SetUp]
public void Setup()
{
_fakeMappings = Builder<SceneMapping>.CreateListOfSize(5).BuildListOfNew();
_fakeMappings = Builder<SceneMapping>.CreateListOfSize(5)
.All()
.With(v => v.FilterRegex = null)
.BuildListOfNew();
_fakeMappings[0].SearchTerm = "Words";
_fakeMappings[1].SearchTerm = "That";
@@ -193,7 +196,7 @@ namespace NzbDrone.Core.Test.DataAugmentation.Scene
Mocker.GetMock<ISceneMappingRepository>().Setup(c => c.All()).Returns(mappings);
var tvdbId = Subject.FindTvdbId(parseTitle);
var seasonNumber = Subject.GetSceneSeasonNumber(parseTitle);
var seasonNumber = Subject.GetSceneSeasonNumber(parseTitle, null);
tvdbId.Should().Be(100);
seasonNumber.Should().Be(expectedSeasonNumber);
@@ -314,6 +317,49 @@ namespace NzbDrone.Core.Test.DataAugmentation.Scene
Subject.GetSceneNames(100, new List<int> { 4 }, new List<int> { 4 }).Should().BeEmpty();
}
[Test]
public void should_filter_by_regex()
{
var mappings = new List<SceneMapping>
{
new SceneMapping { Title = "Amareto", ParseTerm = "amareto", SearchTerm = "Amareto", TvdbId = 100 },
new SceneMapping { Title = "Amareto", ParseTerm = "amareto", SearchTerm = "Amareto", TvdbId = 101, FilterRegex="-Viva$" }
};
Mocker.GetMock<ISceneMappingRepository>().Setup(c => c.All()).Returns(mappings);
Subject.FindTvdbId("Amareto", "Amareto.S01E01.720p.WEB-DL-Viva").Should().Be(101);
Subject.FindTvdbId("Amareto", "Amareto.S01E01.720p.WEB-DL-DMO").Should().Be(100);
}
[Test]
public void should_throw_if_multiple_mappings()
{
var mappings = new List<SceneMapping>
{
new SceneMapping { Title = "Amareto", ParseTerm = "amareto", SearchTerm = "Amareto", TvdbId = 100 },
new SceneMapping { Title = "Amareto", ParseTerm = "amareto", SearchTerm = "Amareto", TvdbId = 101 }
};
Mocker.GetMock<ISceneMappingRepository>().Setup(c => c.All()).Returns(mappings);
Assert.Throws<InvalidSceneMappingException>(() => Subject.FindTvdbId("Amareto", "Amareto.S01E01.720p.WEB-DL-Viva"));
}
[Test]
public void should_not_throw_if_multiple_mappings_with_same_tvdbid()
{
var mappings = new List<SceneMapping>
{
new SceneMapping { Title = "Amareto", ParseTerm = "amareto", SearchTerm = "Amareto", TvdbId = 100 },
new SceneMapping { Title = "Amareto", ParseTerm = "amareto", SearchTerm = "Amareto", TvdbId = 100 }
};
Mocker.GetMock<ISceneMappingRepository>().Setup(c => c.All()).Returns(mappings);
Subject.FindTvdbId("Amareto", "Amareto.S01E01.720p.WEB-DL-Viva").Should().Be(100);
}
private void AssertNoUpdate()
{
_provider1.Verify(c => c.GetSceneMappings(), Times.Once());

View File

@@ -5,6 +5,7 @@ using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.DataAugmentation.Xem;
using NzbDrone.Core.DataAugmentation.Xem.Model;
using NzbDrone.Core.Test.Framework;
@@ -98,7 +99,6 @@ namespace NzbDrone.Core.Test.DataAugmentation.SceneNumbering
});
}
[Test]
public void should_not_fetch_scenenumbering_if_not_listed()
{
@@ -308,5 +308,19 @@ namespace NzbDrone.Core.Test.DataAugmentation.SceneNumbering
episode.SceneSeasonNumber.Should().NotHaveValue();
episode.SceneEpisodeNumber.Should().NotHaveValue();
}
[Test]
public void should_skip_mapping_when_scene_information_is_all_zero()
{
GivenTvdbMappings();
AddTvdbMapping(0, 0, 0, 8, 3, 1); // 3x01 -> 3x01
AddTvdbMapping(0, 0, 0, 9, 3, 2); // 3x02 -> 3x02
Subject.Handle(new SeriesUpdatedEvent(_series));
Mocker.GetMock<IEpisodeService>()
.Verify(v => v.UpdateEpisodes(It.Is<List<Episode>>(e => e.Any(c => c.SceneAbsoluteEpisodeNumber == 0 && c.SceneSeasonNumber == 0 && c.SceneEpisodeNumber == 0))), Times.Never());
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.DecisionEngineTests
{
[TestFixture]
public class BlockedIndexerSpecificationFixture : CoreTest<BlockedIndexerSpecification>
{
private RemoteEpisode _remoteEpisode;
[SetUp]
public void Setup()
{
_remoteEpisode = new RemoteEpisode
{
Release = new ReleaseInfo { IndexerId = 1 }
};
Mocker.GetMock<IIndexerStatusService>()
.Setup(v => v.GetBlockedProviders())
.Returns(new List<IndexerStatus>());
}
private void WithBlockedIndexer()
{
Mocker.GetMock<IIndexerStatusService>()
.Setup(v => v.GetBlockedProviders())
.Returns(new List<IndexerStatus> { new IndexerStatus { ProviderId = 1, DisabledTill = DateTime.UtcNow } });
}
[Test]
public void should_return_true_if_no_blocked_indexer()
{
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_false_if_blocked_indexer()
{
WithBlockedIndexer();
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
Subject.Type.Should().Be(RejectionType.Temporary);
}
}
}

View File

@@ -28,6 +28,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
private Mock<IDecisionEngineSpecification> _fail2;
private Mock<IDecisionEngineSpecification> _fail3;
private Mock<IDecisionEngineSpecification> _failDelayed1;
[SetUp]
public void Setup()
{
@@ -39,14 +41,19 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_fail2 = new Mock<IDecisionEngineSpecification>();
_fail3 = new Mock<IDecisionEngineSpecification>();
_failDelayed1 = new Mock<IDecisionEngineSpecification>();
_pass1.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null)).Returns(Decision.Accept);
_pass2.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null)).Returns(Decision.Accept);
_pass3.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null)).Returns(Decision.Accept);
_fail1.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null)).Returns(Decision.Reject("fail1"));
_fail2.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null)).Returns(Decision.Reject("fail2"));
_fail3.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null)).Returns(Decision.Reject("fail3"));
_failDelayed1.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null)).Returns(Decision.Reject("failDelayed1"));
_failDelayed1.SetupGet(c => c.Priority).Returns(SpecificationPriority.Disk);
_reports = new List<ReleaseInfo> { new ReleaseInfo { Title = "The.Office.S03E115.DVDRip.XviD-OSiTV" } };
_remoteEpisode = new RemoteEpisode {
Series = new Series(),
@@ -78,6 +85,25 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_pass3.Verify(c => c.IsSatisfiedBy(_remoteEpisode, null), Times.Once());
}
[Test]
public void should_call_delayed_specifications_if_non_delayed_passed()
{
GivenSpecifications(_pass1, _failDelayed1);
Subject.GetRssDecision(_reports).ToList();
_failDelayed1.Verify(c => c.IsSatisfiedBy(_remoteEpisode, null), Times.Once());
}
[Test]
public void should_not_call_delayed_specifications_if_non_delayed_failed()
{
GivenSpecifications(_fail1, _failDelayed1);
Subject.GetRssDecision(_reports).ToList();
_failDelayed1.Verify(c => c.IsSatisfiedBy(_remoteEpisode, null), Times.Never());
}
[Test]
public void should_return_rejected_if_single_specs_fail()
{
@@ -214,10 +240,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
var criteria = new SeasonSearchCriteria { Episodes = episodes.Take(1).ToList(), SeasonNumber = 1 };
var reports = episodes.Select(v =>
new ReleaseInfo()
{
Title = string.Format("{0}.S{1:00}E{2:00}.720p.WEB-DL-DRONE", series.Title, v.SceneSeasonNumber, v.SceneEpisodeNumber)
var reports = episodes.Select(v =>
new ReleaseInfo()
{
Title = string.Format("{0}.S{1:00}E{2:00}.720p.WEB-DL-DRONE", series.Title, v.SceneSeasonNumber, v.SceneEpisodeNumber)
}).ToList();
Mocker.GetMock<IParsingService>()
@@ -289,4 +315,4 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
ExceptionVerification.ExpectedErrors(1);
}
}
}
}

View File

@@ -137,5 +137,17 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
WithFirstEpisodeUnmonitored();
_monitoredEpisodeSpecification.IsSatisfiedBy(_parseResultSingle, new SingleEpisodeSearchCriteria{ MonitoredEpisodesOnly = true}).Accepted.Should().BeFalse();
}
[Test]
public void should_return_false_if_all_episodes_are_not_monitored_for_season_pack_release()
{
WithSecondEpisodeUnmonitored();
_parseResultMulti.ParsedEpisodeInfo = new ParsedEpisodeInfo
{
FullSeason = true
};
_monitoredEpisodeSpecification.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeFalse();
}
}
}

View File

@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine.Specifications.RssSync;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Common.Disk;
using Moq;
using NzbDrone.Test.Common;
using System.IO;
namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
{
[TestFixture]
public class DeletedEpisodeFileSpecificationFixture : CoreTest<DeletedEpisodeFileSpecification>
{
private RemoteEpisode _parseResultMulti;
private RemoteEpisode _parseResultSingle;
private EpisodeFile _firstFile;
private EpisodeFile _secondFile;
[SetUp]
public void Setup()
{
_firstFile = new EpisodeFile
{
Id = 1,
RelativePath = "My.Series.S01E01.mkv",
Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)),
DateAdded = DateTime.Now
};
_secondFile = new EpisodeFile
{
Id = 2,
RelativePath = "My.Series.S01E02.mkv",
Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)),
DateAdded = DateTime.Now
};
var singleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 } };
var doubleEpisodeList = new List<Episode> {
new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 },
new Episode { EpisodeFile = _secondFile, EpisodeFileId = 2 }
};
var fakeSeries = Builder<Series>.CreateNew()
.With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p })
.With(c => c.Path = @"C:\Series\My.Series".AsOsAgnostic())
.Build();
_parseResultMulti = new RemoteEpisode
{
Series = fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
Episodes = doubleEpisodeList
};
_parseResultSingle = new RemoteEpisode
{
Series = fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
Episodes = singleEpisodeList
};
GivenUnmonitorDeletedEpisodes(true);
}
private void GivenUnmonitorDeletedEpisodes(bool enabled)
{
Mocker.GetMock<IConfigService>()
.SetupGet(v => v.AutoUnmonitorPreviouslyDownloadedEpisodes)
.Returns(enabled);
}
private void WithExistingFile(EpisodeFile episodeFile)
{
var path = Path.Combine(@"C:\Series\My.Series".AsOsAgnostic(), episodeFile.RelativePath);
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.FileExists(path))
.Returns(true);
}
[Test]
public void should_return_true_when_unmonitor_deleted_episdes_is_off()
{
GivenUnmonitorDeletedEpisodes(false);
Subject.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_when_searching()
{
Subject.IsSatisfiedBy(_parseResultSingle, new SeasonSearchCriteria()).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_if_file_exists()
{
WithExistingFile(_firstFile);
Subject.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_false_if_file_is_missing()
{
Subject.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse();
}
[Test]
public void should_return_true_if_both_of_multiple_episode_exist()
{
WithExistingFile(_firstFile);
WithExistingFile(_secondFile);
Subject.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_false_if_one_of_multiple_episode_is_missing()
{
WithExistingFile(_firstFile);
Subject.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeFalse();
}
}
}

View File

@@ -0,0 +1,111 @@
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.DecisionEngine.Specifications.Search;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.TorrentRss;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.DecisionEngineTests.Search
{
[TestFixture]
public class TorrentSeedingSpecificationFixture : TestBase<TorrentSeedingSpecification>
{
private Series _series;
private RemoteEpisode _remoteEpisode;
private IndexerDefinition _indexerDefinition;
[SetUp]
public void Setup()
{
_series = Builder<Series>.CreateNew().With(s => s.Id = 1).Build();
_remoteEpisode = new RemoteEpisode
{
Series = _series,
Release = new TorrentInfo
{
IndexerId = 1,
Title = "Series.Title.S01.720p.BluRay.X264-RlsGrp",
Seeders = 0
}
};
_indexerDefinition = new IndexerDefinition
{
Settings = new TorrentRssIndexerSettings { MinimumSeeders = 5 }
};
Mocker.GetMock<IIndexerFactory>()
.Setup(v => v.Get(1))
.Returns(_indexerDefinition);
}
private void GivenReleaseSeeders(int? seeders)
{
(_remoteEpisode.Release as TorrentInfo).Seeders = seeders;
}
[Test]
public void should_return_true_if_not_torrent()
{
_remoteEpisode.Release = new ReleaseInfo
{
IndexerId = 1,
Title = "Series.Title.S01.720p.BluRay.X264-RlsGrp"
};
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_if_indexer_not_specified()
{
_remoteEpisode.Release.IndexerId = 0;
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_if_indexer_no_longer_exists()
{
Mocker.GetMock<IIndexerFactory>()
.Setup(v => v.Get(It.IsAny<int>()))
.Callback<int>(i => { throw new ModelNotFoundException(typeof(IndexerDefinition), i); });
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_if_seeds_unknown()
{
GivenReleaseSeeders(null);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[TestCase(5)]
[TestCase(6)]
public void should_return_true_if_seeds_above_or_equal_to_limit(int seeders)
{
GivenReleaseSeeders(seeders);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[TestCase(0)]
[TestCase(4)]
public void should_return_false_if_seeds_belove_limit(int seeders)
{
GivenReleaseSeeders(seeders);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
}
}
}

View File

@@ -6,7 +6,9 @@ using Moq;
using NUnit.Framework;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
@@ -35,7 +37,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
.Build();
}
private RemoteEpisode GetRemoteEpisode(List<Episode> episodes, QualityModel quality)
private RemoteEpisode GetRemoteEpisode(List<Episode> episodes, QualityModel quality, DownloadProtocol downloadProtocol = DownloadProtocol.Usenet)
{
var remoteEpisode = new RemoteEpisode();
remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo();
@@ -45,6 +47,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
remoteEpisode.Episodes.AddRange(episodes);
remoteEpisode.Release = new ReleaseInfo();
remoteEpisode.Release.DownloadProtocol = downloadProtocol;
remoteEpisode.Release.PublishDate = DateTime.UtcNow;
remoteEpisode.Series = Builder<Series>.CreateNew()
@@ -192,7 +195,6 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode, new Rejection("Failure!", RejectionType.Temporary)));
decisions.Add(new DownloadDecision(remoteEpisode));
Subject.ProcessDecisions(decisions);
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteEpisode>()), Times.Never());
@@ -209,7 +211,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
decisions.Add(new DownloadDecision(remoteEpisode, new Rejection("Failure!", RejectionType.Temporary)));
Subject.ProcessDecisions(decisions);
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.Add(It.IsAny<DownloadDecision>()), Times.Never());
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.Add(It.IsAny<DownloadDecision>(), It.IsAny<PendingReleaseReason>()), Times.Never());
}
[Test]
@@ -223,7 +225,43 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
decisions.Add(new DownloadDecision(remoteEpisode, new Rejection("Failure!", RejectionType.Temporary)));
Subject.ProcessDecisions(decisions);
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.Add(It.IsAny<DownloadDecision>()), Times.Exactly(2));
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.Add(It.IsAny<DownloadDecision>(), It.IsAny<PendingReleaseReason>()), Times.Exactly(2));
}
[Test]
public void should_add_to_failed_if_already_failed_for_that_protocol()
{
var episodes = new List<Episode> { GetEpisode(1) };
var remoteEpisode = GetRemoteEpisode(episodes, new QualityModel(Quality.HDTV720p));
var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode));
decisions.Add(new DownloadDecision(remoteEpisode));
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.IsAny<RemoteEpisode>()))
.Throws(new DownloadClientUnavailableException("Download client failed"));
Subject.ProcessDecisions(decisions);
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteEpisode>()), Times.Once());
}
[Test]
public void should_not_add_to_failed_if_failed_for_a_different_protocol()
{
var episodes = new List<Episode> { GetEpisode(1) };
var remoteEpisode = GetRemoteEpisode(episodes, new QualityModel(Quality.HDTV720p), DownloadProtocol.Usenet);
var remoteEpisode2 = GetRemoteEpisode(episodes, new QualityModel(Quality.HDTV720p), DownloadProtocol.Torrent);
var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode));
decisions.Add(new DownloadDecision(remoteEpisode2));
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.Is<RemoteEpisode>(r => r.Release.DownloadProtocol == DownloadProtocol.Usenet)))
.Throws(new DownloadClientUnavailableException("Download client failed"));
Subject.ProcessDecisions(decisions);
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.Is<RemoteEpisode>(r => r.Release.DownloadProtocol == DownloadProtocol.Usenet)), Times.Once());
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.Is<RemoteEpisode>(r => r.Release.DownloadProtocol == DownloadProtocol.Torrent)), Times.Once());
}
}
}

View File

@@ -0,0 +1,156 @@
using System;
using System.Linq;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download
{
public class DownloadClientStatusServiceFixture : CoreTest<DownloadClientStatusService>
{
private DateTime _epoch;
[SetUp]
public void SetUp()
{
_epoch = DateTime.UtcNow;
}
private DownloadClientStatus WithStatus(DownloadClientStatus status)
{
Mocker.GetMock<IDownloadClientStatusRepository>()
.Setup(v => v.FindByProviderId(1))
.Returns(status);
Mocker.GetMock<IDownloadClientStatusRepository>()
.Setup(v => v.All())
.Returns(new[] { status });
return status;
}
private void VerifyUpdate()
{
Mocker.GetMock<IDownloadClientStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<DownloadClientStatus>()), Times.Once());
}
private void VerifyNoUpdate()
{
Mocker.GetMock<IDownloadClientStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<DownloadClientStatus>()), Times.Never());
}
[Test]
public void should_not_consider_blocked_within_5_minutes_since_initial_failure()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(4),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(4),
EscalationLevel = 3
});
Subject.RecordFailure(1);
VerifyUpdate();
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().BeNull();
}
[Test]
public void should_consider_blocked_after_5_minutes_since_initial_failure()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(6),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(120),
EscalationLevel = 3
});
Subject.RecordFailure(1);
VerifyUpdate();
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
}
[Test]
public void should_not_escalate_further_till_after_5_minutes_since_initial_failure()
{
var origStatus = WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(4),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(4),
EscalationLevel = 3
});
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().BeNull();
origStatus.EscalationLevel.Should().Be(3);
}
[Test]
public void should_escalate_further_after_5_minutes_since_initial_failure()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(6),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(120),
EscalationLevel = 3
});
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
status.EscalationLevel.Should().BeGreaterThan(3);
}
[Test]
public void should_not_escalate_beyond_3_hours()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(6),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(120),
EscalationLevel = 3
});
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
status.DisabledTill.Should().HaveValue();
status.DisabledTill.Should().NotBeAfter(_epoch + TimeSpan.FromHours(3.1));
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Collections.Generic;
using FluentAssertions;
@@ -107,6 +107,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbVortexTests
var result = Subject.GetItems().Single();
VerifyQueued(result);
result.CanBeRemoved.Should().BeTrue();
result.CanMoveFiles.Should().BeTrue();
}
[Test]
@@ -118,6 +121,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbVortexTests
var result = Subject.GetItems().Single();
VerifyPaused(result);
result.CanBeRemoved.Should().BeTrue();
result.CanMoveFiles.Should().BeTrue();
}
[Test]
@@ -129,6 +135,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbVortexTests
var result = Subject.GetItems().Single();
VerifyDownloading(result);
result.CanBeRemoved.Should().BeTrue();
result.CanMoveFiles.Should().BeTrue();
}
[Test]
@@ -152,6 +161,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbVortexTests
var result = Subject.GetItems().Single();
VerifyFailed(result);
result.CanBeRemoved.Should().BeTrue();
result.CanMoveFiles.Should().BeTrue();
}
[Test]

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Collections.Generic;
using FluentAssertions;
@@ -19,6 +19,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
private NzbgetQueueItem _queued;
private NzbgetHistoryItem _failed;
private NzbgetHistoryItem _completed;
private Dictionary<string, string> _configItems;
[SetUp]
public void Setup()
@@ -80,13 +81,18 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
DownloadRate = 7000000
});
var configItems = new Dictionary<string, string>();
configItems.Add("Category1.Name", "tv");
configItems.Add("Category1.DestDir", @"/remote/mount/tv");
Mocker.GetMock<INzbgetProxy>()
.Setup(v => v.GetVersion(It.IsAny<NzbgetSettings>()))
.Returns("14.0");
_configItems = new Dictionary<string, string>();
_configItems.Add("Category1.Name", "tv");
_configItems.Add("Category1.DestDir", @"/remote/mount/tv");
Mocker.GetMock<INzbgetProxy>()
.Setup(v => v.GetConfig(It.IsAny<NzbgetSettings>()))
.Returns(configItems);
.Returns(_configItems);
}
protected void GivenFailedDownload()
@@ -167,6 +173,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
var result = Subject.GetItems().Single();
VerifyQueued(result);
result.CanBeRemoved.Should().BeTrue();
result.CanMoveFiles.Should().BeTrue();
}
[Test]
@@ -180,6 +189,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
var result = Subject.GetItems().Single();
VerifyPaused(result);
result.CanBeRemoved.Should().BeTrue();
result.CanMoveFiles.Should().BeTrue();
}
[Test]
@@ -193,6 +205,25 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
var result = Subject.GetItems().Single();
VerifyDownloading(result);
result.CanBeRemoved.Should().BeTrue();
result.CanMoveFiles.Should().BeTrue();
}
[Test]
public void post_processing_item_should_have_required_properties()
{
_queued.ActiveDownloads = 1;
GivenQueue(_queued);
GivenHistory(null);
_queued.RemainingSizeLo = 0;
var result = Subject.GetItems().Single();
result.CanBeRemoved.Should().BeTrue();
result.CanMoveFiles.Should().BeTrue();
}
[Test]
@@ -389,5 +420,18 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
error.IsValid.Should().Be(expected);
}
[TestCase("0", false)]
[TestCase("1", true)]
[TestCase(" 7", false)]
[TestCase("5000000", false)]
public void should_test_keephistory(string keephistory, bool expected)
{
_configItems["KeepHistory"] = keephistory;
var error = Subject.Test();
error.IsValid.Should().Be(expected);
}
}
}

View File

@@ -89,6 +89,12 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
});
}
protected void GivenHighPriority()
{
Subject.Definition.Settings.As<QBittorrentSettings>().OlderTvPriority = (int)QBittorrentPriority.First;
Subject.Definition.Settings.As<QBittorrentSettings>().RecentTvPriority = (int)QBittorrentPriority.First;
}
protected void GivenMaxRatio(float maxRatio, bool removeOnMaxRatio = true)
{
Mocker.GetMock<IQBittorrentProxy>()
@@ -265,6 +271,39 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
id.Should().Be(expectedHash);
}
[Test]
public void Download_should_set_top_priority()
{
GivenHighPriority();
GivenSuccessfulDownload();
var remoteEpisode = CreateRemoteEpisode();
var id = Subject.Download(remoteEpisode);
Mocker.GetMock<IQBittorrentProxy>()
.Verify(v => v.MoveTorrentToTopInQueue(It.IsAny<string>(), It.IsAny<QBittorrentSettings>()), Times.Once());
}
[Test]
public void Download_should_not_fail_if_top_priority_not_available()
{
GivenHighPriority();
GivenSuccessfulDownload();
Mocker.GetMock<IQBittorrentProxy>()
.Setup(v => v.MoveTorrentToTopInQueue(It.IsAny<string>(), It.IsAny<QBittorrentSettings>()))
.Throws(new HttpException(new HttpResponse(new HttpRequest("http://me.local/"), new HttpHeader(), new byte[0], System.Net.HttpStatusCode.Forbidden)));
var remoteEpisode = CreateRemoteEpisode();
var id = Subject.Download(remoteEpisode);
id.Should().NotBeNullOrEmpty();
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void should_return_status_with_outputdirs()
{

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Collections.Generic;
using FizzWare.NBuilder;
@@ -191,7 +191,10 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
var result = Subject.GetItems().Single();
VerifyQueued(result);
result.RemainingTime.Should().NotBe(TimeSpan.Zero);
result.CanBeRemoved.Should().BeTrue();
result.CanMoveFiles.Should().BeTrue();
}
[TestCase(SabnzbdDownloadStatus.Paused)]
@@ -205,6 +208,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
var result = Subject.GetItems().Single();
VerifyPaused(result);
result.CanBeRemoved.Should().BeTrue();
result.CanMoveFiles.Should().BeTrue();
}
[TestCase(SabnzbdDownloadStatus.Checking)]
@@ -227,7 +233,10 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
var result = Subject.GetItems().Single();
VerifyDownloading(result);
result.RemainingTime.Should().NotBe(TimeSpan.Zero);
result.CanBeRemoved.Should().BeTrue();
result.CanMoveFiles.Should().BeTrue();
}
[Test]
@@ -255,6 +264,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
var result = Subject.GetItems().Single();
VerifyFailed(result);
result.CanBeRemoved.Should().BeTrue();
result.CanMoveFiles.Should().BeTrue();
}
[Test]

View File

@@ -148,8 +148,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
[TestCase(TransmissionTorrentStatus.Check, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued)]
[TestCase(TransmissionTorrentStatus.Downloading, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Completed)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed)]
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Downloading)]
public void GetItems_should_return_queued_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus)
{
_queued.Status = apiStatus;
@@ -163,7 +163,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued)]
[TestCase(TransmissionTorrentStatus.Downloading, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Downloading)]
public void GetItems_should_return_downloading_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus)
{
_downloading.Status = apiStatus;

View File

@@ -13,6 +13,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
[TestFixture]
public class VuzeFixture : TransmissionFixtureBase<Vuze>
{
[SetUp]
public void Setup_Vuze()
{
// Vuze never sets isFinished.
_completed.IsFinished = false;
}
[Test]
public void queued_item_should_have_required_properties()
{
@@ -150,8 +157,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
[TestCase(TransmissionTorrentStatus.Check, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued)]
[TestCase(TransmissionTorrentStatus.Downloading, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Completed)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed)]
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Downloading)]
public void GetItems_should_return_queued_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus)
{
_queued.Status = apiStatus;
@@ -165,7 +172,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued)]
[TestCase(TransmissionTorrentStatus.Downloading, DownloadItemStatus.Downloading)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Downloading)]
public void GetItems_should_return_downloading_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus)
{
_downloading.Status = apiStatus;
@@ -180,7 +187,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
[TestCase(TransmissionTorrentStatus.Stopped, DownloadItemStatus.Completed, true)]
[TestCase(TransmissionTorrentStatus.CheckWait, DownloadItemStatus.Downloading, false)]
[TestCase(TransmissionTorrentStatus.Check, DownloadItemStatus.Downloading, false)]
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Completed, false)]
[TestCase(TransmissionTorrentStatus.Queued, DownloadItemStatus.Queued, false)]
[TestCase(TransmissionTorrentStatus.SeedingWait, DownloadItemStatus.Completed, false)]
[TestCase(TransmissionTorrentStatus.Seeding, DownloadItemStatus.Completed, false)]
public void GetItems_should_return_completed_item_as_downloadItemStatus(TransmissionTorrentStatus apiStatus, DownloadItemStatus expectedItemStatus, bool expectedValue)
@@ -298,7 +305,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
}
[Test]
public void should_have_correct_output_directory()
public void should_have_correct_output_directory_for_multifile_torrents()
{
WindowsOnly();
@@ -315,5 +322,25 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
items.First().OutputPath.Should().Be(@"C:\Downloads\" + _title);
}
[Test]
public void should_have_correct_output_directory_for_singlefile_torrents()
{
WindowsOnly();
var fileName = _title + ".mkv";
_downloading.Name = fileName;
_downloading.DownloadDir = @"C:/Downloads";
GivenTorrents(new List<TransmissionTorrent>
{
_downloading
});
var items = Subject.GetItems().ToList();
items.Should().HaveCount(1);
items.First().OutputPath.Should().Be(@"C:\Downloads\" + fileName);
}
}
}

View File

@@ -102,7 +102,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
[Test]
public void should_add()
{
Subject.Add(_temporarilyRejected);
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
VerifyInsert();
}
@@ -112,7 +112,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
{
GivenHeldRelease(_release.Title, _release.Indexer, _release.PublishDate);
Subject.Add(_temporarilyRejected);
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
VerifyNoInsert();
}
@@ -122,7 +122,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
{
GivenHeldRelease(_release.Title + "-RP", _release.Indexer, _release.PublishDate);
Subject.Add(_temporarilyRejected);
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
VerifyInsert();
}
@@ -132,7 +132,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
{
GivenHeldRelease(_release.Title, "AnotherIndexer", _release.PublishDate);
Subject.Add(_temporarilyRejected);
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
VerifyInsert();
}
@@ -142,7 +142,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
{
GivenHeldRelease(_release.Title, _release.Indexer, _release.PublishDate.AddHours(1));
Subject.Add(_temporarilyRejected);
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
VerifyInsert();
}

View File

@@ -27,7 +27,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
public void should_not_ignore_pending_items_from_available_indexer()
{
Mocker.GetMock<IIndexerStatusService>()
.Setup(v => v.GetBlockedIndexers())
.Setup(v => v.GetBlockedProviders())
.Returns(new List<IndexerStatus>());
GivenPendingRelease();
@@ -43,8 +43,8 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
public void should_ignore_pending_items_from_unavailable_indexer()
{
Mocker.GetMock<IIndexerStatusService>()
.Setup(v => v.GetBlockedIndexers())
.Returns(new List<IndexerStatus> { new IndexerStatus { IndexerId = 1, DisabledTill = DateTime.UtcNow.AddHours(2) } });
.Setup(v => v.GetBlockedProviders())
.Returns(new List<IndexerStatus> { new IndexerStatus { ProviderId = 1, DisabledTill = DateTime.UtcNow.AddHours(2) } });
GivenPendingRelease();

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:newznab="http://www.newznab.com/DTD/2010/feeds/attributes/" xmlns:torznab="http://torznab.com/schemas/2015/feed">
<channel>
<atom:link href="https://localhost/feed/rssx" rel="self" type="application/rss+xml" />
<title>Anime Tosho</title>
<link>https://localhost/</link>
<description>Latest releases feed</description>
<language>en-gb</language>
<ttl>30</ttl>
<lastBuildDate>Wed, 17 May 2017 20:36:06 +0000</lastBuildDate>
<newznab:response offset="0"/>
<item>
<title>[finFAGs]_Frame_Arms_Girl_07_(1280x720_TV_AAC)_[1262B6F7].mkv</title>
<pubDate>Wed, 17 May 2017 20:36:06 +0000</pubDate>
<guid isPermaLink="true">https://localhost/view/123451</guid>
<category>Anime</category>
<description><![CDATA[<strong>Total Size</strong>: 301.8 MB<br />]]></description>
<link>https://localhost/view/finfags-_frame_arms_girl_07_-1280x720_tv_aac-_-1262b6f7-mkv.123451</link>
<comments>https://localhost/view/finfags-_frame_arms_girl_07_-1280x720_tv_aac-_-1262b6f7-mkv.123451</comments>
<enclosure url="http://storage.localhost/torrents/123451.torrent" type="application/x-bittorrent" length="0" />
<source url="http://www.tokyotosho.info/details.php?id=123451">TokyoTosho</source>
<newznab:attr name="category" value="5070" />
<newznab:attr name="category" value="100001" />
<newznab:attr name="files" value="1" />
<newznab:attr name="size" value="316477946" />
<torznab:attr name="files" value="1" />
<torznab:attr name="size" value="316477946" />
<torznab:attr name="category" value="5070" />
<torznab:attr name="category" value="100001" />
<torznab:attr name="infohash" value="2d69a861bef5a9f2cdf791b7328e37b7953205e1" />
<torznab:attr name="magneturl" value="magnet:?xt=urn:btih:VU2QYN66WU7FTPXSG3TFDRXW6KTEBPBF" />
</item>
<item>
<title>[HorribleSubs] Frame Arms Girl - 07 [720p].mkv</title>
<pubDate>Mon, 15 May 2017 19:15:56 +0000</pubDate>
<guid isPermaLink="true">https://localhost/view/123452</guid>
<category>Anime</category>
<description><![CDATA[<strong>Total Size</strong>: 452.0 MB<br />]]></description>
<link>https://localhost/view/horriblesubs-frame-arms-girl-07-720p-mkv.123452</link>
<comments>https://localhost/view/horriblesubs-frame-arms-girl-07-720p-mkv.123452</comments>
<enclosure url="http://storage.localhost/torrents/123452.torrent" type="application/x-bittorrent" length="0" />
<enclosure url="http://storage.localhost/nzb/123452.nzb" type="application/x-nzb" length="0" />
<source url="http://www.tokyotosho.info/details.php?id=123452">TokyoTosho</source>
<newznab:attr name="category" value="5070" />
<newznab:attr name="category" value="100001" />
<newznab:attr name="files" value="1" />
<newznab:attr name="size" value="473987489" />
<torznab:attr name="files" value="1" />
<torznab:attr name="size" value="473987489" />
<torznab:attr name="category" value="5070" />
<torznab:attr name="category" value="100001" />
<torznab:attr name="infohash" value="bff4afebcd50c21949ed6a06323d2120c649bd82" />
<torznab:attr name="magneturl" value="magnet:?xt=urn:btih:5QK77JL7LZVIMEGKJ5VVAMMR5EEQMMSN" />
</item>
</channel>
</rss>

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
@@ -26,7 +25,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
public void should_return_error_when_download_client_throws()
{
var downloadClient = Mocker.GetMock<IDownloadClient>();
downloadClient.Setup(s => s.Definition).Returns(new IndexerDefinition{Name = "Test"});
downloadClient.Setup(s => s.Definition).Returns(new DownloadClientDefinition{Name = "Test"});
downloadClient.Setup(s => s.GetItems())
.Throws<Exception>();
@@ -36,8 +35,6 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(new IDownloadClient[] { downloadClient.Object });
Subject.Check().ShouldBeError();
ExceptionVerification.ExpectedErrors(1);
}
[Test]

View File

@@ -22,7 +22,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(_indexers);
Mocker.GetMock<IIndexerStatusService>()
.Setup(v => v.GetBlockedIndexers())
.Setup(v => v.GetBlockedProviders())
.Returns(_blockedIndexers);
}
@@ -40,7 +40,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
{
_blockedIndexers.Add(new IndexerStatus
{
IndexerId = id,
ProviderId = id,
InitialFailure = DateTime.UtcNow.AddHours(-failureHours),
MostRecentFailure = DateTime.UtcNow.AddHours(-0.1),
EscalationLevel = 5,
@@ -57,13 +57,6 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
{
Subject.Check().ShouldBeOk();
}
[Test]
public void should_not_return_error_when_indexer_failed_less_than_an_hour()
{
GivenIndexer(1, 0.1, 0.5);
Subject.Check().ShouldBeOk();
}
[Test]
public void should_return_warning_if_indexer_unavailable()

View File

@@ -13,6 +13,7 @@ using NzbDrone.Core.Qualities;
using System.Collections.Generic;
using NzbDrone.Core.Test.Qualities;
using FluentAssertions;
using NzbDrone.Core.Download;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.HistoryTests
@@ -81,7 +82,13 @@ namespace NzbDrone.Core.Test.HistoryTests
Path = @"C:\Test\Unsorted\Series.s01e01.mkv"
};
Subject.Handle(new EpisodeImportedEvent(localEpisode, episodeFile, true, "sab", "abcd"));
var downloadClientItem = new DownloadClientItem
{
DownloadClient = "sab",
DownloadId = "abcd"
};
Subject.Handle(new EpisodeImportedEvent(localEpisode, episodeFile, new List<EpisodeFile>(), true, downloadClientItem));
Mocker.GetMock<IHistoryRepository>()
.Verify(v => v.Insert(It.Is<History.History>(h => h.SourceTitle == Path.GetFileNameWithoutExtension(localEpisode.Path))));

View File

@@ -0,0 +1,60 @@
using System;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Housekeeping.Housekeepers;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
{
[TestFixture]
public class CleanupDownloadClientUnavailablePendingReleasesFixture : DbTest<CleanupDownloadClientUnavailablePendingReleases, PendingRelease>
{
[Test]
public void should_delete_old_DownloadClientUnavailable_pending_items()
{
var pendingRelease = Builder<PendingRelease>.CreateNew()
.With(h => h.Reason = PendingReleaseReason.DownloadClientUnavailable)
.With(h => h.Added = DateTime.UtcNow.AddDays(-21))
.With(h => h.ParsedEpisodeInfo = new ParsedEpisodeInfo())
.With(h => h.Release = new ReleaseInfo())
.BuildNew();
Db.Insert(pendingRelease);
Subject.Clean();
AllStoredModels.Should().BeEmpty();
}
[Test]
public void should_delete_old_Fallback_pending_items()
{
var pendingRelease = Builder<PendingRelease>.CreateNew()
.With(h => h.Reason = PendingReleaseReason.Fallback)
.With(h => h.Added = DateTime.UtcNow.AddDays(-21))
.With(h => h.ParsedEpisodeInfo = new ParsedEpisodeInfo())
.With(h => h.Release = new ReleaseInfo())
.BuildNew();
Db.Insert(pendingRelease);
Subject.Clean();
AllStoredModels.Should().BeEmpty();
}
[Test]
public void should_not_delete_old_Delay_pending_items()
{
var pendingRelease = Builder<PendingRelease>.CreateNew()
.With(h => h.Reason = PendingReleaseReason.Delay)
.With(h => h.Added = DateTime.UtcNow.AddDays(-21))
.With(h => h.ParsedEpisodeInfo = new ParsedEpisodeInfo())
.With(h => h.Release = new ReleaseInfo())
.BuildNew();
Db.Insert(pendingRelease);
Subject.Clean();
AllStoredModels.Should().HaveCount(1);
}
}
}

View File

@@ -28,7 +28,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
public void should_delete_orphaned_indexerstatus()
{
var status = Builder<IndexerStatus>.CreateNew()
.With(h => h.IndexerId = _indexer.Id)
.With(h => h.ProviderId = _indexer.Id)
.BuildNew();
Db.Insert(status);
@@ -42,13 +42,13 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
GivenIndexer();
var status = Builder<IndexerStatus>.CreateNew()
.With(h => h.IndexerId = _indexer.Id)
.With(h => h.ProviderId = _indexer.Id)
.BuildNew();
Db.Insert(status);
Subject.Clean();
AllStoredModels.Should().HaveCount(1);
AllStoredModels.Should().Contain(h => h.IndexerId == _indexer.Id);
AllStoredModels.Should().Contain(h => h.ProviderId == _indexer.Id);
}
}
}

View File

@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Test.IndexerTests.IPTorrentsTests
Subject.Definition = new IndexerDefinition()
{
Name = "IPTorrents",
Settings = new IPTorrentsSettings() { Url = "http://fake.com/" }
Settings = new IPTorrentsSettings() { BaseUrl = "http://fake.com/" }
};
}

View File

@@ -11,7 +11,7 @@ namespace NzbDrone.Core.Test.IndexerTests
public class IndexerStatusServiceFixture : CoreTest<IndexerStatusService>
{
private DateTime _epoch;
[SetUp]
public void SetUp()
{
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.Test.IndexerTests
private void WithStatus(IndexerStatus status)
{
Mocker.GetMock<IIndexerStatusRepository>()
.Setup(v => v.FindByIndexerId(1))
.Setup(v => v.FindByProviderId(1))
.Returns(status);
Mocker.GetMock<IIndexerStatusRepository>()
@@ -29,25 +29,16 @@ namespace NzbDrone.Core.Test.IndexerTests
.Returns(new[] { status });
}
private void VerifyUpdate(bool updated = true)
private void VerifyUpdate()
{
Mocker.GetMock<IIndexerStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<IndexerStatus>()), Times.Exactly(updated ? 1 : 0));
.Verify(v => v.Upsert(It.IsAny<IndexerStatus>()), Times.Once());
}
[Test]
public void should_start_backoff_on_first_failure()
private void VerifyNoUpdate()
{
WithStatus(new IndexerStatus());
Subject.RecordFailure(1);
VerifyUpdate();
var status = Subject.GetBlockedIndexers().FirstOrDefault();
status.Should().NotBeNull();
status.DisabledTill.Should().HaveValue();
status.DisabledTill.Value.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(5), 500);
Mocker.GetMock<IIndexerStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<IndexerStatus>()), Times.Never());
}
[Test]
@@ -59,7 +50,7 @@ namespace NzbDrone.Core.Test.IndexerTests
VerifyUpdate();
var status = Subject.GetBlockedIndexers().FirstOrDefault();
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().BeNull();
}
@@ -70,22 +61,7 @@ namespace NzbDrone.Core.Test.IndexerTests
Subject.RecordSuccess(1);
VerifyUpdate(false);
}
[Test]
public void should_preserve_escalation_on_intermittent_success()
{
WithStatus(new IndexerStatus { MostRecentFailure = _epoch - TimeSpan.FromSeconds(4), EscalationLevel = 3 });
Subject.RecordSuccess(1);
Subject.RecordSuccess(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedIndexers().FirstOrDefault();
status.Should().NotBeNull();
status.DisabledTill.Should().HaveValue();
status.DisabledTill.Value.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(15), 500);
VerifyNoUpdate();
}
}
}

View File

@@ -21,7 +21,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
{
_settings = new NewznabSettings()
{
Url = "http://indxer.local"
BaseUrl = "http://indxer.local"
};
_caps = ReadAllText("Files/Indexers/Newznab/newznab_caps.xml");

View File

@@ -1,12 +1,15 @@
using System;
using System.Linq;
using System.Net;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
{
@@ -24,7 +27,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
Name = "Newznab",
Settings = new NewznabSettings()
{
Url = "http://indexer.local/",
BaseUrl = "http://indexer.local/",
Categories = new int[] { 1 }
}
};
@@ -43,7 +46,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
var releases = Subject.FetchRecent();
releases.Should().HaveCount(100);
@@ -61,6 +64,35 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
releaseInfo.Size.Should().Be(1183105773);
}
[Test]
public void should_parse_recent_feed_from_newznab_animetosho()
{
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_animetosho.xml");
Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
var releases = Subject.FetchRecent();
releases.Should().HaveCount(1);
releases.First().Should().BeOfType<ReleaseInfo>();
var releaseInfo = releases.First() as ReleaseInfo;
releaseInfo.Title.Should().Be("[HorribleSubs] Frame Arms Girl - 07 [720p].mkv");
releaseInfo.DownloadProtocol.Should().Be(DownloadProtocol.Usenet);
releaseInfo.DownloadUrl.Should().Be("http://storage.localhost/nzb/123452.nzb");
releaseInfo.InfoUrl.Should().Be("https://localhost/view/horriblesubs-frame-arms-girl-07-720p-mkv.123452");
releaseInfo.CommentUrl.Should().Be("https://localhost/view/horriblesubs-frame-arms-girl-07-720p-mkv.123452");
releaseInfo.Indexer.Should().Be(Subject.Definition.Name);
releaseInfo.PublishDate.Should().Be(DateTime.Parse("Mon, 15 May 2017 19:15:56 +0000").ToUniversalTime());
releaseInfo.Size.Should().Be(473987489);
releaseInfo.TvdbId.Should().Be(0);
releaseInfo.TvRageId.Should().Be(0);
}
[Test]
public void should_use_pagesize_reported_by_caps()
{
@@ -69,5 +101,27 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
Subject.PageSize.Should().Be(25);
}
[Test]
public void should_record_indexer_failure_if_caps_throw()
{
var request = new HttpRequest("http://my.indexer.com");
var response = new HttpResponse(request, new HttpHeader(), new byte[0], (HttpStatusCode)429);
response.Headers["Retry-After"] = "300";
Mocker.GetMock<INewznabCapabilitiesProvider>()
.Setup(v => v.GetCapabilities(It.IsAny<NewznabSettings>()))
.Throws(new TooManyRequestsException(request, response));
_caps.MaxPageSize = 30;
_caps.DefaultPageSize = 25;
Subject.FetchRecent().Should().BeEmpty();
Mocker.GetMock<IIndexerStatusService>()
.Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.FromMinutes(5.0)), Times.Once());
ExceptionVerification.ExpectedWarns(1);
}
}
}

View File

@@ -20,10 +20,10 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
{
Subject.Settings = new NewznabSettings()
{
Url = "http://127.0.0.1:1234/",
Categories = new [] { 1, 2 },
AnimeCategories = new [] { 3, 4 },
ApiKey = "abcd",
BaseUrl = "http://127.0.0.1:1234/",
Categories = new [] { 1, 2 },
AnimeCategories = new [] { 3, 4 },
ApiKey = "abcd",
};
_singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria
@@ -84,7 +84,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
page.Url.FullUri.Should().Contain("&cat=3,4&");
}
[Test]
public void should_use_mode_search_for_anime()
{

View File

@@ -15,12 +15,12 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
var setting = new NewznabSettings()
{
ApiKey = "",
Url = url
BaseUrl = url
};
setting.Validate().IsValid.Should().BeFalse();
setting.Validate().Errors.Should().Contain(c => c.PropertyName == "ApiKey");
setting.Validate().Errors.Should().Contain(c => c.PropertyName == nameof(NewznabSettings.ApiKey));
}
@@ -32,13 +32,13 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
var setting = new NewznabSettings
{
ApiKey = "",
Url = url
BaseUrl = url
};
setting.Validate().IsValid.Should().BeFalse();
setting.Validate().Errors.Should().NotContain(c => c.PropertyName == "ApiKey");
setting.Validate().Errors.Should().Contain(c => c.PropertyName == "Url");
setting.Validate().Errors.Should().NotContain(c => c.PropertyName == nameof(NewznabSettings.ApiKey));
setting.Validate().Errors.Should().Contain(c => c.PropertyName == nameof(NewznabSettings.BaseUrl));
}
@@ -49,11 +49,11 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
var setting = new NewznabSettings()
{
ApiKey = "",
Url = url
BaseUrl = url
};
setting.Validate().IsValid.Should().BeTrue();
}
}
}
}

View File

@@ -1,14 +1,17 @@
using System;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Test.IndexerTests
{
public class TestIndexerSettings : IProviderConfig
public class TestIndexerSettings : IIndexerSettings
{
public NzbDroneValidationResult Validate()
{
throw new NotImplementedException();
}
public string BaseUrl { get; set; }
}
}

View File

@@ -5,9 +5,11 @@ using Moq;
using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.Indexers.TorrentRss;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
{
@@ -48,7 +50,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
releases.Should().HaveCount(50);
releases.First().Should().BeOfType<TorrentInfo>();
var torrentInfo = (TorrentInfo)releases.First();
torrentInfo.Title.Should().Be("Conan.2015.02.05.Jeff.Bridges.720p.HDTV.X264-CROOKS");
@@ -239,7 +241,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
torrentInfo.Title.Should().Be("DAYS - 05 (1280x720 HEVC2 AAC).mkv");
torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
torrentInfo.DownloadUrl.Should().Be("http://storage.animetosho.org/torrents/4b58360143d59a55cbd922397a3eaa378165f3ff/DAYS%20-%2005%20%281280x720%20HEVC2%20AAC%29.torrent");
torrentInfo.DownloadUrl.Should().Be("http://storage.animetosho.org/torrents/4b58360143d59a55cbd922397a3eaa378165f3ff/DAYS%20-%2005%20%281280x720%20HEVC2%20AAC%29.torrent");
}
[Test]
@@ -258,5 +260,18 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
torrentInfo.DownloadUrl.Should().Be("https://alpharatio.cc/torrents.php?action=download&authkey=private_auth_key&torrent_pass=private_torrent_pass&id=465831");
}
[Test]
public void should_record_indexer_failure_if_unsupported_feed()
{
GivenRecentFeedResponse("TorrentRss/invalid/TorrentDay_NoPubDate.xml");
Subject.FetchRecent().Should().BeEmpty();
Mocker.GetMock<IIndexerStatusService>()
.Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.Zero), Times.Once());
ExceptionVerification.ExpectedErrors(1);
}
}
}

View File

@@ -254,13 +254,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
}
[TestCase("BitMeTv/BitMeTv.xml")]
[TestCase("Fanzub/fanzub.xml")]
[TestCase("IPTorrents/IPTorrents.xml")]
[TestCase("Newznab/newznab_nzb_su.xml")]
[TestCase("Nyaa/Nyaa.xml")]
[TestCase("Omgwtfnzbs/Omgwtfnzbs.xml")]
[TestCase("Torznab/torznab_hdaccess_net.xml")]
[TestCase("Torznab/torznab_tpb.xml")]
[TestCase("Torznab/torznab_animetosho.xml")]
public void should_detect_recent_feed(string rssXmlFile)
{
GivenRecentFeedResponse(rssXmlFile);
@@ -287,9 +285,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
var ex = Assert.Throws<UnsupportedFeedException>(() => Subject.Detect(_indexerSettings));
ex.Message.Should().Contain("Empty feed");
ExceptionVerification.ExpectedErrors(1);
ex.Message.Should().Contain("Rss feed must have a pubDate");
}
[TestCase("Torrentleech/Torrentleech.xml")]

View File

@@ -25,7 +25,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
Name = "Torznab",
Settings = new TorznabSettings()
{
Url = "http://indexer.local/",
BaseUrl = "http://indexer.local/",
Categories = new int[] { 1 }
}
};
@@ -44,7 +44,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
var releases = Subject.FetchRecent();
releases.Should().HaveCount(5);
@@ -97,6 +97,37 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
releaseInfo.Peers.Should().Be(36724);
}
[Test]
public void should_parse_recent_feed_from_torznab_animetosho()
{
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_animetosho.xml");
Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
var releases = Subject.FetchRecent();
releases.Should().HaveCount(2);
releases.First().Should().BeOfType<TorrentInfo>();
var releaseInfo = releases.First() as TorrentInfo;
releaseInfo.Title.Should().Be("[finFAGs]_Frame_Arms_Girl_07_(1280x720_TV_AAC)_[1262B6F7].mkv");
releaseInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
releaseInfo.DownloadUrl.Should().Be("http://storage.localhost/torrents/123451.torrent");
releaseInfo.InfoUrl.Should().Be("https://localhost/view/finfags-_frame_arms_girl_07_-1280x720_tv_aac-_-1262b6f7-mkv.123451");
releaseInfo.CommentUrl.Should().Be("https://localhost/view/finfags-_frame_arms_girl_07_-1280x720_tv_aac-_-1262b6f7-mkv.123451");
releaseInfo.Indexer.Should().Be(Subject.Definition.Name);
releaseInfo.PublishDate.Should().Be(DateTime.Parse("Wed, 17 May 2017 20:36:06 +0000").ToUniversalTime());
releaseInfo.Size.Should().Be(316477946);
releaseInfo.TvdbId.Should().Be(0);
releaseInfo.TvRageId.Should().Be(0);
releaseInfo.InfoHash.Should().Be("2d69a861bef5a9f2cdf791b7328e37b7953205e1");
releaseInfo.Seeders.Should().BeNull();
releaseInfo.Peers.Should().BeNull();
}
[Test]
public void should_use_pagesize_reported_by_caps()
{

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
@@ -14,6 +14,7 @@ using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
using FluentAssertions;
using NzbDrone.Core.Download;
namespace NzbDrone.Core.Test.MediaFiles
{
@@ -77,7 +78,7 @@ namespace NzbDrone.Core.Test.MediaFiles
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
Mocker.GetMock<IMakeImportDecision>()
.Verify(c => c.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<bool>()),
.Verify(c => c.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<bool>()),
Times.Never());
VerifyNoImport();
@@ -128,7 +129,7 @@ namespace NzbDrone.Core.Test.MediaFiles
imported.Add(new ImportDecision(localEpisode));
Mocker.GetMock<IMakeImportDecision>()
.Setup(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), null, true))
.Setup(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>(), null, true))
.Returns(imported);
Mocker.GetMock<IImportApprovedEpisodes>()
@@ -154,7 +155,7 @@ namespace NzbDrone.Core.Test.MediaFiles
imported.Add(new ImportDecision(localEpisode));
Mocker.GetMock<IMakeImportDecision>()
.Setup(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), null, true))
.Setup(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>(), null, true))
.Returns(imported);
Mocker.GetMock<IImportApprovedEpisodes>()
@@ -163,11 +164,9 @@ namespace NzbDrone.Core.Test.MediaFiles
Mocker.GetMock<IDetectSample>()
.Setup(s => s.IsSample(It.IsAny<Series>(),
It.IsAny<QualityModel>(),
It.IsAny<string>(),
It.IsAny<long>(),
It.IsAny<bool>()))
.Returns(true);
.Returns(DetectSampleResult.Sample);
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
@@ -226,7 +225,7 @@ namespace NzbDrone.Core.Test.MediaFiles
imported.Add(new ImportDecision(localEpisode));
Mocker.GetMock<IMakeImportDecision>()
.Setup(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), null, true))
.Setup(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>(), null, true))
.Returns(imported);
Mocker.GetMock<IImportApprovedEpisodes>()
@@ -235,11 +234,9 @@ namespace NzbDrone.Core.Test.MediaFiles
Mocker.GetMock<IDetectSample>()
.Setup(s => s.IsSample(It.IsAny<Series>(),
It.IsAny<QualityModel>(),
It.IsAny<string>(),
It.IsAny<long>(),
It.IsAny<bool>()))
.Returns(true);
.Returns(DetectSampleResult.Sample);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetFiles(It.IsAny<string>(), SearchOption.AllDirectories))
@@ -280,7 +277,7 @@ namespace NzbDrone.Core.Test.MediaFiles
Subject.ProcessPath(fileName);
Mocker.GetMock<IMakeImportDecision>()
.Verify(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.Is<ParsedEpisodeInfo>(v => v.AbsoluteEpisodeNumbers.First() == 9), true), Times.Once());
.Verify(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>(), It.Is<ParsedEpisodeInfo>(v => v.AbsoluteEpisodeNumbers.First() == 9), true), Times.Once());
}
[Test]
@@ -304,7 +301,7 @@ namespace NzbDrone.Core.Test.MediaFiles
var result = Subject.ProcessPath(fileName);
Mocker.GetMock<IMakeImportDecision>()
.Verify(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), null, true), Times.Once());
.Verify(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>(), null, true), Times.Once());
}
[Test]
@@ -337,7 +334,7 @@ namespace NzbDrone.Core.Test.MediaFiles
imported.Add(new ImportDecision(localEpisode));
Mocker.GetMock<IMakeImportDecision>()
.Setup(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), null, true))
.Setup(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>(), null, true))
.Returns(imported);
Mocker.GetMock<IImportApprovedEpisodes>()
@@ -346,11 +343,9 @@ namespace NzbDrone.Core.Test.MediaFiles
Mocker.GetMock<IDetectSample>()
.Setup(s => s.IsSample(It.IsAny<Series>(),
It.IsAny<QualityModel>(),
It.IsAny<string>(),
It.IsAny<long>(),
It.IsAny<bool>()))
.Returns(true);
.Returns(DetectSampleResult.Sample);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetFileSize(It.IsAny<string>()))

View File

@@ -10,11 +10,12 @@ using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{
[TestFixture]
public class SampleServiceFixture : CoreTest<DetectSample>
public class DetectSampleFixture : CoreTest<DetectSample>
{
private Series _series;
private LocalEpisode _localEpisode;
@@ -42,11 +43,6 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
};
}
private void GivenFileSize(long size)
{
_localEpisode.Size = size;
}
private void GivenRuntime(int seconds)
{
Mocker.GetMock<IVideoFileInfoReader>()
@@ -58,7 +54,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
public void should_return_false_if_season_zero()
{
_localEpisode.Episodes[0].SeasonNumber = 0;
ShouldBeFalse();
ShouldBeNotSample();
}
[Test]
@@ -66,7 +62,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{
_localEpisode.Path = @"C:\Test\some.show.s01e01.flv";
ShouldBeFalse();
ShouldBeNotSample();
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
}
@@ -76,7 +72,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{
_localEpisode.Path = @"C:\Test\some.show.s01e01.strm";
ShouldBeFalse();
ShouldBeNotSample();
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
}
@@ -85,12 +81,9 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
public void should_use_runtime()
{
GivenRuntime(120);
GivenFileSize(1000.Megabytes());
Subject.IsSample(_localEpisode.Series,
_localEpisode.Quality,
_localEpisode.Path,
_localEpisode.Size,
_localEpisode.IsSpecial);
Mocker.GetMock<IVideoFileInfoReader>().Verify(v => v.GetRunTime(It.IsAny<string>()), Times.Once());
@@ -101,7 +94,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{
GivenRuntime(60);
ShouldBeTrue();
ShouldBeSample();
}
[Test]
@@ -109,7 +102,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{
GivenRuntime(600);
ShouldBeFalse();
ShouldBeNotSample();
}
[Test]
@@ -118,29 +111,39 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
_series.Runtime = 6;
GivenRuntime(299);
ShouldBeFalse();
ShouldBeNotSample();
}
[Test]
public void should_fall_back_to_file_size_if_mediainfo_dll_not_found_acceptable_size()
public void should_return_false_if_runtime_greater_than_anime_short_minimum()
{
Mocker.GetMock<IVideoFileInfoReader>()
.Setup(s => s.GetRunTime(It.IsAny<string>()))
.Throws<DllNotFoundException>();
_series.Runtime = 2;
GivenRuntime(60);
GivenFileSize(1000.Megabytes());
ShouldBeFalse();
ShouldBeNotSample();
}
[Test]
public void should_fall_back_to_file_size_if_mediainfo_dll_not_found_undersize()
public void should_return_true_if_runtime_less_than_anime_short_minimum()
{
_series.Runtime = 2;
GivenRuntime(10);
ShouldBeSample();
}
[Test]
public void should_return_indeterminate_if_mediainfo_result_is_null()
{
Mocker.GetMock<IVideoFileInfoReader>()
.Setup(s => s.GetRunTime(It.IsAny<string>()))
.Throws<DllNotFoundException>();
.Returns((TimeSpan?)null);
GivenFileSize(1.Megabytes());
ShouldBeTrue();
Subject.IsSample(_localEpisode.Series,
_localEpisode.Path,
_localEpisode.IsSpecial).Should().Be(DetectSampleResult.Indeterminate);
ExceptionVerification.ExpectedErrors(1);
}
[Test]
@@ -149,7 +152,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
GivenRuntime(600);
_series.SeriesType = SeriesTypes.Daily;
_localEpisode.Episodes[0].SeasonNumber = 0;
ShouldBeFalse();
ShouldBeNotSample();
}
[Test]
@@ -158,25 +161,21 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
_series.SeriesType = SeriesTypes.Anime;
_localEpisode.Episodes[0].SeasonNumber = 0;
ShouldBeFalse();
ShouldBeNotSample();
}
private void ShouldBeTrue()
private void ShouldBeSample()
{
Subject.IsSample(_localEpisode.Series,
_localEpisode.Quality,
_localEpisode.Path,
_localEpisode.Size,
_localEpisode.IsSpecial).Should().BeTrue();
}
private void ShouldBeFalse()
{
Subject.IsSample(_localEpisode.Series,
_localEpisode.Quality,
_localEpisode.Path,
_localEpisode.Size,
_localEpisode.IsSpecial).Should().BeFalse();
_localEpisode.IsSpecial).Should().Be(DetectSampleResult.Sample);
}
private void ShouldBeNotSample()
{
Subject.IsSample(_localEpisode.Series,
_localEpisode.Path,
_localEpisode.IsSpecial).Should().Be(DetectSampleResult.NotSample);
}
}
}

View File

@@ -14,6 +14,7 @@ using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
using FizzWare.NBuilder;
using NzbDrone.Core.Download;
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{
@@ -44,13 +45,13 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
_fail2 = new Mock<IImportDecisionEngineSpecification>();
_fail3 = new Mock<IImportDecisionEngineSpecification>();
_pass1.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(Decision.Accept());
_pass2.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(Decision.Accept());
_pass3.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(Decision.Accept());
_pass1.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>(), It.IsAny<DownloadClientItem>())).Returns(Decision.Accept());
_pass2.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>(), It.IsAny<DownloadClientItem>())).Returns(Decision.Accept());
_pass3.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>(), It.IsAny<DownloadClientItem>())).Returns(Decision.Accept());
_fail1.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(Decision.Reject("_fail1"));
_fail2.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(Decision.Reject("_fail2"));
_fail3.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>())).Returns(Decision.Reject("_fail3"));
_fail1.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>(), It.IsAny<DownloadClientItem>())).Returns(Decision.Reject("_fail1"));
_fail2.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>(), It.IsAny<DownloadClientItem>())).Returns(Decision.Reject("_fail2"));
_fail3.Setup(c => c.IsSatisfiedBy(It.IsAny<LocalEpisode>(), It.IsAny<DownloadClientItem>())).Returns(Decision.Reject("_fail3"));
_series = Builder<Series>.CreateNew()
.With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() })
@@ -90,16 +91,17 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
[Test]
public void should_call_all_specifications()
{
var downloadClientItem = Builder<DownloadClientItem>.CreateNew().Build();
GivenSpecifications(_pass1, _pass2, _pass3, _fail1, _fail2, _fail3);
Subject.GetImportDecisions(_videoFiles, new Series(), null, false);
Subject.GetImportDecisions(_videoFiles, new Series(), downloadClientItem, null, false);
_fail1.Verify(c => c.IsSatisfiedBy(_localEpisode), Times.Once());
_fail2.Verify(c => c.IsSatisfiedBy(_localEpisode), Times.Once());
_fail3.Verify(c => c.IsSatisfiedBy(_localEpisode), Times.Once());
_pass1.Verify(c => c.IsSatisfiedBy(_localEpisode), Times.Once());
_pass2.Verify(c => c.IsSatisfiedBy(_localEpisode), Times.Once());
_pass3.Verify(c => c.IsSatisfiedBy(_localEpisode), Times.Once());
_fail1.Verify(c => c.IsSatisfiedBy(_localEpisode, downloadClientItem), Times.Once());
_fail2.Verify(c => c.IsSatisfiedBy(_localEpisode, downloadClientItem), Times.Once());
_fail3.Verify(c => c.IsSatisfiedBy(_localEpisode, downloadClientItem), Times.Once());
_pass1.Verify(c => c.IsSatisfiedBy(_localEpisode, downloadClientItem), Times.Once());
_pass2.Verify(c => c.IsSatisfiedBy(_localEpisode, downloadClientItem), Times.Once());
_pass3.Verify(c => c.IsSatisfiedBy(_localEpisode, downloadClientItem), Times.Once());
}
[Test]
@@ -184,7 +186,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
GivenSpecifications(_pass1, _pass2, _pass3);
var expectedQuality = QualityParser.ParseQuality(_videoFiles.Single());
var result = Subject.GetImportDecisions(_videoFiles, _series, new ParsedEpisodeInfo{Quality = new QualityModel(Quality.SDTV)}, true);
var result = Subject.GetImportDecisions(_videoFiles, _series, null, new ParsedEpisodeInfo{Quality = new QualityModel(Quality.SDTV)}, true);
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
}
@@ -201,7 +203,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
var expectedQuality = new QualityModel(Quality.SDTV);
var result = Subject.GetImportDecisions(_videoFiles, _series, new ParsedEpisodeInfo { Quality = expectedQuality }, true);
var result = Subject.GetImportDecisions(_videoFiles, _series, null, new ParsedEpisodeInfo { Quality = expectedQuality }, true);
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
}
@@ -217,7 +219,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
var expectedQuality = new QualityModel(Quality.Bluray720p);
var result = Subject.GetImportDecisions(_videoFiles, _series, new ParsedEpisodeInfo { Quality = expectedQuality }, true);
var result = Subject.GetImportDecisions(_videoFiles, _series, null, new ParsedEpisodeInfo { Quality = expectedQuality }, true);
result.Single().LocalEpisode.Quality.Should().Be(expectedQuality);
}
@@ -264,7 +266,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01");
Subject.GetImportDecisions(_videoFiles, _series, folderInfo, true);
Subject.GetImportDecisions(_videoFiles, _series, null, folderInfo, true);
Mocker.GetMock<IParsingService>()
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), null, true), Times.Exactly(3));
@@ -287,7 +289,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
Subject.GetImportDecisions(_videoFiles, _series, folderInfo, true);
Subject.GetImportDecisions(_videoFiles, _series, null, folderInfo, true);
Mocker.GetMock<IParsingService>()
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), null, true), Times.Exactly(2));
@@ -309,7 +311,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
Subject.GetImportDecisions(_videoFiles, _series, folderInfo, true);
Subject.GetImportDecisions(_videoFiles, _series, null, folderInfo, true);
Mocker.GetMock<IParsingService>()
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), true), Times.Exactly(1));
@@ -331,12 +333,20 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
GivenVideoFiles(videoFiles.ToList());
Mocker.GetMock<IDetectSample>()
.Setup(s => s.IsSample(_series, It.IsAny<QualityModel>(), It.Is<string>(c => c.Contains("sample")), It.IsAny<long>(), It.IsAny<bool>()))
.Returns(true);
.Setup(s => s.IsSample(_series, It.IsAny<string>(), It.IsAny<bool>()))
.Returns((Series s, string path, bool special) =>
{
if (path.Contains("sample"))
{
return DetectSampleResult.Sample;
}
return DetectSampleResult.NotSample;
});
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
Subject.GetImportDecisions(_videoFiles, _series, folderInfo, true);
Subject.GetImportDecisions(_videoFiles, _series, null, folderInfo, true);
Mocker.GetMock<IParsingService>()
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), true), Times.Exactly(2));
@@ -358,7 +368,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01.720p.HDTV-LOL");
Subject.GetImportDecisions(_videoFiles, _series, folderInfo, true);
Subject.GetImportDecisions(_videoFiles, _series, null, folderInfo, true);
Mocker.GetMock<IParsingService>()
.Verify(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), null, true), Times.Exactly(1));
@@ -380,7 +390,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
var folderQuality = new QualityModel(Quality.Unknown);
var result = Subject.GetImportDecisions(_videoFiles, _series, new ParsedEpisodeInfo { Quality = folderQuality}, true);
var result = Subject.GetImportDecisions(_videoFiles, _series, null, new ParsedEpisodeInfo { Quality = folderQuality}, true);
result.Single().LocalEpisode.Quality.Should().Be(_quality);
}

View File

@@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
GivenFileSize(100.Megabytes());
GivenFreeSpace(80.Megabytes());
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
ExceptionVerification.ExpectedWarns(1);
}
@@ -73,7 +73,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
GivenFileSize(100.Megabytes());
GivenFreeSpace(150.Megabytes());
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
ExceptionVerification.ExpectedWarns(1);
}
@@ -83,7 +83,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
GivenFileSize(100.Megabytes());
GivenFreeSpace(1.Gigabytes());
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
@@ -92,7 +92,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
GivenFileSize(100.Megabytes());
GivenFreeSpace(1.Gigabytes());
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
Mocker.GetMock<IDiskProvider>()
.Verify(v => v.GetAvailableSpace(_rootFolder), Times.Once());
@@ -104,7 +104,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
GivenFileSize(100.Megabytes());
GivenFreeSpace(null);
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
@@ -116,7 +116,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.Setup(s => s.GetAvailableSpace(It.IsAny<string>()))
.Throws(new TestException());
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
ExceptionVerification.ExpectedErrors(1);
}
@@ -125,7 +125,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
{
_localEpisode.ExistingFile = true;
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
Mocker.GetMock<IDiskProvider>()
.Verify(s => s.GetAvailableSpace(It.IsAny<string>()), Times.Never());
@@ -140,7 +140,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.Setup(s => s.GetAvailableSpace(It.IsAny<string>()))
.Returns(freeSpace);
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
@@ -150,7 +150,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.Setup(s => s.SkipFreeSpaceCheckWhenImporting)
.Returns(true);
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
}
}

View File

@@ -34,13 +34,13 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
{
_localEpisode.ParsedEpisodeInfo.FullSeason = true;
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}
[Test]
public void should_return_true_when_file_does_not_contain_the_full_season()
{
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
}
}

View File

@@ -0,0 +1,123 @@
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.History;
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
{
[TestFixture]
public class GrabbedReleaseQualityFixture : CoreTest<GrabbedReleaseQualitySpecification>
{
private LocalEpisode _localEpisode;
private DownloadClientItem _downloadClientItem;
[SetUp]
public void Setup()
{
_localEpisode = Builder<LocalEpisode>.CreateNew()
.With(l => l.Quality = new QualityModel(Quality.Bluray720p))
.Build();
_downloadClientItem = Builder<DownloadClientItem>.CreateNew()
.Build();
}
private void GivenHistory(List<History.History> history)
{
Mocker.GetMock<IHistoryService>()
.Setup(s => s.FindByDownloadId(It.IsAny<string>()))
.Returns(history);
}
[Test]
public void should_be_accepted_when_downloadClientItem_is_null()
{
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_be_accepted_if_no_history_for_downloadId()
{
GivenHistory(new List<History.History>());
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue();
}
[Test]
public void should_be_accepted_if_no_grabbed_history_for_downloadId()
{
var history = Builder<History.History>.CreateListOfSize(1)
.All()
.With(h => h.EventType = HistoryEventType.Unknown)
.BuildList();
GivenHistory(history);
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue();
}
[Test]
public void should_be_accepted_if_grabbed_history_is_for_a_season_pack()
{
var history = Builder<History.History>.CreateListOfSize(1)
.All()
.With(h => h.EventType = HistoryEventType.Grabbed)
.With(h => h.Quality = _localEpisode.Quality)
.With(h => h.SourceTitle = "Series.Title.S01.720p.HDTV.x264-RlsGroup")
.BuildList();
GivenHistory(history);
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue();
}
[Test]
public void should_be_accepted_if_grabbed_history_quality_is_unknown()
{
var history = Builder<History.History>.CreateListOfSize(1)
.All()
.With(h => h.EventType = HistoryEventType.Grabbed)
.With(h => h.Quality = new QualityModel(Quality.Unknown))
.BuildList();
GivenHistory(history);
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue();
}
[Test]
public void should_be_accepted_if_grabbed_history_quality_matches()
{
var history = Builder<History.History>.CreateListOfSize(1)
.All()
.With(h => h.EventType = HistoryEventType.Grabbed)
.With(h => h.Quality = _localEpisode.Quality)
.BuildList();
GivenHistory(history);
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue();
}
[Test]
public void should_be_rejected_if_grabbed_history_quality_does_not_match()
{
var history = Builder<History.History>.CreateListOfSize(1)
.All()
.With(h => h.EventType = HistoryEventType.Grabbed)
.With(h => h.Quality = new QualityModel(Quality.HDTV720p))
.BuildList();
GivenHistory(history);
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeFalse();
}
}
}

View File

@@ -31,7 +31,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
{
_localEpisode.ExistingFile = true;
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
@@ -39,7 +39,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
{
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title\S01E01.mkv".AsOsAgnostic();
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
@@ -47,7 +47,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
{
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01\S01E01.mkv".AsOsAgnostic();
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
@@ -55,7 +55,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
{
_localEpisode.ParsedEpisodeInfo.EpisodeNumbers = new[] { 1 };
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01.720p.HDTV-Sonarr\S01E01.mkv".AsOsAgnostic();
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
@@ -63,14 +63,14 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
{
_localEpisode.ParsedEpisodeInfo.EpisodeNumbers = new[] { 1 };
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01E02.720p.HDTV-Sonarr\S01E01.mkv".AsOsAgnostic();
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_be_rejected_if_file_and_folder_do_not_have_same_episode()
{
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01.720p.HDTV-Sonarr\S01E05.mkv".AsOsAgnostic();
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}
[Test]
@@ -78,7 +78,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
{
_localEpisode.ParsedEpisodeInfo.EpisodeNumbers = new[] { 5, 6 };
_localEpisode.Path = @"C:\Test\Unsorted\Series.Title.S01E01E02.720p.HDTV-Sonarr\S01E05E06.mkv".AsOsAgnostic();
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}
}
}

View File

@@ -42,7 +42,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
public void should_return_true_for_existing_file()
{
_localEpisode.ExistingFile = true;
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
}
}

View File

@@ -48,7 +48,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
[Test]
public void should_return_true_if_not_in_working_folder()
{
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
@@ -59,7 +59,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
GivenInWorkingFolder();
GivenLastWriteTimeUtc(DateTime.UtcNow.AddHours(-1));
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
@@ -68,7 +68,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
GivenInWorkingFolder();
GivenLastWriteTimeUtc(DateTime.UtcNow);
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}
[Test]
@@ -79,7 +79,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
GivenInWorkingFolder();
GivenLastWriteTimeUtc(DateTime.UtcNow.AddDays(-5));
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}
}
}

View File

@@ -0,0 +1,96 @@
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Marr.Data;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
{
[TestFixture]
public class SameFileSpecificationFixture : CoreTest<SameFileSpecification>
{
private LocalEpisode _localEpisode;
[SetUp]
public void Setup()
{
_localEpisode = Builder<LocalEpisode>.CreateNew()
.With(l => l.Size = 150.Megabytes())
.Build();
}
[Test]
public void should_be_accepted_if_no_existing_file()
{
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
.TheFirst(1)
.With(e => e.EpisodeFileId = 0)
.BuildList();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_be_accepted_if_multiple_existing_files()
{
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(2)
.TheFirst(1)
.With(e => e.EpisodeFileId = 1)
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile
{
Size = _localEpisode.Size
}))
.TheNext(1)
.With(e => e.EpisodeFileId = 2)
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile
{
Size = _localEpisode.Size
}))
.Build()
.ToList();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_be_accepted_if_file_size_is_different()
{
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
.TheFirst(1)
.With(e => e.EpisodeFileId = 1)
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile
{
Size = _localEpisode.Size + 100.Megabytes()
}))
.Build()
.ToList();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_be_reject_if_file_size_is_the_same()
{
_localEpisode.Episodes = Builder<Episode>.CreateListOfSize(1)
.TheFirst(1)
.With(e => e.EpisodeFileId = 1)
.With(e => e.EpisodeFile = new LazyLoaded<EpisodeFile>(
new EpisodeFile
{
Size = _localEpisode.Size
}))
.Build()
.ToList();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}
}
}

View File

@@ -45,7 +45,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.Build()
.ToList();
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
@@ -58,7 +58,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.Build()
.ToList();
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
@@ -75,7 +75,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.Build()
.ToList();
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
@@ -92,7 +92,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.Build()
.ToList();
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeTrue();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
}
[Test]
@@ -109,7 +109,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.Build()
.ToList();
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}
[Test]
@@ -126,7 +126,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.Build()
.ToList();
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}
[Test]
@@ -150,7 +150,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
.Build()
.ToList();
Subject.IsSatisfiedBy(_localEpisode).Accepted.Should().BeFalse();
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
}
}
}

View File

@@ -0,0 +1,140 @@
using System.IO;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.MediaFileDeletionService
{
[TestFixture]
public class DeleteEpisodeFileFixture : CoreTest<Core.MediaFiles.MediaFileDeletionService>
{
private static readonly string RootFolder = @"C:\Test\TV";
private Series _series;
private EpisodeFile _episodeFile;
[SetUp]
public void Setup()
{
_series = Builder<Series>.CreateNew()
.With(s => s.Path = Path.Combine(RootFolder, "Series Title"))
.Build();
_episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.RelativePath = "Series Title - S01E01")
.With(f => f.Path = Path.Combine(_series.Path, "Series Title - S01E01"))
.Build();
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetParentFolder(_series.Path))
.Returns(RootFolder);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetParentFolder(_episodeFile.Path))
.Returns(_series.Path);
}
private void GivenRootFolderExists()
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FolderExists(RootFolder))
.Returns(true);
}
private void GivenRootFolderHasFolders()
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetDirectories(RootFolder))
.Returns(new[] { _series.Path });
}
private void GivenSeriesFolderExists()
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FolderExists(_series.Path))
.Returns(true);
}
[Test]
public void should_throw_if_root_folder_does_not_exist()
{
Assert.Throws<NzbDroneClientException>(() => Subject.DeleteEpisodeFile(_series, _episodeFile));
}
[Test]
public void should_should_throw_if_root_folder_is_empty()
{
GivenRootFolderExists();
Assert.Throws<NzbDroneClientException>(() => Subject.DeleteEpisodeFile(_series, _episodeFile));
}
[Test]
public void should_delete_from_db_if_series_folder_does_not_exist()
{
GivenRootFolderExists();
GivenRootFolderHasFolders();
Subject.DeleteEpisodeFile(_series, _episodeFile);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Delete(_episodeFile, DeleteMediaFileReason.Manual), Times.Once());
Mocker.GetMock<IRecycleBinProvider>().Verify(v => v.DeleteFile(_episodeFile.Path, It.IsAny<string>()), Times.Never());
}
[Test]
public void should_delete_from_db_if_episode_file_does_not_exist()
{
GivenRootFolderExists();
GivenRootFolderHasFolders();
GivenSeriesFolderExists();
Subject.DeleteEpisodeFile(_series, _episodeFile);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Delete(_episodeFile, DeleteMediaFileReason.Manual), Times.Once());
Mocker.GetMock<IRecycleBinProvider>().Verify(v => v.DeleteFile(_episodeFile.Path, It.IsAny<string>()), Times.Never());
}
[Test]
public void should_delete_from_disk_and_db_if_episode_file_exists()
{
GivenRootFolderExists();
GivenRootFolderHasFolders();
GivenSeriesFolderExists();
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FileExists(_episodeFile.Path))
.Returns(true);
Subject.DeleteEpisodeFile(_series, _episodeFile);
Mocker.GetMock<IRecycleBinProvider>().Verify(v => v.DeleteFile(_episodeFile.Path, "Series Title"), Times.Once());
Mocker.GetMock<IMediaFileService>().Verify(v => v.Delete(_episodeFile, DeleteMediaFileReason.Manual), Times.Once());
}
[Test]
public void should_handle_error_deleting_episode_file()
{
GivenRootFolderExists();
GivenRootFolderHasFolders();
GivenSeriesFolderExists();
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FileExists(_episodeFile.Path))
.Returns(true);
Mocker.GetMock<IRecycleBinProvider>()
.Setup(s => s.DeleteFile(_episodeFile.Path, "Series Title"))
.Throws(new IOException());
Assert.Throws<NzbDroneClientException>(() => Subject.DeleteEpisodeFile(_series, _episodeFile));
ExceptionVerification.ExpectedErrors(1);
Mocker.GetMock<IRecycleBinProvider>().Verify(v => v.DeleteFile(_episodeFile.Path, "Series Title"), Times.Once());
Mocker.GetMock<IMediaFileService>().Verify(v => v.Delete(_episodeFile, DeleteMediaFileReason.Manual), Times.Never());
}
}
}

View File

@@ -1,11 +1,12 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
{
[TestFixture]
public class FormattedAudioChannelsFixture
public class FormatAudioChannelsFixture : TestBase
{
[Test]
public void should_subtract_one_from_AudioChannels_as_total_channels_if_LFE_in_AudioChannelPositionsText()
@@ -17,7 +18,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
AudioChannelPositionsText = "Front: L C R, Side: L R, LFE"
};
mediaInfoModel.FormattedAudioChannels.Should().Be(5.1m);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
}
[Test]
@@ -30,7 +31,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
AudioChannelPositionsText = "Front: L R"
};
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
}
[Test]
@@ -44,7 +45,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
SchemaRevision = 2
};
mediaInfoModel.FormattedAudioChannels.Should().Be(0);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(0);
}
[Test]
@@ -58,7 +59,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
SchemaRevision = 3
};
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
}
[Test]
@@ -72,7 +73,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
SchemaRevision = 3
};
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
}
[Test]
@@ -86,7 +87,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
SchemaRevision = 3
};
mediaInfoModel.FormattedAudioChannels.Should().Be(5.1m);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
}
[Test]
@@ -100,7 +101,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
SchemaRevision = 3
};
mediaInfoModel.FormattedAudioChannels.Should().Be(7.1m);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(7.1m);
}
[Test]
@@ -114,7 +115,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
SchemaRevision = 3
};
mediaInfoModel.FormattedAudioChannels.Should().Be(7.1m);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(7.1m);
}
}
}

View File

@@ -0,0 +1,49 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
{
[TestFixture]
public class FormatAudioCodecFixture : TestBase
{
[TestCase("AC-3", "AC3")]
[TestCase("E-AC-3", "EAC3")]
[TestCase("MPEG Audio", "MPEG Audio")]
[TestCase("DTS", "DTS")]
public void should_format_audio_format(string audioFormat, string expectedFormat)
{
var mediaInfoModel = new MediaInfoModel
{
AudioFormat = audioFormat
};
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel).Should().Be(expectedFormat);
}
[Test]
public void should_return_MP3_for_MPEG_Audio_with_Layer_3_for_the_profile()
{
var mediaInfoModel = new MediaInfoModel
{
AudioFormat = "MPEG Audio",
AudioProfile = "Layer 3"
};
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel).Should().Be("MP3");
}
[Test]
public void should_return_AudioFormat_by_default()
{
var mediaInfoModel = new MediaInfoModel
{
AudioFormat = "Other Audio Format"
};
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel).Should().Be(mediaInfoModel.AudioFormat);
ExceptionVerification.ExpectedErrors(1);
}
}
}

View File

@@ -0,0 +1,40 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
{
[TestFixture]
public class FormatVideoCodecFixture : TestBase
{
[TestCase("AVC", null, "x264")]
[TestCase("AVC", "source.title.x264.720p-Sonarr", "x264")]
[TestCase("AVC", "source.title.h264.720p-Sonarr", "h264")]
[TestCase("V_MPEGH/ISO/HEVC", null, "x265")]
[TestCase("V_MPEGH/ISO/HEVC", "source.title.x265.720p-Sonarr", "x265")]
[TestCase("V_MPEGH/ISO/HEVC", "source.title.h265.720p-Sonarr", "h265")]
[TestCase("MPEG-2 Video", null, "MPEG2")]
public void should_format_video_codec_with_source_title(string videoCodec, string sceneName, string expectedFormat)
{
var mediaInfoModel = new MediaInfoModel
{
VideoCodec = videoCodec
};
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
}
[Test]
public void should_return_VideoCodec_by_default()
{
var mediaInfoModel = new MediaInfoModel
{
VideoCodec = "VideoCodec"
};
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, null).Should().Be(mediaInfoModel.VideoCodec);
ExceptionVerification.ExpectedErrors(1);
}
}
}

View File

@@ -1,4 +1,4 @@
using System.IO;
using System.IO;
using FluentAssertions;
using Moq;
using NUnit.Framework;
@@ -30,11 +30,9 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
{
var path = Path.Combine(TestContext.CurrentContext.TestDirectory, "Files", "Media", "H264_sample.mp4");
Subject.GetRunTime(path).Seconds.Should().Be(10);
Subject.GetRunTime(path).Value.Seconds.Should().Be(10);
}
[Test]
public void get_info()
{
@@ -86,7 +84,6 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
info.VideoCodec.Should().Be("AVC");
info.VideoFps.Should().Be(24);
info.Width.Should().Be(480);
}
[Test]

View File

@@ -1,121 +1,211 @@
//using System;
//using System.Collections.Generic;
//using Moq;
//using NUnit.Framework;
//using NzbDrone.Common;
//using NzbDrone.Core.Messaging.Commands;
//using NzbDrone.Core.Messaging.Commands.Tracking;
//using NzbDrone.Core.Messaging.Events;
//using NzbDrone.Test.Common;
//
//namespace NzbDrone.Core.Test.Messaging.Commands
//{
// [TestFixture]
// public class CommandExecutorFixture : TestBase<CommandExecutor>
// {
// private Mock<IExecute<CommandA>> _executorA;
// private Mock<IExecute<CommandB>> _executorB;
//
// [SetUp]
// public void Setup()
// {
// _executorA = new Mock<IExecute<CommandA>>();
// _executorB = new Mock<IExecute<CommandB>>();
//
// Mocker.GetMock<IServiceFactory>()
// .Setup(c => c.Build(typeof(IExecute<CommandA>)))
// .Returns(_executorA.Object);
//
// Mocker.GetMock<IServiceFactory>()
// .Setup(c => c.Build(typeof(IExecute<CommandB>)))
// .Returns(_executorB.Object);
//
//
// Mocker.GetMock<ITrackCommands>()
// .Setup(c => c.FindExisting(It.IsAny<Command>()))
// .Returns<Command>(null);
// }
//
// [Test]
// public void should_publish_command_to_executor()
// {
// var commandA = new CommandA();
//
// Subject.Push(commandA);
//
// _executorA.Verify(c => c.Execute(commandA), Times.Once());
// }
//
// [Test]
// public void should_publish_command_by_with_optional_arg_using_name()
// {
// Mocker.GetMock<IServiceFactory>().Setup(c => c.GetImplementations(typeof(Command)))
// .Returns(new List<Type> { typeof(CommandA), typeof(CommandB) });
//
// Subject.Push(typeof(CommandA).FullName);
// _executorA.Verify(c => c.Execute(It.IsAny<CommandA>()), Times.Once());
// }
//
//
// [Test]
// public void should_not_publish_to_incompatible_executor()
// {
// var commandA = new CommandA();
//
// Subject.Push(commandA);
//
// _executorA.Verify(c => c.Execute(commandA), Times.Once());
// _executorB.Verify(c => c.Execute(It.IsAny<CommandB>()), Times.Never());
// }
//
// [Test]
// public void broken_executor_should_throw_the_exception()
// {
// var commandA = new CommandA();
//
// _executorA.Setup(c => c.Execute(It.IsAny<CommandA>()))
// .Throws(new NotImplementedException());
//
// Assert.Throws<NotImplementedException>(() => Subject.Push(commandA));
// }
//
//
// [Test]
// public void broken_executor_should_publish_executed_event()
// {
// var commandA = new CommandA();
//
// _executorA.Setup(c => c.Execute(It.IsAny<CommandA>()))
// .Throws(new NotImplementedException());
//
// Assert.Throws<NotImplementedException>(() => Subject.Push(commandA));
//
// VerifyEventPublished<CommandExecutedEvent>();
// }
//
// [Test]
// public void should_publish_executed_event_on_success()
// {
// var commandA = new CommandA();
// Subject.Push(commandA);
//
// VerifyEventPublished<CommandExecutedEvent>();
// }
// }
//
// public class CommandA : Command
// {
// public CommandA(int id = 0)
// {
// }
// }
//
// public class CommandB : Command
// {
//
// public CommandB()
// {
// }
// }
//
//}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.Messaging.Commands
{
[TestFixture]
public class CommandExecutorFixture : TestBase<CommandExecutor>
{
private BlockingCollection<CommandModel> _commandQueue;
private Mock<IExecute<CommandA>> _executorA;
private Mock<IExecute<CommandB>> _executorB;
private bool _commandExecuted = false;
[SetUp]
public void Setup()
{
_executorA = new Mock<IExecute<CommandA>>();
_executorB = new Mock<IExecute<CommandB>>();
Mocker.GetMock<IServiceFactory>()
.Setup(c => c.Build(typeof(IExecute<CommandA>)))
.Returns(_executorA.Object);
Mocker.GetMock<IServiceFactory>()
.Setup(c => c.Build(typeof(IExecute<CommandB>)))
.Returns(_executorB.Object);
}
private void GivenCommandQueue()
{
_commandQueue = new BlockingCollection<CommandModel>(new CommandQueue());
Mocker.GetMock<IManageCommandQueue>()
.Setup(s => s.Queue(It.IsAny<CancellationToken>()))
.Returns(_commandQueue.GetConsumingEnumerable);
}
private void WaitForExecution(CommandModel commandModel)
{
Mocker.GetMock<IManageCommandQueue>()
.Setup(s => s.Complete(It.Is<CommandModel>(c => c == commandModel), It.IsAny<string>()))
.Callback(() => _commandExecuted = true);
Mocker.GetMock<IManageCommandQueue>()
.Setup(s => s.Fail(It.Is<CommandModel>(c => c == commandModel), It.IsAny<string>(), It.IsAny<Exception>()))
.Callback(() => _commandExecuted = true);
while (!_commandExecuted)
{
Thread.Sleep(100);
}
var t1 = 1;
}
[Test]
public void should_start_executor_threads()
{
Subject.Handle(new ApplicationStartedEvent());
Mocker.GetMock<IManageCommandQueue>()
.Verify(v => v.Queue(It.IsAny<CancellationToken>()), Times.AtLeastOnce());
}
[Test]
public void should_execute_on_executor()
{
GivenCommandQueue();
var commandA = new CommandA();
var commandModel = new CommandModel
{
Body = commandA
};
Subject.Handle(new ApplicationStartedEvent());
_commandQueue.Add(commandModel);
WaitForExecution(commandModel);
_executorA.Verify(c => c.Execute(commandA), Times.Once());
}
[Test]
public void should_not_execute_on_incompatible_executor()
{
GivenCommandQueue();
var commandA = new CommandA();
var commandModel = new CommandModel
{
Body = commandA
};
Subject.Handle(new ApplicationStartedEvent());
_commandQueue.Add(commandModel);
WaitForExecution(commandModel);
_executorA.Verify(c => c.Execute(commandA), Times.Once());
_executorB.Verify(c => c.Execute(It.IsAny<CommandB>()), Times.Never());
}
[Test]
public void broken_executor_should_publish_executed_event()
{
GivenCommandQueue();
var commandA = new CommandA();
var commandModel = new CommandModel
{
Body = commandA
};
_executorA.Setup(s => s.Execute(It.IsAny<CommandA>()))
.Throws(new NotImplementedException());
Subject.Handle(new ApplicationStartedEvent());
_commandQueue.Add(commandModel);
WaitForExecution(commandModel);
VerifyEventPublished<CommandExecutedEvent>();
ExceptionVerification.ExpectedErrors(1);
}
[Test]
public void should_publish_executed_event_on_success()
{
GivenCommandQueue();
var commandA = new CommandA();
var commandModel = new CommandModel
{
Body = commandA
};
Subject.Handle(new ApplicationStartedEvent());
_commandQueue.Add(commandModel);
WaitForExecution(commandModel);
VerifyEventPublished<CommandExecutedEvent>();
}
[Test]
public void should_use_completion_message()
{
GivenCommandQueue();
var commandA = new CommandA();
var commandModel = new CommandModel
{
Body = commandA
};
Subject.Handle(new ApplicationStartedEvent());
_commandQueue.Add(commandModel);
WaitForExecution(commandModel);
Mocker.GetMock<IManageCommandQueue>()
.Setup(s => s.Complete(It.Is<CommandModel>(c => c == commandModel), commandA.CompletionMessage))
.Callback(() => _commandExecuted = true);
}
[Test]
public void should_use_last_progress_message_if_completion_message_is_null()
{
GivenCommandQueue();
var commandA = new CommandA();
var commandModel = new CommandModel
{
Body = commandA,
Message = "Do work"
};
Subject.Handle(new ApplicationStartedEvent());
_commandQueue.Add(commandModel);
WaitForExecution(commandModel);
Mocker.GetMock<IManageCommandQueue>()
.Setup(s => s.Complete(It.Is<CommandModel>(c => c == commandModel), commandModel.Message))
.Callback(() => _commandExecuted = true);
}
}
public class CommandA : Command
{
public CommandA(int id = 0)
{
}
}
public class CommandB : Command
{
public CommandB()
{
}
public override string CompletionMessage => null;
}
}

View File

@@ -53,7 +53,7 @@ namespace NzbDrone.Core.Test.MetadataSource.SkyHook
{
series.Should().NotBeNull();
series.Title.Should().NotBeNullOrWhiteSpace();
series.CleanTitle.Should().Be(Parser.Parser.CleanSeriesTitle(series.Title));
series.CleanTitle.Should().Be(Parser.NormalizeParsedTitle.CleanSeriesTitle(series.Title));
series.SortTitle.Should().Be(SeriesTitleNormalizer.Normalize(series.Title, series.TvdbId));
series.Overview.Should().NotBeNullOrWhiteSpace();
series.AirTime.Should().NotBeNullOrWhiteSpace();

View File

@@ -159,10 +159,13 @@
<Compile Include="DecisionEngineTests\MinimumAgeSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RetentionSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RssSync\DelaySpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RssSync\DeletedEpisodeFileSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RssSync\ProperSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\Search\TorrentSeedingSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\Search\SeriesSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\SameEpisodesSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RawDiskSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\BlockedIndexerSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\UpgradeDiskSpecificationFixture.cs" />
<Compile Include="DiskSpace\DiskSpaceServiceFixture.cs" />
<Compile Include="Download\CompletedDownloadServiceFixture.cs" />
@@ -177,6 +180,7 @@
<Compile Include="Download\DownloadClientTests\DownloadStationTests\SharedFolderResolverFixture.cs" />
<Compile Include="Download\DownloadClientTests\DownloadStationTests\UsenetDownloadStationFixture.cs" />
<Compile Include="Download\DownloadClientTests\HadoukenTests\HadoukenFixture.cs" />
<Compile Include="Download\DownloadClientStatusServiceFixture.cs" />
<Compile Include="Download\DownloadClientTests\NzbgetTests\NzbgetFixture.cs" />
<Compile Include="Download\DownloadClientTests\NzbVortexTests\NzbVortexFixture.cs" />
<Compile Include="Download\DownloadClientTests\PneumaticProviderFixture.cs" />
@@ -234,6 +238,7 @@
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedIndexerStatusFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedHistoryItemsFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedMetadataFilesFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupDownloadClientUnavailablePendingReleasesFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupUnusedTagsFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedPendingReleasesFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\FixFutureRunScheduledTasksFixture.cs" />
@@ -279,16 +284,21 @@
<Compile Include="MediaFiles\DownloadedEpisodesImportServiceFixture.cs" />
<Compile Include="MediaFiles\EpisodeFileMovingServiceTests\MoveEpisodeFileFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMakerFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\SampleServiceFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\DetectSampleFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\FreeSpaceSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\FullSeasonSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\SameFileSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\GrabbedReleaseQualityFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\MatchesFolderSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotSampleSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotUnpackingSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\UpgradeSpecificationFixture.cs" />
<Compile Include="MediaFiles\ImportApprovedEpisodesFixture.cs" />
<Compile Include="MediaFiles\MediaFileDeletionService\DeleteEpisodeFileFixture.cs" />
<Compile Include="MediaFiles\MediaFileRepositoryFixture.cs" />
<Compile Include="MediaFiles\MediaInfo\FormattedAudioChannelsFixture.cs" />
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatAudioCodecFixture.cs" />
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatVideoCodecFixture.cs" />
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatAudioChannelsFixture.cs" />
<Compile Include="Messaging\Commands\CommandQueueManagerFixture.cs" />
<Compile Include="MetadataSource\SkyHook\SkyHookProxySearchFixture.cs" />
<Compile Include="MetadataSource\SearchSeriesComparerFixture.cs" />
@@ -298,6 +308,8 @@
<Compile Include="OrganizerTests\FileNameBuilderTests\CleanTitleFixture.cs" />
<Compile Include="OrganizerTests\FileNameBuilderTests\EpisodeTitleCollapseFixture.cs" />
<Compile Include="OrganizerTests\FileNameBuilderTests\MultiEpisodeFixture.cs" />
<Compile Include="OrganizerTests\FileNameBuilderTests\TitleTheFixture.cs" />
<Compile Include="OrganizerTests\NormalizeOfficialTitleFixture.cs" />
<Compile Include="ParserTests\MiniSeriesEpisodeParserFixture.cs" />
<Compile Include="Qualities\RevisionComparableFixture.cs" />
<Compile Include="QueueTests\QueueServiceFixture.cs" />
@@ -363,7 +375,8 @@
<Compile Include="Qualities\QualityModelComparerFixture.cs" />
<Compile Include="RootFolderTests\RootFolderServiceFixture.cs" />
<Compile Include="SeriesStatsTests\SeriesStatisticsFixture.cs" />
<Compile Include="ThingiProvider\ProviderBaseFixture.cs" />
<Compile Include="ThingiProviderTests\ProviderStatusServiceFixture.cs" />
<Compile Include="ThingiProviderTests\ProviderBaseFixture.cs" />
<Compile Include="ThingiProviderTests\NullConfigFixture.cs" />
<Compile Include="TvTests\EpisodeServiceTests\FindEpisodeByTitleFixture.cs" />
<Compile Include="TvTests\EpisodeServiceTests\HandleEpisodeFileDeletedFixture.cs" />
@@ -383,6 +396,7 @@
<Compile Include="TvTests\SeriesServiceTests\UpdateMultipleSeriesFixture.cs" />
<Compile Include="TvTests\SeriesServiceTests\UpdateSeriesFixture.cs" />
<Compile Include="TvTests\SeriesTitleNormalizerFixture.cs" />
<Compile Include="TvTests\SeriesTitleSlugValidatorFixture.cs" />
<Compile Include="TvTests\ShouldRefreshSeriesFixture.cs" />
<Compile Include="UpdateTests\UpdatePackageProviderFixture.cs" />
<Compile Include="UpdateTests\UpdateServiceFixture.cs" />
@@ -426,6 +440,9 @@
<Content Include="Files\Indexers\TorrentRss\LimeTorrents.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Files\Indexers\Torznab\torznab_animetosho.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="License.txt" />
<None Include="Files\Indexers\BroadcastheNet\RecentFeed.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>

View File

@@ -16,4 +16,4 @@ namespace NzbDrone.Core.Test.OrganizerTests
}
}
}
}

View File

@@ -0,0 +1,83 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
{
[TestFixture]
public class TitleTheFixture : CoreTest<FileNameBuilder>
{
private Series _series;
private Episode _episode;
private EpisodeFile _episodeFile;
private NamingConfig _namingConfig;
[SetUp]
public void Setup()
{
_series = Builder<Series>
.CreateNew()
.With(s => s.Title = "South Park")
.Build();
_episode = Builder<Episode>.CreateNew()
.With(e => e.Title = "City Sushi")
.With(e => e.SeasonNumber = 15)
.With(e => e.EpisodeNumber = 6)
.With(e => e.AbsoluteEpisodeNumber = 100)
.Build();
_episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "SonarrTest" };
_namingConfig = NamingConfig.Default;
_namingConfig.RenameEpisodes = true;
Mocker.GetMock<INamingConfigService>()
.Setup(c => c.GetConfig()).Returns(_namingConfig);
Mocker.GetMock<IQualityDefinitionService>()
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
.Returns<Quality>(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
}
[TestCase("The Mist", "Mist, The")]
[TestCase("A Place to Call Home", "Place to Call Home, A")]
[TestCase("An Adventure in Space and Time", "Adventure in Space and Time, An")]
[TestCase("The Flash (2010)", "Flash, The (2010)")]
[TestCase("A League Of Their Own (AU)", "League Of Their Own, A (AU)")]
[TestCase("The Fixer (ZH) (2015)", "Fixer, The (ZH) (2015)")]
[TestCase("The Sixth Sense 2 (Thai)", "Sixth Sense 2, The (Thai)")]
[TestCase("The Amazing Race (Latin America)", "Amazing Race, The (Latin America)")]
[TestCase("The Rat Pack (A&E)", "Rat Pack, The (A&E)")]
[TestCase("The Climax: I (Almost) Got Away With It (2016)", "Climax- I (Almost) Got Away With It, The (2016)")]
//[TestCase("", "")]
public void should_get_expected_title_back(string title, string expected)
{
_series.Title = title;
_namingConfig.StandardEpisodeFormat = "{Series TitleThe}";
Subject.BuildFileName(new List<Episode> { _episode }, _series, _episodeFile)
.Should().Be(expected);
}
[TestCase("A")]
[TestCase("Anne")]
[TestCase("Theodore")]
[TestCase("3%")]
public void should_not_change_title(string title)
{
_series.Title = title;
_namingConfig.StandardEpisodeFormat = "{Series TitleThe}";
Subject.BuildFileName(new List<Episode> { _episode }, _series, _episodeFile)
.Should().Be(title);
}
}
}

View File

@@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.OrganizerTests
{
[TestFixture]
public class NormalizeOfficialTitleFixture : CoreTest
{
[TestCase("$#*! My Dad Says", "S#* My Dad Says")]
//[TestCase("", "")]
public void should_scenify_special_cases(string title, string expected)
{
// These need special handling on a case by case basis.
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("@midnight", "At midnight")]
[TestCase("Murder @ 9", "Murder at 9")]
[TestCase("T@gged", "Tagged")]
[TestCase("PUCHIM@S", "PUCHIMAS")]
[TestCase("extr@", "extra")]
[TestCase("Live@Much", "Live at Much")]
//[TestCase("", "")]
public void should_scenify_at_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("3%", "3 Percent")]
//[TestCase("", "")]
public void should_scenify_percent_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Law & Order (UK)", "Law and Order UK")]
[TestCase("Sun, Sea and A&E", "Sun Sea and A and E")]
//[TestCase("", "")]
public void should_scenify_and_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Code:Breaker", "Code Breaker")]
[TestCase("Transformers: Prime", "Transformers Prime")]
[TestCase("Mobile Suit Gundam UC RE:0096", "Mobile Suit Gundam UC RE 0096")]
[TestCase("What the Bleep!?: Down the Rabbit Hole", "What the Bleep Down the Rabbit Hole")]
//[TestCase("", "")]
public void should_scenify_colon_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Sun, Sea and A&E", "Sun Sea and A and E")]
[TestCase("The $25,000 Pyramid", "The 25000 Pyramid")]
//[TestCase("", "")]
public void should_scenify_comma_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
//[TestCase("The $100,000 Pyramid", "The 100000 Dollar Pyramid")]
[TestCase("$25 Million Dollar Hoax", "25 Million Dollar Hoax")]
[TestCase("Arli$$", "Arliss")]
[TestCase("Country Buck$", "Country Bucks")]
[TestCase("Tamara Ecclestone: Billion $$ Girl", "Tamara Ecclestone Billion Dollar Girl")]
[TestCase("$#*! My Dad Says", "S#* My Dad Says")]
//[TestCase("", "")]
public void should_scenify_dollar_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Separation?!", "Separation")]
[TestCase("Snog Marry Avoid?", "Snog Marry Avoid")]
[TestCase("What the Bleep!?: Down the Rabbit Hole", "What the Bleep Down the Rabbit Hole")]
//[TestCase("", "")]
public void should_scenify_question_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Separation?!", "Separation")]
[TestCase("What the Bleep!?: Down the Rabbit Hole", "What the Bleep Down the Rabbit Hole")]
[TestCase("What's Happening!!", "Whats Happening")]
//[TestCase("", "")]
public void should_scenify_exclamation_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Bro'Town", "Bro Town")]
[TestCase("'Til Death", "Til Death")]
[TestCase("Those Who Can't", "Those Who Cant")]
[TestCase("Paul O'Grady: For the Love of Dogs", "Paul O Grady For the Love of Dogs")]
[TestCase("Bitchin' Rides", "Bitchin Rides")]
[TestCase("Trust Me, I'm a Vet", "Trust Me Im a Vet")]
[TestCase("You're the Worst", "Youre the Worst")]
//[TestCase("", "")]
public void should_scenify_quote_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Robotics;Notes", "Robotics Notes")]
[TestCase("Myself; Yourself", "Myself Yourself")]
//[TestCase("", "")]
public void should_scenify_semicolon_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Acquisitions Incorporated: The \"C\" Team", "Acquisitions Incorporated The C Team")]
//[TestCase("", "")]
public void should_scenify_doublequote_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
}
}

View File

@@ -134,5 +134,23 @@ namespace NzbDrone.Core.Test.ParserTests
{
"Tokyo Ghoul A".CleanSeriesTitle().Should().Be("tokyoghoula");
}
[TestCase("A 120% deal", "a120percentdeal")]
[TestCase("The z0%e", "thez0e")]
[TestCase("That f$%king mess", "thatfkingmess")]
public void should_replace_percentage_character(string title, string normalizedTitle)
{
title.CleanSeriesTitle().Should().Be(normalizedTitle);
}
[TestCase("@midnight", "atmidnight")]
[TestCase("Murder @ 9", "murderat9")]
[TestCase("T@gged", "tagged")]
[TestCase("PUCHIM@S", "puchimas")]
[TestCase("Live@Much", "liveamuch")] // liveatmuch
public void should_replace_at_character(string title, string normalizedTitle)
{
title.CleanSeriesTitle().Should().Be(normalizedTitle);
}
}
}

View File

@@ -1,6 +1,8 @@
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ParserTests
@@ -47,7 +49,7 @@ namespace NzbDrone.Core.Test.ParserTests
public void should_remove_accents_from_title()
{
const string title = "Carniv\u00E0le";
title.CleanSeriesTitle().Should().Be("carnivale");
}
@@ -62,5 +64,13 @@ namespace NzbDrone.Core.Test.ParserTests
{
Parser.Parser.ParseTitle(postTitle).SeriesTitle.Should().Be(title);
}
[TestCase("Revolution.S01E02.Chained.Heat.mkv")]
[TestCase("Dexter - S01E01 - Title.avi")]
public void should_parse_quality_from_extension(string title)
{
Parser.Parser.ParseTitle(title).Quality.Quality.Should().NotBe(Quality.Unknown);
Parser.Parser.ParseTitle(title).Quality.QualitySource.Should().Be(QualitySource.Extension);
}
}
}

View File

@@ -192,7 +192,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
GivenAbsoluteNumberingSeries();
_parsedEpisodeInfo.Special = true;
Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null);
Mocker.GetMock<IEpisodeService>()
@@ -210,7 +210,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
GivenAbsoluteNumberingSeries();
Mocker.GetMock<ISceneMappingService>()
.Setup(s => s.GetSceneSeasonNumber(_parsedEpisodeInfo.SeriesTitle))
.Setup(s => s.GetSceneSeasonNumber(_parsedEpisodeInfo.SeriesTitle, It.IsAny<string>()))
.Returns(seasonNumber);
Mocker.GetMock<IEpisodeService>()
@@ -234,7 +234,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
GivenAbsoluteNumberingSeries();
Mocker.GetMock<ISceneMappingService>()
.Setup(s => s.GetSceneSeasonNumber(_parsedEpisodeInfo.SeriesTitle))
.Setup(s => s.GetSceneSeasonNumber(_parsedEpisodeInfo.SeriesTitle, It.IsAny<string>()))
.Returns(seasonNumber);
Mocker.GetMock<IEpisodeService>()
@@ -258,7 +258,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
GivenAbsoluteNumberingSeries();
Mocker.GetMock<ISceneMappingService>()
.Setup(s => s.GetSceneSeasonNumber(_parsedEpisodeInfo.SeriesTitle))
.Setup(s => s.GetSceneSeasonNumber(_parsedEpisodeInfo.SeriesTitle, It.IsAny<string>()))
.Returns(seasonNumber);
Mocker.GetMock<IEpisodeService>()
@@ -280,7 +280,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
const int tvdbSeasonNumber = 5;
Mocker.GetMock<ISceneMappingService>()
.Setup(s => s.FindSceneMapping(_parsedEpisodeInfo.SeriesTitle))
.Setup(s => s.FindSceneMapping(_parsedEpisodeInfo.SeriesTitle, It.IsAny<string>()))
.Returns(new SceneMapping { SeasonNumber = tvdbSeasonNumber, SceneSeasonNumber = _parsedEpisodeInfo.SeasonNumber });
Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null);
@@ -298,7 +298,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
const int tvdbSeasonNumber = 5;
Mocker.GetMock<ISceneMappingService>()
.Setup(s => s.FindSceneMapping(_parsedEpisodeInfo.SeriesTitle))
.Setup(s => s.FindSceneMapping(_parsedEpisodeInfo.SeriesTitle, It.IsAny<string>()))
.Returns(new SceneMapping { SeasonNumber = tvdbSeasonNumber, SceneSeasonNumber = _parsedEpisodeInfo.SeasonNumber + 100 });
Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null);
@@ -330,7 +330,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
const int tvdbSeasonNumber = -1;
Mocker.GetMock<ISceneMappingService>()
.Setup(s => s.FindSceneMapping(_parsedEpisodeInfo.SeriesTitle))
.Setup(s => s.FindSceneMapping(_parsedEpisodeInfo.SeriesTitle, It.IsAny<string>()))
.Returns(new SceneMapping { SeasonNumber = tvdbSeasonNumber, SceneSeasonNumber = _parsedEpisodeInfo.SeasonNumber });
Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null);

View File

@@ -118,7 +118,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
GivenMatchByTvRageId();
Mocker.GetMock<ISceneMappingService>()
.Setup(v => v.FindTvdbId(It.IsAny<string>()))
.Setup(v => v.FindTvdbId(It.IsAny<string>(), It.IsAny<string>()))
.Returns(10);
var result = Subject.Map(_parsedEpisodeInfo, _series.TvdbId, _series.TvRageId);
@@ -199,7 +199,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
public void should_use_tvdbid_matching_when_alias_is_found()
{
Mocker.GetMock<ISceneMappingService>()
.Setup(s => s.FindTvdbId(It.IsAny<string>()))
.Setup(s => s.FindTvdbId(It.IsAny<string>(), It.IsAny<string>()))
.Returns(_series.TvdbId);
Subject.Map(_parsedEpisodeInfo, _series.TvdbId, _series.TvRageId, _singleEpisodeSearchCriteria);

View File

@@ -57,6 +57,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Lost.S04E04.720p.BluRay.x264-xHD-1", "xHD")]
[TestCase("Blue.Bloods.S05E11.720p.HDTV.X264-DIMENSION-1", "DIMENSION")]
[TestCase("saturday.night.live.s40e11.kevin.hart_sia.720p.hdtv.x264-w4f-sample.mkv", "w4f")]
[TestCase("The.Sequel.2017.S05E02.1080p.WEB-DL.DD5.1.H264-EVL-Scrambled", "EVL")]
public void should_not_include_repost_in_release_group(string title, string expected)
{
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);

View File

@@ -128,6 +128,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Jeopardy - S2016E231", "Jeopardy", 2016, 231)]
[TestCase("Jeopardy - 2016x231", "Jeopardy", 2016, 231)]
[TestCase("Shortland.Street.S26E022.HDTV.x264-FiHTV", "Shortland Street", 26, 22)]
[TestCase("Super.Potatoes.S01.Ep06.1080p.BluRay.DTS.x264-MiR", "Super Potatoes", 1, 6)]
//[TestCase("", "", 0, 0)]
public void should_parse_single_episode(string postTitle, string title, int seasonNumber, int episodeNumber)
{

View File

@@ -14,4 +14,4 @@ namespace NzbDrone.Core.Test.ThingiProviderTests
Subject.Validate().IsValid.Should().BeTrue();
}
}
}
}

View File

@@ -5,9 +5,8 @@ using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ThingiProvider
namespace NzbDrone.Core.Test.ThingiProviderTests
{
public class ProviderRepositoryFixture : DbTest<IndexerRepository, IndexerDefinition>
{
[Test]
@@ -27,4 +26,4 @@ namespace NzbDrone.Core.Test.ThingiProvider
storedSetting.ShouldBeEquivalentTo(newznabSettings, o=>o.IncludingAllRuntimeProperties());
}
}
}
}

View File

@@ -0,0 +1,126 @@
using System;
using System.Linq;
using FluentAssertions;
using Moq;
using NLog;
using NUnit.Framework;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.ThingiProvider.Status;
namespace NzbDrone.Core.Test.ThingiProviderTests
{
public class MockProviderStatus : ProviderStatusBase
{
}
public interface IMockProvider : IProvider
{
}
public interface IMockProviderStatusRepository : IProviderStatusRepository<MockProviderStatus>
{
}
public class MockProviderStatusService : ProviderStatusServiceBase<IMockProvider, MockProviderStatus>
{
public MockProviderStatusService(IMockProviderStatusRepository providerStatusRepository, IEventAggregator eventAggregator, Logger logger)
: base(providerStatusRepository, eventAggregator, logger)
{
}
}
public class ProviderStatusServiceFixture : CoreTest<MockProviderStatusService>
{
private DateTime _epoch;
[SetUp]
public void SetUp()
{
_epoch = DateTime.UtcNow;
}
private void WithStatus(MockProviderStatus status)
{
Mocker.GetMock<IMockProviderStatusRepository>()
.Setup(v => v.FindByProviderId(1))
.Returns(status);
Mocker.GetMock<IMockProviderStatusRepository>()
.Setup(v => v.All())
.Returns(new[] { status });
}
private void VerifyUpdate()
{
Mocker.GetMock<IMockProviderStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<MockProviderStatus>()), Times.Once());
}
private void VerifyNoUpdate()
{
Mocker.GetMock<IMockProviderStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<MockProviderStatus>()), Times.Never());
}
[Test]
public void should_start_backoff_on_first_failure()
{
WithStatus(new MockProviderStatus());
Subject.RecordFailure(1);
VerifyUpdate();
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
status.DisabledTill.Should().HaveValue();
status.DisabledTill.Value.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(5), 500);
}
[Test]
public void should_cancel_backoff_on_success()
{
WithStatus(new MockProviderStatus { EscalationLevel = 2 });
Subject.RecordSuccess(1);
VerifyUpdate();
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().BeNull();
}
[Test]
public void should_not_store_update_if_already_okay()
{
WithStatus(new MockProviderStatus { EscalationLevel = 0 });
Subject.RecordSuccess(1);
VerifyNoUpdate();
}
[Test]
public void should_preserve_escalation_on_intermittent_success()
{
WithStatus(new MockProviderStatus
{
InitialFailure = _epoch - TimeSpan.FromSeconds(20),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(4),
EscalationLevel = 3
});
Subject.RecordSuccess(1);
Subject.RecordSuccess(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
status.DisabledTill.Should().HaveValue();
status.DisabledTill.Value.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(15), 500);
}
}
}

View File

@@ -194,6 +194,46 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeMonitoredServiceTests
.Verify(v => v.UpdateEpisodes(It.Is<List<Episode>>(l => l.All(e => !e.Monitored))));
}
[Test]
public void should_should_not_monitor_episodes_if_season_is_not_monitored()
{
_series = Builder<Series>.CreateNew()
.With(s => s.Seasons = Builder<Season>.CreateListOfSize(2)
.TheFirst(1)
.With(n => n.Monitored = true)
.TheLast(1)
.With(n => n.Monitored = false)
.Build()
.ToList())
.Build();
var episodes = Builder<Episode>.CreateListOfSize(10)
.All()
.With(e => e.Monitored = true)
.With(e => e.EpisodeFileId = 0)
.With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-7))
.TheFirst(5)
.With(e => e.SeasonNumber = 1)
.TheLast(5)
.With(e => e.SeasonNumber = 2)
.BuildList();
Mocker.GetMock<IEpisodeService>()
.Setup(s => s.GetEpisodeBySeries(It.IsAny<int>()))
.Returns(episodes);
Subject.SetEpisodeMonitoredStatus(_series, new MonitoringOptions
{
IgnoreEpisodesWithFiles = true,
IgnoreEpisodesWithoutFiles = false
});
VerifyMonitored(e => e.SeasonNumber == 1);
VerifyNotMonitored(e => e.SeasonNumber == 2);
VerifySeasonMonitored(s => s.SeasonNumber == 1);
VerifySeasonNotMonitored(s => s.SeasonNumber == 2);
}
private void VerifyMonitored(Func<Episode, bool> predicate)
{
Mocker.GetMock<IEpisodeService>()

View File

@@ -0,0 +1,76 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using FluentValidation.Validators;
using NUnit.Framework;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.TvTests
{
[TestFixture]
public class SeriesTitleSlugValidatorFixture : CoreTest<SeriesTitleSlugValidator>
{
private List<Series> _series;
private TestValidator<Series> _validator;
[SetUp]
public void Setup()
{
_series = Builder<Series>.CreateListOfSize(1)
.Build()
.ToList();
_validator = new TestValidator<Series>
{
v => v.RuleFor(s => s.TitleSlug).SetValidator(Subject)
};
Mocker.GetMock<ISeriesService>()
.Setup(s => s.GetAllSeries())
.Returns(_series);
}
[Test]
public void should_not_be_valid_if_there_is_an_existing_series_with_the_same_title_slug()
{
var series = Builder<Series>.CreateNew()
.With(s => s.Id = 100)
.With(s => s.TitleSlug = _series.First().TitleSlug)
.Build();
_validator.Validate(series).IsValid.Should().BeFalse();
}
[Test]
public void should_be_valid_if_there_is_not_an_existing_series_with_the_same_title_slug()
{
var series = Builder<Series>.CreateNew()
.With(s => s.TitleSlug = "MyTitleSlug")
.Build();
_validator.Validate(series).IsValid.Should().BeTrue();
}
[Test]
public void should_be_valid_if_there_is_an_existing_series_with_a_null_title_slug()
{
_series.First().TitleSlug = null;
var series = Builder<Series>.CreateNew()
.With(s => s.TitleSlug = "MyTitleSlug")
.Build();
_validator.Validate(series).IsValid.Should().BeTrue();
}
[Test]
public void should_be_valid_when_updating_an_existing_series()
{
_validator.Validate(_series.First().JsonClone()).IsValid.Should().BeTrue();
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -142,7 +142,21 @@ namespace NzbDrone.Core.Configuration
public bool LaunchBrowser => GetValueBoolean("LaunchBrowser", true);
public string ApiKey => GetValue("ApiKey", GenerateApiKey());
public string ApiKey
{
get
{
var apiKey = GetValue("ApiKey", GenerateApiKey());
if (apiKey.IsNullOrWhiteSpace())
{
apiKey = GenerateApiKey();
SetValue("ApiKey", apiKey);
}
return apiKey;
}
}
public AuthenticationType AuthenticationMethod
{

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Common.Exceptions;
namespace NzbDrone.Core.DataAugmentation.Scene
{
public class InvalidSceneMappingException : NzbDroneException
{
public InvalidSceneMappingException(IEnumerable<SceneMapping> mappings)
: base(FormatMessage(mappings))
{
}
private static string FormatMessage(IEnumerable<SceneMapping> mappings)
{
return string.Format("Scene Mappings contains a conflict for tvdbids {0}. Please notify Sonarr developers.", string.Join(",", mappings.Select(v => v.TvdbId.ToString())));
}
}
}

View File

@@ -17,6 +17,9 @@ namespace NzbDrone.Core.DataAugmentation.Scene
public int? SeasonNumber { get; set; }
public int? SceneSeasonNumber { get; set; }
public string FilterRegex { get; set; }
public string Type { get; set; }
}
}
}

View File

@@ -8,17 +8,18 @@ using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
using System.Collections.Generic;
using NzbDrone.Core.Tv.Events;
using System.Text.RegularExpressions;
namespace NzbDrone.Core.DataAugmentation.Scene
{
public interface ISceneMappingService
{
List<string> GetSceneNames(int tvdbId, List<int> seasonNumbers, List<int> sceneSeasonNumbers);
int? FindTvdbId(string title);
int? FindTvdbId(string sceneTitle, string releaseTitle);
List<SceneMapping> FindByTvdbId(int tvdbId);
SceneMapping FindSceneMapping(string title);
int? GetSceneSeasonNumber(string title);
int? GetTvdbSeasonNumber(string title);
SceneMapping FindSceneMapping(string sceneTitle, string releaseTitle);
int? GetSceneSeasonNumber(string seriesTitle, string releaseTitle);
int? GetTvdbSeasonNumber(string seriesTitle, string releaseTitle);
int? GetSceneSeasonNumber(int tvdbId, int seasonNumber);
}
@@ -65,14 +66,14 @@ namespace NzbDrone.Core.DataAugmentation.Scene
return FilterNonEnglish(names);
}
public int? FindTvdbId(string title)
public int? FindTvdbId(string seriesTitle)
{
var mapping = FindMapping(title);
return FindTvdbId(seriesTitle, null);
}
if (mapping == null)
return null;
return mapping.TvdbId;
public int? FindTvdbId(string seriesTitle, string releaseTitle)
{
return FindSceneMapping(seriesTitle, releaseTitle)?.TvdbId;
}
public List<SceneMapping> FindByTvdbId(int tvdbId)
@@ -92,33 +93,33 @@ namespace NzbDrone.Core.DataAugmentation.Scene
return mappings;
}
public SceneMapping FindSceneMapping(string title)
public SceneMapping FindSceneMapping(string seriesTitle, string releaseTitle)
{
return FindMapping(title);
}
var mappings = FindMappings(seriesTitle, releaseTitle);
public int? GetSceneSeasonNumber(string title)
{
var mapping = FindMapping(title);
if (mapping == null)
if (mappings == null)
{
return null;
}
return mapping.SceneSeasonNumber;
}
var distinctMappings = mappings.DistinctBy(v => v.TvdbId).ToList();
public int? GetTvdbSeasonNumber(string title)
{
var mapping = FindMapping(title);
if (mapping == null)
if (distinctMappings.Count <= 1)
{
return null;
return distinctMappings.FirstOrDefault();
}
return mapping.SeasonNumber;
throw new InvalidSceneMappingException(mappings);
}
public int? GetSceneSeasonNumber(string seriesTitle, string releaseTitle)
{
return FindSceneMapping(seriesTitle, releaseTitle)?.SceneSeasonNumber;
}
public int? GetTvdbSeasonNumber(string seriesTitle, string releaseTitle)
{
return FindSceneMapping(seriesTitle, releaseTitle)?.SeasonNumber;
}
public int? GetSceneSeasonNumber(int tvdbId, int seasonNumber)
@@ -184,44 +185,48 @@ namespace NzbDrone.Core.DataAugmentation.Scene
_logger.Error(ex, "Failed to Update Scene Mappings.");
}
}
RefreshCache();
_eventAggregator.PublishEvent(new SceneMappingsUpdatedEvent());
}
private SceneMapping FindMapping(string title)
private List<SceneMapping> FindMappings(string seriesTitle, string releaseTitle)
{
if (_getTvdbIdCache.Count == 0)
{
RefreshCache();
}
var candidates = _getTvdbIdCache.Find(title.CleanSeriesTitle());
var candidates = _getTvdbIdCache.Find(seriesTitle.CleanSeriesTitle());
if (candidates == null)
{
return null;
}
if (candidates.Count == 1)
candidates = FilterSceneMappings(candidates, releaseTitle);
if (candidates.Count <= 1)
{
return candidates.First();
return candidates;
}
var exactMatch = candidates.OrderByDescending(v => v.SeasonNumber)
.FirstOrDefault(v => v.Title == title);
.Where(v => v.Title == seriesTitle)
.ToList();
if (exactMatch != null)
if (exactMatch.Any())
{
return exactMatch;
}
var closestMatch = candidates.OrderBy(v => title.LevenshteinDistance(v.Title, 10, 1, 10))
var closestMatch = candidates.OrderBy(v => seriesTitle.LevenshteinDistance(v.Title, 10, 1, 10))
.ThenByDescending(v => v.SeasonNumber)
.First();
return closestMatch;
return candidates.Where(v => v.Title == closestMatch.Title).ToList();
}
private void RefreshCache()
@@ -232,6 +237,26 @@ namespace NzbDrone.Core.DataAugmentation.Scene
_findByTvdbIdCache.Update(mappings.GroupBy(v => v.TvdbId).ToDictionary(v => v.Key.ToString(), v => v.ToList()));
}
private List<SceneMapping> FilterSceneMappings(List<SceneMapping> candidates, string releaseTitle)
{
var filteredCandidates = candidates.Where(v => v.FilterRegex.IsNotNullOrWhiteSpace()).ToList();
var normalCandidates = candidates.Except(filteredCandidates).ToList();
if (releaseTitle.IsNullOrWhiteSpace())
{
return normalCandidates;
}
filteredCandidates = filteredCandidates.Where(v => Regex.IsMatch(releaseTitle, v.FilterRegex)).ToList();
if (filteredCandidates.Any())
{
return filteredCandidates;
}
return normalCandidates;
}
private List<string> FilterNonEnglish(List<string> titles)
{
return titles.Where(title => title.All(c => c <= 255)).ToList();

View File

@@ -61,7 +61,15 @@ namespace NzbDrone.Core.DataAugmentation.Xem
if (episode == null)
{
_logger.Debug("Information hasn't been added to TheTVDB yet, skipping.");
_logger.Debug("Information hasn't been added to TheTVDB yet, skipping");
continue;
}
if (mapping.Scene.Absolute == 0 &&
mapping.Scene.Season == 0 &&
mapping.Scene.Episode == 0)
{
_logger.Debug("Mapping for {0} S{1:00}E{2:00} is invalid, skipping", series, mapping.Tvdb.Season, mapping.Tvdb.Episode);
continue;
}

Some files were not shown because too many files have changed in this diff Show More