mirror of
https://github.com/Radarr/Radarr.git
synced 2024-10-05 15:47:20 +02:00
Fixed: Automation/Integration/Unit Tests
This commit is contained in:
parent
944f420270
commit
7f221c7834
21
.gitattributes
vendored
21
.gitattributes
vendored
@ -1,22 +1,9 @@
|
|||||||
# Auto detect text files and perform LF normalization
|
# Auto detect text files and perform LF normalization
|
||||||
*text eol=lf
|
*text eol=lf
|
||||||
|
|
||||||
|
# Explicitly set bash scripts to have unix endings
|
||||||
|
*.sh text eol=lf
|
||||||
|
|
||||||
# Custom for Visual Studio
|
# Custom for Visual Studio
|
||||||
*.cs diff=csharp
|
*.cs diff=csharp
|
||||||
#*.sln merge=union
|
*.sln merge=union
|
||||||
#*.csproj merge=union
|
|
||||||
#*.vbproj merge=union
|
|
||||||
#*.fsproj merge=union
|
|
||||||
#*.dbproj merge=union
|
|
||||||
|
|
||||||
# Standard to msysgit
|
|
||||||
*.doc diff=astextplain
|
|
||||||
*.DOC diff=astextplain
|
|
||||||
*.docx diff=astextplain
|
|
||||||
*.DOCX diff=astextplain
|
|
||||||
*.dot diff=astextplain
|
|
||||||
*.DOT diff=astextplain
|
|
||||||
*.pdf diff=astextplain
|
|
||||||
*.PDF diff=astextplain
|
|
||||||
*.rtf diff=astextplain
|
|
||||||
*.RTF diff=astextplain
|
|
||||||
|
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -1,4 +0,0 @@
|
|||||||
[submodule "src/ExternalModules/CurlSharp"]
|
|
||||||
path = src/ExternalModules/CurlSharp
|
|
||||||
url = https://github.com/Sonarr/CurlSharp.git
|
|
||||||
branch = master
|
|
@ -238,17 +238,16 @@ stages:
|
|||||||
buildType: 'current'
|
buildType: 'current'
|
||||||
artifactName: WindowsTests
|
artifactName: WindowsTests
|
||||||
targetPath: $(testsFolder)
|
targetPath: $(testsFolder)
|
||||||
|
- bash: |
|
||||||
|
wget https://mediaarea.net/repo/deb/repo-mediaarea_1.0-9_all.deb
|
||||||
|
sudo dpkg -i repo-mediaarea_1.0-9_all.deb
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libmediainfo-dev libmediainfo0v5 mediainfo
|
||||||
|
displayName: Install mediainfo
|
||||||
|
condition: and(succeeded(), eq(variables['osName'], 'Linux'))
|
||||||
- powershell: Set-Service SCardSvr -StartupType Manual
|
- powershell: Set-Service SCardSvr -StartupType Manual
|
||||||
displayName: Enable Windows Test Service
|
displayName: Enable Windows Test Service
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||||
- bash: |
|
|
||||||
sudo apt install dos2unix
|
|
||||||
dos2unix ${TESTSFOLDER}/test.sh
|
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Linux'))
|
|
||||||
- bash: |
|
|
||||||
brew install dos2unix
|
|
||||||
dos2unix ${TESTSFOLDER}/test.sh
|
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Mac'))
|
|
||||||
- task: Bash@3
|
- task: Bash@3
|
||||||
displayName: Run Tests
|
displayName: Run Tests
|
||||||
env:
|
env:
|
||||||
@ -326,14 +325,6 @@ stages:
|
|||||||
mkdir -p ./bin/
|
mkdir -p ./bin/
|
||||||
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
|
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
|
||||||
displayName: Move Package Contents
|
displayName: Move Package Contents
|
||||||
- bash: |
|
|
||||||
sudo apt install dos2unix
|
|
||||||
dos2unix ${TESTSFOLDER}/test.sh
|
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Linux'))
|
|
||||||
- bash: |
|
|
||||||
brew install dos2unix
|
|
||||||
dos2unix ${TESTSFOLDER}/test.sh
|
|
||||||
condition: and(succeeded(), eq(variables['osName'], 'Mac'))
|
|
||||||
- task: Bash@3
|
- task: Bash@3
|
||||||
displayName: Run Integration Tests
|
displayName: Run Integration Tests
|
||||||
inputs:
|
inputs:
|
||||||
|
4
build.sh
4
build.sh
@ -139,7 +139,7 @@ Build()
|
|||||||
RunGulp()
|
RunGulp()
|
||||||
{
|
{
|
||||||
ProgressStart 'yarn install'
|
ProgressStart 'yarn install'
|
||||||
yarn install
|
yarn install --frozen-lockfile
|
||||||
ProgressEnd 'yarn install'
|
ProgressEnd 'yarn install'
|
||||||
|
|
||||||
LintUI
|
LintUI
|
||||||
@ -254,8 +254,6 @@ PackageTests()
|
|||||||
|
|
||||||
echo "Adding Radarr.Core.dll.config (for dllmap)"
|
echo "Adding Radarr.Core.dll.config (for dllmap)"
|
||||||
cp $sourceFolder/NzbDrone.Core/Radarr.Core.dll.config $testPackageFolder
|
cp $sourceFolder/NzbDrone.Core/Radarr.Core.dll.config $testPackageFolder
|
||||||
echo "Copying CurlSharp libraries"
|
|
||||||
cp $sourceFolder/ExternalModules/CurlSharp/libs/i386/* $testPackageFolder
|
|
||||||
|
|
||||||
echo "Adding sqlite dylibs"
|
echo "Adding sqlite dylibs"
|
||||||
cp $sourceFolder/Libraries/Sqlite/*.dylib $testPackageFolder
|
cp $sourceFolder/Libraries/Sqlite/*.dylib $testPackageFolder
|
||||||
|
@ -16,7 +16,9 @@ class About extends Component {
|
|||||||
const {
|
const {
|
||||||
version,
|
version,
|
||||||
isMonoRuntime,
|
isMonoRuntime,
|
||||||
|
isDocker,
|
||||||
runtimeVersion,
|
runtimeVersion,
|
||||||
|
migrationVersion,
|
||||||
appData,
|
appData,
|
||||||
startupPath,
|
startupPath,
|
||||||
mode,
|
mode,
|
||||||
@ -41,6 +43,19 @@ class About extends Component {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isDocker &&
|
||||||
|
<DescriptionListItem
|
||||||
|
title="Docker"
|
||||||
|
data={'True'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
<DescriptionListItem
|
||||||
|
title="DB Migration"
|
||||||
|
data={migrationVersion}
|
||||||
|
/>
|
||||||
|
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
title="AppData directory"
|
title="AppData directory"
|
||||||
data={appData}
|
data={appData}
|
||||||
@ -77,6 +92,8 @@ About.propTypes = {
|
|||||||
version: PropTypes.string.isRequired,
|
version: PropTypes.string.isRequired,
|
||||||
isMonoRuntime: PropTypes.bool.isRequired,
|
isMonoRuntime: PropTypes.bool.isRequired,
|
||||||
runtimeVersion: PropTypes.string.isRequired,
|
runtimeVersion: PropTypes.string.isRequired,
|
||||||
|
isDocker: PropTypes.bool.isRequired,
|
||||||
|
migrationVersion: PropTypes.number.isRequired,
|
||||||
appData: PropTypes.string.isRequired,
|
appData: PropTypes.string.isRequired,
|
||||||
startupPath: PropTypes.string.isRequired,
|
startupPath: PropTypes.string.isRequired,
|
||||||
mode: PropTypes.string.isRequired,
|
mode: PropTypes.string.isRequired,
|
||||||
|
@ -154,12 +154,26 @@ class Health extends Component {
|
|||||||
const internalLink = getInternalLink(item.source);
|
const internalLink = getInternalLink(item.source);
|
||||||
const testLink = getTestLink(item.source, this.props);
|
const testLink = getTestLink(item.source, this.props);
|
||||||
|
|
||||||
|
let kind = kinds.WARNING;
|
||||||
|
switch (item.type.toLowerCase()) {
|
||||||
|
case 'error':
|
||||||
|
kind = kinds.DANGER;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case 'warning':
|
||||||
|
kind = kinds.WARNING;
|
||||||
|
break;
|
||||||
|
case 'notice':
|
||||||
|
kind = kinds.INFO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow key={`health${item.message}`}>
|
<TableRow key={`health${item.message}`}>
|
||||||
<TableRowCell>
|
<TableRowCell>
|
||||||
<Icon
|
<Icon
|
||||||
name={icons.DANGER}
|
name={icons.DANGER}
|
||||||
kind={item.type.toLowerCase() === 'error' ? kinds.DANGER : kinds.WARNING}
|
kind={kind}
|
||||||
title={titleCase(item.type)}
|
title={titleCase(item.type)}
|
||||||
/>
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
import formatDate from 'Utilities/Date/formatDate';
|
import formatDate from 'Utilities/Date/formatDate';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
@ -26,7 +26,7 @@ class Updates extends Component {
|
|||||||
generalSettingsError,
|
generalSettingsError,
|
||||||
items,
|
items,
|
||||||
isInstallingUpdate,
|
isInstallingUpdate,
|
||||||
updateMechanism,
|
isDocker,
|
||||||
shortDateFormat,
|
shortDateFormat,
|
||||||
onInstallLatestPress
|
onInstallLatestPress
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -37,12 +37,6 @@ class Updates extends Component {
|
|||||||
const hasUpdateToInstall = hasUpdates && _.some(items, { installable: true, latest: true });
|
const hasUpdateToInstall = hasUpdates && _.some(items, { installable: true, latest: true });
|
||||||
const noUpdateToInstall = hasUpdates && !hasUpdateToInstall;
|
const noUpdateToInstall = hasUpdates && !hasUpdateToInstall;
|
||||||
|
|
||||||
const externalUpdaterMessages = {
|
|
||||||
external: 'Unable to update Radarr directly, Radarr is configured to use an external update mechanism',
|
|
||||||
apt: 'Unable to update Radarr directly, use apt to install the update',
|
|
||||||
docker: 'Unable to update Radarr directly, update the docker container to receive the update'
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent title="Updates">
|
<PageContent title="Updates">
|
||||||
<PageContentBodyConnector>
|
<PageContentBodyConnector>
|
||||||
@ -58,29 +52,24 @@ class Updates extends Component {
|
|||||||
|
|
||||||
{
|
{
|
||||||
hasUpdateToInstall &&
|
hasUpdateToInstall &&
|
||||||
<div className={styles.messageContainer}>
|
<div className={styles.updateAvailable}>
|
||||||
{
|
{
|
||||||
updateMechanism === 'builtIn' || updateMechanism === 'script' ?
|
!isDocker &&
|
||||||
<SpinnerButton
|
<SpinnerButton
|
||||||
className={styles.updateAvailable}
|
className={styles.updateAvailable}
|
||||||
kind={kinds.PRIMARY}
|
kind={kinds.PRIMARY}
|
||||||
isSpinning={isInstallingUpdate}
|
isSpinning={isInstallingUpdate}
|
||||||
onPress={onInstallLatestPress}
|
onPress={onInstallLatestPress}
|
||||||
>
|
>
|
||||||
Install Latest
|
Install Latest
|
||||||
</SpinnerButton> :
|
</SpinnerButton>
|
||||||
|
}
|
||||||
|
|
||||||
<Fragment>
|
{
|
||||||
<Icon
|
isDocker &&
|
||||||
name={icons.WARNING}
|
<div className={styles.upToDateMessage}>
|
||||||
kind={kinds.WARNING}
|
An update is available. Please update your Docker image and re-create the container.
|
||||||
size={30}
|
</div>
|
||||||
/>
|
|
||||||
|
|
||||||
<div className={styles.message}>
|
|
||||||
{externalUpdaterMessages[updateMechanism] || externalUpdaterMessages.external}
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -209,6 +198,7 @@ Updates.propTypes = {
|
|||||||
generalSettingsError: PropTypes.object,
|
generalSettingsError: PropTypes.object,
|
||||||
items: PropTypes.array.isRequired,
|
items: PropTypes.array.isRequired,
|
||||||
isInstallingUpdate: PropTypes.bool.isRequired,
|
isInstallingUpdate: PropTypes.bool.isRequired,
|
||||||
|
isDocker: PropTypes.bool.isRequired,
|
||||||
updateMechanism: PropTypes.string,
|
updateMechanism: PropTypes.string,
|
||||||
shortDateFormat: PropTypes.string.isRequired,
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
onInstallLatestPress: PropTypes.func.isRequired
|
onInstallLatestPress: PropTypes.func.isRequired
|
||||||
|
@ -7,6 +7,7 @@ import { fetchUpdates } from 'Store/Actions/systemActions';
|
|||||||
import { executeCommand } from 'Store/Actions/commandActions';
|
import { executeCommand } from 'Store/Actions/commandActions';
|
||||||
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
|
||||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||||
|
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
|
||||||
import * as commandNames from 'Commands/commandNames';
|
import * as commandNames from 'Commands/commandNames';
|
||||||
import Updates from './Updates';
|
import Updates from './Updates';
|
||||||
|
|
||||||
@ -16,12 +17,14 @@ function createMapStateToProps() {
|
|||||||
(state) => state.system.updates,
|
(state) => state.system.updates,
|
||||||
(state) => state.settings.general,
|
(state) => state.settings.general,
|
||||||
createUISettingsSelector(),
|
createUISettingsSelector(),
|
||||||
|
createSystemStatusSelector(),
|
||||||
createCommandExecutingSelector(commandNames.APPLICATION_UPDATE),
|
createCommandExecutingSelector(commandNames.APPLICATION_UPDATE),
|
||||||
(
|
(
|
||||||
currentVersion,
|
currentVersion,
|
||||||
updates,
|
updates,
|
||||||
generalSettings,
|
generalSettings,
|
||||||
uiSettings,
|
uiSettings,
|
||||||
|
systemStatus,
|
||||||
isInstallingUpdate
|
isInstallingUpdate
|
||||||
) => {
|
) => {
|
||||||
const {
|
const {
|
||||||
@ -40,6 +43,7 @@ function createMapStateToProps() {
|
|||||||
generalSettingsError: generalSettings.error,
|
generalSettingsError: generalSettings.error,
|
||||||
items,
|
items,
|
||||||
isInstallingUpdate,
|
isInstallingUpdate,
|
||||||
|
isDocker: systemStatus.isDocker,
|
||||||
updateMechanism: generalSettings.item.updateMechanism,
|
updateMechanism: generalSettings.item.updateMechanism,
|
||||||
shortDateFormat: uiSettings.shortDateFormat
|
shortDateFormat: uiSettings.shortDateFormat
|
||||||
};
|
};
|
||||||
|
@ -20,8 +20,10 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<Configuration Condition="'$(Configuration)'==''">Release</Configuration>
|
||||||
<!-- Centralize intermediate and default outputs -->
|
<!-- Centralize intermediate and default outputs -->
|
||||||
<IntermediateOutputPath>$(RadarrRootDir)_temp\obj\$(Configuration)\$(MSBuildProjectName)\</IntermediateOutputPath>
|
<BaseIntermediateOutputPath>$(RadarrRootDir)_temp\obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
|
||||||
|
<IntermediateOutputPath>$(RadarrRootDir)_temp\obj\$(MSBuildProjectName)\$(Configuration)\</IntermediateOutputPath>
|
||||||
<OutputPath>$(RadarrRootDir)_temp\bin\$(Configuration)\$(MSBuildProjectName)\</OutputPath>
|
<OutputPath>$(RadarrRootDir)_temp\bin\$(Configuration)\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
|
||||||
<!-- Output to _output and _tests respectively -->
|
<!-- Output to _output and _tests respectively -->
|
||||||
@ -30,6 +32,7 @@
|
|||||||
<OutputPath Condition="'$(RadarrOutputType)'=='Update'">$(RadarrRootDir)_output\Radarr.Update\</OutputPath>
|
<OutputPath Condition="'$(RadarrOutputType)'=='Update'">$(RadarrRootDir)_output\Radarr.Update\</OutputPath>
|
||||||
|
|
||||||
<!-- Paths relative to project file for better readability -->
|
<!-- Paths relative to project file for better readability -->
|
||||||
|
<BaseIntermediateOutputPath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(BaseIntermediateOutputPath)'))</BaseIntermediateOutputPath>
|
||||||
<IntermediateOutputPath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)'))</IntermediateOutputPath>
|
<IntermediateOutputPath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)'))</IntermediateOutputPath>
|
||||||
<OutputPath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(OutputPath)'))</OutputPath>
|
<OutputPath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)', '$(OutputPath)'))</OutputPath>
|
||||||
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
Subproject commit cfdbbbd9c6b9612c2756245049a8234ce87dc576
|
|
@ -1,9 +1,10 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NzbDrone.Common.Disk;
|
|
||||||
using Nancy;
|
using Nancy;
|
||||||
using Nancy.Responses;
|
using Nancy.Responses;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using Radarr.Http;
|
using Radarr.Http;
|
||||||
|
|
||||||
@ -54,6 +55,8 @@ private List<LogFileResource> GetLogFilesResponse()
|
|||||||
|
|
||||||
private Response GetLogFileResponse(string filename)
|
private Response GetLogFileResponse(string filename)
|
||||||
{
|
{
|
||||||
|
LogManager.Flush();
|
||||||
|
|
||||||
var filePath = GetLogFilePath(filename);
|
var filePath = GetLogFilePath(filename);
|
||||||
|
|
||||||
if (!_diskProvider.FileExists(filePath))
|
if (!_diskProvider.FileExists(filePath))
|
||||||
|
@ -34,7 +34,9 @@ public AutomationTest()
|
|||||||
[OneTimeSetUp]
|
[OneTimeSetUp]
|
||||||
public void SmokeTestSetup()
|
public void SmokeTestSetup()
|
||||||
{
|
{
|
||||||
driver = new FirefoxDriver();
|
var options = new FirefoxOptions();
|
||||||
|
options.AddArguments("--headless");
|
||||||
|
driver = new FirefoxDriver(options);
|
||||||
|
|
||||||
_runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger());
|
_runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger());
|
||||||
_runner.KillAll();
|
_runner.KillAll();
|
||||||
@ -45,7 +47,7 @@ public void SmokeTestSetup()
|
|||||||
var page = new PageBase(driver);
|
var page = new PageBase(driver);
|
||||||
page.WaitForNoSpinner();
|
page.WaitForNoSpinner();
|
||||||
|
|
||||||
driver.ExecuteScript("window.NzbDrone.NameViews = true;");
|
driver.ExecuteScript("window.Radarr.NameViews = true;");
|
||||||
|
|
||||||
GetPageErrors().Should().BeEmpty();
|
GetPageErrors().Should().BeEmpty();
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,11 @@ public void Setup()
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void series_page()
|
public void movie_page()
|
||||||
{
|
{
|
||||||
page.SeriesNavIcon.Click();
|
page.MovieNavIcon.Click();
|
||||||
page.WaitForNoSpinner();
|
page.WaitForNoSpinner();
|
||||||
page.FindByClass("iv-series-index-seriesindexlayout").Should().NotBeNull();
|
page.Find(By.CssSelector("div[class*='MovieIndex']")).Should().NotBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -30,7 +30,7 @@ public void calendar_page()
|
|||||||
page.CalendarNavIcon.Click();
|
page.CalendarNavIcon.Click();
|
||||||
page.WaitForNoSpinner();
|
page.WaitForNoSpinner();
|
||||||
|
|
||||||
page.FindByClass("iv-calendar-calendarlayout").Should().NotBeNull();
|
page.Find(By.CssSelector("div[class*='CalendarPage']")).Should().NotBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -39,16 +39,9 @@ public void activity_page()
|
|||||||
page.ActivityNavIcon.Click();
|
page.ActivityNavIcon.Click();
|
||||||
page.WaitForNoSpinner();
|
page.WaitForNoSpinner();
|
||||||
|
|
||||||
page.FindByClass("iv-activity-activitylayout").Should().NotBeNull();
|
page.Find(By.LinkText("Queue")).Should().NotBeNull();
|
||||||
}
|
page.Find(By.LinkText("History")).Should().NotBeNull();
|
||||||
|
page.Find(By.LinkText("Blacklist")).Should().NotBeNull();
|
||||||
[Test]
|
|
||||||
public void wanted_page()
|
|
||||||
{
|
|
||||||
page.WantedNavIcon.Click();
|
|
||||||
page.WaitForNoSpinner();
|
|
||||||
|
|
||||||
page.FindByClass("iv-wanted-missing-missinglayout").Should().NotBeNull();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -57,20 +50,20 @@ public void system_page()
|
|||||||
page.SystemNavIcon.Click();
|
page.SystemNavIcon.Click();
|
||||||
page.WaitForNoSpinner();
|
page.WaitForNoSpinner();
|
||||||
|
|
||||||
page.FindByClass("iv-system-systemlayout").Should().NotBeNull();
|
page.Find(By.CssSelector("div[class*='Health']")).Should().NotBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void add_series_page()
|
public void add_movie_page()
|
||||||
{
|
{
|
||||||
page.SeriesNavIcon.Click();
|
page.MovieNavIcon.Click();
|
||||||
page.WaitForNoSpinner();
|
page.WaitForNoSpinner();
|
||||||
|
|
||||||
page.Find(By.LinkText("Add Series")).Click();
|
page.Find(By.LinkText("Add New")).Click();
|
||||||
|
|
||||||
page.WaitForNoSpinner();
|
page.WaitForNoSpinner();
|
||||||
|
|
||||||
page.FindByClass("iv-addseries-addserieslayout").Should().NotBeNull();
|
page.Find(By.CssSelector("input[class*='AddNewMovie/searchInput']")).Should().NotBeNull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -47,16 +47,14 @@ public void WaitForNoSpinner(int timeout = 30)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public IWebElement SeriesNavIcon => FindByClass("x-series-nav");
|
public IWebElement MovieNavIcon => Find(By.LinkText("Movies"));
|
||||||
|
|
||||||
public IWebElement CalendarNavIcon => FindByClass("x-calendar-nav");
|
public IWebElement CalendarNavIcon => Find(By.LinkText("Calendar"));
|
||||||
|
|
||||||
public IWebElement ActivityNavIcon => FindByClass("x-activity-nav");
|
public IWebElement ActivityNavIcon => Find(By.LinkText("Activity"));
|
||||||
|
|
||||||
public IWebElement WantedNavIcon => FindByClass("x-wanted-nav");
|
public IWebElement SettingNavIcon => Find(By.LinkText("Settings"));
|
||||||
|
|
||||||
public IWebElement SettingNavIcon => FindByClass("x-settings-nav");
|
public IWebElement SystemNavIcon => Find(By.PartialLinkText("System"));
|
||||||
|
|
||||||
public IWebElement SystemNavIcon => FindByClass("x-system-nav");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,7 @@
|
|||||||
<Platforms>x86</Platforms>
|
<Platforms>x86</Platforms>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Selenium.Firefox.WebDriver" Version="0.24.0" />
|
||||||
<PackageReference Include="Selenium.Support" Version="3.141.0" />
|
<PackageReference Include="Selenium.Support" Version="3.141.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -248,12 +248,14 @@ public void should_be_able_to_rename_open_hardlinks_with_fileshare_delete()
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
[Ignore("No longer behaving this way in a Windows 10 Feature Update")]
|
||||||
public void should_not_be_able_to_rename_open_hardlinks_with_fileshare_none()
|
public void should_not_be_able_to_rename_open_hardlinks_with_fileshare_none()
|
||||||
{
|
{
|
||||||
Assert.Throws<IOException>(() => DoHardLinkRename(FileShare.None));
|
Assert.Throws<IOException>(() => DoHardLinkRename(FileShare.None));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
[Ignore("No longer behaving this way in a Windows 10 Feature Update")]
|
||||||
public void should_not_be_able_to_rename_open_hardlinks_with_fileshare_write()
|
public void should_not_be_able_to_rename_open_hardlinks_with_fileshare_write()
|
||||||
{
|
{
|
||||||
Assert.Throws<IOException>(() => DoHardLinkRename(FileShare.Read));
|
Assert.Throws<IOException>(() => DoHardLinkRename(FileShare.Read));
|
||||||
|
@ -22,7 +22,6 @@ namespace NzbDrone.Common.Test.Http
|
|||||||
{
|
{
|
||||||
[IntegrationTest]
|
[IntegrationTest]
|
||||||
[TestFixture(typeof(ManagedHttpDispatcher))]
|
[TestFixture(typeof(ManagedHttpDispatcher))]
|
||||||
[TestFixture(typeof(CurlHttpDispatcher))]
|
|
||||||
public class HttpClientFixture<TDispatcher> : TestBase<HttpClient> where TDispatcher : IHttpDispatcher
|
public class HttpClientFixture<TDispatcher> : TestBase<HttpClient> where TDispatcher : IHttpDispatcher
|
||||||
{
|
{
|
||||||
private string[] _httpBinHosts;
|
private string[] _httpBinHosts;
|
||||||
|
@ -9,22 +9,21 @@
|
|||||||
using NzbDrone.Common.Processes;
|
using NzbDrone.Common.Processes;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
using NzbDrone.Test.Dummy;
|
using NzbDrone.Test.Dummy;
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace NzbDrone.Common.Test
|
namespace NzbDrone.Common.Test
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class ProcessProviderTests : TestBase<ProcessProvider>
|
public class ProcessProviderFixture : TestBase<ProcessProvider>
|
||||||
{
|
{
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
Process.GetProcessesByName(DummyApp.DUMMY_PROCCESS_NAME).ToList().ForEach(c =>
|
Process.GetProcessesByName(DummyApp.DUMMY_PROCCESS_NAME).ToList().ForEach(c =>
|
||||||
{
|
{
|
||||||
c.Kill();
|
c.Kill();
|
||||||
c.WaitForExit();
|
c.WaitForExit();
|
||||||
});
|
});
|
||||||
|
|
||||||
Process.GetProcessesByName(DummyApp.DUMMY_PROCCESS_NAME).Should().BeEmpty();
|
Process.GetProcessesByName(DummyApp.DUMMY_PROCCESS_NAME).Should().BeEmpty();
|
||||||
}
|
}
|
||||||
@ -42,7 +41,7 @@ public void TearDown()
|
|||||||
{
|
{
|
||||||
TestLogger.Warn(ex, "{0} when killing process", ex.Message);
|
TestLogger.Warn(ex, "{0} when killing process", ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,18 +64,9 @@ public void GetProcessById_should_return_null_for_invalid_process(int processId)
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[Ignore("Shit appveyor")]
|
|
||||||
public void Should_be_able_to_start_process()
|
public void Should_be_able_to_start_process()
|
||||||
{
|
{
|
||||||
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
|
var process = StartDummyProcess();
|
||||||
UriBuilder uri = new UriBuilder(codeBase);
|
|
||||||
string path = Uri.UnescapeDataString(uri.Path);
|
|
||||||
var rPath = Path.GetDirectoryName(path);
|
|
||||||
|
|
||||||
var root = Directory.GetParent(rPath).Parent.Parent.Parent;
|
|
||||||
var DummyAppDir = Path.Combine(root.FullName, "NzbDrone.Test.Dummy", "bin", "Release");
|
|
||||||
|
|
||||||
var process = Subject.Start(Path.Combine(DummyAppDir, DummyApp.DUMMY_PROCCESS_NAME + ".exe"));
|
|
||||||
|
|
||||||
Subject.Exists(DummyApp.DUMMY_PROCCESS_NAME).Should()
|
Subject.Exists(DummyApp.DUMMY_PROCCESS_NAME).Should()
|
||||||
.BeTrue("excepted one dummy process to be already running");
|
.BeTrue("excepted one dummy process to be already running");
|
||||||
@ -88,6 +78,7 @@ public void Should_be_able_to_start_process()
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
[Explicit]
|
||||||
public void Should_be_able_to_start_powershell()
|
public void Should_be_able_to_start_powershell()
|
||||||
{
|
{
|
||||||
WindowsOnly();
|
WindowsOnly();
|
||||||
@ -137,7 +128,6 @@ public void Should_be_able_to_start_python()
|
|||||||
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[Ignore("Shit appveyor")]
|
|
||||||
public void kill_all_should_kill_all_process_with_name()
|
public void kill_all_should_kill_all_process_with_name()
|
||||||
{
|
{
|
||||||
var dummy1 = StartDummyProcess();
|
var dummy1 = StartDummyProcess();
|
||||||
@ -151,7 +141,8 @@ public void kill_all_should_kill_all_process_with_name()
|
|||||||
|
|
||||||
private Process StartDummyProcess()
|
private Process StartDummyProcess()
|
||||||
{
|
{
|
||||||
return Subject.Start(DummyApp.DUMMY_PROCCESS_NAME + ".exe");
|
var path = Path.Combine(TestContext.CurrentContext.TestDirectory, DummyApp.DUMMY_PROCCESS_NAME + ".exe");
|
||||||
|
return Subject.Start(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -161,4 +152,4 @@ public void ToString_on_new_processInfo()
|
|||||||
ExceptionVerification.MarkInconclusive(typeof(Win32Exception));
|
ExceptionVerification.MarkInconclusive(typeof(Win32Exception));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,9 +11,4 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System.ServiceProcess" />
|
<Reference Include="System.ServiceProcess" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<None Include="..\ExternalModules\CurlSharp\libs\i386\*.*">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
</Project>
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Security.Principal;
|
||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -61,6 +62,10 @@ public void Exists_should_not_find_random_service()
|
|||||||
[Test]
|
[Test]
|
||||||
public void Service_should_be_installed_and_then_uninstalled()
|
public void Service_should_be_installed_and_then_uninstalled()
|
||||||
{
|
{
|
||||||
|
if (!IsAnAdministrator())
|
||||||
|
{
|
||||||
|
Assert.Inconclusive("Can't run test without Administrator rights");
|
||||||
|
}
|
||||||
|
|
||||||
Subject.ServiceExist(TEMP_SERVICE_NAME).Should().BeFalse("Service already installed");
|
Subject.ServiceExist(TEMP_SERVICE_NAME).Should().BeFalse("Service already installed");
|
||||||
Subject.Install(TEMP_SERVICE_NAME);
|
Subject.Install(TEMP_SERVICE_NAME);
|
||||||
@ -100,9 +105,13 @@ public void Should_be_able_to_start_and_stop_service()
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[Ignore("Shit appveyor")]
|
public void should_throw_if_starting_a_running_service()
|
||||||
public void should_throw_if_starting_a_running_serivce()
|
|
||||||
{
|
{
|
||||||
|
if (!IsAnAdministrator())
|
||||||
|
{
|
||||||
|
Assert.Inconclusive("Can't run test without Administrator rights");
|
||||||
|
}
|
||||||
|
|
||||||
Subject.GetService(ALWAYS_INSTALLED_SERVICE).Status
|
Subject.GetService(ALWAYS_INSTALLED_SERVICE).Status
|
||||||
.Should().NotBe(ServiceControllerStatus.Running);
|
.Should().NotBe(ServiceControllerStatus.Running);
|
||||||
|
|
||||||
@ -128,5 +137,10 @@ public void Should_log_warn_if_on_stop_if_service_is_already_stopped()
|
|||||||
|
|
||||||
ExceptionVerification.ExpectedWarns(1);
|
ExceptionVerification.ExpectedWarns(1);
|
||||||
}
|
}
|
||||||
|
private static bool IsAnAdministrator()
|
||||||
|
{
|
||||||
|
var principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
|
||||||
|
return principal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<configuration>
|
|
||||||
<dllmap dll="libcurl.dll" target="libcurl.so.4" />
|
|
||||||
<dllmap os="osx" dll="libcurl.dll" target="libcurl.4.dylib"/>
|
|
||||||
<!--<dllmap os="freebsd" dll="libcurl.dll" target="libcurl.so.4" />-->
|
|
||||||
<!--<dllmap os="solaris" dll="libcurl.dll" target="libcurl.so.4" />-->
|
|
||||||
</configuration>
|
|
@ -27,6 +27,8 @@ static BuildInfo()
|
|||||||
Release = $"{Version}-{Branch}";
|
Release = $"{Version}-{Branch}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string AppName { get; } = "Radarr";
|
||||||
|
|
||||||
public static Version Version { get; }
|
public static Version Version { get; }
|
||||||
public static String Branch { get; }
|
public static String Branch { get; }
|
||||||
public static string Release { get; }
|
public static string Release { get; }
|
||||||
|
@ -15,6 +15,9 @@ public class OsInfo : IOsInfo
|
|||||||
public static bool IsOsx => Os == Os.Osx;
|
public static bool IsOsx => Os == Os.Osx;
|
||||||
public static bool IsWindows => Os == Os.Windows;
|
public static bool IsWindows => Os == Os.Windows;
|
||||||
|
|
||||||
|
// this needs to not be static so we can mock it
|
||||||
|
public bool IsDocker { get; }
|
||||||
|
|
||||||
public string Version { get; }
|
public string Version { get; }
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public string FullName { get; }
|
public string FullName { get; }
|
||||||
@ -83,8 +86,10 @@ public OsInfo(IEnumerable<IOsVersionAdapter> versionAdapters, Logger logger)
|
|||||||
FullName = Name;
|
FullName = Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
Environment.SetEnvironmentVariable("OS_NAME", Name);
|
if (IsLinux && File.Exists("/proc/1/cgroup") && File.ReadAllText("/proc/1/cgroup").Contains("/docker/"))
|
||||||
Environment.SetEnvironmentVariable("OS_VERSION", Version);
|
{
|
||||||
|
IsDocker = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,6 +98,7 @@ public interface IOsInfo
|
|||||||
string Version { get; }
|
string Version { get; }
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
string FullName { get; }
|
string FullName { get; }
|
||||||
|
bool IsDocker { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Os
|
public enum Os
|
||||||
|
@ -35,7 +35,16 @@ public RuntimeInfo(IServiceProvider serviceProvider, Logger logger)
|
|||||||
|
|
||||||
static RuntimeInfo()
|
static RuntimeInfo()
|
||||||
{
|
{
|
||||||
IsProduction = InternalIsProduction();
|
var officialBuild = InternalIsOfficialBuild();
|
||||||
|
|
||||||
|
// An build running inside of the testing environment. (Analytics disabled)
|
||||||
|
IsTesting = InternalIsTesting();
|
||||||
|
|
||||||
|
// An official build running outside of the testing environment. (Analytics configurable)
|
||||||
|
IsProduction = !IsTesting && officialBuild;
|
||||||
|
|
||||||
|
// An unofficial build running outside of the testing environment. (Analytics enabled)
|
||||||
|
IsDevelopment = !IsTesting && !officialBuild && !InternalIsDebug();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DateTime StartTime
|
public DateTime StartTime
|
||||||
@ -104,23 +113,21 @@ public RuntimeMode Mode
|
|||||||
public bool RestartPending { get; set; }
|
public bool RestartPending { get; set; }
|
||||||
public string ExecutingApplication { get; }
|
public string ExecutingApplication { get; }
|
||||||
|
|
||||||
|
public static bool IsTesting { get; }
|
||||||
public static bool IsProduction { get; }
|
public static bool IsProduction { get; }
|
||||||
|
public static bool IsDevelopment { get; }
|
||||||
|
|
||||||
private static bool InternalIsProduction()
|
|
||||||
|
private static bool InternalIsTesting()
|
||||||
{
|
{
|
||||||
if (BuildInfo.IsDebug || Debugger.IsAttached) return false;
|
|
||||||
|
|
||||||
//Official builds will never have such a high revision
|
|
||||||
if (BuildInfo.Version.Revision > 10000) return false;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var lowerProcessName = Process.GetCurrentProcess().ProcessName.ToLower();
|
var lowerProcessName = Process.GetCurrentProcess().ProcessName.ToLower();
|
||||||
|
|
||||||
if (lowerProcessName.Contains("vshost")) return false;
|
if (lowerProcessName.Contains("vshost")) return true;
|
||||||
if (lowerProcessName.Contains("nunit")) return false;
|
if (lowerProcessName.Contains("nunit")) return true;
|
||||||
if (lowerProcessName.Contains("jetbrain")) return false;
|
if (lowerProcessName.Contains("jetbrain")) return true;
|
||||||
if (lowerProcessName.Contains("resharper")) return false;
|
if (lowerProcessName.Contains("resharper")) return true;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -130,7 +137,7 @@ private static bool InternalIsProduction()
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var currentAssemblyLocation = typeof(RuntimeInfo).Assembly.Location;
|
var currentAssemblyLocation = typeof(RuntimeInfo).Assembly.Location;
|
||||||
if (currentAssemblyLocation.ToLower().Contains("_output")) return false;
|
if (currentAssemblyLocation.ToLower().Contains("_output")) return true;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -138,13 +145,28 @@ private static bool InternalIsProduction()
|
|||||||
}
|
}
|
||||||
|
|
||||||
var lowerCurrentDir = Directory.GetCurrentDirectory().ToLower();
|
var lowerCurrentDir = Directory.GetCurrentDirectory().ToLower();
|
||||||
if (lowerCurrentDir.Contains("teamcity")) return false;
|
if (lowerCurrentDir.Contains("vsts")) return true;
|
||||||
if (lowerCurrentDir.Contains("buildagent")) return false;
|
if (lowerCurrentDir.Contains("buildagent")) return true;
|
||||||
if (lowerCurrentDir.Contains("_output")) return false;
|
if (lowerCurrentDir.Contains("_output")) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool InternalIsDebug()
|
||||||
|
{
|
||||||
|
if (BuildInfo.IsDebug || Debugger.IsAttached) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool InternalIsOfficialBuild()
|
||||||
|
{
|
||||||
|
//Official builds will never have such a high revision
|
||||||
|
if (BuildInfo.Version.Major >= 10 || BuildInfo.Version.Revision > 10000) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsWindowsTray { get; private set; }
|
public bool IsWindowsTray { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,342 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using CurlSharp;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Common.Http.Proxy;
|
|
||||||
|
|
||||||
namespace NzbDrone.Common.Http.Dispatchers
|
|
||||||
{
|
|
||||||
public class CurlHttpDispatcher : IHttpDispatcher
|
|
||||||
{
|
|
||||||
private static readonly Regex ExpiryDate = new Regex(@"(expires=)([^;]+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
|
||||||
|
|
||||||
private readonly IHttpProxySettingsProvider _proxySettingsProvider;
|
|
||||||
private readonly IUserAgentBuilder _userAgentBuilder;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
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 CurlHttpDispatcher(IHttpProxySettingsProvider proxySettingsProvider, IUserAgentBuilder userAgentBuilder, Logger logger)
|
|
||||||
{
|
|
||||||
_proxySettingsProvider = proxySettingsProvider;
|
|
||||||
_userAgentBuilder = userAgentBuilder;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CheckAvailability()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return CurlGlobalHandle.Instance.Initialize();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.Trace(ex, "Initializing curl failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
|
|
||||||
{
|
|
||||||
if (!CheckAvailability())
|
|
||||||
{
|
|
||||||
throw new ApplicationException("Curl failed to initialize.");
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (CurlGlobalHandle.Instance)
|
|
||||||
{
|
|
||||||
Stream responseStream = new MemoryStream();
|
|
||||||
Stream headerStream = new MemoryStream();
|
|
||||||
|
|
||||||
using (var curlEasy = new CurlEasy())
|
|
||||||
{
|
|
||||||
curlEasy.AutoReferer = false;
|
|
||||||
curlEasy.WriteFunction = (b, s, n, o) =>
|
|
||||||
{
|
|
||||||
responseStream.Write(b, 0, s * n);
|
|
||||||
return s * n;
|
|
||||||
};
|
|
||||||
curlEasy.HeaderFunction = (b, s, n, o) =>
|
|
||||||
{
|
|
||||||
headerStream.Write(b, 0, s * n);
|
|
||||||
return s * n;
|
|
||||||
};
|
|
||||||
|
|
||||||
AddProxy(curlEasy, request);
|
|
||||||
|
|
||||||
curlEasy.Url = request.Url.FullUri;
|
|
||||||
|
|
||||||
switch (request.Method)
|
|
||||||
{
|
|
||||||
case HttpMethod.GET:
|
|
||||||
curlEasy.HttpGet = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HttpMethod.POST:
|
|
||||||
curlEasy.Post = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HttpMethod.PUT:
|
|
||||||
curlEasy.Put = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotSupportedException($"HttpCurl method {request.Method} not supported");
|
|
||||||
}
|
|
||||||
curlEasy.UserAgent = _userAgentBuilder.GetUserAgent(request.UseSimplifiedUserAgent);
|
|
||||||
curlEasy.FollowLocation = false;
|
|
||||||
|
|
||||||
if (request.RequestTimeout != TimeSpan.Zero)
|
|
||||||
{
|
|
||||||
curlEasy.Timeout = (int)Math.Ceiling(request.RequestTimeout.TotalSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OsInfo.IsWindows)
|
|
||||||
{
|
|
||||||
curlEasy.CaInfo = _caBundleFilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cookies != null)
|
|
||||||
{
|
|
||||||
curlEasy.Cookie = cookies.GetCookieHeader((Uri)request.Url);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.ContentData != null)
|
|
||||||
{
|
|
||||||
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
|
|
||||||
using (var httpRequestHeaders = SerializeHeaders(request))
|
|
||||||
{
|
|
||||||
curlEasy.HttpHeader = httpRequestHeaders;
|
|
||||||
|
|
||||||
var result = curlEasy.Perform();
|
|
||||||
|
|
||||||
if (result != CurlCode.Ok)
|
|
||||||
{
|
|
||||||
switch (result)
|
|
||||||
{
|
|
||||||
case CurlCode.SslCaCert:
|
|
||||||
case (CurlCode)77:
|
|
||||||
throw new WebException(string.Format("Curl Error {0} for Url {1}, issues with your operating system SSL Root Certificate Bundle (ca-bundle).", result, curlEasy.Url));
|
|
||||||
default:
|
|
||||||
throw new WebException(string.Format("Curl Error {0} for Url {1}", result, curlEasy.Url));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var webHeaderCollection = ProcessHeaderStream(request, cookies, headerStream);
|
|
||||||
var responseData = ProcessResponseStream(request, responseStream, webHeaderCollection);
|
|
||||||
|
|
||||||
var httpHeader = new HttpHeader(webHeaderCollection);
|
|
||||||
|
|
||||||
return new HttpResponse(request, httpHeader, responseData, (HttpStatusCode)curlEasy.ResponseCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddProxy(CurlEasy curlEasy, HttpRequest request)
|
|
||||||
{
|
|
||||||
var proxySettings = _proxySettingsProvider.GetProxySettings(request);
|
|
||||||
if (proxySettings != null)
|
|
||||||
|
|
||||||
{
|
|
||||||
switch (proxySettings.Type)
|
|
||||||
{
|
|
||||||
case ProxyType.Http:
|
|
||||||
curlEasy.SetOpt(CurlOption.ProxyType, CurlProxyType.Http);
|
|
||||||
curlEasy.SetOpt(CurlOption.ProxyAuth, CurlHttpAuth.Basic);
|
|
||||||
curlEasy.SetOpt(CurlOption.ProxyUserPwd, proxySettings.Username + ":" + proxySettings.Password.ToString());
|
|
||||||
break;
|
|
||||||
case ProxyType.Socks4:
|
|
||||||
curlEasy.SetOpt(CurlOption.ProxyType, CurlProxyType.Socks4);
|
|
||||||
curlEasy.SetOpt(CurlOption.ProxyUsername, proxySettings.Username);
|
|
||||||
curlEasy.SetOpt(CurlOption.ProxyPassword, proxySettings.Password);
|
|
||||||
break;
|
|
||||||
case ProxyType.Socks5:
|
|
||||||
curlEasy.SetOpt(CurlOption.ProxyType, CurlProxyType.Socks5);
|
|
||||||
curlEasy.SetOpt(CurlOption.ProxyUsername, proxySettings.Username);
|
|
||||||
curlEasy.SetOpt(CurlOption.ProxyPassword, proxySettings.Password);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
curlEasy.SetOpt(CurlOption.Proxy, proxySettings.Host + ":" + proxySettings.Port.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private CurlSlist SerializeHeaders(HttpRequest request)
|
|
||||||
{
|
|
||||||
if (!request.Headers.ContainsKey("Accept-Encoding"))
|
|
||||||
{
|
|
||||||
request.Headers.Add("Accept-Encoding", "gzip");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.Headers.ContentType == null)
|
|
||||||
{
|
|
||||||
request.Headers.ContentType = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
var curlHeaders = new CurlSlist();
|
|
||||||
foreach (var header in request.Headers)
|
|
||||||
{
|
|
||||||
curlHeaders.Append(header.Key + ": " + header.Value.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return curlHeaders;
|
|
||||||
}
|
|
||||||
|
|
||||||
private WebHeaderCollection ProcessHeaderStream(HttpRequest request, CookieContainer cookies, Stream headerStream)
|
|
||||||
{
|
|
||||||
headerStream.Position = 0;
|
|
||||||
var headerData = headerStream.ToBytes();
|
|
||||||
var headerString = Encoding.ASCII.GetString(headerData);
|
|
||||||
|
|
||||||
var webHeaderCollection = new WebHeaderCollection();
|
|
||||||
|
|
||||||
// following a redirect we could have two sets of headers, so only process the last one
|
|
||||||
foreach (var header in headerString.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).Reverse())
|
|
||||||
{
|
|
||||||
if (!header.Contains(":")) break;
|
|
||||||
webHeaderCollection.Add(header);
|
|
||||||
}
|
|
||||||
|
|
||||||
var setCookie = webHeaderCollection.Get("Set-Cookie");
|
|
||||||
if (setCookie != null && setCookie.Length > 0 && cookies != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
cookies.SetCookies((Uri)request.Url, FixSetCookieHeader(setCookie));
|
|
||||||
}
|
|
||||||
catch (CookieException ex)
|
|
||||||
{
|
|
||||||
_logger.Debug("Rejected cookie {0}: {1}", ex.InnerException.Message, setCookie);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return webHeaderCollection;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string FixSetCookieHeader(string setCookie)
|
|
||||||
{
|
|
||||||
// fix up the date if it was malformed
|
|
||||||
var setCookieClean = ExpiryDate.Replace(setCookie, delegate(Match match)
|
|
||||||
{
|
|
||||||
string shortFormat = "ddd, dd-MMM-yy HH:mm:ss";
|
|
||||||
string longFormat = "ddd, dd-MMM-yyyy HH:mm:ss";
|
|
||||||
DateTime dt;
|
|
||||||
if (DateTime.TryParseExact(match.Groups[2].Value, longFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt) ||
|
|
||||||
DateTime.TryParseExact(match.Groups[2].Value, shortFormat, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt) ||
|
|
||||||
DateTime.TryParse(match.Groups[2].Value, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt))
|
|
||||||
return match.Groups[1].Value + dt.ToUniversalTime().ToString(longFormat, CultureInfo.InvariantCulture) + " GMT";
|
|
||||||
else
|
|
||||||
return match.Value;
|
|
||||||
});
|
|
||||||
return setCookieClean;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] ProcessResponseStream(HttpRequest request, Stream responseStream, WebHeaderCollection webHeaderCollection)
|
|
||||||
{
|
|
||||||
byte[] bytes = null;
|
|
||||||
responseStream.Position = 0;
|
|
||||||
|
|
||||||
if (responseStream.Length != 0)
|
|
||||||
{
|
|
||||||
var encoding = webHeaderCollection["Content-Encoding"];
|
|
||||||
if (encoding != null)
|
|
||||||
{
|
|
||||||
if (encoding.IndexOf("gzip") != -1)
|
|
||||||
{
|
|
||||||
using (var zipStream = new GZipStream(responseStream, CompressionMode.Decompress))
|
|
||||||
{
|
|
||||||
bytes = zipStream.ToBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
webHeaderCollection.Remove("Content-Encoding");
|
|
||||||
}
|
|
||||||
else if (encoding.IndexOf("deflate") != -1)
|
|
||||||
{
|
|
||||||
using (var deflateStream = new DeflateStream(responseStream, CompressionMode.Decompress))
|
|
||||||
{
|
|
||||||
bytes = deflateStream.ToBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
webHeaderCollection.Remove("Content-Encoding");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytes == null) bytes = responseStream.ToBytes();
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class CurlGlobalHandle : SafeHandle
|
|
||||||
{
|
|
||||||
public static readonly CurlGlobalHandle Instance = new CurlGlobalHandle();
|
|
||||||
|
|
||||||
private bool _initialized;
|
|
||||||
private bool _available;
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
base.Dispose(disposing);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CurlGlobalHandle()
|
|
||||||
: base(IntPtr.Zero, true)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Initialize()
|
|
||||||
{
|
|
||||||
lock (CurlGlobalHandle.Instance)
|
|
||||||
{
|
|
||||||
if (_initialized)
|
|
||||||
return _available;
|
|
||||||
|
|
||||||
_initialized = true;
|
|
||||||
_available = Curl.GlobalInit(CurlInitFlag.All) == CurlCode.Ok;
|
|
||||||
|
|
||||||
return _available;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool ReleaseHandle()
|
|
||||||
{
|
|
||||||
if (_initialized && _available)
|
|
||||||
{
|
|
||||||
Curl.GlobalCleanup();
|
|
||||||
_available = false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool IsInvalid => !_initialized || !_available;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.Cache;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
|
|
||||||
namespace NzbDrone.Common.Http.Dispatchers
|
|
||||||
{
|
|
||||||
public class FallbackHttpDispatcher : IHttpDispatcher
|
|
||||||
{
|
|
||||||
private readonly ManagedHttpDispatcher _managedDispatcher;
|
|
||||||
private readonly CurlHttpDispatcher _curlDispatcher;
|
|
||||||
private readonly IPlatformInfo _platformInfo;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
private readonly ICached<bool> _curlTLSFallbackCache;
|
|
||||||
|
|
||||||
public FallbackHttpDispatcher(ManagedHttpDispatcher managedDispatcher, CurlHttpDispatcher curlDispatcher, ICacheManager cacheManager, IPlatformInfo platformInfo, Logger logger)
|
|
||||||
{
|
|
||||||
_managedDispatcher = managedDispatcher;
|
|
||||||
_curlDispatcher = curlDispatcher;
|
|
||||||
_platformInfo = platformInfo;
|
|
||||||
_curlTLSFallbackCache = cacheManager.GetCache<bool>(GetType(), "curlTLSFallback");
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
|
|
||||||
{
|
|
||||||
if (PlatformInfo.IsMono && request.Url.Scheme == "https")
|
|
||||||
{
|
|
||||||
if (!_curlTLSFallbackCache.Find(request.Url.Host))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return _managedDispatcher.GetResponse(request, cookies);
|
|
||||||
}
|
|
||||||
catch (TlsFailureException)
|
|
||||||
{
|
|
||||||
_logger.Debug("https request failed in tls error for {0}, trying curl fallback.", request.Url.Host);
|
|
||||||
|
|
||||||
_curlTLSFallbackCache.Set(request.Url.Host, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_curlDispatcher.CheckAvailability())
|
|
||||||
{
|
|
||||||
return _curlDispatcher.GetResponse(request, cookies);
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Trace("Curl not available, using default WebClient.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return _managedDispatcher.GetResponse(request, cookies);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,7 +24,7 @@ public class HttpProvider : IHttpProvider
|
|||||||
public HttpProvider(Logger logger)
|
public HttpProvider(Logger logger)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_userAgent = string.Format("Radarr {0}", BuildInfo.Version);
|
_userAgent = $"{BuildInfo.AppName}/{BuildInfo.Version.ToString(2)}";
|
||||||
ServicePointManager.Expect100Continue = false;
|
ServicePointManager.Expect100Continue = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,10 +9,10 @@ namespace NzbDrone.Common.Http
|
|||||||
public class TlsFailureException : WebException
|
public class TlsFailureException : WebException
|
||||||
{
|
{
|
||||||
public TlsFailureException(WebRequest request, WebException innerException)
|
public TlsFailureException(WebRequest request, WebException innerException)
|
||||||
: base("Failed to establish secure https connection to '" + request.RequestUri + "', libcurl fallback might be unavailable.", innerException, WebExceptionStatus.SecureChannelFailure, innerException.Response)
|
: base("Failed to establish secure https connection to '" + request.RequestUri + "'.", innerException, WebExceptionStatus.SecureChannelFailure, innerException.Response)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -33,8 +33,8 @@ public UserAgentBuilder(IOsInfo osInfo)
|
|||||||
|
|
||||||
var osVersion = osInfo.Version?.ToLower();
|
var osVersion = osInfo.Version?.ToLower();
|
||||||
|
|
||||||
_userAgent = $"Radarr/{BuildInfo.Version} ({osName} {osVersion})";
|
_userAgent = $"{BuildInfo.AppName}/{BuildInfo.Version} ({osName} {osVersion})";
|
||||||
_userAgentSimplified = $"Radarr/{BuildInfo.Version.ToString(2)}";
|
_userAgentSimplified = $"{BuildInfo.AppName}/{BuildInfo.Version.ToString(2)}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
src/NzbDrone.Common/Instrumentation/InitializeLogger.cs
Normal file
28
src/NzbDrone.Common/Instrumentation/InitializeLogger.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using NLog;
|
||||||
|
using NLog.Fluent;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
using NzbDrone.Common.Instrumentation.Extensions;
|
||||||
|
using NzbDrone.Common.Instrumentation.Sentry;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.Instrumentation
|
||||||
|
{
|
||||||
|
public class InitializeLogger
|
||||||
|
{
|
||||||
|
private readonly IOsInfo _osInfo;
|
||||||
|
|
||||||
|
public InitializeLogger(IOsInfo osInfo)
|
||||||
|
{
|
||||||
|
_osInfo = osInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
var sentryTarget = LogManager.Configuration.AllTargets.OfType<SentryTarget>().FirstOrDefault();
|
||||||
|
if (sentryTarget != null)
|
||||||
|
{
|
||||||
|
sentryTarget.UpdateScope(_osInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -58,18 +58,6 @@ public static void Register(IStartupContext startupContext, bool updateApp, bool
|
|||||||
LogManager.ReconfigExistingLoggers();
|
LogManager.ReconfigExistingLoggers();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UnRegisterRemoteLoggers()
|
|
||||||
{
|
|
||||||
var sentryRules = LogManager.Configuration.LoggingRules.Where(r => r.Targets.Any(t => t.Name == "sentryTarget"));
|
|
||||||
|
|
||||||
foreach (var rules in sentryRules)
|
|
||||||
{
|
|
||||||
rules.Targets.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
LogManager.ReconfigExistingLoggers();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RegisterSentry(bool updateClient)
|
private static void RegisterSentry(bool updateClient)
|
||||||
{
|
{
|
||||||
string dsn;
|
string dsn;
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
using NLog.Common;
|
using NLog.Common;
|
||||||
using NLog.Targets;
|
using NLog.Targets;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
using Sentry;
|
using Sentry;
|
||||||
using Sentry.Protocol;
|
using Sentry.Protocol;
|
||||||
|
|
||||||
@ -61,6 +62,7 @@ public class SentryTarget : TargetWithLayout
|
|||||||
{LogLevel.Warn, BreadcrumbLevel.Warning},
|
{LogLevel.Warn, BreadcrumbLevel.Warning},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly DateTime _startTime = DateTime.UtcNow;
|
||||||
private readonly IDisposable _sdk;
|
private readonly IDisposable _sdk;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
@ -68,10 +70,8 @@ public class SentryTarget : TargetWithLayout
|
|||||||
private bool _unauthorized;
|
private bool _unauthorized;
|
||||||
|
|
||||||
public bool FilterEvents { get; set; }
|
public bool FilterEvents { get; set; }
|
||||||
public string UpdateBranch { get; set; }
|
public bool SentryEnabled { get; set; }
|
||||||
public Version DatabaseVersion { get; set; }
|
|
||||||
public int DatabaseMigration { get; set; }
|
|
||||||
|
|
||||||
public SentryTarget(string dsn)
|
public SentryTarget(string dsn)
|
||||||
{
|
{
|
||||||
_sdk = SentrySdk.Init(o =>
|
_sdk = SentrySdk.Init(o =>
|
||||||
@ -79,34 +79,81 @@ public SentryTarget(string dsn)
|
|||||||
o.Dsn = new Dsn(dsn);
|
o.Dsn = new Dsn(dsn);
|
||||||
o.AttachStacktrace = true;
|
o.AttachStacktrace = true;
|
||||||
o.MaxBreadcrumbs = 200;
|
o.MaxBreadcrumbs = 200;
|
||||||
o.SendDefaultPii = true;
|
o.SendDefaultPii = false;
|
||||||
o.Debug = false;
|
o.Debug = false;
|
||||||
o.DiagnosticsLevel = SentryLevel.Debug;
|
o.DiagnosticsLevel = SentryLevel.Debug;
|
||||||
o.Release = BuildInfo.Release;
|
o.Release = BuildInfo.Release;
|
||||||
|
if (PlatformInfo.IsMono)
|
||||||
|
{
|
||||||
|
// Mono 6.0 broke GzipStream.WriteAsync
|
||||||
|
// TODO: Check specific version
|
||||||
|
o.RequestBodyCompressionLevel = System.IO.Compression.CompressionLevel.NoCompression;
|
||||||
|
}
|
||||||
o.BeforeSend = x => SentryCleanser.CleanseEvent(x);
|
o.BeforeSend = x => SentryCleanser.CleanseEvent(x);
|
||||||
o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x);
|
o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x);
|
||||||
|
o.Environment = BuildInfo.Branch;
|
||||||
});
|
});
|
||||||
|
|
||||||
SentrySdk.ConfigureScope(scope =>
|
InitializeScope();
|
||||||
{
|
|
||||||
scope.User = new User {
|
|
||||||
Username = HashUtil.AnonymousToken()
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.SetTag("osfamily", OsInfo.Os.ToString());
|
|
||||||
scope.SetTag("runtime", PlatformInfo.PlatformName);
|
|
||||||
scope.SetTag("culture", Thread.CurrentThread.CurrentCulture.Name);
|
|
||||||
scope.SetTag("branch", BuildInfo.Branch);
|
|
||||||
scope.SetTag("version", BuildInfo.Version.ToString());
|
|
||||||
scope.SetTag("production", RuntimeInfo.IsProduction.ToString());
|
|
||||||
});
|
|
||||||
|
|
||||||
_debounce = new SentryDebounce();
|
_debounce = new SentryDebounce();
|
||||||
|
|
||||||
// initialize to true and reconfigure later
|
// initialize to true and reconfigure later
|
||||||
// Otherwise it will default to false and any errors occuring
|
// Otherwise it will default to false and any errors occuring
|
||||||
// before config file gets read will not be filtered
|
// before config file gets read will not be filtered
|
||||||
FilterEvents = true;
|
FilterEvents = true;
|
||||||
|
SentryEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeScope()
|
||||||
|
{
|
||||||
|
SentrySdk.ConfigureScope(scope =>
|
||||||
|
{
|
||||||
|
scope.User = new User
|
||||||
|
{
|
||||||
|
Id = HashUtil.AnonymousToken()
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.Contexts.App.Name = BuildInfo.AppName;
|
||||||
|
scope.Contexts.App.Version = BuildInfo.Version.ToString();
|
||||||
|
scope.Contexts.App.StartTime = _startTime;
|
||||||
|
scope.Contexts.App.Hash = HashUtil.AnonymousToken();
|
||||||
|
scope.Contexts.App.Build = BuildInfo.Release; // Git commit cache?
|
||||||
|
|
||||||
|
scope.SetTag("culture", Thread.CurrentThread.CurrentCulture.Name);
|
||||||
|
scope.SetTag("branch", BuildInfo.Branch);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateScope(IOsInfo osInfo)
|
||||||
|
{
|
||||||
|
SentrySdk.ConfigureScope(scope =>
|
||||||
|
{
|
||||||
|
scope.SetTag("is_docker", $"{osInfo.IsDocker}");
|
||||||
|
|
||||||
|
if (osInfo.Name != null && PlatformInfo.IsMono)
|
||||||
|
{
|
||||||
|
// Sentry auto-detection of non-Windows platforms isn't that accurate on certain devices.
|
||||||
|
scope.Contexts.OperatingSystem.Name = osInfo.Name.FirstCharToUpper();
|
||||||
|
scope.Contexts.OperatingSystem.RawDescription = osInfo.FullName;
|
||||||
|
scope.Contexts.OperatingSystem.Version = osInfo.Version.ToString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateScope(Version databaseVersion, int migration, string updateBranch, IPlatformInfo platformInfo)
|
||||||
|
{
|
||||||
|
SentrySdk.ConfigureScope(scope =>
|
||||||
|
{
|
||||||
|
scope.Environment = updateBranch;
|
||||||
|
scope.SetTag("runtime_version", $"{PlatformInfo.PlatformName} {platformInfo.Version}");
|
||||||
|
|
||||||
|
if (databaseVersion != default(Version))
|
||||||
|
{
|
||||||
|
scope.SetTag("sqlite_version", $"{databaseVersion}");
|
||||||
|
scope.SetTag("database_migration", $"{migration}");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnError(Exception ex)
|
private void OnError(Exception ex)
|
||||||
@ -191,7 +238,7 @@ public bool IsSentryMessage(LogEventInfo logEvent)
|
|||||||
|
|
||||||
protected override void Write(LogEventInfo logEvent)
|
protected override void Write(LogEventInfo logEvent)
|
||||||
{
|
{
|
||||||
if (_unauthorized)
|
if (_unauthorized || !SentryEnabled)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -227,27 +274,12 @@ protected override void Write(LogEventInfo logEvent)
|
|||||||
{
|
{
|
||||||
Level = LoggingLevelMap[logEvent.Level],
|
Level = LoggingLevelMap[logEvent.Level],
|
||||||
Logger = logEvent.LoggerName,
|
Logger = logEvent.LoggerName,
|
||||||
Message = logEvent.FormattedMessage,
|
Message = logEvent.FormattedMessage
|
||||||
Environment = UpdateBranch
|
|
||||||
};
|
};
|
||||||
|
|
||||||
sentryEvent.SetExtras(extras);
|
sentryEvent.SetExtras(extras);
|
||||||
sentryEvent.SetFingerprint(fingerPrint);
|
sentryEvent.SetFingerprint(fingerPrint);
|
||||||
|
|
||||||
// this can't be in the constructor as at that point OsInfo won't have
|
|
||||||
// populated these values yet
|
|
||||||
var osName = Environment.GetEnvironmentVariable("OS_NAME");
|
|
||||||
var osVersion = Environment.GetEnvironmentVariable("OS_VERSION");
|
|
||||||
var isDocker = Environment.GetEnvironmentVariable("OS_IS_DOCKER");
|
|
||||||
var runTimeVersion = Environment.GetEnvironmentVariable("RUNTIME_VERSION");
|
|
||||||
|
|
||||||
sentryEvent.SetTag("os_name", osName);
|
|
||||||
sentryEvent.SetTag("os_version", $"{osName} {osVersion}");
|
|
||||||
sentryEvent.SetTag("is_docker", isDocker);
|
|
||||||
sentryEvent.SetTag("runtime_version", $"{PlatformInfo.PlatformName} {runTimeVersion}");
|
|
||||||
sentryEvent.SetTag("sqlite_version", $"{DatabaseVersion}");
|
|
||||||
sentryEvent.SetTag("database_migration", $"{DatabaseMigration}");
|
|
||||||
|
|
||||||
SentrySdk.CaptureEvent(sentryEvent);
|
SentrySdk.CaptureEvent(sentryEvent);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -328,6 +328,8 @@ private List<Process> GetProcessesByName(string name)
|
|||||||
|
|
||||||
var monoProcesses = Process.GetProcessesByName("mono")
|
var monoProcesses = Process.GetProcessesByName("mono")
|
||||||
.Union(Process.GetProcessesByName("mono-sgen"))
|
.Union(Process.GetProcessesByName("mono-sgen"))
|
||||||
|
.Union(Process.GetProcessesByName("mono-sgen32"))
|
||||||
|
.Union(Process.GetProcessesByName("mono-sgen64"))
|
||||||
.Where(process =>
|
.Where(process =>
|
||||||
process.Modules.Cast<ProcessModule>()
|
process.Modules.Cast<ProcessModule>()
|
||||||
.Any(module =>
|
.Any(module =>
|
||||||
|
@ -11,9 +11,6 @@
|
|||||||
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
||||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\ExternalModules\CurlSharp\CurlSharp\CurlSharp.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System.Configuration.Install" />
|
<Reference Include="System.Configuration.Install" />
|
||||||
<Reference Include="System.ServiceProcess" />
|
<Reference Include="System.ServiceProcess" />
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
using NzbDrone.Common.Exceptions;
|
using NzbDrone.Common.Exceptions;
|
||||||
using NzbDrone.Common.Instrumentation;
|
using NzbDrone.Common.Instrumentation;
|
||||||
using Radarr.Host;
|
using Radarr.Host;
|
||||||
|
using Radarr.Host.AccessControl;
|
||||||
|
|
||||||
namespace NzbDrone.Console
|
namespace NzbDrone.Console
|
||||||
{
|
{
|
||||||
@ -50,6 +51,13 @@ public static void Main(string[] args)
|
|||||||
Logger.Fatal(ex.Message + ". This can happen if another instance of Radarr is already running another application is using the same port (default: 7878) or the user has insufficient permissions");
|
Logger.Fatal(ex.Message + ". This can happen if another instance of Radarr is already running another application is using the same port (default: 7878) or the user has insufficient permissions");
|
||||||
Exit(ExitCodes.RecoverableFailure);
|
Exit(ExitCodes.RecoverableFailure);
|
||||||
}
|
}
|
||||||
|
catch (RemoteAccessException ex)
|
||||||
|
{
|
||||||
|
System.Console.WriteLine("");
|
||||||
|
System.Console.WriteLine("");
|
||||||
|
Logger.Fatal(ex, "EPIC FAIL!");
|
||||||
|
Exit(ExitCodes.Normal);
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
System.Console.WriteLine("");
|
System.Console.WriteLine("");
|
||||||
|
@ -30,10 +30,9 @@ protected void UseRealHttp()
|
|||||||
|
|
||||||
Mocker.SetConstant<IHttpProxySettingsProvider>(new HttpProxySettingsProvider(Mocker.Resolve<ConfigService>()));
|
Mocker.SetConstant<IHttpProxySettingsProvider>(new HttpProxySettingsProvider(Mocker.Resolve<ConfigService>()));
|
||||||
Mocker.SetConstant<ICreateManagedWebProxy>(new ManagedWebProxyFactory(Mocker.Resolve<CacheManager>()));
|
Mocker.SetConstant<ICreateManagedWebProxy>(new ManagedWebProxyFactory(Mocker.Resolve<CacheManager>()));
|
||||||
Mocker.SetConstant<ManagedHttpDispatcher>(new ManagedHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<ICreateManagedWebProxy>(), Mocker.Resolve<UserAgentBuilder>(), Mocker.Resolve<IPlatformInfo>(), TestLogger));
|
Mocker.SetConstant<IHttpDispatcher>(new ManagedHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<ICreateManagedWebProxy>(), Mocker.Resolve<UserAgentBuilder>(), Mocker.Resolve<IPlatformInfo>(), TestLogger));
|
||||||
Mocker.SetConstant<CurlHttpDispatcher>(new CurlHttpDispatcher(Mocker.Resolve<IHttpProxySettingsProvider>(), Mocker.Resolve<UserAgentBuilder>(), TestLogger));
|
|
||||||
Mocker.SetConstant<IHttpProvider>(new HttpProvider(TestLogger));
|
Mocker.SetConstant<IHttpProvider>(new HttpProvider(TestLogger));
|
||||||
Mocker.SetConstant<IHttpClient>(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), Mocker.Resolve<FallbackHttpDispatcher>(), Mocker.Resolve<UserAgentBuilder>(), TestLogger));
|
Mocker.SetConstant<IHttpClient>(new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<CacheManager>(), Mocker.Resolve<RateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), Mocker.Resolve<UserAgentBuilder>(), TestLogger));
|
||||||
Mocker.SetConstant<IRadarrCloudRequestBuilder>(new RadarrCloudRequestBuilder());
|
Mocker.SetConstant<IRadarrCloudRequestBuilder>(new RadarrCloudRequestBuilder());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Data.SQLite;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FluentMigrator.Runner;
|
using FluentMigrator.Runner;
|
||||||
@ -99,7 +100,6 @@ protected void SetupContainer()
|
|||||||
{
|
{
|
||||||
WithTempAsAppPath();
|
WithTempAsAppPath();
|
||||||
|
|
||||||
Mocker.SetConstant<IAnnouncer>(Mocker.Resolve<MigrationLogger>());
|
|
||||||
Mocker.SetConstant<IConnectionStringFactory>(Mocker.Resolve<ConnectionStringFactory>());
|
Mocker.SetConstant<IConnectionStringFactory>(Mocker.Resolve<ConnectionStringFactory>());
|
||||||
Mocker.SetConstant<IMigrationController>(Mocker.Resolve<MigrationController>());
|
Mocker.SetConstant<IMigrationController>(Mocker.Resolve<MigrationController>());
|
||||||
|
|
||||||
@ -116,22 +116,15 @@ public virtual void SetupDb()
|
|||||||
[TearDown]
|
[TearDown]
|
||||||
public void TearDown()
|
public void TearDown()
|
||||||
{
|
{
|
||||||
if (TestFolderInfo != null && Directory.Exists(TestFolderInfo.AppDataFolder))
|
// Make sure there are no lingering connections. (When this happens it means we haven't disposed something properly)
|
||||||
|
GC.Collect();
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
|
SQLiteConnection.ClearAllPools();
|
||||||
|
|
||||||
|
if (TestFolderInfo != null)
|
||||||
{
|
{
|
||||||
var files = Directory.GetFiles(TestFolderInfo.AppDataFolder);
|
DeleteTempFolder(TestFolderInfo.AppDataFolder);
|
||||||
|
|
||||||
foreach (var file in files)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
File.Delete(file);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
using System;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
using NzbDrone.Core.HealthCheck.Checks;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class DotnetVersionCheckFixture : CoreTest<DotnetVersionCheck>
|
||||||
|
{
|
||||||
|
private void GivenOutput(string version)
|
||||||
|
{
|
||||||
|
WindowsOnly();
|
||||||
|
|
||||||
|
Mocker.GetMock<IPlatformInfo>()
|
||||||
|
.SetupGet(s => s.Version)
|
||||||
|
.Returns(new Version(version));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("4.7.2")]
|
||||||
|
[TestCase("4.8")]
|
||||||
|
public void should_return_ok(string version)
|
||||||
|
{
|
||||||
|
GivenOutput(version);
|
||||||
|
|
||||||
|
Subject.Check().ShouldBeOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("4.6.2")]
|
||||||
|
[TestCase("4.7")]
|
||||||
|
[TestCase("4.7.1")]
|
||||||
|
public void should_return_notice(string version)
|
||||||
|
{
|
||||||
|
GivenOutput(version);
|
||||||
|
|
||||||
|
Subject.Check().ShouldBeNotice();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void should_return_warning(string version)
|
||||||
|
{
|
||||||
|
GivenOutput(version);
|
||||||
|
|
||||||
|
Subject.Check().ShouldBeWarning();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("4.5")]
|
||||||
|
[TestCase("4.5.2")]
|
||||||
|
[TestCase("4.6.1")]
|
||||||
|
public void should_return_error(string version)
|
||||||
|
{
|
||||||
|
GivenOutput(version);
|
||||||
|
|
||||||
|
Subject.Check().ShouldBeError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,16 @@ public static void ShouldBeOk(this Core.HealthCheck.HealthCheck result)
|
|||||||
result.Type.Should().Be(HealthCheckResult.Ok);
|
result.Type.Should().Be(HealthCheckResult.Ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ShouldBeNotice(this Core.HealthCheck.HealthCheck result, string message = null)
|
||||||
|
{
|
||||||
|
result.Type.Should().Be(HealthCheckResult.Notice);
|
||||||
|
|
||||||
|
if (message.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
result.Message.Should().Contain(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void ShouldBeWarning(this Core.HealthCheck.HealthCheck result, string message = null)
|
public static void ShouldBeWarning(this Core.HealthCheck.HealthCheck result, string message = null)
|
||||||
{
|
{
|
||||||
result.Type.Should().Be(HealthCheckResult.Warning);
|
result.Type.Should().Be(HealthCheckResult.Warning);
|
||||||
|
@ -18,14 +18,9 @@ private void GivenOutput(string version)
|
|||||||
.Returns(new Version(version));
|
.Returns(new Version(version));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[TestCase("4.6")]
|
[TestCase("5.18")]
|
||||||
[TestCase("4.4.2")]
|
[TestCase("5.20")]
|
||||||
[TestCase("4.6")]
|
|
||||||
[TestCase("4.8")]
|
|
||||||
[TestCase("5.0")]
|
|
||||||
[TestCase("5.2")]
|
|
||||||
[TestCase("5.4")]
|
|
||||||
public void should_return_ok(string version)
|
public void should_return_ok(string version)
|
||||||
{
|
{
|
||||||
GivenOutput(version);
|
GivenOutput(version);
|
||||||
@ -33,6 +28,23 @@ public void should_return_ok(string version)
|
|||||||
Subject.Check().ShouldBeOk();
|
Subject.Check().ShouldBeOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase("5.16")]
|
||||||
|
public void should_return_notice(string version)
|
||||||
|
{
|
||||||
|
GivenOutput(version);
|
||||||
|
|
||||||
|
Subject.Check().ShouldBeNotice();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("5.4")]
|
||||||
|
[TestCase("5.8")]
|
||||||
|
public void should_return_warning(string version)
|
||||||
|
{
|
||||||
|
GivenOutput(version);
|
||||||
|
|
||||||
|
Subject.Check().ShouldBeWarning();
|
||||||
|
}
|
||||||
|
|
||||||
[TestCase("2.10.2")]
|
[TestCase("2.10.2")]
|
||||||
[TestCase("2.10.8.1")]
|
[TestCase("2.10.8.1")]
|
||||||
[TestCase("3.0.0.1")]
|
[TestCase("3.0.0.1")]
|
||||||
@ -44,14 +56,6 @@ public void should_return_ok(string version)
|
|||||||
[TestCase("3.10")]
|
[TestCase("3.10")]
|
||||||
[TestCase("4.0.0.0")]
|
[TestCase("4.0.0.0")]
|
||||||
[TestCase("4.2")]
|
[TestCase("4.2")]
|
||||||
public void should_return_warning(string version)
|
|
||||||
{
|
|
||||||
GivenOutput(version);
|
|
||||||
|
|
||||||
Subject.Check().ShouldBeWarning();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[TestCase("4.4.0")]
|
[TestCase("4.4.0")]
|
||||||
[TestCase("4.4.1")]
|
[TestCase("4.4.1")]
|
||||||
public void should_return_error(string version)
|
public void should_return_error(string version)
|
||||||
|
@ -1,121 +1,224 @@
|
|||||||
//using System;
|
using System;
|
||||||
//using System.Collections.Generic;
|
using System.Collections.Concurrent;
|
||||||
//using Moq;
|
using System.Collections.Generic;
|
||||||
//using NUnit.Framework;
|
using System.Threading;
|
||||||
//using NzbDrone.Common;
|
using Moq;
|
||||||
//using NzbDrone.Core.Messaging.Commands;
|
using NUnit.Framework;
|
||||||
//using NzbDrone.Core.Messaging.Commands.Tracking;
|
using NzbDrone.Common;
|
||||||
//using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Lifecycle;
|
||||||
//using NzbDrone.Test.Common;
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
//
|
using NzbDrone.Core.Messaging.Events;
|
||||||
//namespace NzbDrone.Core.Test.Messaging.Commands
|
using NzbDrone.Test.Common;
|
||||||
//{
|
|
||||||
// [TestFixture]
|
namespace NzbDrone.Core.Test.Messaging.Commands
|
||||||
// public class CommandExecutorFixture : TestBase<CommandExecutor>
|
{
|
||||||
// {
|
[TestFixture]
|
||||||
// private Mock<IExecute<CommandA>> _executorA;
|
public class CommandExecutorFixture : TestBase<CommandExecutor>
|
||||||
// private Mock<IExecute<CommandB>> _executorB;
|
{
|
||||||
//
|
private CommandQueue _commandQueue;
|
||||||
// [SetUp]
|
private Mock<IExecute<CommandA>> _executorA;
|
||||||
// public void Setup()
|
private Mock<IExecute<CommandB>> _executorB;
|
||||||
// {
|
|
||||||
// _executorA = new Mock<IExecute<CommandA>>();
|
[SetUp]
|
||||||
// _executorB = new Mock<IExecute<CommandB>>();
|
public void Setup()
|
||||||
//
|
{
|
||||||
// Mocker.GetMock<IServiceFactory>()
|
_executorA = new Mock<IExecute<CommandA>>();
|
||||||
// .Setup(c => c.Build(typeof(IExecute<CommandA>)))
|
_executorB = new Mock<IExecute<CommandB>>();
|
||||||
// .Returns(_executorA.Object);
|
|
||||||
//
|
Mocker.GetMock<IServiceFactory>()
|
||||||
// Mocker.GetMock<IServiceFactory>()
|
.Setup(c => c.Build(typeof(IExecute<CommandA>)))
|
||||||
// .Setup(c => c.Build(typeof(IExecute<CommandB>)))
|
.Returns(_executorA.Object);
|
||||||
// .Returns(_executorB.Object);
|
|
||||||
//
|
Mocker.GetMock<IServiceFactory>()
|
||||||
//
|
.Setup(c => c.Build(typeof(IExecute<CommandB>)))
|
||||||
// Mocker.GetMock<ITrackCommands>()
|
.Returns(_executorB.Object);
|
||||||
// .Setup(c => c.FindExisting(It.IsAny<Command>()))
|
}
|
||||||
// .Returns<Command>(null);
|
|
||||||
// }
|
[TearDown]
|
||||||
//
|
public void TearDown()
|
||||||
// [Test]
|
{
|
||||||
// public void should_publish_command_to_executor()
|
Subject.Handle(new ApplicationShutdownRequested());
|
||||||
// {
|
|
||||||
// var commandA = new CommandA();
|
// Give the threads a bit of time to shut down.
|
||||||
//
|
Thread.Sleep(10);
|
||||||
// Subject.Push(commandA);
|
}
|
||||||
//
|
|
||||||
// _executorA.Verify(c => c.Execute(commandA), Times.Once());
|
private void GivenCommandQueue()
|
||||||
// }
|
{
|
||||||
//
|
_commandQueue = new CommandQueue();
|
||||||
// [Test]
|
|
||||||
// public void should_publish_command_by_with_optional_arg_using_name()
|
Mocker.GetMock<IManageCommandQueue>()
|
||||||
// {
|
.Setup(s => s.Queue(It.IsAny<CancellationToken>()))
|
||||||
// Mocker.GetMock<IServiceFactory>().Setup(c => c.GetImplementations(typeof(Command)))
|
.Returns(_commandQueue.GetConsumingEnumerable);
|
||||||
// .Returns(new List<Type> { typeof(CommandA), typeof(CommandB) });
|
}
|
||||||
//
|
|
||||||
// Subject.Push(typeof(CommandA).FullName);
|
private void QueueAndWaitForExecution(CommandModel commandModel, bool waitPublish = false)
|
||||||
// _executorA.Verify(c => c.Execute(It.IsAny<CommandA>()), Times.Once());
|
{
|
||||||
// }
|
var waitEventComplete = new ManualResetEventSlim();
|
||||||
//
|
var waitEventPublish = new ManualResetEventSlim();
|
||||||
//
|
|
||||||
// [Test]
|
Mocker.GetMock<IManageCommandQueue>()
|
||||||
// public void should_not_publish_to_incompatible_executor()
|
.Setup(s => s.Complete(It.Is<CommandModel>(c => c == commandModel), It.IsAny<string>()))
|
||||||
// {
|
.Callback(() => waitEventComplete.Set());
|
||||||
// var commandA = new CommandA();
|
|
||||||
//
|
Mocker.GetMock<IManageCommandQueue>()
|
||||||
// Subject.Push(commandA);
|
.Setup(s => s.Fail(It.Is<CommandModel>(c => c == commandModel), It.IsAny<string>(), It.IsAny<Exception>()))
|
||||||
//
|
.Callback(() => waitEventComplete.Set());
|
||||||
// _executorA.Verify(c => c.Execute(commandA), Times.Once());
|
|
||||||
// _executorB.Verify(c => c.Execute(It.IsAny<CommandB>()), Times.Never());
|
Mocker.GetMock<IEventAggregator>()
|
||||||
// }
|
.Setup(s => s.PublishEvent<CommandExecutedEvent>(It.IsAny<CommandExecutedEvent>()))
|
||||||
//
|
.Callback(() => waitEventPublish.Set());
|
||||||
// [Test]
|
|
||||||
// public void broken_executor_should_throw_the_exception()
|
_commandQueue.Add(commandModel);
|
||||||
// {
|
|
||||||
// var commandA = new CommandA();
|
if (!waitEventComplete.Wait(2000))
|
||||||
//
|
{
|
||||||
// _executorA.Setup(c => c.Execute(It.IsAny<CommandA>()))
|
Assert.Fail("Command did not Complete/Fail within 2 sec");
|
||||||
// .Throws(new NotImplementedException());
|
}
|
||||||
//
|
|
||||||
// Assert.Throws<NotImplementedException>(() => Subject.Push(commandA));
|
if (waitPublish && !waitEventPublish.Wait(500))
|
||||||
// }
|
{
|
||||||
//
|
Assert.Fail("Command did not Publish within 500 msec");
|
||||||
//
|
}
|
||||||
// [Test]
|
}
|
||||||
// public void broken_executor_should_publish_executed_event()
|
|
||||||
// {
|
[Test]
|
||||||
// var commandA = new CommandA();
|
public void should_start_executor_threads()
|
||||||
//
|
{
|
||||||
// _executorA.Setup(c => c.Execute(It.IsAny<CommandA>()))
|
Subject.Handle(new ApplicationStartedEvent());
|
||||||
// .Throws(new NotImplementedException());
|
|
||||||
//
|
Mocker.GetMock<IManageCommandQueue>()
|
||||||
// Assert.Throws<NotImplementedException>(() => Subject.Push(commandA));
|
.Verify(v => v.Queue(It.IsAny<CancellationToken>()), Times.AtLeastOnce());
|
||||||
//
|
}
|
||||||
// VerifyEventPublished<CommandExecutedEvent>();
|
|
||||||
// }
|
[Test]
|
||||||
//
|
public void should_execute_on_executor()
|
||||||
// [Test]
|
{
|
||||||
// public void should_publish_executed_event_on_success()
|
GivenCommandQueue();
|
||||||
// {
|
var commandA = new CommandA();
|
||||||
// var commandA = new CommandA();
|
var commandModel = new CommandModel
|
||||||
// Subject.Push(commandA);
|
{
|
||||||
//
|
Body = commandA
|
||||||
// VerifyEventPublished<CommandExecutedEvent>();
|
};
|
||||||
// }
|
|
||||||
// }
|
Subject.Handle(new ApplicationStartedEvent());
|
||||||
//
|
|
||||||
// public class CommandA : Command
|
QueueAndWaitForExecution(commandModel);
|
||||||
// {
|
|
||||||
// public CommandA(int id = 0)
|
_executorA.Verify(c => c.Execute(commandA), Times.Once());
|
||||||
// {
|
}
|
||||||
// }
|
|
||||||
// }
|
[Test]
|
||||||
//
|
public void should_not_execute_on_incompatible_executor()
|
||||||
// public class CommandB : Command
|
{
|
||||||
// {
|
GivenCommandQueue();
|
||||||
//
|
var commandA = new CommandA();
|
||||||
// public CommandB()
|
var commandModel = new CommandModel
|
||||||
// {
|
{
|
||||||
// }
|
Body = commandA
|
||||||
// }
|
};
|
||||||
//
|
|
||||||
//}
|
Subject.Handle(new ApplicationStartedEvent());
|
||||||
|
|
||||||
|
QueueAndWaitForExecution(commandModel);
|
||||||
|
|
||||||
|
_executorA.Verify(c => c.Execute(commandA), Times.Once());
|
||||||
|
_executorB.Verify(c => c.Execute(It.IsAny<CommandB>()), Times.Never());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void broken_executor_should_publish_executed_event()
|
||||||
|
{
|
||||||
|
GivenCommandQueue();
|
||||||
|
var commandA = new CommandA();
|
||||||
|
var commandModel = new CommandModel
|
||||||
|
{
|
||||||
|
Body = commandA
|
||||||
|
};
|
||||||
|
|
||||||
|
_executorA.Setup(s => s.Execute(It.IsAny<CommandA>()))
|
||||||
|
.Throws(new NotImplementedException());
|
||||||
|
|
||||||
|
Subject.Handle(new ApplicationStartedEvent());
|
||||||
|
|
||||||
|
QueueAndWaitForExecution(commandModel);
|
||||||
|
|
||||||
|
VerifyEventPublished<CommandExecutedEvent>();
|
||||||
|
|
||||||
|
ExceptionVerification.WaitForErrors(1, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_publish_executed_event_on_success()
|
||||||
|
{
|
||||||
|
GivenCommandQueue();
|
||||||
|
var commandA = new CommandA();
|
||||||
|
var commandModel = new CommandModel
|
||||||
|
{
|
||||||
|
Body = commandA
|
||||||
|
};
|
||||||
|
|
||||||
|
Subject.Handle(new ApplicationStartedEvent());
|
||||||
|
|
||||||
|
QueueAndWaitForExecution(commandModel);
|
||||||
|
|
||||||
|
VerifyEventPublished<CommandExecutedEvent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_use_completion_message()
|
||||||
|
{
|
||||||
|
GivenCommandQueue();
|
||||||
|
var commandA = new CommandA();
|
||||||
|
var commandModel = new CommandModel
|
||||||
|
{
|
||||||
|
Body = commandA
|
||||||
|
};
|
||||||
|
|
||||||
|
Subject.Handle(new ApplicationStartedEvent());
|
||||||
|
|
||||||
|
QueueAndWaitForExecution(commandModel);
|
||||||
|
|
||||||
|
Mocker.GetMock<IManageCommandQueue>()
|
||||||
|
.Verify(s => s.Complete(It.Is<CommandModel>(c => c == commandModel), commandA.CompletionMessage), Times.Once());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_use_last_progress_message_if_completion_message_is_null()
|
||||||
|
{
|
||||||
|
GivenCommandQueue();
|
||||||
|
var commandB = new CommandB();
|
||||||
|
var commandModel = new CommandModel
|
||||||
|
{
|
||||||
|
Body = commandB,
|
||||||
|
Message = "Do work"
|
||||||
|
};
|
||||||
|
|
||||||
|
Subject.Handle(new ApplicationStartedEvent());
|
||||||
|
|
||||||
|
QueueAndWaitForExecution(commandModel);
|
||||||
|
|
||||||
|
Mocker.GetMock<IManageCommandQueue>()
|
||||||
|
.Verify(s => s.Complete(It.Is<CommandModel>(c => c == commandModel), commandModel.Message), Times.Once());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CommandA : Command
|
||||||
|
{
|
||||||
|
public CommandA(int id = 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CommandB : Command
|
||||||
|
{
|
||||||
|
|
||||||
|
public CommandB()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string CompletionMessage => null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -18,7 +18,8 @@ public void Setup()
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestCase("Prometheus", "Prometheus")]
|
[TestCase("Prometheus", "Prometheus")]
|
||||||
[TestCase("The Man from U.N.C.L.E.", "The Man from U.N.C.L.E.")]
|
// TODO: TMDB Doesn't like when we clean periods from this
|
||||||
|
// [TestCase("The Man from U.N.C.L.E.", "The Man from U.N.C.L.E.")]
|
||||||
[TestCase("imdb:tt2527336", "Star Wars: The Last Jedi")]
|
[TestCase("imdb:tt2527336", "Star Wars: The Last Jedi")]
|
||||||
[TestCase("imdb:tt2798920", "Annihilation")]
|
[TestCase("imdb:tt2798920", "Annihilation")]
|
||||||
public void successful_search(string title, string expected)
|
public void successful_search(string title, string expected)
|
||||||
|
@ -42,9 +42,9 @@ private void GivenMovieLastRefreshedYesterday()
|
|||||||
_movie.LastInfoSync = DateTime.UtcNow.AddDays(-1);
|
_movie.LastInfoSync = DateTime.UtcNow.AddDays(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenMovieLastRefreshedHalfADayAgo()
|
private void GivenMovieLastRefreshedADayAgo()
|
||||||
{
|
{
|
||||||
_movie.LastInfoSync = DateTime.UtcNow.AddHours(-12);
|
_movie.LastInfoSync = DateTime.UtcNow.AddHours(-24);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenMovieLastRefreshedRecently()
|
private void GivenMovieLastRefreshedRecently()
|
||||||
@ -58,15 +58,15 @@ private void GivenRecentlyReleased()
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_true_if_in_cinemas_movie_last_refreshed_more_than_6_hours_ago()
|
public void should_return_true_if_in_cinemas_movie_last_refreshed_more_than_12_hours_ago()
|
||||||
{
|
{
|
||||||
GivenMovieLastRefreshedHalfADayAgo();
|
GivenMovieLastRefreshedADayAgo();
|
||||||
|
|
||||||
Subject.ShouldRefresh(_movie).Should().BeTrue();
|
Subject.ShouldRefresh(_movie).Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_false_if_in_cinemas_movie_last_refreshed_less_than_6_hours_ago()
|
public void should_return_false_if_in_cinemas_movie_last_refreshed_less_than_12_hours_ago()
|
||||||
{
|
{
|
||||||
GivenMovieLastRefreshedRecently();
|
GivenMovieLastRefreshedRecently();
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ private void WithNonExpired()
|
|||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
Mocker.GetMock<IConfigService>().SetupGet(s => s.RecycleBin).Returns(RecycleBin);
|
Mocker.GetMock<IConfigService>().SetupGet(s => s.RecycleBin).Returns(RecycleBin);
|
||||||
|
Mocker.GetMock<IConfigService>().SetupGet(s => s.RecycleBinCleanupDays).Returns(7);
|
||||||
|
|
||||||
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetDirectories(RecycleBin))
|
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetDirectories(RecycleBin))
|
||||||
.Returns(new [] { @"C:\Test\RecycleBin\Folder1", @"C:\Test\RecycleBin\Folder2", @"C:\Test\RecycleBin\Folder3" });
|
.Returns(new [] { @"C:\Test\RecycleBin\Folder1", @"C:\Test\RecycleBin\Folder2", @"C:\Test\RecycleBin\Folder3" });
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
<Link>Files\1024.png</Link>
|
<Link>Files\1024.png</Link>
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<Reference Include="System.Data.SQLite">
|
||||||
|
<HintPath>..\Libraries\Sqlite\System.Data.SQLite.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<None Update="Files\**\*.*">
|
<None Update="Files\**\*.*">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
@ -87,6 +87,16 @@ private void GivenInstallScript(string path)
|
|||||||
.Returns(true);
|
.Returns(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_not_update_if_inside_docker()
|
||||||
|
{
|
||||||
|
Mocker.GetMock<IOsInfo>().Setup(x => x.IsDocker).Returns(true);
|
||||||
|
|
||||||
|
Subject.Invoking(x => x.Execute(new ApplicationUpdateCommand()))
|
||||||
|
.Should().Throw<CommandFailedException>()
|
||||||
|
.WithMessage("Updating is disabled inside a docker container. Please update the container image.");
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_delete_sandbox_before_update_if_folder_exists()
|
public void should_delete_sandbox_before_update_if_folder_exists()
|
||||||
{
|
{
|
||||||
|
@ -1,22 +1,40 @@
|
|||||||
using NzbDrone.Common.EnvironmentInfo;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
using NzbDrone.Core.History;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Analytics
|
namespace NzbDrone.Core.Analytics
|
||||||
{
|
{
|
||||||
public interface IAnalyticsService
|
public interface IAnalyticsService
|
||||||
{
|
{
|
||||||
bool IsEnabled { get; }
|
bool IsEnabled { get; }
|
||||||
|
bool InstallIsActive { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AnalyticsService : IAnalyticsService
|
public class AnalyticsService : IAnalyticsService
|
||||||
{
|
{
|
||||||
private readonly IConfigFileProvider _configFileProvider;
|
private readonly IConfigFileProvider _configFileProvider;
|
||||||
|
private readonly IHistoryService _historyService;
|
||||||
|
|
||||||
public AnalyticsService(IConfigFileProvider configFileProvider)
|
public AnalyticsService(IHistoryService historyService, IConfigFileProvider configFileProvider)
|
||||||
{
|
{
|
||||||
_configFileProvider = configFileProvider;
|
_configFileProvider = configFileProvider;
|
||||||
|
_historyService = historyService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsEnabled => _configFileProvider.AnalyticsEnabled;
|
public bool IsEnabled => _configFileProvider.AnalyticsEnabled && RuntimeInfo.IsProduction || RuntimeInfo.IsDevelopment;
|
||||||
|
|
||||||
|
public bool InstallIsActive
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var lastRecord = _historyService.Paged(new PagingSpec<History.History>() { Page = 0, PageSize = 1, SortKey = "date", SortDirection = SortDirection.Descending });
|
||||||
|
var monthAgo = DateTime.UtcNow.AddMonths(-1);
|
||||||
|
|
||||||
|
return lastRecord.Records.Any(v => v.Date > monthAgo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -34,6 +34,7 @@ public interface IConfigFileProvider : IHandleAsync<ApplicationStartedEvent>,
|
|||||||
bool AnalyticsEnabled { get; }
|
bool AnalyticsEnabled { get; }
|
||||||
string LogLevel { get; }
|
string LogLevel { get; }
|
||||||
string ConsoleLogLevel { get; }
|
string ConsoleLogLevel { get; }
|
||||||
|
bool FilterSentryEvents { get; }
|
||||||
string Branch { get; }
|
string Branch { get; }
|
||||||
string ApiKey { get; }
|
string ApiKey { get; }
|
||||||
string SslCertHash { get; }
|
string SslCertHash { get; }
|
||||||
@ -182,7 +183,7 @@ public AuthenticationType AuthenticationMethod
|
|||||||
|
|
||||||
public string LogLevel => GetValue("LogLevel", "info");
|
public string LogLevel => GetValue("LogLevel", "info");
|
||||||
public string ConsoleLogLevel => GetValue("ConsoleLogLevel", string.Empty, persist: false);
|
public string ConsoleLogLevel => GetValue("ConsoleLogLevel", string.Empty, persist: false);
|
||||||
|
public bool FilterSentryEvents => GetValueBoolean("FilterSentryEvents", true, persist: false);
|
||||||
public string SslCertHash => GetValue("SslCertHash", "");
|
public string SslCertHash => GetValue("SslCertHash", "");
|
||||||
|
|
||||||
public string UrlBase
|
public string UrlBase
|
||||||
@ -364,11 +365,6 @@ public void HandleAsync(ApplicationStartedEvent message)
|
|||||||
{
|
{
|
||||||
EnsureDefaultConfigFile();
|
EnsureDefaultConfigFile();
|
||||||
DeleteOldValues();
|
DeleteOldValues();
|
||||||
|
|
||||||
if (!AnalyticsEnabled)
|
|
||||||
{
|
|
||||||
NzbDroneLogger.UnRegisterRemoteLoggers();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute(ResetApiKeyCommand message)
|
public void Execute(ResetApiKeyCommand message)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Core.Datastore
|
namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
public class CorruptDatabaseException : NzbDroneException
|
public class CorruptDatabaseException : RadarrStartupException
|
||||||
{
|
{
|
||||||
public CorruptDatabaseException(string message, params object[] args) : base(message, args)
|
public CorruptDatabaseException(string message, params object[] args) : base(message, args)
|
||||||
{
|
{
|
||||||
|
@ -9,6 +9,7 @@ public interface IDatabase
|
|||||||
{
|
{
|
||||||
IDataMapper GetDataMapper();
|
IDataMapper GetDataMapper();
|
||||||
Version Version { get; }
|
Version Version { get; }
|
||||||
|
int Migration { get; }
|
||||||
void Vacuum();
|
void Vacuum();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +43,16 @@ public Version Version
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Migration
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var migration = _datamapperFactory()
|
||||||
|
.ExecuteScalar("SELECT version from VersionInfo ORDER BY version DESC LIMIT 1").ToString();
|
||||||
|
return Convert.ToInt32(migration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Vacuum()
|
public void Vacuum()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
using NzbDrone.Common.Composition;
|
using NzbDrone.Common.Composition;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
using NzbDrone.Common.Exceptions;
|
||||||
using NzbDrone.Common.Instrumentation;
|
using NzbDrone.Common.Instrumentation;
|
||||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||||
|
|
||||||
@ -124,7 +125,11 @@ private void CreateMain(string connectionString, MigrationContext migrationConte
|
|||||||
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://github.com/Sonarr/Sonarr/wiki/FAQ#i-use-sonarr-on-a-mac-and-it-suddenly-stopped-working-what-happened", e, fileName);
|
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://github.com/Sonarr/Sonarr/wiki/FAQ#i-use-sonarr-on-a-mac-and-it-suddenly-stopped-working-what-happened", e, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://github.com/Sonarr/Sonarr/wiki/FAQ#i-am-getting-an-error-database-disk-image-is-malformed", e, fileName);
|
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://github.com/Lidarr/Lidarr/wiki/FAQ#i-am-getting-an-error-database-disk-image-is-malformed", e, fileName);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RadarrStartupException(e, "Error creating main database");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,6 +159,10 @@ private void CreateLog(string connectionString, MigrationContext migrationContex
|
|||||||
|
|
||||||
_migrationController.Migrate(connectionString, migrationContext);
|
_migrationController.Migrate(connectionString, migrationContext);
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RadarrStartupException(e, "Error creating log database");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ public IDataMapper GetDataMapper()
|
|||||||
|
|
||||||
public Version Version => _database.Version;
|
public Version Version => _database.Version;
|
||||||
|
|
||||||
|
public int Migration => _database.Migration;
|
||||||
|
|
||||||
public void Vacuum()
|
public void Vacuum()
|
||||||
{
|
{
|
||||||
_database.Vacuum();
|
_database.Vacuum();
|
||||||
|
@ -24,6 +24,8 @@ public IDataMapper GetDataMapper()
|
|||||||
|
|
||||||
public Version Version => _database.Version;
|
public Version Version => _database.Version;
|
||||||
|
|
||||||
|
public int Migration => _database.Migration;
|
||||||
|
|
||||||
public void Vacuum()
|
public void Vacuum()
|
||||||
{
|
{
|
||||||
_database.Vacuum();
|
_database.Vacuum();
|
||||||
|
@ -57,7 +57,8 @@ public void Migrate(string connectionString, MigrationContext migrationContext)
|
|||||||
SQLiteConnection.ClearAllPools();
|
SQLiteConnection.ClearAllPools();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processor.Dispose();
|
||||||
|
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
|
|
||||||
|
@ -237,7 +237,7 @@ private ValidationFailure TestConnection()
|
|||||||
}
|
}
|
||||||
catch (WebException ex)
|
catch (WebException ex)
|
||||||
{
|
{
|
||||||
_logger.Error(ex, "Unble to test connection");
|
_logger.Error(ex, "Unable to test connection");
|
||||||
switch (ex.Status)
|
switch (ex.Status)
|
||||||
{
|
{
|
||||||
case WebExceptionStatus.ConnectFailure:
|
case WebExceptionStatus.ConnectFailure:
|
||||||
|
52
src/NzbDrone.Core/HealthCheck/Checks/DotnetVersionCheck.cs
Normal file
52
src/NzbDrone.Core/HealthCheck/Checks/DotnetVersionCheck.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using System;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.HealthCheck.Checks
|
||||||
|
{
|
||||||
|
public class DotnetVersionCheck : HealthCheckBase
|
||||||
|
{
|
||||||
|
private readonly IPlatformInfo _platformInfo;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public DotnetVersionCheck(IPlatformInfo platformInfo, Logger logger)
|
||||||
|
{
|
||||||
|
_platformInfo = platformInfo;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override HealthCheck Check()
|
||||||
|
{
|
||||||
|
if (!PlatformInfo.IsDotNet)
|
||||||
|
{
|
||||||
|
return new HealthCheck(GetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
var dotnetVersion = _platformInfo.Version;
|
||||||
|
|
||||||
|
// Target .Net version, which would allow us to increase our target framework
|
||||||
|
var targetVersion = new Version("4.7.2");
|
||||||
|
if (dotnetVersion >= targetVersion)
|
||||||
|
{
|
||||||
|
_logger.Debug("Dotnet version is {0} or better: {1}", targetVersion, dotnetVersion);
|
||||||
|
return new HealthCheck(GetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supported .net version but below our desired target
|
||||||
|
var stableVersion = new Version("4.6.2");
|
||||||
|
if (dotnetVersion >= stableVersion)
|
||||||
|
{
|
||||||
|
_logger.Debug("Dotnet version is {0} or better: {1}", stableVersion, dotnetVersion);
|
||||||
|
return new HealthCheck(GetType(), HealthCheckResult.Notice,
|
||||||
|
$"Currently installed .Net Framework {dotnetVersion} is supported but we recommend upgrading to at least {targetVersion}.",
|
||||||
|
"#currently-installed-net-framework-is-supported-but-upgrading-is-recommended");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HealthCheck(GetType(), HealthCheckResult.Error,
|
||||||
|
$"Currently installed .Net Framework {dotnetVersion} is old and unsupported. Please upgrade the .Net Framework to at least {targetVersion}.",
|
||||||
|
"#currently-installed-net-framework-is-old-and-unsupported");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CheckOnSchedule => false;
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,9 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using NLog.Fluent;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
using NzbDrone.Common.Instrumentation.Extensions;
|
||||||
|
|
||||||
namespace NzbDrone.Core.HealthCheck.Checks
|
namespace NzbDrone.Core.HealthCheck.Checks
|
||||||
{
|
{
|
||||||
@ -26,11 +28,14 @@ public override HealthCheck Check()
|
|||||||
|
|
||||||
var monoVersion = _platformInfo.Version;
|
var monoVersion = _platformInfo.Version;
|
||||||
|
|
||||||
if (monoVersion >= new Version("5.0.0") && Environment.GetEnvironmentVariable("MONO_TLS_PROVIDER") == "legacy")
|
if (monoVersion >= new Version("5.8.0") && Environment.GetEnvironmentVariable("MONO_TLS_PROVIDER") == "legacy")
|
||||||
{
|
{
|
||||||
// Mono 5.0 still has issues in combination with libmediainfo, so disabling this check for now.
|
_logger.Debug()
|
||||||
//_logger.Debug("Mono version 5.0.0 or higher and legacy TLS provider is selected, recommending user to switch to btls.");
|
.Message("Mono version {0} and legacy TLS provider is selected, recommending user to switch to btls.", monoVersion)
|
||||||
//return new HealthCheck(GetType(), HealthCheckResult.Warning, "Radarr now supports Mono 5.x with btls enabled, consider removing MONO_TLS_PROVIDER=legacy option");
|
.WriteSentryDebug("LegacyTlsProvider", monoVersion.ToString())
|
||||||
|
.Write();
|
||||||
|
|
||||||
|
return new HealthCheck(GetType(), HealthCheckResult.Warning, "Sonarr Mono 4.x tls workaround still enabled, consider removing MONO_TLS_PROVIDER=legacy environment option");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HealthCheck(GetType());
|
return new HealthCheck(GetType());
|
||||||
|
@ -24,19 +24,47 @@ public override HealthCheck Check()
|
|||||||
|
|
||||||
var monoVersion = _platformInfo.Version;
|
var monoVersion = _platformInfo.Version;
|
||||||
|
|
||||||
|
// Known buggy Mono versions
|
||||||
if (monoVersion == new Version("4.4.0") || monoVersion == new Version("4.4.1"))
|
if (monoVersion == new Version("4.4.0") || monoVersion == new Version("4.4.1"))
|
||||||
{
|
{
|
||||||
_logger.Debug("Mono version {0}", monoVersion);
|
_logger.Debug("Mono version {0}", monoVersion);
|
||||||
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Your Mono version {monoVersion} has a bug that causes issues connecting to indexers/download clients. You should upgrade to a higher version");
|
return new HealthCheck(GetType(), HealthCheckResult.Error,
|
||||||
|
$"Currently installed Mono version {monoVersion} has a bug that causes issues connecting to indexers/download clients. You should upgrade to a higher version",
|
||||||
|
"#currently-installed-mono-version-is-old-and-unsupported");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (monoVersion >= new Version("4.4.2"))
|
// Currently best stable Mono version (5.18 gets us .net 4.7.2 support)
|
||||||
|
var bestVersion = new Version("5.20");
|
||||||
|
var targetVersion = new Version("5.18");
|
||||||
|
if (monoVersion >= targetVersion)
|
||||||
{
|
{
|
||||||
_logger.Debug("Mono version is 4.4.2 or better: {0}", monoVersion);
|
_logger.Debug("Mono version is {0} or better: {1}", targetVersion, monoVersion);
|
||||||
return new HealthCheck(GetType());
|
return new HealthCheck(GetType());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HealthCheck(GetType(), HealthCheckResult.Warning, "You are running an old and unsupported version of Mono. Please upgrade Mono for improved stability.");
|
// Stable Mono versions
|
||||||
|
var stableVersion = new Version("5.16");
|
||||||
|
if (monoVersion >= stableVersion)
|
||||||
|
{
|
||||||
|
_logger.Debug("Mono version is {0} or better: {1}", stableVersion, monoVersion);
|
||||||
|
return new HealthCheck(GetType(), HealthCheckResult.Notice,
|
||||||
|
$"Currently installed Mono version {monoVersion} is supported but upgrading to {bestVersion} is recommended.",
|
||||||
|
"#currently-installed-mono-version-is-supported-but-upgrading-is-recommended");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old but supported Mono versions, there are known bugs
|
||||||
|
var supportedVersion = new Version("5.4");
|
||||||
|
if (monoVersion >= supportedVersion)
|
||||||
|
{
|
||||||
|
_logger.Debug("Mono version is {0} or better: {1}", supportedVersion, monoVersion);
|
||||||
|
return new HealthCheck(GetType(), HealthCheckResult.Warning,
|
||||||
|
$"Currently installed Mono version {monoVersion} is supported but has some known issues. Please upgrade Mono to version {bestVersion}.",
|
||||||
|
"#currently-installed-mono-version-is-supported-but-upgrading-is-recommended");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HealthCheck(GetType(), HealthCheckResult.Error,
|
||||||
|
$"Currently installed Mono version {monoVersion} is old and unsupported. Please upgrade Mono to version {bestVersion}.",
|
||||||
|
"#currently-installed-mono-version-is-old-and-unsupported");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CheckOnSchedule => false;
|
public override bool CheckOnSchedule => false;
|
||||||
|
@ -46,7 +46,8 @@ private static HttpUri MakeWikiUrl(string fragment)
|
|||||||
public enum HealthCheckResult
|
public enum HealthCheckResult
|
||||||
{
|
{
|
||||||
Ok = 0,
|
Ok = 0,
|
||||||
Warning = 1,
|
Notice = 1,
|
||||||
Error = 2
|
Warning = 2,
|
||||||
|
Error = 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Http;
|
using NzbDrone.Common.Http;
|
||||||
@ -107,7 +108,7 @@ private IEnumerable<IndexerRequest> GetMovieRequest(MovieSearchCriteria searchCr
|
|||||||
requestBuilder.AddQueryParam("limit", "100");
|
requestBuilder.AddQueryParam("limit", "100");
|
||||||
requestBuilder.AddQueryParam("token", _tokenProvider.GetToken(Settings));
|
requestBuilder.AddQueryParam("token", _tokenProvider.GetToken(Settings));
|
||||||
requestBuilder.AddQueryParam("format", "json_extended");
|
requestBuilder.AddQueryParam("format", "json_extended");
|
||||||
requestBuilder.AddQueryParam("app_id", "Radarr");
|
requestBuilder.AddQueryParam("app_id", BuildInfo.AppName);
|
||||||
|
|
||||||
yield return new IndexerRequest(requestBuilder.Build());
|
yield return new IndexerRequest(requestBuilder.Build());
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NLog.Config;
|
using NLog.Config;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Instrumentation.Sentry;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Configuration.Events;
|
using NzbDrone.Core.Configuration.Events;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
@ -40,6 +42,9 @@ public void Reconfigure()
|
|||||||
SetMinimumLogLevel(rules, "appFileDebug", minimumLogLevel <= LogLevel.Debug ? LogLevel.Debug : LogLevel.Off);
|
SetMinimumLogLevel(rules, "appFileDebug", minimumLogLevel <= LogLevel.Debug ? LogLevel.Debug : LogLevel.Off);
|
||||||
SetMinimumLogLevel(rules, "appFileTrace", minimumLogLevel <= LogLevel.Trace ? LogLevel.Trace : LogLevel.Off);
|
SetMinimumLogLevel(rules, "appFileTrace", minimumLogLevel <= LogLevel.Trace ? LogLevel.Trace : LogLevel.Off);
|
||||||
|
|
||||||
|
//Sentry
|
||||||
|
ReconfigureSentry();
|
||||||
|
|
||||||
LogManager.ReconfigExistingLoggers();
|
LogManager.ReconfigExistingLoggers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,6 +72,16 @@ private void SetMinimumLogLevel(LoggingRule rule, LogLevel minimumLogLevel)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ReconfigureSentry()
|
||||||
|
{
|
||||||
|
var sentryTarget = LogManager.Configuration.AllTargets.OfType<SentryTarget>().FirstOrDefault();
|
||||||
|
if (sentryTarget != null)
|
||||||
|
{
|
||||||
|
sentryTarget.SentryEnabled = RuntimeInfo.IsProduction && _configFileProvider.AnalyticsEnabled || RuntimeInfo.IsDevelopment;
|
||||||
|
sentryTarget.FilterEvents = _configFileProvider.FilterSentryEvents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<LogLevel> GetLogLevels()
|
private List<LogLevel> GetLogLevels()
|
||||||
{
|
{
|
||||||
return new List<LogLevel>
|
return new List<LogLevel>
|
||||||
|
42
src/NzbDrone.Core/Instrumentation/ReconfigureSentry.cs
Normal file
42
src/NzbDrone.Core/Instrumentation/ReconfigureSentry.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
using NzbDrone.Common.Instrumentation.Sentry;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
using NzbDrone.Core.Lifecycle;
|
||||||
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Instrumentation
|
||||||
|
{
|
||||||
|
public class ReconfigureSentry : IHandleAsync<ApplicationStartedEvent>
|
||||||
|
{
|
||||||
|
private readonly IConfigFileProvider _configFileProvider;
|
||||||
|
private readonly IPlatformInfo _platformInfo;
|
||||||
|
private readonly IMainDatabase _database;
|
||||||
|
|
||||||
|
public ReconfigureSentry(IConfigFileProvider configFileProvider,
|
||||||
|
IPlatformInfo platformInfo,
|
||||||
|
IMainDatabase database)
|
||||||
|
{
|
||||||
|
_configFileProvider = configFileProvider;
|
||||||
|
_platformInfo = platformInfo;
|
||||||
|
_database = database;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reconfigure()
|
||||||
|
{
|
||||||
|
// Extended sentry config
|
||||||
|
var sentryTarget = LogManager.Configuration.AllTargets.OfType<SentryTarget>().FirstOrDefault();
|
||||||
|
if (sentryTarget != null)
|
||||||
|
{
|
||||||
|
sentryTarget.UpdateScope(_database.Version, _database.Migration, _configFileProvider.Branch, _platformInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleAsync(ApplicationStartedEvent message)
|
||||||
|
{
|
||||||
|
Reconfigure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
using NLog;
|
using NLog;
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
using NzbDrone.Core.Rest;
|
using NzbDrone.Core.Rest;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Boxcar
|
namespace NzbDrone.Core.Notifications.Boxcar
|
||||||
{
|
{
|
||||||
@ -75,7 +76,7 @@ private void SendNotification(string title, string message, RestRequest request,
|
|||||||
request.AddParameter("user_credentials", settings.Token);
|
request.AddParameter("user_credentials", settings.Token);
|
||||||
request.AddParameter("notification[title]", title);
|
request.AddParameter("notification[title]", title);
|
||||||
request.AddParameter("notification[long_message]", message);
|
request.AddParameter("notification[long_message]", message);
|
||||||
request.AddParameter("notification[source_name]", "Radarr");
|
request.AddParameter("notification[source_name]", BuildInfo.AppName);
|
||||||
request.AddParameter("notification[icon_url]", "https://raw.githubusercontent.com/Radarr/Radarr/develop/Logo/64.png");
|
request.AddParameter("notification[icon_url]", "https://raw.githubusercontent.com/Radarr/Radarr/develop/Logo/64.png");
|
||||||
|
|
||||||
client.ExecuteAndValidate(request);
|
client.ExecuteAndValidate(request);
|
||||||
|
@ -43,10 +43,10 @@ private HttpRequestBuilder BuildRequest(string clientIdentifier)
|
|||||||
var requestBuilder = new HttpRequestBuilder("https://plex.tv")
|
var requestBuilder = new HttpRequestBuilder("https://plex.tv")
|
||||||
.Accept(HttpAccept.Json)
|
.Accept(HttpAccept.Json)
|
||||||
.AddQueryParam("X-Plex-Client-Identifier", clientIdentifier)
|
.AddQueryParam("X-Plex-Client-Identifier", clientIdentifier)
|
||||||
.AddQueryParam("X-Plex-Product", "Radarr")
|
.AddQueryParam("X-Plex-Product", BuildInfo.AppName)
|
||||||
.AddQueryParam("X-Plex-Platform", "Windows")
|
.AddQueryParam("X-Plex-Platform", "Windows")
|
||||||
.AddQueryParam("X-Plex-Platform-Version", "7")
|
.AddQueryParam("X-Plex-Platform-Version", "7")
|
||||||
.AddQueryParam("X-Plex-Device-Name", "Radarr")
|
.AddQueryParam("X-Plex-Device-Name", BuildInfo.AppName)
|
||||||
.AddQueryParam("X-Plex-Version", BuildInfo.Version.ToString());
|
.AddQueryParam("X-Plex-Version", BuildInfo.Version.ToString());
|
||||||
|
|
||||||
return requestBuilder;
|
return requestBuilder;
|
||||||
|
@ -31,10 +31,10 @@ public PlexTvPinUrlResponse GetPinUrl()
|
|||||||
var requestBuilder = new HttpRequestBuilder("https://plex.tv/api/v2/pins")
|
var requestBuilder = new HttpRequestBuilder("https://plex.tv/api/v2/pins")
|
||||||
.Accept(HttpAccept.Json)
|
.Accept(HttpAccept.Json)
|
||||||
.AddQueryParam("X-Plex-Client-Identifier", clientIdentifier)
|
.AddQueryParam("X-Plex-Client-Identifier", clientIdentifier)
|
||||||
.AddQueryParam("X-Plex-Product", "Radarr")
|
.AddQueryParam("X-Plex-Product", BuildInfo.AppName)
|
||||||
.AddQueryParam("X-Plex-Platform", "Windows")
|
.AddQueryParam("X-Plex-Platform", "Windows")
|
||||||
.AddQueryParam("X-Plex-Platform-Version", "7")
|
.AddQueryParam("X-Plex-Platform-Version", "7")
|
||||||
.AddQueryParam("X-Plex-Device-Name", "Radarr")
|
.AddQueryParam("X-Plex-Device-Name", BuildInfo.AppName)
|
||||||
.AddQueryParam("X-Plex-Version", BuildInfo.Version.ToString())
|
.AddQueryParam("X-Plex-Version", BuildInfo.Version.ToString())
|
||||||
.AddQueryParam("strong", true);
|
.AddQueryParam("strong", true);
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ public PlexTvSignInUrlResponse GetSignInUrl(string callbackUrl, int pinId, strin
|
|||||||
.AddQueryParam("clientID", clientIdentifier)
|
.AddQueryParam("clientID", clientIdentifier)
|
||||||
.AddQueryParam("forwardUrl", callbackUrl)
|
.AddQueryParam("forwardUrl", callbackUrl)
|
||||||
.AddQueryParam("code", pinCode)
|
.AddQueryParam("code", pinCode)
|
||||||
.AddQueryParam("context[device][product]", "Radarr")
|
.AddQueryParam("context[device][product]", BuildInfo.AppName)
|
||||||
.AddQueryParam("context[device][platform]", "Windows")
|
.AddQueryParam("context[device][platform]", "Windows")
|
||||||
.AddQueryParam("context[device][platformVersion]", "7")
|
.AddQueryParam("context[device][platformVersion]", "7")
|
||||||
.AddQueryParam("context[device][version]", BuildInfo.Version.ToString());
|
.AddQueryParam("context[device][version]", BuildInfo.Version.ToString());
|
||||||
|
@ -155,10 +155,10 @@ private HttpRequestBuilder BuildRequest(string resource, HttpMethod method, Plex
|
|||||||
var requestBuilder = new HttpRequestBuilder($"{scheme}://{settings.Host}:{settings.Port}")
|
var requestBuilder = new HttpRequestBuilder($"{scheme}://{settings.Host}:{settings.Port}")
|
||||||
.Accept(HttpAccept.Json)
|
.Accept(HttpAccept.Json)
|
||||||
.AddQueryParam("X-Plex-Client-Identifier", _configService.PlexClientIdentifier)
|
.AddQueryParam("X-Plex-Client-Identifier", _configService.PlexClientIdentifier)
|
||||||
.AddQueryParam("X-Plex-Product", "Radarr")
|
.AddQueryParam("X-Plex-Product", BuildInfo.AppName)
|
||||||
.AddQueryParam("X-Plex-Platform", "Windows")
|
.AddQueryParam("X-Plex-Platform", "Windows")
|
||||||
.AddQueryParam("X-Plex-Platform-Version", "7")
|
.AddQueryParam("X-Plex-Platform-Version", "7")
|
||||||
.AddQueryParam("X-Plex-Device-Name", "Radarr")
|
.AddQueryParam("X-Plex-Device-Name", BuildInfo.AppName)
|
||||||
.AddQueryParam("X-Plex-Version", BuildInfo.Version.ToString());
|
.AddQueryParam("X-Plex-Version", BuildInfo.Version.ToString());
|
||||||
|
|
||||||
if (settings.AuthToken.IsNotNullOrWhiteSpace())
|
if (settings.AuthToken.IsNotNullOrWhiteSpace())
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using Prowlin;
|
using Prowlin;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Prowl
|
namespace NzbDrone.Core.Notifications.Prowl
|
||||||
@ -26,7 +27,7 @@ public void SendNotification(string title, string message, string apiKey, Notifi
|
|||||||
{
|
{
|
||||||
var notification = new Prowlin.Notification
|
var notification = new Prowlin.Notification
|
||||||
{
|
{
|
||||||
Application = "Radarr",
|
Application = BuildInfo.AppName,
|
||||||
Description = message,
|
Description = message,
|
||||||
Event = title,
|
Event = title,
|
||||||
Priority = priority,
|
Priority = priority,
|
||||||
|
@ -9,7 +9,7 @@ public static RestClient BuildClient(string baseUrl)
|
|||||||
{
|
{
|
||||||
var restClient = new RestClient(baseUrl)
|
var restClient = new RestClient(baseUrl)
|
||||||
{
|
{
|
||||||
UserAgent = $"Radarr/{BuildInfo.Version} ({OsInfo.Os})"
|
UserAgent = $"{BuildInfo.AppName}/{BuildInfo.Version} ({OsInfo.Os})"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ public class InstallUpdateService : IExecute<ApplicationUpdateCommand>
|
|||||||
private readonly IConfigFileProvider _configFileProvider;
|
private readonly IConfigFileProvider _configFileProvider;
|
||||||
private readonly IRuntimeInfo _runtimeInfo;
|
private readonly IRuntimeInfo _runtimeInfo;
|
||||||
private readonly IBackupService _backupService;
|
private readonly IBackupService _backupService;
|
||||||
|
private readonly IOsInfo _osInfo;
|
||||||
|
|
||||||
|
|
||||||
public InstallUpdateService(ICheckUpdateService checkUpdateService,
|
public InstallUpdateService(ICheckUpdateService checkUpdateService,
|
||||||
@ -46,6 +47,7 @@ public InstallUpdateService(ICheckUpdateService checkUpdateService,
|
|||||||
IConfigFileProvider configFileProvider,
|
IConfigFileProvider configFileProvider,
|
||||||
IRuntimeInfo runtimeInfo,
|
IRuntimeInfo runtimeInfo,
|
||||||
IBackupService backupService,
|
IBackupService backupService,
|
||||||
|
IOsInfo osInfo,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
if (configFileProvider == null)
|
if (configFileProvider == null)
|
||||||
@ -64,6 +66,7 @@ public InstallUpdateService(ICheckUpdateService checkUpdateService,
|
|||||||
_configFileProvider = configFileProvider;
|
_configFileProvider = configFileProvider;
|
||||||
_runtimeInfo = runtimeInfo;
|
_runtimeInfo = runtimeInfo;
|
||||||
_backupService = backupService;
|
_backupService = backupService;
|
||||||
|
_osInfo = osInfo;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,6 +212,11 @@ public void Execute(ApplicationUpdateCommand message)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_osInfo.IsDocker)
|
||||||
|
{
|
||||||
|
throw new CommandFailedException("Updating is disabled inside a docker container. Please update the container image.");
|
||||||
|
}
|
||||||
|
|
||||||
if (OsInfo.IsNotWindows && !_configFileProvider.UpdateAutomatically && message.Trigger != CommandTrigger.Manual)
|
if (OsInfo.IsNotWindows && !_configFileProvider.UpdateAutomatically && message.Trigger != CommandTrigger.Manual)
|
||||||
{
|
{
|
||||||
_logger.ProgressDebug("Auto-update not enabled, not installing available update.");
|
_logger.ProgressDebug("Auto-update not enabled, not installing available update.");
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
using NzbDrone.Common.Cloud;
|
using NzbDrone.Common.Cloud;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Http;
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Analytics;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Update
|
namespace NzbDrone.Core.Update
|
||||||
{
|
{
|
||||||
@ -15,14 +16,16 @@ public interface IUpdatePackageProvider
|
|||||||
public class UpdatePackageProvider : IUpdatePackageProvider
|
public class UpdatePackageProvider : IUpdatePackageProvider
|
||||||
{
|
{
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
private readonly IPlatformInfo _platformInfo;
|
|
||||||
private readonly IHttpRequestBuilderFactory _requestBuilder;
|
private readonly IHttpRequestBuilderFactory _requestBuilder;
|
||||||
|
private readonly IPlatformInfo _platformInfo;
|
||||||
|
private readonly IAnalyticsService _analyticsService;
|
||||||
|
|
||||||
public UpdatePackageProvider(IHttpClient httpClient, IRadarrCloudRequestBuilder requestBuilder, IPlatformInfo platformInfo)
|
public UpdatePackageProvider(IHttpClient httpClient, IRadarrCloudRequestBuilder requestBuilder, IAnalyticsService analyticsService, IPlatformInfo platformInfo)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
|
||||||
_platformInfo = platformInfo;
|
_platformInfo = platformInfo;
|
||||||
|
_analyticsService = analyticsService;
|
||||||
_requestBuilder = requestBuilder.Services;
|
_requestBuilder = requestBuilder.Services;
|
||||||
|
_httpClient = httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UpdatePackage GetLatestUpdate(string branch, Version currentVersion)
|
public UpdatePackage GetLatestUpdate(string branch, Version currentVersion)
|
||||||
@ -32,10 +35,15 @@ public UpdatePackage GetLatestUpdate(string branch, Version currentVersion)
|
|||||||
.AddQueryParam("version", currentVersion)
|
.AddQueryParam("version", currentVersion)
|
||||||
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
||||||
.AddQueryParam("runtimeVer", _platformInfo.Version)
|
.AddQueryParam("runtimeVer", _platformInfo.Version)
|
||||||
.SetSegment("branch", branch)
|
.SetSegment("branch", branch);
|
||||||
.Build();
|
|
||||||
|
|
||||||
var update = _httpClient.Get<UpdatePackageAvailable>(request).Resource;
|
if (_analyticsService.IsEnabled)
|
||||||
|
{
|
||||||
|
// Send if the system is active so we know which versions to deprecate/ignore
|
||||||
|
request.AddQueryParam("active", _analyticsService.InstallIsActive.ToString().ToLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
var update = _httpClient.Get<UpdatePackageAvailable>(request.Build()).Resource;
|
||||||
|
|
||||||
if (!update.Available) return null;
|
if (!update.Available) return null;
|
||||||
|
|
||||||
@ -49,12 +57,17 @@ public List<UpdatePackage> GetRecentUpdates(string branch, Version currentVersio
|
|||||||
.AddQueryParam("version", currentVersion)
|
.AddQueryParam("version", currentVersion)
|
||||||
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
||||||
.AddQueryParam("runtimeVer", _platformInfo.Version)
|
.AddQueryParam("runtimeVer", _platformInfo.Version)
|
||||||
.SetSegment("branch", branch)
|
.SetSegment("branch", branch);
|
||||||
.Build();
|
|
||||||
|
|
||||||
var updates = _httpClient.Get<List<UpdatePackage>>(request);
|
if (_analyticsService.IsEnabled)
|
||||||
|
{
|
||||||
|
// Send if the system is active so we know which versions to deprecate/ignore
|
||||||
|
request.AddQueryParam("active", _analyticsService.InstallIsActive.ToString().ToLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
var updates = _httpClient.Get<List<UpdatePackage>>(request.Build());
|
||||||
|
|
||||||
return updates.Resource;
|
return updates.Resource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -29,7 +29,6 @@ public void Route_should_call_install_service_when_application_mode_is_install()
|
|||||||
|
|
||||||
Mocker.GetMock<IProcessProvider>()
|
Mocker.GetMock<IProcessProvider>()
|
||||||
.Setup(c => c.SpawnNewProcess("sc.exe", It.IsAny<string>(), null, true));
|
.Setup(c => c.SpawnNewProcess("sc.exe", It.IsAny<string>(), null, true));
|
||||||
|
|
||||||
Mocker.GetMock<IRuntimeInfo>().SetupGet(c => c.IsUserInteractive).Returns(true);
|
Mocker.GetMock<IRuntimeInfo>().SetupGet(c => c.IsUserInteractive).Returns(true);
|
||||||
|
|
||||||
Subject.Route(ApplicationModes.InstallService);
|
Subject.Route(ApplicationModes.InstallService);
|
||||||
|
46
src/NzbDrone.Host/AccessControl/RemoteAccessAdapter.cs
Normal file
46
src/NzbDrone.Host/AccessControl/RemoteAccessAdapter.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
|
||||||
|
namespace Radarr.Host.AccessControl
|
||||||
|
{
|
||||||
|
public interface IRemoteAccessAdapter
|
||||||
|
{
|
||||||
|
void MakeAccessible(bool passive);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RemoteAccessAdapter : IRemoteAccessAdapter
|
||||||
|
{
|
||||||
|
private readonly IRuntimeInfo _runtimeInfo;
|
||||||
|
private readonly IUrlAclAdapter _urlAclAdapter;
|
||||||
|
private readonly IFirewallAdapter _firewallAdapter;
|
||||||
|
private readonly ISslAdapter _sslAdapter;
|
||||||
|
|
||||||
|
public RemoteAccessAdapter(IRuntimeInfo runtimeInfo,
|
||||||
|
IUrlAclAdapter urlAclAdapter,
|
||||||
|
IFirewallAdapter firewallAdapter,
|
||||||
|
ISslAdapter sslAdapter)
|
||||||
|
{
|
||||||
|
_runtimeInfo = runtimeInfo;
|
||||||
|
_urlAclAdapter = urlAclAdapter;
|
||||||
|
_firewallAdapter = firewallAdapter;
|
||||||
|
_sslAdapter = sslAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MakeAccessible(bool passive)
|
||||||
|
{
|
||||||
|
if (OsInfo.IsWindows)
|
||||||
|
{
|
||||||
|
if (_runtimeInfo.IsAdmin)
|
||||||
|
{
|
||||||
|
_firewallAdapter.MakeAccessible();
|
||||||
|
_sslAdapter.Register();
|
||||||
|
}
|
||||||
|
else if (!passive)
|
||||||
|
{
|
||||||
|
throw new RemoteAccessException("Failed to register URLs for Radarr. Radarr will not be accessible remotely");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_urlAclAdapter.ConfigureUrls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
src/NzbDrone.Host/AccessControl/RemoteAccessException.cs
Normal file
24
src/NzbDrone.Host/AccessControl/RemoteAccessException.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using NzbDrone.Common.Exceptions;
|
||||||
|
|
||||||
|
namespace Radarr.Host.AccessControl
|
||||||
|
{
|
||||||
|
public class RemoteAccessException : NzbDroneException
|
||||||
|
{
|
||||||
|
public RemoteAccessException(string message, params object[] args) : base(message, args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoteAccessException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoteAccessException(string message, Exception innerException, params object[] args) : base(message, innerException, args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoteAccessException(string message, Exception innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,5 +7,6 @@ public enum ApplicationModes
|
|||||||
InstallService,
|
InstallService,
|
||||||
UninstallService,
|
UninstallService,
|
||||||
Service,
|
Service,
|
||||||
|
RegisterUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ public static void Start(StartupContext startupContext, IUserAlert userAlert, Ac
|
|||||||
}
|
}
|
||||||
|
|
||||||
_container = MainAppContainerBuilder.BuildContainer(startupContext);
|
_container = MainAppContainerBuilder.BuildContainer(startupContext);
|
||||||
|
_container.Resolve<InitializeLogger>().Initialize();
|
||||||
_container.Resolve<IAppFolderFactory>().Register();
|
_container.Resolve<IAppFolderFactory>().Register();
|
||||||
_container.Resolve<IProvidePidFile>().Write();
|
_container.Resolve<IProvidePidFile>().Write();
|
||||||
|
|
||||||
@ -109,11 +110,17 @@ private static void EnsureSingleInstance(bool isService, IStartupContext startup
|
|||||||
|
|
||||||
private static ApplicationModes GetApplicationMode(IStartupContext startupContext)
|
private static ApplicationModes GetApplicationMode(IStartupContext startupContext)
|
||||||
{
|
{
|
||||||
if (startupContext.Flags.Contains(StartupContext.HELP))
|
if (startupContext.Help)
|
||||||
{
|
{
|
||||||
return ApplicationModes.Help;
|
return ApplicationModes.Help;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (OsInfo.IsWindows && startupContext.RegisterUrl)
|
||||||
|
{
|
||||||
|
return ApplicationModes.RegisterUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (OsInfo.IsWindows && startupContext.InstallService)
|
if (OsInfo.IsWindows && startupContext.InstallService)
|
||||||
{
|
{
|
||||||
return ApplicationModes.InstallService;
|
return ApplicationModes.InstallService;
|
||||||
@ -138,6 +145,7 @@ private static bool IsInUtilityMode(ApplicationModes applicationMode)
|
|||||||
{
|
{
|
||||||
case ApplicationModes.InstallService:
|
case ApplicationModes.InstallService:
|
||||||
case ApplicationModes.UninstallService:
|
case ApplicationModes.UninstallService:
|
||||||
|
case ApplicationModes.RegisterUrl:
|
||||||
case ApplicationModes.Help:
|
case ApplicationModes.Help:
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@ -31,7 +31,6 @@ private MainAppContainerBuilder(StartupContext args, List<string> assemblies)
|
|||||||
AutoRegisterImplementations<NzbDronePersistentConnection>();
|
AutoRegisterImplementations<NzbDronePersistentConnection>();
|
||||||
|
|
||||||
Container.Register<INancyBootstrapper, RadarrBootstrapper>();
|
Container.Register<INancyBootstrapper, RadarrBootstrapper>();
|
||||||
Container.Register<IHttpDispatcher, FallbackHttpDispatcher>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,41 +8,26 @@ namespace Radarr.Host.Owin
|
|||||||
public class OwinHostController : IHostController
|
public class OwinHostController : IHostController
|
||||||
{
|
{
|
||||||
private readonly IOwinAppFactory _owinAppFactory;
|
private readonly IOwinAppFactory _owinAppFactory;
|
||||||
private readonly IRuntimeInfo _runtimeInfo;
|
private readonly IRemoteAccessAdapter _removeAccessAdapter;
|
||||||
private readonly IUrlAclAdapter _urlAclAdapter;
|
private readonly IUrlAclAdapter _urlAclAdapter;
|
||||||
private readonly IFirewallAdapter _firewallAdapter;
|
|
||||||
private readonly ISslAdapter _sslAdapter;
|
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
private IDisposable _owinApp;
|
private IDisposable _owinApp;
|
||||||
|
|
||||||
public OwinHostController(
|
public OwinHostController(
|
||||||
IOwinAppFactory owinAppFactory,
|
IOwinAppFactory owinAppFactory,
|
||||||
IRuntimeInfo runtimeInfo,
|
IRemoteAccessAdapter removeAccessAdapter,
|
||||||
IUrlAclAdapter urlAclAdapter,
|
IUrlAclAdapter urlAclAdapter,
|
||||||
IFirewallAdapter firewallAdapter,
|
|
||||||
ISslAdapter sslAdapter,
|
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_owinAppFactory = owinAppFactory;
|
_owinAppFactory = owinAppFactory;
|
||||||
_runtimeInfo = runtimeInfo;
|
_removeAccessAdapter = removeAccessAdapter;
|
||||||
_urlAclAdapter = urlAclAdapter;
|
_urlAclAdapter = urlAclAdapter;
|
||||||
_firewallAdapter = firewallAdapter;
|
|
||||||
_sslAdapter = sslAdapter;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartServer()
|
public void StartServer()
|
||||||
{
|
{
|
||||||
if (OsInfo.IsWindows)
|
_removeAccessAdapter.MakeAccessible(true);
|
||||||
{
|
|
||||||
if (_runtimeInfo.IsAdmin)
|
|
||||||
{
|
|
||||||
_firewallAdapter.MakeAccessible();
|
|
||||||
_sslAdapter.Register();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_urlAclAdapter.ConfigureUrls();
|
|
||||||
|
|
||||||
_logger.Info("Listening on the following URLs:");
|
_logger.Info("Listening on the following URLs:");
|
||||||
foreach (var url in _urlAclAdapter.Urls)
|
foreach (var url in _urlAclAdapter.Urls)
|
||||||
@ -53,7 +38,6 @@ public void StartServer()
|
|||||||
_owinApp = _owinAppFactory.CreateApp(_urlAclAdapter.Urls);
|
_owinApp = _owinAppFactory.CreateApp(_urlAclAdapter.Urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void StopServer()
|
public void StopServer()
|
||||||
{
|
{
|
||||||
if (_owinApp == null) return;
|
if (_owinApp == null) return;
|
||||||
@ -63,8 +47,5 @@ public void StopServer()
|
|||||||
_owinApp = null;
|
_owinApp = null;
|
||||||
_logger.Info("Host has stopped");
|
_logger.Info("Host has stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,6 +11,7 @@
|
|||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using Radarr.Host.Owin.MiddleWare;
|
using Radarr.Host.Owin.MiddleWare;
|
||||||
using Owin;
|
using Owin;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
|
||||||
namespace Radarr.Host.Owin
|
namespace Radarr.Host.Owin
|
||||||
{
|
{
|
||||||
@ -70,7 +71,7 @@ public IDisposable CreateApp(List<string> urls)
|
|||||||
|
|
||||||
private void BuildApp(IAppBuilder appBuilder)
|
private void BuildApp(IAppBuilder appBuilder)
|
||||||
{
|
{
|
||||||
appBuilder.Properties["host.AppName"] = "NzbDrone";
|
appBuilder.Properties["host.AppName"] = BuildInfo.AppName;
|
||||||
|
|
||||||
foreach (var middleWare in _owinMiddleWares.OrderBy(c => c.Order))
|
foreach (var middleWare in _owinMiddleWares.OrderBy(c => c.Order))
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
|
using System;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
using NzbDrone.Common.Processes;
|
||||||
|
using Radarr.Host.AccessControl;
|
||||||
|
using IServiceProvider = NzbDrone.Common.IServiceProvider;
|
||||||
|
|
||||||
|
|
||||||
namespace Radarr.Host
|
namespace Radarr.Host
|
||||||
{
|
{
|
||||||
@ -10,15 +15,24 @@ public class Router
|
|||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly IConsoleService _consoleService;
|
private readonly IConsoleService _consoleService;
|
||||||
private readonly IRuntimeInfo _runtimeInfo;
|
private readonly IRuntimeInfo _runtimeInfo;
|
||||||
|
private readonly IProcessProvider _processProvider;
|
||||||
|
private readonly IRemoteAccessAdapter _remoteAccessAdapter;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public Router(INzbDroneServiceFactory nzbDroneServiceFactory, IServiceProvider serviceProvider,
|
public Router(INzbDroneServiceFactory nzbDroneServiceFactory,
|
||||||
IConsoleService consoleService, IRuntimeInfo runtimeInfo, Logger logger)
|
IServiceProvider serviceProvider,
|
||||||
|
IConsoleService consoleService,
|
||||||
|
IRuntimeInfo runtimeInfo,
|
||||||
|
IProcessProvider processProvider,
|
||||||
|
IRemoteAccessAdapter remoteAccessAdapter,
|
||||||
|
Logger logger)
|
||||||
{
|
{
|
||||||
_nzbDroneServiceFactory = nzbDroneServiceFactory;
|
_nzbDroneServiceFactory = nzbDroneServiceFactory;
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
_consoleService = consoleService;
|
_consoleService = consoleService;
|
||||||
_runtimeInfo = runtimeInfo;
|
_runtimeInfo = runtimeInfo;
|
||||||
|
_processProvider = processProvider;
|
||||||
|
_remoteAccessAdapter = remoteAccessAdapter;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,8 +64,13 @@ public void Route(ApplicationModes applicationModes)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_remoteAccessAdapter.MakeAccessible(true);
|
||||||
_serviceProvider.Install(ServiceProvider.SERVICE_NAME);
|
_serviceProvider.Install(ServiceProvider.SERVICE_NAME);
|
||||||
_serviceProvider.Start(ServiceProvider.SERVICE_NAME);
|
_serviceProvider.SetPermissions(ServiceProvider.SERVICE_NAME);
|
||||||
|
|
||||||
|
// Start the service and exit.
|
||||||
|
// Ensures that there isn't an instance of Radarr already running that the service account cannot stop.
|
||||||
|
_processProvider.SpawnNewProcess("sc.exe", $"start {ServiceProvider.SERVICE_NAME}", null, true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -67,6 +86,13 @@ public void Route(ApplicationModes applicationModes)
|
|||||||
_serviceProvider.Uninstall(ServiceProvider.SERVICE_NAME);
|
_serviceProvider.Uninstall(ServiceProvider.SERVICE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ApplicationModes.RegisterUrl:
|
||||||
|
{
|
||||||
|
_logger.Debug("Regiser URL selected");
|
||||||
|
_remoteAccessAdapter.MakeAccessible(false);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -76,7 +102,5 @@ public void Route(ApplicationModes applicationModes)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Api.Movies;
|
using Radarr.Api.V2.Movies;
|
||||||
|
|
||||||
namespace NzbDrone.Integration.Test.ApiTests
|
namespace NzbDrone.Integration.Test.ApiTests
|
||||||
{
|
{
|
||||||
@ -15,7 +15,7 @@ public void should_be_able_to_add_to_blacklist()
|
|||||||
{
|
{
|
||||||
_movie = EnsureMovie(11, "The Blacklist");
|
_movie = EnsureMovie(11, "The Blacklist");
|
||||||
|
|
||||||
Blacklist.Post(new Api.Blacklist.BlacklistResource
|
Blacklist.Post(new Radarr.Api.V2.Blacklist.BlacklistResource
|
||||||
{
|
{
|
||||||
MovieId = _movie.Id,
|
MovieId = _movie.Id,
|
||||||
SourceTitle = "Blacklist.S01E01.Brought.To.You.By-BoomBoxHD"
|
SourceTitle = "Blacklist.S01E01.Brought.To.You.By-BoomBoxHD"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Api.Movies;
|
using Radarr.Api.V2.Movies;
|
||||||
using NzbDrone.Integration.Test.Client;
|
using NzbDrone.Integration.Test.Client;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Api.Commands;
|
using NzbDrone.Integration.Test.Client;
|
||||||
|
|
||||||
namespace NzbDrone.Integration.Test.ApiTests
|
namespace NzbDrone.Integration.Test.ApiTests
|
||||||
{
|
{
|
||||||
@ -10,7 +10,7 @@ public class CommandFixture : IntegrationTest
|
|||||||
[Test]
|
[Test]
|
||||||
public void should_be_able_to_run_rss_sync()
|
public void should_be_able_to_run_rss_sync()
|
||||||
{
|
{
|
||||||
var response = Commands.Post(new CommandResource { Name = "rsssync" });
|
var response = Commands.Post(new SimpleCommandResource { Name = "rsssync" });
|
||||||
|
|
||||||
response.Id.Should().NotBe(0);
|
response.Id.Should().NotBe(0);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Api.DiskSpace;
|
using Radarr.Api.V2.DiskSpace;
|
||||||
using NzbDrone.Integration.Test.Client;
|
using NzbDrone.Integration.Test.Client;
|
||||||
|
|
||||||
namespace NzbDrone.Integration.Test.ApiTests
|
namespace NzbDrone.Integration.Test.ApiTests
|
||||||
|
@ -16,8 +16,8 @@ public void add_downloadclient_without_name_should_return_badrequest()
|
|||||||
var schema = DownloadClients.Schema().First(v => v.Implementation == "UsenetBlackhole");
|
var schema = DownloadClients.Schema().First(v => v.Implementation == "UsenetBlackhole");
|
||||||
|
|
||||||
schema.Enable = true;
|
schema.Enable = true;
|
||||||
schema.Fields.First(v => v.Name == "WatchFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Watch");
|
schema.Fields.First(v => v.Name == "watchFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Watch");
|
||||||
schema.Fields.First(v => v.Name == "NzbFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Nzb");
|
schema.Fields.First(v => v.Name == "nzbFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Nzb");
|
||||||
|
|
||||||
DownloadClients.InvalidPost(schema);
|
DownloadClients.InvalidPost(schema);
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ public void add_downloadclient_without_nzbfolder_should_return_badrequest()
|
|||||||
|
|
||||||
schema.Enable = true;
|
schema.Enable = true;
|
||||||
schema.Name = "Test UsenetBlackhole";
|
schema.Name = "Test UsenetBlackhole";
|
||||||
schema.Fields.First(v => v.Name == "WatchFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Watch");
|
schema.Fields.First(v => v.Name == "watchFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Watch");
|
||||||
|
|
||||||
DownloadClients.InvalidPost(schema);
|
DownloadClients.InvalidPost(schema);
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ public void add_downloadclient_without_watchfolder_should_return_badrequest()
|
|||||||
|
|
||||||
schema.Enable = true;
|
schema.Enable = true;
|
||||||
schema.Name = "Test UsenetBlackhole";
|
schema.Name = "Test UsenetBlackhole";
|
||||||
schema.Fields.First(v => v.Name == "NzbFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Nzb");
|
schema.Fields.First(v => v.Name == "nzbFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Nzb");
|
||||||
|
|
||||||
DownloadClients.InvalidPost(schema);
|
DownloadClients.InvalidPost(schema);
|
||||||
}
|
}
|
||||||
@ -59,8 +59,8 @@ public void add_downloadclient()
|
|||||||
|
|
||||||
schema.Enable = true;
|
schema.Enable = true;
|
||||||
schema.Name = "Test UsenetBlackhole";
|
schema.Name = "Test UsenetBlackhole";
|
||||||
schema.Fields.First(v => v.Name == "WatchFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Watch");
|
schema.Fields.First(v => v.Name == "watchFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Watch");
|
||||||
schema.Fields.First(v => v.Name == "NzbFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Nzb");
|
schema.Fields.First(v => v.Name == "nzbFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Nzb");
|
||||||
|
|
||||||
var result = DownloadClients.Post(schema);
|
var result = DownloadClients.Post(schema);
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ public void update_downloadclient()
|
|||||||
EnsureNoDownloadClient();
|
EnsureNoDownloadClient();
|
||||||
var client = EnsureDownloadClient();
|
var client = EnsureDownloadClient();
|
||||||
|
|
||||||
client.Fields.First(v => v.Name == "NzbFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Nzb2");
|
client.Fields.First(v => v.Name == "nzbFolder").Value = GetTempDirectory("Download", "UsenetBlackhole", "Nzb2");
|
||||||
var result = DownloadClients.Put(client);
|
var result = DownloadClients.Put(client);
|
||||||
|
|
||||||
result.Should().NotBeNull();
|
result.Should().NotBeNull();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Radarr.Api.V2.Movies;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
namespace NzbDrone.Integration.Test.ApiTests
|
namespace NzbDrone.Integration.Test.ApiTests
|
||||||
@ -10,11 +11,11 @@ public class MovieEditorFixture : IntegrationTest
|
|||||||
{
|
{
|
||||||
private void GivenExistingMovie()
|
private void GivenExistingMovie()
|
||||||
{
|
{
|
||||||
foreach (var title in new[] { "90210", "Dexter" })
|
foreach (var title in new[] { "The Dark Knight", "Pulp Fiction" })
|
||||||
{
|
{
|
||||||
var newMovie = Movies.Lookup(title).First();
|
var newMovie = Movies.Lookup(title).First();
|
||||||
|
|
||||||
newMovie.ProfileId = 1;
|
newMovie.QualityProfileId = 1;
|
||||||
newMovie.Path = string.Format(@"C:\Test\{0}", title).AsOsAgnostic();
|
newMovie.Path = string.Format(@"C:\Test\{0}", title).AsOsAgnostic();
|
||||||
|
|
||||||
Movies.Post(newMovie);
|
Movies.Post(newMovie);
|
||||||
@ -26,17 +27,18 @@ public void should_be_able_to_update_multiple_movies()
|
|||||||
{
|
{
|
||||||
GivenExistingMovie();
|
GivenExistingMovie();
|
||||||
|
|
||||||
var movie = Movies.All();
|
var movies = Movies.All();
|
||||||
|
|
||||||
foreach (var s in movie)
|
var movieEditor = new MovieEditorResource
|
||||||
{
|
{
|
||||||
s.ProfileId = 2;
|
QualityProfileId = 2,
|
||||||
}
|
MovieIds = movies.Select(o => o.Id).ToList()
|
||||||
|
};
|
||||||
|
|
||||||
var result = Movies.Editor(movie);
|
var result = Movies.Editor(movieEditor);
|
||||||
|
|
||||||
result.Should().HaveCount(2);
|
result.Should().HaveCount(2);
|
||||||
result.TrueForAll(s => s.ProfileId == 2).Should().BeTrue();
|
result.TrueForAll(s => s.QualityProfileId == 2).Should().BeTrue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ public void add_movie_with_tags_should_store_them()
|
|||||||
|
|
||||||
var movie = Movies.Lookup("imdb:tt0110912").Single();
|
var movie = Movies.Lookup("imdb:tt0110912").Single();
|
||||||
|
|
||||||
movie.ProfileId = 1;
|
movie.QualityProfileId = 1;
|
||||||
movie.Path = Path.Combine(MovieRootFolder, movie.Title);
|
movie.Path = Path.Combine(MovieRootFolder, movie.Title);
|
||||||
movie.Tags = new HashSet<int>();
|
movie.Tags = new HashSet<int>();
|
||||||
movie.Tags.Add(tag.Id);
|
movie.Tags.Add(tag.Id);
|
||||||
@ -48,7 +48,7 @@ public void add_movie_without_path_should_return_badrequest()
|
|||||||
|
|
||||||
var movie = Movies.Lookup("imdb:tt0110912").Single();
|
var movie = Movies.Lookup("imdb:tt0110912").Single();
|
||||||
|
|
||||||
movie.ProfileId = 1;
|
movie.QualityProfileId = 1;
|
||||||
|
|
||||||
Movies.InvalidPost(movie);
|
Movies.InvalidPost(movie);
|
||||||
}
|
}
|
||||||
@ -60,14 +60,14 @@ public void add_movie()
|
|||||||
|
|
||||||
var movie = Movies.Lookup("imdb:tt0110912").Single();
|
var movie = Movies.Lookup("imdb:tt0110912").Single();
|
||||||
|
|
||||||
movie.ProfileId = 1;
|
movie.QualityProfileId = 1;
|
||||||
movie.Path = Path.Combine(MovieRootFolder, movie.Title);
|
movie.Path = Path.Combine(MovieRootFolder, movie.Title);
|
||||||
|
|
||||||
var result = Movies.Post(movie);
|
var result = Movies.Post(movie);
|
||||||
|
|
||||||
result.Should().NotBeNull();
|
result.Should().NotBeNull();
|
||||||
result.Id.Should().NotBe(0);
|
result.Id.Should().NotBe(0);
|
||||||
result.ProfileId.Should().Be(1);
|
result.QualityProfileId.Should().Be(1);
|
||||||
result.Path.Should().Be(Path.Combine(MovieRootFolder, movie.Title));
|
result.Path.Should().Be(Path.Combine(MovieRootFolder, movie.Title));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,16 +105,16 @@ public void update_movie_profile_id()
|
|||||||
var movie = EnsureMovie(680, "Pulp Fiction");
|
var movie = EnsureMovie(680, "Pulp Fiction");
|
||||||
|
|
||||||
var profileId = 1;
|
var profileId = 1;
|
||||||
if (movie.ProfileId == profileId)
|
if (movie.QualityProfileId == profileId)
|
||||||
{
|
{
|
||||||
profileId = 2;
|
profileId = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
movie.ProfileId = profileId;
|
movie.QualityProfileId = profileId;
|
||||||
|
|
||||||
var result = Movies.Put(movie);
|
var result = Movies.Put(movie);
|
||||||
|
|
||||||
Movies.Get(movie.Id).ProfileId.Should().Be(profileId);
|
Movies.Get(movie.Id).QualityProfileId.Should().Be(profileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test, Order(3)]
|
[Test, Order(3)]
|
||||||
|
@ -25,11 +25,11 @@ public void should_be_able_to_get_by_id()
|
|||||||
public void should_be_able_to_update()
|
public void should_be_able_to_update()
|
||||||
{
|
{
|
||||||
var config = NamingConfig.GetSingle();
|
var config = NamingConfig.GetSingle();
|
||||||
config.RenameEpisodes = false;
|
config.RenameMovies = false;
|
||||||
config.StandardMovieFormat = "{Movie Title}";
|
config.StandardMovieFormat = "{Movie Title}";
|
||||||
|
|
||||||
var result = NamingConfig.Put(config);
|
var result = NamingConfig.Put(config);
|
||||||
result.RenameEpisodes.Should().BeFalse();
|
result.RenameMovies.Should().BeFalse();
|
||||||
result.StandardMovieFormat.Should().Be(config.StandardMovieFormat);
|
result.StandardMovieFormat.Should().Be(config.StandardMovieFormat);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ public void should_be_able_to_update()
|
|||||||
public void should_get_bad_request_if_standard_format_is_empty()
|
public void should_get_bad_request_if_standard_format_is_empty()
|
||||||
{
|
{
|
||||||
var config = NamingConfig.GetSingle();
|
var config = NamingConfig.GetSingle();
|
||||||
config.RenameEpisodes = true;
|
config.RenameMovies = true;
|
||||||
config.StandardMovieFormat = "";
|
config.StandardMovieFormat = "";
|
||||||
|
|
||||||
var errors = NamingConfig.InvalidPut(config);
|
var errors = NamingConfig.InvalidPut(config);
|
||||||
@ -49,7 +49,7 @@ public void should_get_bad_request_if_standard_format_is_empty()
|
|||||||
public void should_get_bad_request_if_standard_format_doesnt_contain_title()
|
public void should_get_bad_request_if_standard_format_doesnt_contain_title()
|
||||||
{
|
{
|
||||||
var config = NamingConfig.GetSingle();
|
var config = NamingConfig.GetSingle();
|
||||||
config.RenameEpisodes = true;
|
config.RenameMovies = true;
|
||||||
config.StandardMovieFormat = "{quality}";
|
config.StandardMovieFormat = "{quality}";
|
||||||
|
|
||||||
var errors = NamingConfig.InvalidPut(config);
|
var errors = NamingConfig.InvalidPut(config);
|
||||||
@ -60,7 +60,7 @@ public void should_get_bad_request_if_standard_format_doesnt_contain_title()
|
|||||||
public void should_not_require_format_when_rename_episodes_is_false()
|
public void should_not_require_format_when_rename_episodes_is_false()
|
||||||
{
|
{
|
||||||
var config = NamingConfig.GetSingle();
|
var config = NamingConfig.GetSingle();
|
||||||
config.RenameEpisodes = false;
|
config.RenameMovies = false;
|
||||||
config.StandardMovieFormat = "";
|
config.StandardMovieFormat = "";
|
||||||
|
|
||||||
var errors = NamingConfig.InvalidPut(config);
|
var errors = NamingConfig.InvalidPut(config);
|
||||||
@ -71,7 +71,7 @@ public void should_not_require_format_when_rename_episodes_is_false()
|
|||||||
public void should_require_format_when_rename_episodes_is_true()
|
public void should_require_format_when_rename_episodes_is_true()
|
||||||
{
|
{
|
||||||
var config = NamingConfig.GetSingle();
|
var config = NamingConfig.GetSingle();
|
||||||
config.RenameEpisodes = true;
|
config.RenameMovies = true;
|
||||||
config.StandardMovieFormat = "";
|
config.StandardMovieFormat = "";
|
||||||
|
|
||||||
var errors = NamingConfig.InvalidPut(config);
|
var errors = NamingConfig.InvalidPut(config);
|
||||||
@ -82,7 +82,7 @@ public void should_require_format_when_rename_episodes_is_true()
|
|||||||
public void should_get_bad_request_if_movie_folder_format_does_not_contain_movie_title()
|
public void should_get_bad_request_if_movie_folder_format_does_not_contain_movie_title()
|
||||||
{
|
{
|
||||||
var config = NamingConfig.GetSingle();
|
var config = NamingConfig.GetSingle();
|
||||||
config.RenameEpisodes = true;
|
config.RenameMovies = true;
|
||||||
config.MovieFolderFormat = "This and That";
|
config.MovieFolderFormat = "This and That";
|
||||||
|
|
||||||
var errors = NamingConfig.InvalidPut(config);
|
var errors = NamingConfig.InvalidPut(config);
|
||||||
|
@ -34,7 +34,7 @@ public void should_be_able_to_add_a_new_notification()
|
|||||||
var xbmc = schema.Single(s => s.Implementation.Equals("Xbmc", StringComparison.InvariantCultureIgnoreCase));
|
var xbmc = schema.Single(s => s.Implementation.Equals("Xbmc", StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
|
||||||
xbmc.Name = "Test XBMC";
|
xbmc.Name = "Test XBMC";
|
||||||
xbmc.Fields.Single(f => f.Name.Equals("Host")).Value = "localhost";
|
xbmc.Fields.Single(f => f.Name.Equals("host")).Value = "localhost";
|
||||||
|
|
||||||
var result = Notifications.Post(xbmc);
|
var result = Notifications.Post(xbmc);
|
||||||
Notifications.Delete(result.Id);
|
Notifications.Delete(result.Id);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Api.Indexers;
|
using Radarr.Api.V2.Indexers;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Api.RootFolders;
|
using Radarr.Api.V2.RootFolders;
|
||||||
|
|
||||||
namespace NzbDrone.Integration.Test.ApiTests
|
namespace NzbDrone.Integration.Test.ApiTests
|
||||||
{
|
{
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using FluentAssertions;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
|
|
||||||
namespace NzbDrone.Integration.Test.ApiTests.WantedTests
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
public class CutoffUnmetFixture : IntegrationTest
|
|
||||||
{
|
|
||||||
[Test, Order(1)]
|
|
||||||
public void cutoff_should_have_monitored_items()
|
|
||||||
{
|
|
||||||
EnsureProfileCutoff(1, Quality.HDTV720p);
|
|
||||||
var movie = EnsureMovie(680, "Pulp Fiction", true);
|
|
||||||
EnsureMovieFile(movie, Quality.SDTV);
|
|
||||||
|
|
||||||
var result = WantedCutoffUnmet.GetPaged(0, 15, "physicalRelease", "desc", "monitored", "true");
|
|
||||||
|
|
||||||
result.Records.Should().NotBeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Order(1)]
|
|
||||||
public void cutoff_should_not_have_unmonitored_items()
|
|
||||||
{
|
|
||||||
EnsureProfileCutoff(1, Quality.HDTV720p);
|
|
||||||
var movie = EnsureMovie(680, "Pulp Fiction", false);
|
|
||||||
EnsureMovieFile(movie, Quality.SDTV);
|
|
||||||
|
|
||||||
var result = WantedCutoffUnmet.GetPaged(0, 15, "physicalRelease", "desc", "monitored", "true");
|
|
||||||
|
|
||||||
result.Records.Should().BeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Order(1)]
|
|
||||||
public void cutoff_should_not_have_released_items()
|
|
||||||
{
|
|
||||||
EnsureProfileCutoff(1, Quality.HDTV720p);
|
|
||||||
var movie = EnsureMovie(680, "Pulp Fiction", true);
|
|
||||||
EnsureMovieFile(movie, Quality.SDTV);
|
|
||||||
|
|
||||||
var result = WantedCutoffUnmet.GetPaged(0, 15, "physicalRelease", "desc", "status", "inCinemas");
|
|
||||||
|
|
||||||
result.Records.Should().BeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Order(1)]
|
|
||||||
public void cutoff_should_have_movie()
|
|
||||||
{
|
|
||||||
EnsureProfileCutoff(1, Quality.HDTV720p);
|
|
||||||
var movie = EnsureMovie(680, "Pulp Fiction", true);
|
|
||||||
EnsureMovieFile(movie, Quality.SDTV);
|
|
||||||
|
|
||||||
var result = WantedCutoffUnmet.GetPaged(0, 15, "physicalRelease", "desc");
|
|
||||||
|
|
||||||
result.Records.First().Title.Should().Be("Pulp Fiction");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Order(2)]
|
|
||||||
public void cutoff_should_have_unmonitored_items()
|
|
||||||
{
|
|
||||||
EnsureProfileCutoff(1, Quality.HDTV720p);
|
|
||||||
var movie = EnsureMovie(680, "Pulp Fiction", false);
|
|
||||||
EnsureMovieFile(movie, Quality.SDTV);
|
|
||||||
|
|
||||||
var result = WantedCutoffUnmet.GetPaged(0, 15, "physicalRelease", "desc", "monitored", "false");
|
|
||||||
|
|
||||||
result.Records.Should().NotBeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Order(2)]
|
|
||||||
public void cutoff_should_have_released_items()
|
|
||||||
{
|
|
||||||
EnsureProfileCutoff(1, Quality.HDTV720p);
|
|
||||||
var movie = EnsureMovie(680, "Pulp Fiction", false);
|
|
||||||
EnsureMovieFile(movie, Quality.SDTV);
|
|
||||||
|
|
||||||
var result = WantedCutoffUnmet.GetPaged(0, 15, "physicalRelease", "desc", "status", "released");
|
|
||||||
|
|
||||||
result.Records.Should().NotBeEmpty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,81 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using FluentAssertions;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
|
|
||||||
namespace NzbDrone.Integration.Test.ApiTests.WantedTests
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
public class MissingFixture : IntegrationTest
|
|
||||||
{
|
|
||||||
[Test, Order(0)]
|
|
||||||
public void missing_should_be_empty()
|
|
||||||
{
|
|
||||||
EnsureNoMovie(680, "Pulp Fiction");
|
|
||||||
|
|
||||||
var result = WantedMissing.GetPaged(0, 15, "physicalRelease", "desc");
|
|
||||||
|
|
||||||
result.Records.Should().BeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Order(1)]
|
|
||||||
public void missing_should_have_monitored_items()
|
|
||||||
{
|
|
||||||
EnsureMovie(680, "Pulp Fiction", true);
|
|
||||||
|
|
||||||
var result = WantedMissing.GetPaged(0, 15, "physicalRelease", "desc", "monitored", "true");
|
|
||||||
|
|
||||||
result.Records.Should().NotBeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Order(1)]
|
|
||||||
public void missing_should_have_movie()
|
|
||||||
{
|
|
||||||
EnsureMovie(680, "Pulp Fiction", true);
|
|
||||||
|
|
||||||
var result = WantedMissing.GetPaged(0, 15, "physicalRelease", "desc");
|
|
||||||
|
|
||||||
result.Records.First().Title.Should().Be("Pulp Fiction");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Order(1)]
|
|
||||||
public void missing_should_not_have_unmonitored_items()
|
|
||||||
{
|
|
||||||
EnsureMovie(680, "Pulp Fiction", false);
|
|
||||||
|
|
||||||
var result = WantedMissing.GetPaged(0, 15, "physicalRelease", "desc", "monitored", "true");
|
|
||||||
|
|
||||||
result.Records.Should().BeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Order(1)]
|
|
||||||
public void missing_should_not_have_released_items()
|
|
||||||
{
|
|
||||||
EnsureMovie(680, "Pulp Fiction", false);
|
|
||||||
|
|
||||||
var result = WantedMissing.GetPaged(0, 15, "physicalRelease", "desc", "status", "inCinemas");
|
|
||||||
|
|
||||||
result.Records.Should().BeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Order(2)]
|
|
||||||
public void missing_should_have_unmonitored_items()
|
|
||||||
{
|
|
||||||
EnsureMovie(680, "Pulp Fiction", false);
|
|
||||||
|
|
||||||
var result = WantedMissing.GetPaged(0, 15, "physicalRelease", "desc", "monitored", "false");
|
|
||||||
|
|
||||||
result.Records.Should().NotBeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test, Order(2)]
|
|
||||||
public void missing_should_have_released_items()
|
|
||||||
{
|
|
||||||
EnsureMovie(680, "Pulp Fiction", false);
|
|
||||||
|
|
||||||
var result = WantedMissing.GetPaged(0, 15, "physicalRelease", "desc", "status", "released");
|
|
||||||
|
|
||||||
result.Records.Should().NotBeEmpty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,6 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Api;
|
|
||||||
using Radarr.Http.REST;
|
using Radarr.Http.REST;
|
||||||
using Radarr.Http;
|
using Radarr.Http;
|
||||||
using NzbDrone.Common.Serializer;
|
using NzbDrone.Common.Serializer;
|
||||||
@ -40,7 +39,7 @@ public RestRequest BuildRequest(string command = "")
|
|||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T Execute<T>(IRestRequest request, HttpStatusCode statusCode) where T : class, new()
|
public string Execute(IRestRequest request, HttpStatusCode statusCode)
|
||||||
{
|
{
|
||||||
_logger.Info("{0}: {1}", request.Method, _restClient.BuildUri(request));
|
_logger.Info("{0}: {1}", request.Method, _restClient.BuildUri(request));
|
||||||
|
|
||||||
@ -58,7 +57,14 @@ public RestRequest BuildRequest(string command = "")
|
|||||||
|
|
||||||
response.StatusCode.Should().Be(statusCode);
|
response.StatusCode.Should().Be(statusCode);
|
||||||
|
|
||||||
return Json.Deserialize<T>(response.Content);
|
return response.Content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Execute<T>(IRestRequest request, HttpStatusCode statusCode) where T : class, new()
|
||||||
|
{
|
||||||
|
var content = Execute(request, statusCode);
|
||||||
|
|
||||||
|
return Json.Deserialize<T>(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertDisableCache(IList<Parameter> headers)
|
private static void AssertDisableCache(IList<Parameter> headers)
|
||||||
|
@ -1,23 +1,47 @@
|
|||||||
using NzbDrone.Api.Commands;
|
using RestSharp;
|
||||||
using RestSharp;
|
|
||||||
using NzbDrone.Core.Messaging.Commands;
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System;
|
||||||
|
using Radarr.Http.REST;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace NzbDrone.Integration.Test.Client
|
namespace NzbDrone.Integration.Test.Client
|
||||||
{
|
{
|
||||||
public class CommandClient : ClientBase<CommandResource>
|
public class SimpleCommandResource : RestResource
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string CommandName { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
public CommandPriority Priority { get; set; }
|
||||||
|
public CommandStatus Status { get; set; }
|
||||||
|
public DateTime Queued { get; set; }
|
||||||
|
public DateTime? Started { get; set; }
|
||||||
|
public DateTime? Ended { get; set; }
|
||||||
|
public TimeSpan? Duration { get; set; }
|
||||||
|
public string Exception { get; set; }
|
||||||
|
public CommandTrigger Trigger { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public Command Body { get; set; }
|
||||||
|
[JsonProperty("body")]
|
||||||
|
public Command BodyReadOnly { get { return Body; } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CommandClient : ClientBase<SimpleCommandResource>
|
||||||
{
|
{
|
||||||
public CommandClient(IRestClient restClient, string apiKey)
|
public CommandClient(IRestClient restClient, string apiKey)
|
||||||
: base(restClient, apiKey)
|
: base(restClient, apiKey, "command")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommandResource PostAndWait(CommandResource command)
|
public SimpleCommandResource PostAndWait<T>(T command) where T : Command, new()
|
||||||
{
|
{
|
||||||
var result = Post(command);
|
var request = BuildRequest();
|
||||||
|
request.AddBody(command);
|
||||||
|
var result = Post<SimpleCommandResource>(request);
|
||||||
result.Id.Should().NotBe(0);
|
result.Id.Should().NotBe(0);
|
||||||
|
|
||||||
for (var i = 0; i < 50; i++)
|
for (var i = 0; i < 50; i++)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NzbDrone.Api.DownloadClient;
|
using Radarr.Api.V2.DownloadClient;
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
|
|
||||||
namespace NzbDrone.Integration.Test.Client
|
namespace NzbDrone.Integration.Test.Client
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using NzbDrone.Api.Indexers;
|
using Radarr.Api.V2.Indexers;
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
|
|
||||||
namespace NzbDrone.Integration.Test.Client
|
namespace NzbDrone.Integration.Test.Client
|
||||||
|
24
src/NzbDrone.Integration.Test/Client/LogsClient.cs
Normal file
24
src/NzbDrone.Integration.Test/Client/LogsClient.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using RestSharp;
|
||||||
|
|
||||||
|
namespace NzbDrone.Integration.Test.Client
|
||||||
|
{
|
||||||
|
public class LogsClient : ClientBase
|
||||||
|
{
|
||||||
|
public LogsClient(IRestClient restClient, string apiKey)
|
||||||
|
: base(restClient, apiKey, "log/file")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] GetLogFileLines(string filename)
|
||||||
|
{
|
||||||
|
var request = BuildRequest(filename);
|
||||||
|
var content = Execute(request, System.Net.HttpStatusCode.OK);
|
||||||
|
|
||||||
|
var lines = content.Split('\n');
|
||||||
|
lines = Array.ConvertAll(lines, s => s.TrimEnd('\r'));
|
||||||
|
Array.Resize(ref lines, lines.Length - 1);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using NzbDrone.Api.Movies;
|
using Radarr.Api.V2.Movies;
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
|
|
||||||
namespace NzbDrone.Integration.Test.Client
|
namespace NzbDrone.Integration.Test.Client
|
||||||
@ -19,7 +19,7 @@ public List<MovieResource> Lookup(string term)
|
|||||||
return Get<List<MovieResource>>(request);
|
return Get<List<MovieResource>>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MovieResource> Editor(List<MovieResource> movie)
|
public List<MovieResource> Editor(MovieEditorResource movie)
|
||||||
{
|
{
|
||||||
var request = BuildRequest("editor");
|
var request = BuildRequest("editor");
|
||||||
request.AddJsonBody(movie);
|
request.AddJsonBody(movie);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NzbDrone.Api.Notifications;
|
using Radarr.Api.V2.Notifications;
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
|
|
||||||
namespace NzbDrone.Integration.Test.Client
|
namespace NzbDrone.Integration.Test.Client
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using NzbDrone.Api.Indexers;
|
using Radarr.Api.V2.Indexers;
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
|
|
||||||
namespace NzbDrone.Integration.Test.Client
|
namespace NzbDrone.Integration.Test.Client
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user