2017-11-03 22:15:48 +00:00
|
|
|
#!/usr/bin/env python
|
2018-03-04 20:49:56 +00:00
|
|
|
# coding: utf-8
|
2017-11-03 22:15:48 +00:00
|
|
|
#
|
|
|
|
# This file is part of Supysonic.
|
|
|
|
# Supysonic is a Python implementation of the Subsonic server API.
|
|
|
|
#
|
|
|
|
# Copyright (C) 2017 Alban 'spl0k' Féron
|
|
|
|
#
|
|
|
|
# Distributed under terms of the GNU AGPLv3 license.
|
|
|
|
|
|
|
|
import time
|
|
|
|
import unittest
|
|
|
|
|
2017-12-21 22:29:00 +00:00
|
|
|
from pony.orm import db_session, commit
|
|
|
|
|
2017-11-03 22:15:48 +00:00
|
|
|
from supysonic.db import Folder, Artist, Album, Track
|
|
|
|
|
|
|
|
from .apitestbase import ApiTestBase
|
|
|
|
|
|
|
|
class SearchTestCase(ApiTestBase):
|
|
|
|
def setUp(self):
|
|
|
|
super(SearchTestCase, self).setUp()
|
|
|
|
|
2017-12-21 22:29:00 +00:00
|
|
|
with db_session:
|
|
|
|
root = Folder(root = True, name = 'Root folder', path = 'tests/assets')
|
2017-11-03 22:15:48 +00:00
|
|
|
|
2017-12-21 22:29:00 +00:00
|
|
|
for letter in 'ABC':
|
|
|
|
folder = Folder(name = letter + 'rtist', path = 'tests/assets/{}rtist'.format(letter), parent = root)
|
|
|
|
artist = Artist(name = letter + 'rtist')
|
2017-11-03 22:15:48 +00:00
|
|
|
|
2017-12-21 22:29:00 +00:00
|
|
|
for lether in 'AB':
|
|
|
|
afolder = Folder(
|
|
|
|
name = letter + lether + 'lbum',
|
|
|
|
path = 'tests/assets/{0}rtist/{0}{1}lbum'.format(letter, lether),
|
|
|
|
parent = folder
|
|
|
|
)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
2017-12-21 22:29:00 +00:00
|
|
|
album = Album(name = letter + lether + 'lbum', artist = artist)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
2017-12-21 22:29:00 +00:00
|
|
|
for num, song in enumerate([ 'One', 'Two', 'Three' ]):
|
|
|
|
track = Track(
|
|
|
|
disc = 1,
|
|
|
|
number = num,
|
|
|
|
title = song,
|
|
|
|
duration = 2,
|
|
|
|
album = album,
|
|
|
|
artist = artist,
|
|
|
|
bitrate = 320,
|
|
|
|
path = 'tests/assets/{0}rtist/{0}{1}lbum/{2}'.format(letter, lether, song),
|
|
|
|
content_type = 'audio/mpeg',
|
|
|
|
last_modification = 0,
|
|
|
|
root_folder = root,
|
|
|
|
folder = afolder
|
|
|
|
)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
2017-12-21 22:29:00 +00:00
|
|
|
commit()
|
2017-11-03 22:15:48 +00:00
|
|
|
|
2017-12-21 22:29:00 +00:00
|
|
|
self.assertEqual(Folder.select().count(), 10)
|
|
|
|
self.assertEqual(Artist.select().count(), 3)
|
|
|
|
self.assertEqual(Album.select().count(), 6)
|
|
|
|
self.assertEqual(Track.select().count(), 18)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
def __track_as_pseudo_unique_str(self, elem):
|
|
|
|
return elem.get('artist') + elem.get('album') + elem.get('title')
|
|
|
|
|
|
|
|
def test_search(self):
|
|
|
|
# invalid parameters
|
|
|
|
self._make_request('search', { 'count': 'string' }, error = 0)
|
|
|
|
self._make_request('search', { 'offset': 'sstring' }, error = 0)
|
|
|
|
self._make_request('search', { 'newerThan': 'ssstring' }, error = 0)
|
|
|
|
|
|
|
|
# no search
|
|
|
|
self._make_request('search', error = 10)
|
|
|
|
|
|
|
|
# non existent artist (but searched string present in other fields)
|
|
|
|
rv, child = self._make_request('search', { 'artist': 'One' }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 0)
|
|
|
|
self.assertEqual(child.get('totalHits'), '0')
|
|
|
|
self.assertEqual(child.get('offset'), '0')
|
|
|
|
|
|
|
|
rv, child = self._make_request('search', { 'artist': 'AAlbum' }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 0)
|
|
|
|
|
|
|
|
# non existent album (but search present in other fields)
|
|
|
|
rv, child = self._make_request('search', { 'album': 'One' }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 0)
|
|
|
|
|
|
|
|
rv, child = self._make_request('search', { 'album': 'Artist' }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 0)
|
|
|
|
|
|
|
|
# non existent title (but search present in other fields)
|
|
|
|
rv, child = self._make_request('search', { 'title': 'AAlbum' }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 0)
|
|
|
|
|
|
|
|
rv, child = self._make_request('search', { 'title': 'Artist' }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 0)
|
|
|
|
|
|
|
|
# non existent anything
|
|
|
|
rv, child = self._make_request('search', { 'any': 'Chaos' }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 0)
|
|
|
|
|
|
|
|
# artist search
|
|
|
|
rv, child = self._make_request('search', { 'artist': 'Artist' }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 1)
|
|
|
|
self.assertEqual(child.get('totalHits'), '1')
|
|
|
|
self.assertEqual(child[0].get('title'), 'Artist')
|
|
|
|
|
|
|
|
rv, child = self._make_request('search', { 'artist': 'rti' }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 3)
|
|
|
|
self.assertEqual(child.get('totalHits'), '3')
|
|
|
|
|
|
|
|
# same as above, but created in the future
|
|
|
|
future = int(time.time() * 1000 + 1000)
|
|
|
|
rv, child = self._make_request('search', { 'artist': 'rti', 'newerThan': future }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 0)
|
|
|
|
|
|
|
|
# album search
|
|
|
|
rv, child = self._make_request('search', { 'album': 'AAlbum' }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 1)
|
|
|
|
self.assertEqual(child[0].get('title'), 'AAlbum')
|
|
|
|
self.assertEqual(child[0].get('artist'), 'Artist')
|
|
|
|
|
|
|
|
rv, child = self._make_request('search', { 'album': 'lbu' }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 6)
|
|
|
|
|
|
|
|
# same as above, but created in the future
|
|
|
|
rv, child = self._make_request('search', { 'album': 'lbu', 'newerThan': future }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 0)
|
|
|
|
|
|
|
|
# song search
|
|
|
|
rv, child = self._make_request('search', { 'title': 'One' }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 6)
|
|
|
|
for i in range(6):
|
|
|
|
self.assertEqual(child[i].get('title'), 'One')
|
|
|
|
|
|
|
|
rv, child = self._make_request('search', { 'title': 'e' }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 12)
|
|
|
|
|
|
|
|
# same as above, but created in the future
|
|
|
|
rv, child = self._make_request('search', { 'title': 'e', 'newerThan': future }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 0)
|
|
|
|
|
|
|
|
# any field search
|
|
|
|
rv, child = self._make_request('search', { 'any': 'r' }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 10) # root + 3 artists (*rtist) + 6 songs (Three)
|
|
|
|
|
|
|
|
# same as above, but created in the future
|
|
|
|
rv, child = self._make_request('search', { 'any': 'r', 'newerThan': future }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 0)
|
|
|
|
|
|
|
|
# paging
|
|
|
|
songs = []
|
|
|
|
for offset in range(0, 12, 2):
|
|
|
|
rv, child = self._make_request('search', { 'title': 'e', 'count': 2, 'offset': offset }, tag = 'searchResult')
|
|
|
|
self.assertEqual(len(child), 2)
|
|
|
|
self.assertEqual(child.get('totalHits'), '12')
|
|
|
|
self.assertEqual(child.get('offset'), str(offset))
|
|
|
|
for song in map(self.__track_as_pseudo_unique_str, child):
|
|
|
|
self.assertNotIn(song, songs)
|
2017-11-08 22:21:52 +00:00
|
|
|
songs.append(song)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
def test_search2(self):
|
|
|
|
# invalid parameters
|
2018-02-25 21:28:47 +00:00
|
|
|
self._make_request('search2', { 'query': 'a', 'artistCount': 'string' }, error = 0)
|
|
|
|
self._make_request('search2', { 'query': 'a', 'artistOffset': 'sstring' }, error = 0)
|
|
|
|
self._make_request('search2', { 'query': 'a', 'albumCount': 'string' }, error = 0)
|
|
|
|
self._make_request('search2', { 'query': 'a', 'albumOffset': 'sstring' }, error = 0)
|
|
|
|
self._make_request('search2', { 'query': 'a', 'songCount': 'string' }, error = 0)
|
|
|
|
self._make_request('search2', { 'query': 'a', 'songOffset': 'sstring' }, error = 0)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
# no search
|
|
|
|
self._make_request('search2', error = 10)
|
|
|
|
|
|
|
|
# non existent anything
|
|
|
|
rv, child = self._make_request('search2', { 'query': 'Chaos' }, tag = 'searchResult2')
|
|
|
|
self.assertEqual(len(child), 0)
|
|
|
|
|
|
|
|
# artist search
|
|
|
|
rv, child = self._make_request('search2', { 'query': 'Artist' }, tag = 'searchResult2')
|
|
|
|
self.assertEqual(len(child), 1)
|
2017-11-08 22:21:52 +00:00
|
|
|
self.assertEqual(len(self._xpath(child, './artist')), 1)
|
|
|
|
self.assertEqual(len(self._xpath(child, './album')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './song')), 0)
|
2017-11-03 22:15:48 +00:00
|
|
|
self.assertEqual(child[0].get('name'), 'Artist')
|
|
|
|
|
|
|
|
rv, child = self._make_request('search2', { 'query': 'rti' }, tag = 'searchResult2')
|
|
|
|
self.assertEqual(len(child), 3)
|
2017-11-08 22:21:52 +00:00
|
|
|
self.assertEqual(len(self._xpath(child, './artist')), 3)
|
|
|
|
self.assertEqual(len(self._xpath(child, './album')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './song')), 0)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
# album search
|
|
|
|
rv, child = self._make_request('search2', { 'query': 'AAlbum' }, tag = 'searchResult2')
|
|
|
|
self.assertEqual(len(child), 1)
|
2017-11-08 22:21:52 +00:00
|
|
|
self.assertEqual(len(self._xpath(child, './artist')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './album')), 1)
|
|
|
|
self.assertEqual(len(self._xpath(child, './song')), 0)
|
2017-11-03 22:15:48 +00:00
|
|
|
self.assertEqual(child[0].get('title'), 'AAlbum')
|
|
|
|
self.assertEqual(child[0].get('artist'), 'Artist')
|
|
|
|
|
|
|
|
rv, child = self._make_request('search2', { 'query': 'lbu' }, tag = 'searchResult2')
|
|
|
|
self.assertEqual(len(child), 6)
|
2017-11-08 22:21:52 +00:00
|
|
|
self.assertEqual(len(self._xpath(child, './artist')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './album')), 6)
|
|
|
|
self.assertEqual(len(self._xpath(child, './song')), 0)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
# song search
|
|
|
|
rv, child = self._make_request('search2', { 'query': 'One' }, tag = 'searchResult2')
|
|
|
|
self.assertEqual(len(child), 6)
|
2017-11-08 22:21:52 +00:00
|
|
|
self.assertEqual(len(self._xpath(child, './artist')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './album')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './song')), 6)
|
2017-11-03 22:15:48 +00:00
|
|
|
for i in range(6):
|
|
|
|
self.assertEqual(child[i].get('title'), 'One')
|
|
|
|
|
|
|
|
rv, child = self._make_request('search2', { 'query': 'e' }, tag = 'searchResult2')
|
|
|
|
self.assertEqual(len(child), 12)
|
2017-11-08 22:21:52 +00:00
|
|
|
self.assertEqual(len(self._xpath(child, './artist')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './album')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './song')), 12)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
# any field search
|
|
|
|
rv, child = self._make_request('search2', { 'query': 'r' }, tag = 'searchResult2')
|
|
|
|
self.assertEqual(len(child), 9)
|
2017-11-08 22:21:52 +00:00
|
|
|
self.assertEqual(len(self._xpath(child, './artist')), 3)
|
|
|
|
self.assertEqual(len(self._xpath(child, './album')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './song')), 6)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
# paging
|
|
|
|
artists = []
|
|
|
|
for offset in range(0, 4, 2):
|
|
|
|
rv, child = self._make_request('search2', { 'query': 'r', 'artistCount': 2, 'artistOffset': offset }, tag = 'searchResult2')
|
2017-11-08 22:21:52 +00:00
|
|
|
names = self._xpath(child, './artist/@name')
|
|
|
|
self.assertLessEqual(len(names), 2)
|
|
|
|
for name in names:
|
|
|
|
self.assertNotIn(name, artists)
|
|
|
|
artists.append(name)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
songs = []
|
|
|
|
for offset in range(0, 6, 2):
|
|
|
|
rv, child = self._make_request('search2', { 'query': 'r', 'songCount': 2, 'songOffset': offset }, tag = 'searchResult2')
|
2017-11-08 22:21:52 +00:00
|
|
|
elems = self._xpath(child, './song')
|
2017-11-03 22:15:48 +00:00
|
|
|
self.assertEqual(len(elems), 2)
|
|
|
|
for song in map(self.__track_as_pseudo_unique_str, elems):
|
|
|
|
self.assertNotIn(song, songs)
|
2017-11-08 22:21:52 +00:00
|
|
|
songs.append(song)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
# Almost identical as above. Test dataset (and tests) should probably be changed
|
|
|
|
# to have folders that don't share names with artists or albums
|
|
|
|
def test_search3(self):
|
|
|
|
# invalid parameters
|
2018-02-25 21:28:47 +00:00
|
|
|
self._make_request('search3', { 'query': 'a', 'artistCount': 'string' }, error = 0)
|
|
|
|
self._make_request('search3', { 'query': 'a', 'artistOffset': 'sstring' }, error = 0)
|
|
|
|
self._make_request('search3', { 'query': 'a', 'albumCount': 'string' }, error = 0)
|
|
|
|
self._make_request('search3', { 'query': 'a', 'albumOffset': 'sstring' }, error = 0)
|
|
|
|
self._make_request('search3', { 'query': 'a', 'songCount': 'string' }, error = 0)
|
|
|
|
self._make_request('search3', { 'query': 'a', 'songOffset': 'sstring' }, error = 0)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
# no search
|
|
|
|
self._make_request('search3', error = 10)
|
|
|
|
|
|
|
|
# non existent anything
|
|
|
|
rv, child = self._make_request('search3', { 'query': 'Chaos' }, tag = 'searchResult3')
|
|
|
|
self.assertEqual(len(child), 0)
|
|
|
|
|
|
|
|
# artist search
|
|
|
|
rv, child = self._make_request('search3', { 'query': 'Artist' }, tag = 'searchResult3')
|
|
|
|
self.assertEqual(len(child), 1)
|
2017-11-08 22:21:52 +00:00
|
|
|
self.assertEqual(len(self._xpath(child, './artist')), 1)
|
|
|
|
self.assertEqual(len(self._xpath(child, './album')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './song')), 0)
|
2017-11-03 22:15:48 +00:00
|
|
|
self.assertEqual(child[0].get('name'), 'Artist')
|
|
|
|
|
|
|
|
rv, child = self._make_request('search3', { 'query': 'rti' }, tag = 'searchResult3')
|
|
|
|
self.assertEqual(len(child), 3)
|
2017-11-08 22:21:52 +00:00
|
|
|
self.assertEqual(len(self._xpath(child, './artist')), 3)
|
|
|
|
self.assertEqual(len(self._xpath(child, './album')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './song')), 0)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
# album search
|
|
|
|
rv, child = self._make_request('search3', { 'query': 'AAlbum' }, tag = 'searchResult3')
|
|
|
|
self.assertEqual(len(child), 1)
|
2017-11-08 22:21:52 +00:00
|
|
|
self.assertEqual(len(self._xpath(child, './artist')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './album')), 1)
|
|
|
|
self.assertEqual(len(self._xpath(child, './song')), 0)
|
2017-11-03 22:15:48 +00:00
|
|
|
self.assertEqual(child[0].get('name'), 'AAlbum')
|
|
|
|
self.assertEqual(child[0].get('artist'), 'Artist')
|
|
|
|
|
|
|
|
rv, child = self._make_request('search3', { 'query': 'lbu' }, tag = 'searchResult3')
|
|
|
|
self.assertEqual(len(child), 6)
|
2017-11-08 22:21:52 +00:00
|
|
|
self.assertEqual(len(self._xpath(child, './artist')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './album')), 6)
|
|
|
|
self.assertEqual(len(self._xpath(child, './song')), 0)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
# song search
|
|
|
|
rv, child = self._make_request('search3', { 'query': 'One' }, tag = 'searchResult3')
|
|
|
|
self.assertEqual(len(child), 6)
|
2017-11-08 22:21:52 +00:00
|
|
|
self.assertEqual(len(self._xpath(child, './artist')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './album')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './song')), 6)
|
2017-11-03 22:15:48 +00:00
|
|
|
for i in range(6):
|
|
|
|
self.assertEqual(child[i].get('title'), 'One')
|
|
|
|
|
|
|
|
rv, child = self._make_request('search3', { 'query': 'e' }, tag = 'searchResult3')
|
|
|
|
self.assertEqual(len(child), 12)
|
2017-11-08 22:21:52 +00:00
|
|
|
self.assertEqual(len(self._xpath(child, './artist')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './album')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './song')), 12)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
# any field search
|
|
|
|
rv, child = self._make_request('search3', { 'query': 'r' }, tag = 'searchResult3')
|
|
|
|
self.assertEqual(len(child), 9)
|
2017-11-08 22:21:52 +00:00
|
|
|
self.assertEqual(len(self._xpath(child, './artist')), 3)
|
|
|
|
self.assertEqual(len(self._xpath(child, './album')), 0)
|
|
|
|
self.assertEqual(len(self._xpath(child, './song')), 6)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
# paging
|
|
|
|
artists = []
|
|
|
|
for offset in range(0, 4, 2):
|
|
|
|
rv, child = self._make_request('search3', { 'query': 'r', 'artistCount': 2, 'artistOffset': offset }, tag = 'searchResult3')
|
2017-11-08 22:21:52 +00:00
|
|
|
names = self._xpath(child, './artist/@name')
|
|
|
|
self.assertLessEqual(len(names), 2)
|
|
|
|
for name in names:
|
|
|
|
self.assertNotIn(name, artists)
|
|
|
|
artists.append(name)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
songs = []
|
|
|
|
for offset in range(0, 6, 2):
|
|
|
|
rv, child = self._make_request('search3', { 'query': 'r', 'songCount': 2, 'songOffset': offset }, tag = 'searchResult3')
|
2017-11-08 22:21:52 +00:00
|
|
|
elems = self._xpath(child, './song')
|
2017-11-03 22:15:48 +00:00
|
|
|
self.assertEqual(len(elems), 2)
|
|
|
|
for song in map(self.__track_as_pseudo_unique_str, elems):
|
|
|
|
self.assertNotIn(song, songs)
|
2017-11-08 22:21:52 +00:00
|
|
|
songs.append(song)
|
2017-11-03 22:15:48 +00:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
unittest.main()
|
|
|
|
|