mirror of
https://github.com/ultrasonic/ultrasonic
synced 2025-02-18 04:30:48 +01:00
commit
4f44977e55
41
.circleci/config.yml
Normal file
41
.circleci/config.yml
Normal file
@ -0,0 +1,41 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/android:api-26-alpha
|
||||
working_directory: ~/ultrasonic
|
||||
envoronment:
|
||||
JVM_OPTS: -Xmx3200m
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: gradle-cache-{{ checksum "dependencies.gradle" }}
|
||||
- run:
|
||||
name: checkstyle
|
||||
command: ./gradlew -Pqc ktlintCheck
|
||||
- run:
|
||||
name: build
|
||||
command: ./gradlew assembleDebug
|
||||
- run:
|
||||
name: unit-tests
|
||||
command: |
|
||||
./gradlew :subsonic-api:test :ultrasonic:testDebugUnitTest
|
||||
./gradlew jacocoFullReport
|
||||
bash <(curl -s https://codecov.io/bash)
|
||||
- run:
|
||||
name: lint
|
||||
command: ./gradlew lint
|
||||
- run:
|
||||
name: static analysis
|
||||
command: ./gradlew -Pqc detektCheck
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.gradle
|
||||
key: gradle-cache-{{ checksum "dependencies.gradle" }}
|
||||
- store_artifacts:
|
||||
path: ultrasonic/build/reports
|
||||
path: subsonic-api/build/reports
|
||||
destination: reports
|
||||
- store_artifacts:
|
||||
path: build/reports/jacoco/jacocoFullReport/
|
||||
|
27
CONTRIBUTING.md
Normal file
27
CONTRIBUTING.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Contributing
|
||||
|
||||
Ultrasonic development is a community project, and contributions are welcomed.
|
||||
|
||||
First, see if your issue haven’t been yet reported [here](https://github.com/ultrasonic/ultrasonic/issues),
|
||||
then, please, first discuss the change you wish to make via [a new issue](https://github.com/ultrasonic/ultrasonic/issues/new).
|
||||
|
||||
By default Pull Request should be opened against **develop** branch, PR against **master** branch should be used only
|
||||
for critical bugfixes.
|
||||
|
||||
### Here are a few guidelines you should follow before submitting:
|
||||
1. **License Acceptance:** All contributions must be licensed as [GNU GPLv3](LICENSE) to be accepted.
|
||||
Use `git commit --signoff` to acknowledge this.
|
||||
2. **App is migrating to [Kotlin](https://kotlinlang.org/) programming language:** new Pull Requests
|
||||
should be written in this programming language.
|
||||
3. **No Breakage:** New features or changes to existing ones must not degrade the user experience.
|
||||
4. **Coding standards** best-practices should be followed, comment generously, and avoid "clever" algorithms.
|
||||
Refactoring existing messes is great, but watch out for breakage.
|
||||
5. **No large PR:*** Try to limit the scope of PR only to the related issue, so it will be easier to review
|
||||
and test.
|
||||
|
||||
## Pull Request Process
|
||||
|
||||
1. Ensure all commits are signed-off.
|
||||
2. Check tests for the new code are added.
|
||||
3. Check code style is passing.
|
||||
4. Check code static analysis is passing.
|
@ -1 +1,20 @@
|
||||
Please, take note that this project is STALLED. See README.md for more info.
|
||||
## Problem description
|
||||
|
||||
Describe your problem here. Describe what you want to happen, and what happens
|
||||
if you try to do it. If you have a stack trace or any logs, please format them using
|
||||
github triple backquote notation
|
||||
|
||||
### Steps to reproduce
|
||||
|
||||
Describe how somebody else could observe the same behavior you do. Don't share here any logins and
|
||||
passwords!
|
||||
|
||||
## System information
|
||||
|
||||
* **Ultrasonic version**: *version of the app*
|
||||
* **Android version**: *Version of Android OS on the device*
|
||||
* **Device info**: *Device manufacturer, model*
|
||||
|
||||
## Additional notes
|
||||
|
||||
Include any extra notes here. Otherwise you may remove this section.
|
||||
|
28
README.md
28
README.md
@ -1,12 +1,28 @@
|
||||
# Ultrasonic
|
||||
Subsonic Music Streamer Android client
|
||||
[![Build Status](https://circleci.com/gh/ultrasonic/ultrasonic/tree/develop.svg?style=shield&circle-token=:circle-token)](https://circleci.com/gh/ultrasonic)
|
||||
[![Codecov branch](https://img.shields.io/codecov/c/github/ultrasonic/ultrasonic/develop.svg)]()
|
||||
[![ktlint](https://img.shields.io/badge/code%20style-%E2%9D%A4-FF4081.svg)](https://ktlint.github.io/)
|
||||
|
||||
## About project status
|
||||
Ultrasonic is free, open-source [Subsonic](http://www.subsonic.org/) [API](http://www.subsonic.org/pages/api.jsp) compatible music streaming Android client.
|
||||
|
||||
WARNING: This project is **STALLED**
|
||||
## Download
|
||||
|
||||
This means that I don't have time to fix bugs or add new features, but you can send me **Pull Requests** and I **promise** that I shall merge and upload to Play Store.
|
||||
App is available to download at following stores:
|
||||
|
||||
If you want contribute at project you can see the [issues](https://github.com/ogarcia/ultrasonic/issues) page and try to fix the discovered bugs or implement the enhancements that users report there.
|
||||
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="70">](https://play.google.com/store/apps/details?id=org.moire.ultrasonic)
|
||||
[<img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="70">](https://f-droid.org/packages/org.moire.ultrasonic/)
|
||||
|
||||
## Bugs and issue
|
||||
|
||||
If you want give more active development, please contact with me cause I can give you access to repository to do work faster.
|
||||
First, see if your issue haven’t been yet reported [here](https://github.com/ultrasonic/ultrasonic/issues),
|
||||
otherwise open [a new issue](https://github.com/ultrasonic/ultrasonic/issues/new).
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING](CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
This software is licensed under the terms of the GNU General Public License version 3 (GPLv3).
|
||||
|
||||
Full text of the license is available in the [LICENSE](LICENSE) file and [online](https://opensource.org/licenses/gpl-3.0.html).
|
||||
|
24
build.gradle
24
build.gradle
@ -1,19 +1,39 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
apply from: 'dependencies.gradle'
|
||||
|
||||
buildscript {
|
||||
apply from: 'dependencies.gradle'
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
maven { url "https://plugins.gradle.org/m2/" }
|
||||
}
|
||||
dependencies {
|
||||
classpath gradlePlugins.androidTools
|
||||
classpath gradlePlugins.kotlin
|
||||
classpath gradlePlugins.ktlintGradle
|
||||
classpath(gradlePlugins.detekt) {
|
||||
exclude module: 'kotlin-compiler-embeddable'
|
||||
exclude module: 'kotlin-stdlib'
|
||||
}
|
||||
classpath gradlePlugins.jacocoAndroid
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
// Buildscript here is required by detekt
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
apply from: 'gradle_scripts/jacoco.gradle'
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion(versions.gradle)
|
||||
distributionType("all")
|
||||
}
|
||||
|
@ -2,18 +2,61 @@ ext.versions = [
|
||||
minSdk : 14,
|
||||
targetSdk : 22,
|
||||
compileSdk : 22,
|
||||
gradle : '4.3.1',
|
||||
|
||||
buildTools : "25.0.2",
|
||||
androidTools : "2.2.3",
|
||||
buildTools : "25.0.3",
|
||||
androidTools : "2.3.3",
|
||||
ktlint : "0.12.1",
|
||||
ktlintGradle : "2.3.0",
|
||||
detekt : "1.0.0.RC5-4",
|
||||
jacoco : "0.7.9",
|
||||
jacocoAndroid : "0.1.2",
|
||||
|
||||
androidSupport : "22.2.1",
|
||||
|
||||
kotlin : "1.1.60",
|
||||
|
||||
retrofit : "2.1.0",
|
||||
jackson : "2.9.0",
|
||||
okhttp : "3.9.0",
|
||||
|
||||
junit : "4.12",
|
||||
mockito : "2.12.0",
|
||||
mockitoKotlin : "1.5.0",
|
||||
kluent : "1.26",
|
||||
apacheCodecs : "1.10",
|
||||
]
|
||||
|
||||
ext.gradlePlugins = [
|
||||
androidTools : "com.android.tools.build:gradle:$versions.androidTools"
|
||||
androidTools : "com.android.tools.build:gradle:$versions.androidTools",
|
||||
kotlin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin",
|
||||
ktlintGradle : "gradle.plugin.org.jlleitschuh.gradle:ktlint-gradle:$versions.ktlintGradle",
|
||||
detekt : "gradle.plugin.io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$versions.detekt",
|
||||
jacocoAndroid : "com.dicedmelon.gradle:jacoco-android:$versions.jacocoAndroid"
|
||||
]
|
||||
|
||||
ext.androidSupport = [
|
||||
support : "com.android.support:support-v4:$versions.androidSupport",
|
||||
design : "com.android.support:design:$versions.androidSupport",
|
||||
]
|
||||
|
||||
ext.other = [
|
||||
kotlinStdlib : "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin",
|
||||
kotlinReflect : "org.jetbrains.kotlin:kotlin-reflect:$versions.kotlin",
|
||||
retrofit : "com.squareup.retrofit2:retrofit:$versions.retrofit",
|
||||
gsonConverter : "com.squareup.retrofit2:converter-gson:$versions.retrofit",
|
||||
jacksonConverter : "com.squareup.retrofit2:converter-jackson:$versions.retrofit",
|
||||
jacksonKotlin : "com.fasterxml.jackson.module:jackson-module-kotlin:$versions.jackson",
|
||||
okhttpLogging : "com.squareup.okhttp3:logging-interceptor:$versions.okhttp",
|
||||
]
|
||||
|
||||
ext.testing = [
|
||||
junit : "junit:junit:$versions.junit",
|
||||
kotlinJunit : "org.jetbrains.kotlin:kotlin-test-junit:$versions.kotlin",
|
||||
mockitoKotlin : "com.nhaarman:mockito-kotlin:$versions.mockitoKotlin",
|
||||
mockito : "org.mockito:mockito-core:$versions.mockito",
|
||||
mockitoInline : "org.mockito:mockito-inline:$versions.mockito",
|
||||
kluent : "org.amshove.kluent:kluent:$versions.kluent",
|
||||
mockWebServer : "com.squareup.okhttp3:mockwebserver:$versions.okhttp",
|
||||
apacheCodecs : "commons-codec:commons-codec:$versions.apacheCodecs",
|
||||
]
|
||||
|
111
detekt-config.yml
Normal file
111
detekt-config.yml
Normal file
@ -0,0 +1,111 @@
|
||||
autoCorrect: true
|
||||
failFast: false
|
||||
|
||||
build:
|
||||
warningThreshold: 0
|
||||
failThreshold: 0
|
||||
weights:
|
||||
complexity: 2
|
||||
formatting: 1
|
||||
LongParameterList: 1
|
||||
comments: 1
|
||||
|
||||
potential-bugs:
|
||||
active: true
|
||||
DuplicateCaseInWhenExpression:
|
||||
active: true
|
||||
EqualsWithHashCodeExist:
|
||||
active: true
|
||||
ExplicitGarbageCollectionCall:
|
||||
active: true
|
||||
LateinitUsage:
|
||||
active: false
|
||||
UnsafeCallOnNullableType:
|
||||
active: false
|
||||
UnsafeCast:
|
||||
active: false
|
||||
|
||||
performance:
|
||||
active: true
|
||||
ForEachOnRange:
|
||||
active: true
|
||||
SpreadOperator:
|
||||
active: true
|
||||
|
||||
exceptions:
|
||||
active: true
|
||||
|
||||
empty-blocks:
|
||||
active: true
|
||||
|
||||
complexity:
|
||||
active: true
|
||||
LongMethod:
|
||||
threshold: 20
|
||||
LongParameterList:
|
||||
threshold: 5
|
||||
LargeClass:
|
||||
threshold: 150
|
||||
ComplexMethod:
|
||||
threshold: 10
|
||||
TooManyFunctions:
|
||||
threshold: 20
|
||||
ComplexCondition:
|
||||
threshold: 3
|
||||
LabeledExpression:
|
||||
active: false
|
||||
|
||||
code-smell:
|
||||
active: true
|
||||
FeatureEnvy:
|
||||
threshold: 0.5
|
||||
weight: 0.45
|
||||
base: 0.5
|
||||
|
||||
formatting:
|
||||
active: false
|
||||
|
||||
style:
|
||||
active: true
|
||||
NewLineAtEndOfFile:
|
||||
active: true
|
||||
ForbiddenComment:
|
||||
active: true
|
||||
values: 'TODO:,FIXME:,STOPSHIP:'
|
||||
WildcardImport:
|
||||
active: true
|
||||
MaxLineLength:
|
||||
active: true
|
||||
maxLineLength: 120
|
||||
excludePackageStatements: false
|
||||
excludeImportStatements: false
|
||||
NamingConventionViolation:
|
||||
active: true
|
||||
variablePattern: '^(_)?[a-z$][a-zA-Z$0-9]*$'
|
||||
constantPattern: '^([A-Z_]*|serialVersionUID)$'
|
||||
methodPattern: '^[a-z\s`$][a-zA-Z\s$0-9`]*$'
|
||||
classPattern: '[A-Z$][a-zA-Z$]*'
|
||||
enumEntryPattern: '^[A-Z$][a-zA-Z_$0-9]*$'
|
||||
|
||||
comments:
|
||||
active: true
|
||||
CommentOverPrivateMethod:
|
||||
active: true
|
||||
CommentOverPrivateProperty:
|
||||
active: true
|
||||
UndocumentedPublicClass:
|
||||
active: false
|
||||
searchInNestedClass: true
|
||||
searchInInnerClass: true
|
||||
searchInInnerInterface: true
|
||||
UndocumentedPublicFunction:
|
||||
active: false
|
||||
|
||||
# *experimental feature*
|
||||
# Migration rules can be defined in the same config file or a new one
|
||||
migration:
|
||||
active: false
|
||||
imports:
|
||||
# your.package.Class: new.package.or.Class
|
||||
# for example:
|
||||
# io.gitlab.arturbosch.detekt.api.Rule: io.gitlab.arturbosch.detekt.rule.Rule
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,5 @@
|
||||
#Wed Apr 10 15:27:10 PDT 2013
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3.1-all.zip
|
||||
|
29
gradle_scripts/code_quality.gradle
Normal file
29
gradle_scripts/code_quality.gradle
Normal file
@ -0,0 +1,29 @@
|
||||
// Applies code quality plugins when -Pqc is passed to the gradle
|
||||
def isCodeQualityEnabled = project.hasProperty('qc')
|
||||
|
||||
// KtLint
|
||||
if (isCodeQualityEnabled) {
|
||||
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
||||
|
||||
ktlint {
|
||||
version = versions.ktlint
|
||||
android = true
|
||||
}
|
||||
}
|
||||
|
||||
// Detekt
|
||||
if (isCodeQualityEnabled) {
|
||||
if (!project.rootProject.plugins.hasPlugin("io.gitlab.arturbosch.detekt")) {
|
||||
Project rootProject = project.rootProject
|
||||
rootProject.apply {
|
||||
apply plugin: "io.gitlab.arturbosch.detekt"
|
||||
|
||||
detekt {
|
||||
version = versions.detekt
|
||||
profile("main") {
|
||||
config = "${rootProject.projectDir}/detekt-config.yml"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
57
gradle_scripts/jacoco.gradle
Normal file
57
gradle_scripts/jacoco.gradle
Normal file
@ -0,0 +1,57 @@
|
||||
apply plugin: 'jacoco'
|
||||
|
||||
task jacocoMergeReports(type: JacocoMerge) {
|
||||
group = "Reporting"
|
||||
description = "Merge all jacoco reports from projects into one."
|
||||
|
||||
def subsonicApi = project.findProject("subsonic-api")
|
||||
def ultrasonicApp = project.findProject("ultrasonic")
|
||||
executionData(
|
||||
"${subsonicApi.buildDir}/jacoco/test.exec",
|
||||
"${ultrasonicApp.buildDir}/jacoco/testDebugUnitTest.exec",
|
||||
)
|
||||
destinationFile(file("${project.buildDir}/jacoco/jacoco.exec"))
|
||||
}
|
||||
|
||||
def createJacocoFullReportTask() {
|
||||
tasks.create(name: 'jacocoFullReport', type: JacocoReport, dependsOn: 'jacocoMergeReports') {
|
||||
group = "Reporting"
|
||||
description = "Generate full Jacoco coverage report including all modules."
|
||||
|
||||
def subsonicApi = project.findProject("subsonic-api")
|
||||
def ultrasonicApp = project.findProject("ultrasonic")
|
||||
|
||||
classDirectories = files(
|
||||
fileTree(
|
||||
dir: "${subsonicApi.buildDir}/classes/main",
|
||||
excludes: subsonicApi.jacocoExclude
|
||||
),
|
||||
fileTree(
|
||||
dir: "${ultrasonicApp.buildDir}/intermediates/classes/debug/org",
|
||||
excludes: ultrasonicApp.jacocoExclude
|
||||
)
|
||||
)
|
||||
sourceDirectories = files(subsonicApi.sourceSets.main.getAllSource(),
|
||||
ultrasonicApp.extensions.getByName('android').sourceSets.main.java.sourceFiles)
|
||||
executionData = files("${buildDir}/jacoco/jacoco.exec")
|
||||
|
||||
reports {
|
||||
xml.enabled = true
|
||||
html.enabled = true
|
||||
csv.enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We need to wait to all subprojects configuration finish or we don't get sources and exclusions
|
||||
def subprojectsCount = allprojects.size()
|
||||
allprojects {
|
||||
afterEvaluate { subproject ->
|
||||
subprojectsCount--
|
||||
if (subprojectsCount == 0) {
|
||||
apply {
|
||||
createJacocoFullReportTask()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
110
gradlew
vendored
110
gradlew
vendored
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
@ -6,47 +6,6 @@
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
@ -61,9 +20,49 @@ while [ -h "$PRG" ] ; do
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
@ -90,7 +89,7 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
@ -114,6 +113,7 @@ fi
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
@ -154,11 +154,19 @@ if $cygwin ; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
14
gradlew.bat
vendored
14
gradlew.bat
vendored
@ -8,14 +8,14 @@
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
@ -46,10 +46,9 @@ echo location of your Java installation.
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
@ -60,11 +59,6 @@ set _SKIP=2
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
@ -15,6 +15,11 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
baselineFile file("lint-baseline.xml")
|
||||
abortOnError true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
59
library/lint-baseline.xml
Normal file
59
library/lint-baseline.xml
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="4" by="lint 2.3.3">
|
||||
|
||||
<issue
|
||||
id="NewApi"
|
||||
message="Call requires API level 21 (current min is 14): android.widget.AbsListView#setSelectionFromTop"
|
||||
errorLine1=" setSelectionFromTop(movePos, top - padTop);"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/mobeta/android/dslv/DragSortListView.java"
|
||||
line="2936"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="OldTargetApi"
|
||||
message="Not targeting the latest versions of Android; compatibility modes apply. Consider testing and updating this version. Consult the `android.os.Build.VERSION_CODES` javadoc for details."
|
||||
errorLine1=" <uses-sdk android:targetSdkVersion="7""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/AndroidManifest.xml"
|
||||
line="6"
|
||||
column="15"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="GradleOverrides"
|
||||
message="This `targetSdkVersion` value (`7`) is not used; it is always overridden by the value specified in the Gradle build script (`22`)"
|
||||
errorLine1=" <uses-sdk android:targetSdkVersion="7""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/AndroidManifest.xml"
|
||||
line="6"
|
||||
column="15"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="GradleOverrides"
|
||||
message="This `minSdkVersion` value (`7`) is not used; it is always overridden by the value specified in the Gradle build script (`14`)"
|
||||
errorLine1=" android:minSdkVersion="7" />"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/AndroidManifest.xml"
|
||||
line="7"
|
||||
column="7"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ClickableViewAccessibility"
|
||||
message="`com/mobeta/android/dslv/DragSortController#onTouch` should call `View#performClick` when a click is detected"
|
||||
errorLine1=" public boolean onTouch(View v, MotionEvent ev) {"
|
||||
errorLine2=" ~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/mobeta/android/dslv/DragSortController.java"
|
||||
line="238"
|
||||
column="20"/>
|
||||
</issue>
|
||||
|
||||
</issues>
|
@ -15,4 +15,9 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
baselineFile file("lint-baseline.xml")
|
||||
abortOnError true
|
||||
}
|
||||
}
|
||||
|
246
menudrawer/lint-baseline.xml
Normal file
246
menudrawer/lint-baseline.xml
Normal file
@ -0,0 +1,246 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="4" by="lint 2.3.3">
|
||||
|
||||
<issue
|
||||
id="InlinedApi"
|
||||
message="Field requires API level 17 (current min is 14): `android.view.View#LAYOUT_DIRECTION_RTL`"
|
||||
errorLine1=" if (mSlideDrawable != null) mSlideDrawable.setIsRtl(layoutDirection == LAYOUT_DIRECTION_RTL);"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/MenuDrawer.java"
|
||||
line="882"
|
||||
column="80"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="InlinedApi"
|
||||
message="Field requires API level 17 (current min is 14): `android.view.View#LAYOUT_DIRECTION_RTL`"
|
||||
errorLine1=" mSlideDrawable.setIsRtl(ViewHelper.getLayoutDirection(this) == LAYOUT_DIRECTION_RTL);"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/MenuDrawer.java"
|
||||
line="1325"
|
||||
column="72"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="OldTargetApi"
|
||||
message="Not targeting the latest versions of Android; compatibility modes apply. Consider testing and updating this version. Consult the `android.os.Build.VERSION_CODES` javadoc for details."
|
||||
errorLine1=" <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="16" />"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/AndroidManifest.xml"
|
||||
line="7"
|
||||
column="41"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="GradleOverrides"
|
||||
message="This `minSdkVersion` value (`7`) is not used; it is always overridden by the value specified in the Gradle build script (`14`)"
|
||||
errorLine1=" <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="16" />"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/AndroidManifest.xml"
|
||||
line="7"
|
||||
column="15"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="GradleOverrides"
|
||||
message="This `targetSdkVersion` value (`16`) is not used; it is always overridden by the value specified in the Gradle build script (`22`)"
|
||||
errorLine1=" <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="16" />"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/AndroidManifest.xml"
|
||||
line="7"
|
||||
column="41"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ParcelClassLoader"
|
||||
message="Using the default class loader will not work if you are restoring your own classes. Consider using for example `readBundle(getClass().getClassLoader())` instead."
|
||||
errorLine1=" mState = in.readBundle();"
|
||||
errorLine2=" ~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/MenuDrawer.java"
|
||||
line="1630"
|
||||
column="25"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is never < 14"
|
||||
errorLine1=" if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
|
||||
line="41"
|
||||
column="28"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
|
||||
line="43"
|
||||
column="20"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is never < 14"
|
||||
errorLine1=" if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
|
||||
line="51"
|
||||
column="28"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
|
||||
line="53"
|
||||
column="20"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is never < 14"
|
||||
errorLine1=" if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
|
||||
line="59"
|
||||
column="28"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
|
||||
line="61"
|
||||
column="20"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is never < 14"
|
||||
errorLine1=" if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
|
||||
line="67"
|
||||
column="28"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
|
||||
line="69"
|
||||
column="20"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is never < 14"
|
||||
errorLine1=" if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
|
||||
line="77"
|
||||
column="28"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/compat/ActionBarHelper.java"
|
||||
line="79"
|
||||
column="20"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/DraggableDrawer.java"
|
||||
line="572"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/DraggableDrawer.java"
|
||||
line="580"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/DraggableDrawer.java"
|
||||
line="588"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/DraggableDrawer.java"
|
||||
line="596"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="FloatMath"
|
||||
message="Use `java.lang.Math#sqrt` instead of `android.util.FloatMath#sqrt()` since it is faster as of API 8"
|
||||
errorLine1=" float hyp = FloatMath.sqrt(dx * dx + dy * dy);"
|
||||
errorLine2=" ~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/Scroller.java"
|
||||
line="374"
|
||||
column="25"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="FloatMath"
|
||||
message="Use `java.lang.Math#sqrt` instead of `android.util.FloatMath#sqrt()` since it is faster as of API 8"
|
||||
errorLine1=" float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY);"
|
||||
errorLine2=" ~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/net/simonvt/menudrawer/Scroller.java"
|
||||
line="391"
|
||||
column="26"/>
|
||||
</issue>
|
||||
|
||||
</issues>
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="md__drawerOpenIndicatorDesc">Close drawer</string>
|
||||
<string name="md__drawerOpenIndicatorDesc" tools:ignore="MissingTranslation">Close drawer</string>
|
||||
|
||||
<string name="md__drawerClosedIndicatorDesc">Open drawer</string>
|
||||
<string name="md__drawerClosedIndicatorDesc" tools:ignore="MissingTranslation">Open drawer</string>
|
||||
|
||||
</resources>
|
||||
|
@ -15,4 +15,9 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
baselineFile file("lint-baseline.xml")
|
||||
abortOnError true
|
||||
}
|
||||
}
|
||||
|
326
pulltorefresh/lint-baseline.xml
Normal file
326
pulltorefresh/lint-baseline.xml
Normal file
@ -0,0 +1,326 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="4" by="lint 2.3.3">
|
||||
|
||||
<issue
|
||||
id="LocaleFolder"
|
||||
message="The locale folder "`he`" should be called "`iw`" instead; see the `java.util.Locale` documentation">
|
||||
<location
|
||||
file="src/main/res/values-he"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="OldTargetApi"
|
||||
message="Not targeting the latest versions of Android; compatibility modes apply. Consider testing and updating this version. Consult the `android.os.Build.VERSION_CODES` javadoc for details."
|
||||
errorLine1=" <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="16" />"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/AndroidManifest.xml"
|
||||
line="7"
|
||||
column="41"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="GradleOverrides"
|
||||
message="This `minSdkVersion` value (`4`) is not used; it is always overridden by the value specified in the Gradle build script (`14`)"
|
||||
errorLine1=" <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="16" />"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/AndroidManifest.xml"
|
||||
line="7"
|
||||
column="15"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="GradleOverrides"
|
||||
message="This `targetSdkVersion` value (`16`) is not used; it is always overridden by the value specified in the Gradle build script (`22`)"
|
||||
errorLine1=" <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="16" />"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/AndroidManifest.xml"
|
||||
line="7"
|
||||
column="41"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="Deprecated"
|
||||
message="`android:singleLine` is deprecated: Use `maxLines="1"` instead"
|
||||
errorLine1=" android:singleLine="true""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/pull_to_refresh_header_vertical.xml"
|
||||
line="45"
|
||||
column="17"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="Deprecated"
|
||||
message="`android:singleLine` is deprecated: Use `maxLines="1"` instead"
|
||||
errorLine1=" android:singleLine="true""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/pull_to_refresh_header_vertical.xml"
|
||||
line="53"
|
||||
column="17"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="MissingTranslation"
|
||||
message=""`pull_to_refresh_from_bottom_pull_label`" is not translated in "es" (Spanish), "fr" (French), "pt" (Portuguese), "pt-BR" (Portuguese: Brazil)"
|
||||
errorLine1=" <string name="pull_to_refresh_from_bottom_pull_label">@string/pull_to_refresh_pull_label</string>"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/pull_refresh_strings.xml"
|
||||
line="9"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="MissingTranslation"
|
||||
message=""`pull_to_refresh_from_bottom_release_label`" is not translated in "es" (Spanish), "fr" (French), "pt" (Portuguese), "pt-BR" (Portuguese: Brazil)"
|
||||
errorLine1=" <string name="pull_to_refresh_from_bottom_release_label">@string/pull_to_refresh_release_label</string>"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/pull_refresh_strings.xml"
|
||||
line="10"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="MissingTranslation"
|
||||
message=""`pull_to_refresh_from_bottom_refreshing_label`" is not translated in "es" (Spanish), "fr" (French), "pt" (Portuguese), "pt-BR" (Portuguese: Brazil)"
|
||||
errorLine1=" <string name="pull_to_refresh_from_bottom_refreshing_label">@string/pull_to_refresh_refreshing_label</string>"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/pull_refresh_strings.xml"
|
||||
line="11"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="AddJavascriptInterface"
|
||||
message="`WebView.addJavascriptInterface` should not be called with minSdkVersion < 17 for security reasons: JavaScript can use reflection to manipulate application"
|
||||
errorLine1=" webView.addJavascriptInterface(mJsCallback, JS_INTERFACE_PKG);"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/extras/PullToRefreshWebView2.java"
|
||||
line="90"
|
||||
column="11"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="JavascriptInterface"
|
||||
message="None of the methods in the added interface (JsValueCallback) have been annotated with `@android.webkit.JavascriptInterface`; they will not be visible in API 17"
|
||||
errorLine1=" webView.addJavascriptInterface(mJsCallback, JS_INTERFACE_PKG);"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/extras/PullToRefreshWebView2.java"
|
||||
line="90"
|
||||
column="11"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" return VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD && mOverScrollEnabled"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshBase.java"
|
||||
line="211"
|
||||
column="10"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshExpandableListView.java"
|
||||
line="54"
|
||||
column="7"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshGridView.java"
|
||||
line="54"
|
||||
column="7"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshHorizontalScrollView.java"
|
||||
line="53"
|
||||
column="7"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshListView.java"
|
||||
line="207"
|
||||
column="7"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshScrollView.java"
|
||||
line="52"
|
||||
column="7"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshWebView.java"
|
||||
line="98"
|
||||
column="7"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteSdkInt"
|
||||
message="Unnecessary; SDK_INT is always >= 14"
|
||||
errorLine1=" if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/internal/ViewCompat.java"
|
||||
line="44"
|
||||
column="7"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="FloatMath"
|
||||
message="Use `java.lang.Math#floor` instead of `android.util.FloatMath#floor()` since it is faster as of API 8"
|
||||
errorLine1=" float exactContentHeight = FloatMath.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale());"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshWebView.java"
|
||||
line="115"
|
||||
column="30"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="FloatMath"
|
||||
message="Use `java.lang.Math#floor` instead of `android.util.FloatMath#floor()` since it is faster as of API 8"
|
||||
errorLine1=" return (int) Math.max(0, FloatMath.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale())"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshWebView.java"
|
||||
line="161"
|
||||
column="29"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="IconMissingDensityFolder"
|
||||
message="Missing density variation folders in `src/main/res`: drawable-xxhdpi">
|
||||
<location
|
||||
file="src/main/res"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ViewConstructor"
|
||||
message="Custom view `RotateLoadingLayout` is missing constructor used by tools: `(Context)` or `(Context,AttributeSet)` or `(Context,AttributeSet,int)`"
|
||||
errorLine1="public class RotateLoadingLayout extends LoadingLayout {"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/internal/RotateLoadingLayout.java"
|
||||
line="30"
|
||||
column="14"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="[Accessibility] Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ^">
|
||||
<location
|
||||
file="src/main/res/layout/pull_to_refresh_header_horizontal.xml"
|
||||
line="13"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="[Accessibility] Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ^">
|
||||
<location
|
||||
file="src/main/res/layout/pull_to_refresh_header_vertical.xml"
|
||||
line="18"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RtlHardcoded"
|
||||
message="Use "`Gravity.START`" instead of "`Gravity.LEFT`" to ensure correct behavior in right-to-left locales"
|
||||
errorLine1=" lp.gravity = scrollDirection == Orientation.VERTICAL ? Gravity.TOP : Gravity.LEFT;"
|
||||
errorLine2=" ~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/internal/LoadingLayout.java"
|
||||
line="92"
|
||||
column="82"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RtlHardcoded"
|
||||
message="Use "`Gravity.END`" instead of "`Gravity.RIGHT`" to ensure correct behavior in right-to-left locales"
|
||||
errorLine1=" lp.gravity = scrollDirection == Orientation.VERTICAL ? Gravity.BOTTOM : Gravity.RIGHT;"
|
||||
errorLine2=" ~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/internal/LoadingLayout.java"
|
||||
line="102"
|
||||
column="85"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RtlHardcoded"
|
||||
message="Use "`Gravity.END`" instead of "`Gravity.RIGHT`" to ensure correct behavior in right-to-left locales"
|
||||
errorLine1=" params.gravity = Gravity.TOP | Gravity.RIGHT;"
|
||||
errorLine2=" ~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshAdapterViewBase.java"
|
||||
line="344"
|
||||
column="43"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RtlHardcoded"
|
||||
message="Use "`Gravity.END`" instead of "`Gravity.RIGHT`" to ensure correct behavior in right-to-left locales"
|
||||
errorLine1=" params.gravity = Gravity.BOTTOM | Gravity.RIGHT;"
|
||||
errorLine2=" ~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/handmark/pulltorefresh/library/PullToRefreshAdapterViewBase.java"
|
||||
line="359"
|
||||
column="46"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RtlHardcoded"
|
||||
message="Use "`start`" instead of "`left`" to ensure correct behavior in right-to-left locales"
|
||||
errorLine1=" android:layout_gravity="left|center_vertical" >"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/pull_to_refresh_header_vertical.xml"
|
||||
line="16"
|
||||
column="37"/>
|
||||
</issue>
|
||||
|
||||
</issues>
|
@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="pull_to_refresh_pull_label">Pull to refresh…</string>
|
||||
<string name="pull_to_refresh_release_label">Release to refresh…</string>
|
||||
<string name="pull_to_refresh_refreshing_label">Loading…</string>
|
||||
<string name="pull_to_refresh_pull_label" tools:ignore="MissingTranslation">Pull to refresh…</string>
|
||||
<string name="pull_to_refresh_release_label" tools:ignore="MissingTranslation">Release to refresh…</string>
|
||||
<string name="pull_to_refresh_refreshing_label" tools:ignore="MissingTranslation">Loading…</string>
|
||||
|
||||
<!-- Just use standard Pull Down String when pulling up. These can be set for languages which require it -->
|
||||
<string name="pull_to_refresh_from_bottom_pull_label">@string/pull_to_refresh_pull_label</string>
|
||||
<string name="pull_to_refresh_from_bottom_release_label">@string/pull_to_refresh_release_label</string>
|
||||
<string name="pull_to_refresh_from_bottom_refreshing_label">@string/pull_to_refresh_refreshing_label</string>
|
||||
<string name="pull_to_refresh_from_bottom_pull_label" tools:ignore="MissingTranslation">@string/pull_to_refresh_pull_label</string>
|
||||
<string name="pull_to_refresh_from_bottom_release_label" tools:ignore="MissingTranslation">@string/pull_to_refresh_release_label</string>
|
||||
<string name="pull_to_refresh_from_bottom_refreshing_label" tools:ignore="MissingTranslation">@string/pull_to_refresh_refreshing_label</string>
|
||||
|
||||
</resources>
|
@ -1,4 +1,4 @@
|
||||
include ':library'
|
||||
include ':library', ':subsonic-api'
|
||||
include ':menudrawer'
|
||||
include ':pulltorefresh'
|
||||
include ':ultrasonic'
|
||||
|
2
subsonic-api/.gitignore
vendored
Normal file
2
subsonic-api/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/build
|
||||
*.iml
|
62
subsonic-api/build.gradle
Normal file
62
subsonic-api/build.gradle
Normal file
@ -0,0 +1,62 @@
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'jacoco'
|
||||
apply from: '../gradle_scripts/code_quality.gradle'
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += "${projectDir}/src/main/kotlin"
|
||||
test.java.srcDirs += "${projectDir}/src/integrationTest/kotlin"
|
||||
test.resources.srcDirs += "${projectDir}/src/integrationTest/resources"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api other.kotlinStdlib
|
||||
api other.retrofit
|
||||
implementation other.jacksonConverter
|
||||
implementation(other.jacksonKotlin) {
|
||||
exclude module: 'kotlin-reflect'
|
||||
}
|
||||
implementation other.kotlinReflect // for jackson kotlin, but to use the same version
|
||||
implementation other.okhttpLogging
|
||||
|
||||
testImplementation testing.junit
|
||||
testImplementation testing.kotlinJunit
|
||||
testImplementation testing.mockito
|
||||
testImplementation testing.mockitoInline
|
||||
testImplementation testing.mockitoKotlin
|
||||
testImplementation testing.kluent
|
||||
testImplementation testing.mockWebServer
|
||||
testImplementation testing.apacheCodecs
|
||||
}
|
||||
|
||||
jacoco {
|
||||
toolVersion(versions.jacoco)
|
||||
}
|
||||
|
||||
ext {
|
||||
// Excluding data classes
|
||||
jacocoExclude = [
|
||||
'**/models/**'
|
||||
]
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
reports {
|
||||
html.enabled true
|
||||
csv.enabled false
|
||||
xml.enabled true
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
classDirectories = files(classDirectories.files.collect {
|
||||
fileTree(dir: it, excludes: jacocoExclude)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
test.finalizedBy jacocoTestReport
|
||||
test {
|
||||
jacoco {
|
||||
excludes += jacocoExclude
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okio.Okio
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.amshove.kluent.`should contain`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.moire.ultrasonic.api.subsonic.response.SubsonicResponse
|
||||
import org.moire.ultrasonic.api.subsonic.rules.MockWebServerRule
|
||||
import retrofit2.Response
|
||||
import java.nio.charset.Charset
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
|
||||
const val USERNAME = "some-user"
|
||||
const val PASSWORD = "some-password"
|
||||
val CLIENT_VERSION = SubsonicAPIVersions.V1_16_0
|
||||
const val CLIENT_ID = "test-client"
|
||||
|
||||
val dateFormat by lazy(LazyThreadSafetyMode.NONE, {
|
||||
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US)
|
||||
})
|
||||
|
||||
fun MockWebServerRule.enqueueResponse(resourceName: String) {
|
||||
this.mockWebServer.enqueue(MockResponse()
|
||||
.setBody(loadJsonResponse(resourceName))
|
||||
.setHeader("Content-Type", "application/json;charset=UTF-8"))
|
||||
}
|
||||
|
||||
fun MockWebServerRule.loadJsonResponse(name: String): String {
|
||||
val source = Okio.buffer(Okio.source(javaClass.classLoader.getResourceAsStream(name)))
|
||||
return source.readString(Charset.forName("UTF-8"))
|
||||
}
|
||||
|
||||
fun <T> assertResponseSuccessful(response: Response<T>) {
|
||||
response.isSuccessful `should be` true
|
||||
response.body() `should not be` null
|
||||
}
|
||||
|
||||
fun parseDate(dateAsString: String): Calendar {
|
||||
val result = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
|
||||
result.time = dateFormat.parse(dateAsString.replace("Z$".toRegex(), "+0000"))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun <T : SubsonicResponse> checkErrorCallParsed(mockWebServerRule: MockWebServerRule,
|
||||
apiRequest: () -> Response<T>): T {
|
||||
mockWebServerRule.enqueueResponse("generic_error_response.json")
|
||||
|
||||
val response = apiRequest()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body()) {
|
||||
status `should be` SubsonicResponse.Status.ERROR
|
||||
error `should be` SubsonicError.GENERIC
|
||||
}
|
||||
return response.body()
|
||||
}
|
||||
|
||||
fun SubsonicResponse.assertBaseResponseOk() {
|
||||
status `should be` SubsonicResponse.Status.OK
|
||||
version `should be` SubsonicAPIVersions.V1_13_0
|
||||
error `should be` null
|
||||
}
|
||||
|
||||
fun MockWebServerRule.assertRequestParam(responseResourceName: String = "ping_ok.json",
|
||||
expectedParam: String,
|
||||
apiRequest: () -> Response<out Any>) {
|
||||
this.enqueueResponse(responseResourceName)
|
||||
apiRequest()
|
||||
|
||||
val request = this.mockWebServer.takeRequest()
|
||||
|
||||
request.requestLine `should contain` expectedParam
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions.V1_6_0
|
||||
import org.moire.ultrasonic.api.subsonic.interceptors.toHexBytes
|
||||
import org.moire.ultrasonic.api.subsonic.rules.MockWebServerRule
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient.getStreamUrl] method.
|
||||
*/
|
||||
class GetStreamUrlTest {
|
||||
@JvmField @Rule val mockWebServerRule = MockWebServerRule()
|
||||
|
||||
val id = "boom"
|
||||
private lateinit var client: SubsonicAPIClient
|
||||
private lateinit var expectedUrl: String
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
client = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(),
|
||||
USERNAME, PASSWORD, V1_6_0, CLIENT_ID)
|
||||
val baseExpectedUrl = mockWebServerRule.mockWebServer.url("").toString()
|
||||
expectedUrl = "$baseExpectedUrl/rest/stream.view?id=$id&u=$USERNAME" +
|
||||
"&c=$CLIENT_ID&f=json&v=${V1_6_0.restApiVersion}&p=enc:${PASSWORD.toHexBytes()}"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should return valid stream url`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
val streamUrl = client.getStreamUrl(id)
|
||||
|
||||
streamUrl `should equal to` expectedUrl
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should still return stream url if connection failed`() {
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse().setResponseCode(500))
|
||||
|
||||
val streamUrl = client.getStreamUrl(id)
|
||||
|
||||
streamUrl `should equal to` expectedUrl
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.moire.ultrasonic.api.subsonic.rules.MockWebServerRule
|
||||
|
||||
/**
|
||||
* Base class for integration tests for [SubsonicAPIClient] class.
|
||||
*/
|
||||
abstract class SubsonicAPIClientTest {
|
||||
@JvmField @Rule val mockWebServerRule = MockWebServerRule()
|
||||
|
||||
protected lateinit var client: SubsonicAPIClient
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
client = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(), USERNAME, PASSWORD,
|
||||
CLIENT_VERSION, CLIENT_ID)
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIDefinition.addChatMessage] call.
|
||||
*/
|
||||
class SubsonicApiAddChatMessageTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.addChatMessage("some").execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
val response = client.api.addChatMessage("some").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass message in request param`() {
|
||||
val message = "Youuhuuu"
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "message=$message") {
|
||||
client.api.addChatMessage(message = message).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIDefinition.createBookmark] call.
|
||||
*/
|
||||
class SubsonicApiCreateBookmarkTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.createBookmark("1", 1).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
val response = client.api.createBookmark("213", 123213L).execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id in request params`() {
|
||||
val id = "544"
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "id=$id") {
|
||||
client.api.createBookmark(id = id, position = 123).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass position in request params`() {
|
||||
val position = 4412333L
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "position=$position") {
|
||||
client.api.createBookmark(id = "12", position = position).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass comment in request params`() {
|
||||
val comment = "some-comment"
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "comment=$comment") {
|
||||
client.api.createBookmark(id = "1", position = 1, comment = comment).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for createPlaylist call.
|
||||
*/
|
||||
class SubsonicApiCreatePlaylistTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.createPlaylist().execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should hanlde ok response`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
val response = client.api.createPlaylist().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id param in request`() {
|
||||
val id = "56"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "playlistId=$id") {
|
||||
client.api.createPlaylist(id = id).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass name param in request`() {
|
||||
val name = "some-name"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "name=$name") {
|
||||
client.api.createPlaylist(name = name).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass song id param in request`() {
|
||||
val songId = listOf("4410", "852")
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "songId=${songId[0]}&songId=${songId[1]}") {
|
||||
client.api.createPlaylist(songIds = songId).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
import java.util.Calendar
|
||||
|
||||
/**
|
||||
* Instrumentation test for [SubsonicAPIDefinition.createShare] call.
|
||||
*/
|
||||
class SubsonicApiCreateShareTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error responce`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.createShare(listOf("some-id")).execute()
|
||||
}
|
||||
|
||||
response.shares `should equal` emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_shares_ok.json")
|
||||
|
||||
val response = client.api.createShare(listOf("some-id")).execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
response.body().shares.size `should equal to` 1
|
||||
with(response.body().shares[0]) {
|
||||
id `should equal to` "0"
|
||||
url `should equal to` "https://subsonic.com/ext/share/awdwo?jwt=eyJhbGciOiJIUzI1NiJ9." +
|
||||
"eyJwYXRoIjoiL2V4dC9zaGFyZS9hd2R3byIsImV4cCI6MTU0MTYyNjQzMX0.iy8dkt_ZZc8hJ692" +
|
||||
"UxorHdHWFU2RB-fMCmCA4IJ_dTw"
|
||||
username `should equal to` "admin"
|
||||
created `should equal` parseDate("2017-11-07T21:33:51.748Z")
|
||||
expires `should equal` parseDate("2018-11-07T21:33:51.748Z")
|
||||
lastVisited `should equal` parseDate("2018-11-07T21:33:51.748Z")
|
||||
description `should equal to` "Awesome link!"
|
||||
visitCount `should equal to` 0
|
||||
items.size `should equal to` 1
|
||||
items[0] `should equal` MusicDirectoryChild(id = "4212", parent = "4186", isDir = false,
|
||||
title = "Heaven Knows", album = "Going to Hell", artist = "The Pretty Reckless",
|
||||
track = 3, year = 2014, genre = "Hard Rock", coverArt = "4186", size = 9025090,
|
||||
contentType = "audio/mpeg", suffix = "mp3", duration = 225, bitRate = 320,
|
||||
path = "The Pretty Reckless/Going to Hell/03 Heaven Knows.mp3", isVideo = false,
|
||||
playCount = 2, discNumber = 1, created = parseDate("2016-10-23T21:30:40.000Z"),
|
||||
albumId = "388", artistId = "238", type = "music")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass ids in request param`() {
|
||||
val idsList = listOf("some-id1", "some-id2")
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "id=${idsList[0]}&id=${idsList[1]}") {
|
||||
client.api.createShare(idsList).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass description in request param`() {
|
||||
val description = "description-banana"
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "description=$description") {
|
||||
client.api.createShare(idsToShare = listOf("id1", "id2"), description = description)
|
||||
.execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass expires in request param`() {
|
||||
val expires = Calendar.getInstance().timeInMillis
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "expires=$expires") {
|
||||
client.api.createShare(idsToShare = listOf("id1"), expires = expires).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIDefinition.deleteBookmark] call.
|
||||
*/
|
||||
class SubsonicApiDeleteBookmarkTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.deleteBookmark("1").execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
val response = client.api.deleteBookmark("1").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id in request params`() {
|
||||
val id = "233"
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "id=$id") {
|
||||
client.api.deleteBookmark(id).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Instrumentation test for [SubsonicAPIClient] for deletePlaylist call.
|
||||
*/
|
||||
class SubsonicApiDeletePlaylistTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.deletePlaylist("10").execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
val response = client.api.deletePlaylist("10").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id param in request`() {
|
||||
val id = "534"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "id=$id") {
|
||||
client.api.deletePlaylist(id).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIDefinition.deleteShare] call.
|
||||
*/
|
||||
class SubsonicApiDeleteShareTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.deleteShare("123").execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
val response = client.api.deleteShare("12").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id in request params`() {
|
||||
val id = "224"
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "id=$id") {
|
||||
client.api.deleteShare(id).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.Album
|
||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType
|
||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType.STARRED
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for getAlbumList2() call.
|
||||
*/
|
||||
@Suppress("NamingConventionViolation")
|
||||
class SubsonicApiGetAlbumList2Test : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getAlbumList2(STARRED).execute()
|
||||
}
|
||||
|
||||
response.albumList `should equal` emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_album_list_2_ok.json")
|
||||
|
||||
val response = client.api.getAlbumList2(STARRED).execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().albumList) {
|
||||
this.size `should equal to` 2
|
||||
this[0] `should equal` Album(id = "962", name = "Fury", artist = "Sick Puppies",
|
||||
artistId = "473", coverArt = "al-962", songCount = 13, duration = 2591,
|
||||
created = parseDate("2017-09-02T17:34:51.000Z"), year = 2016,
|
||||
genre = "Alternative Rock")
|
||||
this[1] `should equal` Album(id = "961", name = "Endless Forms Most Beautiful",
|
||||
artist = "Nightwish", artistId = "559", coverArt = "al-961", songCount = 22,
|
||||
duration = 9469, created = parseDate("2017-09-02T16:22:47.000Z"),
|
||||
year = 2015, genre = "Symphonic Metal")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass type in request params`() {
|
||||
val type = AlbumListType.SORTED_BY_NAME
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_2_ok.json",
|
||||
expectedParam = "type=${type.typeName}") {
|
||||
client.api.getAlbumList2(type = type).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass size in request param`() {
|
||||
val size = 45
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_2_ok.json",
|
||||
expectedParam = "size=$size") {
|
||||
client.api.getAlbumList2(STARRED, size = size).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass offset in request param`() {
|
||||
val offset = 33
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_2_ok.json",
|
||||
expectedParam = "offset=$offset") {
|
||||
client.api.getAlbumList2(STARRED, offset = offset).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass from year in request params`() {
|
||||
val fromYear = 3030
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_2_ok.json",
|
||||
expectedParam = "fromYear=$fromYear") {
|
||||
client.api.getAlbumList2(STARRED, fromYear = fromYear).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass toYear in request param`() {
|
||||
val toYear = 2014
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_2_ok.json",
|
||||
expectedParam = "toYear=$toYear") {
|
||||
client.api.getAlbumList2(STARRED, toYear = toYear).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass genre in request param`() {
|
||||
val genre = "MathRock"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_2_ok.json",
|
||||
expectedParam = "genre=$genre") {
|
||||
client.api.getAlbumList2(STARRED, genre = genre).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass music folder id in request param`() {
|
||||
val musicFolderId = "9422"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_2_ok.json",
|
||||
expectedParam = "musicFolderId=$musicFolderId") {
|
||||
client.api.getAlbumList2(STARRED, musicFolderId = musicFolderId).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType
|
||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType.BY_GENRE
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
|
||||
/**
|
||||
* Integration tests for [SubsonicAPIDefinition] for getAlbumList call.
|
||||
*/
|
||||
class SubsonicApiGetAlbumListRequestTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getAlbumList(BY_GENRE).execute()
|
||||
}
|
||||
|
||||
response.albumList `should equal` emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_album_list_ok.json")
|
||||
|
||||
val response = client.api.getAlbumList(BY_GENRE).execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().albumList) {
|
||||
size `should equal to` 2
|
||||
this[1] `should equal` MusicDirectoryChild(id = "9997", parent = "9996", isDir = true,
|
||||
title = "Endless Forms Most Beautiful", album = "Endless Forms Most Beautiful",
|
||||
artist = "Nightwish", year = 2015, genre = "Symphonic Metal",
|
||||
coverArt = "9997", playCount = 11,
|
||||
created = parseDate("2017-09-02T16:22:49.000Z"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass type in request params`() {
|
||||
val listType = AlbumListType.HIGHEST
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_ok.json",
|
||||
expectedParam = "type=${listType.typeName}") {
|
||||
client.api.getAlbumList(type = listType).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass size in request params`() {
|
||||
val size = 45
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_ok.json",
|
||||
expectedParam = "size=$size") {
|
||||
client.api.getAlbumList(type = BY_GENRE, size = size).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass offset in request params`() {
|
||||
val offset = 3
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_ok.json",
|
||||
expectedParam = "offset=$offset") {
|
||||
client.api.getAlbumList(type = BY_GENRE, offset = offset).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass from year in request params`() {
|
||||
val fromYear = 2001
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_ok.json",
|
||||
expectedParam = "fromYear=$fromYear") {
|
||||
client.api.getAlbumList(type = BY_GENRE, fromYear = fromYear).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass to year in request params`() {
|
||||
val toYear = 2017
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_ok.json",
|
||||
expectedParam = "toYear=$toYear") {
|
||||
client.api.getAlbumList(type = BY_GENRE, toYear = toYear).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass genre in request params`() {
|
||||
val genre = "Rock"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_ok.json",
|
||||
expectedParam = "genre=$genre") {
|
||||
client.api.getAlbumList(type = BY_GENRE, genre = genre).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass music folder id in request params`() {
|
||||
val folderId = "545"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_list_ok.json",
|
||||
expectedParam = "musicFolderId=$folderId") {
|
||||
client.api.getAlbumList(type = BY_GENRE, musicFolderId = folderId).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.Album
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for getAlbum call.
|
||||
*/
|
||||
class SubsonicApiGetAlbumTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should parse error responce`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getAlbum("56").execute()
|
||||
}
|
||||
|
||||
response.album `should not be` null
|
||||
response.album `should equal` Album()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should add id to request params`() {
|
||||
val id = "76"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_album_ok.json",
|
||||
expectedParam = "id=$id") {
|
||||
client.api.getAlbum(id).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_album_ok.json")
|
||||
|
||||
val response = client.api.getAlbum("512").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().album) {
|
||||
id `should equal to` "618"
|
||||
name `should equal to` "Black Ice"
|
||||
artist `should equal to` "AC/DC"
|
||||
artistId `should equal to` "362"
|
||||
coverArt `should equal to` "al-618"
|
||||
songCount `should equal to` 15
|
||||
duration `should equal to` 3331
|
||||
created `should equal` parseDate("2016-10-23T15:31:22.000Z")
|
||||
year `should equal to` 2008
|
||||
genre `should equal to` "Hard Rock"
|
||||
songList.size `should equal to` 15
|
||||
songList[0] `should equal` MusicDirectoryChild(id = "6491", parent = "6475", isDir = false,
|
||||
title = "Rock 'n' Roll Train", album = "Black Ice", artist = "AC/DC",
|
||||
track = 1, year = 2008, genre = "Hard Rock", coverArt = "6475", size = 7205451,
|
||||
contentType = "audio/mpeg", suffix = "mp3", duration = 261, bitRate = 219,
|
||||
path = "AC_DC/Black Ice/01 Rock 'n' Roll Train.mp3", isVideo = false,
|
||||
playCount = 0, discNumber = 1, created = parseDate("2016-10-23T15:31:20.000Z"),
|
||||
albumId = "618", artistId = "362", type = "music")
|
||||
songList[5] `should equal` MusicDirectoryChild(id = "6492", parent = "6475", isDir = false,
|
||||
title = "Smash 'n' Grab", album = "Black Ice", artist = "AC/DC", track = 6,
|
||||
year = 2008, genre = "Hard Rock", coverArt = "6475", size = 6697204,
|
||||
contentType = "audio/mpeg", suffix = "mp3", duration = 246, bitRate = 216,
|
||||
path = "AC_DC/Black Ice/06 Smash 'n' Grab.mp3", isVideo = false, playCount = 0,
|
||||
discNumber = 1, created = parseDate("2016-10-23T15:31:20.000Z"),
|
||||
albumId = "618", artistId = "362", type = "music")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.Album
|
||||
import org.moire.ultrasonic.api.subsonic.models.Artist
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for getArtist call.
|
||||
*/
|
||||
class SubsonicApiGetArtistTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should parse error call`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getArtist("101").execute()
|
||||
}
|
||||
|
||||
response.artist `should not be` null
|
||||
response.artist `should equal` Artist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id param in request`() {
|
||||
val id = "929"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_artist_ok.json",
|
||||
expectedParam = "id=$id") {
|
||||
client.api.getArtist(id).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_artist_ok.json")
|
||||
|
||||
val response = client.api.getArtist("100").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().artist) {
|
||||
id `should equal to` "362"
|
||||
name `should equal to` "AC/DC"
|
||||
coverArt `should equal to` "ar-362"
|
||||
albumCount `should equal to` 2
|
||||
albumsList.size `should equal to` 2
|
||||
albumsList[0] `should equal` Album(id = "618", name = "Black Ice", artist = "AC/DC",
|
||||
artistId = "362", coverArt = "al-618", songCount = 15, duration = 3331,
|
||||
created = parseDate("2016-10-23T15:31:22.000Z"),
|
||||
year = 2008, genre = "Hard Rock")
|
||||
albumsList[1] `should equal` Album(id = "617", name = "Rock or Bust", artist = "AC/DC",
|
||||
artistId = "362", coverArt = "al-617", songCount = 11, duration = 2095,
|
||||
created = parseDate("2016-10-23T15:31:23.000Z"),
|
||||
year = 2014, genre = "Hard Rock")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.Artist
|
||||
import org.moire.ultrasonic.api.subsonic.models.Index
|
||||
import org.moire.ultrasonic.api.subsonic.models.Indexes
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for getArtists() request.
|
||||
*/
|
||||
class SubsonicApiGetArtistsTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should parse get artists error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getArtists(null).execute()
|
||||
}
|
||||
|
||||
response.indexes `should not be` null
|
||||
response.indexes `should equal` Indexes()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse get artists ok reponse`() {
|
||||
mockWebServerRule.enqueueResponse("get_artists_ok.json")
|
||||
|
||||
val response = client.api.getArtists(null).execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().indexes) {
|
||||
lastModified `should equal to` 0L
|
||||
ignoredArticles `should equal to` "The El La Los Las Le Les"
|
||||
shortcutList `should equal` emptyList()
|
||||
indexList.size `should equal to` 2
|
||||
indexList `should equal` listOf(
|
||||
Index(name = "A", artists = listOf(
|
||||
Artist(id = "362", name = "AC/DC", coverArt = "ar-362", albumCount = 2),
|
||||
Artist(id = "254", name = "Acceptance", coverArt = "ar-254", albumCount = 1)
|
||||
)),
|
||||
Index(name = "T", artists = listOf(
|
||||
Artist(id = "516", name = "Tangerine Dream", coverArt = "ar-516", albumCount = 1),
|
||||
Artist(id = "242", name = "Taproot", coverArt = "ar-242", albumCount = 2)
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass param on query for get artists call`() {
|
||||
mockWebServerRule.enqueueResponse("get_artists_ok.json")
|
||||
val musicFolderId = "101"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_artists_ok.json",
|
||||
expectedParam = "musicFolderId=$musicFolderId") {
|
||||
client.api.getArtists(musicFolderId).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient.getAvatar] call.
|
||||
*/
|
||||
class SubsonicApiGetAvatarTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle api error response`() {
|
||||
mockWebServerRule.enqueueResponse("generic_error_response.json")
|
||||
|
||||
val response = client.getAvatar("some")
|
||||
|
||||
with(response) {
|
||||
stream `should be` null
|
||||
responseHttpCode `should equal to` 200
|
||||
apiError `should equal` SubsonicError.GENERIC
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle server error`() {
|
||||
val httpErrorCode = 500
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse().setResponseCode(httpErrorCode))
|
||||
|
||||
val response = client.getAvatar("some")
|
||||
|
||||
with(response) {
|
||||
stream `should equal` null
|
||||
responseHttpCode `should equal to` httpErrorCode
|
||||
apiError `should be` null
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should return successful call stream`() {
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse()
|
||||
.setBody(mockWebServerRule.loadJsonResponse("ping_ok.json")))
|
||||
|
||||
val response = client.stream("some")
|
||||
|
||||
with(response) {
|
||||
responseHttpCode `should equal to` 200
|
||||
apiError `should be` null
|
||||
stream `should not be` null
|
||||
val expectedContent = mockWebServerRule.loadJsonResponse("ping_ok.json")
|
||||
stream!!.bufferedReader().readText() `should equal to` expectedContent
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass username as param`() {
|
||||
val username = "Guardian"
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "username=$username") {
|
||||
client.api.getAvatar(username).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIDefinition.getBookmarks] call.
|
||||
*/
|
||||
class SubsonicApiGetBookmarksTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getBookmarks().execute()
|
||||
}
|
||||
|
||||
response.bookmarkList `should equal` emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_bookmarks_ok.json")
|
||||
|
||||
val response = client.api.getBookmarks().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
response.body().bookmarkList.size `should equal to` 1
|
||||
with(response.body().bookmarkList[0]) {
|
||||
position `should equal to` 107914
|
||||
username `should equal to` "CaptainEurope"
|
||||
comment `should equal to` "Look at this"
|
||||
created `should equal` parseDate("2017-11-18T15:22:22.144Z")
|
||||
changed `should equal` parseDate("2017-11-18T15:22:22.144Z")
|
||||
entry `should equal` MusicDirectoryChild(id = "10349", parent = "10342",
|
||||
isDir = false, title = "Amerika", album = "Home of the Strange",
|
||||
artist = "Young the Giant", track = 1, year = 2016, genre = "Indie Rock",
|
||||
coverArt = "10342", size = 9628673, contentType = "audio/mpeg",
|
||||
suffix = "mp3", duration = 240, bitRate = 320,
|
||||
path = "Young the Giant/Home of the Strange/01 Amerika.mp3",
|
||||
isVideo = false, playCount = 2, discNumber = 1,
|
||||
created = parseDate("2017-11-01T17:46:52.000Z"),
|
||||
albumId = "984", artistId = "571", type = "music")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.ChatMessage
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIDefinition.getChatMessages] call.
|
||||
*/
|
||||
class SubsonicApiGetChatMessagesTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getChatMessages().execute()
|
||||
}
|
||||
|
||||
response.chatMessages `should equal` emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_chat_messages_ok.json")
|
||||
|
||||
val response = client.api.getChatMessages().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().chatMessages) {
|
||||
size `should equal to` 2
|
||||
this[0] `should equal` ChatMessage(username = "sindre", time = 1269771845310,
|
||||
message = "Sindre was here")
|
||||
this[1] `should equal` ChatMessage(username = "ben", time = 1269771842504,
|
||||
message = "Ben too")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass since in request param`() {
|
||||
val since = 21388L
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "since=$since") {
|
||||
client.api.getChatMessages(since = since).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for [SubsonicAPIDefinition.getCoverArt] call.
|
||||
*/
|
||||
class SubsonicApiGetCoverArtTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle api error response`() {
|
||||
mockWebServerRule.enqueueResponse("generic_error_response.json")
|
||||
|
||||
val response = client.getCoverArt("some-id")
|
||||
|
||||
with(response) {
|
||||
stream `should be` null
|
||||
responseHttpCode `should equal to` 200
|
||||
apiError `should equal` SubsonicError.GENERIC
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle server error`() {
|
||||
val httpErrorCode = 404
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse().setResponseCode(httpErrorCode))
|
||||
|
||||
val response = client.getCoverArt("some-id")
|
||||
|
||||
with(response) {
|
||||
stream `should be` null
|
||||
responseHttpCode `should equal` 404
|
||||
apiError `should be` null
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should return successful call stream`() {
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse()
|
||||
.setBody(mockWebServerRule.loadJsonResponse("ping_ok.json")))
|
||||
|
||||
val response = client.getCoverArt("some-id")
|
||||
|
||||
with(response) {
|
||||
responseHttpCode `should equal to` 200
|
||||
apiError `should be` null
|
||||
stream `should not be` null
|
||||
val expectedContent = mockWebServerRule.loadJsonResponse("ping_ok.json")
|
||||
stream!!.bufferedReader().readText() `should equal to` expectedContent
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id as parameter`() {
|
||||
val id = "ca123994"
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json", id) {
|
||||
client.api.getCoverArt(id).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass size as a parameter`() {
|
||||
val size = 45600L
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json", size.toString()) {
|
||||
client.api.getCoverArt("some-id", size).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.Genre
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIDefinition.getGenres] call.
|
||||
*/
|
||||
class SubsonicApiGetGenresTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getGenres().execute()
|
||||
}
|
||||
|
||||
response.genresList `should equal` emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_genres_ok.json")
|
||||
|
||||
val response = client.api.getGenres().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().genresList) {
|
||||
size `should equal to` 5
|
||||
this[0] `should equal` Genre(1186, 103, "Rock")
|
||||
this[1] `should equal` Genre(896, 72, "Electronic")
|
||||
this[2] `should equal` Genre(790, 59, "Alternative Rock")
|
||||
this[3] `should equal` Genre(622, 97, "Trance")
|
||||
this[4] `should equal` Genre(476, 36, "Hard Rock")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.Artist
|
||||
import org.moire.ultrasonic.api.subsonic.models.Index
|
||||
import org.moire.ultrasonic.api.subsonic.models.Indexes
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for getIndexes() request.
|
||||
*/
|
||||
class SubsonicApiGetIndexesTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should parse get indexes ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_indexes_ok.json")
|
||||
|
||||
val response = client.api.getIndexes(null, null).execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
response.body().indexes `should not be` null
|
||||
with(response.body().indexes) {
|
||||
lastModified `should equal` 1491069027523
|
||||
ignoredArticles `should equal` "The El La Los Las Le Les"
|
||||
shortcutList `should equal` listOf(
|
||||
Artist(id = "889", name = "podcasts"),
|
||||
Artist(id = "890", name = "audiobooks")
|
||||
)
|
||||
indexList `should equal` mutableListOf(
|
||||
Index("A", listOf(
|
||||
Artist(id = "50", name = "Ace Of Base",
|
||||
starred = parseDate("2017-04-02T20:16:29.815Z")),
|
||||
Artist(id = "379", name = "A Perfect Circle")
|
||||
)),
|
||||
Index("H", listOf(
|
||||
Artist(id = "299", name = "Haddaway"),
|
||||
Artist(id = "297", name = "Halestorm")
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should add music folder id as a query param for getIndexes api call`() {
|
||||
val musicFolderId = "9"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_indexes_ok.json",
|
||||
expectedParam = "musicFolderId=$musicFolderId") {
|
||||
client.api.getIndexes(musicFolderId, null).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should add ifModifiedSince as a query param for getIndexes api call`() {
|
||||
val ifModifiedSince = System.currentTimeMillis()
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_indexes_ok.json",
|
||||
expectedParam = "ifModifiedSince=$ifModifiedSince") {
|
||||
client.api.getIndexes(null, ifModifiedSince).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse get indexes error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getIndexes(null, null).execute()
|
||||
}
|
||||
|
||||
response.indexes `should not be` null
|
||||
response.indexes `should equal` Indexes()
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.License
|
||||
|
||||
/**
|
||||
* Integration test [SubsonicAPIClient] for getLicense() request.
|
||||
*/
|
||||
class SubsonicApiGetLicenseTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should parse get license ok response`() {
|
||||
mockWebServerRule.enqueueResponse("license_ok.json")
|
||||
|
||||
val response = client.api.getLicense().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body()) {
|
||||
assertBaseResponseOk()
|
||||
license `should equal` License(valid = true,
|
||||
trialExpires = parseDate("2016-11-23T20:17:15.206Z"),
|
||||
email = "someone@example.net",
|
||||
licenseExpires = parseDate("8994-08-17T07:12:55.807Z"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse get license error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getLicense().execute()
|
||||
}
|
||||
|
||||
response.license `should not be` null
|
||||
response.license.email `should equal` License().email
|
||||
response.license.valid `should equal` License().valid
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for getLyrics() call.
|
||||
*/
|
||||
class SubsonicApiGetLyricsTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getLyrics().execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_lyrics_ok.json")
|
||||
|
||||
val response = client.api.getLyrics().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().lyrics) {
|
||||
artist `should equal to` "Amorphis"
|
||||
title `should equal to` "Alone"
|
||||
text `should equal to` "Tear dimmed rememberance\nIn a womb of time\nBreath upon " +
|
||||
"me\nPossessed by the"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass artist param in request`() {
|
||||
val artist = "some-artist"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_lyrics_ok.json",
|
||||
expectedParam = "artist=$artist") {
|
||||
client.api.getLyrics(artist = artist).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass title param in request`() {
|
||||
val title = "some-title"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_lyrics_ok.json",
|
||||
expectedParam = "title=$title") {
|
||||
client.api.getLyrics(title = title).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectory
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for getMusicDirectory request.
|
||||
*/
|
||||
class SubsonicApiGetMusicDirectoryTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should parse getMusicDirectory error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getMusicDirectory("1").execute()
|
||||
}
|
||||
|
||||
response.musicDirectory `should not be` null
|
||||
response.musicDirectory `should equal` MusicDirectory()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GetMusicDirectory should add directory id to query params`() {
|
||||
val directoryId = "124"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_music_directory_ok.json",
|
||||
expectedParam = "id=$directoryId") {
|
||||
client.api.getMusicDirectory(directoryId).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse get music directory ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_music_directory_ok.json")
|
||||
|
||||
val response = client.api.getMusicDirectory("1").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
|
||||
response.body().musicDirectory `should not be` null
|
||||
with(response.body().musicDirectory) {
|
||||
id `should equal to` "4836"
|
||||
parent `should equal to` "300"
|
||||
name `should equal` "12 Stones"
|
||||
userRating `should equal to` 5
|
||||
averageRating `should equal to` 5.0f
|
||||
starred `should equal` null
|
||||
playCount `should equal to` 1
|
||||
childList.size `should be` 2
|
||||
childList[0] `should equal` MusicDirectoryChild(id = "4844", parent = "4836", isDir = false,
|
||||
title = "Crash", album = "12 Stones", artist = "12 Stones", track = 1, year = 2002,
|
||||
genre = "Alternative Rock", coverArt = "4836", size = 5348318L,
|
||||
contentType = "audio/mpeg", suffix = "mp3", duration = 222, bitRate = 192,
|
||||
path = "12 Stones/12 Stones/01 Crash.mp3", isVideo = false, playCount = 0,
|
||||
discNumber = 1, created = parseDate("2016-10-23T15:19:10.000Z"),
|
||||
albumId = "454", artistId = "288", type = "music")
|
||||
childList[1] `should equal` MusicDirectoryChild(id = "4845", parent = "4836", isDir = false,
|
||||
title = "Broken", album = "12 Stones", artist = "12 Stones", track = 2, year = 2002,
|
||||
genre = "Alternative Rock", coverArt = "4836", size = 4309043L,
|
||||
contentType = "audio/mpeg", suffix = "mp3", duration = 179, bitRate = 192,
|
||||
path = "12 Stones/12 Stones/02 Broken.mp3", isVideo = false, playCount = 0,
|
||||
discNumber = 1, created = parseDate("2016-10-23T15:19:09.000Z"),
|
||||
albumId = "454", artistId = "288", type = "music")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicFolder
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for getMusicFolders() request.
|
||||
*/
|
||||
class SubsonicApiGetMusicFoldersTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should parse get music folders ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_music_folders_ok.json")
|
||||
|
||||
val response = client.api.getMusicFolders().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body()) {
|
||||
assertBaseResponseOk()
|
||||
musicFolders `should equal` listOf(
|
||||
MusicFolder("0", "Music"),
|
||||
MusicFolder("2", "Test"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse get music folders error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getMusicFolders().execute()
|
||||
}
|
||||
|
||||
response.musicFolders `should equal` emptyList()
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
import org.moire.ultrasonic.api.subsonic.models.Playlist
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for getPlaylist call.
|
||||
*/
|
||||
class SubsonicApiGetPlaylistTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should parse error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getPlaylist("10").execute()
|
||||
}
|
||||
|
||||
response.playlist `should not be` null
|
||||
response.playlist `should equal` Playlist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_playlist_ok.json")
|
||||
|
||||
val response = client.api.getPlaylist("4").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().playlist) {
|
||||
id `should equal to` "0"
|
||||
name `should equal to` "Aug 27, 2017 11:17 AM"
|
||||
owner `should equal to` "admin"
|
||||
public `should equal to` false
|
||||
songCount `should equal to` 16
|
||||
duration `should equal to` 3573
|
||||
created `should equal` parseDate("2017-08-27T11:17:26.216Z")
|
||||
changed `should equal` parseDate("2017-08-27T11:17:26.218Z")
|
||||
coverArt `should equal to` "pl-0"
|
||||
entriesList.size `should equal to` 2
|
||||
entriesList[1] `should equal` MusicDirectoryChild(id = "4215", parent = "4186",
|
||||
isDir = false, title = "Going to Hell", album = "Going to Hell",
|
||||
artist = "The Pretty Reckless", track = 2, year = 2014,
|
||||
genre = "Hard Rock", coverArt = "4186", size = 11089627,
|
||||
contentType = "audio/mpeg", suffix = "mp3", duration = 277, bitRate = 320,
|
||||
path = "The Pretty Reckless/Going to Hell/02 Going to Hell.mp3",
|
||||
isVideo = false, playCount = 0, discNumber = 1,
|
||||
created = parseDate("2016-10-23T21:30:41.000Z"),
|
||||
albumId = "388", artistId = "238", type = "music")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id as request param`() {
|
||||
val playlistId = "453"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_playlist_ok.json",
|
||||
expectedParam = "id=$playlistId") {
|
||||
client.api.getPlaylist(playlistId).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.Playlist
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for getPlaylists call.
|
||||
*/
|
||||
class SubsonicApiGetPlaylistsTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should parse error call`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getPlaylists().execute()
|
||||
}
|
||||
|
||||
response.playlists `should not be` null
|
||||
response.playlists `should equal` emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_playlists_ok.json")
|
||||
|
||||
val response = client.api.getPlaylists().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().playlists) {
|
||||
size `should equal to` 1
|
||||
this[0] `should equal` Playlist(id = "0", name = "Aug 27, 2017 11:17 AM",
|
||||
owner = "admin", public = false, songCount = 16, duration = 3573,
|
||||
comment = "Some comment",
|
||||
created = parseDate("2017-08-27T11:17:26.216Z"),
|
||||
changed = parseDate("2017-08-27T11:17:26.218Z"),
|
||||
coverArt = "pl-0")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass username as a parameter`() {
|
||||
val username = "SomeUsername"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_playlists_ok.json",
|
||||
expectedParam = "username=$username") {
|
||||
client.api.getPlaylists(username = username).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for getPodcasts call.
|
||||
*/
|
||||
class SubsonicApiGetPodcastsTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getPodcasts().execute()
|
||||
}
|
||||
|
||||
response.podcastChannels `should not be` null
|
||||
response.podcastChannels `should equal` emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_podcasts_ok.json")
|
||||
|
||||
val response = client.api.getPodcasts().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
val podcastChannelsList = response.body().podcastChannels
|
||||
podcastChannelsList.size `should equal to` 1
|
||||
with(podcastChannelsList[0]) {
|
||||
id `should equal to` "2"
|
||||
url `should equal to` "http://feeds.codenewbie.org/cnpodcast.xml"
|
||||
title `should equal to` "CodeNewbie"
|
||||
description `should equal to` "Stories and interviews from people on their coding journey."
|
||||
coverArt `should equal to` "pod-2"
|
||||
originalImageUrl `should equal to` "http://codenewbie.blubrry.com/wp-content/uploads/powerpress/220808.jpg"
|
||||
status `should equal to` "completed"
|
||||
errorMessage `should equal to` ""
|
||||
episodeList.size `should equal to` 10
|
||||
episodeList[0] `should equal` MusicDirectoryChild(id = "148", parent = "9959", isDir = false,
|
||||
title = "S1:EP3 – How to teach yourself computer science (Vaidehi Joshi)",
|
||||
album = "CodeNewbie", artist = "podcasts", coverArt = "9959",
|
||||
size = 38274221, contentType = "audio/mpeg", suffix = "mp3",
|
||||
duration = 2397, bitRate = 128, isVideo = false, playCount = 0,
|
||||
created = parseDate("2017-08-30T09:33:39.000Z"), type = "podcast",
|
||||
streamId = "9982", channelId = "2",
|
||||
description = "Vaidehi decided to take on a year-long challenge. " +
|
||||
"She'd pick a computer science topic every week, do tons of research " +
|
||||
"and write a technical blog post explaining it in simple terms and " +
|
||||
"beautiful illustrations. And then she actually did it. She tells us " +
|
||||
"about her project, basecs, how it's changed her as a developer, and " +
|
||||
"how she handles the trolls and negativity from people who don't " +
|
||||
"appreciate her work. Show Notes Technical Writer position at " +
|
||||
"CodeNewbie basecs 100 Days of Code Conway's Game of Life Hexes and " +
|
||||
"Other Magical Numbers (Vaidehi's blog post) Bits, Bytes, Building " +
|
||||
"With Binary (Vaidehi's blog post) Rust",
|
||||
status = "completed", publishDate = parseDate("2017-08-29T00:01:01.000Z"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass include episodes in request`() {
|
||||
val includeEpisodes = true
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_podcasts_ok.json",
|
||||
expectedParam = "includeEpisodes=$includeEpisodes") {
|
||||
client.api.getPodcasts(includeEpisodes = includeEpisodes).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id in request param`() {
|
||||
val id = "249"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_podcasts_ok.json",
|
||||
expectedParam = "id=$id") {
|
||||
client.api.getPodcasts(id = id).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for getRandomSongs call.
|
||||
*/
|
||||
class SubsonicApiGetRandomSongsTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getRandomSongs().execute()
|
||||
}
|
||||
|
||||
response.songsList `should equal` emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_random_songs_ok.json")
|
||||
|
||||
val response = client.api.getRandomSongs().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().songsList) {
|
||||
size `should equal to` 3
|
||||
this[1] `should equal` MusicDirectoryChild(id = "3061", parent = "3050", isDir = false,
|
||||
title = "Sure as Hell", album = "Who Are You Now?", artist = "This Providence",
|
||||
track = 1, year = 2009, genre = "Indie Rock", coverArt = "3050",
|
||||
size = 1969692, contentType = "audio/mpeg", suffix = "mp3", duration = 110,
|
||||
bitRate = 142, path = "This Providence/Who Are You Now_/01 Sure as Hell.mp3",
|
||||
isVideo = false, playCount = 0, discNumber = 1,
|
||||
created = parseDate("2016-10-23T21:32:46.000Z"), albumId = "272",
|
||||
artistId = "152", type = "music")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass size in request param`() {
|
||||
val size = 384433
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_random_songs_ok.json",
|
||||
expectedParam = "size=$size") {
|
||||
client.api.getRandomSongs(size = size).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass genre in request param`() {
|
||||
val genre = "PostRock"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_random_songs_ok.json",
|
||||
expectedParam = "genre=$genre") {
|
||||
client.api.getRandomSongs(genre = genre).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass from year in request param`() {
|
||||
val fromYear = 1919
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_random_songs_ok.json",
|
||||
expectedParam = "fromYear=$fromYear") {
|
||||
client.api.getRandomSongs(fromYear = fromYear).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass to year in request params`() {
|
||||
val toYear = 2012
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_random_songs_ok.json",
|
||||
expectedParam = "toYear=$toYear") {
|
||||
client.api.getRandomSongs(toYear = toYear).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass music folder id in request param`() {
|
||||
val musicFolderId = "4919"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_random_songs_ok.json",
|
||||
expectedParam = "musicFolderId=$musicFolderId") {
|
||||
client.api.getRandomSongs(musicFolderId = musicFolderId).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIDefinition.getShares] call.
|
||||
*/
|
||||
class SubsonicApiGetSharesTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getShares().execute()
|
||||
}
|
||||
|
||||
response.shares `should equal` emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_shares_ok.json")
|
||||
|
||||
val response = client.api.getShares().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
response.body().shares.size `should equal to` 1
|
||||
with(response.body().shares[0]) {
|
||||
id `should equal to` "0"
|
||||
url `should equal to` "https://subsonic.com/ext/share/awdwo?jwt=eyJhbGciOiJIUzI1" +
|
||||
"NiJ9.eyJwYXRoIjoiL2V4dC9zaGFyZS9hd2R3byIsImV4cCI6MTU0MTYyNjQzMX0.iy8dkt_ZZc8" +
|
||||
"hJ692UxorHdHWFU2RB-fMCmCA4IJ_dTw"
|
||||
username `should equal to` "admin"
|
||||
created `should equal` parseDate("2017-11-07T21:33:51.748Z")
|
||||
expires `should equal` parseDate("2018-11-07T21:33:51.748Z")
|
||||
lastVisited `should equal` parseDate("2018-11-07T21:33:51.748Z")
|
||||
visitCount `should equal to` 0
|
||||
description `should equal to` "Awesome link!"
|
||||
items.size `should equal to` 1
|
||||
items[0] `should equal` MusicDirectoryChild(id = "4212", parent = "4186", isDir = false,
|
||||
title = "Heaven Knows", album = "Going to Hell", artist = "The Pretty Reckless",
|
||||
track = 3, year = 2014, genre = "Hard Rock", coverArt = "4186", size = 9025090,
|
||||
contentType = "audio/mpeg", suffix = "mp3", duration = 225, bitRate = 320,
|
||||
path = "The Pretty Reckless/Going to Hell/03 Heaven Knows.mp3", isVideo = false,
|
||||
playCount = 2, discNumber = 1, created = parseDate("2016-10-23T21:30:40.000Z"),
|
||||
albumId = "388", artistId = "238", type = "music")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIDefinition.getSongsByGenre] call.
|
||||
*/
|
||||
class SubsonicApiGetSongsByGenreTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getSongsByGenre("Metal").execute()
|
||||
}
|
||||
|
||||
response.songsList `should equal` emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_songs_by_genre_ok.json")
|
||||
|
||||
val response = client.api.getSongsByGenre("Trance").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
response.body().songsList.size `should equal to` 2
|
||||
with(response.body().songsList) {
|
||||
this[0] `should equal` MusicDirectoryChild(id = "575", parent = "576", isDir = false,
|
||||
title = "Time Machine (Vadim Zhukov Remix)", album = "668",
|
||||
artist = "Tasadi", year = 2008, genre = "Trance", size = 22467672,
|
||||
contentType = "audio/mpeg", suffix = "mp3", duration = 561, bitRate = 320,
|
||||
path = "Tasadi/668/00 Time Machine (Vadim Zhukov Remix).mp3",
|
||||
isVideo = false, playCount = 0, created = parseDate("2016-10-23T21:58:29.000Z"),
|
||||
albumId = "0", artistId = "0", type = "music")
|
||||
this[1] `should equal` MusicDirectoryChild(id = "621", parent = "622", isDir = false,
|
||||
title = "My Heart (Vadim Zhukov Remix)", album = "668",
|
||||
artist = "DJ Polyakov PPK Feat Kate Cameron", year = 2009, genre = "Trance",
|
||||
size = 26805932, contentType = "audio/mpeg", suffix = "mp3", duration = 670,
|
||||
bitRate = 320,
|
||||
path = "DJ Polyakov PPK Feat Kate Cameron/668/00 My Heart (Vadim Zhukov Remix).mp3",
|
||||
isVideo = false, playCount = 2, created = parseDate("2016-10-23T21:58:29.000Z"),
|
||||
albumId = "5", artistId = "4", type = "music")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass genre in request param`() {
|
||||
val genre = "Rock"
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "genre=$genre") {
|
||||
client.api.getSongsByGenre(genre = genre).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass count in request param`() {
|
||||
val count = 494
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "count=$count") {
|
||||
client.api.getSongsByGenre("Trance", count = count).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass offset in request param`() {
|
||||
val offset = 31
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "offset=$offset") {
|
||||
client.api.getSongsByGenre("Trance", offset = offset).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass music folder id in request param`() {
|
||||
val musicFolderId = "1010"
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "musicFolderId=$musicFolderId") {
|
||||
client.api.getSongsByGenre("Trance", musicFolderId = musicFolderId).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.Artist
|
||||
import org.moire.ultrasonic.api.subsonic.models.SearchTwoResult
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for getStarred2 call.
|
||||
*/
|
||||
@Suppress("NamingConventionViolation")
|
||||
class SubsonicApiGetStarred2Test : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getStarred2().execute()
|
||||
}
|
||||
|
||||
response.starred2 `should equal` SearchTwoResult()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok reponse`() {
|
||||
mockWebServerRule.enqueueResponse("get_starred_2_ok.json")
|
||||
|
||||
val response = client.api.getStarred2().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().starred2) {
|
||||
albumList `should equal` emptyList()
|
||||
artistList.size `should equal to` 1
|
||||
artistList[0] `should equal` Artist(id = "364", name = "Parov Stelar",
|
||||
starred = parseDate("2017-08-12T18:32:58.768Z"))
|
||||
songList `should equal` emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass music folder id in request param`() {
|
||||
val musicFolderId = "441"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_starred_2_ok.json",
|
||||
expectedParam = "musicFolderId=$musicFolderId") {
|
||||
client.api.getStarred2(musicFolderId = musicFolderId).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.Artist
|
||||
import org.moire.ultrasonic.api.subsonic.models.SearchTwoResult
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for getStarred call.
|
||||
*/
|
||||
class SubsonicApiGetStarredTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getStarred().execute()
|
||||
}
|
||||
|
||||
response.starred `should equal` SearchTwoResult()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok reponse`() {
|
||||
mockWebServerRule.enqueueResponse("get_starred_ok.json")
|
||||
|
||||
val response = client.api.getStarred().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().starred) {
|
||||
albumList `should equal` emptyList()
|
||||
artistList.size `should equal to` 1
|
||||
artistList[0] `should equal` Artist(id = "364", name = "Parov Stelar",
|
||||
starred = parseDate("2017-08-12T18:32:58.768Z"))
|
||||
songList `should equal` emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass music folder id in request param`() {
|
||||
val musicFolderId = "441"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "get_starred_ok.json",
|
||||
expectedParam = "musicFolderId=$musicFolderId") {
|
||||
client.api.getStarred(musicFolderId = musicFolderId).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.User
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIDefinition.getUser] call.
|
||||
*/
|
||||
class SubsonicApiGetUserTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getUser("some").execute()
|
||||
}
|
||||
|
||||
response.user `should equal` User()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_user_ok.json")
|
||||
|
||||
val response = client.api.getUser("some").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().user) {
|
||||
username `should equal to` "GodOfUniverse"
|
||||
email `should equal to` "some.mail@example.com"
|
||||
scrobblingEnabled `should equal to` false
|
||||
adminRole `should equal to` true
|
||||
settingsRole `should equal to` true
|
||||
downloadRole `should equal to` true
|
||||
uploadRole `should equal to` true
|
||||
playlistRole `should equal to` true
|
||||
coverArtRole `should equal to` true
|
||||
commentRole `should equal to` true
|
||||
podcastRole `should equal to` true
|
||||
streamRole `should equal to` true
|
||||
jukeboxRole `should equal to` true
|
||||
shareRole `should equal to` true
|
||||
videoConverstionRole `should equal to` false
|
||||
folderList.size `should equal to` 1
|
||||
folderList[0] `should equal to` 0
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass username in request param`() {
|
||||
val username = "Mighty"
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "username=$username") {
|
||||
client.api.getUser(username).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIDefinition.getVideos] call.
|
||||
*/
|
||||
class SubsonicApiGetVideosListTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.getVideos().execute()
|
||||
}
|
||||
|
||||
response.videosList `should equal` emptyList()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("get_videos_ok.json")
|
||||
|
||||
val response = client.api.getVideos().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().videosList) {
|
||||
size `should equal to` 1
|
||||
this[0] `should equal` MusicDirectoryChild(id = "10402", parent = "10401", isDir = false,
|
||||
title = "MVI_0512", album = "Incoming", size = 21889646,
|
||||
contentType = "video/avi", suffix = "avi", transcodedContentType = "video/x-flv",
|
||||
transcodedSuffix = "flv", path = "Incoming/MVI_0512.avi", isVideo = true,
|
||||
playCount = 0, created = parseDate("2017-11-19T12:34:33.000Z"), type = "video")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.JukeboxAction
|
||||
import org.moire.ultrasonic.api.subsonic.models.JukeboxAction.GET
|
||||
import org.moire.ultrasonic.api.subsonic.models.JukeboxAction.STATUS
|
||||
import org.moire.ultrasonic.api.subsonic.models.JukeboxStatus
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIDefinition.jukeboxControl] call.
|
||||
*/
|
||||
class SubsonicApiJukeboxControlTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.jukeboxControl(GET).execute()
|
||||
}
|
||||
|
||||
response.jukebox `should equal` JukeboxStatus()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response with jukebox status`() {
|
||||
mockWebServerRule.enqueueResponse("jukebox_control_status_ok.json")
|
||||
|
||||
val response = client.api.jukeboxControl(STATUS).execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().jukebox) {
|
||||
currentIndex `should equal to` 94
|
||||
playing `should equal to` true
|
||||
gain `should equal to` 0.32f
|
||||
position `should equal to` 3
|
||||
playlistEntries `should equal` emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response with jukebox playlist`() {
|
||||
mockWebServerRule.enqueueResponse("jukebox_control_playlist_ok.json")
|
||||
|
||||
val response = client.api.jukeboxControl(GET).execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().jukebox) {
|
||||
currentIndex `should equal to` 887
|
||||
playing `should equal to` false
|
||||
gain `should equal to` 0.88f
|
||||
position `should equal to` 2
|
||||
playlistEntries.size `should equal to` 2
|
||||
playlistEntries[1] `should equal` MusicDirectoryChild(id = "4215", parent = "4186",
|
||||
isDir = false, title = "Going to Hell", album = "Going to Hell",
|
||||
artist = "The Pretty Reckless", track = 2, year = 2014, genre = "Hard Rock",
|
||||
coverArt = "4186", size = 11089627, contentType = "audio/mpeg",
|
||||
suffix = "mp3", duration = 277, bitRate = 320,
|
||||
path = "The Pretty Reckless/Going to Hell/02 Going to Hell.mp3", isVideo = false,
|
||||
playCount = 0, discNumber = 1,
|
||||
created = parseDate("2016-10-23T21:30:41.000Z"), albumId = "388",
|
||||
artistId = "238", type = "music")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass action in request params`() {
|
||||
val action = JukeboxAction.SET_GAIN
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "action=$action") {
|
||||
client.api.jukeboxControl(action).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass index in request params`() {
|
||||
val index = 440
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "index=$index") {
|
||||
client.api.jukeboxControl(GET, index = index).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass offset in request params`() {
|
||||
val offset = 58223
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "offset=$offset") {
|
||||
client.api.jukeboxControl(GET, offset = offset).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass ids in request params`() {
|
||||
val id = listOf("some-id1", "some-id2")
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "id=${id[0]}&id=${id[1]}") {
|
||||
client.api.jukeboxControl(GET, ids = id).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass gain in request params`() {
|
||||
val gain = 0.73f
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "gain=$gain") {
|
||||
client.api.jukeboxControl(GET, gain = gain).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should contain`
|
||||
import org.amshove.kluent.`should not contain`
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] that checks proper user password handling.
|
||||
*/
|
||||
class SubsonicApiPasswordTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should pass PasswordMD5Interceptor in query params for api version 1 13 0`() {
|
||||
val clientV12 = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(), USERNAME,
|
||||
PASSWORD, SubsonicAPIVersions.V1_14_0, CLIENT_ID)
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
clientV12.api.ping().execute()
|
||||
|
||||
with(mockWebServerRule.mockWebServer.takeRequest()) {
|
||||
requestLine `should contain` "&s="
|
||||
requestLine `should contain` "&t="
|
||||
requestLine `should not contain` "&p=enc:"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass PasswordHexInterceptor in query params for api version 1 12 0`() {
|
||||
val clientV11 = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(), USERNAME,
|
||||
PASSWORD, SubsonicAPIVersions.V1_12_0, CLIENT_ID)
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
clientV11.api.ping().execute()
|
||||
|
||||
with(mockWebServerRule.mockWebServer.takeRequest()) {
|
||||
requestLine `should not contain` "&s="
|
||||
requestLine `should not contain` "&t="
|
||||
requestLine `should contain` "&p=enc:"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] that checks ping api call.
|
||||
*/
|
||||
class SubsonicApiPingRequestTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should parse ping ok response`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
val response = client.api.ping().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body()) {
|
||||
assertBaseResponseOk()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse ping error response`() {
|
||||
checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.ping().execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.junit.Test
|
||||
import java.util.Calendar
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for scrobble call.
|
||||
*/
|
||||
class SubsonicApiScrobbleTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.scrobble("id").execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
val response = client.api.scrobble("id").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id param in request`() {
|
||||
val id = "some-id"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "id=$id") {
|
||||
client.api.scrobble(id = id).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass time param in request`() {
|
||||
val time = Calendar.getInstance().timeInMillis
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "time=$time") {
|
||||
client.api.scrobble(id = "some-id", time = time).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass submission param in request`() {
|
||||
val submission = false
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "submission=$submission") {
|
||||
client.api.scrobble(id = "some-id", submission = submission).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
import org.moire.ultrasonic.api.subsonic.models.SearchResult
|
||||
import java.util.Calendar
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for search call.
|
||||
*/
|
||||
class SubsonicApiSearchTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should parse error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.search().execute()
|
||||
}
|
||||
|
||||
response.searchResult `should not be` null
|
||||
response.searchResult `should equal` SearchResult()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse ok response`() {
|
||||
mockWebServerRule.enqueueResponse("search_ok.json")
|
||||
|
||||
val response = client.api.search().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().searchResult) {
|
||||
offset `should equal to` 10
|
||||
totalHits `should equal to` 53
|
||||
matchList.size `should equal to` 1
|
||||
matchList[0] `should equal` MusicDirectoryChild(id = "5831", parent = "5766",
|
||||
isDir = false, title = "You'll Be Under My Wheels",
|
||||
album = "Need for Speed Most Wanted", artist = "The Prodigy",
|
||||
track = 17, year = 2005, genre = "Rap", coverArt = "5766",
|
||||
size = 5607024, contentType = "audio/mpeg", suffix = "mp3", duration = 233,
|
||||
bitRate = 192,
|
||||
path = "Compilations/Need for Speed Most Wanted/17 You'll Be Under My Wheels.mp3",
|
||||
isVideo = false, playCount = 0, discNumber = 1,
|
||||
created = parseDate("2016-10-23T20:09:02.000Z"), albumId = "568",
|
||||
artistId = "505", type = "music")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass artist param`() {
|
||||
val artist = "some-artist"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search_ok.json",
|
||||
expectedParam = "artist=$artist") {
|
||||
client.api.search(artist = artist).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass album param`() {
|
||||
val album = "some-album"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search_ok.json",
|
||||
expectedParam = "album=$album") {
|
||||
client.api.search(album = album).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass title param`() {
|
||||
val title = "some-title"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search_ok.json",
|
||||
expectedParam = "title=$title") {
|
||||
client.api.search(title = title).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should contain any param`() {
|
||||
val any = "AnyString"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search_ok.json",
|
||||
expectedParam = "any=$any") {
|
||||
client.api.search(any = any).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should contain count param`() {
|
||||
val count = 11
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search_ok.json",
|
||||
expectedParam = "count=$count") {
|
||||
client.api.search(count = count).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should contain offset param`() {
|
||||
val offset = 54
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search_ok.json",
|
||||
expectedParam = "offset=$offset") {
|
||||
client.api.search(offset = offset).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should contain newerThan param`() {
|
||||
val newerThan = Calendar.getInstance()
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search_ok.json",
|
||||
expectedParam = "newerThan=${newerThan.time.time}") {
|
||||
client.api.search(newerThan = newerThan.time.time).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.Album
|
||||
import org.moire.ultrasonic.api.subsonic.models.Artist
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
import org.moire.ultrasonic.api.subsonic.models.SearchThreeResult
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for search3 call.
|
||||
*/
|
||||
class SubsonicApiSearchThreeTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should parse error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.search3("some-query").execute()
|
||||
}
|
||||
|
||||
response.searchResult `should not be` null
|
||||
response.searchResult `should equal` SearchThreeResult()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse ok response`() {
|
||||
mockWebServerRule.enqueueResponse("search3_ok.json")
|
||||
|
||||
val response = client.api.search3("some-query").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().searchResult) {
|
||||
artistList.size `should equal to` 1
|
||||
artistList[0] `should equal` Artist(id = "505", name = "The Prodigy", coverArt = "ar-505",
|
||||
albumCount = 5)
|
||||
albumList.size `should equal to` 1
|
||||
albumList[0] `should equal` Album(id = "855", name = "Always Outnumbered, Never Outgunned",
|
||||
artist = "The Prodigy", artistId = "505", coverArt = "al-855", songCount = 12,
|
||||
duration = 3313, created = parseDate("2016-10-23T20:57:27.000Z"),
|
||||
year = 2004, genre = "Electronic")
|
||||
songList.size `should equal to` 1
|
||||
songList[0] `should equal` MusicDirectoryChild(id = "5831", parent = "5766", isDir = false,
|
||||
title = "You'll Be Under My Wheels", album = "Need for Speed Most Wanted",
|
||||
artist = "The Prodigy", track = 17, year = 2005, genre = "Rap",
|
||||
coverArt = "5766", size = 5607024, contentType = "audio/mpeg",
|
||||
suffix = "mp3", duration = 233, bitRate = 192,
|
||||
path = "Compilations/Need for Speed Most Wanted/17 You'll Be Under My Wheels.mp3",
|
||||
isVideo = false, playCount = 0, discNumber = 1,
|
||||
created = parseDate("2016-10-23T20:09:02.000Z"), albumId = "568",
|
||||
artistId = "505", type = "music")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass query as request param`() {
|
||||
val query = "some-wip-query"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search3_ok.json", apiRequest = {
|
||||
client.api.search3(query = query).execute()
|
||||
}, expectedParam = "query=$query")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass artist count as request param`() {
|
||||
val artistCount = 67
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search3_ok.json",
|
||||
expectedParam = "artistCount=$artistCount") {
|
||||
client.api.search3("some", artistCount = artistCount).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass artist offset as request param`() {
|
||||
val artistOffset = 34
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search3_ok.json",
|
||||
expectedParam = "artistOffset=$artistOffset") {
|
||||
client.api.search3("some", artistOffset = artistOffset).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass album count as request param`() {
|
||||
val albumCount = 21
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search3_ok.json",
|
||||
expectedParam = "albumCount=$albumCount") {
|
||||
client.api.search3("some", albumCount = albumCount).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass album offset as request param`() {
|
||||
val albumOffset = 43
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search3_ok.json",
|
||||
expectedParam = "albumOffset=$albumOffset") {
|
||||
client.api.search3("some", albumOffset = albumOffset).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass song count as request param`() {
|
||||
val songCount = 15
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search3_ok.json",
|
||||
expectedParam = "songCount=$songCount") {
|
||||
client.api.search3("some", songCount = songCount).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass music folder id as request param`() {
|
||||
val musicFolderId = "43"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search3_ok.json",
|
||||
expectedParam = "musicFolderId=$musicFolderId") {
|
||||
client.api.search3("some", musicFolderId = musicFolderId).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.models.Artist
|
||||
import org.moire.ultrasonic.api.subsonic.models.MusicDirectoryChild
|
||||
import org.moire.ultrasonic.api.subsonic.models.SearchTwoResult
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for search2 call.
|
||||
*/
|
||||
class SubsonicApiSearchTwoTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
val response = checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.search2("some-query").execute()
|
||||
}
|
||||
|
||||
response.searchResult `should not be` null
|
||||
response.searchResult `should equal` SearchTwoResult()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse ok response`() {
|
||||
mockWebServerRule.enqueueResponse("search2_ok.json")
|
||||
|
||||
val response = client.api.search2("some-query").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
with(response.body().searchResult) {
|
||||
artistList.size `should equal to` 1
|
||||
artistList[0] `should equal` Artist(id = "522", name = "The Prodigy")
|
||||
albumList.size `should equal to` 1
|
||||
albumList[0] `should equal` MusicDirectoryChild(id = "8867", parent = "522", isDir = true,
|
||||
title = "Always Outnumbered, Never Outgunned",
|
||||
album = "Always Outnumbered, Never Outgunned", artist = "The Prodigy",
|
||||
year = 2004, genre = "Electronic", coverArt = "8867", playCount = 0,
|
||||
created = parseDate("2016-10-23T20:57:27.000Z"))
|
||||
songList.size `should equal to` 1
|
||||
songList[0] `should equal` MusicDirectoryChild(id = "5831", parent = "5766", isDir = false,
|
||||
title = "You'll Be Under My Wheels", album = "Need for Speed Most Wanted",
|
||||
artist = "The Prodigy", track = 17, year = 2005, genre = "Rap",
|
||||
coverArt = "5766", size = 5607024, contentType = "audio/mpeg",
|
||||
suffix = "mp3", duration = 233, bitRate = 192,
|
||||
path = "Compilations/Need for Speed Most Wanted/17 You'll Be Under My Wheels.mp3",
|
||||
isVideo = false, playCount = 0, discNumber = 1,
|
||||
created = parseDate("2016-10-23T20:09:02.000Z"),
|
||||
albumId = "568", artistId = "505", type = "music")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass query id in request param`() {
|
||||
val query = "some"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search2_ok.json",
|
||||
expectedParam = "query=$query") {
|
||||
client.api.search2(query).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass artist count in request param`() {
|
||||
val artistCount = 45
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search2_ok.json",
|
||||
expectedParam = "artistCount=$artistCount") {
|
||||
client.api.search2("some", artistCount = artistCount).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass artist offset in request param`() {
|
||||
val artistOffset = 13
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search2_ok.json",
|
||||
expectedParam = "artistOffset=$artistOffset") {
|
||||
client.api.search2("some", artistOffset = artistOffset).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass album count in request param`() {
|
||||
val albumCount = 30
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search2_ok.json",
|
||||
expectedParam = "albumCount=$albumCount") {
|
||||
client.api.search2("some", albumCount = albumCount).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass album offset in request param`() {
|
||||
val albumOffset = 91
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search2_ok.json",
|
||||
expectedParam = "albumOffset=$albumOffset") {
|
||||
client.api.search2("some", albumOffset = albumOffset).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass song count in request param`() {
|
||||
val songCount = 22
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search2_ok.json",
|
||||
expectedParam = "songCount=$songCount") {
|
||||
client.api.search2("some", songCount = songCount).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass music folder id in request param`() {
|
||||
val musicFolderId = "565"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "search2_ok.json",
|
||||
expectedParam = "musicFolderId=$musicFolderId") {
|
||||
client.api.search2("some", musicFolderId = musicFolderId).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.response.SubsonicResponse
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for star request.
|
||||
*/
|
||||
class SubsonicApiStarTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should parse star ok response`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
val response = client.api.star().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
response.body().status `should be` SubsonicResponse.Status.OK
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse star error response`() {
|
||||
checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.star().execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id param`() {
|
||||
val id = "110"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "id=$id") {
|
||||
client.api.star(id = id).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass artist id param`() {
|
||||
val artistId = "123"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "artistId=$artistId") {
|
||||
client.api.star(artistId = artistId).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass album id param`() {
|
||||
val albumId = "1001"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "albumId=$albumId") {
|
||||
client.api.star(albumId = albumId).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.`should not be`
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for [SubsonicAPIDefinition.stream] call.
|
||||
*/
|
||||
class SubsonicApiStreamTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle api error response`() {
|
||||
mockWebServerRule.enqueueResponse("generic_error_response.json")
|
||||
|
||||
val response = client.stream("some-id")
|
||||
|
||||
with(response) {
|
||||
stream `should be` null
|
||||
responseHttpCode `should equal to` 200
|
||||
apiError `should equal` SubsonicError.GENERIC
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle server error`() {
|
||||
val httpErrorCode = 404
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse().setResponseCode(httpErrorCode))
|
||||
|
||||
val response = client.stream("some-id")
|
||||
|
||||
with(response) {
|
||||
stream `should be` null
|
||||
responseHttpCode `should equal to` httpErrorCode
|
||||
apiError `should be` null
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should return successfull call stream`() {
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse()
|
||||
.setBody(mockWebServerRule.loadJsonResponse("ping_ok.json")))
|
||||
|
||||
val response = client.stream("some-id")
|
||||
|
||||
with(response) {
|
||||
responseHttpCode `should equal to` 200
|
||||
apiError `should be` null
|
||||
stream `should not be` null
|
||||
val expectedContent = mockWebServerRule.loadJsonResponse("ping_ok.json")
|
||||
stream!!.bufferedReader().readText() `should equal to` expectedContent
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id as parameter`() {
|
||||
val id = "asdo123"
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json", id) {
|
||||
client.api.stream(id = id).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass max bit rate as param`() {
|
||||
val maxBitRate = 360
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json",
|
||||
"maxBitRate=$maxBitRate") {
|
||||
client.api.stream("some-id", maxBitRate = maxBitRate).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass format as param`() {
|
||||
val format = "aac"
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json",
|
||||
"format=$format") {
|
||||
client.api.stream("some-id", format = format).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass time offset as param`() {
|
||||
val timeOffset = 155
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json",
|
||||
"timeOffset=$timeOffset") {
|
||||
client.api.stream("some-id", timeOffset = timeOffset).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass video size as param`() {
|
||||
val videoSize = "44144"
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json",
|
||||
"size=$videoSize") {
|
||||
client.api.stream("some-id", videoSize = videoSize).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass estimate content length as param`() {
|
||||
val estimateContentLength = true
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json",
|
||||
"estimateContentLength=$estimateContentLength") {
|
||||
client.api.stream("some-id", estimateContentLength = estimateContentLength).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass converted as param`() {
|
||||
val converted = false
|
||||
|
||||
mockWebServerRule.assertRequestParam("ping_ok.json",
|
||||
"converted=$converted") {
|
||||
client.api.stream("some-id", converted = converted).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.response.SubsonicResponse
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for unstar call.
|
||||
*/
|
||||
class SubsonicApiUnstarTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should parse ok response`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
val response = client.api.unstar().execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
response.body().status `should be` SubsonicResponse.Status.OK
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should parse error response`() {
|
||||
checkErrorCallParsed(mockWebServerRule, {
|
||||
client.api.unstar().execute()
|
||||
})
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id param`() {
|
||||
val id = "545"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "id=$id") {
|
||||
client.api.unstar(id = id).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass artistId param`() {
|
||||
val artistId = "644"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "artistId=$artistId") {
|
||||
client.api.unstar(artistId = artistId).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass albumId param`() {
|
||||
val albumId = "3344"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "albumId=$albumId") {
|
||||
client.api.unstar(albumId = albumId).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIClient] for updatePlaylist call.
|
||||
*/
|
||||
class SubsonicApiUpdatePlaylistTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.updatePlaylist("10").execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
val response = client.api.updatePlaylist("15").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass playlist id param in request`() {
|
||||
val id = "5453"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "playlistId=$id") {
|
||||
client.api.updatePlaylist(id = id).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass name param in request`() {
|
||||
val name = "some-name"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "name=$name") {
|
||||
client.api.updatePlaylist("22", name = name).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass comment param in request`() {
|
||||
val comment = "some-unusual-comment"
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "comment=$comment") {
|
||||
client.api.updatePlaylist("42", comment = comment).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass public param in request`() {
|
||||
val public = true
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "public=$public") {
|
||||
client.api.updatePlaylist("53", public = public).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass song ids to update in request`() {
|
||||
val songIds = listOf("45", "81")
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "songIdToAdd=${songIds[0]}&songIdToAdd=${songIds[1]}") {
|
||||
client.api.updatePlaylist("25", songIdsToAdd = songIds).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass song indexes to remove in request`() {
|
||||
val songIndexesToRemove = listOf(129, 1)
|
||||
|
||||
mockWebServerRule.assertRequestParam(responseResourceName = "ping_ok.json",
|
||||
expectedParam = "songIndexToRemove=${songIndexesToRemove[0]}&" +
|
||||
"songIndexToRemove=${songIndexesToRemove[1]}") {
|
||||
client.api.updatePlaylist("49", songIndexesToRemove = songIndexesToRemove).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test for [SubsonicAPIDefinition.updateShare] call.
|
||||
*/
|
||||
class SubsonicApiUpdateShareTest : SubsonicAPIClientTest() {
|
||||
@Test
|
||||
fun `Should handle error response`() {
|
||||
checkErrorCallParsed(mockWebServerRule) {
|
||||
client.api.updateShare("11").execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should handle ok response`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
val response = client.api.updateShare("12").execute()
|
||||
|
||||
assertResponseSuccessful(response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass id in request params`() {
|
||||
val id = "4432"
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "id=$id") {
|
||||
client.api.updateShare(id = id).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass description in request params`() {
|
||||
val description = "some-description"
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "description=$description") {
|
||||
client.api.updateShare("123", description = description).execute()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should pass expires in request params`() {
|
||||
val expires = 223123123L
|
||||
|
||||
mockWebServerRule.assertRequestParam(expectedParam = "expires=$expires") {
|
||||
client.api.updateShare("12", expires = expires).execute()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package org.moire.ultrasonic.api.subsonic.interceptors
|
||||
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.moire.ultrasonic.api.subsonic.rules.MockWebServerRule
|
||||
|
||||
/**
|
||||
* Base class for testing [okhttp3.Interceptor] implementations.
|
||||
*/
|
||||
abstract class BaseInterceptorTest {
|
||||
@Rule @JvmField val mockWebServerRule = MockWebServerRule()
|
||||
|
||||
lateinit var client: OkHttpClient
|
||||
|
||||
abstract val interceptor: Interceptor
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
client = OkHttpClient.Builder().addInterceptor(interceptor).build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates [Request] to use with [mockWebServerRule].
|
||||
*
|
||||
* @param additionalParams passes [Request.Builder] to add additionally required
|
||||
* params to the [Request].
|
||||
*/
|
||||
fun createRequest(additionalParams: (Request.Builder) -> Unit): Request = Request.Builder()
|
||||
.url(mockWebServerRule.mockWebServer.url("/"))
|
||||
.also { additionalParams(it) }
|
||||
.build()
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package org.moire.ultrasonic.api.subsonic.interceptors
|
||||
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.amshove.kluent.`should contain`
|
||||
import org.amshove.kluent.`should not contain`
|
||||
import org.apache.commons.codec.binary.Hex
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.PASSWORD
|
||||
|
||||
/**
|
||||
* Integration test for [PasswordHexInterceptor].
|
||||
*/
|
||||
class PasswordHexInterceptorTest : BaseInterceptorTest() {
|
||||
private val password = "some-password"
|
||||
|
||||
override val interceptor: Interceptor get() = PasswordHexInterceptor(password)
|
||||
|
||||
@Test
|
||||
fun `Should pass hex encoded password in query params`() {
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse())
|
||||
val request = createRequest { }
|
||||
|
||||
client.newCall(request).execute()
|
||||
|
||||
with(mockWebServerRule.mockWebServer.takeRequest()) {
|
||||
requestLine `should not contain` "s="
|
||||
requestLine `should not contain` "t="
|
||||
val encodedPassword = String(Hex.encodeHex(PASSWORD.toByteArray(), false))
|
||||
requestLine `should contain` "p=enc:$encodedPassword"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package org.moire.ultrasonic.api.subsonic.interceptors
|
||||
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.amshove.kluent.`should contain`
|
||||
import org.amshove.kluent.`should not contain`
|
||||
import org.apache.commons.codec.binary.Hex
|
||||
import org.junit.Test
|
||||
import java.security.MessageDigest
|
||||
|
||||
/**
|
||||
* Integration test for [PasswordMD5Interceptor].
|
||||
*/
|
||||
class PasswordMD5InterceptorTest : BaseInterceptorTest() {
|
||||
private val password = "some-password"
|
||||
override val interceptor: Interceptor get() = PasswordMD5Interceptor(password)
|
||||
|
||||
@Test
|
||||
fun `Should pass password hash and salt in query params`() {
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse())
|
||||
val request = createRequest { }
|
||||
|
||||
client.newCall(request).execute()
|
||||
|
||||
with(mockWebServerRule.mockWebServer.takeRequest()) {
|
||||
requestLine `should contain` "s="
|
||||
requestLine `should contain` "t="
|
||||
requestLine `should not contain` "p=enc:"
|
||||
|
||||
val salt = requestLine.split('&').find { it.startsWith("s=") }
|
||||
?.substringAfter('=')?.substringBefore(" ")
|
||||
val expectedToken = String(Hex.encodeHex(MessageDigest.getInstance("MD5")
|
||||
.digest("$password$salt".toByteArray()), true))
|
||||
requestLine `should contain` "t=$expectedToken"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package org.moire.ultrasonic.api.subsonic.interceptors
|
||||
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.amshove.kluent.`should contain`
|
||||
import org.amshove.kluent.`should equal to`
|
||||
import org.amshove.kluent.`should not contain`
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Unit test for [RangeHeaderInterceptor].
|
||||
*/
|
||||
class RangeHeaderInterceptorTest : BaseInterceptorTest() {
|
||||
|
||||
override val interceptor: Interceptor
|
||||
get() = RangeHeaderInterceptor()
|
||||
|
||||
@Test
|
||||
fun `Should update uppercase range header`() {
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse())
|
||||
val offset = 111
|
||||
val request = createRequest {
|
||||
it.addHeader("Range", "$offset")
|
||||
}
|
||||
|
||||
client.newCall(request).execute()
|
||||
|
||||
val executedRequest = mockWebServerRule.mockWebServer.takeRequest()
|
||||
executedRequest.headers.names() `should contain` "Range"
|
||||
executedRequest.headers["Range"]!! `should equal to` "bytes=$offset-"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should not add range header if request doesnt contain it`() {
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse())
|
||||
val request = createRequest { }
|
||||
|
||||
client.newCall(request).execute()
|
||||
|
||||
val executedRequest = mockWebServerRule.mockWebServer.takeRequest()
|
||||
executedRequest.headers.names() `should not contain` "Range"
|
||||
executedRequest.headers.names() `should not contain` "range"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should update lowercase range header`() {
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse())
|
||||
val offset = 51233
|
||||
val request = createRequest {
|
||||
it.addHeader("range", "$offset")
|
||||
}
|
||||
|
||||
client.newCall(request).execute()
|
||||
|
||||
val executedRequest = mockWebServerRule.mockWebServer.takeRequest()
|
||||
executedRequest.headers.names() `should contain` "Range"
|
||||
executedRequest.headers["Range"]!! `should equal to` "bytes=$offset-"
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package org.moire.ultrasonic.api.subsonic.interceptors
|
||||
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.amshove.kluent.`should contain`
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.junit.Test
|
||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions
|
||||
import org.moire.ultrasonic.api.subsonic.enqueueResponse
|
||||
import kotlin.LazyThreadSafetyMode.NONE
|
||||
|
||||
/**
|
||||
* Integration test for [VersionInterceptor].
|
||||
*/
|
||||
class VersionInterceptorTest : BaseInterceptorTest() {
|
||||
private val initialProtocolVersion = SubsonicAPIVersions.V1_1_0
|
||||
private var updatedProtocolVersion = SubsonicAPIVersions.V1_1_0
|
||||
|
||||
override val interceptor: Interceptor by lazy(NONE) {
|
||||
VersionInterceptor(initialProtocolVersion) {
|
||||
updatedProtocolVersion = it
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should add initial protocol version to request`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
val request = createRequest {}
|
||||
|
||||
client.newCall(request).execute()
|
||||
|
||||
val requestLine = mockWebServerRule.mockWebServer.takeRequest().requestLine
|
||||
|
||||
requestLine `should contain` "v=${initialProtocolVersion.restApiVersion}"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should update version from response`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
client.newCall(createRequest {}).execute()
|
||||
|
||||
(interceptor as VersionInterceptor).protocolVersion `should equal` SubsonicAPIVersions.V1_13_0
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should not update version if response json doesn't contain version`() {
|
||||
mockWebServerRule.enqueueResponse("non_subsonic_response.json")
|
||||
|
||||
client.newCall(createRequest {}).execute()
|
||||
|
||||
(interceptor as VersionInterceptor).protocolVersion `should equal` initialProtocolVersion
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should not update version on non-json response`() {
|
||||
mockWebServerRule.mockWebServer.enqueue(MockResponse()
|
||||
.setBody("asdqwnekjnqwkjen")
|
||||
.setHeader("Content-Type", "application/octet-stream"))
|
||||
|
||||
client.newCall(createRequest {}).execute()
|
||||
|
||||
(interceptor as VersionInterceptor).protocolVersion `should equal` initialProtocolVersion
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Should notify notifier on version change`() {
|
||||
mockWebServerRule.enqueueResponse("ping_ok.json")
|
||||
|
||||
client.newCall(createRequest {}).execute()
|
||||
|
||||
updatedProtocolVersion `should equal` SubsonicAPIVersions.V1_13_0
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package org.moire.ultrasonic.api.subsonic.rules
|
||||
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
|
||||
/**
|
||||
* Starts mock web server for test and shut it down after.
|
||||
*/
|
||||
class MockWebServerRule : TestRule {
|
||||
val mockWebServer = MockWebServer()
|
||||
|
||||
override fun apply(base: Statement?, description: Description?): Statement {
|
||||
val ruleStatement = object : Statement() {
|
||||
override fun evaluate() {
|
||||
try {
|
||||
mockWebServer.start()
|
||||
base?.evaluate()
|
||||
} finally {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
}
|
||||
}
|
||||
return ruleStatement
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "failed",
|
||||
"version" : "1.13.0",
|
||||
"error" : {
|
||||
"code" : 0,
|
||||
"message" : "Generic error."
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"albumList2" : {
|
||||
"album" : [ {
|
||||
"id" : "962",
|
||||
"name" : "Fury",
|
||||
"artist" : "Sick Puppies",
|
||||
"artistId" : "473",
|
||||
"coverArt" : "al-962",
|
||||
"songCount" : 13,
|
||||
"duration" : 2591,
|
||||
"created" : "2017-09-02T17:34:51.000Z",
|
||||
"year" : 2016,
|
||||
"genre" : "Alternative Rock"
|
||||
}, {
|
||||
"id" : "961",
|
||||
"name" : "Endless Forms Most Beautiful",
|
||||
"artist" : "Nightwish",
|
||||
"artistId" : "559",
|
||||
"coverArt" : "al-961",
|
||||
"songCount" : 22,
|
||||
"duration" : 9469,
|
||||
"created" : "2017-09-02T16:22:47.000Z",
|
||||
"year" : 2015,
|
||||
"genre" : "Symphonic Metal"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"albumList" : {
|
||||
"album" : [ {
|
||||
"id" : "10020",
|
||||
"parent" : "490",
|
||||
"isDir" : true,
|
||||
"title" : "Fury",
|
||||
"album" : "Fury",
|
||||
"artist" : "Sick Puppies",
|
||||
"year" : 2016,
|
||||
"genre" : "Alternative Rock",
|
||||
"coverArt" : "10020",
|
||||
"playCount" : 13,
|
||||
"created" : "2017-09-02T17:34:51.000Z"
|
||||
}, {
|
||||
"id" : "9997",
|
||||
"parent" : "9996",
|
||||
"isDir" : true,
|
||||
"title" : "Endless Forms Most Beautiful",
|
||||
"album" : "Endless Forms Most Beautiful",
|
||||
"artist" : "Nightwish",
|
||||
"year" : 2015,
|
||||
"genre" : "Symphonic Metal",
|
||||
"coverArt" : "9997",
|
||||
"playCount" : 11,
|
||||
"created" : "2017-09-02T16:22:49.000Z"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
379
subsonic-api/src/integrationTest/resources/get_album_ok.json
Normal file
379
subsonic-api/src/integrationTest/resources/get_album_ok.json
Normal file
@ -0,0 +1,379 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"album" : {
|
||||
"id" : "618",
|
||||
"name" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"artistId" : "362",
|
||||
"coverArt" : "al-618",
|
||||
"songCount" : 15,
|
||||
"duration" : 3331,
|
||||
"created" : "2016-10-23T15:31:22.000Z",
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"song" : [ {
|
||||
"id" : "6491",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "Rock 'n' Roll Train",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 1,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 7205451,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 261,
|
||||
"bitRate" : 219,
|
||||
"path" : "AC_DC/Black Ice/01 Rock 'n' Roll Train.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:20.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "6500",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "Skies on Fire",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 2,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 5634607,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 214,
|
||||
"bitRate" : 209,
|
||||
"path" : "AC_DC/Black Ice/02 Skies on Fire.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:22.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "6496",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "Big Jack",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 3,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 6274247,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 237,
|
||||
"bitRate" : 211,
|
||||
"path" : "AC_DC/Black Ice/03 Big Jack.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:22.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "6488",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "Anything Goes",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 4,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 5763074,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 202,
|
||||
"bitRate" : 227,
|
||||
"path" : "AC_DC/Black Ice/04 Anything Goes.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:20.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "6501",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "War Machine",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 5,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 4962101,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 189,
|
||||
"bitRate" : 208,
|
||||
"path" : "AC_DC/Black Ice/05 War Machine.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:20.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "6492",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "Smash 'n' Grab",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 6,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 6697204,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 246,
|
||||
"bitRate" : 216,
|
||||
"path" : "AC_DC/Black Ice/06 Smash 'n' Grab.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:20.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "6499",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "Spoilin' for a Fight",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 7,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 5481070,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 197,
|
||||
"bitRate" : 222,
|
||||
"path" : "AC_DC/Black Ice/07 Spoilin' for a Fight.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:21.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "6487",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "Wheels",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 8,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 5837241,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 208,
|
||||
"bitRate" : 223,
|
||||
"path" : "AC_DC/Black Ice/08 Wheels.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:22.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "6495",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "Decibel",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 9,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 5251982,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 213,
|
||||
"bitRate" : 195,
|
||||
"path" : "AC_DC/Black Ice/09 Decibel.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:19.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "6494",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "Stormy May Day",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 10,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 5105168,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 190,
|
||||
"bitRate" : 214,
|
||||
"path" : "AC_DC/Black Ice/10 Stormy May Day.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:20.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "6493",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "She Likes Rock 'n' Roll",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 11,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 6135766,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 232,
|
||||
"bitRate" : 210,
|
||||
"path" : "AC_DC/Black Ice/11 She Likes Rock 'n' Roll.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:22.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "6497",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "Money Made",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 12,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 6816386,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 255,
|
||||
"bitRate" : 213,
|
||||
"path" : "AC_DC/Black Ice/12 Money Made.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:21.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "6489",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "Rock 'n' Roll Dream",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 13,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 7486707,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 280,
|
||||
"bitRate" : 212,
|
||||
"path" : "AC_DC/Black Ice/13 Rock 'n' Roll Dream.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:20.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "6498",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "Rocking All the Way",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 14,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 4927026,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 202,
|
||||
"bitRate" : 194,
|
||||
"path" : "AC_DC/Black Ice/14 Rocking All the Way.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:22.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "6490",
|
||||
"parent" : "6475",
|
||||
"isDir" : false,
|
||||
"title" : "Black Ice",
|
||||
"album" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"track" : 15,
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "6475",
|
||||
"size" : 5192799,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 205,
|
||||
"bitRate" : 202,
|
||||
"path" : "AC_DC/Black Ice/15 Black Ice.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:22.000Z",
|
||||
"albumId" : "618",
|
||||
"artistId" : "362",
|
||||
"type" : "music"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"artist" : {
|
||||
"id" : "362",
|
||||
"name" : "AC/DC",
|
||||
"coverArt" : "ar-362",
|
||||
"albumCount" : 2,
|
||||
"album" : [ {
|
||||
"id" : "618",
|
||||
"name" : "Black Ice",
|
||||
"artist" : "AC/DC",
|
||||
"artistId" : "362",
|
||||
"coverArt" : "al-618",
|
||||
"songCount" : 15,
|
||||
"duration" : 3331,
|
||||
"created" : "2016-10-23T15:31:22.000Z",
|
||||
"year" : 2008,
|
||||
"genre" : "Hard Rock"
|
||||
}, {
|
||||
"id" : "617",
|
||||
"name" : "Rock or Bust",
|
||||
"artist" : "AC/DC",
|
||||
"artistId" : "362",
|
||||
"coverArt" : "al-617",
|
||||
"songCount" : 11,
|
||||
"duration" : 2095,
|
||||
"created" : "2016-10-23T15:31:23.000Z",
|
||||
"year" : 2014,
|
||||
"genre" : "Hard Rock"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
{
|
||||
"subsonic-response": {
|
||||
"status": "ok",
|
||||
"version": "1.15.0",
|
||||
"artists": {
|
||||
"ignoredArticles": "The El La Los Las Le Les",
|
||||
"index": [
|
||||
{
|
||||
"name": "A",
|
||||
"artist": [
|
||||
{
|
||||
"id": "362",
|
||||
"name": "AC/DC",
|
||||
"coverArt": "ar-362",
|
||||
"albumCount": 2
|
||||
},
|
||||
{
|
||||
"id": "254",
|
||||
"name": "Acceptance",
|
||||
"coverArt": "ar-254",
|
||||
"albumCount": 1
|
||||
}
|
||||
]
|
||||
}, {
|
||||
"name": "T",
|
||||
"artist": [
|
||||
{
|
||||
"id": "516",
|
||||
"name": "Tangerine Dream",
|
||||
"coverArt": "ar-516",
|
||||
"albumCount": 1
|
||||
},
|
||||
{
|
||||
"id": "242",
|
||||
"name": "Taproot",
|
||||
"coverArt": "ar-242",
|
||||
"albumCount": 2
|
||||
}
|
||||
]
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"bookmarks" : {
|
||||
"bookmark" : [ {
|
||||
"position" : 107914,
|
||||
"username" : "CaptainEurope",
|
||||
"comment" : "Look at this",
|
||||
"created" : "2017-11-18T15:22:22.144Z",
|
||||
"changed" : "2017-11-18T15:22:22.144Z",
|
||||
"entry" : {
|
||||
"id" : "10349",
|
||||
"parent" : "10342",
|
||||
"isDir" : false,
|
||||
"title" : "Amerika",
|
||||
"album" : "Home of the Strange",
|
||||
"artist" : "Young the Giant",
|
||||
"track" : 1,
|
||||
"year" : 2016,
|
||||
"genre" : "Indie Rock",
|
||||
"coverArt" : "10342",
|
||||
"size" : 9628673,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 240,
|
||||
"bitRate" : 320,
|
||||
"path" : "Young the Giant/Home of the Strange/01 Amerika.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 2,
|
||||
"discNumber" : 1,
|
||||
"created" : "2017-11-01T17:46:52.000Z",
|
||||
"albumId" : "984",
|
||||
"artistId" : "571",
|
||||
"type" : "music"
|
||||
}
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"chatMessages" : {
|
||||
"chatMessage" : [ {
|
||||
"username" : "sindre",
|
||||
"time" : 1269771845310,
|
||||
"message" : "Sindre was here"
|
||||
}, {
|
||||
"username" : "ben",
|
||||
"time" : 1269771842504,
|
||||
"message" : "Ben too"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"genres" : {
|
||||
"genre" : [ {
|
||||
"songCount" : 1186,
|
||||
"albumCount" : 103,
|
||||
"value" : "Rock"
|
||||
}, {
|
||||
"songCount" : 896,
|
||||
"albumCount" : 72,
|
||||
"value" : "Electronic"
|
||||
}, {
|
||||
"songCount" : 790,
|
||||
"albumCount" : 59,
|
||||
"value" : "Alternative Rock"
|
||||
}, {
|
||||
"songCount" : 622,
|
||||
"albumCount" : 97,
|
||||
"value" : "Trance"
|
||||
}, {
|
||||
"songCount" : 476,
|
||||
"albumCount" : 36,
|
||||
"value" : "Hard Rock"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.13.0",
|
||||
"indexes" : {
|
||||
"lastModified" : 1491069027523,
|
||||
"ignoredArticles" : "The El La Los Las Le Les",
|
||||
"shortcut" : [ {
|
||||
"id" : "889",
|
||||
"name" : "podcasts"
|
||||
},
|
||||
{
|
||||
"id" : "890",
|
||||
"name" : "audiobooks"
|
||||
} ],
|
||||
"index" : [ {
|
||||
"name" : "A",
|
||||
"artist" : [ {
|
||||
"id" : "50",
|
||||
"name" : "Ace Of Base",
|
||||
"starred" : "2017-04-02T20:16:29.815Z"
|
||||
}, {
|
||||
"id" : "379",
|
||||
"name" : "A Perfect Circle"
|
||||
} ]
|
||||
}, {
|
||||
"name" : "H",
|
||||
"artist" : [ {
|
||||
"id" : "299",
|
||||
"name" : "Haddaway"
|
||||
}, {
|
||||
"id" : "297",
|
||||
"name" : "Halestorm"
|
||||
} ]
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"lyrics" : {
|
||||
"artist" : "Amorphis",
|
||||
"title" : "Alone",
|
||||
"value" : "Tear dimmed rememberance\nIn a womb of time\nBreath upon me\nPossessed by the"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"directory" : {
|
||||
"id" : "4836",
|
||||
"parent" : "300",
|
||||
"name" : "12 Stones",
|
||||
"userRating" : 5,
|
||||
"averageRating" : 5.0,
|
||||
"playCount" : 1,
|
||||
"child" : [ {
|
||||
"id" : "4844",
|
||||
"parent" : "4836",
|
||||
"isDir" : false,
|
||||
"title" : "Crash",
|
||||
"album" : "12 Stones",
|
||||
"artist" : "12 Stones",
|
||||
"track" : 1,
|
||||
"year" : 2002,
|
||||
"genre" : "Alternative Rock",
|
||||
"coverArt" : "4836",
|
||||
"size" : 5348318,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 222,
|
||||
"bitRate" : 192,
|
||||
"path" : "12 Stones/12 Stones/01 Crash.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:19:10.000Z",
|
||||
"albumId" : "454",
|
||||
"artistId" : "288",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "4845",
|
||||
"parent" : "4836",
|
||||
"isDir" : false,
|
||||
"title" : "Broken",
|
||||
"album" : "12 Stones",
|
||||
"artist" : "12 Stones",
|
||||
"track" : 2,
|
||||
"year" : 2002,
|
||||
"genre" : "Alternative Rock",
|
||||
"coverArt" : "4836",
|
||||
"size" : 4309043,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 179,
|
||||
"bitRate" : 192,
|
||||
"path" : "12 Stones/12 Stones/02 Broken.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:19:09.000Z",
|
||||
"albumId" : "454",
|
||||
"artistId" : "288",
|
||||
"type" : "music"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.13.0",
|
||||
"musicFolders" : {
|
||||
"musicFolder" : [ {
|
||||
"id" : 0,
|
||||
"name" : "Music"
|
||||
}, {
|
||||
"id" : 2,
|
||||
"name" : "Test"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"playlist" : {
|
||||
"id" : "0",
|
||||
"name" : "Aug 27, 2017 11:17 AM",
|
||||
"owner" : "admin",
|
||||
"public" : false,
|
||||
"songCount" : 16,
|
||||
"duration" : 3573,
|
||||
"created" : "2017-08-27T11:17:26.216Z",
|
||||
"changed" : "2017-08-27T11:17:26.218Z",
|
||||
"coverArt" : "pl-0",
|
||||
"entry" : [ {
|
||||
"id" : "4209",
|
||||
"parent" : "4186",
|
||||
"isDir" : false,
|
||||
"title" : "Follow Me Down",
|
||||
"album" : "Going to Hell",
|
||||
"artist" : "The Pretty Reckless",
|
||||
"track" : 1,
|
||||
"year" : 2014,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "4186",
|
||||
"size" : 11229681,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 280,
|
||||
"bitRate" : 320,
|
||||
"path" : "The Pretty Reckless/Going to Hell/01 Follow Me Down.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 1,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T21:30:43.000Z",
|
||||
"albumId" : "388",
|
||||
"artistId" : "238",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "4215",
|
||||
"parent" : "4186",
|
||||
"isDir" : false,
|
||||
"title" : "Going to Hell",
|
||||
"album" : "Going to Hell",
|
||||
"artist" : "The Pretty Reckless",
|
||||
"track" : 2,
|
||||
"year" : 2014,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "4186",
|
||||
"size" : 11089627,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 277,
|
||||
"bitRate" : 320,
|
||||
"path" : "The Pretty Reckless/Going to Hell/02 Going to Hell.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T21:30:41.000Z",
|
||||
"albumId" : "388",
|
||||
"artistId" : "238",
|
||||
"type" : "music"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"playlists" : {
|
||||
"playlist" : [ {
|
||||
"id" : "0",
|
||||
"name" : "Aug 27, 2017 11:17 AM",
|
||||
"comment" : "Some comment",
|
||||
"owner" : "admin",
|
||||
"public" : false,
|
||||
"songCount" : 16,
|
||||
"duration" : 3573,
|
||||
"created" : "2017-08-27T11:17:26.216Z",
|
||||
"changed" : "2017-08-27T11:17:26.218Z",
|
||||
"coverArt" : "pl-0"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
154
subsonic-api/src/integrationTest/resources/get_podcasts_ok.json
Normal file
154
subsonic-api/src/integrationTest/resources/get_podcasts_ok.json
Normal file
@ -0,0 +1,154 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"podcasts" : {
|
||||
"channel" : [ {
|
||||
"id" : "2",
|
||||
"url" : "http://feeds.codenewbie.org/cnpodcast.xml",
|
||||
"title" : "CodeNewbie",
|
||||
"description" : "Stories and interviews from people on their coding journey.",
|
||||
"coverArt" : "pod-2",
|
||||
"originalImageUrl" : "http://codenewbie.blubrry.com/wp-content/uploads/powerpress/220808.jpg",
|
||||
"status" : "completed",
|
||||
"episode" : [ {
|
||||
"id" : "148",
|
||||
"parent" : "9959",
|
||||
"isDir" : false,
|
||||
"title" : "S1:EP3 – How to teach yourself computer science (Vaidehi Joshi)",
|
||||
"album" : "CodeNewbie",
|
||||
"artist" : "podcasts",
|
||||
"coverArt" : "9959",
|
||||
"size" : 38274221,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 2397,
|
||||
"bitRate" : 128,
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"created" : "2017-08-30T09:33:39.000Z",
|
||||
"type" : "podcast",
|
||||
"streamId" : "9982",
|
||||
"channelId" : "2",
|
||||
"description" : "Vaidehi decided to take on a year-long challenge. She'd pick a computer science topic every week, do tons of research and write a technical blog post explaining it in simple terms and beautiful illustrations. And then she actually did it. She tells us about her project, basecs, how it's changed her as a developer, and how she handles the trolls and negativity from people who don't appreciate her work. Show Notes Technical Writer position at CodeNewbie basecs 100 Days of Code Conway's Game of Life Hexes and Other Magical Numbers (Vaidehi's blog post) Bits, Bytes, Building With Binary (Vaidehi's blog post) Rust",
|
||||
"status" : "completed",
|
||||
"publishDate" : "2017-08-29T00:01:01.000Z"
|
||||
}, {
|
||||
"id" : "147",
|
||||
"parent" : "9959",
|
||||
"isDir" : false,
|
||||
"title" : "S1:EP2 – Building community in a virtual world: Moderation tools in VR (Cameron Brown)",
|
||||
"album" : "CodeNewbie",
|
||||
"artist" : "podcasts",
|
||||
"coverArt" : "9959",
|
||||
"size" : 52657014,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 3298,
|
||||
"bitRate" : 128,
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"created" : "2017-08-25T19:32:53.000Z",
|
||||
"type" : "podcast",
|
||||
"streamId" : "9963",
|
||||
"channelId" : "2",
|
||||
"description" : "Rec Room is the most popular virtual reality game. It's a social space where you can play dodgeball, ping pong, darts and more with people from all over the world. But when you're inviting everyone to play, how do you make sure that everyone is safe? What happens when a player attacks someone? What does an attack even look like in a virtual world? Cameron Brown, Chief Creative Officer at Against Gravity, the creators of Rec Room, takes us through the world of social virtual reality and shows how they've designed a system to make their game a welcoming place for all. Show Links Incapsula (sponsor) Dice (sponsor) Technical Writer position at CodeNewbie HTC Vive Rec Room Rec Room's Code of Conduct",
|
||||
"status" : "completed",
|
||||
"publishDate" : "2017-08-24T00:01:01.000Z"
|
||||
}, {
|
||||
"id" : "146",
|
||||
"parent" : "9959",
|
||||
"isDir" : false,
|
||||
"title" : "S1:EP1 – Intro to Accessibility (Stephanie Slattery)",
|
||||
"album" : "CodeNewbie",
|
||||
"artist" : "podcasts",
|
||||
"coverArt" : "9959",
|
||||
"size" : 50708305,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 3176,
|
||||
"bitRate" : 128,
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"created" : "2017-08-16T19:32:53.000Z",
|
||||
"type" : "podcast",
|
||||
"streamId" : "9962",
|
||||
"channelId" : "2",
|
||||
"description" : "We kick off the first episode of our official first season with Stephanie Slattery, a front-end engineer who specializes in accessibility. She breaks down the world of accessibility, giving you the perfect introduction to this topic. She explains the five categories of disabilities, shows us how to implement suggestions from the Web Content Accessibility Guidelines, and shares why she’s so passionate about helping more people experience tech. Show Links Incapsula (sponsor) Hover (sponsor) An Introduction to Web Accessibility (Stephanie's Blog Post) Codeland, CodeNewbie's conference - April 21 & 22 in NYC NeoPets Dev Bootcamp Illinois Institute of Technology W3C WCAG (Web Content Accessibility Guidelines) ADA 1990 Rehabilitation Act of 1973",
|
||||
"status" : "completed",
|
||||
"publishDate" : "2017-08-16T00:01:01.000Z"
|
||||
}, {
|
||||
"id" : "0",
|
||||
"parent" : "9959",
|
||||
"isDir" : false,
|
||||
"title" : "Ep. 146 – Codeland - Mentorship, Technical Blogging, and Open Source Talks from Katrina Owen, Quincy Larson, and Nell Shamrell-Harrington",
|
||||
"album" : "CodeNewbie",
|
||||
"artist" : "podcasts",
|
||||
"coverArt" : "9959",
|
||||
"size" : 47779050,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 2993,
|
||||
"bitRate" : 128,
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"created" : "2017-08-12T18:40:06.000Z",
|
||||
"type" : "podcast",
|
||||
"streamId" : "9960",
|
||||
"channelId" : "2",
|
||||
"description" : "In our final episode of our Codeland mini-series, Katrina Owen shares what it really takes to get that mentor you've always wanted, Quincy Larson gives us his best practices for writing technical blog posts people will actually read, and Nell Shamrell-Harrington explores what it really takes for an open source project to be successful and what you should know as a future contributor. Show Links Flatiron School (sponsor) Incapsula (sponsor) Hover (sponsor) CodeNewbie YouTube channel Codeland, CodeNewbie's conference - April 21 & 22 in NYC Be Lucky—it’s an easy skill to learn by Richard Wiseman How to read Medium articles people will actually read Sample Contribution Guide Sample Testing Guide Travis CI Sample Code of Conduct Open Source Governance Continuous Integration (CI) System",
|
||||
"status" : "completed",
|
||||
"publishDate" : "2017-08-01T00:01:01.000Z"
|
||||
}, {
|
||||
"id" : "1",
|
||||
"isDir" : false,
|
||||
"title" : "Ep. 145 – Codeland - NYPL and Khan Academy talks from Courteney Ervin and Celia La",
|
||||
"channelId" : "2",
|
||||
"description" : "Courteney Ervin shares the ups and downs of building a product for one of the largest library systems in the world. Celia La walks us through the technical challenges (and solutions) of bringing Khan Academy's high quality content to people all over the world. Show Links Flatiron School (sponsor) Hover (sponsor) Incapsula (sponsor) CodeNewbie YouTube channel Codeland, CodeNewbie's conference - April 21 & 22 in NYC Khan Academy Memcached Git Version Control System New York Public Library Integrated Library System",
|
||||
"status" : "skipped",
|
||||
"publishDate" : "2017-07-25T00:01:01.000Z"
|
||||
}, {
|
||||
"id" : "2",
|
||||
"isDir" : false,
|
||||
"title" : "Ep. 144 – Codeland - Accessibility and Education talks from Sterling Walker and Maurice Rogers",
|
||||
"channelId" : "2",
|
||||
"description" : "We wrap up our community talks with Sterling’s story of her very first project at her first dev job: making the app accessible to two blind students. Maurice kicks off our education talks with the story of Abacus, his side project that became the learning system used by thousands of students in his country of Belize. Show Links Flatiron School (sponsor) Hover (sponsor)Incapsula (sponsor) CodeNewbie YouTube channel Codeland, CodeNewbie's conference - April 21 & 22 in NYC An Alphabet of Accessibility Issues Web Content Accessibility Guidelines (WCAG) Abacus Grails Java xkcd comic \"Standards\"",
|
||||
"status" : "skipped",
|
||||
"publishDate" : "2017-07-18T00:01:01.000Z"
|
||||
}, {
|
||||
"id" : "3",
|
||||
"isDir" : false,
|
||||
"title" : "Ep. 143 – Codeland - Community Talks from Valerie Woolard Srinivasan and Rapi Castillo",
|
||||
"channelId" : "2",
|
||||
"description" : "Valerie explores the importance of security in creating powerful and engaged communities, and breaks down three ways your code might be vulnerable. Rapi shares his story of creating a toy coding project in D3.js that sparked a movement and helped thousands of people become more politically engaged. Checkout the videos of these talks on the CodeNewbie YouTube channel. Show Links Flatiron School (sponsor) Hover (sponsor) Incapsula (sponsor) CodeNewbie YouTube channel Codeland, CodeNewbie's conference - April 21 & 22 in NYC Rapi's Talk [VIDEO] Valerie's Talk [VIDEO] Progressive Coders Network D3.js SQL mass assignment man in the middle SQL injection validating inputs sanitizing inputs strong parameters",
|
||||
"status" : "skipped",
|
||||
"publishDate" : "2017-07-06T00:01:01.000Z"
|
||||
}, {
|
||||
"id" : "4",
|
||||
"isDir" : false,
|
||||
"title" : "Ep. 142 – Codeland - Codeland - Mental Health talks from Michelle Morales and Greg Baugues",
|
||||
"channelId" : "2",
|
||||
"description" : "This episode features two talks on mental health that explore two very different sides of this important topic. Michelle’s talk is a technical showcase of how her research project uses open source tools to better diagnose depression. Greg shares his personal struggles with ADHD and bipolar disorder, and how important it is for us to openly talk about mental health. Show Links Flatiron School (sponsor) Hover (sponsor) Incapsula (sponsor) CodeNewbie YouTube channel Codeland, CodeNewbie's conference - April 21 & 22 in NYC CUNY Graduate Center Tom Insel's TED talk on depression Audio/Visual Emotion and Depression Recognition dataset DAIC-WOZ Database Covarep OpenFace OpenMM IBM Watson Speech to Text Natural Language Processing Machine Learning Feature Extraction Automatic Speech Recognition ZocDoc 718-312-8335 (Greg's mental health resource number)",
|
||||
"status" : "skipped",
|
||||
"publishDate" : "2017-06-28T00:01:01.000Z"
|
||||
}, {
|
||||
"id" : "5",
|
||||
"isDir" : false,
|
||||
"title" : "Ep. 141 – Codeland - Interview with NYC's first CTO (Minerva Tantoco)",
|
||||
"channelId" : "2",
|
||||
"description" : "When Minerva Tantoco was first offered the CTO position for New York City, she thought it was a prank. But in 2014, she became the city’s first Chief Technology Officer. She sits down with Codeland’s emcee, Nikhil Paul, to talk about how she started her long, impressive tech career, what programming looked like back her coding days, and how she hopes tech will transform cities for the better. Show Links Flatiron School (sponsor) Hover (sponsor) Incapsula (sponsor) CodeNewbie YouTube channel Mayor de Blasio’s announcement Codeland, CodeNewbie's conference - April 21 & 22 in NYC",
|
||||
"status" : "skipped",
|
||||
"publishDate" : "2017-06-21T00:01:01.000Z"
|
||||
}, {
|
||||
"id" : "6",
|
||||
"isDir" : false,
|
||||
"title" : "Ep. 140 – Codeland - Gaming and City Talks from Chris Algoo, Kate Rabinowitz, Eric Brelsford",
|
||||
"channelId" : "2",
|
||||
"description" : "Chris Algoo shares how he co-created “Breakup Squad,” the game where you have to keep two exes from getting back together. Kate Rabinowitz shows us how open data can help build powerful, insightful tools to better understand and improve your city. Eric Brelsford shares how he used mapping tools to help community members turn vacant lots into beautiful neighborhood spaces. Show Links Flatiron School (sponsor) Hover (sponsor) Incapsula (sponsor) Breakup Squad (trailer) Codeland, CodeNewbie's conference - April 21 & 22 in NYC CodeNewbie YouTube channel Unity Twine FMOD Open Game Art Freesound.org The Big List of Game Making Tools An Introduction to Statistical Learning Interactive Data Visualization for the Web Code for America Brigades DataKind Maptime NYC Urban Reviewer 596 Acres Living Lots NYC NYCommons Open Data R Python Tableau Carto D3 API",
|
||||
"status" : "skipped",
|
||||
"publishDate" : "2017-06-13T00:01:01.000Z"
|
||||
} ]
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"randomSongs" : {
|
||||
"song" : [ {
|
||||
"id" : "4426",
|
||||
"parent" : "4417",
|
||||
"isDir" : false,
|
||||
"title" : "Permanent",
|
||||
"album" : "Phantoms",
|
||||
"artist" : "Acceptance",
|
||||
"track" : 11,
|
||||
"year" : 2005,
|
||||
"genre" : "Rock",
|
||||
"coverArt" : "4417",
|
||||
"size" : 4444381,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 185,
|
||||
"bitRate" : 192,
|
||||
"path" : "Acceptance/Phantoms/11 Permanent.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T15:31:49.000Z",
|
||||
"albumId" : "409",
|
||||
"artistId" : "254",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "3061",
|
||||
"parent" : "3050",
|
||||
"isDir" : false,
|
||||
"title" : "Sure as Hell",
|
||||
"album" : "Who Are You Now?",
|
||||
"artist" : "This Providence",
|
||||
"track" : 1,
|
||||
"year" : 2009,
|
||||
"genre" : "Indie Rock",
|
||||
"coverArt" : "3050",
|
||||
"size" : 1969692,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 110,
|
||||
"bitRate" : 142,
|
||||
"path" : "This Providence/Who Are You Now_/01 Sure as Hell.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T21:32:46.000Z",
|
||||
"albumId" : "272",
|
||||
"artistId" : "152",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "2455",
|
||||
"parent" : "2451",
|
||||
"isDir" : false,
|
||||
"title" : "I Dare You",
|
||||
"album" : "Us and Them",
|
||||
"artist" : "Shinedown",
|
||||
"track" : 4,
|
||||
"year" : 2005,
|
||||
"genre" : "Rock",
|
||||
"coverArt" : "2451",
|
||||
"size" : 6489382,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 234,
|
||||
"bitRate" : 221,
|
||||
"path" : "Shinedown/Us and Them/04 I Dare You.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T21:16:21.000Z",
|
||||
"albumId" : "209",
|
||||
"artistId" : "112",
|
||||
"type" : "music"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"shares" : {
|
||||
"share" : [ {
|
||||
"id" : "0",
|
||||
"url" : "https://subsonic.com/ext/share/awdwo?jwt=eyJhbGciOiJIUzI1NiJ9.eyJwYXRoIjoiL2V4dC9zaGFyZS9hd2R3byIsImV4cCI6MTU0MTYyNjQzMX0.iy8dkt_ZZc8hJ692UxorHdHWFU2RB-fMCmCA4IJ_dTw",
|
||||
"username" : "admin",
|
||||
"created" : "2017-11-07T21:33:51.748Z",
|
||||
"expires" : "2018-11-07T21:33:51.748Z",
|
||||
"lastVisited" : "2018-11-07T21:33:51.748Z",
|
||||
"description" : "Awesome link!",
|
||||
"visitCount" : 0,
|
||||
"entry" : [ {
|
||||
"id" : "4212",
|
||||
"parent" : "4186",
|
||||
"isDir" : false,
|
||||
"title" : "Heaven Knows",
|
||||
"album" : "Going to Hell",
|
||||
"artist" : "The Pretty Reckless",
|
||||
"track" : 3,
|
||||
"year" : 2014,
|
||||
"genre" : "Hard Rock",
|
||||
"coverArt" : "4186",
|
||||
"size" : 9025090,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 225,
|
||||
"bitRate" : 320,
|
||||
"path" : "The Pretty Reckless/Going to Hell/03 Heaven Knows.mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 2,
|
||||
"discNumber" : 1,
|
||||
"created" : "2016-10-23T21:30:40.000Z",
|
||||
"albumId" : "388",
|
||||
"artistId" : "238",
|
||||
"type" : "music"
|
||||
} ]
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"songsByGenre" : {
|
||||
"song" : [ {
|
||||
"id" : "575",
|
||||
"parent" : "576",
|
||||
"isDir" : false,
|
||||
"title" : "Time Machine (Vadim Zhukov Remix)",
|
||||
"album" : "668",
|
||||
"artist" : "Tasadi",
|
||||
"year" : 2008,
|
||||
"genre" : "Trance",
|
||||
"size" : 22467672,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 561,
|
||||
"bitRate" : 320,
|
||||
"path" : "Tasadi/668/00 Time Machine (Vadim Zhukov Remix).mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 0,
|
||||
"created" : "2016-10-23T21:58:29.000Z",
|
||||
"albumId" : "0",
|
||||
"artistId" : "0",
|
||||
"type" : "music"
|
||||
}, {
|
||||
"id" : "621",
|
||||
"parent" : "622",
|
||||
"isDir" : false,
|
||||
"title" : "My Heart (Vadim Zhukov Remix)",
|
||||
"album" : "668",
|
||||
"artist" : "DJ Polyakov PPK Feat Kate Cameron",
|
||||
"year" : 2009,
|
||||
"genre" : "Trance",
|
||||
"size" : 26805932,
|
||||
"contentType" : "audio/mpeg",
|
||||
"suffix" : "mp3",
|
||||
"duration" : 670,
|
||||
"bitRate" : 320,
|
||||
"path" : "DJ Polyakov PPK Feat Kate Cameron/668/00 My Heart (Vadim Zhukov Remix).mp3",
|
||||
"isVideo" : false,
|
||||
"playCount" : 2,
|
||||
"created" : "2016-10-23T21:58:29.000Z",
|
||||
"albumId" : "5",
|
||||
"artistId" : "4",
|
||||
"type" : "music"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"starred2" : {
|
||||
"artist" : [ {
|
||||
"id" : "364",
|
||||
"name" : "Parov Stelar",
|
||||
"starred" : "2017-08-12T18:32:58.768Z"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"starred" : {
|
||||
"artist" : [ {
|
||||
"id" : "364",
|
||||
"name" : "Parov Stelar",
|
||||
"starred" : "2017-08-12T18:32:58.768Z"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
24
subsonic-api/src/integrationTest/resources/get_user_ok.json
Normal file
24
subsonic-api/src/integrationTest/resources/get_user_ok.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"user" : {
|
||||
"username" : "GodOfUniverse",
|
||||
"email" : "some.mail@example.com",
|
||||
"scrobblingEnabled" : false,
|
||||
"adminRole" : true,
|
||||
"settingsRole" : true,
|
||||
"downloadRole" : true,
|
||||
"uploadRole" : true,
|
||||
"playlistRole" : true,
|
||||
"coverArtRole" : true,
|
||||
"commentRole" : true,
|
||||
"podcastRole" : true,
|
||||
"streamRole" : true,
|
||||
"jukeboxRole" : true,
|
||||
"shareRole" : true,
|
||||
"videoConversionRole" : false,
|
||||
"folder" : [ 0 ]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
{
|
||||
"subsonic-response" : {
|
||||
"status" : "ok",
|
||||
"version" : "1.15.0",
|
||||
"videos" : {
|
||||
"video" : [ {
|
||||
"id" : "10402",
|
||||
"parent" : "10401",
|
||||
"isDir" : false,
|
||||
"title" : "MVI_0512",
|
||||
"album" : "Incoming",
|
||||
"size" : 21889646,
|
||||
"contentType" : "video/avi",
|
||||
"suffix" : "avi",
|
||||
"transcodedContentType" : "video/x-flv",
|
||||
"transcodedSuffix" : "flv",
|
||||
"path" : "Incoming/MVI_0512.avi",
|
||||
"isVideo" : true,
|
||||
"playCount" : 0,
|
||||
"created" : "2017-11-19T12:34:33.000Z",
|
||||
"type" : "video"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user