#!/usr/bin/env python # coding: utf-8 # # 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 from pony.orm import db_session, commit from supysonic.db import Folder, Artist, Album, Track from .apitestbase import ApiTestBase class SearchTestCase(ApiTestBase): def setUp(self): super(SearchTestCase, self).setUp() with db_session: root = Folder(root = True, name = 'Root folder', path = 'tests/assets') for letter in 'ABC': folder = Folder(name = letter + 'rtist', path = 'tests/assets/{}rtist'.format(letter), parent = root) artist = Artist(name = letter + 'rtist') for lether in 'AB': afolder = Folder( name = letter + lether + 'lbum', path = 'tests/assets/{0}rtist/{0}{1}lbum'.format(letter, lether), parent = folder ) album = Album(name = letter + lether + 'lbum', artist = artist) 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), last_modification = 0, root_folder = root, folder = afolder ) commit() self.assertEqual(Folder.select().count(), 10) self.assertEqual(Artist.select().count(), 3) self.assertEqual(Album.select().count(), 6) self.assertEqual(Track.select().count(), 18) 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) songs.append(song) def test_search2(self): # invalid parameters 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) # 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) self.assertEqual(len(self._xpath(child, './artist')), 1) self.assertEqual(len(self._xpath(child, './album')), 0) self.assertEqual(len(self._xpath(child, './song')), 0) self.assertEqual(child[0].get('name'), 'Artist') rv, child = self._make_request('search2', { 'query': 'rti' }, tag = 'searchResult2') self.assertEqual(len(child), 3) self.assertEqual(len(self._xpath(child, './artist')), 3) self.assertEqual(len(self._xpath(child, './album')), 0) self.assertEqual(len(self._xpath(child, './song')), 0) # album search rv, child = self._make_request('search2', { 'query': 'AAlbum' }, tag = 'searchResult2') self.assertEqual(len(child), 1) self.assertEqual(len(self._xpath(child, './artist')), 0) self.assertEqual(len(self._xpath(child, './album')), 1) self.assertEqual(len(self._xpath(child, './song')), 0) 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) self.assertEqual(len(self._xpath(child, './artist')), 0) self.assertEqual(len(self._xpath(child, './album')), 6) self.assertEqual(len(self._xpath(child, './song')), 0) # song search rv, child = self._make_request('search2', { 'query': 'One' }, tag = 'searchResult2') self.assertEqual(len(child), 6) self.assertEqual(len(self._xpath(child, './artist')), 0) self.assertEqual(len(self._xpath(child, './album')), 0) self.assertEqual(len(self._xpath(child, './song')), 6) 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) self.assertEqual(len(self._xpath(child, './artist')), 0) self.assertEqual(len(self._xpath(child, './album')), 0) self.assertEqual(len(self._xpath(child, './song')), 12) # any field search rv, child = self._make_request('search2', { 'query': 'r' }, tag = 'searchResult2') self.assertEqual(len(child), 9) self.assertEqual(len(self._xpath(child, './artist')), 3) self.assertEqual(len(self._xpath(child, './album')), 0) self.assertEqual(len(self._xpath(child, './song')), 6) # paging artists = [] for offset in range(0, 4, 2): rv, child = self._make_request('search2', { 'query': 'r', 'artistCount': 2, 'artistOffset': offset }, tag = 'searchResult2') names = self._xpath(child, './artist/@name') self.assertLessEqual(len(names), 2) for name in names: self.assertNotIn(name, artists) artists.append(name) songs = [] for offset in range(0, 6, 2): rv, child = self._make_request('search2', { 'query': 'r', 'songCount': 2, 'songOffset': offset }, tag = 'searchResult2') elems = self._xpath(child, './song') self.assertEqual(len(elems), 2) for song in map(self.__track_as_pseudo_unique_str, elems): self.assertNotIn(song, songs) songs.append(song) # 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 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) # 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) self.assertEqual(len(self._xpath(child, './artist')), 1) self.assertEqual(len(self._xpath(child, './album')), 0) self.assertEqual(len(self._xpath(child, './song')), 0) self.assertEqual(child[0].get('name'), 'Artist') rv, child = self._make_request('search3', { 'query': 'rti' }, tag = 'searchResult3') self.assertEqual(len(child), 3) self.assertEqual(len(self._xpath(child, './artist')), 3) self.assertEqual(len(self._xpath(child, './album')), 0) self.assertEqual(len(self._xpath(child, './song')), 0) # album search rv, child = self._make_request('search3', { 'query': 'AAlbum' }, tag = 'searchResult3') self.assertEqual(len(child), 1) self.assertEqual(len(self._xpath(child, './artist')), 0) self.assertEqual(len(self._xpath(child, './album')), 1) self.assertEqual(len(self._xpath(child, './song')), 0) 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) self.assertEqual(len(self._xpath(child, './artist')), 0) self.assertEqual(len(self._xpath(child, './album')), 6) self.assertEqual(len(self._xpath(child, './song')), 0) # song search rv, child = self._make_request('search3', { 'query': 'One' }, tag = 'searchResult3') self.assertEqual(len(child), 6) self.assertEqual(len(self._xpath(child, './artist')), 0) self.assertEqual(len(self._xpath(child, './album')), 0) self.assertEqual(len(self._xpath(child, './song')), 6) 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) self.assertEqual(len(self._xpath(child, './artist')), 0) self.assertEqual(len(self._xpath(child, './album')), 0) self.assertEqual(len(self._xpath(child, './song')), 12) # any field search rv, child = self._make_request('search3', { 'query': 'r' }, tag = 'searchResult3') self.assertEqual(len(child), 9) self.assertEqual(len(self._xpath(child, './artist')), 3) self.assertEqual(len(self._xpath(child, './album')), 0) self.assertEqual(len(self._xpath(child, './song')), 6) # paging artists = [] for offset in range(0, 4, 2): rv, child = self._make_request('search3', { 'query': 'r', 'artistCount': 2, 'artistOffset': offset }, tag = 'searchResult3') names = self._xpath(child, './artist/@name') self.assertLessEqual(len(names), 2) for name in names: self.assertNotIn(name, artists) artists.append(name) songs = [] for offset in range(0, 6, 2): rv, child = self._make_request('search3', { 'query': 'r', 'songCount': 2, 'songOffset': offset }, tag = 'searchResult3') elems = self._xpath(child, './song') self.assertEqual(len(elems), 2) for song in map(self.__track_as_pseudo_unique_str, elems): self.assertNotIn(song, songs) songs.append(song) if __name__ == '__main__': unittest.main()