Skip to content

Commit e76f4e9

Browse files
authored
Started unit tests for the review controller (#15077)
* Started unit tests for the review controller * Revert "Started unit tests for the review controller" This reverts commit 7746eb1. * Started unit tests for the review controller * FIrst test * Added test for review endpoint (time filter - after + before) * Assert expected event * Added more tests for review endpoint * Added test for review endpoint with all filters * Added test for review endpoint with limit * Comment * Renamed tests to increase readability
1 parent 0df091f commit e76f4e9

File tree

4 files changed

+275
-0
lines changed

4 files changed

+275
-0
lines changed

.cspell/frigate-dictionary.txt

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ argmax
1212
argmin
1313
argpartition
1414
ascontiguousarray
15+
astype
1516
authelia
1617
authentik
1718
autodetected
@@ -195,6 +196,7 @@ poweroff
195196
preexec
196197
probesize
197198
protobuf
199+
pstate
198200
psutil
199201
pubkey
200202
putenv
@@ -278,6 +280,7 @@ uvicorn
278280
vaapi
279281
vainfo
280282
variations
283+
vbios
281284
vconcat
282285
vitb
283286
vstream

frigate/test/http_api/__init__.py

Whitespace-only changes.
+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import datetime
2+
import logging
3+
import os
4+
import unittest
5+
6+
from peewee_migrate import Router
7+
from playhouse.sqlite_ext import SqliteExtDatabase
8+
from playhouse.sqliteq import SqliteQueueDatabase
9+
10+
from frigate.api.fastapi_app import create_fastapi_app
11+
from frigate.config import FrigateConfig
12+
from frigate.models import Event, ReviewSegment
13+
from frigate.review.maintainer import SeverityEnum
14+
from frigate.test.const import TEST_DB, TEST_DB_CLEANUPS
15+
16+
17+
class BaseTestHttp(unittest.TestCase):
18+
def setUp(self, models):
19+
# setup clean database for each test run
20+
migrate_db = SqliteExtDatabase("test.db")
21+
del logging.getLogger("peewee_migrate").handlers[:]
22+
router = Router(migrate_db)
23+
router.run()
24+
migrate_db.close()
25+
self.db = SqliteQueueDatabase(TEST_DB)
26+
self.db.bind(models)
27+
28+
self.minimal_config = {
29+
"mqtt": {"host": "mqtt"},
30+
"cameras": {
31+
"front_door": {
32+
"ffmpeg": {
33+
"inputs": [
34+
{"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]}
35+
]
36+
},
37+
"detect": {
38+
"height": 1080,
39+
"width": 1920,
40+
"fps": 5,
41+
},
42+
}
43+
},
44+
}
45+
self.test_stats = {
46+
"detection_fps": 13.7,
47+
"detectors": {
48+
"cpu1": {
49+
"detection_start": 0.0,
50+
"inference_speed": 91.43,
51+
"pid": 42,
52+
},
53+
"cpu2": {
54+
"detection_start": 0.0,
55+
"inference_speed": 84.99,
56+
"pid": 44,
57+
},
58+
},
59+
"front_door": {
60+
"camera_fps": 0.0,
61+
"capture_pid": 53,
62+
"detection_fps": 0.0,
63+
"pid": 52,
64+
"process_fps": 0.0,
65+
"skipped_fps": 0.0,
66+
},
67+
"service": {
68+
"storage": {
69+
"/dev/shm": {
70+
"free": 50.5,
71+
"mount_type": "tmpfs",
72+
"total": 67.1,
73+
"used": 16.6,
74+
},
75+
"/media/frigate/clips": {
76+
"free": 42429.9,
77+
"mount_type": "ext4",
78+
"total": 244529.7,
79+
"used": 189607.0,
80+
},
81+
"/media/frigate/recordings": {
82+
"free": 0.2,
83+
"mount_type": "ext4",
84+
"total": 8.0,
85+
"used": 7.8,
86+
},
87+
"/tmp/cache": {
88+
"free": 976.8,
89+
"mount_type": "tmpfs",
90+
"total": 1000.0,
91+
"used": 23.2,
92+
},
93+
},
94+
"uptime": 101113,
95+
"version": "0.10.1",
96+
"latest_version": "0.11",
97+
},
98+
}
99+
100+
def tearDown(self):
101+
if not self.db.is_closed():
102+
self.db.close()
103+
104+
try:
105+
for file in TEST_DB_CLEANUPS:
106+
os.remove(file)
107+
except OSError:
108+
pass
109+
110+
def create_app(self, stats=None):
111+
return create_fastapi_app(
112+
FrigateConfig(**self.minimal_config),
113+
self.db,
114+
None,
115+
None,
116+
None,
117+
None,
118+
None,
119+
stats,
120+
None,
121+
)
122+
123+
def insert_mock_event(
124+
self,
125+
id: str,
126+
start_time: datetime.datetime = datetime.datetime.now().timestamp(),
127+
) -> Event:
128+
"""Inserts a basic event model with a given id."""
129+
return Event.insert(
130+
id=id,
131+
label="Mock",
132+
camera="front_door",
133+
start_time=start_time,
134+
end_time=start_time + 20,
135+
top_score=100,
136+
false_positive=False,
137+
zones=list(),
138+
thumbnail="",
139+
region=[],
140+
box=[],
141+
area=0,
142+
has_clip=True,
143+
has_snapshot=True,
144+
).execute()
145+
146+
def insert_mock_review_segment(
147+
self,
148+
id: str,
149+
start_time: datetime.datetime = datetime.datetime.now().timestamp(),
150+
end_time: datetime.datetime = datetime.datetime.now().timestamp() + 20,
151+
) -> Event:
152+
"""Inserts a basic event model with a given id."""
153+
return ReviewSegment.insert(
154+
id=id,
155+
camera="front_door",
156+
start_time=start_time,
157+
end_time=end_time,
158+
has_been_reviewed=False,
159+
severity=SeverityEnum.alert,
160+
thumb_path=False,
161+
data={},
162+
).execute()
+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import datetime
2+
3+
from fastapi.testclient import TestClient
4+
5+
from frigate.models import Event, ReviewSegment
6+
from frigate.test.http_api.base_http_test import BaseTestHttp
7+
8+
9+
class TestHttpReview(BaseTestHttp):
10+
def setUp(self):
11+
super().setUp([Event, ReviewSegment])
12+
13+
# 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
14+
def test_get_review_no_filters_no_matches(self):
15+
app = super().create_app()
16+
now = datetime.datetime.now().timestamp()
17+
18+
with TestClient(app) as client:
19+
super().insert_mock_review_segment("123456.random", now, now + 2)
20+
reviews_response = client.get("/review")
21+
assert reviews_response.status_code == 200
22+
reviews_in_response = reviews_response.json()
23+
assert len(reviews_in_response) == 0
24+
25+
def test_get_review_no_filters(self):
26+
app = super().create_app()
27+
now = datetime.datetime.now().timestamp()
28+
29+
with TestClient(app) as client:
30+
super().insert_mock_review_segment("123456.random", now - 2, now - 1)
31+
reviews_response = client.get("/review")
32+
assert reviews_response.status_code == 200
33+
reviews_in_response = reviews_response.json()
34+
assert len(reviews_in_response) == 1
35+
36+
def test_get_review_with_time_filter_no_matches(self):
37+
app = super().create_app()
38+
now = datetime.datetime.now().timestamp()
39+
40+
with TestClient(app) as client:
41+
id = "123456.random"
42+
super().insert_mock_review_segment(id, now, now + 2)
43+
params = {
44+
"after": now,
45+
"before": now + 3,
46+
}
47+
reviews_response = client.get("/review", params=params)
48+
assert reviews_response.status_code == 200
49+
reviews_in_response = reviews_response.json()
50+
assert len(reviews_in_response) == 0
51+
52+
def test_get_review_with_time_filter(self):
53+
app = super().create_app()
54+
now = datetime.datetime.now().timestamp()
55+
56+
with TestClient(app) as client:
57+
id = "123456.random"
58+
super().insert_mock_review_segment(id, now, now + 2)
59+
params = {
60+
"after": now - 1,
61+
"before": now + 3,
62+
}
63+
reviews_response = client.get("/review", params=params)
64+
assert reviews_response.status_code == 200
65+
reviews_in_response = reviews_response.json()
66+
assert len(reviews_in_response) == 1
67+
assert reviews_in_response[0]["id"] == id
68+
69+
def test_get_review_with_limit_filter(self):
70+
app = super().create_app()
71+
now = datetime.datetime.now().timestamp()
72+
73+
with TestClient(app) as client:
74+
id = "123456.random"
75+
id2 = "654321.random"
76+
super().insert_mock_review_segment(id, now, now + 2)
77+
super().insert_mock_review_segment(id2, now + 1, now + 2)
78+
params = {
79+
"limit": 1,
80+
"after": now,
81+
"before": now + 3,
82+
}
83+
reviews_response = client.get("/review", params=params)
84+
assert reviews_response.status_code == 200
85+
reviews_in_response = reviews_response.json()
86+
assert len(reviews_in_response) == 1
87+
assert reviews_in_response[0]["id"] == id2
88+
89+
def test_get_review_with_all_filters(self):
90+
app = super().create_app()
91+
now = datetime.datetime.now().timestamp()
92+
93+
with TestClient(app) as client:
94+
id = "123456.random"
95+
super().insert_mock_review_segment(id, now, now + 2)
96+
params = {
97+
"cameras": "front_door",
98+
"labels": "all",
99+
"zones": "all",
100+
"reviewed": 0,
101+
"limit": 1,
102+
"severity": "alert",
103+
"after": now - 1,
104+
"before": now + 3,
105+
}
106+
reviews_response = client.get("/review", params=params)
107+
assert reviews_response.status_code == 200
108+
reviews_in_response = reviews_response.json()
109+
assert len(reviews_in_response) == 1
110+
assert reviews_in_response[0]["id"] == id

0 commit comments

Comments
 (0)