mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-29 18:15:37 -04:00
Compare commits
76 Commits
v2.0.0.395
...
v2.0.0.414
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
229986033c | ||
|
|
c249ad5dbe | ||
|
|
e2d6d374ab | ||
|
|
d3adb7ac40 | ||
|
|
0f1afd416b | ||
|
|
2f3bc61af7 | ||
|
|
319b4f13b7 | ||
|
|
54fda3d648 | ||
|
|
2f6fded7c3 | ||
|
|
7934003b5e | ||
|
|
8773d38ddd | ||
|
|
f16f097b3e | ||
|
|
7284ef50eb | ||
|
|
e9248e284e | ||
|
|
aff6af1806 | ||
|
|
c0c35a0eba | ||
|
|
072ca459bd | ||
|
|
0865306064 | ||
|
|
ac14444d34 | ||
|
|
8a6d1ef373 | ||
|
|
dc694b0f34 | ||
|
|
76f8cc81da | ||
|
|
14f737bd60 | ||
|
|
5942ddf9f1 | ||
|
|
ab7b427241 | ||
|
|
15120270b4 | ||
|
|
cc0406653a | ||
|
|
9f34127565 | ||
|
|
71ecc96c70 | ||
|
|
2fa3873503 | ||
|
|
96b7bd3b2b | ||
|
|
e1ea17cabf | ||
|
|
3a162be265 | ||
|
|
502298aab9 | ||
|
|
edea488dbe | ||
|
|
2fabe2d198 | ||
|
|
f2c8156c00 | ||
|
|
942be364dc | ||
|
|
44e09e2220 | ||
|
|
0fcd20ec4a | ||
|
|
4c5707bba8 | ||
|
|
947f494e72 | ||
|
|
3f74a87b45 | ||
|
|
da0bdc5750 | ||
|
|
3ea59cd91b | ||
|
|
9b42dc7082 | ||
|
|
8b1c022244 | ||
|
|
e5cb8bb0bd | ||
|
|
d37343bb7d | ||
|
|
9c91f11cdc | ||
|
|
444fcf5ae5 | ||
|
|
31f8e0a47a | ||
|
|
1e0fcc877b | ||
|
|
1293bab868 | ||
|
|
9de92d18e1 | ||
|
|
5a877cbd62 | ||
|
|
99aa25bf83 | ||
|
|
5f66c15b91 | ||
|
|
af220c7f7b | ||
|
|
59e71a4cd9 | ||
|
|
6b1a4c4198 | ||
|
|
1072c5247c | ||
|
|
6508e920fe | ||
|
|
1154e0eeb3 | ||
|
|
2d96914bfa | ||
|
|
70494c3674 | ||
|
|
d68ad98176 | ||
|
|
eb70a6419c | ||
|
|
22aa759abc | ||
|
|
19b8fb6d8b | ||
|
|
d4bc835b1c | ||
|
|
b99d82cccc | ||
|
|
25d481d5d9 | ||
|
|
23871503a2 | ||
|
|
7c54fa70d7 | ||
|
|
2ffbbb0e71 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -128,3 +128,4 @@ output/*
|
||||
._*
|
||||
|
||||
_start
|
||||
_temp_*/**/*
|
||||
|
||||
@@ -18,7 +18,9 @@ Setup guides, FAQ, the more information we have on the wiki the better.
|
||||
1. Fork Sonarr
|
||||
2. Clone (develop branch) *you may need pull in submodules separately if you client doesn't clone them automatically (CurlSharp)*
|
||||
3. Run `npm install`
|
||||
4. Run `gulp watch` - Used to compile the UI components and copy them (leave this window open)
|
||||
4. Run `npm start` - Used to compile the UI components and copy them.
|
||||
Leave this window open.
|
||||
If you have gulp globally installed you can use `gulp watch` instead
|
||||
5. Compile in Visual Studio
|
||||
|
||||
### Contributing Code ###
|
||||
|
||||
14
build.sh
14
build.sh
@@ -1,5 +1,5 @@
|
||||
#! /bin/bash
|
||||
msBuild='/c/Windows/Microsoft.NET/Framework64/v4.0.30319/'
|
||||
msBuild='/c/Program Files (x86)/MSBuild/14.0/Bin'
|
||||
outputFolder='./_output'
|
||||
outputFolderMono='./_output_mono'
|
||||
outputFolderOsx='./_output_osx'
|
||||
@@ -102,12 +102,12 @@ Build()
|
||||
RunGulp()
|
||||
{
|
||||
echo "##teamcity[progressStart 'npm install']"
|
||||
CheckExitCode npm install
|
||||
npm-cache install npm || CheckExitCode npm install
|
||||
echo "##teamcity[progressFinish 'npm install']"
|
||||
|
||||
echo "##teamcity[progressStart 'Running Gulp']"
|
||||
CheckExitCode gulp build
|
||||
echo "##teamcity[progressFinish 'Running Gulp']"
|
||||
echo "##teamcity[progressStart 'Running gulp']"
|
||||
CheckExitCode npm run build
|
||||
echo "##teamcity[progressFinish 'Running gulp']"
|
||||
}
|
||||
|
||||
CreateMdbs()
|
||||
@@ -208,9 +208,9 @@ PackageTests()
|
||||
find $sourceFolder -path $testSearchPattern -exec cp -r -u -T "{}" $testPackageFolder \;
|
||||
|
||||
if [ $runtime = "dotnet" ] ; then
|
||||
$nuget install NUnit.Runners -Version 2.6.1 -Output $testPackageFolder
|
||||
$nuget install NUnit.ConsoleRunner -Version 3.2.0 -Output $testPackageFolder
|
||||
else
|
||||
mono $nuget install NUnit.Runners -Version 2.6.1 -Output $testPackageFolder
|
||||
mono $nuget install NUnit.ConsoleRunner -Version 3.2.0 -Output $testPackageFolder
|
||||
fi
|
||||
|
||||
cp $outputFolder/*.dll $testPackageFolder
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
EXCLUDE="-exclude:Windows -include:IntegrationTest"
|
||||
TESTDIR="."
|
||||
NUNIT="$TESTDIR/NUnit.Runners.2.6.1/tools/nunit-console-x86.exe"
|
||||
|
||||
mono --debug --runtime=v4.0 $NUNIT $EXCLUDE -xml:NzbDrone.Api.Result.xml $TESTDIR/NzbDrone.Api.Test.dll
|
||||
mono --debug --runtime=v4.0 $NUNIT $EXCLUDE -xml:NzbDrone.Core.Result.xml $TESTDIR/NzbDrone.Core.Test.dll
|
||||
mono --debug --runtime=v4.0 $NUNIT $EXCLUDE -xml:NzbDrone.Integration.Result.xml $TESTDIR/NzbDrone.Integration.Test.dll
|
||||
mono --debug --runtime=v4.0 $NUNIT $EXCLUDE -xml:NzbDrone.Common.Result.xml $TESTDIR/NzbDrone.Common.Test.dll
|
||||
29
osx/Sonarr
29
osx/Sonarr
@@ -1,17 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
|
||||
#get the bundle's MacOS directory full path
|
||||
DIR=$(cd "$(dirname "$0")"; pwd)
|
||||
|
||||
|
||||
#change these values to match your app
|
||||
EXE_PATH="$DIR/NzbDrone.exe"
|
||||
APPNAME="Sonarr"
|
||||
|
||||
#set up environment
|
||||
MONO_FRAMEWORK_PATH=/Library/Frameworks/Mono.framework/Versions/Current
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$DIR:$MONO_FRAMEWORK_PATH/lib:/lib:/usr/lib"
|
||||
export PATH="$MONO_FRAMEWORK_PATH/bin:$PATH"
|
||||
|
||||
if [[ -x '/opt/local/bin/mono' ]]; then
|
||||
export PATH="/opt/local/bin:$PATH"
|
||||
fi
|
||||
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$DIR"
|
||||
|
||||
if [ -e /Library/Frameworks/Mono.framework ]; then
|
||||
MONO_FRAMEWORK_PATH=/Library/Frameworks/Mono.framework/Versions/Current
|
||||
export PATH="$MONO_FRAMEWORK_PATH/bin:$PATH"
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$DYLD_FALLBACK_LIBRARY_PATH:$MONO_FRAMEWORK_PATH/lib"
|
||||
fi
|
||||
|
||||
if [[ -f '/opt/local/lib/libsqlite3.0.dylib' ]]; then
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="/opt/local/lib:$DYLD_FALLBACK_LIBRARY_PATH"
|
||||
fi
|
||||
|
||||
export DYLD_FALLBACK_LIBRARY_PATH="$DYLD_FALLBACK_LIBRARY_PATH:$HOME/lib:/usr/local/lib:/lib:/usr/lib"
|
||||
|
||||
#mono version check
|
||||
REQUIRED_MAJOR=3
|
||||
REQUIRED_MINOR=10
|
||||
@@ -21,6 +35,9 @@ VERSION_MSG="$APPNAME requires Mono Runtime Environment(MRE) $REQUIRED_MAJOR.$RE
|
||||
DOWNLOAD_URL="http://www.mono-project.com/download/#download-mac"
|
||||
|
||||
MONO_VERSION="$(mono --version | grep 'Mono JIT compiler version ' | cut -f5 -d\ )"
|
||||
# if [[ -o DEBUG ]]; then osascript -e "display dialog \"MONO_VERSION: $MONO_VERSION\""; fi
|
||||
|
||||
|
||||
MONO_VERSION_MAJOR="$(echo $MONO_VERSION | cut -f1 -d.)"
|
||||
MONO_VERSION_MINOR="$(echo $MONO_VERSION | cut -f2 -d.)"
|
||||
if [ -z "$MONO_VERSION" ] \
|
||||
|
||||
@@ -4,14 +4,15 @@
|
||||
"description": "Sonarr",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"preinstall": ""
|
||||
"build": "gulp build",
|
||||
"start": "gulp watch"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/Sonarr/Sonarr.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "BSD",
|
||||
"license": "GPL-3.0",
|
||||
"gitHead": "9ff7aa1bf7fe38c4c5bdb92f56c8ad556916ed67",
|
||||
"readmeFilename": "readme.md",
|
||||
"dependencies": {
|
||||
|
||||
@@ -24,7 +24,6 @@ Sonarr is a PVR for Usenet and BitTorrent users. It can monitor multiple RSS fee
|
||||
- Visual Studio 2015 [Free Community Edition](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx)
|
||||
- [Git](http://git-scm.com/downloads)
|
||||
- [NodeJS](http://nodejs.org/download/)
|
||||
- [Gulp](http://gulpjs.com)
|
||||
|
||||
### Setup ###
|
||||
|
||||
@@ -32,8 +31,7 @@ Sonarr is a PVR for Usenet and BitTorrent users. It can monitor multiple RSS fee
|
||||
- Clone the repository into your development machine. [*info*](https://help.github.com/articles/working-with-repositories)
|
||||
- Grab the submodules `git submodule init && git submodule update`
|
||||
- install the required Node Packages `npm install`
|
||||
- install gulp `npm install gulp -g`
|
||||
- start gulp to monitor your dev environment for any changes that need post processing using `gulp watch` command.
|
||||
- start gulp to monitor your dev environment for any changes that need post processing using `npm start` command.
|
||||
|
||||
*Please note gulp must be running at all times while you are working with Sonarr client source files.*
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.0-rc1\lib\net40\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NLog" version="4.2.3" targetFramework="net40" />
|
||||
<package id="NLog" version="4.3.0-rc1" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -46,9 +46,9 @@
|
||||
<HintPath>..\packages\FluentAssertions.4.2.1\lib\net40\FluentAssertions.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
|
||||
<Reference Include="nunit.framework, Version=3.2.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.2.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
@@ -111,4 +111,4 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -3,6 +3,6 @@
|
||||
<package id="FluentAssertions" version="4.2.1" targetFramework="net40" />
|
||||
<package id="Moq" version="4.0.10827" />
|
||||
<package id="NBuilder" version="3.0.1.1" targetFramework="net40" />
|
||||
<package id="NUnit" version="2.6.3" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.2.0" targetFramework="net40" />
|
||||
<package id="ValueInjecter" version="2.3.3" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Nancy;
|
||||
using Nancy.Bootstrapper;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Api.Extensions.Pipelines
|
||||
{
|
||||
public class RequestLoggingPipeline : IRegisterNancyPipeline
|
||||
{
|
||||
private static readonly Logger _loggerHttp = LogManager.GetLogger("Http");
|
||||
private static readonly Logger _loggerApi = LogManager.GetLogger("Api");
|
||||
|
||||
private static int _requestSequenceID;
|
||||
|
||||
public void Register(IPipelines pipelines)
|
||||
{
|
||||
pipelines.BeforeRequest.AddItemToStartOfPipeline(LogStart);
|
||||
pipelines.AfterRequest.AddItemToEndOfPipeline(LogEnd);
|
||||
}
|
||||
|
||||
private Response LogStart(NancyContext context)
|
||||
{
|
||||
var id = Interlocked.Increment(ref _requestSequenceID);
|
||||
|
||||
context.Items["ApiRequestSequenceID"] = id;
|
||||
context.Items["ApiRequestStartTime"] = DateTime.UtcNow;
|
||||
|
||||
var reqPath = GetRequestPathAndQuery(context.Request);
|
||||
|
||||
_loggerHttp.Trace("Req: {0} [{1}] {2}", id, context.Request.Method, reqPath);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void LogEnd(NancyContext context)
|
||||
{
|
||||
var id = (int)context.Items["ApiRequestSequenceID"];
|
||||
var startTime = (DateTime)context.Items["ApiRequestStartTime"];
|
||||
|
||||
var endTime = DateTime.UtcNow;
|
||||
var duration = endTime - startTime;
|
||||
|
||||
var reqPath = GetRequestPathAndQuery(context.Request);
|
||||
|
||||
_loggerHttp.Trace("Res: {0} [{1}] {2}: {3}.{4} ({5} ms)", id, context.Request.Method, reqPath, (int)context.Response.StatusCode, context.Response.StatusCode, (int)duration.TotalMilliseconds);
|
||||
|
||||
if (context.Request.IsApiRequest())
|
||||
{
|
||||
_loggerApi.Debug("[{0}] {1}: {2}.{3} ({4} ms)", context.Request.Method, reqPath, (int)context.Response.StatusCode, context.Response.StatusCode, (int)duration.TotalMilliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetRequestPathAndQuery(Request request)
|
||||
{
|
||||
if (request.Url.Query.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return string.Concat(request.Url.Path, "?", request.Url.Query);
|
||||
}
|
||||
else
|
||||
{
|
||||
return request.Url.Path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.HealthCheck;
|
||||
|
||||
namespace NzbDrone.Api.Health
|
||||
@@ -8,6 +9,6 @@ namespace NzbDrone.Api.Health
|
||||
{
|
||||
public HealthCheckResult Type { get; set; }
|
||||
public string Message { get; set; }
|
||||
public Uri WikiUrl { get; set; }
|
||||
public HttpUri WikiUrl { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.0-rc1\lib\net40\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
@@ -100,6 +100,7 @@
|
||||
<Compile Include="Commands\CommandResource.cs" />
|
||||
<Compile Include="Extensions\AccessControlHeaders.cs" />
|
||||
<Compile Include="Extensions\Pipelines\CorsPipeline.cs" />
|
||||
<Compile Include="Extensions\Pipelines\RequestLoggingPipeline.cs" />
|
||||
<Compile Include="Frontend\Mappers\LoginHtmlMapper.cs" />
|
||||
<Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" />
|
||||
<Compile Include="Indexers\ReleaseModuleBase.cs" />
|
||||
|
||||
@@ -92,11 +92,6 @@ namespace NzbDrone.Api
|
||||
{
|
||||
var providerDefinition = GetDefinition(providerResource, false);
|
||||
|
||||
if (providerDefinition.Enable)
|
||||
{
|
||||
Test(providerDefinition, false);
|
||||
}
|
||||
|
||||
_providerFactory.Update(providerDefinition);
|
||||
}
|
||||
|
||||
@@ -160,8 +155,10 @@ namespace NzbDrone.Api
|
||||
|
||||
private Response Test(TProviderResource providerResource)
|
||||
{
|
||||
var providerDefinition = GetDefinition(providerResource, true);
|
||||
// Don't validate when getting the definition so we can validate afterwards (avoids validation being skipped because the provider is disabled)
|
||||
var providerDefinition = GetDefinition(providerResource, true, false);
|
||||
|
||||
Validate(providerDefinition, true);
|
||||
Test(providerDefinition, true);
|
||||
|
||||
return "{}";
|
||||
|
||||
@@ -16,7 +16,8 @@ namespace NzbDrone.Api.RootFolders
|
||||
RootFolderValidator rootFolderValidator,
|
||||
PathExistsValidator pathExistsValidator,
|
||||
DroneFactoryValidator droneFactoryValidator,
|
||||
MappedNetworkDriveValidator mappedNetworkDriveValidator)
|
||||
MappedNetworkDriveValidator mappedNetworkDriveValidator,
|
||||
StartupFolderValidator startupFolderValidator)
|
||||
: base(signalRBroadcaster)
|
||||
{
|
||||
_rootFolderService = rootFolderService;
|
||||
@@ -32,6 +33,7 @@ namespace NzbDrone.Api.RootFolders
|
||||
.SetValidator(rootFolderValidator)
|
||||
.SetValidator(droneFactoryValidator)
|
||||
.SetValidator(mappedNetworkDriveValidator)
|
||||
.SetValidator(startupFolderValidator)
|
||||
.SetValidator(pathExistsValidator);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,5 +6,6 @@ namespace NzbDrone.Api.Series
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public int SeasonNumber { get; set; }
|
||||
public int SceneSeasonNumber { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
<package id="Nancy.Authentication.Basic" version="0.23.2" targetFramework="net40" />
|
||||
<package id="Nancy.Authentication.Forms" version="0.23.2" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="6.0.6" targetFramework="net40" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net40" />
|
||||
<package id="NLog" version="4.3.0-rc1" targetFramework="net40" />
|
||||
<package id="ValueInjecter" version="2.3.3" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -46,12 +46,12 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.0-rc1\lib\net40\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
|
||||
<Reference Include="nunit.framework, Version=3.2.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.2.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<package id="FluentAssertions" version="4.2.1" targetFramework="net40" />
|
||||
<package id="Moq" version="4.0.10827" />
|
||||
<package id="NBuilder" version="3.0.1.1" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net40" />
|
||||
<package id="NUnit" version="2.6.3" targetFramework="net40" />
|
||||
<package id="NLog" version="4.3.0-rc1" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.2.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -47,12 +47,12 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.0-rc1\lib\net40\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
|
||||
<Reference Include="nunit.framework, Version=3.2.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.2.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="FluentAssertions" version="4.2.1" targetFramework="net40" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net40" />
|
||||
<package id="NUnit" version="2.6.3" targetFramework="net40" />
|
||||
<package id="NLog" version="4.3.0-rc1" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.2.0" targetFramework="net40" />
|
||||
<package id="Selenium.Support" version="2.48.0" targetFramework="net40" />
|
||||
<package id="Selenium.WebDriver" version="2.48.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -89,7 +89,7 @@ namespace NzbDrone.Common.Test.CacheTests
|
||||
int hitCount = 0;
|
||||
_cachedString = new Cached<string>();
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
_cachedString.Get("key", () =>
|
||||
{
|
||||
@@ -97,7 +97,7 @@ namespace NzbDrone.Common.Test.CacheTests
|
||||
return null;
|
||||
}, TimeSpan.FromMilliseconds(300));
|
||||
|
||||
Thread.Sleep(10);
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
hitCount.Should().BeInRange(3, 6);
|
||||
|
||||
@@ -58,18 +58,20 @@ namespace NzbDrone.Common.Test.Http
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Url.Should().Be(request.Url.ToString());
|
||||
response.Resource.Url.Should().Be(request.Url.FullUri);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_simple_post()
|
||||
{
|
||||
var message = "{ my: 1 }";
|
||||
|
||||
var request = new HttpRequest("http://eu.httpbin.org/post");
|
||||
request.Body = "{ my: 1 }";
|
||||
request.SetContent(message);
|
||||
|
||||
var response = Subject.Post<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Data.Should().Be(request.Body);
|
||||
response.Resource.Data.Should().Be(message);
|
||||
}
|
||||
|
||||
[TestCase("gzip")]
|
||||
@@ -162,7 +164,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
public void should_send_cookie()
|
||||
{
|
||||
var request = new HttpRequest("http://eu.httpbin.org/get");
|
||||
request.AddCookie("my", "cookie");
|
||||
request.Cookies["my"] = "cookie";
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
@@ -176,7 +178,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
public void GivenOldCookie()
|
||||
{
|
||||
var oldRequest = new HttpRequest("http://eu.httpbin.org/get");
|
||||
oldRequest.AddCookie("my", "cookie");
|
||||
oldRequest.Cookies["my"] = "cookie";
|
||||
|
||||
var oldClient = new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<ICacheManager>(), Mocker.Resolve<IRateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), Mocker.Resolve<Logger>());
|
||||
|
||||
@@ -260,7 +262,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
var requestSet = new HttpRequest("http://eu.httpbin.org/cookies/set?my=cookie");
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
requestSet.AddCookie("my", "oldcookie");
|
||||
requestSet.Cookies["my"] = "oldcookie";
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
|
||||
@@ -322,10 +324,10 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
// the date is bad in the below - should be 13-Jul-2016
|
||||
string malformedCookie = @"__cfduid=d29e686a9d65800021c66faca0a29b4261436890790; expires=Wed, 13-Jul-16 16:19:50 GMT; path=/; HttpOnly";
|
||||
string url = "http://eu.httpbin.org/response-headers?Set-Cookie=" +
|
||||
System.Uri.EscapeUriString(malformedCookie);
|
||||
var requestSet = new HttpRequestBuilder("http://eu.httpbin.org/response-headers")
|
||||
.AddQueryParam("Set-Cookie", malformedCookie)
|
||||
.Build();
|
||||
|
||||
var requestSet = new HttpRequest(url);
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
|
||||
@@ -376,6 +378,21 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public void should_submit_formparameters_in_body()
|
||||
{
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
public void should_submit_attachments_as_multipart()
|
||||
{
|
||||
Assert.Fail();
|
||||
}
|
||||
|
||||
public void should_submit_formparameters_as_multipart_if_attachments_exist()
|
||||
{
|
||||
Assert.Fail();
|
||||
}
|
||||
}
|
||||
|
||||
public class HttpBinResource
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Test.Common;
|
||||
@@ -8,14 +9,32 @@ namespace NzbDrone.Common.Test.Http
|
||||
[TestFixture]
|
||||
public class HttpRequestBuilderFixture : TestBase
|
||||
{
|
||||
[TestCase("http://host/{seg}/some", "http://host/dir/some")]
|
||||
[TestCase("http://host/some/{seg}", "http://host/some/dir")]
|
||||
public void should_add_single_segment_url_segments(string url, string result)
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder(url);
|
||||
|
||||
requestBuilder.SetSegment("seg", "dir");
|
||||
|
||||
requestBuilder.Build().Url.Should().Be(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void shouldnt_add_value_for_nonexisting_segment()
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder("http://host/{seg}/some");
|
||||
Assert.Throws<InvalidOperationException>(() => requestBuilder.SetSegment("seg2", "dir"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_remove_duplicated_slashes()
|
||||
{
|
||||
var builder = new HttpRequestBuilder("http://domain/");
|
||||
|
||||
var request = builder.Build("/v1/");
|
||||
var request = builder.Resource("/v1/").Build();
|
||||
|
||||
request.Url.ToString().Should().Be("http://domain/v1/");
|
||||
request.Url.FullUri.Should().Be("http://domain/v1/");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,22 +8,5 @@ namespace NzbDrone.Common.Test.Http
|
||||
[TestFixture]
|
||||
public class HttpRequestFixture
|
||||
{
|
||||
[TestCase("http://host/{seg}/some", "http://host/dir/some")]
|
||||
[TestCase("http://host/some/{seg}", "http://host/some/dir")]
|
||||
public void should_add_single_segment_url_segments(string url, string result)
|
||||
{
|
||||
var request = new HttpRequest(url);
|
||||
|
||||
request.AddSegment("seg", "dir");
|
||||
|
||||
request.Url.Should().Be(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void shouldnt_add_value_for_nonexisting_segment()
|
||||
{
|
||||
var request = new HttpRequest("http://host/{seg}/some");
|
||||
Assert.Throws<InvalidOperationException>(() => request.AddSegment("seg2", "dir"));
|
||||
}
|
||||
}
|
||||
}
|
||||
84
src/NzbDrone.Common.Test/Http/HttpUriFixture.cs
Normal file
84
src/NzbDrone.Common.Test/Http/HttpUriFixture.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
public class HttpUriFixture : TestBase
|
||||
{
|
||||
[TestCase("", "", "")]
|
||||
[TestCase("/", "", "/")]
|
||||
[TestCase("base", "", "base")]
|
||||
[TestCase("/base", "", "/base")]
|
||||
[TestCase("/base/", "", "/base/")]
|
||||
[TestCase("", "relative", "relative")]
|
||||
[TestCase("", "/relative", "/relative")]
|
||||
[TestCase("/", "relative", "/relative")]
|
||||
[TestCase("/", "/relative", "/relative")]
|
||||
[TestCase("base", "relative", "relative")]
|
||||
[TestCase("base", "/relative", "/relative")]
|
||||
[TestCase("/base", "relative", "/relative")]
|
||||
[TestCase("/base", "/relative", "/relative")]
|
||||
[TestCase("/base/", "relative", "/base/relative")]
|
||||
[TestCase("/base/", "/relative", "/relative")]
|
||||
[TestCase("base/sub", "relative", "base/relative")]
|
||||
[TestCase("base/sub", "/relative", "/relative")]
|
||||
[TestCase("/base/sub", "relative", "/base/relative")]
|
||||
[TestCase("/base/sub", "/relative", "/relative")]
|
||||
[TestCase("/base/sub/", "relative", "/base/sub/relative")]
|
||||
[TestCase("/base/sub/", "/relative", "/relative")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml", "relative/path", "abc://host.com:8080/root/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml", "/relative/path", "abc://host.com:8080/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml?query=1#fragment", "relative/path", "abc://host.com:8080/root/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml?query=1#fragment", "/relative/path", "abc://host.com:8080/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api", "relative/path", "abc://host.com:8080/root/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api", "/relative/path", "abc://host.com:8080/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api/", "relative/path", "abc://host.com:8080/root/api/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api/", "/relative/path", "abc://host.com:8080/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api/", "//otherhost.com/path", "abc://otherhost.com/path")]
|
||||
public void should_combine_uri(string basePath, string relativePath, string expected)
|
||||
{
|
||||
var newUri = new HttpUri(basePath) + new HttpUri(relativePath);
|
||||
newUri.FullUri.Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestCase("", "", "")]
|
||||
[TestCase("/", "", "/")]
|
||||
[TestCase("base", "", "base")]
|
||||
[TestCase("/base", "", "/base")]
|
||||
[TestCase("/base/", "", "/base/")]
|
||||
[TestCase("", "relative", "relative")]
|
||||
[TestCase("", "/relative", "/relative")]
|
||||
[TestCase("/", "relative", "/relative")]
|
||||
[TestCase("/", "/relative", "/relative")]
|
||||
[TestCase("base", "relative", "base/relative")]
|
||||
[TestCase("base", "/relative", "base/relative")]
|
||||
[TestCase("/base", "relative", "/base/relative")]
|
||||
[TestCase("/base", "/relative", "/base/relative")]
|
||||
[TestCase("/base/", "relative", "/base/relative")]
|
||||
[TestCase("/base/", "/relative", "/base/relative")]
|
||||
[TestCase("base/sub", "relative", "base/sub/relative")]
|
||||
[TestCase("base/sub", "/relative", "base/sub/relative")]
|
||||
[TestCase("/base/sub", "relative", "/base/sub/relative")]
|
||||
[TestCase("/base/sub", "/relative", "/base/sub/relative")]
|
||||
[TestCase("/base/sub/", "relative", "/base/sub/relative")]
|
||||
[TestCase("/base/sub/", "/relative", "/base/sub/relative")]
|
||||
[TestCase("/base/sub/", "relative/", "/base/sub/relative/")]
|
||||
[TestCase("/base/sub/", "/relative/", "/base/sub/relative/")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml", "relative/path", "abc://host.com:8080/root/file.xml/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml", "/relative/path", "abc://host.com:8080/root/file.xml/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml?query=1#fragment", "relative/path", "abc://host.com:8080/root/file.xml/relative/path?query=1#fragment")]
|
||||
[TestCase("abc://host.com:8080/root/file.xml?query=1#fragment", "/relative/path", "abc://host.com:8080/root/file.xml/relative/path?query=1#fragment")]
|
||||
[TestCase("abc://host.com:8080/root/api", "relative/path", "abc://host.com:8080/root/api/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api", "/relative/path", "abc://host.com:8080/root/api/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api/", "relative/path", "abc://host.com:8080/root/api/relative/path")]
|
||||
[TestCase("abc://host.com:8080/root/api/", "/relative/path", "abc://host.com:8080/root/api/relative/path")]
|
||||
public void should_combine_relative_path(string basePath, string relativePath, string expected)
|
||||
{
|
||||
var newUri = new HttpUri(basePath).CombinePath(relativePath);
|
||||
|
||||
newUri.FullUri.Should().Be(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,12 +46,12 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.0-rc1\lib\net40\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
|
||||
<Reference Include="nunit.framework, Version=3.2.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.2.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
@@ -83,6 +83,7 @@
|
||||
<Compile Include="Http\HttpClientFixture.cs" />
|
||||
<Compile Include="Http\HttpRequestBuilderFixture.cs" />
|
||||
<Compile Include="Http\HttpRequestFixture.cs" />
|
||||
<Compile Include="Http\HttpUriFixture.cs" />
|
||||
<Compile Include="InstrumentationTests\CleanseLogMessageFixture.cs" />
|
||||
<Compile Include="LevenshteinDistanceFixture.cs" />
|
||||
<Compile Include="OsPathFixture.cs" />
|
||||
|
||||
@@ -6,6 +6,7 @@ using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Test.Common.Categories;
|
||||
|
||||
namespace NzbDrone.Common.Test
|
||||
{
|
||||
@@ -153,7 +154,7 @@ namespace NzbDrone.Common.Test
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore]
|
||||
[Ignore("Parent, not Grandparent")]
|
||||
public void should_not_be_parent_when_it_is_grandparent()
|
||||
{
|
||||
var path = Path.Combine(_parent, "parent", "child");
|
||||
@@ -205,7 +206,7 @@ namespace NzbDrone.Common.Test
|
||||
public void get_actual_casing_should_return_actual_casing_for_local_dir_in_windows()
|
||||
{
|
||||
WindowsOnly();
|
||||
var path = Directory.GetCurrentDirectory().Replace("c:\\","C:\\");
|
||||
var path = Directory.GetCurrentDirectory().Replace("c:\\","C:\\").Replace("system32", "System32");
|
||||
|
||||
path.ToUpper().GetActualCasing().Should().Be(path);
|
||||
path.ToLower().GetActualCasing().Should().Be(path);
|
||||
@@ -222,6 +223,7 @@ namespace NzbDrone.Common.Test
|
||||
|
||||
[Test]
|
||||
[Explicit]
|
||||
[ManualTest]
|
||||
public void get_actual_casing_should_return_original_casing_for_shares()
|
||||
{
|
||||
var path = @"\\server\Pool\Apps";
|
||||
|
||||
@@ -31,10 +31,20 @@ namespace NzbDrone.Common.Test
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Process.GetProcessesByName(DummyApp.DUMMY_PROCCESS_NAME).ToList().ForEach(c => c.Kill());
|
||||
Process.GetProcessesByName(DummyApp.DUMMY_PROCCESS_NAME).ToList().ForEach(c =>
|
||||
{
|
||||
try
|
||||
{
|
||||
c.Kill();
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
TestLogger.Warn(ex, "{0} when killing process", ex.Message);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void GetById_should_return_null_if_process_doesnt_exist()
|
||||
{
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
using System.ServiceProcess;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Test.Common.Categories;
|
||||
|
||||
namespace NzbDrone.Common.Test
|
||||
{
|
||||
@@ -13,7 +15,6 @@ namespace NzbDrone.Common.Test
|
||||
private const string ALWAYS_INSTALLED_SERVICE = "SCardSvr"; //Smart Card
|
||||
private const string TEMP_SERVICE_NAME = "NzbDrone_Nunit";
|
||||
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
@@ -24,8 +25,10 @@ namespace NzbDrone.Common.Test
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
WindowsOnly();
|
||||
CleanupService();
|
||||
if (OsInfo.IsWindows)
|
||||
{
|
||||
CleanupService();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +73,7 @@ namespace NzbDrone.Common.Test
|
||||
|
||||
[Test]
|
||||
[Explicit]
|
||||
[ManualTest]
|
||||
public void UnInstallService()
|
||||
{
|
||||
Subject.UnInstall(ServiceProvider.NZBDRONE_SERVICE_NAME);
|
||||
@@ -78,6 +82,7 @@ namespace NzbDrone.Common.Test
|
||||
|
||||
[Test]
|
||||
[Explicit]
|
||||
[ManualTest]
|
||||
public void Should_be_able_to_start_and_stop_service()
|
||||
{
|
||||
Subject.GetService(ALWAYS_INSTALLED_SERVICE).Status
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace NzbDrone.Common.Test.TPLTests
|
||||
|
||||
|
||||
[Test]
|
||||
[Retry(3)]
|
||||
public void should_hold_the_call_for_debounce_duration()
|
||||
{
|
||||
var counter = new Counter();
|
||||
@@ -40,6 +41,7 @@ namespace NzbDrone.Common.Test.TPLTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Retry(3)]
|
||||
public void should_throttle_calls()
|
||||
{
|
||||
var counter = new Counter();
|
||||
@@ -65,6 +67,7 @@ namespace NzbDrone.Common.Test.TPLTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Retry(3)]
|
||||
public void should_hold_the_call_while_paused()
|
||||
{
|
||||
var counter = new Counter();
|
||||
@@ -98,6 +101,7 @@ namespace NzbDrone.Common.Test.TPLTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Retry(3)]
|
||||
public void should_handle_pause_reentrancy()
|
||||
{
|
||||
var counter = new Counter();
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace NzbDrone.Common.Test.TPLTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Retry(3)]
|
||||
public void should_wait_for_existing()
|
||||
{
|
||||
GivenExisting("me", _epoch + TimeSpan.FromMilliseconds(200));
|
||||
@@ -70,7 +71,7 @@ namespace NzbDrone.Common.Test.TPLTests
|
||||
Subject.WaitAndPulse("me", TimeSpan.FromMilliseconds(400));
|
||||
watch.Stop();
|
||||
|
||||
watch.ElapsedMilliseconds.Should().BeInRange(195, 250);
|
||||
watch.ElapsedMilliseconds.Should().BeInRange(175, 250);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
<packages>
|
||||
<package id="FluentAssertions" version="4.2.1" targetFramework="net40" />
|
||||
<package id="Moq" version="4.0.10827" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net40" />
|
||||
<package id="NUnit" version="2.6.3" targetFramework="net40" />
|
||||
<package id="NLog" version="4.3.0-rc1" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.2.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -1,19 +0,0 @@
|
||||
using NzbDrone.Common.Http;
|
||||
|
||||
namespace NzbDrone.Common.Cloud
|
||||
{
|
||||
public interface IDroneServicesRequestBuilder
|
||||
{
|
||||
HttpRequest Build(string path);
|
||||
}
|
||||
|
||||
public class DroneServicesHttpRequestBuilder : HttpRequestBuilder, IDroneServicesRequestBuilder
|
||||
{
|
||||
private const string ROOT_URL = "http://services.sonarr.tv/v1/";
|
||||
|
||||
public DroneServicesHttpRequestBuilder()
|
||||
: base(ROOT_URL)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs
Normal file
28
src/NzbDrone.Common/Cloud/SonarrCloudRequestBuilder.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using NzbDrone.Common.Http;
|
||||
|
||||
namespace NzbDrone.Common.Cloud
|
||||
{
|
||||
public interface ISonarrCloudRequestBuilder
|
||||
{
|
||||
IHttpRequestBuilderFactory Services { get; }
|
||||
IHttpRequestBuilderFactory SkyHookTvdb { get; }
|
||||
}
|
||||
|
||||
public class SonarrCloudRequestBuilder : ISonarrCloudRequestBuilder
|
||||
{
|
||||
public SonarrCloudRequestBuilder()
|
||||
{
|
||||
Services = new HttpRequestBuilder("http://services.sonarr.tv/v1/")
|
||||
.CreateFactory();
|
||||
|
||||
SkyHookTvdb = new HttpRequestBuilder("http://skyhook.sonarr.tv/v1/tvdb/{route}/{language}/")
|
||||
.SetSegment("language", "en")
|
||||
.CreateFactory();
|
||||
}
|
||||
|
||||
public IHttpRequestBuilderFactory Services { get; private set; }
|
||||
|
||||
public IHttpRequestBuilderFactory SkyHookTvdb { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,16 @@ namespace NzbDrone.Common.Crypto
|
||||
|
||||
public static int GetHashInt31(string target)
|
||||
{
|
||||
byte[] hash;
|
||||
var hash = GetHash(target);
|
||||
return BitConverter.ToInt32(hash, 0) & 0x7fffffff;
|
||||
}
|
||||
|
||||
public static byte[] GetHash(string target)
|
||||
{
|
||||
lock (Sha1)
|
||||
{
|
||||
hash = Sha1.ComputeHash(Encoding.Default.GetBytes(target));
|
||||
return Sha1.ComputeHash(Encoding.Default.GetBytes(target));
|
||||
}
|
||||
return BitConverter.ToInt32(hash, 0) & 0x7fffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,18 +293,30 @@ namespace NzbDrone.Common.Disk
|
||||
var sid = new SecurityIdentifier(accountSid, null);
|
||||
|
||||
var directoryInfo = new DirectoryInfo(filename);
|
||||
var directorySecurity = directoryInfo.GetAccessControl();
|
||||
var directorySecurity = directoryInfo.GetAccessControl(AccessControlSections.Access);
|
||||
|
||||
var rules = directorySecurity.GetAccessRules(true, false, typeof(SecurityIdentifier));
|
||||
|
||||
if (rules.OfType<FileSystemAccessRule>().Any(acl => acl.AccessControlType == controlType && (acl.FileSystemRights & rights) == rights && acl.IdentityReference.Equals(sid)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var accessRule = new FileSystemAccessRule(sid, rights,
|
||||
InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
|
||||
PropagationFlags.None, controlType);
|
||||
PropagationFlags.InheritOnly, controlType);
|
||||
|
||||
directorySecurity.AddAccessRule(accessRule);
|
||||
directoryInfo.SetAccessControl(directorySecurity);
|
||||
bool modified;
|
||||
directorySecurity.ModifyAccessRule(AccessControlModification.Add, accessRule, out modified);
|
||||
|
||||
if (modified)
|
||||
{
|
||||
directoryInfo.SetAccessControl(directorySecurity);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn(e, string.Format("Couldn't set permission for {0}. account:{1} rights:{2} accessControlType:{3}", filename, accountSid, rights, controlType));
|
||||
Logger.Warn(e, "Couldn't set permission for {0}. account:{1} rights:{2} accessControlType:{3}", filename, accountSid, rights, controlType);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace NzbDrone.Common.EnvironmentInfo
|
||||
{
|
||||
try
|
||||
{
|
||||
_diskProvider.SetPermissions(_appFolderInfo.AppDataFolder, WellKnownSidType.WorldSid, FileSystemRights.FullControl, AccessControlType.Allow);
|
||||
_diskProvider.SetPermissions(_appFolderInfo.AppDataFolder, WellKnownSidType.WorldSid, FileSystemRights.Modify, AccessControlType.Allow);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace NzbDrone.Common.Extensions
|
||||
|
||||
public static void Add<TKey, TValue>(this ICollection<KeyValuePair<TKey, TValue>> collection, TKey key, TValue value)
|
||||
{
|
||||
collection.Add(key, value);
|
||||
collection.Add(new KeyValuePair<TKey, TValue>(key, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,5 +100,10 @@ namespace NzbDrone.Common.Extensions
|
||||
.Select(x => Convert.ToByte(input.Substring(x, 2), 16))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public static string ToHexString(this byte[] input)
|
||||
{
|
||||
return string.Concat(Array.ConvertAll(input, x => x.ToString("X2")));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
using System.Reflection;
|
||||
|
||||
namespace NzbDrone.Common.Http.Dispatchers
|
||||
{
|
||||
@@ -21,6 +22,21 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
|
||||
private static readonly Logger _logger = NzbDroneLogger.GetLogger(typeof(CurlHttpDispatcher));
|
||||
|
||||
private const string _caBundleFileName = "curl-ca-bundle.crt";
|
||||
private static readonly string _caBundleFilePath;
|
||||
|
||||
static CurlHttpDispatcher()
|
||||
{
|
||||
if (Assembly.GetExecutingAssembly().Location.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
_caBundleFilePath = Path.Combine(Assembly.GetExecutingAssembly().Location, "..", _caBundleFileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
_caBundleFilePath = _caBundleFileName;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CheckAvailability()
|
||||
{
|
||||
try
|
||||
@@ -41,11 +57,6 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
throw new ApplicationException("Curl failed to initialize.");
|
||||
}
|
||||
|
||||
if (request.NetworkCredential != null)
|
||||
{
|
||||
throw new NotImplementedException("Credentials not supported for curl dispatcher.");
|
||||
}
|
||||
|
||||
lock (CurlGlobalHandle.Instance)
|
||||
{
|
||||
Stream responseStream = new MemoryStream();
|
||||
@@ -64,8 +75,8 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
headerStream.Write(b, 0, s * n);
|
||||
return s * n;
|
||||
};
|
||||
|
||||
curlEasy.Url = request.Url.AbsoluteUri;
|
||||
|
||||
curlEasy.Url = request.Url.FullUri;
|
||||
switch (request.Method)
|
||||
{
|
||||
case HttpMethod.GET:
|
||||
@@ -86,21 +97,25 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
curlEasy.UserAgent = UserAgentBuilder.UserAgent;
|
||||
curlEasy.FollowLocation = request.AllowAutoRedirect;
|
||||
|
||||
if (request.RequestTimeout != TimeSpan.Zero)
|
||||
{
|
||||
curlEasy.Timeout = (int)Math.Ceiling(request.RequestTimeout.TotalSeconds);
|
||||
}
|
||||
|
||||
if (OsInfo.IsWindows)
|
||||
{
|
||||
curlEasy.CaInfo = "curl-ca-bundle.crt";
|
||||
curlEasy.CaInfo = _caBundleFilePath;
|
||||
}
|
||||
|
||||
if (cookies != null)
|
||||
{
|
||||
curlEasy.Cookie = cookies.GetCookieHeader(request.Url);
|
||||
curlEasy.Cookie = cookies.GetCookieHeader((Uri)request.Url);
|
||||
}
|
||||
|
||||
if (!request.Body.IsNullOrWhiteSpace())
|
||||
if (request.ContentData != null)
|
||||
{
|
||||
// TODO: This might not go well with encoding.
|
||||
curlEasy.PostFieldSize = request.Body.Length;
|
||||
curlEasy.SetOpt(CurlOption.CopyPostFields, request.Body);
|
||||
curlEasy.PostFieldSize = request.ContentData.Length;
|
||||
curlEasy.SetOpt(CurlOption.CopyPostFields, new string(Array.ConvertAll(request.ContentData, v => (char)v)));
|
||||
}
|
||||
|
||||
// Yes, we have to keep a ref to the object to prevent corrupting the unmanaged state
|
||||
@@ -175,7 +190,7 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
{
|
||||
try
|
||||
{
|
||||
cookies.SetCookies(request.Url, FixSetCookieHeader(setCookie));
|
||||
cookies.SetCookies((Uri)request.Url, FixSetCookieHeader(setCookie));
|
||||
}
|
||||
catch (CookieException ex)
|
||||
{
|
||||
|
||||
@@ -8,34 +8,35 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
{
|
||||
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
|
||||
{
|
||||
var webRequest = (HttpWebRequest)WebRequest.Create(request.Url);
|
||||
var webRequest = (HttpWebRequest)WebRequest.Create((Uri)request.Url);
|
||||
|
||||
// Deflate is not a standard and could break depending on implementation.
|
||||
// we should just stick with the more compatible Gzip
|
||||
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
|
||||
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
|
||||
|
||||
webRequest.Credentials = request.NetworkCredential;
|
||||
webRequest.Method = request.Method.ToString();
|
||||
webRequest.UserAgent = UserAgentBuilder.UserAgent;
|
||||
webRequest.KeepAlive = false;
|
||||
webRequest.KeepAlive = request.ConnectionKeepAlive;
|
||||
webRequest.AllowAutoRedirect = request.AllowAutoRedirect;
|
||||
webRequest.ContentLength = 0;
|
||||
webRequest.CookieContainer = cookies;
|
||||
|
||||
if (request.RequestTimeout != TimeSpan.Zero)
|
||||
{
|
||||
webRequest.Timeout = (int)Math.Ceiling(request.RequestTimeout.TotalMilliseconds);
|
||||
}
|
||||
|
||||
if (request.Headers != null)
|
||||
{
|
||||
AddRequestHeaders(webRequest, request.Headers);
|
||||
}
|
||||
|
||||
if (!request.Body.IsNullOrWhiteSpace())
|
||||
if (request.ContentData != null)
|
||||
{
|
||||
var bytes = request.Headers.GetEncodingFromContentType().GetBytes(request.Body.ToCharArray());
|
||||
|
||||
webRequest.ContentLength = bytes.Length;
|
||||
webRequest.ContentLength = request.ContentData.Length;
|
||||
using (var writeStream = webRequest.GetRequestStream())
|
||||
{
|
||||
writeStream.Write(bytes, 0, bytes.Length);
|
||||
writeStream.Write(request.ContentData, 0, request.ContentData.Length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,43 +76,43 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
switch (header.Key)
|
||||
{
|
||||
case "Accept":
|
||||
webRequest.Accept = header.Value.ToString();
|
||||
webRequest.Accept = header.Value;
|
||||
break;
|
||||
case "Connection":
|
||||
webRequest.Connection = header.Value.ToString();
|
||||
webRequest.Connection = header.Value;
|
||||
break;
|
||||
case "Content-Length":
|
||||
webRequest.ContentLength = Convert.ToInt64(header.Value);
|
||||
break;
|
||||
case "Content-Type":
|
||||
webRequest.ContentType = header.Value.ToString();
|
||||
webRequest.ContentType = header.Value;
|
||||
break;
|
||||
case "Date":
|
||||
webRequest.Date = (DateTime)header.Value;
|
||||
webRequest.Date = HttpHeader.ParseDateTime(header.Value);
|
||||
break;
|
||||
case "Expect":
|
||||
webRequest.Expect = header.Value.ToString();
|
||||
webRequest.Expect = header.Value;
|
||||
break;
|
||||
case "Host":
|
||||
webRequest.Host = header.Value.ToString();
|
||||
webRequest.Host = header.Value;
|
||||
break;
|
||||
case "If-Modified-Since":
|
||||
webRequest.IfModifiedSince = (DateTime)header.Value;
|
||||
webRequest.IfModifiedSince = HttpHeader.ParseDateTime(header.Value);
|
||||
break;
|
||||
case "Range":
|
||||
throw new NotImplementedException();
|
||||
case "Referer":
|
||||
webRequest.Referer = header.Value.ToString();
|
||||
webRequest.Referer = header.Value;
|
||||
break;
|
||||
case "Transfer-Encoding":
|
||||
webRequest.TransferEncoding = header.Value.ToString();
|
||||
webRequest.TransferEncoding = header.Value;
|
||||
break;
|
||||
case "User-Agent":
|
||||
throw new NotSupportedException("User-Agent other than Sonarr not allowed.");
|
||||
case "Proxy-Connection":
|
||||
throw new NotImplementedException();
|
||||
default:
|
||||
webRequest.Headers.Add(header.Key, header.Value.ToString());
|
||||
webRequest.Headers.Add(header.Key, header.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace NzbDrone.Common.Http
|
||||
{
|
||||
public sealed class HttpAccept
|
||||
{
|
||||
public static readonly HttpAccept Rss = new HttpAccept("application/rss+xml, text/rss+xml, text/xml");
|
||||
public static readonly HttpAccept Rss = new HttpAccept("application/rss+xml, text/rss+xml, application/xml, text/xml");
|
||||
public static readonly HttpAccept Json = new HttpAccept("application/json");
|
||||
public static readonly HttpAccept Html = new HttpAccept("text/html");
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http.Dispatchers;
|
||||
using NzbDrone.Common.TPL;
|
||||
|
||||
@@ -72,13 +74,18 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
stopWatch.Stop();
|
||||
|
||||
_logger.Trace("{0} ({1:n0} ms)", response, stopWatch.ElapsedMilliseconds);
|
||||
_logger.Trace("{0} ({1} ms)", response, stopWatch.ElapsedMilliseconds);
|
||||
|
||||
foreach (var interceptor in _requestInterceptors)
|
||||
{
|
||||
response = interceptor.PostResponse(response);
|
||||
}
|
||||
|
||||
if (request.LogResponseContent)
|
||||
{
|
||||
_logger.Trace("Response content ({0} bytes): {1}", response.ResponseData.Length, response.Content);
|
||||
}
|
||||
|
||||
if (!RuntimeInfoBase.IsProduction &&
|
||||
(response.StatusCode == HttpStatusCode.Moved ||
|
||||
response.StatusCode == HttpStatusCode.MovedPermanently ||
|
||||
@@ -123,7 +130,7 @@ namespace NzbDrone.Common.Http
|
||||
}
|
||||
}
|
||||
|
||||
var requestCookies = persistentCookieContainer.GetCookies(request.Url);
|
||||
var requestCookies = persistentCookieContainer.GetCookies((Uri)request.Url);
|
||||
|
||||
var cookieContainer = new CookieContainer();
|
||||
|
||||
@@ -144,7 +151,7 @@ namespace NzbDrone.Common.Http
|
||||
{
|
||||
var persistentCookieContainer = _cookieContainerCache.Get("container", () => new CookieContainer());
|
||||
|
||||
var cookies = cookieContainer.GetCookies(request.Url);
|
||||
var cookies = cookieContainer.GetCookies((Uri)request.Url);
|
||||
|
||||
persistentCookieContainer.Add(cookies);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace NzbDrone.Common.Http
|
||||
public HttpResponse Response { get; private set; }
|
||||
|
||||
public HttpException(HttpRequest request, HttpResponse response)
|
||||
: base(string.Format("HTTP request failed: [{0}:{1}] [{2}] at [{3}]", (int)response.StatusCode, response.StatusCode, request.Method, request.Url.ToString()))
|
||||
: base(string.Format("HTTP request failed: [{0}:{1}] [{2}] at [{3}]", (int)response.StatusCode, response.StatusCode, request.Method, request.Url))
|
||||
{
|
||||
Request = request;
|
||||
Response = response;
|
||||
|
||||
14
src/NzbDrone.Common/Http/HttpFormData.cs
Normal file
14
src/NzbDrone.Common/Http/HttpFormData.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NzbDrone.Common.Http
|
||||
{
|
||||
public class HttpFormData
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string FileName { get; set; }
|
||||
public byte[] ContentData { get; set; }
|
||||
public string ContentType { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,37 +4,92 @@ using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
|
||||
namespace NzbDrone.Common.Http
|
||||
{
|
||||
public class HttpHeader : Dictionary<string, object>
|
||||
public class HttpHeader : NameValueCollection, IEnumerable<KeyValuePair<string, string>>, IEnumerable
|
||||
{
|
||||
public HttpHeader(NameValueCollection headers) : base(StringComparer.OrdinalIgnoreCase)
|
||||
public HttpHeader(NameValueCollection headers)
|
||||
: base(headers)
|
||||
{
|
||||
foreach (var key in headers.AllKeys)
|
||||
|
||||
}
|
||||
|
||||
public HttpHeader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool ContainsKey(string key)
|
||||
{
|
||||
key = key.ToLowerInvariant();
|
||||
return AllKeys.Any(v => v.ToLowerInvariant() == key);
|
||||
}
|
||||
|
||||
public string GetSingleValue(string key)
|
||||
{
|
||||
var values = GetValues(key);
|
||||
if (values == null || values.Length == 0)
|
||||
{
|
||||
this[key] = headers[key];
|
||||
return null;
|
||||
}
|
||||
if (values.Length > 1)
|
||||
{
|
||||
throw new ApplicationException(string.Format("Expected {0} to occur only once.", key));
|
||||
}
|
||||
|
||||
return values[0];
|
||||
}
|
||||
|
||||
protected T? GetSingleValue<T>(string key, Func<string, T> converter) where T : struct
|
||||
{
|
||||
var value = GetSingleValue(key);
|
||||
if (value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return converter(value);
|
||||
}
|
||||
protected void SetSingleValue(string key, string value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
Set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public HttpHeader() : base(StringComparer.OrdinalIgnoreCase)
|
||||
protected void SetSingleValue<T>(string key, T? value, Func<T, string> converter = null) where T : struct
|
||||
{
|
||||
|
||||
if (!value.HasValue)
|
||||
{
|
||||
Remove(key);
|
||||
}
|
||||
else if (converter != null)
|
||||
{
|
||||
Set(key, converter(value.Value));
|
||||
}
|
||||
else
|
||||
{
|
||||
Set(key, value.Value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public long? ContentLength
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!ContainsKey("Content-Length"))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return Convert.ToInt64(this["Content-Length"]);
|
||||
return GetSingleValue("Content-Length", Convert.ToInt64);
|
||||
}
|
||||
set
|
||||
{
|
||||
this["Content-Length"] = value;
|
||||
SetSingleValue("Content-Length", value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,15 +97,11 @@ namespace NzbDrone.Common.Http
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!ContainsKey("Content-Type"))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return this["Content-Type"].ToString();
|
||||
return GetSingleValue("Content-Type");
|
||||
}
|
||||
set
|
||||
{
|
||||
this["Content-Type"] = value;
|
||||
SetSingleValue("Content-Type", value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,25 +109,36 @@ namespace NzbDrone.Common.Http
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!ContainsKey("Accept"))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return this["Accept"].ToString();
|
||||
return GetSingleValue("Accept");
|
||||
}
|
||||
set
|
||||
{
|
||||
this["Accept"] = value;
|
||||
SetSingleValue("Accept", value);
|
||||
}
|
||||
}
|
||||
|
||||
public new IEnumerator<KeyValuePair<string, string>> GetEnumerator()
|
||||
{
|
||||
return AllKeys.SelectMany(GetValues, (k, c) => new KeyValuePair<string, string>(k, c)).ToList().GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return base.GetEnumerator();
|
||||
}
|
||||
|
||||
public Encoding GetEncodingFromContentType()
|
||||
{
|
||||
return GetEncodingFromContentType(ContentType ?? string.Empty);
|
||||
}
|
||||
|
||||
public static Encoding GetEncodingFromContentType(string contentType)
|
||||
{
|
||||
Encoding encoding = null;
|
||||
|
||||
if (ContentType.IsNotNullOrWhiteSpace())
|
||||
if (contentType.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var charset = ContentType.ToLowerInvariant()
|
||||
var charset = contentType.ToLowerInvariant()
|
||||
.Split(';', '=', ' ')
|
||||
.SkipWhile(v => v != "charset")
|
||||
.Skip(1).FirstOrDefault();
|
||||
@@ -99,5 +161,18 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
return encoding;
|
||||
}
|
||||
|
||||
public static DateTime ParseDateTime(string value)
|
||||
{
|
||||
return DateTime.ParseExact(value, "R", CultureInfo.InvariantCulture.DateTimeFormat, DateTimeStyles.AssumeUniversal);
|
||||
}
|
||||
|
||||
public static List<KeyValuePair<string, string>> ParseCookies(string cookies)
|
||||
{
|
||||
return cookies.Split(';')
|
||||
.Select(v => v.Trim().Split('='))
|
||||
.Select(v => new KeyValuePair<string, string>(v[0], v[1]))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Common.Http
|
||||
{
|
||||
public class HttpRequest
|
||||
{
|
||||
private readonly Dictionary<string, string> _segments;
|
||||
|
||||
public HttpRequest(string url, HttpAccept httpAccept = null)
|
||||
{
|
||||
UriBuilder = new UriBuilder(url);
|
||||
Url = new HttpUri(url);
|
||||
Headers = new HttpHeader();
|
||||
_segments = new Dictionary<string, string>();
|
||||
AllowAutoRedirect = true;
|
||||
Cookies = new Dictionary<string, string>();
|
||||
|
||||
@@ -28,73 +28,54 @@ namespace NzbDrone.Common.Http
|
||||
}
|
||||
}
|
||||
|
||||
public UriBuilder UriBuilder { get; private set; }
|
||||
|
||||
public Uri Url
|
||||
{
|
||||
get
|
||||
{
|
||||
var uri = UriBuilder.Uri.ToString();
|
||||
|
||||
foreach (var segment in _segments)
|
||||
{
|
||||
uri = uri.Replace(segment.Key, segment.Value);
|
||||
}
|
||||
|
||||
return new Uri(uri);
|
||||
}
|
||||
}
|
||||
|
||||
public HttpUri Url { get; set; }
|
||||
public HttpMethod Method { get; set; }
|
||||
public HttpHeader Headers { get; set; }
|
||||
public string Body { get; set; }
|
||||
public NetworkCredential NetworkCredential { get; set; }
|
||||
public byte[] ContentData { get; set; }
|
||||
public string ContentSummary { get; set; }
|
||||
public bool SuppressHttpError { get; set; }
|
||||
public bool AllowAutoRedirect { get; set; }
|
||||
public bool ConnectionKeepAlive { get; set; }
|
||||
public bool LogResponseContent { get; set; }
|
||||
public Dictionary<string, string> Cookies { get; private set; }
|
||||
public bool StoreResponseCookie { get; set; }
|
||||
public TimeSpan RequestTimeout { get; set; }
|
||||
public TimeSpan RateLimit { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Body == null)
|
||||
return ToString();
|
||||
}
|
||||
|
||||
public string ToString(bool includeMethod = true, bool includeSummary = true)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
if (includeMethod)
|
||||
{
|
||||
return string.Format("Req: [{0}] {1}", Method, Url);
|
||||
builder.AppendFormat("Req: [{0}] ", Method);
|
||||
}
|
||||
|
||||
return string.Format("Req: [{0}] {1} {2} {3}", Method, Url, Environment.NewLine, Body);
|
||||
}
|
||||
builder.Append(Url);
|
||||
|
||||
public void AddSegment(string segment, string value)
|
||||
{
|
||||
var key = "{" + segment + "}";
|
||||
|
||||
if (!UriBuilder.Uri.ToString().Contains(key))
|
||||
if (includeSummary && ContentSummary.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
throw new InvalidOperationException("Segment " + key +" is not defined in Uri");
|
||||
builder.Append(": ");
|
||||
builder.Append(ContentSummary);
|
||||
}
|
||||
|
||||
_segments.Add(key, value);
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public void AddQueryParam(string segment, string value)
|
||||
public void SetContent(byte[] data)
|
||||
{
|
||||
UriBuilder.SetQueryParam(segment, value);
|
||||
ContentData = data;
|
||||
}
|
||||
|
||||
public void AddCookie(string key, string value)
|
||||
public void SetContent(string data)
|
||||
{
|
||||
Cookies[key] = value;
|
||||
}
|
||||
|
||||
public void AddCookie(string cookies)
|
||||
{
|
||||
foreach (var pair in cookies.Split(';'))
|
||||
{
|
||||
var split = pair.Split('=');
|
||||
|
||||
Cookies[split[0].Trim()] = split[1].Trim();
|
||||
}
|
||||
var encoding = HttpHeader.GetEncodingFromContentType(Headers.ContentType);
|
||||
ContentData = encoding.GetBytes(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Common.Http
|
||||
{
|
||||
public class HttpRequestBuilder
|
||||
{
|
||||
public Uri BaseUri { get; private set; }
|
||||
public bool SupressHttpError { get; set; }
|
||||
public HttpMethod Method { get; set; }
|
||||
public HttpAccept HttpAccept { get; set; }
|
||||
public HttpUri BaseUrl { get; private set; }
|
||||
public string ResourceUrl { get; set; }
|
||||
public List<KeyValuePair<string, string>> QueryParams { get; private set; }
|
||||
public List<KeyValuePair<string, string>> SuffixQueryParams { get; private set; }
|
||||
public Dictionary<string, string> Segments { get; private set; }
|
||||
public HttpHeader Headers { get; private set; }
|
||||
public bool SuppressHttpError { get; set; }
|
||||
public bool AllowAutoRedirect { get; set; }
|
||||
public bool ConnectionKeepAlive { get; set; }
|
||||
public bool LogResponseContent { get; set; }
|
||||
public NetworkCredential NetworkCredential { get; set; }
|
||||
public Dictionary<string, string> Cookies { get; private set; }
|
||||
public List<HttpFormData> FormData { get; private set; }
|
||||
|
||||
public Action<HttpRequest> PostProcess { get; set; }
|
||||
|
||||
public HttpRequestBuilder(string baseUri)
|
||||
public HttpRequestBuilder(string baseUrl)
|
||||
{
|
||||
BaseUri = new Uri(baseUri);
|
||||
BaseUrl = new HttpUri(baseUrl);
|
||||
ResourceUrl = string.Empty;
|
||||
Method = HttpMethod.GET;
|
||||
QueryParams = new List<KeyValuePair<string, string>>();
|
||||
SuffixQueryParams = new List<KeyValuePair<string, string>>();
|
||||
Segments = new Dictionary<string, string>();
|
||||
Headers = new HttpHeader();
|
||||
Cookies = new Dictionary<string, string>();
|
||||
FormData = new List<HttpFormData>();
|
||||
}
|
||||
|
||||
public virtual HttpRequest Build(string path)
|
||||
public HttpRequestBuilder(bool useHttps, string host, int port, string urlBase = null)
|
||||
: this(BuildBaseUrl(useHttps, host, port, urlBase))
|
||||
{
|
||||
if (BaseUri.ToString().EndsWith("/"))
|
||||
|
||||
}
|
||||
|
||||
public static string BuildBaseUrl(bool useHttps, string host, int port, string urlBase = null)
|
||||
{
|
||||
var protocol = useHttps ? "https" : "http";
|
||||
|
||||
if (urlBase.IsNotNullOrWhiteSpace() && !urlBase.StartsWith("/"))
|
||||
{
|
||||
path = path.TrimStart('/');
|
||||
urlBase = "/" + urlBase;
|
||||
}
|
||||
|
||||
var request = new HttpRequest(BaseUri + path)
|
||||
return string.Format("{0}://{1}:{2}{3}", protocol, host, port, urlBase);
|
||||
}
|
||||
|
||||
public virtual HttpRequestBuilder Clone()
|
||||
{
|
||||
var clone = MemberwiseClone() as HttpRequestBuilder;
|
||||
clone.QueryParams = new List<KeyValuePair<string, string>>(clone.QueryParams);
|
||||
clone.SuffixQueryParams = new List<KeyValuePair<string, string>>(clone.SuffixQueryParams);
|
||||
clone.Segments = new Dictionary<string, string>(clone.Segments);
|
||||
clone.Headers = new HttpHeader(clone.Headers);
|
||||
clone.Cookies = new Dictionary<string, string>(clone.Cookies);
|
||||
clone.FormData = new List<HttpFormData>(clone.FormData);
|
||||
return clone;
|
||||
}
|
||||
|
||||
protected virtual HttpUri CreateUri()
|
||||
{
|
||||
var url = BaseUrl.CombinePath(ResourceUrl).AddQueryParams(QueryParams.Concat(SuffixQueryParams));
|
||||
|
||||
if (Segments.Any())
|
||||
{
|
||||
SuppressHttpError = SupressHttpError,
|
||||
NetworkCredential = NetworkCredential
|
||||
};
|
||||
var fullUri = url.FullUri;
|
||||
|
||||
foreach (var segment in Segments)
|
||||
{
|
||||
fullUri = fullUri.Replace(segment.Key, segment.Value);
|
||||
}
|
||||
|
||||
url = new HttpUri(fullUri);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
protected virtual HttpRequest CreateRequest()
|
||||
{
|
||||
return new HttpRequest(CreateUri().FullUri, HttpAccept);
|
||||
}
|
||||
|
||||
protected virtual void Apply(HttpRequest request)
|
||||
{
|
||||
request.Method = Method;
|
||||
request.SuppressHttpError = SuppressHttpError;
|
||||
request.AllowAutoRedirect = AllowAutoRedirect;
|
||||
request.ConnectionKeepAlive = ConnectionKeepAlive;
|
||||
request.LogResponseContent = LogResponseContent;
|
||||
|
||||
if (NetworkCredential != null)
|
||||
{
|
||||
var authInfo = NetworkCredential.UserName + ":" + NetworkCredential.Password;
|
||||
authInfo = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(authInfo));
|
||||
request.Headers.Set("Authorization", "Basic " + authInfo);
|
||||
}
|
||||
|
||||
foreach (var header in Headers)
|
||||
{
|
||||
request.Headers.Set(header.Key, header.Value);
|
||||
}
|
||||
|
||||
foreach (var cookie in Cookies)
|
||||
{
|
||||
request.Cookies[cookie.Key] = cookie.Value;
|
||||
}
|
||||
|
||||
ApplyFormData(request);
|
||||
}
|
||||
|
||||
public virtual HttpRequest Build()
|
||||
{
|
||||
var request = CreateRequest();
|
||||
|
||||
Apply(request);
|
||||
|
||||
if (PostProcess != null)
|
||||
{
|
||||
@@ -36,5 +136,237 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
public IHttpRequestBuilderFactory CreateFactory()
|
||||
{
|
||||
return new HttpRequestBuilderFactory(this);
|
||||
}
|
||||
|
||||
protected virtual void ApplyFormData(HttpRequest request)
|
||||
{
|
||||
if (FormData.Empty()) return;
|
||||
|
||||
if (request.ContentData != null)
|
||||
{
|
||||
throw new ApplicationException("Cannot send HttpRequest Body and FormData simultaneously.");
|
||||
}
|
||||
|
||||
var shouldSendAsMultipart = FormData.Any(v => v.ContentType != null || v.FileName != null || v.ContentData.Length > 1024);
|
||||
|
||||
if (shouldSendAsMultipart)
|
||||
{
|
||||
var boundary = "-----------------------------" + DateTime.Now.Ticks.ToString("x14");
|
||||
var partBoundary = string.Format("--{0}\r\n", boundary);
|
||||
var endBoundary = string.Format("--{0}--\r\n", boundary);
|
||||
|
||||
var bodyStream = new MemoryStream();
|
||||
var summary = new StringBuilder();
|
||||
|
||||
using (var writer = new StreamWriter(bodyStream, new UTF8Encoding(false)))
|
||||
{
|
||||
foreach (var formData in FormData)
|
||||
{
|
||||
writer.Write(partBoundary);
|
||||
|
||||
writer.Write("Content-Disposition: form-data");
|
||||
if (formData.Name.IsNotNullOrWhiteSpace()) writer.Write("; name=\"{0}\"", formData.Name);
|
||||
if (formData.FileName.IsNotNullOrWhiteSpace()) writer.Write("; filename=\"{0}\"", formData.FileName);
|
||||
writer.Write("\r\n");
|
||||
|
||||
if (formData.ContentType.IsNotNullOrWhiteSpace()) writer.Write("Content-Type: {0}\r\n", formData.ContentType);
|
||||
|
||||
writer.Write("\r\n");
|
||||
writer.Flush();
|
||||
|
||||
bodyStream.Write(formData.ContentData, 0, formData.ContentData.Length);
|
||||
writer.Write("\r\n");
|
||||
|
||||
if (formData.FileName.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
summary.AppendFormat("\r\n{0}={1} ({2} bytes)", formData.Name, formData.FileName, formData.ContentData.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
summary.AppendFormat("\r\n{0}={1}", formData.Name, Encoding.UTF8.GetString(formData.ContentData));
|
||||
}
|
||||
}
|
||||
|
||||
writer.Write(endBoundary);
|
||||
}
|
||||
|
||||
var body = bodyStream.ToArray();
|
||||
|
||||
// TODO: Scan through body to see if we have a boundary collision?
|
||||
|
||||
request.Headers.ContentType = "multipart/form-data; boundary=" + boundary;
|
||||
request.SetContent(body);
|
||||
|
||||
if (request.ContentSummary == null)
|
||||
{
|
||||
request.ContentSummary = summary.ToString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var parameters = FormData.Select(v => string.Format("{0}={1}", v.Name, Uri.EscapeDataString(Encoding.UTF8.GetString(v.ContentData))));
|
||||
var urlencoded = string.Join("&", parameters);
|
||||
var body = Encoding.UTF8.GetBytes(urlencoded);
|
||||
|
||||
request.Headers.ContentType = "application/x-www-form-urlencoded";
|
||||
request.SetContent(body);
|
||||
|
||||
if (request.ContentSummary == null)
|
||||
{
|
||||
request.ContentSummary = urlencoded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual HttpRequestBuilder Resource(string resourceUrl)
|
||||
{
|
||||
if (!ResourceUrl.IsNotNullOrWhiteSpace() || resourceUrl.StartsWith("/"))
|
||||
{
|
||||
ResourceUrl = resourceUrl.TrimStart('/');
|
||||
}
|
||||
else
|
||||
{
|
||||
ResourceUrl = string.Format("{0}/{1}", ResourceUrl.TrimEnd('/'), resourceUrl);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual HttpRequestBuilder KeepAlive(bool keepAlive = true)
|
||||
{
|
||||
ConnectionKeepAlive = keepAlive;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual HttpRequestBuilder Post()
|
||||
{
|
||||
Method = HttpMethod.POST;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual HttpRequestBuilder Accept(HttpAccept accept)
|
||||
{
|
||||
HttpAccept = accept;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual HttpRequestBuilder SetHeader(string name, string value)
|
||||
{
|
||||
Headers.Set(name, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual HttpRequestBuilder AddPrefixQueryParam(string key, object value, bool replace = false)
|
||||
{
|
||||
if (replace)
|
||||
{
|
||||
QueryParams.RemoveAll(v => v.Key == key);
|
||||
SuffixQueryParams.RemoveAll(v => v.Key == key);
|
||||
}
|
||||
|
||||
QueryParams.Insert(0, new KeyValuePair<string, string>(key, value.ToString()));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual HttpRequestBuilder AddQueryParam(string key, object value, bool replace = false)
|
||||
{
|
||||
if (replace)
|
||||
{
|
||||
QueryParams.RemoveAll(v => v.Key == key);
|
||||
SuffixQueryParams.RemoveAll(v => v.Key == key);
|
||||
}
|
||||
|
||||
QueryParams.Add(key, value.ToString());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual HttpRequestBuilder AddSuffixQueryParam(string key, object value, bool replace = false)
|
||||
{
|
||||
if (replace)
|
||||
{
|
||||
QueryParams.RemoveAll(v => v.Key == key);
|
||||
SuffixQueryParams.RemoveAll(v => v.Key == key);
|
||||
}
|
||||
|
||||
SuffixQueryParams.Add(new KeyValuePair<string, string>(key, value.ToString()));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual HttpRequestBuilder SetSegment(string segment, string value, bool dontCheck = false)
|
||||
{
|
||||
var key = string.Concat("{", segment, "}");
|
||||
|
||||
if (!dontCheck && !CreateUri().ToString().Contains(key))
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Segment {0} is not defined in Uri", segment));
|
||||
}
|
||||
|
||||
Segments[key] = value;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual HttpRequestBuilder SetCookies(IEnumerable<KeyValuePair<string, string>> cookies)
|
||||
{
|
||||
foreach (var cookie in cookies)
|
||||
{
|
||||
Cookies[cookie.Key] = cookie.Value;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual HttpRequestBuilder SetCookie(string key, string value)
|
||||
{
|
||||
Cookies[key] = value;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual HttpRequestBuilder AddFormParameter(string key, object value)
|
||||
{
|
||||
if (Method != HttpMethod.POST)
|
||||
{
|
||||
throw new NotSupportedException("HttpRequest Method must be POST to add FormParameter.");
|
||||
}
|
||||
|
||||
FormData.Add(new HttpFormData
|
||||
{
|
||||
Name = key,
|
||||
ContentData = Encoding.UTF8.GetBytes(value.ToString())
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual HttpRequestBuilder AddFormUpload(string name, string fileName, byte[] data, string contentType = "application/octet-stream")
|
||||
{
|
||||
if (Method != HttpMethod.POST)
|
||||
{
|
||||
throw new NotSupportedException("HttpRequest Method must be POST to add FormUpload.");
|
||||
}
|
||||
|
||||
FormData.Add(new HttpFormData
|
||||
{
|
||||
Name = name,
|
||||
FileName = fileName,
|
||||
ContentData = data,
|
||||
ContentType = contentType
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
36
src/NzbDrone.Common/Http/HttpRequestBuilderFactory.cs
Normal file
36
src/NzbDrone.Common/Http/HttpRequestBuilderFactory.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NzbDrone.Common.Http
|
||||
{
|
||||
public interface IHttpRequestBuilderFactory
|
||||
{
|
||||
HttpRequestBuilder Create();
|
||||
}
|
||||
|
||||
public class HttpRequestBuilderFactory : IHttpRequestBuilderFactory
|
||||
{
|
||||
private HttpRequestBuilder _rootBuilder;
|
||||
|
||||
public HttpRequestBuilderFactory(HttpRequestBuilder rootBuilder)
|
||||
{
|
||||
SetRootBuilder(rootBuilder);
|
||||
}
|
||||
|
||||
protected HttpRequestBuilderFactory()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected void SetRootBuilder(HttpRequestBuilder rootBuilder)
|
||||
{
|
||||
_rootBuilder = rootBuilder.Clone();
|
||||
}
|
||||
|
||||
public HttpRequestBuilder Create()
|
||||
{
|
||||
return _rootBuilder.Clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Serializer;
|
||||
|
||||
namespace NzbDrone.Common.Http
|
||||
{
|
||||
public class HttpResponse
|
||||
{
|
||||
private static readonly Regex RegexSetCookie = new Regex("^(.*?)=(.*?)(?:;|$)", RegexOptions.Compiled);
|
||||
|
||||
public HttpResponse(HttpRequest request, HttpHeader headers, byte[] binaryData, HttpStatusCode statusCode = HttpStatusCode.OK)
|
||||
{
|
||||
Request = request;
|
||||
@@ -52,11 +57,31 @@ namespace NzbDrone.Common.Http
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<string, string> GetCookies()
|
||||
{
|
||||
var result = new Dictionary<string, string>();
|
||||
|
||||
var setCookieHeaders = Headers.GetValues("Set-Cookie");
|
||||
if (setCookieHeaders != null)
|
||||
{
|
||||
foreach (var cookie in setCookieHeaders)
|
||||
{
|
||||
var match = RegexSetCookie.Match(cookie);
|
||||
if (match.Success)
|
||||
{
|
||||
result[match.Groups[1].Value] = match.Groups[2].Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var result = string.Format("Res: [{0}] {1} : {2}.{3}", Request.Method, Request.Url, (int)StatusCode, StatusCode);
|
||||
var result = string.Format("Res: [{0}] {1}: {2}.{3}", Request.Method, Request.Url, (int)StatusCode, StatusCode);
|
||||
|
||||
if (HasHttpError && !Headers.ContentType.Equals("text/html", StringComparison.InvariantCultureIgnoreCase))
|
||||
if (HasHttpError && Headers.ContentType.IsNotNullOrWhiteSpace() && !Headers.ContentType.Equals("text/html", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
result += Environment.NewLine + Content;
|
||||
}
|
||||
|
||||
272
src/NzbDrone.Common/Http/HttpUri.cs
Normal file
272
src/NzbDrone.Common/Http/HttpUri.cs
Normal file
@@ -0,0 +1,272 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Common.Http
|
||||
{
|
||||
public class HttpUri : IEquatable<HttpUri>
|
||||
{
|
||||
private static readonly Regex RegexUri = new Regex(@"^(?:(?<scheme>[a-z]+):)?(?://(?<host>[-A-Z0-9.]+)(?::(?<port>[0-9]{1,5}))?)?(?<path>(?:(?:(?<=^)|/)[^/?#\r\n]+)+/?|/)?(?:\?(?<query>[^#\r\n]*))?(?:\#(?<fragment>.*))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private readonly string _uri;
|
||||
public string FullUri { get { return _uri; } }
|
||||
|
||||
public HttpUri(string uri)
|
||||
{
|
||||
_uri = uri ?? string.Empty;
|
||||
|
||||
Parse();
|
||||
}
|
||||
|
||||
public HttpUri(string scheme, string host, int? port, string path, string query, string fragment)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (scheme.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
builder.Append(scheme);
|
||||
builder.Append(":");
|
||||
}
|
||||
|
||||
if (host.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
builder.Append("//");
|
||||
builder.Append(host);
|
||||
if (port.HasValue)
|
||||
{
|
||||
builder.Append(":");
|
||||
builder.Append(port);
|
||||
}
|
||||
}
|
||||
|
||||
if (path.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
if (host.IsNotNullOrWhiteSpace() || path.StartsWith("/"))
|
||||
{
|
||||
builder.Append('/');
|
||||
}
|
||||
builder.Append(path.TrimStart('/'));
|
||||
}
|
||||
|
||||
if (query.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
builder.Append('?');
|
||||
builder.Append(query);
|
||||
}
|
||||
|
||||
if (fragment.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
builder.Append('#');
|
||||
builder.Append(fragment);
|
||||
}
|
||||
|
||||
_uri = builder.ToString();
|
||||
|
||||
Parse();
|
||||
}
|
||||
|
||||
private void Parse()
|
||||
{
|
||||
var match = RegexUri.Match(_uri);
|
||||
|
||||
var scheme = match.Groups["scheme"];
|
||||
var host = match.Groups["host"];
|
||||
var port = match.Groups["port"];
|
||||
var path = match.Groups["path"];
|
||||
var query = match.Groups["query"];
|
||||
var fragment = match.Groups["fragment"];
|
||||
|
||||
if (!match.Success || scheme.Success && !host.Success && path.Success)
|
||||
{
|
||||
throw new ArgumentException("Uri didn't match expected pattern: " + _uri);
|
||||
}
|
||||
|
||||
Scheme = scheme.Value;
|
||||
Host = host.Value;
|
||||
Port = port.Success ? (int?)int.Parse(port.Value) : null;
|
||||
Path = path.Value;
|
||||
Query = query.Value;
|
||||
Fragment = fragment.Value;
|
||||
}
|
||||
|
||||
public string Scheme { get; private set; }
|
||||
public string Host { get; private set; }
|
||||
public int? Port { get; private set; }
|
||||
public string Path { get; private set; }
|
||||
public string Query { get; private set; }
|
||||
public string Fragment { get; private set; }
|
||||
|
||||
private IList<KeyValuePair<string, string>> _queryParams;
|
||||
private IList<KeyValuePair<string, string>> QueryParams
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_queryParams == null)
|
||||
{
|
||||
var dict = new List<KeyValuePair<string, string>>();
|
||||
|
||||
if (Query.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
foreach (var pair in Query.Split('&'))
|
||||
{
|
||||
var split = pair.Split(new[] { '=' }, 2);
|
||||
|
||||
if (split.Length == 1)
|
||||
{
|
||||
dict.Add(new KeyValuePair<string, string>(Uri.UnescapeDataString(split[0]), null));
|
||||
}
|
||||
else
|
||||
{
|
||||
dict.Add(new KeyValuePair<string, string>(Uri.UnescapeDataString(split[0]), Uri.UnescapeDataString(split[1])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_queryParams = dict.AsReadOnly();
|
||||
}
|
||||
return _queryParams;
|
||||
}
|
||||
}
|
||||
|
||||
public HttpUri CombinePath(string path)
|
||||
{
|
||||
return new HttpUri(Scheme, Host, Port, CombinePath(Path, path), Query, Fragment);
|
||||
}
|
||||
|
||||
private static string CombinePath(string basePath, string relativePath)
|
||||
{
|
||||
if (relativePath.IsNullOrWhiteSpace())
|
||||
{
|
||||
return basePath;
|
||||
}
|
||||
|
||||
if (basePath.IsNullOrWhiteSpace())
|
||||
{
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
return basePath.TrimEnd('/') + "/" + relativePath.TrimStart('/');
|
||||
}
|
||||
|
||||
private static string CombineRelativePath(string basePath, string relativePath)
|
||||
{
|
||||
if (relativePath.IsNullOrWhiteSpace())
|
||||
{
|
||||
return basePath;
|
||||
}
|
||||
|
||||
if (relativePath.StartsWith("/"))
|
||||
{
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
var baseSlashIndex = basePath.LastIndexOf('/');
|
||||
|
||||
if (baseSlashIndex >= 0)
|
||||
{
|
||||
return basePath.Substring(0, baseSlashIndex) + "/" + relativePath;
|
||||
}
|
||||
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
public HttpUri SetQuery(string query)
|
||||
{
|
||||
return new HttpUri(Scheme, Host, Port, Path, query, Fragment);
|
||||
}
|
||||
|
||||
public HttpUri AddQueryParam(string key, object value)
|
||||
{
|
||||
var newQuery = string.Concat(Uri.EscapeDataString(key), "=", Uri.EscapeDataString(value.ToString()));
|
||||
|
||||
if (Query.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
newQuery = string.Concat(Query, "&", newQuery);
|
||||
}
|
||||
|
||||
return SetQuery(newQuery);
|
||||
}
|
||||
|
||||
public HttpUri AddQueryParams(IEnumerable<KeyValuePair<string, string>> queryParams)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.Append(Query);
|
||||
|
||||
foreach (var pair in queryParams)
|
||||
{
|
||||
if (builder.Length != 0)
|
||||
{
|
||||
builder.Append("&");
|
||||
}
|
||||
builder.Append(Uri.EscapeDataString(pair.Key));
|
||||
builder.Append("=");
|
||||
builder.Append(Uri.EscapeDataString(pair.Value));
|
||||
}
|
||||
|
||||
return SetQuery(builder.ToString());
|
||||
}
|
||||
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _uri.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _uri;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is string)
|
||||
{
|
||||
return _uri.Equals((string)obj);
|
||||
}
|
||||
else if (obj is Uri)
|
||||
{
|
||||
return _uri.Equals(((Uri)obj).OriginalString);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Equals(obj as HttpUri);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(HttpUri other)
|
||||
{
|
||||
if (object.ReferenceEquals(other, null)) return false;
|
||||
|
||||
return _uri.Equals(other._uri);
|
||||
}
|
||||
|
||||
public static explicit operator Uri(HttpUri url)
|
||||
{
|
||||
return new Uri(url.FullUri);
|
||||
}
|
||||
|
||||
public static HttpUri operator +(HttpUri baseUrl, HttpUri relativeUrl)
|
||||
{
|
||||
if (relativeUrl.Scheme.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return relativeUrl;
|
||||
}
|
||||
|
||||
if (relativeUrl.Host.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return new HttpUri(baseUrl.Scheme, relativeUrl.Host, relativeUrl.Port, relativeUrl.Path, relativeUrl.Query, relativeUrl.Fragment);
|
||||
}
|
||||
|
||||
if (relativeUrl.Path.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
return new HttpUri(baseUrl.Scheme, baseUrl.Host, baseUrl.Port, CombineRelativePath(baseUrl.Path, relativeUrl.Path), relativeUrl.Query, relativeUrl.Fragment);
|
||||
}
|
||||
|
||||
return new HttpUri(baseUrl.Scheme, baseUrl.Host, baseUrl.Port, baseUrl.Path, relativeUrl.Query, relativeUrl.Fragment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Common.Serializer;
|
||||
|
||||
namespace NzbDrone.Common.Http
|
||||
{
|
||||
public class JsonRpcRequestBuilder : HttpRequestBuilder
|
||||
{
|
||||
public string Method { get; private set; }
|
||||
public List<object> Parameters { get; private set; }
|
||||
public static HttpAccept JsonRpcHttpAccept = new HttpAccept("application/json-rpc, application/json");
|
||||
public static string JsonRpcContentType = "application/json-rpc";
|
||||
|
||||
public JsonRpcRequestBuilder(string baseUri, string method, IEnumerable<object> parameters)
|
||||
: base (baseUri)
|
||||
public string JsonMethod { get; private set; }
|
||||
public List<object> JsonParameters { get; private set; }
|
||||
|
||||
public JsonRpcRequestBuilder(string baseUrl)
|
||||
: base(baseUrl)
|
||||
{
|
||||
Method = method;
|
||||
Parameters = parameters.ToList();
|
||||
Method = HttpMethod.POST;
|
||||
JsonParameters = new List<object>();
|
||||
}
|
||||
|
||||
public override HttpRequest Build(string path)
|
||||
public JsonRpcRequestBuilder(string baseUrl, string method, IEnumerable<object> parameters)
|
||||
: base (baseUrl)
|
||||
{
|
||||
var request = base.Build(path);
|
||||
request.Method = HttpMethod.POST;
|
||||
request.Headers.Accept = "application/json-rpc, application/json";
|
||||
request.Headers.ContentType = "application/json-rpc";
|
||||
Method = HttpMethod.POST;
|
||||
JsonMethod = method;
|
||||
JsonParameters = parameters.ToList();
|
||||
}
|
||||
|
||||
public override HttpRequestBuilder Clone()
|
||||
{
|
||||
var clone = base.Clone() as JsonRpcRequestBuilder;
|
||||
clone.JsonParameters = new List<object>(JsonParameters);
|
||||
return clone;
|
||||
}
|
||||
|
||||
public JsonRpcRequestBuilder Call(string method, params object[] parameters)
|
||||
{
|
||||
var clone = Clone() as JsonRpcRequestBuilder;
|
||||
clone.JsonMethod = method;
|
||||
clone.JsonParameters = parameters.ToList();
|
||||
return clone;
|
||||
}
|
||||
|
||||
protected override void Apply(HttpRequest request)
|
||||
{
|
||||
base.Apply(request);
|
||||
|
||||
request.Headers.ContentType = JsonRpcContentType;
|
||||
|
||||
var parameterData = new object[JsonParameters.Count];
|
||||
var parameterSummary = new string[JsonParameters.Count];
|
||||
|
||||
for (var i = 0; i < JsonParameters.Count; i++)
|
||||
{
|
||||
ConvertParameter(JsonParameters[i], out parameterData[i], out parameterSummary[i]);
|
||||
}
|
||||
|
||||
var message = new Dictionary<string, object>();
|
||||
message["jsonrpc"] = "2.0";
|
||||
message["method"] = Method;
|
||||
message["params"] = Parameters;
|
||||
message["method"] = JsonMethod;
|
||||
message["params"] = parameterData;
|
||||
message["id"] = CreateNextId();
|
||||
|
||||
request.Body = message.ToJson();
|
||||
request.SetContent(message.ToJson());
|
||||
|
||||
return request;
|
||||
if (request.ContentSummary == null)
|
||||
{
|
||||
request.ContentSummary = string.Format("{0}({1})", JsonMethod, string.Join(", ", parameterSummary));
|
||||
}
|
||||
}
|
||||
|
||||
private void ConvertParameter(object value, out object data, out string summary)
|
||||
{
|
||||
if (value is byte[])
|
||||
{
|
||||
data = Convert.ToBase64String(value as byte[]);
|
||||
summary = string.Format("[blob {0} bytes]", (value as byte[]).Length);
|
||||
}
|
||||
else if (value is Array && ((Array)value).Length > 0)
|
||||
{
|
||||
data = value;
|
||||
summary = "[...]";
|
||||
}
|
||||
else
|
||||
{
|
||||
data = value;
|
||||
summary = JsonConvert.SerializeObject(data);
|
||||
}
|
||||
}
|
||||
|
||||
public string CreateNextId()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace NzbDrone.Common.Http
|
||||
{
|
||||
@@ -6,6 +7,6 @@ namespace NzbDrone.Common.Http
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public T Result { get; set; }
|
||||
public object Error { get; set; }
|
||||
public JToken Error { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Common.Http
|
||||
{
|
||||
public static class UriExtensions
|
||||
{
|
||||
public static void SetQueryParam(this UriBuilder uriBuilder, string key, object value)
|
||||
{
|
||||
var query = uriBuilder.Query;
|
||||
|
||||
if (query.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
query += "&";
|
||||
}
|
||||
|
||||
uriBuilder.Query = query.Trim('?') + key + "=" + Uri.EscapeDataString(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@
|
||||
<HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.0-rc1\lib\net40\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
@@ -67,7 +67,7 @@
|
||||
<Compile Include="Cache\CachedDictionary.cs" />
|
||||
<Compile Include="Cache\ICached.cs" />
|
||||
<Compile Include="Cache\ICachedDictionary.cs" />
|
||||
<Compile Include="Cloud\CloudClient.cs" />
|
||||
<Compile Include="Cloud\SonarrCloudRequestBuilder.cs" />
|
||||
<Compile Include="Composition\Container.cs" />
|
||||
<Compile Include="Composition\ContainerBuilderBase.cs" />
|
||||
<Compile Include="Composition\IContainer.cs" />
|
||||
@@ -156,11 +156,13 @@
|
||||
<Compile Include="Http\HttpAccept.cs" />
|
||||
<Compile Include="Http\HttpClient.cs" />
|
||||
<Compile Include="Http\HttpException.cs" />
|
||||
<Compile Include="Http\HttpFormData.cs" />
|
||||
<Compile Include="Http\HttpHeader.cs" />
|
||||
<Compile Include="Http\HttpMethod.cs" />
|
||||
<Compile Include="Http\HttpProvider.cs" />
|
||||
<Compile Include="Http\HttpRequest.cs" />
|
||||
<Compile Include="Http\HttpResponse.cs" />
|
||||
<Compile Include="Http\HttpUri.cs" />
|
||||
<Compile Include="Http\IHttpRequestInterceptor.cs" />
|
||||
<Compile Include="Http\JsonRpcRequestBuilder.cs" />
|
||||
<Compile Include="Http\JsonRpcResponse.cs" />
|
||||
@@ -168,8 +170,8 @@
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Http\HttpRequestBuilder.cs" />
|
||||
<Compile Include="Http\HttpRequestBuilderFactory.cs" />
|
||||
<Compile Include="Http\TooManyRequestsException.cs" />
|
||||
<Compile Include="Http\UriExtensions.cs" />
|
||||
<Compile Include="Extensions\IEnumerableExtensions.cs" />
|
||||
<Compile Include="Http\UserAgentBuilder.cs" />
|
||||
<Compile Include="Instrumentation\CleanseLogMessage.cs" />
|
||||
@@ -195,6 +197,7 @@
|
||||
<Compile Include="Reflection\ReflectionExtensions.cs" />
|
||||
<Compile Include="Extensions\ResourceExtensions.cs" />
|
||||
<Compile Include="Security\X509CertificateValidationPolicy.cs" />
|
||||
<Compile Include="Serializer\HttpUriConverter.cs" />
|
||||
<Compile Include="Serializer\IntConverter.cs" />
|
||||
<Compile Include="Serializer\Json.cs" />
|
||||
<Compile Include="ServiceFactory.cs" />
|
||||
|
||||
31
src/NzbDrone.Common/Serializer/HttpUriConverter.cs
Normal file
31
src/NzbDrone.Common/Serializer/HttpUriConverter.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Common.Http;
|
||||
|
||||
namespace NzbDrone.Common.Serializer
|
||||
{
|
||||
public class HttpUriConverter : JsonConverter
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else if (value is HttpUri)
|
||||
{
|
||||
writer.WriteValue((value as HttpUri).FullUri);
|
||||
}
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
return new HttpUri(reader.ReadAsString());
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(HttpUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ namespace NzbDrone.Common.Serializer
|
||||
SerializerSetting.Converters.Add(new StringEnumConverter { CamelCaseText = true });
|
||||
//SerializerSetting.Converters.Add(new IntConverter());
|
||||
SerializerSetting.Converters.Add(new VersionConverter());
|
||||
SerializerSetting.Converters.Add(new HttpUriConverter());
|
||||
|
||||
Serializer = JsonSerializer.Create(SerializerSetting);
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<packages>
|
||||
<package id="ICSharpCode.SharpZipLib.Patched" version="0.86.5" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="6.0.6" targetFramework="net40" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net40" />
|
||||
<package id="NLog" version="4.3.0-rc1" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -76,7 +76,7 @@
|
||||
<HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.0-rc1\lib\net40\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<package id="Microsoft.Owin" version="2.1.0" targetFramework="net40" />
|
||||
<package id="Microsoft.Owin.Hosting" version="2.1.0" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="6.0.6" targetFramework="net40" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net40" />
|
||||
<package id="NLog" version="4.3.0-rc1" targetFramework="net40" />
|
||||
<package id="Owin" version="1.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -187,16 +187,15 @@ namespace NzbDrone.Core.Test.DataAugmentation.Scene
|
||||
{
|
||||
var mappings = new List<SceneMapping>
|
||||
{
|
||||
new SceneMapping { Title = "Working!!", ParseTerm = "working", SearchTerm = "Working!!", TvdbId = 100, SeasonNumber = -1 },
|
||||
new SceneMapping { Title = "Working!!", ParseTerm = "working", SearchTerm = "Working!!", TvdbId = 100, SeasonNumber = 1 },
|
||||
new SceneMapping { Title = "Working`!!", ParseTerm = "working", SearchTerm = "Working`!!", TvdbId = 100, SeasonNumber = 2 },
|
||||
new SceneMapping { Title = "Working!!!", ParseTerm = "working", SearchTerm = "Working!!!", TvdbId = 100, SeasonNumber = 3 },
|
||||
new SceneMapping { Title = "Working!!", ParseTerm = "working", SearchTerm = "Working!!", TvdbId = 100, SceneSeasonNumber = 1 },
|
||||
new SceneMapping { Title = "Working`!!", ParseTerm = "working", SearchTerm = "Working`!!", TvdbId = 100, SceneSeasonNumber = 2 },
|
||||
new SceneMapping { Title = "Working!!!", ParseTerm = "working", SearchTerm = "Working!!!", TvdbId = 100, SceneSeasonNumber = 3 },
|
||||
};
|
||||
|
||||
Mocker.GetMock<ISceneMappingRepository>().Setup(c => c.All()).Returns(mappings);
|
||||
|
||||
var tvdbId = Subject.FindTvdbId(parseTitle);
|
||||
var seasonNumber = Subject.GetSeasonNumber(parseTitle);
|
||||
var seasonNumber = Subject.GetSceneSeasonNumber(parseTitle);
|
||||
|
||||
tvdbId.Should().Be(100);
|
||||
seasonNumber.Should().Be(expectedSeasonNumber);
|
||||
@@ -217,7 +216,7 @@ namespace NzbDrone.Core.Test.DataAugmentation.Scene
|
||||
|
||||
foreach (var sceneMapping in _fakeMappings)
|
||||
{
|
||||
Subject.GetSceneNames(sceneMapping.TvdbId, _fakeMappings.Select(m => m.SeasonNumber)).Should().Contain(sceneMapping.SearchTerm);
|
||||
Subject.GetSceneNamesBySeasonNumbers(sceneMapping.TvdbId, _fakeMappings.Select(m => m.SeasonNumber.Value)).Should().Contain(sceneMapping.SearchTerm);
|
||||
Subject.FindTvdbId(sceneMapping.ParseTerm).Should().Be(sceneMapping.TvdbId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,5 +270,23 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
|
||||
result.First().RemoteEpisode.DownloadAllowed.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_a_decision_when_exception_is_caught()
|
||||
{
|
||||
GivenSpecifications(_pass1);
|
||||
|
||||
Mocker.GetMock<IParsingService>().Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<SearchCriteriaBase>()))
|
||||
.Throws<TestException>();
|
||||
|
||||
_reports = new List<ReleaseInfo>
|
||||
{
|
||||
new ReleaseInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"},
|
||||
};
|
||||
|
||||
Subject.GetRssDecision(_reports).Should().HaveCount(1);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using FizzWare.NBuilder;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NzbDrone.Core.Tv;
|
||||
using Moq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class FullSeasonSpecificationFixture : CoreTest<FullSeasonSpecification>
|
||||
{
|
||||
private RemoteEpisode _remoteEpisode;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
var show = Builder<Series>.CreateNew().With(s => s.Id = 1234).Build();
|
||||
_remoteEpisode = new RemoteEpisode
|
||||
{
|
||||
ParsedEpisodeInfo = new ParsedEpisodeInfo
|
||||
{
|
||||
FullSeason = true
|
||||
},
|
||||
Episodes = Builder<Episode>.CreateListOfSize(3)
|
||||
.All()
|
||||
.With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-8))
|
||||
.With(s => s.SeriesId = show.Id)
|
||||
.BuildList(),
|
||||
Series = show,
|
||||
Release = new ReleaseInfo
|
||||
{
|
||||
Title = "Series.Title.S01.720p.BluRay.X264-RlsGrp"
|
||||
}
|
||||
};
|
||||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(s => s.EpisodesBetweenDates(It.IsAny<DateTime>(), It.IsAny<DateTime>(), false))
|
||||
.Returns(new List<Episode>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_is_not_a_full_season()
|
||||
{
|
||||
_remoteEpisode.ParsedEpisodeInfo.FullSeason = false;
|
||||
_remoteEpisode.Episodes.Last().AirDateUtc = DateTime.UtcNow.AddDays(+2);
|
||||
Mocker.Resolve<FullSeasonSpecification>().IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_all_episodes_have_aired()
|
||||
{
|
||||
Mocker.Resolve<FullSeasonSpecification>().IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_one_episode_has_not_aired()
|
||||
{
|
||||
_remoteEpisode.Episodes.Last().AirDateUtc = DateTime.UtcNow.AddDays(+2);
|
||||
Mocker.Resolve<FullSeasonSpecification>().IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_an_episode_does_not_have_an_air_date()
|
||||
{
|
||||
_remoteEpisode.Episodes.Last().AirDateUtc = null;
|
||||
Mocker.Resolve<FullSeasonSpecification>().IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using NzbDrone.Core.DecisionEngine;
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using FizzWare.NBuilder;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
@@ -121,7 +122,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_order_by_smallest_rounded_to_200mb_then_age()
|
||||
public void should_order_by_age_then_largest_rounded_to_200mb()
|
||||
{
|
||||
var remoteEpisodeSd = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.SDTV), size: 100.Megabytes(), age: 1);
|
||||
var remoteEpisodeHdSmallOld = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), size: 1200.Megabytes(), age: 1000);
|
||||
@@ -135,7 +136,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
decisions.Add(new DownloadDecision(remoteEpisodeHdLargeYoung));
|
||||
|
||||
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
|
||||
qualifiedReports.First().RemoteEpisode.Should().Be(remoteEpisodeSmallYoung);
|
||||
qualifiedReports.First().RemoteEpisode.Should().Be(remoteEpisodeHdLargeYoung);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -199,5 +200,153 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
|
||||
qualifiedReports.First().RemoteEpisode.Release.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_prefer_season_pack_above_single_episode()
|
||||
{
|
||||
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1), GivenEpisode(2) }, new QualityModel(Quality.HDTV720p));
|
||||
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p));
|
||||
|
||||
remoteEpisode1.ParsedEpisodeInfo.FullSeason = true;
|
||||
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteEpisode1));
|
||||
decisions.Add(new DownloadDecision(remoteEpisode2));
|
||||
|
||||
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
|
||||
qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.FullSeason.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_prefer_releases_with_more_seeders()
|
||||
{
|
||||
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p));
|
||||
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p));
|
||||
|
||||
var torrentInfo1 = new TorrentInfo();
|
||||
torrentInfo1.PublishDate = DateTime.Now;
|
||||
torrentInfo1.Size = 0;
|
||||
torrentInfo1.DownloadProtocol = DownloadProtocol.Torrent;
|
||||
torrentInfo1.Seeders = 10;
|
||||
|
||||
var torrentInfo2 = torrentInfo1.JsonClone();
|
||||
torrentInfo2.Seeders = 100;
|
||||
|
||||
remoteEpisode1.Release = torrentInfo1;
|
||||
remoteEpisode2.Release = torrentInfo2;
|
||||
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteEpisode1));
|
||||
decisions.Add(new DownloadDecision(remoteEpisode2));
|
||||
|
||||
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
|
||||
((TorrentInfo) qualifiedReports.First().RemoteEpisode.Release).Seeders.Should().Be(torrentInfo2.Seeders);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_prefer_releases_with_more_peers_given_equal_number_of_seeds()
|
||||
{
|
||||
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p));
|
||||
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p));
|
||||
|
||||
var torrentInfo1 = new TorrentInfo();
|
||||
torrentInfo1.PublishDate = DateTime.Now;
|
||||
torrentInfo1.Size = 0;
|
||||
torrentInfo1.DownloadProtocol = DownloadProtocol.Torrent;
|
||||
torrentInfo1.Seeders = 10;
|
||||
torrentInfo1.Peers = 10;
|
||||
|
||||
|
||||
var torrentInfo2 = torrentInfo1.JsonClone();
|
||||
torrentInfo2.Peers = 100;
|
||||
|
||||
remoteEpisode1.Release = torrentInfo1;
|
||||
remoteEpisode2.Release = torrentInfo2;
|
||||
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteEpisode1));
|
||||
decisions.Add(new DownloadDecision(remoteEpisode2));
|
||||
|
||||
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
|
||||
((TorrentInfo)qualifiedReports.First().RemoteEpisode.Release).Peers.Should().Be(torrentInfo2.Peers);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_prefer_releases_with_more_peers_no_seeds()
|
||||
{
|
||||
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p));
|
||||
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p));
|
||||
|
||||
var torrentInfo1 = new TorrentInfo();
|
||||
torrentInfo1.PublishDate = DateTime.Now;
|
||||
torrentInfo1.Size = 0;
|
||||
torrentInfo1.DownloadProtocol = DownloadProtocol.Torrent;
|
||||
torrentInfo1.Seeders = 0;
|
||||
torrentInfo1.Peers = 10;
|
||||
|
||||
|
||||
var torrentInfo2 = torrentInfo1.JsonClone();
|
||||
torrentInfo2.Seeders = 0;
|
||||
torrentInfo2.Peers = 100;
|
||||
|
||||
remoteEpisode1.Release = torrentInfo1;
|
||||
remoteEpisode2.Release = torrentInfo2;
|
||||
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteEpisode1));
|
||||
decisions.Add(new DownloadDecision(remoteEpisode2));
|
||||
|
||||
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
|
||||
((TorrentInfo)qualifiedReports.First().RemoteEpisode.Release).Peers.Should().Be(torrentInfo2.Peers);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_prefer_first_release_if_peers_and_size_are_too_similar()
|
||||
{
|
||||
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p));
|
||||
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p));
|
||||
|
||||
var torrentInfo1 = new TorrentInfo();
|
||||
torrentInfo1.PublishDate = DateTime.Now;
|
||||
torrentInfo1.DownloadProtocol = DownloadProtocol.Torrent;
|
||||
torrentInfo1.Seeders = 1000;
|
||||
torrentInfo1.Peers = 10;
|
||||
torrentInfo1.Size = 200.Megabytes();
|
||||
|
||||
var torrentInfo2 = torrentInfo1.JsonClone();
|
||||
torrentInfo2.Seeders = 1100;
|
||||
torrentInfo2.Peers = 10;
|
||||
torrentInfo1.Size = 250.Megabytes();
|
||||
|
||||
remoteEpisode1.Release = torrentInfo1;
|
||||
remoteEpisode2.Release = torrentInfo2;
|
||||
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteEpisode1));
|
||||
decisions.Add(new DownloadDecision(remoteEpisode2));
|
||||
|
||||
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
|
||||
((TorrentInfo) qualifiedReports.First().RemoteEpisode.Release).Should().Be(torrentInfo1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_prefer_first_release_if_age_and_size_are_too_similar()
|
||||
{
|
||||
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p));
|
||||
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p));
|
||||
|
||||
remoteEpisode1.Release.PublishDate = DateTime.UtcNow.AddDays(-100);
|
||||
remoteEpisode1.Release.Size = 200.Megabytes();
|
||||
|
||||
remoteEpisode2.Release.PublishDate = DateTime.UtcNow.AddDays(-150);
|
||||
remoteEpisode2.Release.Size = 250.Megabytes();
|
||||
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteEpisode1));
|
||||
decisions.Add(new DownloadDecision(remoteEpisode2));
|
||||
|
||||
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
|
||||
qualifiedReports.First().RemoteEpisode.Release.Should().Be(remoteEpisode1.Release);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Test.Common;
|
||||
using System.Threading;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Download.Clients.Blackhole;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
{
|
||||
[TestFixture]
|
||||
public class ScanWatchFolderFixture : CoreTest<ScanWatchFolder>
|
||||
{
|
||||
protected readonly string _title = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE";
|
||||
protected string _completedDownloadFolder = @"c:\blackhole\completed".AsOsAgnostic();
|
||||
|
||||
protected void GivenCompletedItem()
|
||||
{
|
||||
var targetDir = Path.Combine(_completedDownloadFolder, _title);
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetDirectories(_completedDownloadFolder))
|
||||
.Returns(new[] { targetDir });
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetFiles(targetDir, SearchOption.AllDirectories))
|
||||
.Returns(new[] { Path.Combine(targetDir, "somefile.mkv") });
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetFileSize(It.IsAny<string>()))
|
||||
.Returns(1000000);
|
||||
}
|
||||
|
||||
protected void GivenChangedItem()
|
||||
{
|
||||
var currentSize = Mocker.GetMock<IDiskProvider>().Object.GetFileSize("abc");
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetFileSize(It.IsAny<string>()))
|
||||
.Returns(currentSize + 1);
|
||||
}
|
||||
|
||||
private void VerifySingleItem(DownloadItemStatus status)
|
||||
{
|
||||
var items = Subject.GetItems(_completedDownloadFolder, TimeSpan.FromMilliseconds(50)).ToList();
|
||||
|
||||
items.Count.Should().Be(1);
|
||||
items.First().Status.Should().Be(status);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetItems_should_considered_locked_files_queued()
|
||||
{
|
||||
GivenCompletedItem();
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.IsFileLocked(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
Thread.Sleep(60);
|
||||
|
||||
VerifySingleItem(DownloadItemStatus.Downloading);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetItems_should_considered_changing_files_queued()
|
||||
{
|
||||
GivenCompletedItem();
|
||||
|
||||
VerifySingleItem(DownloadItemStatus.Downloading);
|
||||
|
||||
// If we keep changing the file every 20ms we should stay Downloading.
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
TestLogger.Info("Iteration {0}", i);
|
||||
|
||||
GivenChangedItem();
|
||||
|
||||
VerifySingleItem(DownloadItemStatus.Downloading);
|
||||
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
|
||||
// Until it remains unchanged for >=50ms.
|
||||
Thread.Sleep(60);
|
||||
|
||||
VerifySingleItem(DownloadItemStatus.Completed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Clients.TorrentBlackhole;
|
||||
using NzbDrone.Core.Download.Clients.Blackhole;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.MediaFiles.TorrentInfo;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
@@ -30,6 +30,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
_blackholeFolder = @"c:\blackhole\torrent".AsOsAgnostic();
|
||||
_filePath = (@"c:\blackhole\torrent\" + _title + ".torrent").AsOsAgnostic();
|
||||
|
||||
Mocker.SetConstant<IScanWatchFolder>(Mocker.Resolve<ScanWatchFolder>());
|
||||
|
||||
Subject.Definition = new DownloadClientDefinition();
|
||||
Subject.Definition.Settings = new TorrentBlackholeSettings
|
||||
{
|
||||
@@ -56,13 +58,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
protected void GivenCompletedItem()
|
||||
{
|
||||
var targetDir = Path.Combine(_completedDownloadFolder, _title);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetDirectories(_completedDownloadFolder))
|
||||
.Returns(new[] { targetDir });
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetFiles(targetDir, SearchOption.AllDirectories))
|
||||
.Returns(new[] { Path.Combine(_completedDownloadFolder, "somefile.mkv") });
|
||||
.Returns(new[] { Path.Combine(targetDir, "somefile.mkv") });
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetFileSize(It.IsAny<string>()))
|
||||
@@ -87,6 +90,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
[Test]
|
||||
public void completed_download_should_have_required_properties()
|
||||
{
|
||||
Subject.ScanGracePeriod = TimeSpan.Zero;
|
||||
|
||||
GivenCompletedItem();
|
||||
|
||||
var result = Subject.GetItems().Single();
|
||||
@@ -94,6 +99,16 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
VerifyCompleted(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void partial_download_should_have_required_properties()
|
||||
{
|
||||
GivenCompletedItem();
|
||||
|
||||
var result = Subject.GetItems().Single();
|
||||
|
||||
VerifyPostprocessing(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_category()
|
||||
{
|
||||
@@ -112,7 +127,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
|
||||
Subject.Download(remoteEpisode);
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.ToString() == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
@@ -128,7 +143,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
|
||||
Subject.Download(remoteEpisode);
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.ToString() == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(expectedFilename), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
@@ -142,21 +157,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.Download(remoteEpisode));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetItems_should_considered_locked_files_queued()
|
||||
{
|
||||
GivenCompletedItem();
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.IsFileLocked(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
var items = Subject.GetItems().ToList();
|
||||
|
||||
items.Count.Should().Be(1);
|
||||
items.First().Status.Should().Be(DownloadItemStatus.Downloading);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveItem_should_delete_file()
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Clients.UsenetBlackhole;
|
||||
using NzbDrone.Core.Download.Clients.Blackhole;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
@@ -29,6 +29,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
_blackholeFolder = @"c:\blackhole\nzb".AsOsAgnostic();
|
||||
_filePath = (@"c:\blackhole\nzb\" + _title + ".nzb").AsOsAgnostic();
|
||||
|
||||
Mocker.SetConstant<IScanWatchFolder>(Mocker.Resolve<ScanWatchFolder>());
|
||||
|
||||
Subject.Definition = new DownloadClientDefinition();
|
||||
Subject.Definition.Settings = new UsenetBlackholeSettings
|
||||
{
|
||||
@@ -58,7 +60,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetFiles(targetDir, SearchOption.AllDirectories))
|
||||
.Returns(new[] { Path.Combine(_completedDownloadFolder, "somefile.mkv") });
|
||||
.Returns(new[] { Path.Combine(targetDir, "somefile.mkv") });
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetFileSize(It.IsAny<string>()))
|
||||
@@ -68,6 +70,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
[Test]
|
||||
public void completed_download_should_have_required_properties()
|
||||
{
|
||||
Subject.ScanGracePeriod = TimeSpan.Zero;
|
||||
|
||||
GivenCompletedItem();
|
||||
|
||||
var result = Subject.GetItems().Single();
|
||||
@@ -75,6 +79,17 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
VerifyCompleted(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void partial_download_should_have_required_properties()
|
||||
{
|
||||
GivenCompletedItem();
|
||||
|
||||
var result = Subject.GetItems().Single();
|
||||
|
||||
VerifyPostprocessing(result);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void should_return_category()
|
||||
{
|
||||
@@ -93,7 +108,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
|
||||
Subject.Download(remoteEpisode);
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.ToString() == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
@@ -109,25 +124,11 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
|
||||
Subject.Download(remoteEpisode);
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.ToString() == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(expectedFilename), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetItems_should_considered_locked_files_downloading()
|
||||
{
|
||||
GivenCompletedItem();
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.IsFileLocked(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
var result = Subject.GetItems().Single();
|
||||
|
||||
result.Status.Should().Be(DownloadItemStatus.Downloading);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveItem_should_delete_file()
|
||||
{
|
||||
|
||||
@@ -96,6 +96,15 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
|
||||
downloadClientItem.Status.Should().Be(DownloadItemStatus.Downloading);
|
||||
}
|
||||
|
||||
protected void VerifyPostprocessing(DownloadClientItem downloadClientItem)
|
||||
{
|
||||
VerifyIdentifiable(downloadClientItem);
|
||||
|
||||
//downloadClientItem.RemainingTime.Should().NotBe(TimeSpan.Zero);
|
||||
//downloadClientItem.OutputPath.Should().NotBeNullOrEmpty();
|
||||
downloadClientItem.Status.Should().Be(DownloadItemStatus.Downloading);
|
||||
}
|
||||
|
||||
protected void VerifyCompleted(DownloadClientItem downloadClientItem)
|
||||
{
|
||||
VerifyIdentifiable(downloadClientItem);
|
||||
|
||||
@@ -88,10 +88,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbVortexTests
|
||||
|
||||
Mocker.GetMock<INzbVortexProxy>()
|
||||
.Setup(s => s.GetQueue(It.IsAny<int>(), It.IsAny<NzbVortexSettings>()))
|
||||
.Returns(new NzbVortexQueue
|
||||
{
|
||||
Items = list
|
||||
});
|
||||
.Returns(list);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -244,7 +241,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbVortexTests
|
||||
|
||||
Mocker.GetMock<INzbVortexProxy>()
|
||||
.Setup(s => s.GetFiles(It.IsAny<int>(), It.IsAny<NzbVortexSettings>()))
|
||||
.Returns(new NzbVortexFiles{ Files = new List<NzbVortexFile> { new NzbVortexFile { FileName = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE.mkv" } } });
|
||||
.Returns(new List<NzbVortexFile> { new NzbVortexFile { FileName = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE.mkv" } });
|
||||
|
||||
_completed.State = NzbVortexStateType.Done;
|
||||
GivenQueue(_completed);
|
||||
@@ -263,11 +260,11 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbVortexTests
|
||||
|
||||
Mocker.GetMock<INzbVortexProxy>()
|
||||
.Setup(s => s.GetFiles(It.IsAny<int>(), It.IsAny<NzbVortexSettings>()))
|
||||
.Returns(new NzbVortexFiles { Files = new List<NzbVortexFile>
|
||||
{
|
||||
new NzbVortexFile { FileName = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE.mkv" },
|
||||
new NzbVortexFile { FileName = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE.nfo" }
|
||||
} });
|
||||
.Returns(new List<NzbVortexFile>
|
||||
{
|
||||
new NzbVortexFile { FileName = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE.mkv" },
|
||||
new NzbVortexFile { FileName = "Droned.S01E01.Pilot.1080p.WEB-DL-DRONE.nfo" }
|
||||
});
|
||||
|
||||
_completed.State = NzbVortexStateType.Done;
|
||||
GivenQueue(_completed);
|
||||
|
||||
@@ -231,7 +231,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_report_deletestatus_dupe_as_warning()
|
||||
public void should_report_deletestatus_dupe_as_failed()
|
||||
{
|
||||
_completed.DeleteStatus = "DUPE";
|
||||
|
||||
@@ -240,7 +240,20 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
|
||||
|
||||
var result = Subject.GetItems().Single();
|
||||
|
||||
result.Status.Should().Be(DownloadItemStatus.Warning);
|
||||
result.Status.Should().Be(DownloadItemStatus.Failed);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_report_deletestatus_copy_as_failed()
|
||||
{
|
||||
_completed.DeleteStatus = "COPY";
|
||||
|
||||
GivenQueue(null);
|
||||
GivenHistory(_completed);
|
||||
|
||||
var result = Subject.GetItems().Single();
|
||||
|
||||
result.Status.Should().Be(DownloadItemStatus.Failed);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
httpHeader["Location"] = "http://test.sonarr.tv/not-a-real-torrent.torrent";
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(s => s.Get(It.Is<HttpRequest>(h => h.Url.AbsoluteUri == _downloadUrl)))
|
||||
.Setup(s => s.Get(It.Is<HttpRequest>(h => h.Url.FullUri == _downloadUrl)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, httpHeader, new Byte[0], System.Net.HttpStatusCode.Found));
|
||||
}
|
||||
|
||||
|
||||
@@ -188,6 +188,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
|
||||
[TestCase(SabnzbdDownloadStatus.Checking)]
|
||||
[TestCase(SabnzbdDownloadStatus.Downloading)]
|
||||
[TestCase(SabnzbdDownloadStatus.QuickCheck)]
|
||||
[TestCase(SabnzbdDownloadStatus.ToPP)]
|
||||
[TestCase(SabnzbdDownloadStatus.Verifying)]
|
||||
[TestCase(SabnzbdDownloadStatus.Repairing)]
|
||||
[TestCase(SabnzbdDownloadStatus.Fetching)]
|
||||
@@ -231,6 +232,28 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
|
||||
VerifyFailed(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void deleted_queue_item_should_be_ignored()
|
||||
{
|
||||
_queued.Items.First().Status = SabnzbdDownloadStatus.Deleted;
|
||||
|
||||
GivenQueue(_queued);
|
||||
GivenHistory(null);
|
||||
|
||||
Subject.GetItems().Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void deleted_history_item_should_be_ignored()
|
||||
{
|
||||
_completed.Items.First().Status = SabnzbdDownloadStatus.Deleted;
|
||||
|
||||
GivenQueue(null);
|
||||
GivenHistory(_completed);
|
||||
|
||||
Subject.GetItems().Should().BeEmpty();
|
||||
}
|
||||
|
||||
[TestCase("[ TOWN ]-[ http://www.town.ag ]-[ ANIME ]-[Usenet Provider >> http://www.ssl- <<] - [Commie] Aldnoah Zero 18 [234C8FC7]", "[ TOWN ]-[ http-++www.town.ag ]-[ ANIME ]-[Usenet Provider http-++www.ssl- ] - [Commie] Aldnoah Zero 18 [234C8FC7].nzb")]
|
||||
public void Download_should_use_clean_title(string title, string filename)
|
||||
{
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.UTorrentTests
|
||||
httpHeader["Location"] = "http://test.sonarr.tv/not-a-real-torrent.torrent";
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(s => s.Get(It.Is<HttpRequest>(h => h.Url.AbsoluteUri == _downloadUrl)))
|
||||
.Setup(s => s.Get(It.Is<HttpRequest>(h => h.Url.ToString() == _downloadUrl)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, httpHeader, new byte[0], System.Net.HttpStatusCode.Found));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using Marr.Data;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using FluentAssertions;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
|
||||
@@ -84,90 +84,6 @@ namespace NzbDrone.Core.Test
|
||||
dateTime.ToBestDateString().Should().Be(dateTime.ToShortDateString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParentUriString_should_return_self_if_already_parent()
|
||||
{
|
||||
|
||||
var url = "http://www.sonarr.tv";
|
||||
var uri = new Uri(url);
|
||||
|
||||
|
||||
var result = uri.ParentUriString();
|
||||
|
||||
//Resolve
|
||||
result.Should().Be(url);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParentUriString_should_return_parent_url_when_path_is_passed()
|
||||
{
|
||||
|
||||
var url = "http://www.sonarr.tv/test/";
|
||||
var uri = new Uri(url);
|
||||
|
||||
|
||||
var result = uri.ParentUriString();
|
||||
|
||||
//Resolve
|
||||
result.Should().Be("http://www.sonarr.tv");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParentUriString_should_return_parent_url_when_multiple_paths_are_passed()
|
||||
{
|
||||
|
||||
var url = "http://www.sonarr.tv/test/test2";
|
||||
var uri = new Uri(url);
|
||||
|
||||
|
||||
var result = uri.ParentUriString();
|
||||
|
||||
//Resolve
|
||||
result.Should().Be("http://www.sonarr.tv");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParentUriString_should_return_parent_url_when_url_with_query_string_is_passed()
|
||||
{
|
||||
|
||||
var url = "http://www.sonarr.tv/test.aspx?test=10";
|
||||
var uri = new Uri(url);
|
||||
|
||||
|
||||
var result = uri.ParentUriString();
|
||||
|
||||
//Resolve
|
||||
result.Should().Be("http://www.sonarr.tv");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParentUriString_should_return_parent_url_when_url_with_path_and_query_strings_is_passed()
|
||||
{
|
||||
|
||||
var url = "http://www.sonarr.tv/tester/test.aspx?test=10";
|
||||
var uri = new Uri(url);
|
||||
|
||||
|
||||
var result = uri.ParentUriString();
|
||||
|
||||
//Resolve
|
||||
result.Should().Be("http://www.sonarr.tv");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParentUriString_should_return_parent_url_when_url_with_query_strings_is_passed()
|
||||
{
|
||||
|
||||
var url = "http://www.sonarr.tv/test.aspx?test=10&test2=5";
|
||||
var uri = new Uri(url);
|
||||
|
||||
|
||||
var result = uri.ParentUriString();
|
||||
|
||||
//Resolve
|
||||
result.Should().Be("http://www.sonarr.tv");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MaxOrDefault_should_return_zero_when_collection_is_empty()
|
||||
{
|
||||
@@ -211,7 +127,7 @@ namespace NzbDrone.Core.Test
|
||||
public void Truncate_should_truncate_strings_to_max_specified_number_of_bytes()
|
||||
{
|
||||
|
||||
var str = ReadAllText("Files", "LongOverview.txt");
|
||||
var str = ReadAllText("Files/LongOverview.txt");
|
||||
|
||||
|
||||
var resultString = str.Truncate(1000);
|
||||
|
||||
@@ -10,16 +10,11 @@ namespace NzbDrone.Core.Test.Framework
|
||||
{
|
||||
public abstract class CoreTest : TestBase
|
||||
{
|
||||
protected string ReadAllText(params string[] path)
|
||||
{
|
||||
return File.ReadAllText(Path.Combine(path));
|
||||
}
|
||||
|
||||
protected void UseRealHttp()
|
||||
{
|
||||
Mocker.SetConstant<IHttpProvider>(new HttpProvider(TestLogger));
|
||||
Mocker.SetConstant<IHttpClient>(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), TestLogger));
|
||||
Mocker.SetConstant<IDroneServicesRequestBuilder>(new DroneServicesHttpRequestBuilder());
|
||||
Mocker.SetConstant<ISonarrCloudRequestBuilder>(new SonarrCloudRequestBuilder());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.OpenReadStream(imagePath))
|
||||
.Returns(new FileStream("Files\\html_image.jpg".AsOsAgnostic(), FileMode.Open, FileAccess.Read));
|
||||
.Returns(new FileStream(GetTestPath("Files/html_image.jpg"), FileMode.Open, FileAccess.Read));
|
||||
|
||||
|
||||
Subject.Clean();
|
||||
@@ -130,7 +130,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.OpenReadStream(imagePath))
|
||||
.Returns(new FileStream("Files\\emptyfile.txt".AsOsAgnostic(), FileMode.Open, FileAccess.Read));
|
||||
.Returns(new FileStream(GetTestPath("Files/emptyfile.txt"), FileMode.Open, FileAccess.Read));
|
||||
|
||||
|
||||
Subject.Clean();
|
||||
@@ -150,7 +150,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.OpenReadStream(imagePath))
|
||||
.Returns(new FileStream("Files\\Queue.txt".AsOsAgnostic(), FileMode.Open, FileAccess.Read));
|
||||
.Returns(new FileStream(GetTestPath("Files/Queue.txt"), FileMode.Open, FileAccess.Read));
|
||||
|
||||
|
||||
Subject.Clean();
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Housekeeping.Housekeepers;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
|
||||
{
|
||||
[TestFixture]
|
||||
public class CleanupAdditionalUsersFixture : DbTest<CleanupAdditionalUsers, User>
|
||||
{
|
||||
[Test]
|
||||
public void should_delete_additional_users()
|
||||
{
|
||||
var specs = Builder<User>.CreateListOfSize(5)
|
||||
.BuildListOfNew();
|
||||
|
||||
Db.InsertMany(specs);
|
||||
|
||||
Subject.Clean();
|
||||
AllStoredModels.Should().HaveCount(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_delete_if_only_one_user()
|
||||
{
|
||||
var spec = Builder<User>.CreateNew()
|
||||
.BuildNew();
|
||||
|
||||
Db.Insert(spec);
|
||||
|
||||
Subject.Clean();
|
||||
AllStoredModels.Should().HaveCount(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Test.Http
|
||||
|
||||
var newRequest = Subject.PreRequest(request);
|
||||
|
||||
newRequest.Url.AbsoluteUri.Should().Be("http://torcache.net/download/123.torrent");
|
||||
newRequest.Url.FullUri.Should().Be("http://torcache.net/download/123.torrent");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -41,7 +41,7 @@ namespace NzbDrone.Core.Test.Http
|
||||
|
||||
var newRequest = Subject.PreRequest(request);
|
||||
|
||||
newRequest.Url.AbsoluteUri.Should().Be(url);
|
||||
newRequest.Url.FullUri.Should().Be(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,11 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
.Returns<int, int>((i, j) => _xemEpisodes.Where(d => d.SeasonNumber == j).ToList());
|
||||
|
||||
Mocker.GetMock<ISceneMappingService>()
|
||||
.Setup(s => s.GetSceneNames(It.IsAny<int>(), It.IsAny<IEnumerable<int>>()))
|
||||
.Setup(s => s.GetSceneNamesBySeasonNumbers(It.IsAny<int>(), It.IsAny<IEnumerable<int>>()))
|
||||
.Returns(new List<string>());
|
||||
|
||||
Mocker.GetMock<ISceneMappingService>()
|
||||
.Setup(s => s.GetSceneNamesBySceneSeasonNumbers(It.IsAny<int>(), It.IsAny<IEnumerable<int>>()))
|
||||
.Returns(new List<string>());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
@@ -8,16 +9,16 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
{
|
||||
public class SearchDefinitionFixture : CoreTest<SingleEpisodeSearchCriteria>
|
||||
{
|
||||
[TestCase("Betty White's Off Their Rockers", Result = "Betty+Whites+Off+Their+Rockers")]
|
||||
[TestCase("Star Wars: The Clone Wars", Result = "Star+Wars+The+Clone+Wars")]
|
||||
[TestCase("Hawaii Five-0", Result = "Hawaii+Five+0")]
|
||||
[TestCase("Franklin & Bash", Result = "Franklin+and+Bash")]
|
||||
[TestCase("Chicago P.D.", Result = "Chicago+PD")]
|
||||
[TestCase("Kourtney And Khlo\u00E9 Take The Hamptons", Result = "Kourtney+And+Khloe+Take+The+Hamptons")]
|
||||
public string should_replace_some_special_characters(string input)
|
||||
[TestCase("Betty White's Off Their Rockers", "Betty+Whites+Off+Their+Rockers")]
|
||||
[TestCase("Star Wars: The Clone Wars", "Star+Wars+The+Clone+Wars")]
|
||||
[TestCase("Hawaii Five-0", "Hawaii+Five+0")]
|
||||
[TestCase("Franklin & Bash", "Franklin+and+Bash")]
|
||||
[TestCase("Chicago P.D.", "Chicago+PD")]
|
||||
[TestCase("Kourtney And Khlo\u00E9 Take The Hamptons", "Kourtney+And+Khloe+Take+The+Hamptons")]
|
||||
public void should_replace_some_special_characters(string input, string expected)
|
||||
{
|
||||
Subject.SceneTitles = new List<string> { input };
|
||||
return Subject.QueryTitles.First();
|
||||
Subject.QueryTitles.First().Should().Be(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace NzbDrone.Core.Test.IndexerTests
|
||||
[Test]
|
||||
public void should_handle_relative_url()
|
||||
{
|
||||
var xml = ReadAllText("Files", "Indexers", "relative_urls.xml");
|
||||
var xml = ReadAllText("Files/Indexers/relative_urls.xml");
|
||||
|
||||
var result = Subject.ParseResponse(CreateResponse("http://my.indexer.com/api?q=My+Favourite+Show", xml));
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
|
||||
first.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
|
||||
first.DownloadUrl.Should().Be("https://hdbits.org/download.php?id=257142&passkey=fakekey");
|
||||
first.InfoUrl.Should().Be("https://hdbits.org/details.php?id=257142");
|
||||
first.PublishDate.Should().Be(DateTime.Parse("2015-04-04T20:30:46+0000"));
|
||||
first.PublishDate.Should().Be(DateTime.Parse("2015-04-04T20:30:46+0000").ToUniversalTime());
|
||||
first.Size.Should().Be(1718009717);
|
||||
first.InfoHash.Should().Be("EABC50AEF9F53CEDED84ADF14144D3368E586F3A");
|
||||
first.MagnetUrl.Should().BeNullOrEmpty();
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
Url = "http://indxer.local"
|
||||
};
|
||||
|
||||
_caps = ReadAllText("Files", "Indexers", "Newznab", "newznab_caps.xml");
|
||||
_caps = ReadAllText("Files/Indexers/Newznab/newznab_caps.xml");
|
||||
}
|
||||
|
||||
private void GivenCapsResponse(string caps)
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
|
||||
var page = results.GetAllTiers().First().First();
|
||||
|
||||
page.Url.Query.Should().Contain("&cat=1,2,3,4&");
|
||||
page.Url.FullUri.Should().Contain("&cat=1,2,3,4&");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -83,7 +83,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
|
||||
var page = results.GetAllTiers().First().First();
|
||||
|
||||
page.Url.Query.Should().Contain("&cat=3,4&");
|
||||
page.Url.FullUri.Should().Contain("&cat=3,4&");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -95,7 +95,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
|
||||
var page = results.GetAllTiers().First().First();
|
||||
|
||||
page.Url.Query.Should().Contain("?t=search&");
|
||||
page.Url.FullUri.Should().Contain("?t=search&");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -107,9 +107,9 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
|
||||
var pages = results.GetAllTiers().First().Take(3).ToList();
|
||||
|
||||
pages[0].Url.Query.Should().Contain("&offset=0&");
|
||||
pages[1].Url.Query.Should().Contain("&offset=100&");
|
||||
pages[2].Url.Query.Should().Contain("&offset=200&");
|
||||
pages[0].Url.FullUri.Should().Contain("&offset=0&");
|
||||
pages[1].Url.FullUri.Should().Contain("&offset=100&");
|
||||
pages[2].Url.FullUri.Should().Contain("&offset=200&");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -234,6 +234,21 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
page.Url.Query.Should().Contain("q=");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_aggregrated_id_search_if_no_ids_are_known()
|
||||
{
|
||||
_capabilities.SupportedTvSearchParameters = new[] { "q", "rid", "season", "ep" };
|
||||
_capabilities.SupportsAggregateIdSearch = true; // Turns true if indexer supplies supportedParams.
|
||||
|
||||
_singleEpisodeSearchCriteria.Series.TvRageId = 0;
|
||||
|
||||
var results = Subject.GetSearchRequests(_singleEpisodeSearchCriteria);
|
||||
|
||||
var page = results.GetTier(0).First().First();
|
||||
|
||||
page.Url.Query.Should().Contain("q=");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fallback_to_q()
|
||||
{
|
||||
|
||||
@@ -23,8 +23,6 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Mocker.SetConstant<IHttpClient>(Mocker.GetMock<IHttpClient>().Object);
|
||||
Mocker.SetConstant<ICacheManager>(Mocker.Resolve<CacheManager>());
|
||||
Mocker.SetConstant<ITorrentRssSettingsDetector>(Mocker.Resolve<TorrentRssSettingsDetector>());
|
||||
Mocker.SetConstant<ITorrentRssParserFactory>(Mocker.Resolve<TorrentRssParserFactory>());
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Instrumentation;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Test.Common.Categories;
|
||||
|
||||
namespace NzbDrone.Core.Test.InstrumentationTests
|
||||
{
|
||||
@@ -68,6 +69,7 @@ namespace NzbDrone.Core.Test.InstrumentationTests
|
||||
|
||||
[Test]
|
||||
[Explicit]
|
||||
[ManualTest]
|
||||
public void perf_test()
|
||||
{
|
||||
MapRepository.Instance.EnableTraceLogging = false;
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests
|
||||
var mainFile = Path.Combine(TempFolder, "logo.png");
|
||||
var resizedFile = Path.Combine(TempFolder, "logo-170.png");
|
||||
|
||||
File.Copy(@"Files/1024.png", mainFile);
|
||||
File.Copy(GetTestPath("Files/1024.png"), mainFile);
|
||||
|
||||
Subject.Resize(mainFile, resizedFile, 170);
|
||||
|
||||
|
||||
@@ -385,5 +385,24 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
|
||||
result.Single().LocalEpisode.Quality.Should().Be(_quality);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_a_decision_when_exception_is_caught()
|
||||
{
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(c => c.GetLocalEpisode(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<bool>()))
|
||||
.Throws<TestException>();
|
||||
|
||||
_videoFiles = new List<string>
|
||||
{
|
||||
"The.Office.S03E115.DVDRip.XviD-OSiTV"
|
||||
};
|
||||
|
||||
GivenVideoFiles(_videoFiles);
|
||||
|
||||
Subject.GetImportDecisions(_videoFiles, _series).Should().HaveCount(1);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Test.Common.Categories;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
@@ -28,7 +29,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
[Test]
|
||||
public void get_runtime()
|
||||
{
|
||||
var path = Path.Combine(Directory.GetCurrentDirectory(), "Files", "Media", "H264_sample.mp4");
|
||||
var path = Path.Combine(TestContext.CurrentContext.TestDirectory, "Files", "Media", "H264_sample.mp4");
|
||||
|
||||
Subject.GetRunTime(path).Seconds.Should().Be(10);
|
||||
|
||||
@@ -38,7 +39,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
[Test]
|
||||
public void get_info()
|
||||
{
|
||||
var path = Path.Combine(Directory.GetCurrentDirectory(), "Files", "Media", "H264_sample.mp4");
|
||||
var path = Path.Combine(TestContext.CurrentContext.TestDirectory, "Files", "Media", "H264_sample.mp4");
|
||||
|
||||
var info = Subject.GetMediaInfo(path);
|
||||
|
||||
@@ -62,7 +63,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
[Test]
|
||||
public void get_info_unicode()
|
||||
{
|
||||
var srcPath = Path.Combine(Directory.GetCurrentDirectory(), "Files", "Media", "H264_sample.mp4");
|
||||
var srcPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Files", "Media", "H264_sample.mp4");
|
||||
|
||||
var tempPath = GetTempFilePath();
|
||||
Directory.CreateDirectory(tempPath);
|
||||
@@ -92,7 +93,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
[Test]
|
||||
public void should_dispose_file_after_scanning_mediainfo()
|
||||
{
|
||||
var path = Path.Combine(Directory.GetCurrentDirectory(), "Files", "Media", "H264_sample.mp4");
|
||||
var path = Path.Combine(TestContext.CurrentContext.TestDirectory, "Files", "Media", "H264_sample.mp4");
|
||||
|
||||
var info = Subject.GetMediaInfo(path);
|
||||
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
using NzbDrone.Core.Notifications.Prowl;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Test.Common.Categories;
|
||||
|
||||
namespace NzbDrone.Core.Test.NotificationTests
|
||||
{
|
||||
[Explicit]
|
||||
[ManualTest]
|
||||
[TestFixture]
|
||||
public class ProwlProviderTest : CoreTest<ProwlService>
|
||||
{
|
||||
|
||||
@@ -69,12 +69,12 @@
|
||||
<HintPath>..\packages\Newtonsoft.Json.6.0.6\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.0-rc1\lib\net40\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
|
||||
<Reference Include="nunit.framework, Version=3.2.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.2.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
@@ -138,6 +138,7 @@
|
||||
<Compile Include="Datastore\SqliteSchemaDumperTests\SqliteSchemaDumperFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\AcceptableSizeSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\AnimeVersionUpgradeSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\FullSeasonSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\ProtocolSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\CutoffSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\DownloadDecisionMakerFixture.cs" />
|
||||
@@ -158,6 +159,7 @@
|
||||
<Compile Include="DecisionEngineTests\UpgradeDiskSpecificationFixture.cs" />
|
||||
<Compile Include="Download\CompletedDownloadServiceFixture.cs" />
|
||||
<Compile Include="Download\DownloadApprovedReportsTests\DownloadApprovedFixture.cs" />
|
||||
<Compile Include="Download\DownloadClientTests\Blackhole\ScanWatchFolderFixture.cs" />
|
||||
<Compile Include="Download\DownloadClientTests\Blackhole\TorrentBlackholeFixture.cs" />
|
||||
<Compile Include="Download\DownloadClientTests\Blackhole\UsenetBlackholeFixture.cs" />
|
||||
<Compile Include="Download\DownloadClientTests\DelugeTests\DelugeFixture.cs" />
|
||||
@@ -205,6 +207,7 @@
|
||||
<Compile Include="HealthCheck\HealthCheckFixture.cs" />
|
||||
<Compile Include="HistoryTests\HistoryRepositoryFixture.cs" />
|
||||
<Compile Include="HistoryTests\HistoryServiceFixture.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupAdditionalUsersFixture.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupAdditionalNamingSpecsFixture.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupDuplicateMetadataFilesFixture.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedBlacklistFixture.cs" />
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("At_Midnight_140722_720p_HDTV_x264-YesTV", "At Midnight", 2014, 07, 22)]
|
||||
//[TestCase("Corrie.07.01.15", "Corrie", 2015, 1, 7)]
|
||||
[TestCase("The Nightly Show with Larry Wilmore 2015 02 09 WEBRIP s01e13", "The Nightly Show with Larry Wilmore", 2015, 2, 9)]
|
||||
[TestCase("Judge Judy 2016 02 10 S20E121", "Judge Judy", 2016, 2, 10)]
|
||||
//[TestCase("", "", 0, 0, 0)]
|
||||
public void should_parse_daily_episode(string postTitle, string title, int year, int month, int day)
|
||||
{
|
||||
|
||||
@@ -55,7 +55,6 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Series Title.S6.E1-S6E2.Episode Name.1080p.WEB-DL", "Series Title", 6, new[] { 1, 2 })]
|
||||
[TestCase("Series Title.S6.E1E2.Episode Name.1080p.WEB-DL", "Series Title", 6, new[] { 1, 2 })]
|
||||
[TestCase("Series Title.S6.E1-E2-E3.Episode Name.1080p.WEB-DL", "Series Title", 6, new[] { 1, 2, 3 })]
|
||||
[TestCase("Series Title.S6.E1E3.Episode Name.1080p.WEB-DL", "Series Title", 6, new[] { 1, 2, 3 })]
|
||||
//[TestCase("", "", , new [] { })]
|
||||
public void should_parse_multiple_episodes(string postTitle, string title, int season, int[] episodes)
|
||||
{
|
||||
|
||||
@@ -210,7 +210,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
GivenAbsoluteNumberingSeries();
|
||||
|
||||
Mocker.GetMock<ISceneMappingService>()
|
||||
.Setup(s => s.GetSeasonNumber(_parsedEpisodeInfo.SeriesTitle))
|
||||
.Setup(s => s.GetSceneSeasonNumber(_parsedEpisodeInfo.SeriesTitle))
|
||||
.Returns(seasonNumber);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
@@ -234,7 +234,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
GivenAbsoluteNumberingSeries();
|
||||
|
||||
Mocker.GetMock<ISceneMappingService>()
|
||||
.Setup(s => s.GetSeasonNumber(_parsedEpisodeInfo.SeriesTitle))
|
||||
.Setup(s => s.GetSceneSeasonNumber(_parsedEpisodeInfo.SeriesTitle))
|
||||
.Returns(seasonNumber);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
@@ -258,7 +258,7 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
GivenAbsoluteNumberingSeries();
|
||||
|
||||
Mocker.GetMock<ISceneMappingService>()
|
||||
.Setup(s => s.GetSeasonNumber(_parsedEpisodeInfo.SeriesTitle))
|
||||
.Setup(s => s.GetSceneSeasonNumber(_parsedEpisodeInfo.SeriesTitle))
|
||||
.Returns(seasonNumber);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
@@ -273,5 +273,73 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.FindEpisode(It.IsAny<int>(), seasonNumber, It.IsAny<int>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_tvdb_season_number_when_available_and_a_scene_source()
|
||||
{
|
||||
const int tvdbSeasonNumber = 5;
|
||||
|
||||
Mocker.GetMock<ISceneMappingService>()
|
||||
.Setup(s => s.FindSceneMapping(_parsedEpisodeInfo.SeriesTitle))
|
||||
.Returns(new SceneMapping { SeasonNumber = tvdbSeasonNumber, SceneSeasonNumber = _parsedEpisodeInfo.SeasonNumber });
|
||||
|
||||
Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.FindEpisode(_series.Id, _parsedEpisodeInfo.SeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Never());
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.FindEpisode(_series.Id, tvdbSeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_tvdb_season_number_when_available_for_a_different_season_and_a_scene_source()
|
||||
{
|
||||
const int tvdbSeasonNumber = 5;
|
||||
|
||||
Mocker.GetMock<ISceneMappingService>()
|
||||
.Setup(s => s.FindSceneMapping(_parsedEpisodeInfo.SeriesTitle))
|
||||
.Returns(new SceneMapping { SeasonNumber = tvdbSeasonNumber, SceneSeasonNumber = _parsedEpisodeInfo.SeasonNumber + 100 });
|
||||
|
||||
Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.FindEpisode(_series.Id, tvdbSeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Never());
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.FindEpisode(_series.Id, _parsedEpisodeInfo.SeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_tvdb_season_when_not_a_scene_source()
|
||||
{
|
||||
const int tvdbSeasonNumber = 5;
|
||||
|
||||
Subject.GetEpisodes(_parsedEpisodeInfo, _series, false, null);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.FindEpisode(_series.Id, tvdbSeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Never());
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.FindEpisode(_series.Id, _parsedEpisodeInfo.SeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_tvdb_season_when_tvdb_season_number_is_less_than_zero()
|
||||
{
|
||||
const int tvdbSeasonNumber = -1;
|
||||
|
||||
Mocker.GetMock<ISceneMappingService>()
|
||||
.Setup(s => s.FindSceneMapping(_parsedEpisodeInfo.SeriesTitle))
|
||||
.Returns(new SceneMapping { SeasonNumber = tvdbSeasonNumber, SceneSeasonNumber = _parsedEpisodeInfo.SeasonNumber });
|
||||
|
||||
Subject.GetEpisodes(_parsedEpisodeInfo, _series, true, null);
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.FindEpisode(_series.Id, tvdbSeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Never());
|
||||
|
||||
Mocker.GetMock<IEpisodeService>()
|
||||
.Verify(v => v.FindEpisode(_series.Id, _parsedEpisodeInfo.SeasonNumber, _parsedEpisodeInfo.EpisodeNumbers.First()), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +143,8 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Da.Vincis.Demons.S02E04.720p.WEB.DL.nSD.x264-NhaNc3", false)]
|
||||
[TestCase("CSI.Miami.S04E25.720p.iTunesHD.AVC-TVS", false)]
|
||||
[TestCase("Castle.S06E23.720p.WebHD.h264-euHD", false)]
|
||||
[TestCase("The.Nightly.Show.2016.03.14.720p.WEB.x264-spamTV", false)]
|
||||
[TestCase("The.Nightly.Show.2016.03.14.720p.WEB.h264-spamTV", false)]
|
||||
public void should_parse_webdl720p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Quality.WEBDL720p, proper);
|
||||
@@ -157,6 +159,9 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Glee.S04E09.Swan.Song.1080p.WEB-DL.DD5.1.H.264-ECI", false)]
|
||||
[TestCase("The.Big.Bang.Theory.S06E11.The.Santa.Simulation.1080p.WEB-DL.DD5.1.H.264", false)]
|
||||
[TestCase("Rosemary's.Baby.S01E02.Night.2.[WEBDL-1080p].mkv", false)]
|
||||
[TestCase("The.Nightly.Show.2016.03.14.1080p.WEB.x264-spamTV", false)]
|
||||
[TestCase("The.Nightly.Show.2016.03.14.1080p.WEB.h264-spamTV", false)]
|
||||
[TestCase("Psych.S01.1080p.WEB-DL.AAC2.0.AVC-TrollHD", false)]
|
||||
public void should_parse_webdl1080p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Quality.WEBDL1080p, proper);
|
||||
@@ -166,6 +171,9 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("JUST ADD MAGIC S01E01.2160P AMZN WEBRIP DD2.0 X264-TROLLUHD", false)]
|
||||
[TestCase("The.Man.In.The.High.Castle.S01E01.2160p.AMZN.WEBRip.DD2.0.Hi10p.X264-TrollUHD", false)]
|
||||
[TestCase("The Man In the High Castle S01E01 2160p AMZN WEBRip DD2.0 Hi10P x264-TrollUHD", false)]
|
||||
[TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.x264-spamTV", false)]
|
||||
[TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.h264-spamTV", false)]
|
||||
[TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.PROPER.h264-spamTV", true)]
|
||||
public void should_parse_webdl2160p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Quality.WEBDL2160p, proper);
|
||||
@@ -183,6 +191,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("WEEDS.S03E01-06.DUAL.720p.Blu-ray.AC3.-HELLYWOOD.avi", false)]
|
||||
[TestCase("[Elysium]Lucky.Star.01(BD.720p.AAC.DA)[0BB96AD8].mkv", false)]
|
||||
[TestCase("Battlestar.Galactica.S01E01.33.720p.HDDVD.x264-SiNNERS.mkv", false)]
|
||||
[TestCase("The.Expanse.S01E07.RERIP.720p.BluRay.x264-DEMAND", true)]
|
||||
public void should_parse_bluray720p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Quality.Bluray720p, proper);
|
||||
@@ -219,6 +228,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Sonny.With.a.Chance.S02E15", false)]
|
||||
[TestCase("Law & Order: Special Victims Unit - 11x11 - Quickie", false)]
|
||||
[TestCase("Series.Title.S01E01.webm", false)]
|
||||
[TestCase("Droned.S01E01.The.Web.MT-dd", false)]
|
||||
public void quality_parse(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Quality.Unknown, proper);
|
||||
|
||||
@@ -40,6 +40,15 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
Parser.Parser.ParsePath(path).ReleaseGroup.Should().Be("archivist");
|
||||
}
|
||||
|
||||
[TestCase("Marvels.Daredevil.S02E04.720p.WEBRip.x264-SKGTV English", "SKGTV")]
|
||||
[TestCase("Marvels.Daredevil.S02E04.720p.WEBRip.x264-SKGTV_English", "SKGTV")]
|
||||
[TestCase("Marvels.Daredevil.S02E04.720p.WEBRip.x264-SKGTV.English", "SKGTV")]
|
||||
//[TestCase("", "")]
|
||||
public void should_not_include_language_in_release_group(string title, string expected)
|
||||
{
|
||||
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestCase("The.Longest.Mystery.S02E04.720p.WEB-DL.AAC2.0.H.264-EVL-RP", "EVL")]
|
||||
[TestCase("The.Longest.Mystery.S02E04.720p.WEB-DL.AAC2.0.H.264-EVL-RP-RP", "EVL")]
|
||||
[TestCase("The.Longest.Mystery.S02E04.720p.WEB-DL.AAC2.0.H.264-EVL-Obfuscated", "EVL")]
|
||||
|
||||
@@ -118,6 +118,8 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("grp-zoo-s01e11-1080p", "grp-zoo", 1, 11)]
|
||||
[TestCase("Jeopardy!.S2016E14.2016-01-20.avi", "Jeopardy!", 2016, 14)]
|
||||
[TestCase("Ken.Burns.The.Civil.War.5of9.The.Universe.Of.Battle.1990.DVDRip.x264-HANDJOB", "Ken Burns The Civil War", 1, 5)]
|
||||
[TestCase("Judge Judy 2016 02 25 S20E142", "Judge Judy", 20, 142)]
|
||||
[TestCase("Judge Judy 2016 02 25 S20E143", "Judge Judy", 20, 143)]
|
||||
//[TestCase("", "", 0, 0)]
|
||||
public void should_parse_single_episode(string postTitle, string title, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user