mirror of
https://github.com/Readarr/Readarr.git
synced 2026-03-12 15:29:59 -04:00
Compare commits
61 Commits
sonarr-pul
...
v0.3.4.220
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75efbd45e1 | ||
|
|
00cac507ad | ||
|
|
c4850505b0 | ||
|
|
75213c86a1 | ||
|
|
b8c3a42643 | ||
|
|
8acb034aa6 | ||
|
|
889d32552b | ||
|
|
adc5f4db97 | ||
|
|
9d08050f96 | ||
|
|
f8cffbb4cf | ||
|
|
14aeb66142 | ||
|
|
37e8e11e31 | ||
|
|
bdb2f14936 | ||
|
|
a97af657be | ||
|
|
301127e6dc | ||
|
|
1f95bcae4e | ||
|
|
29118cda45 | ||
|
|
09beaa939d | ||
|
|
2107624f1c | ||
|
|
c1c2076e5c | ||
|
|
c31a797bd8 | ||
|
|
ebb2b4eca3 | ||
|
|
3ec5d9b9fe | ||
|
|
1ad84a7c44 | ||
|
|
9d67c18254 | ||
|
|
2e39c7340c | ||
|
|
f75add984f | ||
|
|
f0f6c3eb35 | ||
|
|
d94f866aeb | ||
|
|
618f07d138 | ||
|
|
3db33c988a | ||
|
|
28e38b7f17 | ||
|
|
ca403e6f31 | ||
|
|
51351dee1d | ||
|
|
2081f2e321 | ||
|
|
c10a32534c | ||
|
|
0e415c6ce3 | ||
|
|
a8eb674071 | ||
|
|
7f8a1cf849 | ||
|
|
a3c0d10240 | ||
|
|
3ddeaaefe2 | ||
|
|
6d99de4fe0 | ||
|
|
8b36a5ce92 | ||
|
|
1202a43466 | ||
|
|
82bc2d1aa4 | ||
|
|
ed9af393b7 | ||
|
|
0799cfc885 | ||
|
|
331d0c9a9c | ||
|
|
03c93c9c84 | ||
|
|
60f6ed030b | ||
|
|
cc70d61735 | ||
|
|
a7b965100d | ||
|
|
8901118aef | ||
|
|
99c17d7698 | ||
|
|
b84e83b082 | ||
|
|
4249f5324a | ||
|
|
9e1630e9a4 | ||
|
|
68b2773913 | ||
|
|
ad446b358e | ||
|
|
423a8ecbe1 | ||
|
|
29a12aa3b0 |
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -3,8 +3,7 @@
|
||||
|
||||
# Explicitly set bash scripts to have unix endings
|
||||
*.sh text eol=lf
|
||||
distribution/debian/* text eol=lf
|
||||
macOS/Readarr text eol=lf
|
||||
distribution/osx/Readarr text eol=lf
|
||||
|
||||
# Custom for Visual Studio
|
||||
*.cs diff=csharp
|
||||
|
||||
@@ -30,7 +30,6 @@ Note that only one type of a given book is supported. If you want both an audiob
|
||||
|
||||
[](https://wiki.servarr.com/readarr)
|
||||
[](https://readarr.com/discord)
|
||||
[](https://www.reddit.com/r/readarr)
|
||||
|
||||
Note: GitHub Issues are for Bugs and Feature Requests Only
|
||||
|
||||
|
||||
@@ -9,13 +9,13 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '0.3.2'
|
||||
majorVersion: '0.3.4'
|
||||
minorVersion: $[counter('minorVersion', 1)]
|
||||
readarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(readarrVersion)'
|
||||
sentryOrg: 'servarr'
|
||||
sentryUrl: 'https://sentry.servarr.com'
|
||||
dotnetVersion: '6.0.408'
|
||||
dotnetVersion: '6.0.413'
|
||||
nodeVersion: '16.X'
|
||||
innoVersion: '6.2.0'
|
||||
windowsImage: 'windows-2022'
|
||||
@@ -27,6 +27,10 @@ trigger:
|
||||
include:
|
||||
- develop
|
||||
- master
|
||||
paths:
|
||||
exclude:
|
||||
- .github
|
||||
- src/Readarr.Api.*/openapi.json
|
||||
|
||||
pr:
|
||||
branches:
|
||||
@@ -34,82 +38,37 @@ pr:
|
||||
- develop
|
||||
paths:
|
||||
exclude:
|
||||
- .github
|
||||
- src/NzbDrone.Core/Localization/Core
|
||||
- src/Readarr.Api.*/openapi.json
|
||||
|
||||
stages:
|
||||
|
||||
- stage: Build_Backend_Windows
|
||||
displayName: Build Backend
|
||||
dependsOn: []
|
||||
- stage: Setup
|
||||
displayName: Setup
|
||||
jobs:
|
||||
- job: Backend
|
||||
strategy:
|
||||
matrix:
|
||||
Windows:
|
||||
osName: 'Windows'
|
||||
imageName: ${{ variables.windowsImage }}
|
||||
enableAnalysis: 'false'
|
||||
|
||||
- job:
|
||||
displayName: Build Variables
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
variables:
|
||||
# Disable stylecop here - linting errors get caught by the analyze task
|
||||
EnableAnalyzers: $(enableAnalysis)
|
||||
vmImage: ${{ variables.linuxImage }}
|
||||
steps:
|
||||
# Set the build name properly. The 'name' property won't recursively expand so hack here:
|
||||
- bash: echo "##vso[build.updatebuildnumber]$READARRVERSION"
|
||||
displayName: Set Build Name
|
||||
- checkout: self
|
||||
submodules: true
|
||||
fetchDepth: 1
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
- bash: |
|
||||
SDK_PATH="${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}"
|
||||
BUNDLEDVERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props"
|
||||
|
||||
if ! grep -q freebsd-x64 $BUNDLEDVERSIONS; then
|
||||
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
|
||||
if [[ $BUILD_REASON == "PullRequest" ]]; then
|
||||
git diff origin/develop...HEAD --name-only | grep -E "^(src/|azure-pipelines.yml)"
|
||||
echo $? > not_backend_update
|
||||
else
|
||||
echo 0 > not_backend_update
|
||||
fi
|
||||
displayName: Extra Platform Support
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props'
|
||||
path: $(nugetCacheFolder)
|
||||
displayName: Cache NuGet packages
|
||||
- bash: ./build.sh --backend --enable-bsd
|
||||
displayName: Build Readarr Backend
|
||||
env:
|
||||
NUGET_PACKAGES: $(nugetCacheFolder)
|
||||
- powershell: Get-ChildItem _output\net6.0*,_output\*.Update\* -Recurse | Where { $_.Fullname -notlike "*\publish\*" -and $_.attributes -notlike "*directory*" } | Remove-Item
|
||||
displayName: Clean up intermediate output
|
||||
- publish: $(outputFolder)
|
||||
artifact: '$(osName)Backend'
|
||||
displayName: Publish Backend
|
||||
- publish: '$(testsFolder)/net6.0/win-x64/publish'
|
||||
artifact: win-x64-tests
|
||||
displayName: Publish win-x64 Test Package
|
||||
- publish: '$(testsFolder)/net6.0/linux-x64/publish'
|
||||
artifact: linux-x64-tests
|
||||
displayName: Publish linux-x64 Test Package
|
||||
- publish: '$(testsFolder)/net6.0/linux-x86/publish'
|
||||
artifact: linux-x86-tests
|
||||
displayName: Publish linux-x86 Test Package
|
||||
- publish: '$(testsFolder)/net6.0/linux-musl-x64/publish'
|
||||
artifact: linux-musl-x64-tests
|
||||
displayName: Publish linux-musl-x64 Test Package
|
||||
- publish: '$(testsFolder)/net6.0/freebsd-x64/publish'
|
||||
artifact: freebsd-x64-tests
|
||||
displayName: Publish freebsd-x64 Test Package
|
||||
- publish: '$(testsFolder)/net6.0/osx-x64/publish'
|
||||
artifact: osx-x64-tests
|
||||
displayName: Publish osx-x64 Test Package
|
||||
|
||||
- stage: Build_Backend_Other
|
||||
displayName: Build Backend (Other OS)
|
||||
dependsOn: []
|
||||
cat not_backend_update
|
||||
displayName: Check for Backend File Changes
|
||||
- publish: not_backend_update
|
||||
artifact: not_backend_update
|
||||
displayName: Publish update type
|
||||
- stage: Build_Backend
|
||||
displayName: Build Backend
|
||||
dependsOn: Setup
|
||||
jobs:
|
||||
- job: Backend
|
||||
strategy:
|
||||
@@ -122,6 +81,10 @@ stages:
|
||||
osName: 'Mac'
|
||||
imageName: ${{ variables.macImage }}
|
||||
enableAnalysis: 'false'
|
||||
Windows:
|
||||
osName: 'Windows'
|
||||
imageName: ${{ variables.windowsImage }}
|
||||
enableAnalysis: 'false'
|
||||
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
@@ -137,22 +100,17 @@ stages:
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
- bash: |
|
||||
SDK_PATH="${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}"
|
||||
BUNDLEDVERSIONS="${SDK_PATH}/Microsoft.NETCoreSdk.BundledVersions.props"
|
||||
|
||||
if ! grep -q freebsd-x64 $BUNDLEDVERSIONS; then
|
||||
BUNDLEDVERSIONS=${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}/Microsoft.NETCoreSdk.BundledVersions.props
|
||||
echo $BUNDLEDVERSIONS
|
||||
if grep -q freebsd-x64 $BUNDLEDVERSIONS; then
|
||||
echo "Extra platforms already enabled"
|
||||
else
|
||||
echo "Enabling extra platform support"
|
||||
sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
|
||||
fi
|
||||
displayName: Extra Platform Support
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props'
|
||||
path: $(nugetCacheFolder)
|
||||
displayName: Cache NuGet packages
|
||||
displayName: Enable Extra Platform Support
|
||||
- bash: ./build.sh --backend --enable-extra-platforms
|
||||
displayName: Build Readarr Backend
|
||||
env:
|
||||
NUGET_PACKAGES: $(nugetCacheFolder)
|
||||
- bash: |
|
||||
find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
|
||||
find ${OUTPUTFOLDER} -depth -empty -type d -exec rm -r "{}" \;
|
||||
@@ -160,10 +118,38 @@ stages:
|
||||
find ${TESTSFOLDER} -depth -empty -type d -exec rm -r "{}" \;
|
||||
displayName: Clean up intermediate output
|
||||
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
|
||||
- publish: $(outputFolder)
|
||||
artifact: '$(osName)Backend'
|
||||
displayName: Publish Backend
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
- publish: '$(testsFolder)/net6.0/win-x64/publish'
|
||||
artifact: win-x64-tests
|
||||
displayName: Publish win-x64 Test Package
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
- publish: '$(testsFolder)/net6.0/linux-x64/publish'
|
||||
artifact: linux-x64-tests
|
||||
displayName: Publish linux-x64 Test Package
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
- publish: '$(testsFolder)/net6.0/linux-x86/publish'
|
||||
artifact: linux-x86-tests
|
||||
displayName: Publish linux-x86 Test Package
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
- publish: '$(testsFolder)/net6.0/linux-musl-x64/publish'
|
||||
artifact: linux-musl-x64-tests
|
||||
displayName: Publish linux-musl-x64 Test Package
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
- publish: '$(testsFolder)/net6.0/freebsd-x64/publish'
|
||||
artifact: freebsd-x64-tests
|
||||
displayName: Publish freebsd-x64 Test Package
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
- publish: '$(testsFolder)/net6.0/osx-x64/publish'
|
||||
artifact: osx-x64-tests
|
||||
displayName: Publish osx-x64 Test Package
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
|
||||
- stage: Build_Frontend
|
||||
displayName: Frontend
|
||||
dependsOn: []
|
||||
dependsOn: Setup
|
||||
jobs:
|
||||
- job: Build
|
||||
strategy:
|
||||
@@ -192,7 +178,6 @@ stages:
|
||||
key: 'yarn | "$(osName)" | yarn.lock'
|
||||
restoreKeys: |
|
||||
yarn | "$(osName)"
|
||||
yarn
|
||||
path: $(yarnCacheFolder)
|
||||
displayName: Cache Yarn packages
|
||||
- bash: ./build.sh --frontend
|
||||
@@ -204,10 +189,10 @@ stages:
|
||||
artifact: '$(osName)Frontend'
|
||||
displayName: Publish Frontend
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
|
||||
|
||||
|
||||
- stage: Installer
|
||||
dependsOn:
|
||||
- Build_Backend_Windows
|
||||
- Build_Backend
|
||||
- Build_Frontend
|
||||
jobs:
|
||||
- job: Windows_Installer
|
||||
@@ -231,8 +216,8 @@ stages:
|
||||
displayName: Fetch Frontend
|
||||
- bash: |
|
||||
./build.sh --packages --installer
|
||||
cp setup/output/Readarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x64-installer.exe
|
||||
cp setup/output/Readarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x86-installer.exe
|
||||
cp distribution/windows/setup/output/Readarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x64-installer.exe
|
||||
cp distribution/windows/setup/output/Readarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x86-installer.exe
|
||||
displayName: Create Installers
|
||||
- publish: $(Build.ArtifactStagingDirectory)
|
||||
artifact: 'WindowsInstaller'
|
||||
@@ -240,7 +225,7 @@ stages:
|
||||
|
||||
- stage: Packages
|
||||
dependsOn:
|
||||
- Build_Backend_Windows
|
||||
- Build_Backend
|
||||
- Build_Frontend
|
||||
jobs:
|
||||
- job: Other_Packages
|
||||
@@ -406,14 +391,29 @@ stages:
|
||||
SENTRY_AUTH_TOKEN: $(sentryAuthTokenServarr)
|
||||
SENTRY_ORG: $(sentryOrg)
|
||||
SENTRY_URL: $(sentryUrl)
|
||||
|
||||
|
||||
- stage: Unit_Test
|
||||
displayName: Unit Tests
|
||||
dependsOn: Build_Backend_Windows
|
||||
condition: succeeded()
|
||||
dependsOn: Build_Backend
|
||||
|
||||
jobs:
|
||||
- job: Prepare
|
||||
pool:
|
||||
vmImage: ${{ variables.linuxImage }}
|
||||
steps:
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'not_backend_update'
|
||||
targetPath: '.'
|
||||
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
|
||||
name: setVar
|
||||
|
||||
- job: Unit
|
||||
displayName: Unit Native
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
workspace:
|
||||
clean: all
|
||||
|
||||
@@ -479,6 +479,8 @@ stages:
|
||||
|
||||
- job: Unit_Docker
|
||||
displayName: Unit Docker
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
strategy:
|
||||
matrix:
|
||||
alpine:
|
||||
@@ -492,11 +494,11 @@ stages:
|
||||
|
||||
pool:
|
||||
vmImage: ${{ variables.linuxImage }}
|
||||
|
||||
|
||||
container: $[ variables['containerImage'] ]
|
||||
|
||||
timeoutInMinutes: 10
|
||||
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .NET'
|
||||
@@ -530,12 +532,14 @@ stages:
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: '$(testName) Unit Tests'
|
||||
failTaskOnFailedTests: true
|
||||
|
||||
- job: Unit_LinuxCore_Postgres
|
||||
displayName: Unit Native LinuxCore with Postgres Database
|
||||
|
||||
- job: Unit_LinuxCore_Postgres14
|
||||
displayName: Unit Native LinuxCore with Postgres14 Database
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
variables:
|
||||
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
||||
artifactName: LinuxCoreTests
|
||||
artifactName: linux-x64-tests
|
||||
Readarr__Postgres__Host: 'localhost'
|
||||
Readarr__Postgres__Port: '5432'
|
||||
Readarr__Postgres__User: 'readarr'
|
||||
@@ -545,7 +549,7 @@ stages:
|
||||
vmImage: ${{ variables.linuxImage }}
|
||||
|
||||
timeoutInMinutes: 10
|
||||
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
@@ -556,7 +560,7 @@ stages:
|
||||
displayName: Download Test Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'linux-x64-Tests'
|
||||
artifactName: $(artifactName)
|
||||
targetPath: $(testsFolder)
|
||||
- bash: find ${TESTSFOLDER} -name "Readarr.Test.Dummy" -exec chmod a+x {} \;
|
||||
displayName: Make Test Dummy Executable
|
||||
@@ -579,15 +583,84 @@ stages:
|
||||
inputs:
|
||||
testResultsFormat: 'NUnit'
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: 'LinuxCore Postgres Unit Tests'
|
||||
testRunTitle: 'LinuxCore Postgres14 Unit Tests'
|
||||
failTaskOnFailedTests: true
|
||||
|
||||
- job: Unit_LinuxCore_Postgres15
|
||||
displayName: Unit Native LinuxCore with Postgres15 Database
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
variables:
|
||||
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
||||
artifactName: linux-x64-tests
|
||||
Readarr__Postgres__Host: 'localhost'
|
||||
Readarr__Postgres__Port: '5432'
|
||||
Readarr__Postgres__User: 'readarr'
|
||||
Readarr__Postgres__Password: 'readarr'
|
||||
|
||||
pool:
|
||||
vmImage: ${{ variables.linuxImage }}
|
||||
|
||||
timeoutInMinutes: 10
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Test Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: $(artifactName)
|
||||
targetPath: $(testsFolder)
|
||||
- bash: find ${TESTSFOLDER} -name "Readarr.Test.Dummy" -exec chmod a+x {} \;
|
||||
displayName: Make Test Dummy Executable
|
||||
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
|
||||
- bash: |
|
||||
docker run -d --name=postgres15 \
|
||||
-e POSTGRES_PASSWORD=readarr \
|
||||
-e POSTGRES_USER=readarr \
|
||||
-p 5432:5432/tcp \
|
||||
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
|
||||
postgres:15
|
||||
displayName: Start postgres
|
||||
- bash: |
|
||||
chmod a+x ${TESTSFOLDER}/test.sh
|
||||
ls -lR ${TESTSFOLDER}
|
||||
${TESTSFOLDER}/test.sh Linux Unit Test
|
||||
displayName: Run Tests
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish Test Results
|
||||
inputs:
|
||||
testResultsFormat: 'NUnit'
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: 'LinuxCore Postgres15 Unit Tests'
|
||||
failTaskOnFailedTests: true
|
||||
|
||||
- stage: Integration
|
||||
displayName: Integration
|
||||
dependsOn: Packages
|
||||
|
||||
jobs:
|
||||
- job: Prepare
|
||||
pool:
|
||||
vmImage: ${{ variables.linuxImage }}
|
||||
steps:
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'not_backend_update'
|
||||
targetPath: '.'
|
||||
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
|
||||
name: setVar
|
||||
|
||||
- job: Integration_Native
|
||||
displayName: Integration Native
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
strategy:
|
||||
matrix:
|
||||
MacCore:
|
||||
@@ -608,7 +681,7 @@ stages:
|
||||
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
@@ -630,7 +703,7 @@ stages:
|
||||
targetPath: $(Build.ArtifactStagingDirectory)
|
||||
- task: ExtractFiles@1
|
||||
inputs:
|
||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||
displayName: Extract Package
|
||||
- bash: |
|
||||
@@ -649,8 +722,10 @@ stages:
|
||||
failTaskOnFailedTests: true
|
||||
displayName: Publish Test Results
|
||||
|
||||
- job: Integration_LinuxCore_Postgres
|
||||
displayName: Integration Native LinuxCore with Postgres Database
|
||||
- job: Integration_LinuxCore_Postgres14
|
||||
displayName: Integration Native LinuxCore with Postgres14 Database
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
variables:
|
||||
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
||||
Readarr__Postgres__Host: 'localhost'
|
||||
@@ -682,7 +757,7 @@ stages:
|
||||
targetPath: $(Build.ArtifactStagingDirectory)
|
||||
- task: ExtractFiles@1
|
||||
inputs:
|
||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||
displayName: Extract Package
|
||||
- bash: |
|
||||
@@ -705,12 +780,77 @@ stages:
|
||||
inputs:
|
||||
testResultsFormat: 'NUnit'
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: 'Integration LinuxCore Postgres Database Integration Tests'
|
||||
testRunTitle: 'Integration LinuxCore Postgres14 Database Integration Tests'
|
||||
failTaskOnFailedTests: true
|
||||
displayName: Publish Test Results
|
||||
|
||||
|
||||
- job: Integration_LinuxCore_Postgres15
|
||||
displayName: Integration Native LinuxCore with Postgres Database
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
variables:
|
||||
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
||||
Readarr__Postgres__Host: 'localhost'
|
||||
Readarr__Postgres__Port: '5432'
|
||||
Readarr__Postgres__User: 'readarr'
|
||||
Readarr__Postgres__Password: 'readarr'
|
||||
|
||||
pool:
|
||||
vmImage: ${{ variables.linuxImage }}
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Test Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'linux-x64-tests'
|
||||
targetPath: $(testsFolder)
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Build Artifact
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: Packages
|
||||
itemPattern: '**/$(pattern)'
|
||||
targetPath: $(Build.ArtifactStagingDirectory)
|
||||
- task: ExtractFiles@1
|
||||
inputs:
|
||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||
displayName: Extract Package
|
||||
- bash: |
|
||||
mkdir -p ./bin/
|
||||
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Readarr/. ./bin/
|
||||
displayName: Move Package Contents
|
||||
- bash: |
|
||||
docker run -d --name=postgres15 \
|
||||
-e POSTGRES_PASSWORD=readarr \
|
||||
-e POSTGRES_USER=readarr \
|
||||
-p 5432:5432/tcp \
|
||||
-v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
|
||||
postgres:15
|
||||
displayName: Start postgres
|
||||
- bash: |
|
||||
chmod a+x ${TESTSFOLDER}/test.sh
|
||||
${TESTSFOLDER}/test.sh Linux Integration Test
|
||||
displayName: Run Integration Tests
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
testResultsFormat: 'NUnit'
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: 'Integration LinuxCore Postgres15 Database Integration Tests'
|
||||
failTaskOnFailedTests: true
|
||||
displayName: Publish Test Results
|
||||
|
||||
- job: Integration_FreeBSD
|
||||
displayName: Integration Native FreeBSD
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
workspace:
|
||||
clean: all
|
||||
variables:
|
||||
@@ -755,6 +895,8 @@ stages:
|
||||
|
||||
- job: Integration_Docker
|
||||
displayName: Integration Docker
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
strategy:
|
||||
matrix:
|
||||
alpine:
|
||||
@@ -773,7 +915,7 @@ stages:
|
||||
container: $[ variables['containerImage'] ]
|
||||
|
||||
timeoutInMinutes: 15
|
||||
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .NET'
|
||||
@@ -801,7 +943,7 @@ stages:
|
||||
targetPath: $(Build.ArtifactStagingDirectory)
|
||||
- task: ExtractFiles@1
|
||||
inputs:
|
||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||
displayName: Extract Package
|
||||
- bash: |
|
||||
@@ -823,7 +965,7 @@ stages:
|
||||
- stage: Automation
|
||||
displayName: Automation
|
||||
dependsOn: Packages
|
||||
|
||||
|
||||
jobs:
|
||||
- job: Automation
|
||||
strategy:
|
||||
@@ -833,20 +975,23 @@ stages:
|
||||
artifactName: 'linux-x64'
|
||||
imageName: ${{ variables.linuxImage }}
|
||||
pattern: 'Readarr.*.linux-core-x64.tar.gz'
|
||||
failBuild: true
|
||||
Mac:
|
||||
osName: 'Mac'
|
||||
artifactName: 'osx-x64'
|
||||
imageName: ${{ variables.macImage }}
|
||||
pattern: 'Readarr.*.osx-core-x64.tar.gz'
|
||||
failBuild: true
|
||||
Windows:
|
||||
osName: 'Windows'
|
||||
artifactName: 'win-x64'
|
||||
imageName: ${{ variables.windowsImage }}
|
||||
pattern: 'Readarr.*.windows-core-x64.zip'
|
||||
failBuild: true
|
||||
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core'
|
||||
@@ -868,7 +1013,7 @@ stages:
|
||||
targetPath: $(Build.ArtifactStagingDirectory)
|
||||
- task: ExtractFiles@1
|
||||
inputs:
|
||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
|
||||
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
|
||||
displayName: Extract Package
|
||||
- bash: |
|
||||
@@ -888,20 +1033,35 @@ stages:
|
||||
TargetFolder: '$(Build.ArtifactStagingDirectory)/screenshots'
|
||||
- publish: $(Build.ArtifactStagingDirectory)/screenshots
|
||||
artifact: '$(osName)AutomationScreenshots'
|
||||
condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
|
||||
displayName: Publish Screenshot Bundle
|
||||
condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
testResultsFormat: 'NUnit'
|
||||
testResultsFiles: '**/TestResult.xml'
|
||||
testRunTitle: '$(osName) Automation Tests'
|
||||
failTaskOnFailedTests: true
|
||||
failTaskOnFailedTests: $(failBuild)
|
||||
displayName: Publish Test Results
|
||||
|
||||
- stage: Analyze
|
||||
dependsOn: []
|
||||
dependsOn:
|
||||
- Setup
|
||||
displayName: Analyze
|
||||
|
||||
jobs:
|
||||
- job: Prepare
|
||||
pool:
|
||||
vmImage: ${{ variables.linuxImage }}
|
||||
steps:
|
||||
- checkout: none
|
||||
- task: DownloadPipelineArtifact@2
|
||||
inputs:
|
||||
buildType: 'current'
|
||||
artifactName: 'not_backend_update'
|
||||
targetPath: '.'
|
||||
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
|
||||
name: setVar
|
||||
|
||||
- job: Lint_Frontend
|
||||
displayName: Lint Frontend
|
||||
strategy:
|
||||
@@ -927,7 +1087,6 @@ stages:
|
||||
key: 'yarn | "$(osName)" | yarn.lock'
|
||||
restoreKeys: |
|
||||
yarn | "$(osName)"
|
||||
yarn
|
||||
path: $(yarnCacheFolder)
|
||||
displayName: Cache Yarn packages
|
||||
- bash: ./build.sh --lint
|
||||
@@ -956,11 +1115,16 @@ stages:
|
||||
cliProjectVersion: '$(readarrVersion)'
|
||||
cliSources: './frontend'
|
||||
- task: SonarCloudAnalyze@1
|
||||
|
||||
|
||||
- job: Api_Docs
|
||||
displayName: API Docs
|
||||
dependsOn: Prepare
|
||||
condition: |
|
||||
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop'))
|
||||
and
|
||||
(
|
||||
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')),
|
||||
and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
)
|
||||
|
||||
pool:
|
||||
vmImage: ${{ variables.windowsImage }}
|
||||
@@ -973,7 +1137,7 @@ stages:
|
||||
- checkout: self
|
||||
submodules: true
|
||||
persistCredentials: true
|
||||
fetchDepth: 1
|
||||
fetchDepth: 1
|
||||
- bash: ./docs.sh Windows
|
||||
displayName: Create openapi.json
|
||||
- bash: |
|
||||
@@ -981,10 +1145,9 @@ stages:
|
||||
git config --global user.name "Servarr"
|
||||
git checkout -b api-docs
|
||||
git add .
|
||||
git status
|
||||
if git status | grep modified
|
||||
if git status | grep -q modified
|
||||
then
|
||||
git commit -am 'Automated API Docs update [skip ci]'
|
||||
git commit -am 'Automated API Docs update'
|
||||
git push -f --set-upstream origin api-docs
|
||||
curl -X POST -H "Authorization: token ${GITHUBTOKEN}" -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/readarr/readarr/pulls -d '{"head":"api-docs","base":"develop","title":"Update API docs"}'
|
||||
else
|
||||
@@ -1008,33 +1171,25 @@ stages:
|
||||
|
||||
- job: Analyze_Backend
|
||||
displayName: Backend
|
||||
dependsOn: Prepare
|
||||
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
|
||||
|
||||
variables:
|
||||
disable.coverage.autogenerate: 'true'
|
||||
EnableAnalyzers: 'false'
|
||||
|
||||
pool:
|
||||
vmImage: ${{ variables.linuxImage }}
|
||||
vmImage: ${{ variables.windowsImage }}
|
||||
|
||||
steps:
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core 2.1'
|
||||
inputs:
|
||||
version: 2.1.815
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core 3.1'
|
||||
inputs:
|
||||
version: 3.1.413
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .net core 5.0'
|
||||
displayName: 'Install .net core'
|
||||
inputs:
|
||||
version: $(dotnetVersion)
|
||||
- checkout: self # Need history for Sonar analysis
|
||||
submodules: true
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: 'nuget | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Directory.Packages.props'
|
||||
path: $(nugetCacheFolder)
|
||||
displayName: Cache NuGet packages
|
||||
|
||||
- powershell: Set-Service SCardSvr -StartupType Manual
|
||||
displayName: Enable Windows Test Service
|
||||
- task: SonarCloudPrepare@1
|
||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||
inputs:
|
||||
@@ -1045,16 +1200,14 @@ stages:
|
||||
projectName: 'Readarr'
|
||||
projectVersion: '$(readarrVersion)'
|
||||
extraProperties: |
|
||||
sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,./src/Libraries/**
|
||||
sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,**/ExternalModules/**,./src/Libraries/**
|
||||
sonar.coverage.exclusions=**/Readarr.Api.V1/**/*
|
||||
sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml
|
||||
sonar.cs.nunit.reportsPaths=$(Build.SourcesDirectory)/TestResult.xml
|
||||
- bash: |
|
||||
./build.sh --backend -f net6.0 -r linux-x64
|
||||
TEST_DIR=_tests/net6.0/linux-x64/publish/ ./test.sh Linux Unit Coverage
|
||||
./build.sh --backend -f net6.0 -r win-x64
|
||||
TEST_DIR=_tests/net6.0/win-x64/publish/ ./test.sh Windows Unit Coverage
|
||||
displayName: Coverage Unit Tests
|
||||
env:
|
||||
NUGET_PACKAGES: $(nugetCacheFolder)
|
||||
- task: SonarCloudAnalyze@1
|
||||
condition: eq(variables['System.PullRequest.IsFork'], 'False')
|
||||
displayName: Publish SonarCloud Results
|
||||
@@ -1077,7 +1230,6 @@ stages:
|
||||
- Unit_Test
|
||||
- Integration
|
||||
- Automation
|
||||
- Build_Backend_Other
|
||||
condition: eq(variables['system.pullrequest.isfork'], false)
|
||||
displayName: Build Status Report
|
||||
jobs:
|
||||
@@ -1101,3 +1253,4 @@ stages:
|
||||
DISCORDCHANNELID: $(discordChannelId)
|
||||
DISCORDWEBHOOKKEY: $(discordWebhookKey)
|
||||
DISCORDTHREADID: $(discordThreadId)
|
||||
|
||||
|
||||
6
build.sh
6
build.sh
@@ -23,7 +23,7 @@ UpdateVersionNumber()
|
||||
echo "Updating Version Info"
|
||||
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$READARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
|
||||
sed -i'' -e "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BUILD_SOURCEBRANCHNAME}<\/AssemblyConfiguration>/g" src/Directory.Build.props
|
||||
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$READARRVERSION<\/string>/g" macOS/Readarr.app/Contents/Info.plist
|
||||
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$READARRVERSION<\/string>/g" distribution/osx/Readarr.app/Contents/Info.plist
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ PackageMacOSApp()
|
||||
|
||||
rm -rf $folder
|
||||
mkdir -p $folder
|
||||
cp -r macOS/Readarr.app $folder
|
||||
cp -r distribution/osx/Readarr.app $folder
|
||||
mkdir -p $folder/Readarr.app/Contents/MacOS
|
||||
|
||||
echo "Copying Binaries"
|
||||
@@ -245,7 +245,7 @@ BuildInstaller()
|
||||
local framework="$1"
|
||||
local runtime="$2"
|
||||
|
||||
./_inno/ISCC.exe setup/readarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
|
||||
./_inno/ISCC.exe distribution/windows/setup/readarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
|
||||
}
|
||||
|
||||
InstallInno()
|
||||
|
||||
@@ -44,16 +44,16 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopIcon"; Description: "{cm:CreateDesktopIcon}"
|
||||
Name: "windowsService"; Description: "Install Windows Service (Starts when the computer starts as the LocalService user, you will need to change the user to access network shares)"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
|
||||
Name: "startupShortcut"; Description: "Create shortcut in Startup folder (Starts when you log into Windows)"; GroupDescription: "Start automatically"; Flags: exclusive
|
||||
Name: "windowsService"; Description: "Install Windows Service (Starts when the computer starts as the LocalService user, you will need to change the user to access network shares)"; GroupDescription: "Start automatically"; Flags: exclusive
|
||||
Name: "startupShortcut"; Description: "Create shortcut in Startup folder (Starts when you log into Windows)"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
|
||||
Name: "none"; Description: "Do not start automatically"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
|
||||
|
||||
[Dirs]
|
||||
Name: "{app}"; Permissions: users-modify
|
||||
|
||||
[Files]
|
||||
Source: "..\_artifacts\{#Runtime}\{#Framework}\Readarr\Readarr.exe"; DestDir: "{app}\bin"; Flags: ignoreversion
|
||||
Source: "..\_artifacts\{#Runtime}\{#Framework}\Readarr\*"; Excludes: "Readarr.Update"; DestDir: "{app}\bin"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Readarr\Readarr.exe"; DestDir: "{app}\bin"; Flags: ignoreversion
|
||||
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Readarr\*"; Excludes: "Readarr.Update"; DestDir: "{app}\bin"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||
|
||||
[Icons]
|
||||
@@ -72,12 +72,13 @@ Filename: "{app}\bin\Readarr.exe"; Description: "Open Readarr Web UI"; Flags: po
|
||||
Filename: "{app}\bin\Readarr.exe"; Description: "Start Readarr"; Flags: postinstall skipifsilent nowait; Tasks: startupShortcut none;
|
||||
|
||||
[UninstallRun]
|
||||
Filename: "{app}\bin\Readarr.Console.exe"; Parameters: "/u"; Flags: waituntilterminated skipifdoesntexist
|
||||
Filename: "{app}\bin\readarr.console.exe"; Parameters: "/u"; Flags: waituntilterminated skipifdoesntexist
|
||||
|
||||
[Code]
|
||||
function PrepareToInstall(var NeedsRestart: Boolean): String;
|
||||
var
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Exec(ExpandConstant('{commonappdata}\Readarr\bin\Readarr.Console.exe'), '/u', '', 0, ewWaitUntilTerminated, ResultCode)
|
||||
Exec('net', 'stop readarr', '', 0, ewWaitUntilTerminated, ResultCode)
|
||||
Exec('sc', 'delete readarr', '', 0, ewWaitUntilTerminated, ResultCode)
|
||||
end;
|
||||
@@ -4,14 +4,14 @@ module.exports = {
|
||||
plugins: [
|
||||
// Stage 1
|
||||
'@babel/plugin-proposal-export-default-from',
|
||||
['@babel/plugin-proposal-optional-chaining', { loose }],
|
||||
['@babel/plugin-proposal-nullish-coalescing-operator', { loose }],
|
||||
['@babel/plugin-transform-optional-chaining', { loose }],
|
||||
['@babel/plugin-transform-nullish-coalescing-operator', { loose }],
|
||||
|
||||
// Stage 2
|
||||
'@babel/plugin-proposal-export-namespace-from',
|
||||
'@babel/plugin-transform-export-namespace-from',
|
||||
|
||||
// Stage 3
|
||||
['@babel/plugin-proposal-class-properties', { loose }],
|
||||
['@babel/plugin-transform-class-properties', { loose }],
|
||||
'@babel/plugin-syntax-dynamic-import'
|
||||
],
|
||||
env: {
|
||||
|
||||
@@ -108,7 +108,7 @@ class RemoveQueueItemModal extends Component {
|
||||
type={inputTypes.CHECK}
|
||||
name="remove"
|
||||
value={remove}
|
||||
helpTextWarning={translate('RemoveFromDownloadClientHelpTextWarning')}
|
||||
helpTextWarning={translate('RemoveHelpTextWarning')}
|
||||
isDisabled={!canIgnore}
|
||||
onChange={this.onRemoveChange}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.version {
|
||||
margin: 0 3px;
|
||||
font-weight: bold;
|
||||
font-family: var(--defaultFontFamily);
|
||||
}
|
||||
|
||||
.maintenance {
|
||||
|
||||
@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Button from 'Components/Link/Button';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
|
||||
import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
@@ -64,12 +65,12 @@ function AppUpdatedModalContent(props) {
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Readarr Updated
|
||||
{translate('AppUpdated', { appName: 'Readarr' })}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<div>
|
||||
Version <span className={styles.version}>{version}</span> of Readarr has been installed, in order to get the latest changes you'll need to reload Readarr.
|
||||
<InlineMarkdown data={translate('AppUpdatedVersion', { appName: 'Readarr', version })} blockClassName={styles.version} />
|
||||
</div>
|
||||
|
||||
{
|
||||
@@ -77,16 +78,14 @@ function AppUpdatedModalContent(props) {
|
||||
<div>
|
||||
{
|
||||
!update.changes &&
|
||||
<div className={styles.maintenance}>
|
||||
{translate('MaintenanceRelease')}
|
||||
</div>
|
||||
<div className={styles.maintenance}>{translate('MaintenanceRelease')}</div>
|
||||
}
|
||||
|
||||
{
|
||||
!!update.changes &&
|
||||
<div>
|
||||
<div className={styles.changes}>
|
||||
What's new?
|
||||
{translate('WhatsNew')}
|
||||
</div>
|
||||
|
||||
<UpdateChanges
|
||||
@@ -113,14 +112,14 @@ function AppUpdatedModalContent(props) {
|
||||
<Button
|
||||
onPress={onSeeChangesPress}
|
||||
>
|
||||
Recent Changes
|
||||
{translate('RecentChanges')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
kind={kinds.PRIMARY}
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Reload
|
||||
{translate('Reload')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
||||
@@ -7,6 +7,7 @@ import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import { kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './ConnectionLostModal.css';
|
||||
|
||||
function ConnectionLostModal(props) {
|
||||
@@ -22,16 +23,16 @@ function ConnectionLostModal(props) {
|
||||
>
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
Connection Lost
|
||||
{translate('ConnectionLost')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<div>
|
||||
Readarr has lost its connection to the backend and will need to be reloaded to restore functionality.
|
||||
{translate('ConnectionLostToBackend', { appName: 'Readarr' })}
|
||||
</div>
|
||||
|
||||
<div className={styles.automatic}>
|
||||
Readarr will try to connect automatically, or you can click reload below.
|
||||
{translate('ConnectionLostReconnect', { appName: 'Readarr' })}
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
@@ -39,7 +40,7 @@ function ConnectionLostModal(props) {
|
||||
kind={kinds.PRIMARY}
|
||||
onPress={onModalClose}
|
||||
>
|
||||
Reload
|
||||
{translate('Reload')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
|
||||
@@ -16,7 +16,7 @@ import AuthorIndex from './AuthorIndex';
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createAuthorClientSideCollectionItemsSelector('authorIndex'),
|
||||
createCommandExecutingSelector(commandNames.REFRESH_AUTHOR),
|
||||
createCommandExecutingSelector(commandNames.BULK_REFRESH_AUTHOR),
|
||||
createCommandExecutingSelector(commandNames.RSS_SYNC),
|
||||
createCommandExecutingSelector(commandNames.RENAME_AUTHOR),
|
||||
createCommandExecutingSelector(commandNames.RETAG_AUTHOR),
|
||||
@@ -24,17 +24,17 @@ function createMapStateToProps() {
|
||||
(
|
||||
author,
|
||||
isRefreshingAuthor,
|
||||
isRssSyncExecuting,
|
||||
isOrganizingAuthor,
|
||||
isRetaggingAuthor,
|
||||
isRssSyncExecuting,
|
||||
dimensionsState
|
||||
) => {
|
||||
return {
|
||||
...author,
|
||||
isRefreshingAuthor,
|
||||
isRssSyncExecuting,
|
||||
isOrganizingAuthor,
|
||||
isRetaggingAuthor,
|
||||
isRssSyncExecuting,
|
||||
isSmallScreen: dimensionsState.isSmallScreen
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ import BookIndex from './BookIndex';
|
||||
function createMapStateToProps() {
|
||||
return createSelector(
|
||||
createBookClientSideCollectionItemsSelector('bookIndex'),
|
||||
createCommandExecutingSelector(commandNames.REFRESH_AUTHOR),
|
||||
createCommandExecutingSelector(commandNames.REFRESH_BOOK),
|
||||
createCommandExecutingSelector(commandNames.BULK_REFRESH_AUTHOR),
|
||||
createCommandExecutingSelector(commandNames.BULK_REFRESH_BOOK),
|
||||
createCommandExecutingSelector(commandNames.RSS_SYNC),
|
||||
createCommandExecutingSelector(commandNames.CUTOFF_UNMET_BOOK_SEARCH),
|
||||
createCommandExecutingSelector(commandNames.MISSING_BOOK_SEARCH),
|
||||
|
||||
@@ -143,7 +143,7 @@ class BookshelfFooter extends Component {
|
||||
|
||||
<div>
|
||||
<div className={styles.label}>
|
||||
{selectedCount} Author(s) Selected
|
||||
{translate('CountAuthorsSelected', { selectedCount })}
|
||||
</div>
|
||||
|
||||
<SpinnerButton
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.version {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $breakpointMedium) {
|
||||
.image {
|
||||
height: 250px;
|
||||
|
||||
@@ -6,6 +6,7 @@ interface CssExports {
|
||||
'image': string;
|
||||
'imageContainer': string;
|
||||
'message': string;
|
||||
'version': string;
|
||||
}
|
||||
export const cssExports: CssExports;
|
||||
export default cssExports;
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import styles from './ErrorBoundaryError.css';
|
||||
|
||||
function ErrorBoundaryError(props) {
|
||||
const {
|
||||
className,
|
||||
messageClassName,
|
||||
detailsClassName,
|
||||
message,
|
||||
error,
|
||||
info
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className={messageClassName}>
|
||||
{message}
|
||||
</div>
|
||||
|
||||
<div className={styles.imageContainer}>
|
||||
<img
|
||||
className={styles.image}
|
||||
src={`${window.Readarr.urlBase}/Content/Images/error.png`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<details className={detailsClassName}>
|
||||
{
|
||||
error &&
|
||||
<div>
|
||||
{error.toString()}
|
||||
</div>
|
||||
}
|
||||
|
||||
<div className={styles.info}>
|
||||
{info.componentStack}
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ErrorBoundaryError.propTypes = {
|
||||
className: PropTypes.string.isRequired,
|
||||
messageClassName: PropTypes.string.isRequired,
|
||||
detailsClassName: PropTypes.string.isRequired,
|
||||
message: PropTypes.string.isRequired,
|
||||
error: PropTypes.object.isRequired,
|
||||
info: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
ErrorBoundaryError.defaultProps = {
|
||||
className: styles.container,
|
||||
messageClassName: styles.message,
|
||||
detailsClassName: styles.details,
|
||||
message: 'There was an error loading this content'
|
||||
};
|
||||
|
||||
export default ErrorBoundaryError;
|
||||
77
frontend/src/Components/Error/ErrorBoundaryError.tsx
Normal file
77
frontend/src/Components/Error/ErrorBoundaryError.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import StackTrace from 'stacktrace-js';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './ErrorBoundaryError.css';
|
||||
|
||||
interface ErrorBoundaryErrorProps {
|
||||
className: string;
|
||||
messageClassName: string;
|
||||
detailsClassName: string;
|
||||
message: string;
|
||||
error: Error;
|
||||
info: {
|
||||
componentStack: string;
|
||||
};
|
||||
}
|
||||
|
||||
function ErrorBoundaryError(props: ErrorBoundaryErrorProps) {
|
||||
const {
|
||||
className = styles.container,
|
||||
messageClassName = styles.message,
|
||||
detailsClassName = styles.details,
|
||||
message = translate('ErrorLoadingContent'),
|
||||
error,
|
||||
info,
|
||||
} = props;
|
||||
|
||||
const [detailedError, setDetailedError] = useState<
|
||||
StackTrace.StackFrame[] | null
|
||||
>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
StackTrace.fromError(error).then((de) => {
|
||||
setDetailedError(de);
|
||||
});
|
||||
} else {
|
||||
setDetailedError(null);
|
||||
}
|
||||
}, [error, setDetailedError]);
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className={messageClassName}>{message}</div>
|
||||
|
||||
<div className={styles.imageContainer}>
|
||||
<img
|
||||
className={styles.image}
|
||||
src={`${window.Readarr.urlBase}/Content/Images/error.png`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<details className={detailsClassName}>
|
||||
{error ? <div>{error.message}</div> : null}
|
||||
|
||||
{detailedError ? (
|
||||
detailedError.map((d, index) => {
|
||||
return (
|
||||
<div key={index}>
|
||||
{` at ${d.functionName} (${d.fileName}:${d.lineNumber}:${d.columnNumber})`}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div>{info.componentStack}</div>
|
||||
)}
|
||||
|
||||
{
|
||||
<div className={styles.version}>
|
||||
Version: {window.Readarr.version}
|
||||
</div>
|
||||
}
|
||||
</details>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ErrorBoundaryError;
|
||||
@@ -9,6 +9,10 @@
|
||||
&:hover {
|
||||
background-color: var(--inputHoverBackgroundColor);
|
||||
}
|
||||
|
||||
&.isDisabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.optionCheck {
|
||||
|
||||
@@ -41,7 +41,7 @@ class NumberInput extends Component {
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const { value } = this.props;
|
||||
|
||||
if (value !== prevProps.value && !this.state.isFocused) {
|
||||
if (!isNaN(value) && value !== prevProps.value && !this.state.isFocused) {
|
||||
this.setState({
|
||||
value: value == null ? '' : value.toString()
|
||||
});
|
||||
|
||||
@@ -97,6 +97,7 @@ class SpinnerErrorButton extends Component {
|
||||
|
||||
render() {
|
||||
const {
|
||||
kind,
|
||||
isSpinning,
|
||||
error,
|
||||
children,
|
||||
@@ -112,7 +113,7 @@ class SpinnerErrorButton extends Component {
|
||||
const showIcon = wasSuccessful || hasWarning || hasError;
|
||||
|
||||
let iconName = icons.CHECK;
|
||||
let iconKind = kinds.SUCCESS;
|
||||
let iconKind = kind === kinds.PRIMARY ? kinds.DEFAULT : kinds.SUCCESS;
|
||||
|
||||
if (hasWarning) {
|
||||
iconName = icons.WARNING;
|
||||
@@ -126,6 +127,7 @@ class SpinnerErrorButton extends Component {
|
||||
|
||||
return (
|
||||
<SpinnerButton
|
||||
kind={kind}
|
||||
isSpinning={isSpinning}
|
||||
{...otherProps}
|
||||
>
|
||||
@@ -154,6 +156,7 @@ class SpinnerErrorButton extends Component {
|
||||
}
|
||||
|
||||
SpinnerErrorButton.propTypes = {
|
||||
kind: PropTypes.oneOf(kinds.all),
|
||||
isSpinning: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
children: PropTypes.node.isRequired
|
||||
|
||||
@@ -10,7 +10,8 @@ class InlineMarkdown extends Component {
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
data
|
||||
data,
|
||||
blockClassName
|
||||
} = this.props;
|
||||
|
||||
// For now only replace links or code blocks (not both)
|
||||
@@ -47,7 +48,7 @@ class InlineMarkdown extends Component {
|
||||
markdownBlocks.push(data.substr(endIndex, match.index - endIndex));
|
||||
}
|
||||
|
||||
markdownBlocks.push(<code key={`code-${match.index}`}>{match[0].substring(1, match[0].length - 1)}</code>);
|
||||
markdownBlocks.push(<code key={`code-${match.index}`} className={blockClassName ?? null}>{match[0].substring(1, match[0].length - 1)}</code>);
|
||||
endIndex = match.index + match[0].length;
|
||||
}
|
||||
|
||||
@@ -66,7 +67,8 @@ class InlineMarkdown extends Component {
|
||||
|
||||
InlineMarkdown.propTypes = {
|
||||
className: PropTypes.string,
|
||||
data: PropTypes.string
|
||||
data: PropTypes.string,
|
||||
blockClassName: PropTypes.string
|
||||
};
|
||||
|
||||
export default InlineMarkdown;
|
||||
|
||||
@@ -6,6 +6,7 @@ export const BOOKSHELF = 'bookshelf';
|
||||
export const KEY_VALUE_LIST = 'keyValueList';
|
||||
export const MONITOR_BOOKS_SELECT = 'monitorBooksSelect';
|
||||
export const MONITOR_NEW_ITEMS_SELECT = 'monitorNewItemsSelect';
|
||||
export const FLOAT = 'float';
|
||||
export const NUMBER = 'number';
|
||||
export const OAUTH = 'oauth';
|
||||
export const PASSWORD = 'password';
|
||||
@@ -34,6 +35,7 @@ export const all = [
|
||||
KEY_VALUE_LIST,
|
||||
MONITOR_BOOKS_SELECT,
|
||||
MONITOR_NEW_ITEMS_SELECT,
|
||||
FLOAT,
|
||||
NUMBER,
|
||||
OAUTH,
|
||||
PASSWORD,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
import Icon from 'Components/Icon';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import { icons, sortDirections } from 'Helpers/Props';
|
||||
import { icons, kinds, sortDirections } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import InteractiveSearchRow from './InteractiveSearchRow';
|
||||
import styles from './InteractiveSearch.css';
|
||||
@@ -112,17 +113,17 @@ function InteractiveSearch(props) {
|
||||
|
||||
{
|
||||
!isFetching && isPopulated && !totalReleasesCount ?
|
||||
<div className={styles.blankpad}>
|
||||
No results found
|
||||
</div> :
|
||||
<Alert kind={kinds.INFO}>
|
||||
{translate('NoResultsFound')}
|
||||
</Alert> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
!!totalReleasesCount && isPopulated && !items.length ?
|
||||
<div className={styles.blankpad}>
|
||||
All results are hidden by the applied filter
|
||||
</div> :
|
||||
<Alert kind={kinds.WARNING}>
|
||||
{translate('AllResultsAreHiddenByTheAppliedFilter')}
|
||||
</Alert> :
|
||||
null
|
||||
}
|
||||
|
||||
@@ -157,7 +158,7 @@ function InteractiveSearch(props) {
|
||||
{
|
||||
totalReleasesCount !== items.length && !!items.length ?
|
||||
<div className={styles.filteredMessage}>
|
||||
Some results are hidden by the applied filter
|
||||
{translate('SomeResultsAreHiddenByTheAppliedFilter')}
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ class CustomFormat extends Component {
|
||||
isOpen={this.state.isDeleteCustomFormatModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteCustomFormat')}
|
||||
message={translate('DeleteCustomFormatMessageText', [name])}
|
||||
message={translate('DeleteCustomFormatMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
isSpinning={isDeleting}
|
||||
onConfirm={this.onConfirmDeleteCustomFormat}
|
||||
|
||||
@@ -115,7 +115,7 @@ class Specification extends Component {
|
||||
isOpen={this.state.isDeleteSpecificationModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteCondition')}
|
||||
message={translate('DeleteConditionMessageText', [name])}
|
||||
message={translate('DeleteConditionMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteSpecification}
|
||||
onCancel={this.onDeleteSpecificationModalClose}
|
||||
|
||||
@@ -113,7 +113,7 @@ class DownloadClient extends Component {
|
||||
isOpen={this.state.isDeleteDownloadClientModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteDownloadClient')}
|
||||
message={translate('DeleteDownloadClientMessageText', [name])}
|
||||
message={translate('DeleteDownloadClientMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteDownloadClient}
|
||||
onCancel={this.onDeleteDownloadClientModalClose}
|
||||
|
||||
@@ -180,7 +180,7 @@ function ManageDownloadClientsEditModalContent(
|
||||
|
||||
<ModalFooter className={styles.modalFooter}>
|
||||
<div className={styles.selected}>
|
||||
{translate('CountDownloadClientsSelected', [selectedCount])}
|
||||
{translate('CountDownloadClientsSelected', { selectedCount })}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -286,9 +286,9 @@ function ManageDownloadClientsModalContent(
|
||||
isOpen={isDeleteModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteSelectedDownloadClients')}
|
||||
message={translate('DeleteSelectedDownloadClientsMessageText', [
|
||||
selectedIds.length,
|
||||
])}
|
||||
message={translate('DeleteSelectedDownloadClientsMessageText', {
|
||||
count: selectedIds.length,
|
||||
})}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={onConfirmDelete}
|
||||
onCancel={onDeleteModalClose}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import Alert from 'Components/Alert';
|
||||
import FieldSet from 'Components/FieldSet';
|
||||
import Icon from 'Components/Icon';
|
||||
import Link from 'Components/Link/Link';
|
||||
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
|
||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import { icons, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import EditRemotePathMappingModalConnector from './EditRemotePathMappingModalConnector';
|
||||
import RemotePathMapping from './RemotePathMapping';
|
||||
@@ -50,6 +52,11 @@ class RemotePathMappings extends Component {
|
||||
errorMessage={translate('UnableToLoadRemotePathMappings')}
|
||||
{...otherProps}
|
||||
>
|
||||
|
||||
<Alert kind={kinds.INFO}>
|
||||
<InlineMarkdown data={translate('RemotePathMappingsInfo', { app: 'Readarr', wikiLink: 'https://wiki.servarr.com/readarr/settings#remote-path-mappings' })} />
|
||||
</Alert>
|
||||
|
||||
<div className={styles.remotePathMappingsHeader}>
|
||||
<div className={styles.host}>
|
||||
{translate('Host')}
|
||||
|
||||
@@ -103,7 +103,6 @@ class GeneralSettings extends Component {
|
||||
isResettingApiKey,
|
||||
isWindows,
|
||||
isWindowsService,
|
||||
isDocker,
|
||||
mode,
|
||||
packageUpdateMechanism,
|
||||
onInputChange,
|
||||
@@ -171,7 +170,6 @@ class GeneralSettings extends Component {
|
||||
settings={settings}
|
||||
isWindows={isWindows}
|
||||
packageUpdateMechanism={packageUpdateMechanism}
|
||||
isDocker={isDocker}
|
||||
onInputChange={onInputChange}
|
||||
/>
|
||||
|
||||
@@ -214,7 +212,6 @@ GeneralSettings.propTypes = {
|
||||
hasSettings: PropTypes.bool.isRequired,
|
||||
isWindows: PropTypes.bool.isRequired,
|
||||
isWindowsService: PropTypes.bool.isRequired,
|
||||
isDocker: PropTypes.bool.isRequired,
|
||||
mode: PropTypes.string.isRequired,
|
||||
packageUpdateMechanism: PropTypes.string.isRequired,
|
||||
onInputChange: PropTypes.func.isRequired,
|
||||
|
||||
@@ -26,7 +26,6 @@ function createMapStateToProps() {
|
||||
isResettingApiKey,
|
||||
isWindows: systemStatus.isWindows,
|
||||
isWindowsService: systemStatus.isWindows && systemStatus.mode === 'service',
|
||||
isDocker: systemStatus.isDocker,
|
||||
mode: systemStatus.mode,
|
||||
packageUpdateMechanism: systemStatus.packageUpdateMechanism,
|
||||
...sectionSettings
|
||||
|
||||
@@ -8,12 +8,17 @@ import { inputTypes, sizes } from 'Helpers/Props';
|
||||
import titleCase from 'Utilities/String/titleCase';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
const branchValues = [
|
||||
'master',
|
||||
'develop',
|
||||
'nightly'
|
||||
];
|
||||
|
||||
function UpdateSettings(props) {
|
||||
const {
|
||||
advancedSettings,
|
||||
settings,
|
||||
isWindows,
|
||||
isDocker,
|
||||
packageUpdateMechanism,
|
||||
onInputChange
|
||||
} = props;
|
||||
@@ -44,32 +49,21 @@ function UpdateSettings(props) {
|
||||
|
||||
updateOptions.push({ key: 'script', value: 'Script' });
|
||||
|
||||
if (isDocker) {
|
||||
return (
|
||||
<FieldSet legend={translate('Updates')}>
|
||||
<div>
|
||||
{translate('UpdatingIsDisabledInsideADockerContainerUpdateTheContainerImageInstead')}
|
||||
</div>
|
||||
</FieldSet>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FieldSet legend={translate('Updates')}>
|
||||
<FormGroup
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>
|
||||
{translate('Branch')}
|
||||
</FormLabel>
|
||||
<FormLabel>{translate('Branch')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
type={inputTypes.AUTO_COMPLETE}
|
||||
name="branch"
|
||||
helpText={usingExternalUpdateMechanism ? translate('UsingExternalUpdateMechanismBranchUsedByExternalUpdateMechanism') : translate('UsingExternalUpdateMechanismBranchToUseToUpdateReadarr')}
|
||||
helpLink="https://wiki.servarr.com/readarr/faq#how-do-I-update-my-readarr"
|
||||
{...branch}
|
||||
values={branchValues}
|
||||
onChange={onInputChange}
|
||||
readOnly={usingExternalUpdateMechanism}
|
||||
/>
|
||||
@@ -83,14 +77,13 @@ function UpdateSettings(props) {
|
||||
isAdvanced={true}
|
||||
size={sizes.MEDIUM}
|
||||
>
|
||||
<FormLabel>
|
||||
{translate('Automatic')}
|
||||
</FormLabel>
|
||||
<FormLabel>{translate('Automatic')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="updateAutomatically"
|
||||
helpText={translate('UpdateAutomaticallyHelpText')}
|
||||
helpTextWarning={updateMechanism.value === 'docker' ? translate('AutomaticUpdatesDisabledDocker', { appName: 'Readarr' }) : undefined}
|
||||
onChange={onInputChange}
|
||||
{...updateAutomatically}
|
||||
/>
|
||||
@@ -100,9 +93,7 @@ function UpdateSettings(props) {
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>
|
||||
{translate('Mechanism')}
|
||||
</FormLabel>
|
||||
<FormLabel>{translate('Mechanism')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
@@ -121,9 +112,7 @@ function UpdateSettings(props) {
|
||||
advancedSettings={advancedSettings}
|
||||
isAdvanced={true}
|
||||
>
|
||||
<FormLabel>
|
||||
{translate('ScriptPath')}
|
||||
</FormLabel>
|
||||
<FormLabel>{translate('ScriptPath')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.TEXT}
|
||||
@@ -144,7 +133,6 @@ UpdateSettings.propTypes = {
|
||||
advancedSettings: PropTypes.bool.isRequired,
|
||||
settings: PropTypes.object.isRequired,
|
||||
isWindows: PropTypes.bool.isRequired,
|
||||
isDocker: PropTypes.bool.isRequired,
|
||||
packageUpdateMechanism: PropTypes.string.isRequired,
|
||||
onInputChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -107,7 +107,7 @@ class ImportList extends Component {
|
||||
isOpen={this.state.isDeleteImportListModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteImportList')}
|
||||
message={translate('DeleteImportListMessageText', [name])}
|
||||
message={translate('DeleteImportListMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteImportList}
|
||||
onCancel={this.onDeleteImportListModalClose}
|
||||
|
||||
@@ -184,7 +184,7 @@ function ManageImportListsEditModalContent(
|
||||
|
||||
<ModalFooter className={styles.modalFooter}>
|
||||
<div className={styles.selected}>
|
||||
{translate('CountImportListsSelected', [selectedCount])}
|
||||
{translate('CountImportListsSelected', { selectedCount })}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -283,9 +283,9 @@ function ManageImportListsModalContent(
|
||||
isOpen={isDeleteModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteSelectedImportLists')}
|
||||
message={translate('DeleteSelectedImportListsMessageText', [
|
||||
selectedIds.length,
|
||||
])}
|
||||
message={translate('DeleteSelectedImportListsMessageText', {
|
||||
count: selectedIds.length,
|
||||
})}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={onConfirmDelete}
|
||||
onCancel={onDeleteModalClose}
|
||||
|
||||
@@ -152,7 +152,7 @@ class Indexer extends Component {
|
||||
isOpen={this.state.isDeleteIndexerModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteIndexer')}
|
||||
message={translate('DeleteIndexerMessageText', [name])}
|
||||
message={translate('DeleteIndexerMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteIndexer}
|
||||
onCancel={this.onDeleteIndexerModalClose}
|
||||
|
||||
@@ -178,7 +178,7 @@ function ManageIndexersEditModalContent(
|
||||
|
||||
<ModalFooter className={styles.modalFooter}>
|
||||
<div className={styles.selected}>
|
||||
{translate('CountIndexersSelected', [selectedCount])}
|
||||
{translate('CountIndexersSelected', { selectedCount })}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -281,9 +281,9 @@ function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {
|
||||
isOpen={isDeleteModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteSelectedIndexers')}
|
||||
message={translate('DeleteSelectedIndexersMessageText', [
|
||||
selectedIds.length,
|
||||
])}
|
||||
message={translate('DeleteSelectedIndexersMessageText', {
|
||||
count: selectedIds.length,
|
||||
})}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={onConfirmDelete}
|
||||
onCancel={onDeleteModalClose}
|
||||
|
||||
@@ -95,7 +95,7 @@ class RootFolder extends Component {
|
||||
isOpen={this.state.isDeleteRootFolderModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteRootFolder')}
|
||||
message={translate('DeleteRootFolderMessageText', [name])}
|
||||
message={translate('DeleteRootFolderMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteRootFolder}
|
||||
onCancel={this.onDeleteRootFolderModalClose}
|
||||
|
||||
@@ -227,7 +227,7 @@ class Notification extends Component {
|
||||
isOpen={this.state.isDeleteNotificationModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteNotification')}
|
||||
message={translate('DeleteNotificationMessageText', [name])}
|
||||
message={translate('DeleteNotificationMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeleteNotification}
|
||||
onCancel={this.onDeleteNotificationModalClose}
|
||||
|
||||
@@ -144,7 +144,7 @@ class MetadataProfile extends Component {
|
||||
isOpen={this.state.isDeleteMetadataProfileModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteMetadataProfile')}
|
||||
message={translate('DeleteMetadataProfileMessageText', [name])}
|
||||
message={translate('DeleteMetadataProfileMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
isSpinning={isDeleting}
|
||||
onConfirm={this.onConfirmDeleteMetadataProfile}
|
||||
|
||||
@@ -162,7 +162,7 @@ class QualityProfile extends Component {
|
||||
isOpen={this.state.isDeleteQualityProfileModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteQualityProfile')}
|
||||
message={translate('DeleteQualityProfileMessageText', [name])}
|
||||
message={translate('DeleteQualityProfileMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
isSpinning={isDeleting}
|
||||
onConfirm={this.onConfirmDeleteQualityProfile}
|
||||
|
||||
@@ -4,6 +4,8 @@ import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHand
|
||||
import createSaveProviderHandler, { createCancelSaveProviderHandler } from 'Store/Actions/Creators/createSaveProviderHandler';
|
||||
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
|
||||
import { createThunk } from 'Store/thunks';
|
||||
import monitorNewItemsOptions from 'Utilities/Author/monitorNewItemsOptions';
|
||||
import monitorOptions from 'Utilities/Author/monitorOptions';
|
||||
|
||||
//
|
||||
// Variables
|
||||
@@ -51,6 +53,10 @@ export default {
|
||||
port: 8080,
|
||||
useSsl: false,
|
||||
outputProfile: 'default',
|
||||
defaultQualityProfileId: 0,
|
||||
defaultMetadataProfileId: 0,
|
||||
defaultMonitorOption: monitorOptions[0].key,
|
||||
defaultNewItemMonitorOption: monitorNewItemsOptions[0].key,
|
||||
defaultTags: []
|
||||
},
|
||||
isSaving: false,
|
||||
|
||||
@@ -138,7 +138,7 @@ class BackupRow extends Component {
|
||||
isOpen={isConfirmDeleteModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
title={translate('DeleteBackup')}
|
||||
message={translate('DeleteBackupMessageText', [name])}
|
||||
message={translate('DeleteBackupMessageText', { name })}
|
||||
confirmLabel={translate('Delete')}
|
||||
onConfirm={this.onConfirmDeletePress}
|
||||
onCancel={this.onConfirmDeleteModalClose}
|
||||
|
||||
@@ -146,7 +146,7 @@ class RestoreBackupModalContent extends Component {
|
||||
|
||||
<ModalBody>
|
||||
{
|
||||
!!id && `Would you like to restore the backup '${name}'?`
|
||||
!!id && translate('WouldYouLikeToRestoreBackup', { name })
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -39,6 +39,14 @@ function getInternalLink(source) {
|
||||
to="/settings/downloadclients"
|
||||
/>
|
||||
);
|
||||
case 'NotificationStatusCheck':
|
||||
return (
|
||||
<IconButton
|
||||
name={icons.SETTINGS}
|
||||
title={translate('Settings')}
|
||||
to="/settings/connect"
|
||||
/>
|
||||
);
|
||||
case 'RootFolderCheck':
|
||||
return (
|
||||
<IconButton
|
||||
|
||||
@@ -25,18 +25,19 @@ export async function fetchTranslations(): Promise<boolean> {
|
||||
|
||||
export default function translate(
|
||||
key: string,
|
||||
args?: (string | number | boolean)[]
|
||||
tokens?: Record<string, string | number | boolean>
|
||||
) {
|
||||
if (!(key in translations)) {
|
||||
console.debug(key);
|
||||
}
|
||||
|
||||
const translation = translations[key] || key;
|
||||
|
||||
if (args) {
|
||||
return translation.replace(/\{(\d+)\}/g, (match, index) => {
|
||||
return String(args[index]) ?? match;
|
||||
if (tokens) {
|
||||
// Fallback to the old behaviour for translations not yet updated to use named tokens
|
||||
Object.values(tokens).forEach((value, index) => {
|
||||
tokens[index] = value;
|
||||
});
|
||||
|
||||
return translation.replace(/\{([a-z0-9]+?)\}/gi, (match, tokenMatch) =>
|
||||
String(tokens[tokenMatch] ?? match)
|
||||
);
|
||||
}
|
||||
|
||||
return translation;
|
||||
|
||||
35
package.json
35
package.json
@@ -30,7 +30,7 @@
|
||||
"@fortawesome/free-regular-svg-icons": "6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "6.4.0",
|
||||
"@fortawesome/react-fontawesome": "0.2.0",
|
||||
"@microsoft/signalr": "6.0.16",
|
||||
"@microsoft/signalr": "6.0.21",
|
||||
"@sentry/browser": "7.51.2",
|
||||
"@sentry/integrations": "7.51.2",
|
||||
"@types/node": "18.16.16",
|
||||
@@ -83,30 +83,27 @@
|
||||
"redux-localstorage": "0.4.1",
|
||||
"redux-thunk": "2.3.0",
|
||||
"reselect": "4.1.8",
|
||||
"stacktrace-js": "2.0.2",
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.22.9",
|
||||
"@babel/eslint-parser": "7.22.9",
|
||||
"@babel/plugin-proposal-class-properties": "7.18.6",
|
||||
"@babel/core": "7.22.11",
|
||||
"@babel/eslint-parser": "7.22.11",
|
||||
"@babel/plugin-proposal-export-default-from": "7.22.5",
|
||||
"@babel/plugin-proposal-export-namespace-from": "7.18.9",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6",
|
||||
"@babel/plugin-proposal-optional-chaining": "7.21.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "7.8.3",
|
||||
"@babel/preset-env": "7.22.9",
|
||||
"@babel/preset-env": "7.22.15",
|
||||
"@babel/preset-react": "7.22.5",
|
||||
"@babel/preset-typescript": "7.22.5",
|
||||
"@babel/preset-typescript": "7.22.11",
|
||||
"@types/lodash": "4.14.197",
|
||||
"@types/redux-actions": "2.6.2",
|
||||
"@typescript-eslint/eslint-plugin": "6.0.0",
|
||||
"@typescript-eslint/parser": "6.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "6.5.0",
|
||||
"@typescript-eslint/parser": "6.5.0",
|
||||
"autoprefixer": "10.4.14",
|
||||
"babel-loader": "9.1.3",
|
||||
"babel-plugin-inline-classnames": "2.0.1",
|
||||
"babel-plugin-transform-react-remove-prop-types": "0.4.24",
|
||||
"core-js": "3.31.1",
|
||||
"css-loader": "6.7.3",
|
||||
"core-js": "3.32.1",
|
||||
"css-loader": "6.8.1",
|
||||
"css-modules-typescript-loader": "4.0.1",
|
||||
"eslint": "8.44.0",
|
||||
"eslint-config-prettier": "8.8.0",
|
||||
@@ -120,9 +117,9 @@
|
||||
"file-loader": "6.2.0",
|
||||
"filemanager-webpack-plugin": "8.0.0",
|
||||
"fork-ts-checker-webpack-plugin": "8.0.0",
|
||||
"html-webpack-plugin": "5.5.1",
|
||||
"html-webpack-plugin": "5.5.3",
|
||||
"loader-utils": "^3.2.1",
|
||||
"mini-css-extract-plugin": "2.7.5",
|
||||
"mini-css-extract-plugin": "2.7.6",
|
||||
"postcss": "8.4.23",
|
||||
"postcss-color-function": "4.1.0",
|
||||
"postcss-loader": "7.3.0",
|
||||
@@ -135,14 +132,14 @@
|
||||
"rimraf": "4.4.1",
|
||||
"run-sequence": "2.2.1",
|
||||
"streamqueue": "1.1.2",
|
||||
"style-loader": "3.3.2",
|
||||
"stylelint": "15.10.1",
|
||||
"style-loader": "3.3.3",
|
||||
"stylelint": "15.10.3",
|
||||
"stylelint-order": "6.0.3",
|
||||
"terser-webpack-plugin": "5.3.9",
|
||||
"ts-loader": "9.4.3",
|
||||
"ts-loader": "9.4.4",
|
||||
"typescript-plugin-css-modules": "5.0.1",
|
||||
"url-loader": "4.1.1",
|
||||
"webpack": "5.88.1",
|
||||
"webpack": "5.88.2",
|
||||
"webpack-cli": "5.1.4",
|
||||
"webpack-livereload-plugin": "3.0.2",
|
||||
"worker-loader": "3.0.8"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<PackageVersion Include="AutoFixture" Version="4.17.0" />
|
||||
<PackageVersion Include="coverlet.collector" Version="3.0.4-preview.27.ge7cb7c3b40" PrivateAssets="all" />
|
||||
<PackageVersion Include="Dapper" Version="2.0.123" />
|
||||
<PackageVersion Include="DryIoc.dll" Version="5.4.0" />
|
||||
<PackageVersion Include="DryIoc.dll" Version="5.4.1" />
|
||||
<PackageVersion Include="DryIoc.Microsoft.DependencyInjection" Version="6.2.0" />
|
||||
<PackageVersion Include="Equ" Version="2.3.0" />
|
||||
<PackageVersion Include="FluentAssertions" Version="5.10.3" />
|
||||
@@ -16,7 +16,7 @@
|
||||
<PackageVersion Include="ImpromptuInterface" Version="7.0.1" />
|
||||
<PackageVersion Include="LazyCache" Version="2.4.0" />
|
||||
<PackageVersion Include="Mailkit" Version="3.6.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.16" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.21" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
|
||||
@@ -32,7 +32,7 @@
|
||||
<PackageVersion Include="NLog.Extensions.Logging" Version="5.2.3" />
|
||||
<PackageVersion Include="NLog" Version="5.1.4" />
|
||||
<PackageVersion Include="NLog.Targets.Syslog" Version="7.0.0" />
|
||||
<PackageVersion Include="Npgsql" Version="6.0.9" />
|
||||
<PackageVersion Include="Npgsql" Version="7.0.4" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.2.1" />
|
||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||
<PackageVersion Include="NunitXml.TestLogger" Version="3.0.117" />
|
||||
@@ -43,7 +43,7 @@
|
||||
<PackageVersion Include="Selenium.WebDriver.ChromeDriver" Version="91.0.4472.10100" />
|
||||
<PackageVersion Include="Sentry" Version="3.31.0" />
|
||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.0.1" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.0.2" />
|
||||
<PackageVersion Include="StyleCop.Analyzers" Version="1.1.118" />
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
|
||||
<PackageVersion Include="System.Buffers" Version="4.5.1" />
|
||||
@@ -57,10 +57,10 @@
|
||||
<PackageVersion Include="System.Resources.Extensions" Version="6.0.0" />
|
||||
<PackageVersion Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
<PackageVersion Include="System.Security.Principal.Windows" Version="5.0.0" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="6.0.0" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="6.0.0" />
|
||||
<PackageVersion Include="System.Text.Json" Version="6.0.7" />
|
||||
<PackageVersion Include="System.Text.Json" Version="6.0.8" />
|
||||
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />
|
||||
<PackageVersion Include="TagLibSharp-Lidarr" Version="2.2.0.19" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -40,6 +40,10 @@ namespace NzbDrone.Automation.Test.PageModel
|
||||
var element = d.FindElement(By.ClassName("followingBalls"));
|
||||
return !element.Displayed;
|
||||
}
|
||||
catch (StaleElementReferenceException)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
catch (NoSuchElementException)
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -838,7 +838,7 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
|
||||
// Note: never returns anything.
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.GetFileInfos(It.IsAny<string>(), It.IsAny<SearchOption>()))
|
||||
.Setup(v => v.GetFileInfos(It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Returns(new List<IFileInfo>());
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
@@ -878,8 +878,8 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
.Returns<string>(v => fileSystem.DirectoryInfo.FromDirectoryName(v).GetDirectories().ToList());
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.GetFileInfos(It.IsAny<string>(), It.IsAny<SearchOption>()))
|
||||
.Returns((string v, SearchOption option) => fileSystem.DirectoryInfo.FromDirectoryName(v).GetFiles("*", option).ToList());
|
||||
.Setup(v => v.GetFileInfos(It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Returns((string v, bool recursive) => fileSystem.DirectoryInfo.FromDirectoryName(v).GetFiles("*", new EnumerationOptions { RecurseSubdirectories = recursive }).ToList());
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.GetFileSize(It.IsAny<string>()))
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NLog;
|
||||
@@ -114,21 +115,21 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_simple_get()
|
||||
public async Task should_execute_simple_get()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
|
||||
var response = Subject.Execute(request);
|
||||
var response = await Subject.ExecuteAsync(request);
|
||||
|
||||
response.Content.Should().NotBeNullOrWhiteSpace();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_https_get()
|
||||
public async Task should_execute_https_get()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
|
||||
var response = Subject.Execute(request);
|
||||
var response = await Subject.ExecuteAsync(request);
|
||||
|
||||
response.Content.Should().NotBeNullOrWhiteSpace();
|
||||
}
|
||||
@@ -140,47 +141,47 @@ namespace NzbDrone.Common.Test.Http
|
||||
Mocker.GetMock<IConfigService>().SetupGet(x => x.CertificateValidation).Returns(validationType);
|
||||
var request = new HttpRequest($"https://expired.badssl.com");
|
||||
|
||||
Assert.Throws<HttpRequestException>(() => Subject.Execute(request));
|
||||
Assert.ThrowsAsync<HttpRequestException>(async () => await Subject.ExecuteAsync(request));
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void bad_ssl_should_pass_if_remote_validation_disabled()
|
||||
public async Task bad_ssl_should_pass_if_remote_validation_disabled()
|
||||
{
|
||||
Mocker.GetMock<IConfigService>().SetupGet(x => x.CertificateValidation).Returns(CertificateValidationType.Disabled);
|
||||
|
||||
var request = new HttpRequest($"https://expired.badssl.com");
|
||||
|
||||
Subject.Execute(request);
|
||||
await Subject.ExecuteAsync(request);
|
||||
ExceptionVerification.ExpectedErrors(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_typed_get()
|
||||
public async Task should_execute_typed_get()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get?test=1");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Url.EndsWith("/get?test=1");
|
||||
response.Resource.Args.Should().Contain("test", "1");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_simple_post()
|
||||
public async Task should_execute_simple_post()
|
||||
{
|
||||
var message = "{ my: 1 }";
|
||||
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/post");
|
||||
request.SetContent(message);
|
||||
|
||||
var response = Subject.Post<HttpBinResource>(request);
|
||||
var response = await Subject.PostAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Data.Should().Be(message);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_post_with_content_type()
|
||||
public async Task should_execute_post_with_content_type()
|
||||
{
|
||||
var message = "{ my: 1 }";
|
||||
|
||||
@@ -188,17 +189,16 @@ namespace NzbDrone.Common.Test.Http
|
||||
request.SetContent(message);
|
||||
request.Headers.ContentType = "application/json";
|
||||
|
||||
var response = Subject.Post<HttpBinResource>(request);
|
||||
var response = await Subject.PostAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Data.Should().Be(message);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_execute_get_using_gzip()
|
||||
public async Task should_execute_get_using_gzip()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/gzip");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers["Accept-Encoding"].ToString().Should().Contain("gzip");
|
||||
|
||||
@@ -208,11 +208,10 @@ namespace NzbDrone.Common.Test.Http
|
||||
|
||||
[Test]
|
||||
[Platform(Exclude = "MacOsX", Reason = "Azure agent update prevents brotli on OSX")]
|
||||
public void should_execute_get_using_brotli()
|
||||
public async Task should_execute_get_using_brotli()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/brotli");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers["Accept-Encoding"].ToString().Should().Contain("br");
|
||||
|
||||
@@ -230,7 +229,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/status/{statusCode}");
|
||||
|
||||
var exception = Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request));
|
||||
var exception = Assert.ThrowsAsync<HttpException>(async () => await Subject.GetAsync<HttpBinResource>(request));
|
||||
|
||||
((int)exception.Response.StatusCode).Should().Be(statusCode);
|
||||
|
||||
@@ -243,7 +242,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/status/{HttpStatusCode.NotFound}");
|
||||
request.SuppressHttpErrorStatusCodes = new[] { HttpStatusCode.NotFound };
|
||||
|
||||
var exception = Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request));
|
||||
Assert.ThrowsAsync<HttpException>(async () => await Subject.GetAsync<HttpBinResource>(request));
|
||||
|
||||
ExceptionVerification.IgnoreWarns();
|
||||
}
|
||||
@@ -253,7 +252,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/status/{HttpStatusCode.NotFound}");
|
||||
|
||||
var exception = Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request));
|
||||
var exception = Assert.ThrowsAsync<HttpException>(async () => await Subject.GetAsync<HttpBinResource>(request));
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
@@ -264,28 +263,28 @@ namespace NzbDrone.Common.Test.Http
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/status/{HttpStatusCode.NotFound}");
|
||||
request.LogHttpError = false;
|
||||
|
||||
var exception = Assert.Throws<HttpException>(() => Subject.Get<HttpBinResource>(request));
|
||||
Assert.ThrowsAsync<HttpException>(async () => await Subject.GetAsync<HttpBinResource>(request));
|
||||
|
||||
ExceptionVerification.ExpectedWarns(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_follow_redirects_when_not_in_production()
|
||||
public async Task should_not_follow_redirects_when_not_in_production()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/redirect/1");
|
||||
|
||||
Subject.Get(request);
|
||||
await Subject.GetAsync(request);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_follow_redirects()
|
||||
public async Task should_follow_redirects()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/redirect/1");
|
||||
request.AllowAutoRedirect = true;
|
||||
|
||||
var response = Subject.Get(request);
|
||||
var response = await Subject.GetAsync(request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
@@ -293,12 +292,12 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_follow_redirects()
|
||||
public async Task should_not_follow_redirects()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/redirect/1");
|
||||
request.AllowAutoRedirect = false;
|
||||
|
||||
var response = Subject.Get(request);
|
||||
var response = await Subject.GetAsync(request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Found);
|
||||
|
||||
@@ -306,14 +305,14 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_follow_redirects_to_https()
|
||||
public async Task should_follow_redirects_to_https()
|
||||
{
|
||||
var request = new HttpRequestBuilder($"https://{_httpBinHost}/redirect-to")
|
||||
.AddQueryParam("url", $"https://readarr.com/")
|
||||
.Build();
|
||||
request.AllowAutoRedirect = true;
|
||||
|
||||
var response = Subject.Get(request);
|
||||
var response = await Subject.GetAsync(request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
response.Content.Should().Contain("Readarr");
|
||||
@@ -327,17 +326,17 @@ namespace NzbDrone.Common.Test.Http
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/redirect/6");
|
||||
request.AllowAutoRedirect = true;
|
||||
|
||||
Assert.Throws<WebException>(() => Subject.Get(request));
|
||||
Assert.ThrowsAsync<WebException>(async () => await Subject.GetAsync(request));
|
||||
|
||||
ExceptionVerification.ExpectedErrors(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_send_user_agent()
|
||||
public async Task should_send_user_agent()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().ContainKey("User-Agent");
|
||||
|
||||
@@ -347,24 +346,24 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[TestCase("Accept", "text/xml, text/rss+xml, application/rss+xml")]
|
||||
public void should_send_headers(string header, string value)
|
||||
public async Task should_send_headers(string header, string value)
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
request.Headers.Add(header, value);
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers[header].ToString().Should().Be(value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_download_file()
|
||||
public async Task should_download_file()
|
||||
{
|
||||
var file = GetTempFilePath();
|
||||
|
||||
var url = "https://readarr.com/img/slider/artistdetails.png";
|
||||
|
||||
Subject.DownloadFile(url, file);
|
||||
await Subject.DownloadFileAsync(url, file);
|
||||
|
||||
var fileInfo = new FileInfo(file);
|
||||
fileInfo.Exists.Should().BeTrue();
|
||||
@@ -372,7 +371,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_download_file_with_redirect()
|
||||
public async Task should_download_file_with_redirect()
|
||||
{
|
||||
var file = GetTempFilePath();
|
||||
|
||||
@@ -380,7 +379,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
.AddQueryParam("url", $"https://readarr.com/img/slider/artistdetails.png")
|
||||
.Build();
|
||||
|
||||
Subject.DownloadFile(request.Url.FullUri, file);
|
||||
await Subject.DownloadFileAsync(request.Url.FullUri, file);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(0);
|
||||
|
||||
@@ -394,7 +393,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
var file = GetTempFilePath();
|
||||
|
||||
Assert.Throws<HttpException>(() => Subject.DownloadFile("https://download.sonarr.tv/wrongpath", file));
|
||||
Assert.ThrowsAsync<HttpException>(async () => await Subject.DownloadFileAsync("https://download.sonarr.tv/wrongpath", file));
|
||||
|
||||
File.Exists(file).Should().BeFalse();
|
||||
|
||||
@@ -402,7 +401,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_write_redirect_content_to_stream()
|
||||
public async Task should_not_write_redirect_content_to_stream()
|
||||
{
|
||||
var file = GetTempFilePath();
|
||||
|
||||
@@ -412,7 +411,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
request.AllowAutoRedirect = false;
|
||||
request.ResponseStream = fileStream;
|
||||
|
||||
var response = Subject.Get(request);
|
||||
var response = await Subject.GetAsync(request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Moved);
|
||||
}
|
||||
@@ -427,12 +426,12 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_send_cookie()
|
||||
public async Task should_send_cookie()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
request.Cookies["my"] = "cookie";
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().ContainKey("Cookie");
|
||||
|
||||
@@ -461,13 +460,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_preserve_cookie_during_session()
|
||||
public async Task should_preserve_cookie_during_session()
|
||||
{
|
||||
GivenOldCookie();
|
||||
|
||||
var request = new HttpRequest($"https://{_httpBinHost2}/get");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().ContainKey("Cookie");
|
||||
|
||||
@@ -477,30 +476,30 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_send_cookie_to_other_host()
|
||||
public async Task should_not_send_cookie_to_other_host()
|
||||
{
|
||||
GivenOldCookie();
|
||||
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().NotContainKey("Cookie");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_store_request_cookie()
|
||||
public async Task should_not_store_request_cookie()
|
||||
{
|
||||
var requestGet = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
requestGet.Cookies.Add("my", "cookie");
|
||||
requestGet.AllowAutoRedirect = false;
|
||||
requestGet.StoreRequestCookie = false;
|
||||
requestGet.StoreResponseCookie = false;
|
||||
var responseGet = Subject.Get<HttpBinResource>(requestGet);
|
||||
var responseGet = await Subject.GetAsync<HttpBinResource>(requestGet);
|
||||
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().BeEmpty();
|
||||
|
||||
@@ -508,18 +507,18 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_store_request_cookie()
|
||||
public async Task should_store_request_cookie()
|
||||
{
|
||||
var requestGet = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
requestGet.Cookies.Add("my", "cookie");
|
||||
requestGet.AllowAutoRedirect = false;
|
||||
requestGet.StoreRequestCookie.Should().BeTrue();
|
||||
requestGet.StoreResponseCookie = false;
|
||||
var responseGet = Subject.Get<HttpBinResource>(requestGet);
|
||||
var responseGet = await Subject.GetAsync<HttpBinResource>(requestGet);
|
||||
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
@@ -527,7 +526,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_request_cookie()
|
||||
public async Task should_delete_request_cookie()
|
||||
{
|
||||
var requestDelete = new HttpRequest($"https://{_httpBinHost}/cookies/delete?my");
|
||||
requestDelete.Cookies.Add("my", "cookie");
|
||||
@@ -536,13 +535,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestDelete.StoreResponseCookie = false;
|
||||
|
||||
// Delete and redirect since that's the only way to check the internal temporary cookie container
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestDelete);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestDelete);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_clear_request_cookie()
|
||||
public async Task should_clear_request_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestSet.Cookies.Add("my", "cookie");
|
||||
@@ -550,7 +549,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestSet.StoreRequestCookie = true;
|
||||
requestSet.StoreResponseCookie = false;
|
||||
|
||||
var responseSet = Subject.Get<HttpCookieResource>(requestSet);
|
||||
var responseSet = await Subject.GetAsync<HttpCookieResource>(requestSet);
|
||||
|
||||
var requestClear = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestClear.Cookies.Add("my", null);
|
||||
@@ -558,24 +557,24 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestClear.StoreRequestCookie = true;
|
||||
requestClear.StoreResponseCookie = false;
|
||||
|
||||
var responseClear = Subject.Get<HttpCookieResource>(requestClear);
|
||||
var responseClear = await Subject.GetAsync<HttpCookieResource>(requestClear);
|
||||
|
||||
responseClear.Resource.Cookies.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_store_response_cookie()
|
||||
public async Task should_not_store_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreRequestCookie = false;
|
||||
requestSet.StoreResponseCookie.Should().BeFalse();
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
var responseSet = await Subject.GetAsync(requestSet);
|
||||
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().BeEmpty();
|
||||
|
||||
@@ -583,18 +582,18 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_store_response_cookie()
|
||||
public async Task should_store_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreRequestCookie = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
var responseSet = await Subject.GetAsync(requestSet);
|
||||
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
@@ -602,13 +601,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_temp_store_response_cookie()
|
||||
public async Task should_temp_store_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.AllowAutoRedirect = true;
|
||||
requestSet.StoreRequestCookie = false;
|
||||
requestSet.StoreResponseCookie.Should().BeFalse();
|
||||
var responseSet = Subject.Get<HttpCookieResource>(requestSet);
|
||||
var responseSet = await Subject.GetAsync<HttpCookieResource>(requestSet);
|
||||
|
||||
// Set and redirect since that's the only way to check the internal temporary cookie container
|
||||
responseSet.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
@@ -617,7 +616,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_overwrite_response_cookie()
|
||||
public async Task should_overwrite_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.Cookies.Add("my", "oldcookie");
|
||||
@@ -625,11 +624,11 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestSet.StoreRequestCookie = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
var responseSet = await Subject.GetAsync(requestSet);
|
||||
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
@@ -637,7 +636,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_overwrite_temp_response_cookie()
|
||||
public async Task should_overwrite_temp_response_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"https://{_httpBinHost}/cookies/set?my=cookie");
|
||||
requestSet.Cookies.Add("my", "oldcookie");
|
||||
@@ -645,13 +644,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestSet.StoreRequestCookie = true;
|
||||
requestSet.StoreResponseCookie = false;
|
||||
|
||||
var responseSet = Subject.Get<HttpCookieResource>(requestSet);
|
||||
var responseSet = await Subject.GetAsync<HttpCookieResource>(requestSet);
|
||||
|
||||
responseSet.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "oldcookie");
|
||||
|
||||
@@ -659,14 +658,14 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_delete_response_cookie()
|
||||
public async Task should_not_delete_response_cookie()
|
||||
{
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestCookies.Cookies.Add("my", "cookie");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
requestCookies.StoreRequestCookie = true;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
@@ -675,13 +674,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestDelete.StoreRequestCookie = false;
|
||||
requestDelete.StoreResponseCookie = false;
|
||||
|
||||
var responseDelete = Subject.Get(requestDelete);
|
||||
var responseDelete = await Subject.GetAsync(requestDelete);
|
||||
|
||||
requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestCookies.StoreRequestCookie = false;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
|
||||
responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
@@ -689,14 +688,14 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_response_cookie()
|
||||
public async Task should_delete_response_cookie()
|
||||
{
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestCookies.Cookies.Add("my", "cookie");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
requestCookies.StoreRequestCookie = true;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
@@ -705,13 +704,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestDelete.StoreRequestCookie = false;
|
||||
requestDelete.StoreResponseCookie = true;
|
||||
|
||||
var responseDelete = Subject.Get(requestDelete);
|
||||
var responseDelete = await Subject.GetAsync(requestDelete);
|
||||
|
||||
requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestCookies.StoreRequestCookie = false;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
|
||||
responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().BeEmpty();
|
||||
|
||||
@@ -719,14 +718,14 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_temp_response_cookie()
|
||||
public async Task should_delete_temp_response_cookie()
|
||||
{
|
||||
var requestCookies = new HttpRequest($"https://{_httpBinHost}/cookies");
|
||||
requestCookies.Cookies.Add("my", "cookie");
|
||||
requestCookies.AllowAutoRedirect = false;
|
||||
requestCookies.StoreRequestCookie = true;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
var responseCookies = await Subject.GetAsync<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().HaveCount(1).And.Contain("my", "cookie");
|
||||
|
||||
@@ -734,7 +733,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestDelete.AllowAutoRedirect = true;
|
||||
requestDelete.StoreRequestCookie = false;
|
||||
requestDelete.StoreResponseCookie = false;
|
||||
var responseDelete = Subject.Get<HttpCookieResource>(requestDelete);
|
||||
var responseDelete = await Subject.GetAsync<HttpCookieResource>(requestDelete);
|
||||
|
||||
responseDelete.Resource.Cookies.Should().BeEmpty();
|
||||
|
||||
@@ -752,13 +751,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/status/429");
|
||||
|
||||
Assert.Throws<TooManyRequestsException>(() => Subject.Get(request));
|
||||
Assert.ThrowsAsync<TooManyRequestsException>(async () => await Subject.GetAsync(request));
|
||||
|
||||
ExceptionVerification.IgnoreWarns();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_call_interceptor()
|
||||
public async Task should_call_interceptor()
|
||||
{
|
||||
Mocker.SetConstant<IEnumerable<IHttpRequestInterceptor>>(new[] { Mocker.GetMock<IHttpRequestInterceptor>().Object });
|
||||
|
||||
@@ -772,7 +771,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
|
||||
Subject.Get(request);
|
||||
await Subject.GetAsync(request);
|
||||
|
||||
Mocker.GetMock<IHttpRequestInterceptor>()
|
||||
.Verify(v => v.PreRequest(It.IsAny<HttpRequest>()), Times.Once());
|
||||
@@ -783,7 +782,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
|
||||
[TestCase("en-US")]
|
||||
[TestCase("es-ES")]
|
||||
public void should_parse_malformed_cloudflare_cookie(string culture)
|
||||
public async Task should_parse_malformed_cloudflare_cookie(string culture)
|
||||
{
|
||||
var origCulture = Thread.CurrentThread.CurrentCulture;
|
||||
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture);
|
||||
@@ -799,11 +798,11 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
var responseSet = await Subject.GetAsync(requestSet);
|
||||
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().ContainKey("Cookie");
|
||||
|
||||
@@ -821,7 +820,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[TestCase("lang_code=en; expires=Wed, 23-Dec-2026 18:09:14 GMT; Max-Age=31536000; path=/; domain=.abc.com")]
|
||||
public void should_reject_malformed_domain_cookie(string malformedCookie)
|
||||
public async Task should_reject_malformed_domain_cookie(string malformedCookie)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -831,11 +830,11 @@ namespace NzbDrone.Common.Test.Http
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreResponseCookie = true;
|
||||
|
||||
var responseSet = Subject.Get(requestSet);
|
||||
var responseSet = await Subject.GetAsync(requestSet);
|
||||
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/get");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
var response = await Subject.GetAsync<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Headers.Should().NotContainKey("Cookie");
|
||||
|
||||
@@ -847,12 +846,12 @@ namespace NzbDrone.Common.Test.Http
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_correctly_use_basic_auth_with_basic_network_credential()
|
||||
public async Task should_correctly_use_basic_auth()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/basic-auth/username/password");
|
||||
request.Credentials = new BasicNetworkCredential("username", "password");
|
||||
|
||||
var response = Subject.Execute(request);
|
||||
var response = await Subject.ExecuteAsync(request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using ICSharpCode.SharpZipLib.Core;
|
||||
using ICSharpCode.SharpZipLib.GZip;
|
||||
@@ -11,7 +12,7 @@ namespace NzbDrone.Common
|
||||
public interface IArchiveService
|
||||
{
|
||||
void Extract(string compressedFile, string destination);
|
||||
void CreateZip(string path, params string[] files);
|
||||
void CreateZip(string path, IEnumerable<string> files);
|
||||
}
|
||||
|
||||
public class ArchiveService : IArchiveService
|
||||
@@ -39,7 +40,7 @@ namespace NzbDrone.Common
|
||||
_logger.Debug("Extraction complete.");
|
||||
}
|
||||
|
||||
public void CreateZip(string path, params string[] files)
|
||||
public void CreateZip(string path, IEnumerable<string> files)
|
||||
{
|
||||
using (var zipFile = ZipFile.Create(path))
|
||||
{
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace NzbDrone.Common.Disk
|
||||
{
|
||||
CheckFolderExists(path);
|
||||
|
||||
var dirFiles = GetFiles(path, SearchOption.AllDirectories).ToList();
|
||||
var dirFiles = GetFiles(path, true).ToList();
|
||||
|
||||
if (!dirFiles.Any())
|
||||
{
|
||||
@@ -156,11 +156,11 @@ namespace NzbDrone.Common.Disk
|
||||
return _fileSystem.Directory.EnumerateFileSystemEntries(path).Empty();
|
||||
}
|
||||
|
||||
public string[] GetDirectories(string path)
|
||||
public IEnumerable<string> GetDirectories(string path)
|
||||
{
|
||||
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
|
||||
|
||||
return _fileSystem.Directory.GetDirectories(path);
|
||||
return _fileSystem.Directory.EnumerateDirectories(path);
|
||||
}
|
||||
|
||||
public string[] GetDirectories(string path, SearchOption searchOption)
|
||||
@@ -170,18 +170,22 @@ namespace NzbDrone.Common.Disk
|
||||
return _fileSystem.Directory.GetDirectories(path, "*", searchOption);
|
||||
}
|
||||
|
||||
public string[] GetFiles(string path, SearchOption searchOption)
|
||||
public IEnumerable<string> GetFiles(string path, bool recursive)
|
||||
{
|
||||
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
|
||||
|
||||
return _fileSystem.Directory.GetFiles(path, "*.*", searchOption);
|
||||
return _fileSystem.Directory.EnumerateFiles(path, "*", new EnumerationOptions
|
||||
{
|
||||
RecurseSubdirectories = recursive,
|
||||
IgnoreInaccessible = true
|
||||
});
|
||||
}
|
||||
|
||||
public long GetFolderSize(string path)
|
||||
{
|
||||
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
|
||||
|
||||
return GetFiles(path, SearchOption.AllDirectories).Sum(e => _fileSystem.FileInfo.FromFileName(e).Length);
|
||||
return GetFiles(path, true).Sum(e => _fileSystem.FileInfo.FromFileName(e).Length);
|
||||
}
|
||||
|
||||
public long GetFileSize(string path)
|
||||
@@ -302,8 +306,9 @@ namespace NzbDrone.Common.Disk
|
||||
{
|
||||
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
|
||||
|
||||
var files = _fileSystem.Directory.GetFiles(path, "*.*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
|
||||
Array.ForEach(files, RemoveReadOnly);
|
||||
var files = GetFiles(path, recursive);
|
||||
|
||||
files.ToList().ForEach(RemoveReadOnly);
|
||||
|
||||
_fileSystem.Directory.Delete(path, recursive);
|
||||
}
|
||||
@@ -404,7 +409,7 @@ namespace NzbDrone.Common.Disk
|
||||
{
|
||||
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
|
||||
|
||||
foreach (var file in GetFiles(path, SearchOption.TopDirectoryOnly))
|
||||
foreach (var file in GetFiles(path, false))
|
||||
{
|
||||
DeleteFile(file);
|
||||
}
|
||||
@@ -504,13 +509,17 @@ namespace NzbDrone.Common.Disk
|
||||
return _fileSystem.DirectoryInfo.FromDirectoryName(path);
|
||||
}
|
||||
|
||||
public List<IFileInfo> GetFileInfos(string path, SearchOption searchOption = SearchOption.TopDirectoryOnly)
|
||||
public List<IFileInfo> GetFileInfos(string path, bool recursive = false)
|
||||
{
|
||||
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
|
||||
|
||||
var di = _fileSystem.DirectoryInfo.FromDirectoryName(path);
|
||||
|
||||
return di.GetFiles("*", searchOption).ToList();
|
||||
return di.EnumerateFiles("*", new EnumerationOptions
|
||||
{
|
||||
RecurseSubdirectories = recursive,
|
||||
IgnoreInaccessible = true
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public IFileInfo GetFileInfo(string path)
|
||||
|
||||
@@ -23,8 +23,8 @@ namespace NzbDrone.Common.Disk
|
||||
bool FileExists(string path, StringComparison stringComparison);
|
||||
bool FolderWritable(string path);
|
||||
bool FolderEmpty(string path);
|
||||
string[] GetDirectories(string path);
|
||||
string[] GetFiles(string path, SearchOption searchOption);
|
||||
IEnumerable<string> GetDirectories(string path);
|
||||
IEnumerable<string> GetFiles(string path, bool recursive);
|
||||
long GetFolderSize(string path);
|
||||
long GetFileSize(string path);
|
||||
void CreateFolder(string path);
|
||||
@@ -54,7 +54,7 @@ namespace NzbDrone.Common.Disk
|
||||
IDirectoryInfo GetDirectoryInfo(string path);
|
||||
List<IDirectoryInfo> GetDirectoryInfos(string path);
|
||||
IFileInfo GetFileInfo(string path);
|
||||
List<IFileInfo> GetFileInfos(string path, SearchOption searchOption = SearchOption.TopDirectoryOnly);
|
||||
List<IFileInfo> GetFileInfos(string path, bool recursive = false);
|
||||
void RemoveEmptySubfolders(string path);
|
||||
void SaveStream(Stream stream, string path);
|
||||
bool IsValidFolderPermissionMask(string mask);
|
||||
|
||||
@@ -78,8 +78,8 @@ namespace NzbDrone.Common.EnvironmentInfo
|
||||
}
|
||||
|
||||
if (IsLinux &&
|
||||
((File.Exists("/proc/1/cgroup") && File.ReadAllText("/proc/1/cgroup").Contains("/docker/")) ||
|
||||
(File.Exists("/proc/1/mountinfo") && File.ReadAllText("/proc/1/mountinfo").Contains("/docker/"))))
|
||||
(File.Exists("/.dockerenv") ||
|
||||
(File.Exists("/proc/1/cgroup") && File.ReadAllText("/proc/1/cgroup").Contains("/docker/"))))
|
||||
{
|
||||
IsDocker = true;
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace NzbDrone.Common.Extensions
|
||||
|
||||
public static bool IsPathValid(this string path, PathValidationType validationType)
|
||||
{
|
||||
if (path.ContainsInvalidPathChars() || string.IsNullOrWhiteSpace(path))
|
||||
if (string.IsNullOrWhiteSpace(path) || path.ContainsInvalidPathChars())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -160,6 +160,11 @@ namespace NzbDrone.Common.Extensions
|
||||
|
||||
public static bool ContainsInvalidPathChars(this string text)
|
||||
{
|
||||
if (text.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new ArgumentNullException("text");
|
||||
}
|
||||
|
||||
return text.IndexOfAny(Path.GetInvalidPathChars()) >= 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NzbDrone.Common.Http.Dispatchers
|
||||
{
|
||||
public interface IHttpDispatcher
|
||||
{
|
||||
HttpResponse GetResponse(HttpRequest request, CookieContainer cookies);
|
||||
Task<HttpResponse> GetResponseAsync(HttpRequest request, CookieContainer cookies);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,13 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
_credentialCache = cacheManager.GetCache<CredentialCache>(typeof(ManagedHttpDispatcher), "credentialcache");
|
||||
}
|
||||
|
||||
public HttpResponse GetResponse(HttpRequest request, CookieContainer cookies)
|
||||
public async Task<HttpResponse> GetResponseAsync(HttpRequest request, CookieContainer cookies)
|
||||
{
|
||||
var requestMessage = new HttpRequestMessage(request.Method, (Uri)request.Url);
|
||||
var requestMessage = new HttpRequestMessage(request.Method, (Uri)request.Url)
|
||||
{
|
||||
Version = HttpVersion.Version20,
|
||||
VersionPolicy = HttpVersionPolicy.RequestVersionOrLower
|
||||
};
|
||||
requestMessage.Headers.UserAgent.ParseAdd(_userAgentBuilder.GetUserAgent(request.UseSimplifiedUserAgent));
|
||||
requestMessage.Headers.ConnectionClose = !request.ConnectionKeepAlive;
|
||||
|
||||
@@ -99,7 +103,7 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
|
||||
var httpClient = GetClient(request.Url);
|
||||
|
||||
using var responseMessage = httpClient.Send(requestMessage, HttpCompletionOption.ResponseHeadersRead, cts.Token);
|
||||
using var responseMessage = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cts.Token);
|
||||
{
|
||||
byte[] data = null;
|
||||
|
||||
@@ -107,7 +111,7 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
{
|
||||
if (request.ResponseStream != null && responseMessage.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
responseMessage.Content.CopyTo(request.ResponseStream, null, cts.Token);
|
||||
await responseMessage.Content.CopyToAsync(request.ResponseStream, null, cts.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -123,7 +127,7 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
|
||||
headers.Add(responseMessage.Content.Headers.ToNameValueCollection());
|
||||
|
||||
return new HttpResponse(request, new HttpHeader(headers), data, responseMessage.StatusCode);
|
||||
return new HttpResponse(request, new HttpHeader(headers), data, responseMessage.StatusCode, responseMessage.Version);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +164,8 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
|
||||
var client = new System.Net.Http.HttpClient(handler)
|
||||
{
|
||||
DefaultRequestVersion = HttpVersion.Version20,
|
||||
DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower,
|
||||
Timeout = Timeout.InfiniteTimeSpan
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
@@ -25,6 +26,16 @@ namespace NzbDrone.Common.Http
|
||||
HttpResponse Post(HttpRequest request);
|
||||
HttpResponse<T> Post<T>(HttpRequest request)
|
||||
where T : new();
|
||||
|
||||
Task<HttpResponse> ExecuteAsync(HttpRequest request);
|
||||
Task DownloadFileAsync(string url, string fileName, string userAgent = null);
|
||||
Task<HttpResponse> GetAsync(HttpRequest request);
|
||||
Task<HttpResponse<T>> GetAsync<T>(HttpRequest request)
|
||||
where T : new();
|
||||
Task<HttpResponse> HeadAsync(HttpRequest request);
|
||||
Task<HttpResponse> PostAsync(HttpRequest request);
|
||||
Task<HttpResponse<T>> PostAsync<T>(HttpRequest request)
|
||||
where T : new();
|
||||
}
|
||||
|
||||
public class HttpClient : IHttpClient
|
||||
@@ -52,11 +63,11 @@ namespace NzbDrone.Common.Http
|
||||
_cookieContainerCache = cacheManager.GetCache<CookieContainer>(typeof(HttpClient));
|
||||
}
|
||||
|
||||
public HttpResponse Execute(HttpRequest request)
|
||||
public virtual async Task<HttpResponse> ExecuteAsync(HttpRequest request)
|
||||
{
|
||||
var cookieContainer = InitializeRequestCookies(request);
|
||||
|
||||
var response = ExecuteRequest(request, cookieContainer);
|
||||
var response = await ExecuteRequestAsync(request, cookieContainer);
|
||||
|
||||
if (request.AllowAutoRedirect && response.HasHttpRedirect)
|
||||
{
|
||||
@@ -82,7 +93,7 @@ namespace NzbDrone.Common.Http
|
||||
request.ContentSummary = null;
|
||||
}
|
||||
|
||||
response = ExecuteRequest(request, cookieContainer);
|
||||
response = await ExecuteRequestAsync(request, cookieContainer);
|
||||
}
|
||||
while (response.HasHttpRedirect);
|
||||
}
|
||||
@@ -112,6 +123,11 @@ namespace NzbDrone.Common.Http
|
||||
return response;
|
||||
}
|
||||
|
||||
public HttpResponse Execute(HttpRequest request)
|
||||
{
|
||||
return ExecuteAsync(request).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private static bool RequestRequiresForceGet(HttpStatusCode statusCode, HttpMethod requestMethod)
|
||||
{
|
||||
return statusCode switch
|
||||
@@ -122,7 +138,7 @@ namespace NzbDrone.Common.Http
|
||||
};
|
||||
}
|
||||
|
||||
private HttpResponse ExecuteRequest(HttpRequest request, CookieContainer cookieContainer)
|
||||
private async Task<HttpResponse> ExecuteRequestAsync(HttpRequest request, CookieContainer cookieContainer)
|
||||
{
|
||||
foreach (var interceptor in _requestInterceptors)
|
||||
{
|
||||
@@ -131,14 +147,14 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
if (request.RateLimit != TimeSpan.Zero)
|
||||
{
|
||||
_rateLimitService.WaitAndPulse(request.Url.Host, request.RateLimitKey, request.RateLimit);
|
||||
await _rateLimitService.WaitAndPulseAsync(request.Url.Host, request.RateLimitKey, request.RateLimit);
|
||||
}
|
||||
|
||||
_logger.Trace(request);
|
||||
|
||||
var stopWatch = Stopwatch.StartNew();
|
||||
|
||||
var response = _httpDispatcher.GetResponse(request, cookieContainer);
|
||||
var response = await _httpDispatcher.GetResponseAsync(request, cookieContainer);
|
||||
|
||||
HandleResponseCookies(response, cookieContainer);
|
||||
|
||||
@@ -246,7 +262,7 @@ namespace NzbDrone.Common.Http
|
||||
}
|
||||
}
|
||||
|
||||
public void DownloadFile(string url, string fileName, string userAgent = null)
|
||||
public async Task DownloadFileAsync(string url, string fileName, string userAgent = null)
|
||||
{
|
||||
var fileNamePart = fileName + ".part";
|
||||
|
||||
@@ -261,12 +277,13 @@ namespace NzbDrone.Common.Http
|
||||
_logger.Debug("Downloading [{0}] to [{1}]", url, fileName);
|
||||
|
||||
var stopWatch = Stopwatch.StartNew();
|
||||
using (var fileStream = new FileStream(fileNamePart, FileMode.Create, FileAccess.ReadWrite))
|
||||
await using (var fileStream = new FileStream(fileNamePart, FileMode.Create, FileAccess.ReadWrite))
|
||||
{
|
||||
var request = new HttpRequest(url);
|
||||
request.AllowAutoRedirect = true;
|
||||
request.ResponseStream = fileStream;
|
||||
var response = Get(request);
|
||||
request.RequestTimeout = TimeSpan.FromSeconds(300);
|
||||
var response = await GetAsync(request);
|
||||
|
||||
if (response.Headers.ContentType != null && response.Headers.ContentType.Contains("text/html"))
|
||||
{
|
||||
@@ -293,38 +310,71 @@ namespace NzbDrone.Common.Http
|
||||
}
|
||||
}
|
||||
|
||||
public HttpResponse Get(HttpRequest request)
|
||||
public void DownloadFile(string url, string fileName, string userAgent = null)
|
||||
{
|
||||
// https://docs.microsoft.com/en-us/archive/msdn-magazine/2015/july/async-programming-brownfield-async-development#the-thread-pool-hack
|
||||
Task.Run(() => DownloadFileAsync(url, fileName, userAgent)).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public Task<HttpResponse> GetAsync(HttpRequest request)
|
||||
{
|
||||
request.Method = HttpMethod.Get;
|
||||
return Execute(request);
|
||||
return ExecuteAsync(request);
|
||||
}
|
||||
|
||||
public HttpResponse Get(HttpRequest request)
|
||||
{
|
||||
return Task.Run(() => GetAsync(request)).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task<HttpResponse<T>> GetAsync<T>(HttpRequest request)
|
||||
where T : new()
|
||||
{
|
||||
var response = await GetAsync(request);
|
||||
CheckResponseContentType(response);
|
||||
return new HttpResponse<T>(response);
|
||||
}
|
||||
|
||||
public HttpResponse<T> Get<T>(HttpRequest request)
|
||||
where T : new()
|
||||
{
|
||||
var response = Get(request);
|
||||
CheckResponseContentType(response);
|
||||
return new HttpResponse<T>(response);
|
||||
return Task.Run(() => GetAsync<T>(request)).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public Task<HttpResponse> HeadAsync(HttpRequest request)
|
||||
{
|
||||
request.Method = HttpMethod.Head;
|
||||
return ExecuteAsync(request);
|
||||
}
|
||||
|
||||
public HttpResponse Head(HttpRequest request)
|
||||
{
|
||||
request.Method = HttpMethod.Head;
|
||||
return Execute(request);
|
||||
return Task.Run(() => HeadAsync(request)).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public Task<HttpResponse> PostAsync(HttpRequest request)
|
||||
{
|
||||
request.Method = HttpMethod.Post;
|
||||
return ExecuteAsync(request);
|
||||
}
|
||||
|
||||
public HttpResponse Post(HttpRequest request)
|
||||
{
|
||||
request.Method = HttpMethod.Post;
|
||||
return Execute(request);
|
||||
return Task.Run(() => PostAsync(request)).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task<HttpResponse<T>> PostAsync<T>(HttpRequest request)
|
||||
where T : new()
|
||||
{
|
||||
var response = await PostAsync(request);
|
||||
CheckResponseContentType(response);
|
||||
return new HttpResponse<T>(response);
|
||||
}
|
||||
|
||||
public HttpResponse<T> Post<T>(HttpRequest request)
|
||||
where T : new()
|
||||
{
|
||||
var response = Post(request);
|
||||
CheckResponseContentType(response);
|
||||
return new HttpResponse<T>(response);
|
||||
return Task.Run(() => PostAsync<T>(request)).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void CheckResponseContentType(HttpResponse response)
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace NzbDrone.Common.Http
|
||||
Method = HttpMethod.Get;
|
||||
Url = new HttpUri(url);
|
||||
Headers = new HttpHeader();
|
||||
ConnectionKeepAlive = true;
|
||||
AllowAutoRedirect = true;
|
||||
StoreRequestCookie = true;
|
||||
LogHttpError = true;
|
||||
|
||||
@@ -9,28 +9,31 @@ namespace NzbDrone.Common.Http
|
||||
{
|
||||
public class HttpResponse
|
||||
{
|
||||
private static readonly Regex RegexSetCookie = new Regex("^(.*?)=(.*?)(?:;|$)", RegexOptions.Compiled);
|
||||
private static readonly Regex RegexSetCookie = new ("^(.*?)=(.*?)(?:;|$)", RegexOptions.Compiled);
|
||||
|
||||
public HttpResponse(HttpRequest request, HttpHeader headers, byte[] binaryData, HttpStatusCode statusCode = HttpStatusCode.OK)
|
||||
public HttpResponse(HttpRequest request, HttpHeader headers, byte[] binaryData, HttpStatusCode statusCode = HttpStatusCode.OK, Version version = null)
|
||||
{
|
||||
Request = request;
|
||||
Headers = headers;
|
||||
ResponseData = binaryData;
|
||||
StatusCode = statusCode;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public HttpResponse(HttpRequest request, HttpHeader headers, string content, HttpStatusCode statusCode = HttpStatusCode.OK)
|
||||
public HttpResponse(HttpRequest request, HttpHeader headers, string content, HttpStatusCode statusCode = HttpStatusCode.OK, Version version = null)
|
||||
{
|
||||
Request = request;
|
||||
Headers = headers;
|
||||
ResponseData = Headers.GetEncodingFromContentType().GetBytes(content);
|
||||
_content = content;
|
||||
StatusCode = statusCode;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public HttpRequest Request { get; private set; }
|
||||
public HttpHeader Headers { get; private set; }
|
||||
public HttpStatusCode StatusCode { get; private set; }
|
||||
public Version Version { get; private set; }
|
||||
public byte[] ResponseData { get; private set; }
|
||||
|
||||
private string _content;
|
||||
@@ -84,7 +87,7 @@ namespace NzbDrone.Common.Http
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var result = string.Format("Res: [{0}] {1}: {2}.{3} ({4} bytes)", Request.Method, Request.Url, (int)StatusCode, StatusCode, ResponseData?.Length ?? 0);
|
||||
var result = $"Res: HTTP/{Version} [{Request.Method}] {Request.Url}: {(int)StatusCode}.{StatusCode} ({ResponseData?.Length ?? 0} bytes)";
|
||||
|
||||
if (HasHttpError && Headers.ContentType.IsNotNullOrWhiteSpace() && !Headers.ContentType.Equals("text/html", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
@@ -99,7 +102,7 @@ namespace NzbDrone.Common.Http
|
||||
where T : new()
|
||||
{
|
||||
public HttpResponse(HttpResponse response)
|
||||
: base(response.Request, response.Headers, response.ResponseData, response.StatusCode)
|
||||
: base(response.Request, response.Headers, response.ResponseData, response.StatusCode, response.Version)
|
||||
{
|
||||
Resource = Json.Deserialize<T>(response.Content);
|
||||
}
|
||||
|
||||
@@ -131,9 +131,9 @@ namespace NzbDrone.Common.Instrumentation
|
||||
|
||||
private static void RegisterAppFile(IAppFolderInfo appFolderInfo)
|
||||
{
|
||||
RegisterAppFile(appFolderInfo, "appFileInfo", "Readarr.txt", 5, LogLevel.Info);
|
||||
RegisterAppFile(appFolderInfo, "appFileDebug", "Readarr.debug.txt", 50, LogLevel.Off);
|
||||
RegisterAppFile(appFolderInfo, "appFileTrace", "Readarr.trace.txt", 50, LogLevel.Off);
|
||||
RegisterAppFile(appFolderInfo, "appFileInfo", "readarr.txt", 5, LogLevel.Info);
|
||||
RegisterAppFile(appFolderInfo, "appFileDebug", "readarr.debug.txt", 50, LogLevel.Off);
|
||||
RegisterAppFile(appFolderInfo, "appFileTrace", "readarr.trace.txt", 50, LogLevel.Off);
|
||||
}
|
||||
|
||||
private static void RegisterAppFile(IAppFolderInfo appFolderInfo, string name, string fileName, int maxArchiveFiles, LogLevel minLogLevel)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -10,6 +11,8 @@ namespace NzbDrone.Common.TPL
|
||||
{
|
||||
void WaitAndPulse(string key, TimeSpan interval);
|
||||
void WaitAndPulse(string key, string subKey, TimeSpan interval);
|
||||
Task WaitAndPulseAsync(string key, TimeSpan interval);
|
||||
Task WaitAndPulseAsync(string key, string subKey, TimeSpan interval);
|
||||
}
|
||||
|
||||
public class RateLimitService : IRateLimitService
|
||||
@@ -28,7 +31,34 @@ namespace NzbDrone.Common.TPL
|
||||
WaitAndPulse(key, null, interval);
|
||||
}
|
||||
|
||||
public async Task WaitAndPulseAsync(string key, TimeSpan interval)
|
||||
{
|
||||
await WaitAndPulseAsync(key, null, interval);
|
||||
}
|
||||
|
||||
public void WaitAndPulse(string key, string subKey, TimeSpan interval)
|
||||
{
|
||||
var delay = GetDelay(key, subKey, interval);
|
||||
|
||||
if (delay.TotalSeconds > 0.0)
|
||||
{
|
||||
_logger.Trace("Rate Limit triggered, delaying '{0}' for {1:0.000} sec", key, delay.TotalSeconds);
|
||||
System.Threading.Thread.Sleep(delay);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task WaitAndPulseAsync(string key, string subKey, TimeSpan interval)
|
||||
{
|
||||
var delay = GetDelay(key, subKey, interval);
|
||||
|
||||
if (delay.TotalSeconds > 0.0)
|
||||
{
|
||||
_logger.Trace("Rate Limit triggered, delaying '{0}' for {1:0.000} sec", key, delay.TotalSeconds);
|
||||
await Task.Delay(delay);
|
||||
}
|
||||
}
|
||||
|
||||
private TimeSpan GetDelay(string key, string subKey, TimeSpan interval)
|
||||
{
|
||||
var waitUntil = DateTime.UtcNow.Add(interval);
|
||||
|
||||
@@ -59,13 +89,7 @@ namespace NzbDrone.Common.TPL
|
||||
|
||||
waitUntil -= interval;
|
||||
|
||||
var delay = waitUntil - DateTime.UtcNow;
|
||||
|
||||
if (delay.TotalSeconds > 0.0)
|
||||
{
|
||||
_logger.Trace("Rate Limit triggered, delaying '{0}' for {1:0.000} sec", key, delay.TotalSeconds);
|
||||
System.Threading.Thread.Sleep(delay);
|
||||
}
|
||||
return waitUntil - DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,10 @@ namespace NzbDrone.Core.Test.DiskSpace
|
||||
.Setup(x => x.All())
|
||||
.Returns(new List<RootFolder>() { _rootDir });
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.FolderExists(_rootDir.Path))
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.GetMounts())
|
||||
.Returns(new List<IMount>());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@@ -58,7 +59,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_download_report_if_book_was_not_already_downloaded()
|
||||
public async Task should_download_report_if_book_was_not_already_downloaded()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -66,12 +67,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteBook));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteBook>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_only_download_book_once()
|
||||
public async Task should_only_download_book_once()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -80,12 +81,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteBook));
|
||||
decisions.Add(new DownloadDecision(remoteBook));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteBook>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_download_if_any_book_was_already_downloaded()
|
||||
public async Task should_not_download_if_any_book_was_already_downloaded()
|
||||
{
|
||||
var remoteBook1 = GetRemoteBook(
|
||||
new List<Book> { GetBook(1) },
|
||||
@@ -99,12 +100,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteBook1));
|
||||
decisions.Add(new DownloadDecision(remoteBook2));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteBook>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_downloaded_reports()
|
||||
public async Task should_return_downloaded_reports()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -112,11 +113,13 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteBook));
|
||||
|
||||
Subject.ProcessDecisions(decisions).Grabbed.Should().HaveCount(1);
|
||||
var result = await Subject.ProcessDecisions(decisions);
|
||||
|
||||
result.Grabbed.Should().HaveCount(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_all_downloaded_reports()
|
||||
public async Task should_return_all_downloaded_reports()
|
||||
{
|
||||
var remoteBook1 = GetRemoteBook(
|
||||
new List<Book> { GetBook(1) },
|
||||
@@ -130,11 +133,13 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteBook1));
|
||||
decisions.Add(new DownloadDecision(remoteBook2));
|
||||
|
||||
Subject.ProcessDecisions(decisions).Grabbed.Should().HaveCount(2);
|
||||
var result = await Subject.ProcessDecisions(decisions);
|
||||
|
||||
result.Grabbed.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_only_return_downloaded_reports()
|
||||
public async Task should_only_return_downloaded_reports()
|
||||
{
|
||||
var remoteBook1 = GetRemoteBook(
|
||||
new List<Book> { GetBook(1) },
|
||||
@@ -153,11 +158,13 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteBook2));
|
||||
decisions.Add(new DownloadDecision(remoteBook3));
|
||||
|
||||
Subject.ProcessDecisions(decisions).Grabbed.Should().HaveCount(2);
|
||||
var result = await Subject.ProcessDecisions(decisions);
|
||||
|
||||
result.Grabbed.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_add_to_downloaded_list_when_download_fails()
|
||||
public async Task should_not_add_to_downloaded_list_when_download_fails()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -166,7 +173,11 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteBook));
|
||||
|
||||
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.IsAny<RemoteBook>())).Throws(new Exception());
|
||||
Subject.ProcessDecisions(decisions).Grabbed.Should().BeEmpty();
|
||||
|
||||
var result = await Subject.ProcessDecisions(decisions);
|
||||
|
||||
result.Grabbed.Should().BeEmpty();
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
@@ -181,7 +192,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_grab_if_pending()
|
||||
public async Task should_not_grab_if_pending()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -189,12 +200,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteBook, new Rejection("Failure!", RejectionType.Temporary)));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteBook>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_add_to_pending_if_book_was_grabbed()
|
||||
public async Task should_not_add_to_pending_if_book_was_grabbed()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -203,12 +214,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteBook));
|
||||
decisions.Add(new DownloadDecision(remoteBook, new Rejection("Failure!", RejectionType.Temporary)));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.AddMany(It.IsAny<List<Tuple<DownloadDecision, PendingReleaseReason>>>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_add_to_pending_even_if_already_added_to_pending()
|
||||
public async Task should_add_to_pending_even_if_already_added_to_pending()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -217,12 +228,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
decisions.Add(new DownloadDecision(remoteBook, new Rejection("Failure!", RejectionType.Temporary)));
|
||||
decisions.Add(new DownloadDecision(remoteBook, new Rejection("Failure!", RejectionType.Temporary)));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.AddMany(It.IsAny<List<Tuple<DownloadDecision, PendingReleaseReason>>>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_add_to_failed_if_already_failed_for_that_protocol()
|
||||
public async Task should_add_to_failed_if_already_failed_for_that_protocol()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -234,12 +245,12 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.IsAny<RemoteBook>()))
|
||||
.Throws(new DownloadClientUnavailableException("Download client failed"));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteBook>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_add_to_failed_if_failed_for_a_different_protocol()
|
||||
public async Task should_not_add_to_failed_if_failed_for_a_different_protocol()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3), DownloadProtocol.Usenet);
|
||||
@@ -252,13 +263,13 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.Is<RemoteBook>(r => r.Release.DownloadProtocol == DownloadProtocol.Usenet)))
|
||||
.Throws(new DownloadClientUnavailableException("Download client failed"));
|
||||
|
||||
Subject.ProcessDecisions(decisions);
|
||||
await Subject.ProcessDecisions(decisions);
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.Is<RemoteBook>(r => r.Release.DownloadProtocol == DownloadProtocol.Usenet)), Times.Once());
|
||||
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.Is<RemoteBook>(r => r.Release.DownloadProtocol == DownloadProtocol.Torrent)), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_add_to_rejected_if_release_unavailable_on_indexer()
|
||||
public async Task should_add_to_rejected_if_release_unavailable_on_indexer()
|
||||
{
|
||||
var books = new List<Book> { GetBook(1) };
|
||||
var remoteBook = GetRemoteBook(books, new QualityModel(Quality.MP3));
|
||||
@@ -270,7 +281,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
.Setup(s => s.DownloadReport(It.IsAny<RemoteBook>()))
|
||||
.Throws(new ReleaseUnavailableException(remoteBook.Release, "That 404 Error is not just a Quirk"));
|
||||
|
||||
var result = Subject.ProcessDecisions(decisions);
|
||||
var result = await Subject.ProcessDecisions(decisions);
|
||||
|
||||
result.Grabbed.Should().BeEmpty();
|
||||
result.Rejected.Should().NotBeEmpty();
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
.Returns(new[] { targetDir });
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetFiles(targetDir, SearchOption.AllDirectories))
|
||||
.Setup(c => c.GetFiles(targetDir, true))
|
||||
.Returns(new[] { Path.Combine(targetDir, "somefile.flac") });
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@@ -69,7 +70,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
protected void GivenFailedDownload()
|
||||
{
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
|
||||
.Setup(s => s.GetAsync(It.IsAny<HttpRequest>()))
|
||||
.Throws(new WebException());
|
||||
}
|
||||
|
||||
@@ -82,7 +83,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
.Returns(new[] { targetDir });
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetFiles(targetDir, SearchOption.AllDirectories))
|
||||
.Setup(c => c.GetFiles(targetDir, true))
|
||||
.Returns(new[] { Path.Combine(targetDir, "somefile.flac") });
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
@@ -147,19 +148,19 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_download_file_if_it_doesnt_exist()
|
||||
public async Task Download_should_download_file_if_it_doesnt_exist()
|
||||
{
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_save_magnet_if_enabled()
|
||||
public async Task Download_should_save_magnet_if_enabled()
|
||||
{
|
||||
GivenMagnetFilePath();
|
||||
Subject.Definition.Settings.As<TorrentBlackholeSettings>().SaveMagnetFiles = true;
|
||||
@@ -167,16 +168,16 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = null;
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Never());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_save_magnet_using_specified_extension()
|
||||
public async Task Download_should_save_magnet_using_specified_extension()
|
||||
{
|
||||
var magnetFileExtension = ".url";
|
||||
GivenMagnetFilePath(magnetFileExtension);
|
||||
@@ -187,12 +188,12 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = null;
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Never());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -202,31 +203,31 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = null;
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Never());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Never());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_prefer_torrent_over_magnet()
|
||||
public async Task Download_should_prefer_torrent_over_magnet()
|
||||
{
|
||||
Subject.Definition.Settings.As<TorrentBlackholeSettings>().SaveMagnetFiles = true;
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_magnetFilePath), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_replace_illegal_characters_in_title()
|
||||
public async Task Download_should_replace_illegal_characters_in_title()
|
||||
{
|
||||
var illegalTitle = "Radiohead - Scotch Mist [2008/FLAC/Lossless]";
|
||||
var expectedFilename = Path.Combine(_blackholeFolder, "Radiohead - Scotch Mist [2008+FLAC+Lossless]" + Path.GetExtension(_filePath));
|
||||
@@ -234,11 +235,11 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.Title = illegalTitle;
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(expectedFilename), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -247,7 +248,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = null;
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -317,11 +318,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_hash()
|
||||
public async Task should_return_null_hash()
|
||||
{
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer()).Should().BeNull();
|
||||
var result = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
result.Should().BeNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@@ -74,7 +75,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
.Returns(new[] { targetDir });
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetFiles(targetDir, SearchOption.AllDirectories))
|
||||
.Setup(c => c.GetFiles(targetDir, true))
|
||||
.Returns(new[] { Path.Combine(targetDir, "somefile.flac") });
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
@@ -119,19 +120,19 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_download_file_if_it_doesnt_exist()
|
||||
public async Task Download_should_download_file_if_it_doesnt_exist()
|
||||
{
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(_filePath), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_replace_illegal_characters_in_title()
|
||||
public async Task Download_should_replace_illegal_characters_in_title()
|
||||
{
|
||||
var illegalTitle = "Radiohead - Scotch Mist [2008/FLAC/Lossless]";
|
||||
var expectedFilename = Path.Combine(_blackholeFolder, "Radiohead - Scotch Mist [2008+FLAC+Lossless]" + Path.GetExtension(_filePath));
|
||||
@@ -139,11 +140,11 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.Blackhole
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.Title = illegalTitle;
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.Get(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.GetAsync(It.Is<HttpRequest>(v => v.Url.FullUri == _downloadUrl)), Times.Once());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.OpenWriteStream(expectedFilename), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -200,26 +201,26 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DelugeTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
|
||||
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = magnetUrl;
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().Be(expectedHash);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NLog;
|
||||
@@ -36,8 +37,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
|
||||
.Returns(() => CreateRemoteBook());
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[0]));
|
||||
.Setup(s => s.GetAsync(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), Array.Empty<byte>())));
|
||||
|
||||
Mocker.GetMock<IRemotePathMappingService>()
|
||||
.Setup(v => v.RemapRemoteToLocal(It.IsAny<string>(), It.IsAny<OsPath>()))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -385,7 +386,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_TvDirectory_should_force_directory()
|
||||
public async Task Download_with_TvDirectory_should_force_directory()
|
||||
{
|
||||
GivenSerialNumber();
|
||||
GivenTvDirectory();
|
||||
@@ -393,7 +394,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -402,7 +403,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_category_should_force_directory()
|
||||
public async Task Download_with_category_should_force_directory()
|
||||
{
|
||||
GivenSerialNumber();
|
||||
GivenMusicCategory();
|
||||
@@ -410,7 +411,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -419,14 +420,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_without_TvDirectory_and_Category_should_use_default()
|
||||
public async Task Download_without_TvDirectory_and_Category_should_use_default()
|
||||
{
|
||||
GivenSerialNumber();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -505,7 +506,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
.Setup(s => s.GetSerialNumber(_settings))
|
||||
.Throws(new ApplicationException("Some unknown exception, HttpException or DownloadClientException"));
|
||||
|
||||
Assert.Throws(Is.InstanceOf<Exception>(), () => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.ThrowsAsync(Is.InstanceOf<Exception>(), async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
|
||||
Mocker.GetMock<IDownloadStationTaskProxy>()
|
||||
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), null, _settings), Times.Never());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -262,7 +263,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_TvDirectory_should_force_directory()
|
||||
public async Task Download_with_TvDirectory_should_force_directory()
|
||||
{
|
||||
GivenSerialNumber();
|
||||
GivenTvDirectory();
|
||||
@@ -270,7 +271,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -279,7 +280,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_category_should_force_directory()
|
||||
public async Task Download_with_category_should_force_directory()
|
||||
{
|
||||
GivenSerialNumber();
|
||||
GivenMusicCategory();
|
||||
@@ -287,7 +288,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -296,14 +297,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_without_TvDirectory_and_Category_should_use_default()
|
||||
public async Task Download_without_TvDirectory_and_Category_should_use_default()
|
||||
{
|
||||
GivenSerialNumber();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -382,7 +383,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
.Setup(s => s.GetSerialNumber(_settings))
|
||||
.Throws(new ApplicationException("Some unknown exception, HttpException or DownloadClientException"));
|
||||
|
||||
Assert.Throws(Is.InstanceOf<Exception>(), () => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.ThrowsAsync(Is.InstanceOf<Exception>(), async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
|
||||
Mocker.GetMock<IDownloadStationTaskProxy>()
|
||||
.Verify(v => v.AddTaskFromUrl(It.IsAny<string>(), null, _settings), Times.Never());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -103,8 +104,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
|
||||
protected void GivenSuccessfulDownload()
|
||||
{
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(s => s.Get(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[1000]));
|
||||
.Setup(s => s.GetAsync(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), new byte[1000])));
|
||||
|
||||
Mocker.GetMock<IHadoukenProxy>()
|
||||
.Setup(s => s.AddTorrentUri(It.IsAny<HadoukenSettings>(), It.IsAny<string>()))
|
||||
@@ -196,13 +197,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
@@ -277,7 +278,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_from_magnet_link_should_return_hash_uppercase()
|
||||
public async Task Download_from_magnet_link_should_return_hash_uppercase()
|
||||
{
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
@@ -286,13 +287,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
|
||||
Mocker.GetMock<IHadoukenProxy>()
|
||||
.Setup(v => v.AddTorrentUri(It.IsAny<HadoukenSettings>(), It.IsAny<string>()));
|
||||
|
||||
var result = Subject.Download(remoteBook, CreateIndexer());
|
||||
var result = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Assert.IsFalse(result.Any(c => char.IsLower(c)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_from_torrent_file_should_return_hash_uppercase()
|
||||
public async Task Download_from_torrent_file_should_return_hash_uppercase()
|
||||
{
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
@@ -300,7 +301,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
|
||||
.Setup(v => v.AddTorrentFile(It.IsAny<HadoukenSettings>(), It.IsAny<byte[]>()))
|
||||
.Returns("hash");
|
||||
|
||||
var result = Subject.Download(remoteBook, CreateIndexer());
|
||||
var result = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Assert.IsFalse(result.Any(c => char.IsLower(c)));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -200,13 +201,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbVortexTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
@@ -218,7 +219,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbVortexTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
Assert.Throws<DownloadClientException>(() => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.ThrowsAsync<DownloadClientException>(async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@@ -339,13 +340,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
@@ -357,7 +358,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
Assert.Throws<DownloadClientRejectedReleaseException>(() => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.ThrowsAsync<DownloadClientRejectedReleaseException>(async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
using NLog;
|
||||
@@ -65,15 +66,15 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
|
||||
|
||||
private void WithFailedDownload()
|
||||
{
|
||||
Mocker.GetMock<IHttpClient>().Setup(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Throws(new WebException());
|
||||
Mocker.GetMock<IHttpClient>().Setup(c => c.DownloadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Throws(new WebException());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_download_file_if_it_doesnt_exist()
|
||||
public async Task should_download_file_if_it_doesnt_exist()
|
||||
{
|
||||
Subject.Download(_remoteBook, _indexer);
|
||||
await Subject.Download(_remoteBook, _indexer);
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(_nzbUrl, _nzbPath, null), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(_nzbUrl, _nzbPath, null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -81,7 +82,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
|
||||
{
|
||||
WithFailedDownload();
|
||||
|
||||
Assert.Throws<WebException>(() => Subject.Download(_remoteBook, _indexer));
|
||||
Assert.ThrowsAsync<WebException>(async () => await Subject.Download(_remoteBook, _indexer));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -90,7 +91,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
|
||||
_remoteBook.Release.Title = "Alien Ant Farm - Discography";
|
||||
_remoteBook.ParsedBookInfo.Discography = true;
|
||||
|
||||
Assert.Throws<NotSupportedException>(() => Subject.Download(_remoteBook, _indexer));
|
||||
Assert.ThrowsAsync<NotSupportedException>(async () => await Subject.Download(_remoteBook, _indexer));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -100,15 +101,15 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_replace_illegal_characters_in_title()
|
||||
public async Task should_replace_illegal_characters_in_title()
|
||||
{
|
||||
var illegalTitle = "Saturday Night Live - S38E08 - Jeremy Renner/Maroon 5 [SDTV]";
|
||||
var expectedFilename = Path.Combine(_pneumaticFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV].nzb");
|
||||
_remoteBook.Release.Title = illegalTitle;
|
||||
|
||||
Subject.Download(_remoteBook, _indexer);
|
||||
await Subject.Download(_remoteBook, _indexer);
|
||||
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFile(It.IsAny<string>(), expectedFilename, null), Times.Once());
|
||||
Mocker.GetMock<IHttpClient>().Verify(c => c.DownloadFileAsync(It.IsAny<string>(), expectedFilename, null), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -449,26 +450,26 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
|
||||
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = magnetUrl;
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().Be(expectedHash);
|
||||
}
|
||||
@@ -483,7 +484,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR";
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -496,28 +497,28 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = "magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp://abc";
|
||||
|
||||
Assert.DoesNotThrow(() => Subject.Download(remoteBook, CreateIndexer()));
|
||||
Assert.DoesNotThrowAsync(async () => await Subject.Download(remoteBook, CreateIndexer()));
|
||||
|
||||
Mocker.GetMock<IQBittorrentProxy>()
|
||||
.Verify(s => s.AddTorrentFromUrl(It.IsAny<string>(), It.IsAny<TorrentSeedConfiguration>(), It.IsAny<QBittorrentSettings>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_set_top_priority()
|
||||
public async Task Download_should_set_top_priority()
|
||||
{
|
||||
GivenHighPriority();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<IQBittorrentProxy>()
|
||||
.Verify(v => v.MoveTorrentToTopInQueue(It.IsAny<string>(), It.IsAny<QBittorrentSettings>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_not_fail_if_top_priority_not_available()
|
||||
public async Task Download_should_not_fail_if_top_priority_not_available()
|
||||
{
|
||||
GivenHighPriority();
|
||||
GivenSuccessfulDownload();
|
||||
@@ -528,7 +529,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -555,27 +556,27 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_handle_http_redirect_to_magnet()
|
||||
public async Task Download_should_handle_http_redirect_to_magnet()
|
||||
{
|
||||
GivenRedirectToMagnet();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_handle_http_redirect_to_torrent()
|
||||
public async Task Download_should_handle_http_redirect_to_torrent()
|
||||
{
|
||||
GivenRedirectToTorrent();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
@@ -633,7 +634,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
.Returns(new QBittorrentTorrentProperties
|
||||
{
|
||||
Hash = "HASH",
|
||||
SeedingTime = seedingTime
|
||||
SeedingTime = seedingTime * 60
|
||||
});
|
||||
|
||||
return torrent;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -121,13 +122,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.RTorrentTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@@ -300,27 +301,27 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
|
||||
}
|
||||
|
||||
[TestCase("[ TOWN ]-[ http://www.town.ag ]-[ ANIME ]-[Usenet Provider >> http://www.ssl- <<] - [Commie] Aldnoah Zero 18 [234C8FC7]", "[ TOWN ]-[ http-++www.town.ag ]-[ ANIME ]-[Usenet Provider http-++www.ssl- ] - [Commie] Aldnoah Zero 18 [234C8FC7].nzb")]
|
||||
public void Download_should_use_clean_title(string title, string filename)
|
||||
public async Task Download_should_use_clean_title(string title, string filename)
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.Title = title;
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<ISabnzbdProxy>()
|
||||
.Verify(v => v.DownloadNzb(It.IsAny<byte[]>(), filename, It.IsAny<string>(), It.IsAny<int>(), It.IsAny<SabnzbdSettings>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
@@ -353,7 +354,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_use_sabRecentTvPriority_when_recentEpisode_is_true()
|
||||
public async Task Download_should_use_sabRecentTvPriority_when_recentEpisode_is_true()
|
||||
{
|
||||
Mocker.GetMock<ISabnzbdProxy>()
|
||||
.Setup(s => s.DownloadNzb(It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<string>(), (int)SabnzbdPriority.High, It.IsAny<SabnzbdSettings>()))
|
||||
@@ -366,7 +367,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
Subject.Download(remoteBook, CreateIndexer());
|
||||
await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
Mocker.GetMock<ISabnzbdProxy>()
|
||||
.Verify(v => v.DownloadNzb(It.IsAny<byte[]>(), It.IsAny<string>(), It.IsAny<string>(), (int)SabnzbdPriority.High, It.IsAny<SabnzbdSettings>()), Times.Once());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -55,26 +56,26 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_TvDirectory_should_force_directory()
|
||||
public async Task Download_with_TvDirectory_should_force_directory()
|
||||
{
|
||||
GivenTvDirectory();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -83,14 +84,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_category_should_force_directory()
|
||||
public async Task Download_with_category_should_force_directory()
|
||||
{
|
||||
GivenMusicCategory();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -99,7 +100,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_category_should_not_have_double_slashes()
|
||||
public async Task Download_with_category_should_not_have_double_slashes()
|
||||
{
|
||||
GivenMusicCategory();
|
||||
GivenSuccessfulDownload();
|
||||
@@ -108,7 +109,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -117,13 +118,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_without_TvDirectory_and_Category_should_use_default()
|
||||
public async Task Download_without_TvDirectory_and_Category_should_use_default()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -132,14 +133,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
|
||||
}
|
||||
|
||||
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
|
||||
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = magnetUrl;
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().Be(expectedHash);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -228,13 +229,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.UTorrentTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
@@ -252,14 +253,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.UTorrentTests
|
||||
|
||||
// Proxy.GetTorrents does not return original url. So item has to be found via magnet url.
|
||||
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
|
||||
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = magnetUrl;
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().Be(expectedHash);
|
||||
}
|
||||
@@ -350,27 +351,27 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.UTorrentTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_handle_http_redirect_to_magnet()
|
||||
public async Task Download_should_handle_http_redirect_to_magnet()
|
||||
{
|
||||
GivenRedirectToMagnet();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_handle_http_redirect_to_torrent()
|
||||
public async Task Download_should_handle_http_redirect_to_torrent()
|
||||
{
|
||||
GivenRedirectToTorrent();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -63,26 +64,26 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_should_return_unique_id()
|
||||
public async Task Download_should_return_unique_id()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_TvDirectory_should_force_directory()
|
||||
public async Task Download_with_TvDirectory_should_force_directory()
|
||||
{
|
||||
GivenTvDirectory();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -91,14 +92,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_category_should_force_directory()
|
||||
public async Task Download_with_category_should_force_directory()
|
||||
{
|
||||
GivenMusicCategory();
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -107,7 +108,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_with_category_should_not_have_double_slashes()
|
||||
public async Task Download_with_category_should_not_have_double_slashes()
|
||||
{
|
||||
GivenMusicCategory();
|
||||
GivenSuccessfulDownload();
|
||||
@@ -116,7 +117,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -125,13 +126,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_without_TvDirectory_and_Category_should_use_default()
|
||||
public async Task Download_without_TvDirectory_and_Category_should_use_default()
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().NotBeNullOrEmpty();
|
||||
|
||||
@@ -140,14 +141,14 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
|
||||
}
|
||||
|
||||
[TestCase("magnet:?xt=urn:btih:ZPBPA2P6ROZPKRHK44D5OW6NHXU5Z6KR&tr=udp", "CBC2F069FE8BB2F544EAE707D75BCD3DE9DCF951")]
|
||||
public void Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
public async Task Download_should_get_hash_from_magnet_url(string magnetUrl, string expectedHash)
|
||||
{
|
||||
GivenSuccessfulDownload();
|
||||
|
||||
var remoteBook = CreateRemoteBook();
|
||||
remoteBook.Release.DownloadUrl = magnetUrl;
|
||||
|
||||
var id = Subject.Download(remoteBook, CreateIndexer());
|
||||
var id = await Subject.Download(remoteBook, CreateIndexer());
|
||||
|
||||
id.Should().Be(expectedHash);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -77,23 +78,23 @@ namespace NzbDrone.Core.Test.Download
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_report_should_publish_on_grab_event()
|
||||
public async Task Download_report_should_publish_on_grab_event()
|
||||
{
|
||||
var mock = WithUsenetClient();
|
||||
mock.Setup(s => s.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()));
|
||||
|
||||
Subject.DownloadReport(_parseResult);
|
||||
await Subject.DownloadReport(_parseResult);
|
||||
|
||||
VerifyEventPublished<BookGrabbedEvent>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Download_report_should_grab_using_client()
|
||||
public async Task Download_report_should_grab_using_client()
|
||||
{
|
||||
var mock = WithUsenetClient();
|
||||
mock.Setup(s => s.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()));
|
||||
|
||||
Subject.DownloadReport(_parseResult);
|
||||
await Subject.DownloadReport(_parseResult);
|
||||
|
||||
mock.Verify(s => s.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()), Times.Once());
|
||||
}
|
||||
@@ -105,7 +106,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
mock.Setup(s => s.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()))
|
||||
.Throws(new WebException());
|
||||
|
||||
Assert.Throws<WebException>(() => Subject.DownloadReport(_parseResult));
|
||||
Assert.ThrowsAsync<WebException>(async () => await Subject.DownloadReport(_parseResult));
|
||||
|
||||
VerifyEventNotPublished<BookGrabbedEvent>();
|
||||
}
|
||||
@@ -120,7 +121,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
throw new ReleaseDownloadException(v.Release, "Error", new WebException());
|
||||
});
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.DownloadReport(_parseResult));
|
||||
Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.DownloadReport(_parseResult));
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Once());
|
||||
@@ -140,7 +141,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
throw new ReleaseDownloadException(v.Release, "Error", new TooManyRequestsException(request, response));
|
||||
});
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.DownloadReport(_parseResult));
|
||||
Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.DownloadReport(_parseResult));
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.FromMinutes(5.0)), Times.Once());
|
||||
@@ -160,7 +161,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
throw new ReleaseDownloadException(v.Release, "Error", new TooManyRequestsException(request, response));
|
||||
});
|
||||
|
||||
Assert.Throws<ReleaseDownloadException>(() => Subject.DownloadReport(_parseResult));
|
||||
Assert.ThrowsAsync<ReleaseDownloadException>(async () => await Subject.DownloadReport(_parseResult));
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(),
|
||||
@@ -174,7 +175,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
mock.Setup(s => s.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()))
|
||||
.Throws(new DownloadClientException("Some Error"));
|
||||
|
||||
Assert.Throws<DownloadClientException>(() => Subject.DownloadReport(_parseResult));
|
||||
Assert.ThrowsAsync<DownloadClientException>(async () => await Subject.DownloadReport(_parseResult));
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Never());
|
||||
@@ -190,7 +191,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
throw new ReleaseUnavailableException(v.Release, "Error", new WebException());
|
||||
});
|
||||
|
||||
Assert.Throws<ReleaseUnavailableException>(() => Subject.DownloadReport(_parseResult));
|
||||
Assert.ThrowsAsync<ReleaseUnavailableException>(async () => await Subject.DownloadReport(_parseResult));
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(), It.IsAny<TimeSpan>()), Times.Never());
|
||||
@@ -199,14 +200,14 @@ namespace NzbDrone.Core.Test.Download
|
||||
[Test]
|
||||
public void should_not_attempt_download_if_client_isnt_configured()
|
||||
{
|
||||
Assert.Throws<DownloadClientUnavailableException>(() => Subject.DownloadReport(_parseResult));
|
||||
Assert.ThrowsAsync<DownloadClientUnavailableException>(async () => await Subject.DownloadReport(_parseResult));
|
||||
|
||||
Mocker.GetMock<IDownloadClient>().Verify(c => c.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()), Times.Never());
|
||||
VerifyEventNotPublished<BookGrabbedEvent>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_attempt_download_even_if_client_is_disabled()
|
||||
public async Task should_attempt_download_even_if_client_is_disabled()
|
||||
{
|
||||
var mockUsenet = WithUsenetClient();
|
||||
|
||||
@@ -221,7 +222,7 @@ namespace NzbDrone.Core.Test.Download
|
||||
}
|
||||
});
|
||||
|
||||
Subject.DownloadReport(_parseResult);
|
||||
await Subject.DownloadReport(_parseResult);
|
||||
|
||||
Mocker.GetMock<IDownloadClientStatusService>().Verify(c => c.GetBlockedProviders(), Times.Never());
|
||||
mockUsenet.Verify(c => c.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()), Times.Once());
|
||||
@@ -229,26 +230,26 @@ namespace NzbDrone.Core.Test.Download
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_send_download_to_correct_usenet_client()
|
||||
public async Task should_send_download_to_correct_usenet_client()
|
||||
{
|
||||
var mockTorrent = WithTorrentClient();
|
||||
var mockUsenet = WithUsenetClient();
|
||||
|
||||
Subject.DownloadReport(_parseResult);
|
||||
await Subject.DownloadReport(_parseResult);
|
||||
|
||||
mockTorrent.Verify(c => c.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()), Times.Never());
|
||||
mockUsenet.Verify(c => c.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_send_download_to_correct_torrent_client()
|
||||
public async Task should_send_download_to_correct_torrent_client()
|
||||
{
|
||||
var mockTorrent = WithTorrentClient();
|
||||
var mockUsenet = WithUsenetClient();
|
||||
|
||||
_parseResult.Release.DownloadProtocol = DownloadProtocol.Torrent;
|
||||
|
||||
Subject.DownloadReport(_parseResult);
|
||||
await Subject.DownloadReport(_parseResult);
|
||||
|
||||
mockTorrent.Verify(c => c.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()), Times.Once());
|
||||
mockUsenet.Verify(c => c.Download(It.IsAny<RemoteBook>(), It.IsAny<IIndexer>()), Times.Never());
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Notifications;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class NotificationStatusCheckFixture : CoreTest<NotificationStatusCheck>
|
||||
{
|
||||
private List<INotification> _notifications = new List<INotification>();
|
||||
private List<NotificationStatus> _blockedNotifications = new List<NotificationStatus>();
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Mocker.GetMock<INotificationFactory>()
|
||||
.Setup(v => v.GetAvailableProviders())
|
||||
.Returns(_notifications);
|
||||
|
||||
Mocker.GetMock<INotificationStatusService>()
|
||||
.Setup(v => v.GetBlockedProviders())
|
||||
.Returns(_blockedNotifications);
|
||||
|
||||
Mocker.GetMock<ILocalizationService>()
|
||||
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
|
||||
.Returns("Some Warning Message");
|
||||
}
|
||||
|
||||
private Mock<INotification> GivenNotification(int id, double backoffHours, double failureHours)
|
||||
{
|
||||
var mockNotification = new Mock<INotification>();
|
||||
mockNotification.SetupGet(s => s.Definition).Returns(new NotificationDefinition { Id = id });
|
||||
|
||||
_notifications.Add(mockNotification.Object);
|
||||
|
||||
if (backoffHours != 0.0)
|
||||
{
|
||||
_blockedNotifications.Add(new NotificationStatus
|
||||
{
|
||||
ProviderId = id,
|
||||
InitialFailure = DateTime.UtcNow.AddHours(-failureHours),
|
||||
MostRecentFailure = DateTime.UtcNow.AddHours(-0.1),
|
||||
EscalationLevel = 5,
|
||||
DisabledTill = DateTime.UtcNow.AddHours(backoffHours)
|
||||
});
|
||||
}
|
||||
|
||||
return mockNotification;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_return_error_when_no_notifications()
|
||||
{
|
||||
Subject.Check().ShouldBeOk();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_warning_if_notification_unavailable()
|
||||
{
|
||||
GivenNotification(1, 10.0, 24.0);
|
||||
GivenNotification(2, 0.0, 0.0);
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_error_if_all_notifications_unavailable()
|
||||
{
|
||||
GivenNotification(1, 10.0, 24.0);
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_warning_if_few_notifications_unavailable()
|
||||
{
|
||||
GivenNotification(1, 10.0, 24.0);
|
||||
GivenNotification(2, 10.0, 24.0);
|
||||
GivenNotification(3, 0.0, 0.0);
|
||||
|
||||
Subject.Check().ShouldBeWarning();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,9 @@ using NzbDrone.Core.Books;
|
||||
using NzbDrone.Core.HealthCheck.Checks;
|
||||
using NzbDrone.Core.ImportLists;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
@@ -23,7 +25,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
.Returns("Some Warning Message");
|
||||
}
|
||||
|
||||
private void GivenMissingRootFolder()
|
||||
private void GivenMissingRootFolder(string rootFolderPath)
|
||||
{
|
||||
var author = Builder<Author>.CreateListOfSize(1)
|
||||
.Build()
|
||||
@@ -41,9 +43,9 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
.Setup(s => s.All())
|
||||
.Returns(importList);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.GetParentFolder(author.First().Path))
|
||||
.Returns(@"C:\Books");
|
||||
Mocker.GetMock<IRootFolderService>()
|
||||
.Setup(s => s.GetBestRootFolderPath(It.IsAny<string>()))
|
||||
.Returns(rootFolderPath);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.FolderExists(It.IsAny<string>()))
|
||||
@@ -67,7 +69,25 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
[Test]
|
||||
public void should_return_error_if_book_parent_is_missing()
|
||||
{
|
||||
GivenMissingRootFolder();
|
||||
GivenMissingRootFolder(@"C:\Books".AsOsAgnostic());
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_error_if_series_path_is_for_posix_os()
|
||||
{
|
||||
WindowsOnly();
|
||||
GivenMissingRootFolder("/mnt/books");
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_error_if_series_path_is_for_windows()
|
||||
{
|
||||
PosixOnly();
|
||||
GivenMissingRootFolder(@"C:\Books");
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Housekeeping.Housekeepers;
|
||||
using NzbDrone.Core.Notifications;
|
||||
using NzbDrone.Core.Notifications.Join;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
|
||||
{
|
||||
[TestFixture]
|
||||
public class CleanupOrphanedNotificationStatusFixture : DbTest<CleanupOrphanedNotificationStatus, NotificationStatus>
|
||||
{
|
||||
private NotificationDefinition _notification;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_notification = Builder<NotificationDefinition>.CreateNew()
|
||||
.With(s => s.Settings = new JoinSettings { })
|
||||
.BuildNew();
|
||||
}
|
||||
|
||||
private void GivenNotification()
|
||||
{
|
||||
Db.Insert(_notification);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_orphaned_notificationstatus()
|
||||
{
|
||||
var status = Builder<NotificationStatus>.CreateNew()
|
||||
.With(h => h.ProviderId = _notification.Id)
|
||||
.BuildNew();
|
||||
Db.Insert(status);
|
||||
|
||||
Subject.Clean();
|
||||
AllStoredModels.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_delete_unorphaned_notificationstatus()
|
||||
{
|
||||
GivenNotification();
|
||||
|
||||
var status = Builder<NotificationStatus>.CreateNew()
|
||||
.With(h => h.ProviderId = _notification.Id)
|
||||
.BuildNew();
|
||||
Db.Insert(status);
|
||||
|
||||
Subject.Clean();
|
||||
AllStoredModels.Should().HaveCount(1);
|
||||
AllStoredModels.Should().Contain(h => h.ProviderId == _notification.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Books;
|
||||
@@ -27,11 +28,11 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
|
||||
Mocker.GetMock<ISearchForReleases>()
|
||||
.Setup(s => s.AuthorSearch(_author.Id, false, true, false))
|
||||
.Returns(new List<DownloadDecision>());
|
||||
.Returns(Task.FromResult(new List<DownloadDecision>()));
|
||||
|
||||
Mocker.GetMock<IProcessDownloadDecisions>()
|
||||
.Setup(s => s.ProcessDecisions(It.IsAny<List<DownloadDecision>>()))
|
||||
.Returns(new ProcessedDecisions(new List<DownloadDecision>(), new List<DownloadDecision>(), new List<DownloadDecision>()));
|
||||
.Returns(Task.FromResult(new ProcessedDecisions(new List<DownloadDecision>(), new List<DownloadDecision>(), new List<DownloadDecision>())));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@@ -60,13 +61,13 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
|
||||
_mockIndexer.Setup(v => v.Fetch(It.IsAny<BookSearchCriteria>()))
|
||||
.Callback<BookSearchCriteria>(s => result.Add(s))
|
||||
.Returns(new List<Parser.Model.ReleaseInfo>());
|
||||
.Returns(Task.FromResult<IList<Parser.Model.ReleaseInfo>>(new List<Parser.Model.ReleaseInfo>()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Tags_IndexerTags_AuthorNoTags_IndexerNotIncluded()
|
||||
public async Task Tags_IndexerTags_AuthorNoTags_IndexerNotIncluded()
|
||||
{
|
||||
_mockIndexer.SetupGet(s => s.Definition).Returns(new IndexerDefinition
|
||||
{
|
||||
@@ -76,7 +77,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
|
||||
var allCriteria = WatchForSearchCriteria();
|
||||
|
||||
Subject.BookSearch(_firstBook, false, true, false);
|
||||
await Subject.BookSearch(_firstBook, false, true, false);
|
||||
|
||||
var criteria = allCriteria.OfType<BookSearchCriteria>().ToList();
|
||||
|
||||
@@ -84,7 +85,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Tags_IndexerNoTags_AuthorTags_IndexerIncluded()
|
||||
public async Task Tags_IndexerNoTags_AuthorTags_IndexerIncluded()
|
||||
{
|
||||
_mockIndexer.SetupGet(s => s.Definition).Returns(new IndexerDefinition
|
||||
{
|
||||
@@ -102,7 +103,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
|
||||
var allCriteria = WatchForSearchCriteria();
|
||||
|
||||
Subject.BookSearch(_firstBook, false, true, false);
|
||||
await Subject.BookSearch(_firstBook, false, true, false);
|
||||
|
||||
var criteria = allCriteria.OfType<BookSearchCriteria>().ToList();
|
||||
|
||||
@@ -110,7 +111,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Tags_IndexerAndAuthorTagsMatch_IndexerIncluded()
|
||||
public async Task Tags_IndexerAndAuthorTagsMatch_IndexerIncluded()
|
||||
{
|
||||
_mockIndexer.SetupGet(s => s.Definition).Returns(new IndexerDefinition
|
||||
{
|
||||
@@ -129,7 +130,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
|
||||
var allCriteria = WatchForSearchCriteria();
|
||||
|
||||
Subject.BookSearch(_firstBook, false, true, false);
|
||||
await Subject.BookSearch(_firstBook, false, true, false);
|
||||
|
||||
var criteria = allCriteria.OfType<BookSearchCriteria>().ToList();
|
||||
|
||||
@@ -137,7 +138,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Tags_IndexerAndAuthorTagsMismatch_IndexerNotIncluded()
|
||||
public async Task Tags_IndexerAndAuthorTagsMismatch_IndexerNotIncluded()
|
||||
{
|
||||
_mockIndexer.SetupGet(s => s.Definition).Returns(new IndexerDefinition
|
||||
{
|
||||
@@ -156,7 +157,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
||||
|
||||
var allCriteria = WatchForSearchCriteria();
|
||||
|
||||
Subject.BookSearch(_firstBook, false, true, false);
|
||||
await Subject.BookSearch(_firstBook, false, true, false);
|
||||
|
||||
var criteria = allCriteria.OfType<BookSearchCriteria>().ToList();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -26,15 +27,15 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_FileList()
|
||||
public async Task should_parse_recent_feed_from_FileList()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/FileList/RecentFeed.json");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(2);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -30,24 +31,24 @@ namespace NzbDrone.Core.Test.IndexerTests.GazelleTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_gazelle()
|
||||
public async Task should_parse_recent_feed_from_gazelle()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Gazelle/Gazelle.json");
|
||||
var indexFeed = ReadAllText(@"Files/Indexers/Gazelle/GazelleIndex.json");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get && v.Url.FullUri.Contains("ajax.php?action=browse"))))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader { ContentType = "application/json" }, recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get && v.Url.FullUri.Contains("ajax.php?action=browse"))))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader { ContentType = "application/json" }, recentFeed)));
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Post && v.Url.FullUri.Contains("ajax.php?action=index"))))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), indexFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Post && v.Url.FullUri.Contains("ajax.php?action=index"))))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), indexFeed)));
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Post && v.Url.FullUri.Contains("login.php"))))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), indexFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Post && v.Url.FullUri.Contains("login.php"))))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), indexFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(4);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -84,15 +85,15 @@ namespace NzbDrone.Core.Test.IndexerTests.IPTorrentsTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_IPTorrents()
|
||||
public async Task should_parse_recent_feed_from_IPTorrents()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/IPTorrents/IPTorrents.xml");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -39,15 +40,15 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_newznab_nzb_su()
|
||||
public async Task should_parse_recent_feed_from_newznab_nzb_su()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Newznab/newznab_nzb_su.xml");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(100);
|
||||
|
||||
@@ -83,7 +84,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_record_indexer_failure_if_caps_throw()
|
||||
public async Task should_record_indexer_failure_if_caps_throw()
|
||||
{
|
||||
var request = new HttpRequest("http://my.indexer.com");
|
||||
var response = new HttpResponse(request, new HttpHeader(), new byte[0], (HttpStatusCode)429);
|
||||
@@ -96,7 +97,9 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
_caps.MaxPageSize = 30;
|
||||
_caps.DefaultPageSize = 25;
|
||||
|
||||
Subject.FetchRecent().Should().BeEmpty();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().BeEmpty();
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.FromMinutes(5.0)), Times.Once());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -26,15 +27,15 @@ namespace NzbDrone.Core.Test.IndexerTests.NyaaTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_Nyaa()
|
||||
public async Task should_parse_recent_feed_from_Nyaa()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Nyaa/Nyaa.xml");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(4);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
{
|
||||
var result = new List<ValidationFailure>();
|
||||
SetupNLog(); // Enable this to enable trace logging with nlog for debugging purposes
|
||||
Test(result);
|
||||
Test(result).GetAwaiter().GetResult();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -34,17 +35,21 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/" + rssXmlFile);
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.ExecuteAsync(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.IsAny<HttpRequest>()))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_ImmortalSeed()
|
||||
public async Task should_parse_recent_feed_from_ImmortalSeed()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/ImmortalSeed.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(50);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -66,11 +71,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_Ezrss()
|
||||
public async Task should_parse_recent_feed_from_Ezrss()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/Ezrss.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(3);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -92,13 +97,13 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_ShowRSS_info()
|
||||
public async Task should_parse_recent_feed_from_ShowRSS_info()
|
||||
{
|
||||
Subject.Definition.Settings.As<TorrentRssIndexerSettings>().AllowZeroSize = true;
|
||||
|
||||
GivenRecentFeedResponse("TorrentRss/ShowRSS.info.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -120,13 +125,13 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_Doki()
|
||||
public async Task should_parse_recent_feed_from_Doki()
|
||||
{
|
||||
Subject.Definition.Settings.As<TorrentRssIndexerSettings>().AllowZeroSize = true;
|
||||
|
||||
GivenRecentFeedResponse("TorrentRss/Doki.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -148,11 +153,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_ExtraTorrents()
|
||||
public async Task should_parse_recent_feed_from_ExtraTorrents()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/ExtraTorrents.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -174,11 +179,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_LimeTorrents()
|
||||
public async Task should_parse_recent_feed_from_LimeTorrents()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/LimeTorrents.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -200,11 +205,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_AnimeTosho_without_size()
|
||||
public async Task should_parse_recent_feed_from_AnimeTosho_without_size()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/AnimeTosho_NoSize.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(2);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -226,11 +231,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_multi_enclosure_from_AnimeTosho()
|
||||
public async Task should_parse_multi_enclosure_from_AnimeTosho()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/AnimeTosho_NoSize.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(2);
|
||||
releases.Last().Should().BeOfType<TorrentInfo>();
|
||||
@@ -243,11 +248,11 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_AlphaRatio()
|
||||
public async Task should_parse_recent_feed_from_AlphaRatio()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/AlphaRatio.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(2);
|
||||
releases.Last().Should().BeOfType<TorrentInfo>();
|
||||
@@ -260,12 +265,12 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_EveolutionWorld_without_size()
|
||||
public async Task should_parse_recent_feed_from_EveolutionWorld_without_size()
|
||||
{
|
||||
Subject.Definition.Settings.As<TorrentRssIndexerSettings>().AllowZeroSize = true;
|
||||
GivenRecentFeedResponse("TorrentRss/EvolutionWorld.xml");
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(2);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
@@ -287,11 +292,13 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_record_indexer_failure_if_unsupported_feed()
|
||||
public async Task should_record_indexer_failure_if_unsupported_feed()
|
||||
{
|
||||
GivenRecentFeedResponse("TorrentRss/invalid/TorrentDay_NoPubDate.xml");
|
||||
|
||||
Subject.FetchRecent().Should().BeEmpty();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().BeEmpty();
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Verify(v => v.RecordFailure(It.IsAny<int>(), TimeSpan.Zero), Times.Once());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -26,15 +27,15 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentleechTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_Torrentleech()
|
||||
public async Task should_parse_recent_feed_from_Torrentleech()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Torrentleech/Torrentleech.xml");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
releases.First().Should().BeOfType<TorrentInfo>();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
@@ -44,15 +45,15 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_torznab_hdaccess_net()
|
||||
public async Task should_parse_recent_feed_from_torznab_hdaccess_net()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_hdaccess_net.xml");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
|
||||
@@ -73,15 +74,15 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_recent_feed_from_torznab_tpb()
|
||||
public async Task should_parse_recent_feed_from_torznab_tpb()
|
||||
{
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_tpb.xml");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var releases = Subject.FetchRecent();
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
releases.Should().HaveCount(5);
|
||||
|
||||
@@ -140,8 +141,8 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
|
||||
(Subject.Definition.Settings as TorznabSettings).BaseUrl = baseUrl;
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
var result = new NzbDroneValidationResult(Subject.Test());
|
||||
result.IsValid.Should().BeTrue();
|
||||
@@ -155,8 +156,8 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
|
||||
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_tpb.xml");
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
|
||||
(Subject.Definition.Settings as TorznabSettings).ApiPath = apiPath;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user