From 7746eb146f813a1226544844d42eda31ed60432b Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Sun, 17 Nov 2024 16:16:11 +0000 Subject: [PATCH 01/15] Started unit tests for the review controller --- .cspell/frigate-dictionary.txt | 3 + frigate/test/http_api/__init__.py | 0 frigate/test/http_api/base_http_test.py | 143 ++++++++++++++++++++++ frigate/test/http_api/test_http_review.py | 20 +++ 4 files changed, 166 insertions(+) create mode 100644 frigate/test/http_api/__init__.py create mode 100644 frigate/test/http_api/base_http_test.py create mode 100644 frigate/test/http_api/test_http_review.py diff --git a/.cspell/frigate-dictionary.txt b/.cspell/frigate-dictionary.txt index b019f8492d..64fd7ca721 100644 --- a/.cspell/frigate-dictionary.txt +++ b/.cspell/frigate-dictionary.txt @@ -12,6 +12,7 @@ argmax argmin argpartition ascontiguousarray +astype authelia authentik autodetected @@ -195,6 +196,7 @@ poweroff preexec probesize protobuf +pstate psutil pubkey putenv @@ -278,6 +280,7 @@ uvicorn vaapi vainfo variations +vbios vconcat vitb vstream diff --git a/frigate/test/http_api/__init__.py b/frigate/test/http_api/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frigate/test/http_api/base_http_test.py b/frigate/test/http_api/base_http_test.py new file mode 100644 index 0000000000..565e65a610 --- /dev/null +++ b/frigate/test/http_api/base_http_test.py @@ -0,0 +1,143 @@ +import datetime +import logging +import os +import unittest + +from peewee_migrate import Router +from playhouse.sqlite_ext import SqliteExtDatabase +from playhouse.sqliteq import SqliteQueueDatabase + +from frigate.api.fastapi_app import create_fastapi_app +from frigate.config import FrigateConfig +from frigate.models import Event +from frigate.test.const import TEST_DB, TEST_DB_CLEANUPS + + +class BaseTestHttp(unittest.TestCase): + def setUp(self, models): + # setup clean database for each test run + migrate_db = SqliteExtDatabase("test.db") + del logging.getLogger("peewee_migrate").handlers[:] + router = Router(migrate_db) + router.run() + migrate_db.close() + self.db = SqliteQueueDatabase(TEST_DB) + self.db.bind(models) + + self.minimal_config = { + "mqtt": {"host": "mqtt"}, + "cameras": { + "front_door": { + "ffmpeg": { + "inputs": [ + {"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]} + ] + }, + "detect": { + "height": 1080, + "width": 1920, + "fps": 5, + }, + } + }, + } + self.test_stats = { + "detection_fps": 13.7, + "detectors": { + "cpu1": { + "detection_start": 0.0, + "inference_speed": 91.43, + "pid": 42, + }, + "cpu2": { + "detection_start": 0.0, + "inference_speed": 84.99, + "pid": 44, + }, + }, + "front_door": { + "camera_fps": 0.0, + "capture_pid": 53, + "detection_fps": 0.0, + "pid": 52, + "process_fps": 0.0, + "skipped_fps": 0.0, + }, + "service": { + "storage": { + "/dev/shm": { + "free": 50.5, + "mount_type": "tmpfs", + "total": 67.1, + "used": 16.6, + }, + "/media/frigate/clips": { + "free": 42429.9, + "mount_type": "ext4", + "total": 244529.7, + "used": 189607.0, + }, + "/media/frigate/recordings": { + "free": 0.2, + "mount_type": "ext4", + "total": 8.0, + "used": 7.8, + }, + "/tmp/cache": { + "free": 976.8, + "mount_type": "tmpfs", + "total": 1000.0, + "used": 23.2, + }, + }, + "uptime": 101113, + "version": "0.10.1", + "latest_version": "0.11", + }, + } + + def tearDown(self): + if not self.db.is_closed(): + self.db.close() + + try: + for file in TEST_DB_CLEANUPS: + os.remove(file) + except OSError: + pass + + def create_app(self, stats= None): + return create_fastapi_app( + FrigateConfig(**self.minimal_config), + self.db, + None, + None, + None, + None, + None, + stats, + None, + ) + + def insert_mock_event( + self, + id: str, + start_time: datetime.datetime = datetime.datetime.now().timestamp(), + ) -> Event: + """Inserts a basic event model with a given id.""" + return Event.insert( + id=id, + label="Mock", + camera="front_door", + start_time=start_time, + end_time=start_time + 20, + top_score=100, + false_positive=False, + zones=list(), + thumbnail="", + region=[], + box=[], + area=0, + has_clip=True, + has_snapshot=True, + ).execute() diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py new file mode 100644 index 0000000000..fd50f40c94 --- /dev/null +++ b/frigate/test/http_api/test_http_review.py @@ -0,0 +1,20 @@ +from fastapi.testclient import TestClient + +from frigate.models import Event, ReviewSegment +from frigate.test.http_api.base_http_test import BaseTestHttp + + +class TestHttpReview(BaseTestHttp): + def setUp(self): + super().setUp([Event, ReviewSegment]) + + def test_get_bad_event(self): + app = super().create_app() + id = "123456.random" + bad_id = "654321.other" + + with TestClient(app) as client: + super().insert_mock_event(id) + event_response = client.get(f"/events/{bad_id}") + assert event_response.status_code == 404 + assert event_response.json() == "Event not found" From 137f4e28fc048d496dbbfb35c67517593ac7d07d Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Sun, 17 Nov 2024 16:18:36 +0000 Subject: [PATCH 02/15] Revert "Started unit tests for the review controller" This reverts commit 7746eb146f813a1226544844d42eda31ed60432b. --- .cspell/frigate-dictionary.txt | 3 - frigate/test/http_api/__init__.py | 0 frigate/test/http_api/base_http_test.py | 143 ---------------------- frigate/test/http_api/test_http_review.py | 20 --- 4 files changed, 166 deletions(-) delete mode 100644 frigate/test/http_api/__init__.py delete mode 100644 frigate/test/http_api/base_http_test.py delete mode 100644 frigate/test/http_api/test_http_review.py diff --git a/.cspell/frigate-dictionary.txt b/.cspell/frigate-dictionary.txt index 64fd7ca721..b019f8492d 100644 --- a/.cspell/frigate-dictionary.txt +++ b/.cspell/frigate-dictionary.txt @@ -12,7 +12,6 @@ argmax argmin argpartition ascontiguousarray -astype authelia authentik autodetected @@ -196,7 +195,6 @@ poweroff preexec probesize protobuf -pstate psutil pubkey putenv @@ -280,7 +278,6 @@ uvicorn vaapi vainfo variations -vbios vconcat vitb vstream diff --git a/frigate/test/http_api/__init__.py b/frigate/test/http_api/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frigate/test/http_api/base_http_test.py b/frigate/test/http_api/base_http_test.py deleted file mode 100644 index 565e65a610..0000000000 --- a/frigate/test/http_api/base_http_test.py +++ /dev/null @@ -1,143 +0,0 @@ -import datetime -import logging -import os -import unittest - -from peewee_migrate import Router -from playhouse.sqlite_ext import SqliteExtDatabase -from playhouse.sqliteq import SqliteQueueDatabase - -from frigate.api.fastapi_app import create_fastapi_app -from frigate.config import FrigateConfig -from frigate.models import Event -from frigate.test.const import TEST_DB, TEST_DB_CLEANUPS - - -class BaseTestHttp(unittest.TestCase): - def setUp(self, models): - # setup clean database for each test run - migrate_db = SqliteExtDatabase("test.db") - del logging.getLogger("peewee_migrate").handlers[:] - router = Router(migrate_db) - router.run() - migrate_db.close() - self.db = SqliteQueueDatabase(TEST_DB) - self.db.bind(models) - - self.minimal_config = { - "mqtt": {"host": "mqtt"}, - "cameras": { - "front_door": { - "ffmpeg": { - "inputs": [ - {"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]} - ] - }, - "detect": { - "height": 1080, - "width": 1920, - "fps": 5, - }, - } - }, - } - self.test_stats = { - "detection_fps": 13.7, - "detectors": { - "cpu1": { - "detection_start": 0.0, - "inference_speed": 91.43, - "pid": 42, - }, - "cpu2": { - "detection_start": 0.0, - "inference_speed": 84.99, - "pid": 44, - }, - }, - "front_door": { - "camera_fps": 0.0, - "capture_pid": 53, - "detection_fps": 0.0, - "pid": 52, - "process_fps": 0.0, - "skipped_fps": 0.0, - }, - "service": { - "storage": { - "/dev/shm": { - "free": 50.5, - "mount_type": "tmpfs", - "total": 67.1, - "used": 16.6, - }, - "/media/frigate/clips": { - "free": 42429.9, - "mount_type": "ext4", - "total": 244529.7, - "used": 189607.0, - }, - "/media/frigate/recordings": { - "free": 0.2, - "mount_type": "ext4", - "total": 8.0, - "used": 7.8, - }, - "/tmp/cache": { - "free": 976.8, - "mount_type": "tmpfs", - "total": 1000.0, - "used": 23.2, - }, - }, - "uptime": 101113, - "version": "0.10.1", - "latest_version": "0.11", - }, - } - - def tearDown(self): - if not self.db.is_closed(): - self.db.close() - - try: - for file in TEST_DB_CLEANUPS: - os.remove(file) - except OSError: - pass - - def create_app(self, stats= None): - return create_fastapi_app( - FrigateConfig(**self.minimal_config), - self.db, - None, - None, - None, - None, - None, - stats, - None, - ) - - def insert_mock_event( - self, - id: str, - start_time: datetime.datetime = datetime.datetime.now().timestamp(), - ) -> Event: - """Inserts a basic event model with a given id.""" - return Event.insert( - id=id, - label="Mock", - camera="front_door", - start_time=start_time, - end_time=start_time + 20, - top_score=100, - false_positive=False, - zones=list(), - thumbnail="", - region=[], - box=[], - area=0, - has_clip=True, - has_snapshot=True, - ).execute() diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py deleted file mode 100644 index fd50f40c94..0000000000 --- a/frigate/test/http_api/test_http_review.py +++ /dev/null @@ -1,20 +0,0 @@ -from fastapi.testclient import TestClient - -from frigate.models import Event, ReviewSegment -from frigate.test.http_api.base_http_test import BaseTestHttp - - -class TestHttpReview(BaseTestHttp): - def setUp(self): - super().setUp([Event, ReviewSegment]) - - def test_get_bad_event(self): - app = super().create_app() - id = "123456.random" - bad_id = "654321.other" - - with TestClient(app) as client: - super().insert_mock_event(id) - event_response = client.get(f"/events/{bad_id}") - assert event_response.status_code == 404 - assert event_response.json() == "Event not found" From 3b61d3c4f1b24aa6c49fdc2e3d75848e49eba8be Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Wed, 20 Nov 2024 14:10:18 +0000 Subject: [PATCH 03/15] Added test for review endpoint (severity filter) --- frigate/test/http_api/base_http_test.py | 3 +- frigate/test/http_api/test_http_review.py | 53 ++++++++++++++++++----- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/frigate/test/http_api/base_http_test.py b/frigate/test/http_api/base_http_test.py index 013785692a..ee4de1ec02 100644 --- a/frigate/test/http_api/base_http_test.py +++ b/frigate/test/http_api/base_http_test.py @@ -148,6 +148,7 @@ def insert_mock_review_segment( id: str, start_time: datetime.datetime = datetime.datetime.now().timestamp(), end_time: datetime.datetime = datetime.datetime.now().timestamp() + 20, + severity:SeverityEnum = SeverityEnum.alert, ) -> Event: """Inserts a basic event model with a given id.""" return ReviewSegment.insert( @@ -156,7 +157,7 @@ def insert_mock_review_segment( start_time=start_time, end_time=end_time, has_been_reviewed=False, - severity=SeverityEnum.alert, + severity=severity, thumb_path=False, data={}, ).execute() diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py index 19e1f26f83..57c92934c5 100644 --- a/frigate/test/http_api/test_http_review.py +++ b/frigate/test/http_api/test_http_review.py @@ -3,19 +3,20 @@ from fastapi.testclient import TestClient from frigate.models import Event, ReviewSegment +from frigate.review.maintainer import SeverityEnum from frigate.test.http_api.base_http_test import BaseTestHttp class TestHttpReview(BaseTestHttp): def setUp(self): super().setUp([Event, ReviewSegment]) + self.app = super().create_app() # Does not return any data point since the end time (before parameter) is not passed and the review segment end_time is 2 seconds from now def test_get_review_no_filters_no_matches(self): - app = super().create_app() now = datetime.datetime.now().timestamp() - with TestClient(app) as client: + with TestClient(self.app) as client: super().insert_mock_review_segment("123456.random", now, now + 2) reviews_response = client.get("/review") assert reviews_response.status_code == 200 @@ -23,10 +24,9 @@ def test_get_review_no_filters_no_matches(self): assert len(reviews_in_response) == 0 def test_get_review_no_filters(self): - app = super().create_app() now = datetime.datetime.now().timestamp() - with TestClient(app) as client: + with TestClient(self.app) as client: super().insert_mock_review_segment("123456.random", now - 2, now - 1) reviews_response = client.get("/review") assert reviews_response.status_code == 200 @@ -34,10 +34,9 @@ def test_get_review_no_filters(self): assert len(reviews_in_response) == 1 def test_get_review_with_time_filter_no_matches(self): - app = super().create_app() now = datetime.datetime.now().timestamp() - with TestClient(app) as client: + with TestClient(self.app) as client: id = "123456.random" super().insert_mock_review_segment(id, now, now + 2) params = { @@ -50,10 +49,9 @@ def test_get_review_with_time_filter_no_matches(self): assert len(reviews_in_response) == 0 def test_get_review_with_time_filter(self): - app = super().create_app() now = datetime.datetime.now().timestamp() - with TestClient(app) as client: + with TestClient(self.app) as client: id = "123456.random" super().insert_mock_review_segment(id, now, now + 2) params = { @@ -67,10 +65,9 @@ def test_get_review_with_time_filter(self): assert reviews_in_response[0]["id"] == id def test_get_review_with_limit_filter(self): - app = super().create_app() now = datetime.datetime.now().timestamp() - with TestClient(app) as client: + with TestClient(self.app) as client: id = "123456.random" id2 = "654321.random" super().insert_mock_review_segment(id, now, now + 2) @@ -86,11 +83,43 @@ def test_get_review_with_limit_filter(self): assert len(reviews_in_response) == 1 assert reviews_in_response[0]["id"] == id2 + def test_get_review_with_severity_filters_no_matches(self): + now = datetime.datetime.now().timestamp() + + with TestClient(self.app) as client: + id = "123456.random" + super().insert_mock_review_segment(id, now, now + 2, SeverityEnum.detection) + params = { + "severity": "detection", + "after": now - 1, + "before": now + 3, + } + reviews_response = client.get("/review", params=params) + assert reviews_response.status_code == 200 + reviews_in_response = reviews_response.json() + assert len(reviews_in_response) == 1 + assert reviews_in_response[0]["id"] == id + + def test_get_review_with_severity_filters(self): + now = datetime.datetime.now().timestamp() + + with TestClient(self.app) as client: + id = "123456.random" + super().insert_mock_review_segment(id, now, now + 2, SeverityEnum.detection) + params = { + "severity": "alert", + "after": now - 1, + "before": now + 3, + } + reviews_response = client.get("/review", params=params) + assert reviews_response.status_code == 200 + reviews_in_response = reviews_response.json() + assert len(reviews_in_response) == 0 + def test_get_review_with_all_filters(self): - app = super().create_app() now = datetime.datetime.now().timestamp() - with TestClient(app) as client: + with TestClient(self.app) as client: id = "123456.random" super().insert_mock_review_segment(id, now, now + 2) params = { From 731a887d984a300a301b74dc094a8a6ee21287fc Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Sun, 24 Nov 2024 10:28:42 +0000 Subject: [PATCH 04/15] Added tests for review summary endpoint (with filters/no filters) --- frigate/test/http_api/test_http_review.py | 62 +++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py index 57c92934c5..fc944d856f 100644 --- a/frigate/test/http_api/test_http_review.py +++ b/frigate/test/http_api/test_http_review.py @@ -12,6 +12,10 @@ def setUp(self): super().setUp([Event, ReviewSegment]) self.app = super().create_app() + #################################################################################################################### + ################################### Review Endpoint ############################################################# + #################################################################################################################### + # Does not return any data point since the end time (before parameter) is not passed and the review segment end_time is 2 seconds from now def test_get_review_no_filters_no_matches(self): now = datetime.datetime.now().timestamp() @@ -137,3 +141,61 @@ def test_get_review_with_all_filters(self): reviews_in_response = reviews_response.json() assert len(reviews_in_response) == 1 assert reviews_in_response[0]["id"] == id + + #################################################################################################################### + ################################### Review Summary Endpoint ##################################################### + #################################################################################################################### + def test_get_review_summary_all_filters(self): + with TestClient(self.app) as client: + id = "123456.random" + super().insert_mock_review_segment(id) + params = { + "cameras": "front_door", + "labels": "all", + "zones": "all", + "timezone": "utc", + } + review_summary_request = client.get("/review/summary", params=params) + assert review_summary_request.status_code == 200 + review_summary_response = review_summary_request.json() + today = datetime.date.today().strftime('%Y-%m-%d') # e.g. '2024-11-24' + expected_response = { + 'last24Hours': { + 'reviewed_alert': 0, + 'reviewed_detection': 0, + 'total_alert': 1, + 'total_detection': 0 + }, + today: { + 'day': today, + 'reviewed_alert': 0, + 'reviewed_detection': 0, + 'total_alert': 1, + 'total_detection': 0 + } + } + self.assertEqual(review_summary_response, expected_response) + + def test_get_review_summary_no_filters(self): + with TestClient(self.app) as client: + super().insert_mock_review_segment("123456.random") + review_summary_request = client.get("/review/summary") + assert review_summary_request.status_code == 200 + review_summary_response = review_summary_request.json() + today = datetime.date.today().strftime('%Y-%m-%d') # e.g. '2024-11-24' + expected_response = { + 'last24Hours': { + 'reviewed_alert': 0, + 'reviewed_detection': 0, + 'total_alert': 1, + 'total_detection': 0 + }, + today: { + 'day': today, + 'reviewed_alert': 0, + 'reviewed_detection': 0, + 'total_alert': 1, + 'total_detection': 0 + } + } + self.assertEqual(review_summary_response, expected_response) \ No newline at end of file From 22408e9e484eae436faefea5b197d69893a2d81a Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Sun, 24 Nov 2024 10:59:13 +0000 Subject: [PATCH 05/15] Added tests for review summary endpoint (multiple days with alerts in the last month) --- frigate/api/review.py | 3 +- frigate/test/http_api/test_http_review.py | 112 +++++++++++++++------- 2 files changed, 78 insertions(+), 37 deletions(-) diff --git a/frigate/api/review.py b/frigate/api/review.py index 21b4686404..1caa7f8a51 100644 --- a/frigate/api/review.py +++ b/frigate/api/review.py @@ -230,6 +230,7 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): label_clause = reduce(operator.or_, label_clauses) clauses.append((label_clause)) + day_in_seconds = 60 * 60 * 24 last_month = ( ReviewSegment.select( fn.strftime( @@ -292,7 +293,7 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): ) .where(reduce(operator.and_, clauses)) .group_by( - (ReviewSegment.start_time + seconds_offset).cast("int") / (3600 * 24), + (ReviewSegment.start_time + seconds_offset).cast("int") / day_in_seconds, ) .order_by(ReviewSegment.start_time.desc()) ) diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py index fc944d856f..6239f6fa7b 100644 --- a/frigate/test/http_api/test_http_review.py +++ b/frigate/test/http_api/test_http_review.py @@ -1,4 +1,4 @@ -import datetime +from datetime import datetime, timedelta from fastapi.testclient import TestClient @@ -18,7 +18,7 @@ def setUp(self): # Does not return any data point since the end time (before parameter) is not passed and the review segment end_time is 2 seconds from now def test_get_review_no_filters_no_matches(self): - now = datetime.datetime.now().timestamp() + now = datetime.now().timestamp() with TestClient(self.app) as client: super().insert_mock_review_segment("123456.random", now, now + 2) @@ -28,7 +28,7 @@ def test_get_review_no_filters_no_matches(self): assert len(reviews_in_response) == 0 def test_get_review_no_filters(self): - now = datetime.datetime.now().timestamp() + now = datetime.now().timestamp() with TestClient(self.app) as client: super().insert_mock_review_segment("123456.random", now - 2, now - 1) @@ -38,7 +38,7 @@ def test_get_review_no_filters(self): assert len(reviews_in_response) == 1 def test_get_review_with_time_filter_no_matches(self): - now = datetime.datetime.now().timestamp() + now = datetime.now().timestamp() with TestClient(self.app) as client: id = "123456.random" @@ -53,7 +53,7 @@ def test_get_review_with_time_filter_no_matches(self): assert len(reviews_in_response) == 0 def test_get_review_with_time_filter(self): - now = datetime.datetime.now().timestamp() + now = datetime.now().timestamp() with TestClient(self.app) as client: id = "123456.random" @@ -69,7 +69,7 @@ def test_get_review_with_time_filter(self): assert reviews_in_response[0]["id"] == id def test_get_review_with_limit_filter(self): - now = datetime.datetime.now().timestamp() + now = datetime.now().timestamp() with TestClient(self.app) as client: id = "123456.random" @@ -88,7 +88,7 @@ def test_get_review_with_limit_filter(self): assert reviews_in_response[0]["id"] == id2 def test_get_review_with_severity_filters_no_matches(self): - now = datetime.datetime.now().timestamp() + now = datetime.now().timestamp() with TestClient(self.app) as client: id = "123456.random" @@ -105,7 +105,7 @@ def test_get_review_with_severity_filters_no_matches(self): assert reviews_in_response[0]["id"] == id def test_get_review_with_severity_filters(self): - now = datetime.datetime.now().timestamp() + now = datetime.now().timestamp() with TestClient(self.app) as client: id = "123456.random" @@ -121,7 +121,7 @@ def test_get_review_with_severity_filters(self): assert len(reviews_in_response) == 0 def test_get_review_with_all_filters(self): - now = datetime.datetime.now().timestamp() + now = datetime.now().timestamp() with TestClient(self.app) as client: id = "123456.random" @@ -158,21 +158,22 @@ def test_get_review_summary_all_filters(self): review_summary_request = client.get("/review/summary", params=params) assert review_summary_request.status_code == 200 review_summary_response = review_summary_request.json() - today = datetime.date.today().strftime('%Y-%m-%d') # e.g. '2024-11-24' + # e.g. '2024-11-24' + today_formatted = datetime.date.today().strftime("%Y-%m-%d") expected_response = { - 'last24Hours': { - 'reviewed_alert': 0, - 'reviewed_detection': 0, - 'total_alert': 1, - 'total_detection': 0 + "last24Hours": { + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 1, + "total_detection": 0, + }, + today_formatted: { + "day": today_formatted, + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 1, + "total_detection": 0, }, - today: { - 'day': today, - 'reviewed_alert': 0, - 'reviewed_detection': 0, - 'total_alert': 1, - 'total_detection': 0 - } } self.assertEqual(review_summary_response, expected_response) @@ -182,20 +183,59 @@ def test_get_review_summary_no_filters(self): review_summary_request = client.get("/review/summary") assert review_summary_request.status_code == 200 review_summary_response = review_summary_request.json() - today = datetime.date.today().strftime('%Y-%m-%d') # e.g. '2024-11-24' + # e.g. '2024-11-24' + today_formatted = datetime.date.today().strftime("%Y-%m-%d") expected_response = { - 'last24Hours': { - 'reviewed_alert': 0, - 'reviewed_detection': 0, - 'total_alert': 1, - 'total_detection': 0 + "last24Hours": { + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 1, + "total_detection": 0, + }, + today_formatted: { + "day": today_formatted, + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 1, + "total_detection": 0, }, - today: { - 'day': today, - 'reviewed_alert': 0, - 'reviewed_detection': 0, - 'total_alert': 1, - 'total_detection': 0 - } } - self.assertEqual(review_summary_response, expected_response) \ No newline at end of file + self.assertEqual(review_summary_response, expected_response) + + def test_get_review_summary_multiple_days(self): + now = datetime.now() + five_days_ago = datetime.today() - timedelta(days=5) + + with TestClient(self.app) as client: + super().insert_mock_review_segment("123456.random", now.timestamp() - 2, now.timestamp() - 1) + super().insert_mock_review_segment("654321.random", five_days_ago.timestamp(), five_days_ago.timestamp() + 1) + review_summary_request = client.get("/review/summary") + assert review_summary_request.status_code == 200 + review_summary_response = review_summary_request.json() + # e.g. '2024-11-24' + today_formatted = now.strftime("%Y-%m-%d") + # e.g. '2024-11-19' + five_days_ago_formatted = five_days_ago.strftime("%Y-%m-%d") + expected_response = { + "last24Hours": { + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 1, + "total_detection": 0, + }, + today_formatted: { + "day": today_formatted, + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 1, + "total_detection": 0, + }, + five_days_ago_formatted: { + "day": five_days_ago_formatted, + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 1, + "total_detection": 0, + }, + } + self.assertEqual(review_summary_response, expected_response) From 6ad57af9b1a1c2bbd9b1fc26f3687ca22478a4c2 Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Sun, 24 Nov 2024 12:05:05 +0000 Subject: [PATCH 06/15] Added tests for review summary endpoint (multiple days with alerts in the last month and edge case) --- frigate/test/http_api/test_http_review.py | 63 +++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py index 6239f6fa7b..33d3101c42 100644 --- a/frigate/test/http_api/test_http_review.py +++ b/frigate/test/http_api/test_http_review.py @@ -239,3 +239,66 @@ def test_get_review_summary_multiple_days(self): }, } self.assertEqual(review_summary_response, expected_response) + + def test_get_review_summary_multiple_days_edge_cases(self): + now = datetime.now() + five_days_ago = datetime.today() - timedelta(days=5) + twenty_days_ago = datetime.today() - timedelta(days=20) + one_month_ago = datetime.today() - timedelta(days=30) + + with TestClient(self.app) as client: + super().insert_mock_review_segment("123456.random", now.timestamp()) + super().insert_mock_review_segment("123457.random", five_days_ago.timestamp()) + super().insert_mock_review_segment("123458.random", twenty_days_ago.timestamp(), None, SeverityEnum.detection) + # One month ago plus 5 seconds fits within the condition (review.start_time > month_ago). Assuming that the endpoint does not take more than 5 seconds to be invoked + super().insert_mock_review_segment("123459.random", one_month_ago.timestamp() + 5, None, SeverityEnum.detection) + # This won't appear in the output since it's within last month start_time clause (review.start_time > month_ago) + super().insert_mock_review_segment("123450.random", one_month_ago.timestamp() + 0) + review_summary_request = client.get("/review/summary") + assert review_summary_request.status_code == 200 + review_summary_response = review_summary_request.json() + # e.g. '2024-11-24' + today_formatted = now.strftime("%Y-%m-%d") + # e.g. '2024-11-19' + five_days_ago_formatted = five_days_ago.strftime("%Y-%m-%d") + # e.g. '2024-11-04' + twenty_days_ago_formatted = five_days_ago.strftime("%Y-%m-%d") + # e.g. '2024-10-24' + one_month_ago_formatted = five_days_ago.strftime("%Y-%m-%d") + expected_response = { + "last24Hours": { + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 1, + "total_detection": 0, + }, + today_formatted: { + "day": today_formatted, + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 1, + "total_detection": 0, + }, + five_days_ago_formatted: { + "day": five_days_ago_formatted, + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 1, + "total_detection": 0, + }, + twenty_days_ago_formatted: { + "day": twenty_days_ago_formatted, + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 0, + "total_detection": 1, + }, + one_month_ago_formatted: { + "day": one_month_ago_formatted, + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 0, + "total_detection": 1, + }, + } + self.assertEqual(review_summary_response, expected_response) From ea7fd7d6ef9642c1638335ee9f2a0127cd85696e Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Sun, 24 Nov 2024 13:22:10 +0000 Subject: [PATCH 07/15] Added test for summary endpoint with multiple reviews in the same day and multiple in the same day with reviewed/not reviewed counters --- frigate/api/review.py | 17 ++--- frigate/test/http_api/base_http_test.py | 5 +- frigate/test/http_api/test_http_review.py | 78 +++++++++++++++++++++++ 3 files changed, 90 insertions(+), 10 deletions(-) diff --git a/frigate/api/review.py b/frigate/api/review.py index 1caa7f8a51..04e3e6dcd5 100644 --- a/frigate/api/review.py +++ b/frigate/api/review.py @@ -26,6 +26,7 @@ ) from frigate.api.defs.tags import Tags from frigate.models import Recordings, ReviewSegment +from frigate.review.maintainer import SeverityEnum from frigate.util.builtin import get_tz_modifiers logger = logging.getLogger(__name__) @@ -161,7 +162,7 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): None, [ ( - (ReviewSegment.severity == "alert"), + (ReviewSegment.severity == SeverityEnum.alert), ReviewSegment.has_been_reviewed, ) ], @@ -173,7 +174,7 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): None, [ ( - (ReviewSegment.severity == "detection"), + (ReviewSegment.severity == SeverityEnum.detection), ReviewSegment.has_been_reviewed, ) ], @@ -185,7 +186,7 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): None, [ ( - (ReviewSegment.severity == "alert"), + (ReviewSegment.severity == SeverityEnum.alert), 1, ) ], @@ -197,7 +198,7 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): None, [ ( - (ReviewSegment.severity == "detection"), + (ReviewSegment.severity == SeverityEnum.detection), 1, ) ], @@ -247,7 +248,7 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): None, [ ( - (ReviewSegment.severity == "alert"), + (ReviewSegment.severity == SeverityEnum.alert), ReviewSegment.has_been_reviewed, ) ], @@ -259,7 +260,7 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): None, [ ( - (ReviewSegment.severity == "detection"), + (ReviewSegment.severity == SeverityEnum.detection), ReviewSegment.has_been_reviewed, ) ], @@ -271,7 +272,7 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): None, [ ( - (ReviewSegment.severity == "alert"), + (ReviewSegment.severity == SeverityEnum.alert), 1, ) ], @@ -283,7 +284,7 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): None, [ ( - (ReviewSegment.severity == "detection"), + (ReviewSegment.severity == SeverityEnum.detection), 1, ) ], diff --git a/frigate/test/http_api/base_http_test.py b/frigate/test/http_api/base_http_test.py index ee4de1ec02..4027bfa989 100644 --- a/frigate/test/http_api/base_http_test.py +++ b/frigate/test/http_api/base_http_test.py @@ -148,7 +148,8 @@ def insert_mock_review_segment( id: str, start_time: datetime.datetime = datetime.datetime.now().timestamp(), end_time: datetime.datetime = datetime.datetime.now().timestamp() + 20, - severity:SeverityEnum = SeverityEnum.alert, + severity: SeverityEnum = SeverityEnum.alert, + has_been_reviewed: bool = False, ) -> Event: """Inserts a basic event model with a given id.""" return ReviewSegment.insert( @@ -156,7 +157,7 @@ def insert_mock_review_segment( camera="front_door", start_time=start_time, end_time=end_time, - has_been_reviewed=False, + has_been_reviewed=has_been_reviewed, severity=severity, thumb_path=False, data={}, diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py index 33d3101c42..87057dfe05 100644 --- a/frigate/test/http_api/test_http_review.py +++ b/frigate/test/http_api/test_http_review.py @@ -1,4 +1,5 @@ from datetime import datetime, timedelta +import json from fastapi.testclient import TestClient @@ -302,3 +303,80 @@ def test_get_review_summary_multiple_days_edge_cases(self): }, } self.assertEqual(review_summary_response, expected_response) + + def test_get_review_summary_multiple_in_same_day(self): + now = datetime.now() + five_days_ago = datetime.today() - timedelta(days=5) + + with TestClient(self.app) as client: + super().insert_mock_review_segment("123456.random", now.timestamp()) + five_days_ago_ts = five_days_ago.timestamp() + for i in range(20): + super().insert_mock_review_segment(f"123456_{i}.random_alert", five_days_ago_ts, five_days_ago_ts, SeverityEnum.alert) + for i in range(15): + super().insert_mock_review_segment(f"123456_{i}.random_detection", five_days_ago_ts, five_days_ago_ts, SeverityEnum.detection) + review_summary_request = client.get("/review/summary") + assert review_summary_request.status_code == 200 + review_summary_response = review_summary_request.json() + # e.g. '2024-11-24' + today_formatted = now.strftime("%Y-%m-%d") + # e.g. '2024-11-19' + five_days_ago_formatted = five_days_ago.strftime("%Y-%m-%d") + expected_response = { + "last24Hours": { + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 1, + "total_detection": 0, + }, + today_formatted: { + "day": today_formatted, + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 1, + "total_detection": 0, + }, + five_days_ago_formatted: { + "day": five_days_ago_formatted, + "reviewed_alert": 0, + "reviewed_detection": 0, + "total_alert": 20, + "total_detection": 15, + }, + } + self.assertEqual(review_summary_response, expected_response) + + def test_get_review_summary_multiple_in_same_day_with_reviewed(self): + five_days_ago = datetime.today() - timedelta(days=5) + + with TestClient(self.app) as client: + five_days_ago_ts = five_days_ago.timestamp() + for i in range(10): + super().insert_mock_review_segment(f"123456_{i}.random_alert_not_reviewed", five_days_ago_ts, five_days_ago_ts, SeverityEnum.alert, False) + for i in range(10): + super().insert_mock_review_segment(f"123456_{i}.random_alert_reviewed", five_days_ago_ts, five_days_ago_ts, SeverityEnum.alert, True) + for i in range(10): + super().insert_mock_review_segment(f"123456_{i}.random_detection_not_reviewed", five_days_ago_ts, five_days_ago_ts, SeverityEnum.detection, False) + for i in range(5): + super().insert_mock_review_segment(f"123456_{i}.random_detection_reviewed", five_days_ago_ts, five_days_ago_ts, SeverityEnum.detection, True) + review_summary_request = client.get("/review/summary") + assert review_summary_request.status_code == 200 + review_summary_response = review_summary_request.json() + # e.g. '2024-11-19' + five_days_ago_formatted = five_days_ago.strftime("%Y-%m-%d") + expected_response = { + "last24Hours": { + "reviewed_alert": None, + "reviewed_detection": None, + "total_alert": None, + "total_detection": None, + }, + five_days_ago_formatted: { + "day": five_days_ago_formatted, + "reviewed_alert": 10, + "reviewed_detection": 5, + "total_alert": 20, + "total_detection": 15, + }, + } + self.assertEqual(review_summary_response, expected_response) \ No newline at end of file From 73f56dcf22cbfbe8d8f7ff06100211d8d293093e Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Sun, 24 Nov 2024 15:11:09 +0000 Subject: [PATCH 08/15] Fix tests --- frigate/test/http_api/test_http_review.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py index 87057dfe05..4c35ae6e00 100644 --- a/frigate/test/http_api/test_http_review.py +++ b/frigate/test/http_api/test_http_review.py @@ -148,8 +148,7 @@ def test_get_review_with_all_filters(self): #################################################################################################################### def test_get_review_summary_all_filters(self): with TestClient(self.app) as client: - id = "123456.random" - super().insert_mock_review_segment(id) + super().insert_mock_review_segment("123456.random") params = { "cameras": "front_door", "labels": "all", @@ -160,7 +159,7 @@ def test_get_review_summary_all_filters(self): assert review_summary_request.status_code == 200 review_summary_response = review_summary_request.json() # e.g. '2024-11-24' - today_formatted = datetime.date.today().strftime("%Y-%m-%d") + today_formatted = datetime.today().strftime("%Y-%m-%d") expected_response = { "last24Hours": { "reviewed_alert": 0, @@ -185,7 +184,7 @@ def test_get_review_summary_no_filters(self): assert review_summary_request.status_code == 200 review_summary_response = review_summary_request.json() # e.g. '2024-11-24' - today_formatted = datetime.date.today().strftime("%Y-%m-%d") + today_formatted = datetime.today().strftime("%Y-%m-%d") expected_response = { "last24Hours": { "reviewed_alert": 0, @@ -263,9 +262,9 @@ def test_get_review_summary_multiple_days_edge_cases(self): # e.g. '2024-11-19' five_days_ago_formatted = five_days_ago.strftime("%Y-%m-%d") # e.g. '2024-11-04' - twenty_days_ago_formatted = five_days_ago.strftime("%Y-%m-%d") + twenty_days_ago_formatted = twenty_days_ago.strftime("%Y-%m-%d") # e.g. '2024-10-24' - one_month_ago_formatted = five_days_ago.strftime("%Y-%m-%d") + one_month_ago_formatted = one_month_ago.strftime("%Y-%m-%d") expected_response = { "last24Hours": { "reviewed_alert": 0, From 6759b5c9bc5fcf9173ad901cbc7821d0701211bf Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Sun, 24 Nov 2024 15:12:41 +0000 Subject: [PATCH 09/15] Format with ruff --- frigate/test/http_api/test_http_review.py | 80 +++++++++++++++++++---- 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py index 4c35ae6e00..61d0249bea 100644 --- a/frigate/test/http_api/test_http_review.py +++ b/frigate/test/http_api/test_http_review.py @@ -207,8 +207,14 @@ def test_get_review_summary_multiple_days(self): five_days_ago = datetime.today() - timedelta(days=5) with TestClient(self.app) as client: - super().insert_mock_review_segment("123456.random", now.timestamp() - 2, now.timestamp() - 1) - super().insert_mock_review_segment("654321.random", five_days_ago.timestamp(), five_days_ago.timestamp() + 1) + super().insert_mock_review_segment( + "123456.random", now.timestamp() - 2, now.timestamp() - 1 + ) + super().insert_mock_review_segment( + "654321.random", + five_days_ago.timestamp(), + five_days_ago.timestamp() + 1, + ) review_summary_request = client.get("/review/summary") assert review_summary_request.status_code == 200 review_summary_response = review_summary_request.json() @@ -248,12 +254,26 @@ def test_get_review_summary_multiple_days_edge_cases(self): with TestClient(self.app) as client: super().insert_mock_review_segment("123456.random", now.timestamp()) - super().insert_mock_review_segment("123457.random", five_days_ago.timestamp()) - super().insert_mock_review_segment("123458.random", twenty_days_ago.timestamp(), None, SeverityEnum.detection) + super().insert_mock_review_segment( + "123457.random", five_days_ago.timestamp() + ) + super().insert_mock_review_segment( + "123458.random", + twenty_days_ago.timestamp(), + None, + SeverityEnum.detection, + ) # One month ago plus 5 seconds fits within the condition (review.start_time > month_ago). Assuming that the endpoint does not take more than 5 seconds to be invoked - super().insert_mock_review_segment("123459.random", one_month_ago.timestamp() + 5, None, SeverityEnum.detection) + super().insert_mock_review_segment( + "123459.random", + one_month_ago.timestamp() + 5, + None, + SeverityEnum.detection, + ) # This won't appear in the output since it's within last month start_time clause (review.start_time > month_ago) - super().insert_mock_review_segment("123450.random", one_month_ago.timestamp() + 0) + super().insert_mock_review_segment( + "123450.random", one_month_ago.timestamp() + 0 + ) review_summary_request = client.get("/review/summary") assert review_summary_request.status_code == 200 review_summary_response = review_summary_request.json() @@ -311,9 +331,19 @@ def test_get_review_summary_multiple_in_same_day(self): super().insert_mock_review_segment("123456.random", now.timestamp()) five_days_ago_ts = five_days_ago.timestamp() for i in range(20): - super().insert_mock_review_segment(f"123456_{i}.random_alert", five_days_ago_ts, five_days_ago_ts, SeverityEnum.alert) + super().insert_mock_review_segment( + f"123456_{i}.random_alert", + five_days_ago_ts, + five_days_ago_ts, + SeverityEnum.alert, + ) for i in range(15): - super().insert_mock_review_segment(f"123456_{i}.random_detection", five_days_ago_ts, five_days_ago_ts, SeverityEnum.detection) + super().insert_mock_review_segment( + f"123456_{i}.random_detection", + five_days_ago_ts, + five_days_ago_ts, + SeverityEnum.detection, + ) review_summary_request = client.get("/review/summary") assert review_summary_request.status_code == 200 review_summary_response = review_summary_request.json() @@ -351,13 +381,37 @@ def test_get_review_summary_multiple_in_same_day_with_reviewed(self): with TestClient(self.app) as client: five_days_ago_ts = five_days_ago.timestamp() for i in range(10): - super().insert_mock_review_segment(f"123456_{i}.random_alert_not_reviewed", five_days_ago_ts, five_days_ago_ts, SeverityEnum.alert, False) + super().insert_mock_review_segment( + f"123456_{i}.random_alert_not_reviewed", + five_days_ago_ts, + five_days_ago_ts, + SeverityEnum.alert, + False, + ) for i in range(10): - super().insert_mock_review_segment(f"123456_{i}.random_alert_reviewed", five_days_ago_ts, five_days_ago_ts, SeverityEnum.alert, True) + super().insert_mock_review_segment( + f"123456_{i}.random_alert_reviewed", + five_days_ago_ts, + five_days_ago_ts, + SeverityEnum.alert, + True, + ) for i in range(10): - super().insert_mock_review_segment(f"123456_{i}.random_detection_not_reviewed", five_days_ago_ts, five_days_ago_ts, SeverityEnum.detection, False) + super().insert_mock_review_segment( + f"123456_{i}.random_detection_not_reviewed", + five_days_ago_ts, + five_days_ago_ts, + SeverityEnum.detection, + False, + ) for i in range(5): - super().insert_mock_review_segment(f"123456_{i}.random_detection_reviewed", five_days_ago_ts, five_days_ago_ts, SeverityEnum.detection, True) + super().insert_mock_review_segment( + f"123456_{i}.random_detection_reviewed", + five_days_ago_ts, + five_days_ago_ts, + SeverityEnum.detection, + True, + ) review_summary_request = client.get("/review/summary") assert review_summary_request.status_code == 200 review_summary_response = review_summary_request.json() @@ -378,4 +432,4 @@ def test_get_review_summary_multiple_in_same_day_with_reviewed(self): "total_detection": 15, }, } - self.assertEqual(review_summary_response, expected_response) \ No newline at end of file + self.assertEqual(review_summary_response, expected_response) From 77f64fef8c77be0f935759936dd636326abcea55 Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Sun, 24 Nov 2024 15:16:05 +0000 Subject: [PATCH 10/15] Minor fixes --- frigate/test/http_api/base_http_test.py | 4 ++-- frigate/test/http_api/test_http_review.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/frigate/test/http_api/base_http_test.py b/frigate/test/http_api/base_http_test.py index 4027bfa989..b31cb47c25 100644 --- a/frigate/test/http_api/base_http_test.py +++ b/frigate/test/http_api/base_http_test.py @@ -146,8 +146,8 @@ def insert_mock_event( def insert_mock_review_segment( self, id: str, - start_time: datetime.datetime = datetime.datetime.now().timestamp(), - end_time: datetime.datetime = datetime.datetime.now().timestamp() + 20, + start_time: float = datetime.datetime.now().timestamp(), + end_time: float = datetime.datetime.now().timestamp() + 20, severity: SeverityEnum = SeverityEnum.alert, has_been_reviewed: bool = False, ) -> Event: diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py index 61d0249bea..279b7f3ce2 100644 --- a/frigate/test/http_api/test_http_review.py +++ b/frigate/test/http_api/test_http_review.py @@ -251,6 +251,7 @@ def test_get_review_summary_multiple_days_edge_cases(self): five_days_ago = datetime.today() - timedelta(days=5) twenty_days_ago = datetime.today() - timedelta(days=20) one_month_ago = datetime.today() - timedelta(days=30) + one_month_ago_ts = one_month_ago.timestamp() with TestClient(self.app) as client: super().insert_mock_review_segment("123456.random", now.timestamp()) @@ -259,7 +260,7 @@ def test_get_review_summary_multiple_days_edge_cases(self): ) super().insert_mock_review_segment( "123458.random", - twenty_days_ago.timestamp(), + one_month_ago_ts, None, SeverityEnum.detection, ) @@ -270,9 +271,9 @@ def test_get_review_summary_multiple_days_edge_cases(self): None, SeverityEnum.detection, ) - # This won't appear in the output since it's within last month start_time clause (review.start_time > month_ago) + # This won't appear in the output since it's not within last month start_time clause (review.start_time > month_ago) super().insert_mock_review_segment( - "123450.random", one_month_ago.timestamp() + 0 + "123450.random", one_month_ago_ts ) review_summary_request = client.get("/review/summary") assert review_summary_request.status_code == 200 From 21eda966d29a93a45f400b90795b7e5c139a0578 Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Sun, 24 Nov 2024 15:16:56 +0000 Subject: [PATCH 11/15] ruff --- frigate/test/http_api/test_http_review.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py index 279b7f3ce2..c85d44b8cc 100644 --- a/frigate/test/http_api/test_http_review.py +++ b/frigate/test/http_api/test_http_review.py @@ -272,9 +272,7 @@ def test_get_review_summary_multiple_days_edge_cases(self): SeverityEnum.detection, ) # This won't appear in the output since it's not within last month start_time clause (review.start_time > month_ago) - super().insert_mock_review_segment( - "123450.random", one_month_ago_ts - ) + super().insert_mock_review_segment("123450.random", one_month_ago_ts) review_summary_request = client.get("/review/summary") assert review_summary_request.status_code == 200 review_summary_response = review_summary_request.json() From 6084d907c047fe3a633582ffa41c04bc3bb54fbe Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Sun, 24 Nov 2024 15:50:59 +0000 Subject: [PATCH 12/15] Added unit tests for POST /reviews/viewed --- frigate/test/http_api/test_http_review.py | 71 +++++++++++++++++++++-- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py index c85d44b8cc..c45c8a35b9 100644 --- a/frigate/test/http_api/test_http_review.py +++ b/frigate/test/http_api/test_http_review.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -import json from fastapi.testclient import TestClient @@ -14,7 +13,7 @@ def setUp(self): self.app = super().create_app() #################################################################################################################### - ################################### Review Endpoint ############################################################# + ################################### GET /review Endpoint ######################################################## #################################################################################################################### # Does not return any data point since the end time (before parameter) is not passed and the review segment end_time is 2 seconds from now @@ -144,7 +143,7 @@ def test_get_review_with_all_filters(self): assert reviews_in_response[0]["id"] == id #################################################################################################################### - ################################### Review Summary Endpoint ##################################################### + ################################### GET /review/summary Endpoint ################################################# #################################################################################################################### def test_get_review_summary_all_filters(self): with TestClient(self.app) as client: @@ -260,14 +259,14 @@ def test_get_review_summary_multiple_days_edge_cases(self): ) super().insert_mock_review_segment( "123458.random", - one_month_ago_ts, + twenty_days_ago.timestamp(), None, SeverityEnum.detection, ) # One month ago plus 5 seconds fits within the condition (review.start_time > month_ago). Assuming that the endpoint does not take more than 5 seconds to be invoked super().insert_mock_review_segment( "123459.random", - one_month_ago.timestamp() + 5, + one_month_ago_ts + 5, None, SeverityEnum.detection, ) @@ -432,3 +431,65 @@ def test_get_review_summary_multiple_in_same_day_with_reviewed(self): }, } self.assertEqual(review_summary_response, expected_response) + + #################################################################################################################### + ################################### POST reviews/viewed Endpoint ################################################ + #################################################################################################################### + def test_post_reviews_viewed_no_body(self): + with TestClient(self.app) as client: + super().insert_mock_review_segment("123456.random") + reviews_mark_viewed_many_response = client.post("/reviews/viewed") + # Missing ids + assert reviews_mark_viewed_many_response.status_code == 422 + + def test_post_reviews_viewed_no_body_ids(self): + with TestClient(self.app) as client: + super().insert_mock_review_segment("123456.random") + body = {"ids": [""]} + reviews_mark_viewed_many_response = client.post( + "/reviews/viewed", json=body + ) + # Missing ids + assert reviews_mark_viewed_many_response.status_code == 422 + + def test_post_reviews_viewed_non_existent_id(self): + with TestClient(self.app) as client: + id = "123456.random" + super().insert_mock_review_segment(id) + body = {"ids": ["1"]} + reviews_mark_viewed_many_request = client.post("/reviews/viewed", json=body) + assert reviews_mark_viewed_many_request.status_code == 200 + reviews_mark_viewed_many_response = reviews_mark_viewed_many_request.json() + assert reviews_mark_viewed_many_response["success"] == True + assert ( + reviews_mark_viewed_many_response["message"] + == "Reviewed multiple items" + ) + # Verify that in DB the review segment was not changed + review_segment_in_db = ( + ReviewSegment.select(ReviewSegment.has_been_reviewed) + .where(ReviewSegment.id == id) + .get() + ) + assert review_segment_in_db.has_been_reviewed == False + + def test_post_reviews_viewed(self): + with TestClient(self.app) as client: + id = "123456.random" + super().insert_mock_review_segment(id) + body = {"ids": [id]} + reviews_mark_viewed_many_request = client.post("/reviews/viewed", json=body) + assert reviews_mark_viewed_many_request.status_code == 200 + reviews_mark_viewed_many_response = reviews_mark_viewed_many_request.json() + assert reviews_mark_viewed_many_response["success"] == True + assert ( + reviews_mark_viewed_many_response["message"] + == "Reviewed multiple items" + ) + # Verify that in DB the review segment was changed + review_segment_in_db = ( + ReviewSegment.select(ReviewSegment.has_been_reviewed) + .where(ReviewSegment.id == id) + .get() + ) + assert review_segment_in_db.has_been_reviewed == True From 3e57e2e5e370487f764078e797e71306a65fa309 Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Sun, 24 Nov 2024 17:13:45 +0000 Subject: [PATCH 13/15] Added unit tests for POST /reviews/delete --- frigate/test/http_api/base_http_test.py | 20 +- frigate/test/http_api/test_http_review.py | 211 ++++++++++++++++------ 2 files changed, 173 insertions(+), 58 deletions(-) diff --git a/frigate/test/http_api/base_http_test.py b/frigate/test/http_api/base_http_test.py index b31cb47c25..ad1d449c5b 100644 --- a/frigate/test/http_api/base_http_test.py +++ b/frigate/test/http_api/base_http_test.py @@ -9,7 +9,7 @@ from frigate.api.fastapi_app import create_fastapi_app from frigate.config import FrigateConfig -from frigate.models import Event, ReviewSegment +from frigate.models import Event, Recordings, ReviewSegment from frigate.review.maintainer import SeverityEnum from frigate.test.const import TEST_DB, TEST_DB_CLEANUPS @@ -151,7 +151,7 @@ def insert_mock_review_segment( severity: SeverityEnum = SeverityEnum.alert, has_been_reviewed: bool = False, ) -> Event: - """Inserts a basic event model with a given id.""" + """Inserts a review segment model with a given id.""" return ReviewSegment.insert( id=id, camera="front_door", @@ -162,3 +162,19 @@ def insert_mock_review_segment( thumb_path=False, data={}, ).execute() + + def insert_mock_recording( + self, + id: str, + start_time: float = datetime.datetime.now().timestamp(), + end_time: float = datetime.datetime.now().timestamp() + 20, + ) -> Event: + """Inserts a recording model with a given id.""" + return Recordings.insert( + id=id, + path=id, + camera="front_door", + start_time=start_time, + end_time=end_time, + duration=end_time - start_time, + ).execute() diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py index c45c8a35b9..bdec482ea2 100644 --- a/frigate/test/http_api/test_http_review.py +++ b/frigate/test/http_api/test_http_review.py @@ -2,16 +2,28 @@ from fastapi.testclient import TestClient -from frigate.models import Event, ReviewSegment +from frigate.models import Event, Recordings, ReviewSegment from frigate.review.maintainer import SeverityEnum from frigate.test.http_api.base_http_test import BaseTestHttp class TestHttpReview(BaseTestHttp): def setUp(self): - super().setUp([Event, ReviewSegment]) + super().setUp([Event, Recordings, ReviewSegment]) self.app = super().create_app() + def _get_reviews(self, ids: list[str]): + return list( + ReviewSegment.select(ReviewSegment.id) + .where(ReviewSegment.id.in_(ids)) + .execute() + ) + + def _get_recordings(self, ids: list[str]): + return list( + Recordings.select(Recordings.id).where(Recordings.id.in_(ids)).execute() + ) + #################################################################################################################### ################################### GET /review Endpoint ######################################################## #################################################################################################################### @@ -24,8 +36,8 @@ def test_get_review_no_filters_no_matches(self): super().insert_mock_review_segment("123456.random", now, now + 2) reviews_response = client.get("/review") assert reviews_response.status_code == 200 - reviews_in_response = reviews_response.json() - assert len(reviews_in_response) == 0 + reviews_response_json = reviews_response.json() + assert len(reviews_response_json) == 0 def test_get_review_no_filters(self): now = datetime.now().timestamp() @@ -34,8 +46,8 @@ def test_get_review_no_filters(self): super().insert_mock_review_segment("123456.random", now - 2, now - 1) reviews_response = client.get("/review") assert reviews_response.status_code == 200 - reviews_in_response = reviews_response.json() - assert len(reviews_in_response) == 1 + reviews_response_json = reviews_response.json() + assert len(reviews_response_json) == 1 def test_get_review_with_time_filter_no_matches(self): now = datetime.now().timestamp() @@ -49,8 +61,8 @@ def test_get_review_with_time_filter_no_matches(self): } reviews_response = client.get("/review", params=params) assert reviews_response.status_code == 200 - reviews_in_response = reviews_response.json() - assert len(reviews_in_response) == 0 + reviews_response_json = reviews_response.json() + assert len(reviews_response_json) == 0 def test_get_review_with_time_filter(self): now = datetime.now().timestamp() @@ -64,9 +76,9 @@ def test_get_review_with_time_filter(self): } reviews_response = client.get("/review", params=params) assert reviews_response.status_code == 200 - reviews_in_response = reviews_response.json() - assert len(reviews_in_response) == 1 - assert reviews_in_response[0]["id"] == id + reviews_response_json = reviews_response.json() + assert len(reviews_response_json) == 1 + assert reviews_response_json[0]["id"] == id def test_get_review_with_limit_filter(self): now = datetime.now().timestamp() @@ -83,9 +95,9 @@ def test_get_review_with_limit_filter(self): } reviews_response = client.get("/review", params=params) assert reviews_response.status_code == 200 - reviews_in_response = reviews_response.json() - assert len(reviews_in_response) == 1 - assert reviews_in_response[0]["id"] == id2 + reviews_response_json = reviews_response.json() + assert len(reviews_response_json) == 1 + assert reviews_response_json[0]["id"] == id2 def test_get_review_with_severity_filters_no_matches(self): now = datetime.now().timestamp() @@ -100,9 +112,9 @@ def test_get_review_with_severity_filters_no_matches(self): } reviews_response = client.get("/review", params=params) assert reviews_response.status_code == 200 - reviews_in_response = reviews_response.json() - assert len(reviews_in_response) == 1 - assert reviews_in_response[0]["id"] == id + reviews_response_json = reviews_response.json() + assert len(reviews_response_json) == 1 + assert reviews_response_json[0]["id"] == id def test_get_review_with_severity_filters(self): now = datetime.now().timestamp() @@ -117,8 +129,8 @@ def test_get_review_with_severity_filters(self): } reviews_response = client.get("/review", params=params) assert reviews_response.status_code == 200 - reviews_in_response = reviews_response.json() - assert len(reviews_in_response) == 0 + reviews_response_json = reviews_response.json() + assert len(reviews_response_json) == 0 def test_get_review_with_all_filters(self): now = datetime.now().timestamp() @@ -138,9 +150,9 @@ def test_get_review_with_all_filters(self): } reviews_response = client.get("/review", params=params) assert reviews_response.status_code == 200 - reviews_in_response = reviews_response.json() - assert len(reviews_in_response) == 1 - assert reviews_in_response[0]["id"] == id + reviews_response_json = reviews_response.json() + assert len(reviews_response_json) == 1 + assert reviews_response_json[0]["id"] == id #################################################################################################################### ################################### GET /review/summary Endpoint ################################################# @@ -154,9 +166,9 @@ def test_get_review_summary_all_filters(self): "zones": "all", "timezone": "utc", } - review_summary_request = client.get("/review/summary", params=params) - assert review_summary_request.status_code == 200 - review_summary_response = review_summary_request.json() + review_summary_response = client.get("/review/summary", params=params) + assert review_summary_response.status_code == 200 + review_summary_response_json = review_summary_response.json() # e.g. '2024-11-24' today_formatted = datetime.today().strftime("%Y-%m-%d") expected_response = { @@ -174,14 +186,14 @@ def test_get_review_summary_all_filters(self): "total_detection": 0, }, } - self.assertEqual(review_summary_response, expected_response) + self.assertEqual(review_summary_response_json, expected_response) def test_get_review_summary_no_filters(self): with TestClient(self.app) as client: super().insert_mock_review_segment("123456.random") - review_summary_request = client.get("/review/summary") - assert review_summary_request.status_code == 200 - review_summary_response = review_summary_request.json() + review_summary_response = client.get("/review/summary") + assert review_summary_response.status_code == 200 + review_summary_response_json = review_summary_response.json() # e.g. '2024-11-24' today_formatted = datetime.today().strftime("%Y-%m-%d") expected_response = { @@ -199,7 +211,7 @@ def test_get_review_summary_no_filters(self): "total_detection": 0, }, } - self.assertEqual(review_summary_response, expected_response) + self.assertEqual(review_summary_response_json, expected_response) def test_get_review_summary_multiple_days(self): now = datetime.now() @@ -214,9 +226,9 @@ def test_get_review_summary_multiple_days(self): five_days_ago.timestamp(), five_days_ago.timestamp() + 1, ) - review_summary_request = client.get("/review/summary") - assert review_summary_request.status_code == 200 - review_summary_response = review_summary_request.json() + review_summary_response = client.get("/review/summary") + assert review_summary_response.status_code == 200 + review_summary_response_json = review_summary_response.json() # e.g. '2024-11-24' today_formatted = now.strftime("%Y-%m-%d") # e.g. '2024-11-19' @@ -243,7 +255,7 @@ def test_get_review_summary_multiple_days(self): "total_detection": 0, }, } - self.assertEqual(review_summary_response, expected_response) + self.assertEqual(review_summary_response_json, expected_response) def test_get_review_summary_multiple_days_edge_cases(self): now = datetime.now() @@ -272,9 +284,9 @@ def test_get_review_summary_multiple_days_edge_cases(self): ) # This won't appear in the output since it's not within last month start_time clause (review.start_time > month_ago) super().insert_mock_review_segment("123450.random", one_month_ago_ts) - review_summary_request = client.get("/review/summary") - assert review_summary_request.status_code == 200 - review_summary_response = review_summary_request.json() + review_summary_response = client.get("/review/summary") + assert review_summary_response.status_code == 200 + review_summary_response_json = review_summary_response.json() # e.g. '2024-11-24' today_formatted = now.strftime("%Y-%m-%d") # e.g. '2024-11-19' @@ -319,7 +331,7 @@ def test_get_review_summary_multiple_days_edge_cases(self): "total_detection": 1, }, } - self.assertEqual(review_summary_response, expected_response) + self.assertEqual(review_summary_response_json, expected_response) def test_get_review_summary_multiple_in_same_day(self): now = datetime.now() @@ -342,9 +354,9 @@ def test_get_review_summary_multiple_in_same_day(self): five_days_ago_ts, SeverityEnum.detection, ) - review_summary_request = client.get("/review/summary") - assert review_summary_request.status_code == 200 - review_summary_response = review_summary_request.json() + review_summary_response = client.get("/review/summary") + assert review_summary_response.status_code == 200 + review_summary_response_json = review_summary_response.json() # e.g. '2024-11-24' today_formatted = now.strftime("%Y-%m-%d") # e.g. '2024-11-19' @@ -371,7 +383,7 @@ def test_get_review_summary_multiple_in_same_day(self): "total_detection": 15, }, } - self.assertEqual(review_summary_response, expected_response) + self.assertEqual(review_summary_response_json, expected_response) def test_get_review_summary_multiple_in_same_day_with_reviewed(self): five_days_ago = datetime.today() - timedelta(days=5) @@ -410,9 +422,9 @@ def test_get_review_summary_multiple_in_same_day_with_reviewed(self): SeverityEnum.detection, True, ) - review_summary_request = client.get("/review/summary") - assert review_summary_request.status_code == 200 - review_summary_response = review_summary_request.json() + review_summary_response = client.get("/review/summary") + assert review_summary_response.status_code == 200 + review_summary_response_json = review_summary_response.json() # e.g. '2024-11-19' five_days_ago_formatted = five_days_ago.strftime("%Y-%m-%d") expected_response = { @@ -430,7 +442,7 @@ def test_get_review_summary_multiple_in_same_day_with_reviewed(self): "total_detection": 15, }, } - self.assertEqual(review_summary_response, expected_response) + self.assertEqual(review_summary_response_json, expected_response) #################################################################################################################### ################################### POST reviews/viewed Endpoint ################################################ @@ -457,12 +469,16 @@ def test_post_reviews_viewed_non_existent_id(self): id = "123456.random" super().insert_mock_review_segment(id) body = {"ids": ["1"]} - reviews_mark_viewed_many_request = client.post("/reviews/viewed", json=body) - assert reviews_mark_viewed_many_request.status_code == 200 - reviews_mark_viewed_many_response = reviews_mark_viewed_many_request.json() - assert reviews_mark_viewed_many_response["success"] == True + reviews_mark_viewed_many_response = client.post( + "/reviews/viewed", json=body + ) + assert reviews_mark_viewed_many_response.status_code == 200 + reviews_mark_viewed_many_response_json = ( + reviews_mark_viewed_many_response.json() + ) + assert reviews_mark_viewed_many_response_json["success"] == True assert ( - reviews_mark_viewed_many_response["message"] + reviews_mark_viewed_many_response_json["message"] == "Reviewed multiple items" ) # Verify that in DB the review segment was not changed @@ -478,12 +494,16 @@ def test_post_reviews_viewed(self): id = "123456.random" super().insert_mock_review_segment(id) body = {"ids": [id]} - reviews_mark_viewed_many_request = client.post("/reviews/viewed", json=body) - assert reviews_mark_viewed_many_request.status_code == 200 - reviews_mark_viewed_many_response = reviews_mark_viewed_many_request.json() - assert reviews_mark_viewed_many_response["success"] == True + reviews_mark_viewed_many_response = client.post( + "/reviews/viewed", json=body + ) + assert reviews_mark_viewed_many_response.status_code == 200 + reviews_mark_viewed_many_response_json = ( + reviews_mark_viewed_many_response.json() + ) + assert reviews_mark_viewed_many_response_json["success"] == True assert ( - reviews_mark_viewed_many_response["message"] + reviews_mark_viewed_many_response_json["message"] == "Reviewed multiple items" ) # Verify that in DB the review segment was changed @@ -493,3 +513,82 @@ def test_post_reviews_viewed(self): .get() ) assert review_segment_in_db.has_been_reviewed == True + + #################################################################################################################### + ################################### POST reviews/delete Endpoint ################################################ + #################################################################################################################### + def test_post_reviews_delete_no_body(self): + with TestClient(self.app) as client: + super().insert_mock_review_segment("123456.random") + reviews_delete_many_response = client.post("/reviews/delete") + # Missing ids + assert reviews_delete_many_response.status_code == 422 + + def test_post_reviews_delete_no_body_ids(self): + with TestClient(self.app) as client: + super().insert_mock_review_segment("123456.random") + body = {"ids": [""]} + reviews_delete_many_response = client.post("/reviews/delete", json=body) + # Missing ids + assert reviews_delete_many_response.status_code == 422 + + def test_post_reviews_delete_non_existent_id(self): + with TestClient(self.app) as client: + id = "123456.random" + super().insert_mock_review_segment(id) + body = {"ids": ["1"]} + reviews_delete_many_response = client.post("/reviews/delete", json=body) + assert reviews_delete_many_response.status_code == 200 + reviews_delete_many_response_json = reviews_delete_many_response.json() + assert reviews_delete_many_response_json["success"] == True + assert reviews_delete_many_response_json["message"] == "Delete reviews" + # Verify that in DB the review segment was not deleted + review_segment_in_db = ( + ReviewSegment.select(ReviewSegment.id) + .where(ReviewSegment.id == id) + .get() + ) + assert review_segment_in_db.id == id + + def test_post_reviews_delete(self): + with TestClient(self.app) as client: + id = "123456.random" + super().insert_mock_review_segment(id) + body = {"ids": [id]} + reviews_delete_many_response = client.post("/reviews/delete", json=body) + assert reviews_delete_many_response.status_code == 200 + reviews_delete_many_response_json = reviews_delete_many_response.json() + assert reviews_delete_many_response_json["success"] == True + assert reviews_delete_many_response_json["message"] == "Delete reviews" + # Verify that in DB the review segment was deleted + review_segment_in_db = ( + ReviewSegment.select(ReviewSegment.id) + .where(ReviewSegment.id == id) + .get_or_none() + ) + assert review_segment_in_db == None + + def test_post_reviews_delete_many(self): + with TestClient(self.app) as client: + ids = ["123456.random", "654321.random"] + for id in ids: + super().insert_mock_review_segment(id) + super().insert_mock_recording(id) + + review_ids_in_db_before = self._get_reviews(ids) + recordings_ids_in_db_before = self._get_recordings(ids) + assert len(review_ids_in_db_before) == 2 + assert len(recordings_ids_in_db_before) == 2 + + body = {"ids": ids} + reviews_delete_many_response = client.post("/reviews/delete", json=body) + assert reviews_delete_many_response.status_code == 200 + reviews_delete_many_response_json = reviews_delete_many_response.json() + assert reviews_delete_many_response_json["success"] == True + assert reviews_delete_many_response_json["message"] == "Delete reviews" + + # Verify that in DB all review segments and recordings that were passed were deleted + review_ids_in_db_after = self._get_reviews(ids) + recording_ids_in_db_after = self._get_recordings(ids) + assert len(review_ids_in_db_after) == 0 + assert len(recording_ids_in_db_after) == 0 From 6a70e9bd4487f9a7ad67b4df66ce9b30b213c935 Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Sun, 24 Nov 2024 17:23:19 +0000 Subject: [PATCH 14/15] Simplified names to ease readability --- frigate/test/http_api/test_http_review.py | 202 ++++++++++------------ 1 file changed, 93 insertions(+), 109 deletions(-) diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py index bdec482ea2..3bca27ab71 100644 --- a/frigate/test/http_api/test_http_review.py +++ b/frigate/test/http_api/test_http_review.py @@ -34,20 +34,20 @@ def test_get_review_no_filters_no_matches(self): with TestClient(self.app) as client: super().insert_mock_review_segment("123456.random", now, now + 2) - reviews_response = client.get("/review") - assert reviews_response.status_code == 200 - reviews_response_json = reviews_response.json() - assert len(reviews_response_json) == 0 + response = client.get("/review") + assert response.status_code == 200 + response_json = response.json() + assert len(response_json) == 0 def test_get_review_no_filters(self): now = datetime.now().timestamp() with TestClient(self.app) as client: super().insert_mock_review_segment("123456.random", now - 2, now - 1) - reviews_response = client.get("/review") - assert reviews_response.status_code == 200 - reviews_response_json = reviews_response.json() - assert len(reviews_response_json) == 1 + response = client.get("/review") + assert response.status_code == 200 + response_json = response.json() + assert len(response_json) == 1 def test_get_review_with_time_filter_no_matches(self): now = datetime.now().timestamp() @@ -59,10 +59,10 @@ def test_get_review_with_time_filter_no_matches(self): "after": now, "before": now + 3, } - reviews_response = client.get("/review", params=params) - assert reviews_response.status_code == 200 - reviews_response_json = reviews_response.json() - assert len(reviews_response_json) == 0 + response = client.get("/review", params=params) + assert response.status_code == 200 + response_json = response.json() + assert len(response_json) == 0 def test_get_review_with_time_filter(self): now = datetime.now().timestamp() @@ -74,11 +74,11 @@ def test_get_review_with_time_filter(self): "after": now - 1, "before": now + 3, } - reviews_response = client.get("/review", params=params) - assert reviews_response.status_code == 200 - reviews_response_json = reviews_response.json() - assert len(reviews_response_json) == 1 - assert reviews_response_json[0]["id"] == id + response = client.get("/review", params=params) + assert response.status_code == 200 + response_json = response.json() + assert len(response_json) == 1 + assert response_json[0]["id"] == id def test_get_review_with_limit_filter(self): now = datetime.now().timestamp() @@ -93,11 +93,11 @@ def test_get_review_with_limit_filter(self): "after": now, "before": now + 3, } - reviews_response = client.get("/review", params=params) - assert reviews_response.status_code == 200 - reviews_response_json = reviews_response.json() - assert len(reviews_response_json) == 1 - assert reviews_response_json[0]["id"] == id2 + response = client.get("/review", params=params) + assert response.status_code == 200 + response_json = response.json() + assert len(response_json) == 1 + assert response_json[0]["id"] == id2 def test_get_review_with_severity_filters_no_matches(self): now = datetime.now().timestamp() @@ -110,11 +110,11 @@ def test_get_review_with_severity_filters_no_matches(self): "after": now - 1, "before": now + 3, } - reviews_response = client.get("/review", params=params) - assert reviews_response.status_code == 200 - reviews_response_json = reviews_response.json() - assert len(reviews_response_json) == 1 - assert reviews_response_json[0]["id"] == id + response = client.get("/review", params=params) + assert response.status_code == 200 + response_json = response.json() + assert len(response_json) == 1 + assert response_json[0]["id"] == id def test_get_review_with_severity_filters(self): now = datetime.now().timestamp() @@ -127,10 +127,10 @@ def test_get_review_with_severity_filters(self): "after": now - 1, "before": now + 3, } - reviews_response = client.get("/review", params=params) - assert reviews_response.status_code == 200 - reviews_response_json = reviews_response.json() - assert len(reviews_response_json) == 0 + response = client.get("/review", params=params) + assert response.status_code == 200 + response_json = response.json() + assert len(response_json) == 0 def test_get_review_with_all_filters(self): now = datetime.now().timestamp() @@ -148,11 +148,11 @@ def test_get_review_with_all_filters(self): "after": now - 1, "before": now + 3, } - reviews_response = client.get("/review", params=params) - assert reviews_response.status_code == 200 - reviews_response_json = reviews_response.json() - assert len(reviews_response_json) == 1 - assert reviews_response_json[0]["id"] == id + response = client.get("/review", params=params) + assert response.status_code == 200 + response_json = response.json() + assert len(response_json) == 1 + assert response_json[0]["id"] == id #################################################################################################################### ################################### GET /review/summary Endpoint ################################################# @@ -166,9 +166,9 @@ def test_get_review_summary_all_filters(self): "zones": "all", "timezone": "utc", } - review_summary_response = client.get("/review/summary", params=params) - assert review_summary_response.status_code == 200 - review_summary_response_json = review_summary_response.json() + response = client.get("/review/summary", params=params) + assert response.status_code == 200 + response_json = response.json() # e.g. '2024-11-24' today_formatted = datetime.today().strftime("%Y-%m-%d") expected_response = { @@ -186,14 +186,14 @@ def test_get_review_summary_all_filters(self): "total_detection": 0, }, } - self.assertEqual(review_summary_response_json, expected_response) + self.assertEqual(response_json, expected_response) def test_get_review_summary_no_filters(self): with TestClient(self.app) as client: super().insert_mock_review_segment("123456.random") - review_summary_response = client.get("/review/summary") - assert review_summary_response.status_code == 200 - review_summary_response_json = review_summary_response.json() + response = client.get("/review/summary") + assert response.status_code == 200 + response_json = response.json() # e.g. '2024-11-24' today_formatted = datetime.today().strftime("%Y-%m-%d") expected_response = { @@ -211,7 +211,7 @@ def test_get_review_summary_no_filters(self): "total_detection": 0, }, } - self.assertEqual(review_summary_response_json, expected_response) + self.assertEqual(response_json, expected_response) def test_get_review_summary_multiple_days(self): now = datetime.now() @@ -226,9 +226,9 @@ def test_get_review_summary_multiple_days(self): five_days_ago.timestamp(), five_days_ago.timestamp() + 1, ) - review_summary_response = client.get("/review/summary") - assert review_summary_response.status_code == 200 - review_summary_response_json = review_summary_response.json() + response = client.get("/review/summary") + assert response.status_code == 200 + response_json = response.json() # e.g. '2024-11-24' today_formatted = now.strftime("%Y-%m-%d") # e.g. '2024-11-19' @@ -255,7 +255,7 @@ def test_get_review_summary_multiple_days(self): "total_detection": 0, }, } - self.assertEqual(review_summary_response_json, expected_response) + self.assertEqual(response_json, expected_response) def test_get_review_summary_multiple_days_edge_cases(self): now = datetime.now() @@ -284,9 +284,9 @@ def test_get_review_summary_multiple_days_edge_cases(self): ) # This won't appear in the output since it's not within last month start_time clause (review.start_time > month_ago) super().insert_mock_review_segment("123450.random", one_month_ago_ts) - review_summary_response = client.get("/review/summary") - assert review_summary_response.status_code == 200 - review_summary_response_json = review_summary_response.json() + response = client.get("/review/summary") + assert response.status_code == 200 + response_json = response.json() # e.g. '2024-11-24' today_formatted = now.strftime("%Y-%m-%d") # e.g. '2024-11-19' @@ -331,7 +331,7 @@ def test_get_review_summary_multiple_days_edge_cases(self): "total_detection": 1, }, } - self.assertEqual(review_summary_response_json, expected_response) + self.assertEqual(response_json, expected_response) def test_get_review_summary_multiple_in_same_day(self): now = datetime.now() @@ -354,9 +354,9 @@ def test_get_review_summary_multiple_in_same_day(self): five_days_ago_ts, SeverityEnum.detection, ) - review_summary_response = client.get("/review/summary") - assert review_summary_response.status_code == 200 - review_summary_response_json = review_summary_response.json() + response = client.get("/review/summary") + assert response.status_code == 200 + response_json = response.json() # e.g. '2024-11-24' today_formatted = now.strftime("%Y-%m-%d") # e.g. '2024-11-19' @@ -383,7 +383,7 @@ def test_get_review_summary_multiple_in_same_day(self): "total_detection": 15, }, } - self.assertEqual(review_summary_response_json, expected_response) + self.assertEqual(response_json, expected_response) def test_get_review_summary_multiple_in_same_day_with_reviewed(self): five_days_ago = datetime.today() - timedelta(days=5) @@ -422,9 +422,9 @@ def test_get_review_summary_multiple_in_same_day_with_reviewed(self): SeverityEnum.detection, True, ) - review_summary_response = client.get("/review/summary") - assert review_summary_response.status_code == 200 - review_summary_response_json = review_summary_response.json() + response = client.get("/review/summary") + assert response.status_code == 200 + response_json = response.json() # e.g. '2024-11-19' five_days_ago_formatted = five_days_ago.strftime("%Y-%m-%d") expected_response = { @@ -442,7 +442,7 @@ def test_get_review_summary_multiple_in_same_day_with_reviewed(self): "total_detection": 15, }, } - self.assertEqual(review_summary_response_json, expected_response) + self.assertEqual(response_json, expected_response) #################################################################################################################### ################################### POST reviews/viewed Endpoint ################################################ @@ -450,37 +450,28 @@ def test_get_review_summary_multiple_in_same_day_with_reviewed(self): def test_post_reviews_viewed_no_body(self): with TestClient(self.app) as client: super().insert_mock_review_segment("123456.random") - reviews_mark_viewed_many_response = client.post("/reviews/viewed") + response = client.post("/reviews/viewed") # Missing ids - assert reviews_mark_viewed_many_response.status_code == 422 + assert response.status_code == 422 def test_post_reviews_viewed_no_body_ids(self): with TestClient(self.app) as client: super().insert_mock_review_segment("123456.random") body = {"ids": [""]} - reviews_mark_viewed_many_response = client.post( - "/reviews/viewed", json=body - ) + response = client.post("/reviews/viewed", json=body) # Missing ids - assert reviews_mark_viewed_many_response.status_code == 422 + assert response.status_code == 422 def test_post_reviews_viewed_non_existent_id(self): with TestClient(self.app) as client: id = "123456.random" super().insert_mock_review_segment(id) body = {"ids": ["1"]} - reviews_mark_viewed_many_response = client.post( - "/reviews/viewed", json=body - ) - assert reviews_mark_viewed_many_response.status_code == 200 - reviews_mark_viewed_many_response_json = ( - reviews_mark_viewed_many_response.json() - ) - assert reviews_mark_viewed_many_response_json["success"] == True - assert ( - reviews_mark_viewed_many_response_json["message"] - == "Reviewed multiple items" - ) + response = client.post("/reviews/viewed", json=body) + assert response.status_code == 200 + response = response.json() + assert response["success"] == True + assert response["message"] == "Reviewed multiple items" # Verify that in DB the review segment was not changed review_segment_in_db = ( ReviewSegment.select(ReviewSegment.has_been_reviewed) @@ -494,18 +485,11 @@ def test_post_reviews_viewed(self): id = "123456.random" super().insert_mock_review_segment(id) body = {"ids": [id]} - reviews_mark_viewed_many_response = client.post( - "/reviews/viewed", json=body - ) - assert reviews_mark_viewed_many_response.status_code == 200 - reviews_mark_viewed_many_response_json = ( - reviews_mark_viewed_many_response.json() - ) - assert reviews_mark_viewed_many_response_json["success"] == True - assert ( - reviews_mark_viewed_many_response_json["message"] - == "Reviewed multiple items" - ) + response = client.post("/reviews/viewed", json=body) + assert response.status_code == 200 + response = response.json() + assert response["success"] == True + assert response["message"] == "Reviewed multiple items" # Verify that in DB the review segment was changed review_segment_in_db = ( ReviewSegment.select(ReviewSegment.has_been_reviewed) @@ -520,28 +504,28 @@ def test_post_reviews_viewed(self): def test_post_reviews_delete_no_body(self): with TestClient(self.app) as client: super().insert_mock_review_segment("123456.random") - reviews_delete_many_response = client.post("/reviews/delete") + response = client.post("/reviews/delete") # Missing ids - assert reviews_delete_many_response.status_code == 422 + assert response.status_code == 422 def test_post_reviews_delete_no_body_ids(self): with TestClient(self.app) as client: super().insert_mock_review_segment("123456.random") body = {"ids": [""]} - reviews_delete_many_response = client.post("/reviews/delete", json=body) + response = client.post("/reviews/delete", json=body) # Missing ids - assert reviews_delete_many_response.status_code == 422 + assert response.status_code == 422 def test_post_reviews_delete_non_existent_id(self): with TestClient(self.app) as client: id = "123456.random" super().insert_mock_review_segment(id) body = {"ids": ["1"]} - reviews_delete_many_response = client.post("/reviews/delete", json=body) - assert reviews_delete_many_response.status_code == 200 - reviews_delete_many_response_json = reviews_delete_many_response.json() - assert reviews_delete_many_response_json["success"] == True - assert reviews_delete_many_response_json["message"] == "Delete reviews" + response = client.post("/reviews/delete", json=body) + assert response.status_code == 200 + response_json = response.json() + assert response_json["success"] == True + assert response_json["message"] == "Delete reviews" # Verify that in DB the review segment was not deleted review_segment_in_db = ( ReviewSegment.select(ReviewSegment.id) @@ -555,11 +539,11 @@ def test_post_reviews_delete(self): id = "123456.random" super().insert_mock_review_segment(id) body = {"ids": [id]} - reviews_delete_many_response = client.post("/reviews/delete", json=body) - assert reviews_delete_many_response.status_code == 200 - reviews_delete_many_response_json = reviews_delete_many_response.json() - assert reviews_delete_many_response_json["success"] == True - assert reviews_delete_many_response_json["message"] == "Delete reviews" + response = client.post("/reviews/delete", json=body) + assert response.status_code == 200 + response_json = response.json() + assert response_json["success"] == True + assert response_json["message"] == "Delete reviews" # Verify that in DB the review segment was deleted review_segment_in_db = ( ReviewSegment.select(ReviewSegment.id) @@ -581,11 +565,11 @@ def test_post_reviews_delete_many(self): assert len(recordings_ids_in_db_before) == 2 body = {"ids": ids} - reviews_delete_many_response = client.post("/reviews/delete", json=body) - assert reviews_delete_many_response.status_code == 200 - reviews_delete_many_response_json = reviews_delete_many_response.json() - assert reviews_delete_many_response_json["success"] == True - assert reviews_delete_many_response_json["message"] == "Delete reviews" + response = client.post("/reviews/delete", json=body) + assert response.status_code == 200 + response_json = response.json() + assert response_json["success"] == True + assert response_json["message"] == "Delete reviews" # Verify that in DB all review segments and recordings that were passed were deleted review_ids_in_db_after = self._get_reviews(ids) From bb0c54e3dfd5e9e664e5c6b2c62a316192997d42 Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Sun, 24 Nov 2024 17:28:36 +0000 Subject: [PATCH 15/15] Simplify by using method _get_reviews --- frigate/test/http_api/test_http_review.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/frigate/test/http_api/test_http_review.py b/frigate/test/http_api/test_http_review.py index 3bca27ab71..3bd8779aa9 100644 --- a/frigate/test/http_api/test_http_review.py +++ b/frigate/test/http_api/test_http_review.py @@ -527,12 +527,9 @@ def test_post_reviews_delete_non_existent_id(self): assert response_json["success"] == True assert response_json["message"] == "Delete reviews" # Verify that in DB the review segment was not deleted - review_segment_in_db = ( - ReviewSegment.select(ReviewSegment.id) - .where(ReviewSegment.id == id) - .get() - ) - assert review_segment_in_db.id == id + review_ids_in_db_after = self._get_reviews([id]) + assert len(review_ids_in_db_after) == 1 + assert review_ids_in_db_after[0].id == id def test_post_reviews_delete(self): with TestClient(self.app) as client: @@ -545,12 +542,8 @@ def test_post_reviews_delete(self): assert response_json["success"] == True assert response_json["message"] == "Delete reviews" # Verify that in DB the review segment was deleted - review_segment_in_db = ( - ReviewSegment.select(ReviewSegment.id) - .where(ReviewSegment.id == id) - .get_or_none() - ) - assert review_segment_in_db == None + review_ids_in_db_after = self._get_reviews([id]) + assert len(review_ids_in_db_after) == 0 def test_post_reviews_delete_many(self): with TestClient(self.app) as client: