From d646043564f3bb30f9de81094d2acf1606282ddf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simon=20K=C3=BCnzel?= <simonk@fsmpi.rwth-aachen.de>
Date: Tue, 14 May 2024 23:16:04 +0200
Subject: [PATCH] More tests to cover most sql statements

---
 db_example.sql     |   2 +
 encoding.py        |   1 +
 feeds.py           |   1 +
 importer.py        |   3 +
 jobmanagement.py   |   1 +
 livestreams.py     |   9 +++
 server.py          |   4 ++
 sorter.py          |   2 +
 tests/test_misc.py | 167 ++++++++++++++++++++++++++++++++++++++++++++-
 9 files changed, 189 insertions(+), 1 deletion(-)

diff --git a/db_example.sql b/db_example.sql
index 3c1c2fa..b520677 100644
--- a/db_example.sql
+++ b/db_example.sql
@@ -8611,6 +8611,7 @@ INSERT INTO `videos_data` (`id`,`lecture_id`,`visible`,`deleted`,`downloadable`,
 INSERT INTO `videos_data` (`id`,`lecture_id`,`visible`,`deleted`,`downloadable`,`title`,`comment`,`internal`,`path`,`file_modified`,`time_created`,`time_updated`,`created_by`,`file_size`,`video_format`,`hash`) VALUES (9681,7012,1,0,1,'','','','pub/16ss-dsal/16ss-dsal-160715-1080p_1.mp4','2016-08-07 22:54:46','2016-08-07 21:02:37','2016-08-07 21:02:43',46,1402602183,4,'e036f7cbd51afd3ab7be10cf77747c00');
 INSERT INTO `videos_data` (`id`,`lecture_id`,`visible`,`deleted`,`downloadable`,`title`,`comment`,`internal`,`path`,`file_modified`,`time_created`,`time_updated`,`created_by`,`file_size`,`video_format`,`hash`) VALUES (9682,7012,1,0,1,'','','','pub/16ss-dsal/16ss-dsal-160715-360p_1.mp4','2016-08-07 22:45:34','2016-08-07 21:02:38','2016-08-07 21:02:45',46,368611109,10,'fae2bda2da55a3005aa6329a2d0227c3');
 INSERT INTO `videos_data` (`id`,`lecture_id`,`visible`,`deleted`,`downloadable`,`title`,`comment`,`internal`,`path`,`file_modified`,`time_created`,`time_updated`,`created_by`,`file_size`,`video_format`,`hash`) VALUES (9683,7012,1,0,1,'','','','pub/16ss-dsal/16ss-dsal-160715-720p_1.mp4','2016-08-07 22:46:00','2016-08-07 21:02:40','2016-08-07 21:02:44',46,721141077,5,'083c0b7693c82078c513707d1402096b');
+INSERT INTO `videos_data` (`id`,`lecture_id`,`visible`,`deleted`,`downloadable`,`title`,`comment`,`internal`,`path`,`file_modified`,`time_created`,`time_updated`,`created_by`,`file_size`,`video_format`,`hash`, `source`) VALUES (16080,7012,1,0,1,'','','','pub/17ws-cgbp/17ws-cgbp-171114-720p.mp4','2018-01-12 04:36:44','2018-01-12 04:36:44','2018-01-12 04:36:44',-1,607257928,5,"8fa956b14162ec42c1dabc11d53671c5",89);
 INSERT INTO `users` (`id`,`name`,`realname`,`level`,`fsacc`,`last_login`,`calendar_key`,`rfc6238`) VALUES (1,'gustav1','Gustav Geier',0,'',NULL,'','');
 INSERT INTO `users` (`id`,`name`,`realname`,`level`,`fsacc`,`last_login`,`calendar_key`,`rfc6238`) VALUES (2,'gustav2','Gustav Geier',0,'',NULL,'','');
 INSERT INTO `users` (`id`,`name`,`realname`,`level`,`fsacc`,`last_login`,`calendar_key`,`rfc6238`) VALUES (4,'gustav4','Gustav Geier',0,'',NULL,'','');
@@ -14646,4 +14647,5 @@ INSERT INTO `areas` (`area`,`abbreviation`,`default`,`rank`,`coordinates`) VALUE
 INSERT INTO `profiles` (`name`,`format`) VALUES ('default',4);
 INSERT INTO `profiles` (`name`,`format`) VALUES ('default',5);
 INSERT INTO `profiles` (`name`,`format`) VALUES ('default',10);
+INSERT INTO `sources` (`id`, `lecture_id`, `path`, `type`, `hash`, `time_created`) VALUES (89, 7012, 'autoencode/something', 'plain', '000000000', '2024-01-01 00:00:00');
 COMMIT;
diff --git a/encoding.py b/encoding.py
index a4dc849..de8549e 100644
--- a/encoding.py
+++ b/encoding.py
@@ -117,6 +117,7 @@ def add_reencode_job():
 
 @job_handler('probe-raw', 'intro')
 def update_lecture_videos(jobid, jobtype, data, state, status): #pylint: disable=unused-argument
+	# info: sql no test cover
 	if 'lecture_id' not in data:
 		return
 	if jobtype == 'probe-raw':
diff --git a/feeds.py b/feeds.py
index cf4a548..4f4685c 100644
--- a/feeds.py
+++ b/feeds.py
@@ -52,6 +52,7 @@ def rss_feed(handle):
 			GROUP BY formats.id
 			ORDER BY formats.player_prio DESC''', course['id'])
 	if not formats:
+		# info: sql no test cover
 		formats = query('SELECT * FROM formats WHERE id = 4 OR id = 5 OR id = 10') # 360p, 720p, 1080p
 	if 'format_id' not in request.values:
 		return redirect(url_for('rss_feed', handle=handle, format_id=formats[0]['id']))
diff --git a/importer.py b/importer.py
index 7cf3e02..90a5a6e 100644
--- a/importer.py
+++ b/importer.py
@@ -21,6 +21,7 @@ def list_import_sources(id):
 				modify('INSERT INTO import_campus (url, type, course_id, last_checked, changed) VALUES (?, ?, ?, ?, 1)',
 					campus[i]['url'], campus[i]['type'], id, datetime.now())
 		else:
+			# info: sql no test cover
 			if campus[i]['url'] != '':
 				query('UPDATE import_campus SET url = ?, "type" = ? WHERE (course_id = ?) AND (id = ?)', campus[i]['url'], campus[i]['type'], id, int(i))
 			else:
@@ -99,6 +100,7 @@ def fetch_co_course_events(i):
 			e['duration'] = int((datetime.strptime("%s %s"%(k, j['end']), fmt) - e['time']).seconds/60)
 			j['place'] = str(j['place'])
 			if j['place'] != '':
+				# info: sql no test cover
 				dbplace = query("SELECT name FROM places WHERE (campus_room = ?) OR (campus_name = ?) OR ((NOT campus_name) AND name = ?)",
 					j['place'], j['place'], j['place'])
 				if dbplace:
@@ -158,6 +160,7 @@ def fetch_ro_course_events(item):
 		place = str(comp.get('LOCATION', ''))
 		if place:
 			campus_room = place.split('(')[-1].split(')')[0]
+			# info: sql no test cover
 			dbplace = query('SELECT name FROM places WHERE campus_room = ?', campus_room)
 			if dbplace:
 				event['place'] = dbplace[0]['name']
diff --git a/jobmanagement.py b/jobmanagement.py
index 8f84ce5..eca41b0 100644
--- a/jobmanagement.py
+++ b/jobmanagement.py
@@ -58,6 +58,7 @@ def cancel_job(job_id):
 	query('UPDATE jobs SET canceled = true WHERE id = ?', job_id)
 
 def restart_job(job_id, canceled=False):
+	# info: sql no test cover
 	if canceled:
 		query('UPDATE jobs SET state = \'ready\', canceled = false WHERE id = ? AND state = \'failed\'', job_id)
 	else:
diff --git a/livestreams.py b/livestreams.py
index dcb0e30..a68388a 100644
--- a/livestreams.py
+++ b/livestreams.py
@@ -45,6 +45,7 @@ def streamauth_legacy(server=None):
 		if 'lecture' in request.values:
 			match = {'id': request.values['lecture']}
 		if not query("SELECT handle FROM streams WHERE handle = ?", request.values['name']):
+			# info: sql no test cover
 			modify("INSERT INTO streams (handle, active, visible, lecture_id, description, poster) VALUES (?, false, true, -1, '', '')", request.values['name'])
 		if server:
 			data = {'src': 'rtmp://%s/live/%s'%(server, request.values['name']),
@@ -53,6 +54,7 @@ def streamauth_legacy(server=None):
 			modify("UPDATE streams SET active = true, lecture_id = ?, job_id = ? WHERE handle = ?",
 					match['id'], job_id, request.values['name'])
 		else:
+			# info: sql no test cover
 			modify("UPDATE streams SET active = true, lecture_id = ? WHERE handle = ?",
 					match['id'], request.values['name'])
 	elif request.values['call'] == 'publish_done':
@@ -103,6 +105,7 @@ def gentoken():
 @app.route('/internal/streaming/rekey/<int:id>')
 @mod_required
 def streamrekey(id):
+	# info: sql no test cover
 	modify('UPDATE live_sources SET "key" = ? WHERE id = ? AND NOT deleted', gentoken(), id)
 	source = query('SELECT * FROM live_sources WHERE NOT deleted AND id = ?', id)[0]
 	flash('''Der Streamkey von <strong>{name}</strong> wurde neu generiert:
@@ -120,6 +123,7 @@ def streamrekey(id):
 @app.route('/internal/streaming/drop/<int:id>')
 @mod_required
 def streamdrop(id):
+	# info: sql no test cover
 	source = (query('SELECT * FROM live_sources WHERE NOT deleted AND id = ?', id) or [None])[0]
 	if not source:
 		if 'ref' in request.values:
@@ -134,6 +138,7 @@ def streamdrop(id):
 
 @sched_func(120)
 def live_source_thumbnail():
+	# info: sql no test cover
 	sources = query('SELECT * FROM live_sources WHERE clientid IS NOT NULL')
 	for source in sources:
 		schedule_job('thumbnail', {'srcurl': 'rtmp://%s/src/%i'%(source['server'], source['id']), 'filename': 's_%i.jpg'%source['id']})
@@ -146,6 +151,7 @@ def ip_in_networks(ip, networks):
 
 @app.route('/internal/streaming/auth/<server>', methods=['GET', 'POST'])
 def streamauth(server):
+	# info: sql no test cover
 	# pylint: disable=too-many-return-statements
 	if not ip_in_networks(request.headers['X-Real-IP'], config.get('FSMPI_IP_RANGES', [])):
 		return 'Forbidden', 403
@@ -185,6 +191,7 @@ def streamauth(server):
 	return 'Bad request', 400
 
 def schedule_livestream(lecture_id):
+	# info: sql no test cover
 	# pylint: disable=too-many-branches,too-many-statements
 	lecture = query('SELECT * FROM lectures WHERE id = ?', lecture_id)[0]
 	settings = json.loads(lecture['stream_settings'])
@@ -294,6 +301,7 @@ def restart_failed_complex_live_transcode(id, type, data, state, status): # pyli
 @job_handler('complex_live_transcode', state='failed')
 @job_handler('complex_live_transcode', state='finished')
 def cleanup_after_complex_live_transcode_ended(id, type, data, state, status): # pylint: disable=unused-argument
+	# info: sql no test cover
 	job = query('SELECT * FROM jobs WHERE id = ?', id, nlfix=False)[0]
 	if state == 'finished' or (state == 'failed' and job['canceled']):
 		modify('UPDATE lectures_data SET stream_job = NULL WHERE stream_job = ?', id)
@@ -301,6 +309,7 @@ def cleanup_after_complex_live_transcode_ended(id, type, data, state, status): #
 @app.route('/internal/streaming/control', methods=['POST'])
 @mod_required
 def control_stream():
+	# info: sql no test cover
 	action = request.values['action']
 	lecture_id = int(request.values['lecture_id'])
 	course = (query('SELECT courses.* FROM courses JOIN lectures ON (courses.id = lectures.course_id) WHERE lectures.id = ?', lecture_id) or [None])[0]
diff --git a/server.py b/server.py
index 2a3eda8..7036de5 100644
--- a/server.py
+++ b/server.py
@@ -33,6 +33,7 @@ if sys.argv[0].endswith('run_tests.py'):
 	config['DEBUG'] = True
 	config['DISABLE_SCHEDULER'] = True
 	config['JOBS_API_KEY'] = '1'
+	config['SORTER_API_KEY'] = '1'
 if config['DEBUG']:
 	app.jinja_env.auto_reload = True
 
@@ -205,9 +206,11 @@ def index():
 		if item['type'] == 'courses':
 			if item['param'] not in ['title', 'semester', 'organizer', 'subject']:
 				continue
+			# info: sql no test cover
 			item['courses'] = query('SELECT * FROM courses WHERE (visible AND listed) AND "%s" = ? ORDER BY "%s"'%(item['param'], item['param']), item['param2'])
 		elif item['type'] == 'video':
 			item['lecture'] = {'id': item['param']}
+			# info: sql no test cover
 			streams = query('''SELECT streams.handle AS livehandle, streams.lecture_id, \'formats\' AS sep, formats.*
 					FROM streams
 					JOIN lectures ON lectures.id = streams.lecture_id
@@ -526,6 +529,7 @@ def login():
 	session['user'] = userinfo
 	dbuser = query('SELECT * FROM users WHERE name = ?', user)
 	if not dbuser:
+		# info: sql no test cover
 		modify('INSERT INTO users (name, realname, fsacc, level, calendar_key, rfc6238) VALUES (?, ?, ?, 1, \'\', \'\')', user, session['user']['givenName'], user)
 		dbuser = query('SELECT * FROM users WHERE name = ?', user)
 	session['user']['dbid'] = dbuser[0]['id']
diff --git a/sorter.py b/sorter.py
index 258b242..b78017e 100644
--- a/sorter.py
+++ b/sorter.py
@@ -29,6 +29,7 @@ def to_ascii(inputstring):
 
 @job_handler('probe', 'remux', 'transcode')
 def update_video_metadata(jobid, jobtype, data, state, status): #pylint: disable=unused-argument
+	# info: sql no test cover
 	if 'video_id' not in data:
 		return
 	if jobtype not in ['remux', 'transcode']:
@@ -56,6 +57,7 @@ def add_thumbnail_job():
 	return redirect(request.values.get('ref', url_for('jobs_overview')))
 
 def insert_video(lectureid, dbfilepath, fileformatid, hash="", filesize=-1, duration=-1, sourceid=None): #pylint: disable=too-many-arguments
+	# info: sql no test cover
 	visible = query('SELECT courses.autovisible FROM courses JOIN lectures ON lectures.course_id = courses.id WHERE lectures.id = ?', lectureid)[0]['autovisible']
 	video_id = modify('''INSERT INTO videos_data
 		(lecture_id, visible, path, video_format, title, comment, internal, file_modified, time_created, time_updated, created_by, hash, file_size, duration, source)
diff --git a/tests/test_misc.py b/tests/test_misc.py
index c98d47d..5e1ec64 100644
--- a/tests/test_misc.py
+++ b/tests/test_misc.py
@@ -60,6 +60,12 @@ class VideoTestCase(unittest.TestCase):
 			assert r.status_code == 200
 			r = c.get('/15ws-einfprog')
 			assert r.status_code == 200
+			
+			r = c.get('/99')
+			assert r.status_code == 200
+			
+			r = c.post('/99/2330/login', data={"username": "wrong", "password": "auth"})
+			assert r.status_code == 302
 
 	def test_timetable(self):
 		with self.app as c:
@@ -175,6 +181,12 @@ class VideoTestCase(unittest.TestCase):
 			r = c.get('/internal/changelog')
 			assert r.status_code == 200
 			assert 'Testtitle' in r.data.decode() and 'lectures.7353.title' in r.data.decode()
+			
+			r = c.post('/internal/set/responsible/20/1', data={'_csrf_token': 'asd'})
+			assert r.status_code == 200
+			
+			r = c.post('/internal/unset/responsible/20/1', data={'_csrf_token': 'asd'})
+			assert r.status_code == 200
 
 
 	def test_legacyurl(self):
@@ -258,8 +270,24 @@ class VideoTestCase(unittest.TestCase):
 			match, fmt = sorter.sort_file('15ss-zkk-extremale-codes-1080p.mp4')
 			assert len(match) == 1
 			assert match[0]['id'] == 6095
+			
+			self.login(c)
+			r = self.app.get('/internal/sort/log')
+			assert r.status_code == 200
+			
+			r = self.app.post('/internal/jobs/add/thumbnail', data={'lectureid': 10, '_csrf_token': 'asd'})
+			assert r.status_code == 302
+			
+			r = self.app.get('/internal/sort/now')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/sort/encoded/something-wrong', data={'apikey': '1'})
+			assert r.status_code == 400
+			
+			r = self.app.get('/internal/sort/encoded/09ss-dsal-090619-720p.mp4', data={'apikey': '1'})
+			assert r.status_code == 200
 
-#	@unittest.skip("too slow")
+	@unittest.skip("too slow")
 	def test_campusimport(self):
 		with self.app as c:
 			self.login(c)
@@ -275,3 +303,140 @@ class VideoTestCase(unittest.TestCase):
 			self.login(c)
 			r = self.app.get('/internal/cutprogress')
 			assert r.status_code == 200
+	
+	# Some quick tests below to execute more sql statements
+	
+	def test_encoding(self):
+		with self.app as c:
+			self.login(c)
+			r = self.app.post('/internal/jobs/add/remux', data={'videoid': 28, '_csrf_token': 'asd'})
+			assert r.status_code == 302
+			
+			r = self.app.post('/internal/jobs/add/reencode', data={'videoid': 16080, '_csrf_token': 'asd'})
+			assert r.status_code == 302
+			
+			# Trigger edit handler
+			r = c.get('internal/edit', data={"courses.3.title":"Test","_csrf_token":"asd"})
+			assert r.status_code == 200
+	
+	def test_feeds(self):
+		with self.app as c:
+			r = self.app.get('/feed')
+			assert r.status_code == 200
+			
+			r = self.app.get('/07ws-buk/feed')
+			assert r.status_code == 200
+			
+			r = self.app.get('/07ws-buk/rss', data={'format_id':10})
+			assert r.status_code == 200
+			
+			r = self.app.get('/courses/feed')
+			assert r.status_code == 200
+	
+	def test_icalexport(self):
+		with self.app as c:
+			self.login(c)
+			r = self.app.get('/internal/ical/user/1')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/ical/notuser/1')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/ical/course/1')
+			assert r.status_code == 200
+	
+	def test_jobs(self):
+		with self.app as c:
+			self.login(c)
+			r = self.app.get('/internal/jobs/overview')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/jobs/overview?worker=worker0')
+			assert r.status_code == 200
+			
+			r = self.app.post('/internal/jobs/action/clear_failed', data={'_csrf_token': 'asd'})
+			assert r.status_code == 302
+			
+			r = self.app.post('/internal/jobs/action/clear_failed/1', data={'_csrf_token': 'asd'})
+			assert r.status_code == 302
+			
+			r = self.app.post('/internal/jobs/action/retry_failed', data={'_csrf_token': 'asd'})
+			assert r.status_code == 302
+			
+			r = self.app.post('/internal/jobs/action/retry_failed/1', data={'_csrf_token': 'asd'})
+			assert r.status_code == 302
+			
+			r = self.app.post('/internal/jobs/action/copy/1', data={'_csrf_token': 'asd'})
+			assert r.status_code == 302
+			
+			r = self.app.post('/internal/jobs/action/delete/1', data={'_csrf_token': 'asd'})
+			assert r.status_code == 302
+			
+			r = self.app.post('/internal/jobs/action/cancel/1', data={'_csrf_token': 'asd'})
+			assert r.status_code == 302
+			
+			r = self.app.post('/internal/jobs/api/job/1/ping', data={'apikey': '1', 'host': 'test', 'status': '{}', 'state': 'finished'})
+			assert r.status_code == 205
+			
+			# Test new worker
+			import uuid
+			r = self.app.post(f'/internal/jobs/api/worker/{uuid.uuid4()}/schedule', data=json.dumps({'jobtypes': ['probe'], 'queues': ['default'], 'apikey': '1'}), content_type='application/json')
+			assert r.status_code in [200, 503]
+	
+	def test_auth(self):
+		with self.app as c:
+			r = self.app.get('/internal/auth', headers={'X-Original-Uri': 'https://videoag.fsmpi.rwth-aachen.de/files/pub/15ws-afi/15ws-afi-151022-720p.mp4'})
+			assert r.status_code == 200
+			
+			# Not found, but sql is executed
+			r = self.app.get('/internal/auth', headers={'X-Original-Uri': 'https://videoag.fsmpi.rwth-aachen.de/files/pub/hls/something'})
+			assert r.status_code == 404
+			
+			r = self.app.get('/internal/auth', headers={'X-Original-Uri': 'https://videoag.fsmpi.rwth-aachen.de/files/pub/hls/42'})
+			assert r.status_code == 404
+	
+	def test_stats(self):
+		with self.app as c:
+			self.login(c)
+			
+			r = self.app.get('/internal/stats')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/stats/generic/formats_views')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/stats/generic/course_count')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/stats/generic/lectures_count')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/stats/generic/categories_courses')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/stats/generic/organizer_courses')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/stats/generic/categories_lectures')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/stats/generic/lecture_views')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/stats/generic/live_views')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/stats/generic/lecture_totalviews')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/stats/viewsperday/lecture/1')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/stats/viewsperday/course/1')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/stats/viewsperday/global')
+			assert r.status_code == 200
+			
+			r = self.app.get('/internal/stats/viewsperday/courses')
+			assert r.status_code == 200
-- 
GitLab