Compare commits

...

18 Commits

Author SHA1 Message Date
f304314fd9 [P] Integrate additional store testing into CI pipeline
All checks were successful
Component testing / Hub testing (push) Successful in 24s
Component testing / Store testing (push) Successful in 47s
Component testing / Integration smoke testing (push) Successful in 3m0s
2026-03-27 16:38:38 +02:00
a578a96347 Merge branch 'github/dev' into dev
All checks were successful
Component testing / Hub testing (push) Successful in 24s
Component testing / Store testing (push) Successful in 35s
Component testing / Integration smoke testing (push) Successful in 2m51s
2026-03-27 16:28:41 +02:00
VladiusVostokus
5749dd825f Merge pull request #30 from Rhinemann/lab4/gryshaiev-SCRUM-82-test-new-records-in-db
Add test for new records in DB
2026-03-27 16:14:51 +02:00
86c6c14502 [L4] Correct road state string 2026-03-27 16:03:41 +02:00
561004a49c [L4] Fix relative module imports 2026-03-27 16:03:38 +02:00
SimonSanich
7d75a15de1 Add test for new records in DB 2026-03-27 15:14:17 +02:00
ІМ-24 Владислав Коваленко
eca98c4469 fix: actually use session variable in websocket GET
All checks were successful
Component testing / Hub testing (push) Successful in 23s
Component testing / Store testing (push) Successful in 24s
Component testing / Integration smoke testing (push) Successful in 2m42s
2026-03-26 16:52:00 +00:00
ІМ-24 Владислав Коваленко
c553384ce7 fix: add session rollback in DELETE
All checks were successful
Component testing / Hub testing (push) Successful in 17s
Component testing / Store testing (push) Successful in 28s
Component testing / Integration smoke testing (push) Successful in 2m44s
2026-03-26 15:59:57 +00:00
ІМ-24 Владислав Коваленко
1bf5687505 fix: add session rollback in PUT 2026-03-26 15:56:34 +00:00
ІМ-24 Владислав Коваленко
0d9dcef994 fix: close connection after GET in websocket 2026-03-26 15:52:33 +00:00
ІМ-24 Владислав Коваленко
d073243c67 refactor: use try block for error handling and session rollback
All checks were successful
Component testing / Hub testing (push) Successful in 23s
Component testing / Store testing (push) Successful in 30s
Component testing / Integration smoke testing (push) Successful in 2m28s
2026-03-26 14:07:37 +00:00
ІМ-24 Владислав Коваленко
0d364ddf61 fix: remove visibility update from PUT endpoint 2026-03-26 14:07:37 +00:00
ІМ-24 Владислав Коваленко
05c94bda81 feat: update visibility field with websocket 2026-03-26 14:07:37 +00:00
ІМ-24 Владислав Коваленко
26230df612 feat: add schema for receiving data from websocker 2026-03-26 14:07:37 +00:00
ІМ-24 Владислав Коваленко
ae10e212cb fix: remove visible from schemas, so only store knows about it 2026-03-26 14:07:37 +00:00
ІМ-24 Владислав Коваленко
e2e68e8506 fix: add visible columnt to .sql init script 2026-03-26 14:07:37 +00:00
ІМ-24 Владислав Коваленко
1375e6e4be feat: add visibility field to database table 2026-03-26 14:07:37 +00:00
ІМ-24 Владислав Коваленко
154c5c3a78 feat: add visibility field 2026-03-26 14:07:37 +00:00
7 changed files with 100 additions and 8 deletions

View File

@@ -28,18 +28,27 @@ jobs:
- name: Clone repository - name: Clone repository
run: git clone --revision ${{ gitea.sha }} --depth 1 ${{ gitea.server_url }}/${{ gitea.repository }} run: git clone --revision ${{ gitea.sha }} --depth 1 ${{ gitea.server_url }}/${{ gitea.repository }}
- name: Start postgres_db container for testing
working-directory: IoT-Systems
run: docker-compose up -d postgres_db
- name: Build Store testing container - name: Build Store testing container
working-directory: IoT-Systems working-directory: IoT-Systems
run: docker build -t local/store/${{gitea.sha}} -f store/Dockerfile-test . run: docker build -t local/store/${{gitea.sha}} -f store/Dockerfile-test .
- name: Run Store tests - name: Run Store tests
working-directory: IoT-Systems working-directory: IoT-Systems
run: docker run --rm -it local/store/${{gitea.sha}} run: docker run --network host --rm -it local/store/${{gitea.sha}}
- name: Clean up containers - name: Clean up containers
if: ${{always()}} if: ${{always()}}
run: docker image rm local/store/${{gitea.sha}} run: docker image rm local/store/${{gitea.sha}}
- name: Clean up docker-compose
if: ${{always()}}
working-directory: IoT-Systems
run: docker-compose down -v --remove-orphans
integration-smoke-test: integration-smoke-test:
name: Integration smoke testing name: Integration smoke testing
runs-on: host-arch-x86_64 runs-on: host-arch-x86_64

View File

@@ -6,6 +6,7 @@ WORKDIR /app
COPY store/requirements.txt . COPY store/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt RUN pip install --no-cache-dir -r requirements.txt
RUN pip install --root-user-action ignore --no-cache-dir pytest httpx
# Copy the entire application into the container # Copy the entire application into the container
COPY store/. . COPY store/. .
# Run the main.py script inside the container when it starts # Run the main.py script inside the container when it starts

View File

@@ -7,5 +7,6 @@ CREATE TABLE processed_agent_data (
z FLOAT, z FLOAT,
latitude FLOAT, latitude FLOAT,
longitude FLOAT, longitude FLOAT,
timestamp TIMESTAMP timestamp TIMESTAMP,
visible BOOLEAN
); );

View File

@@ -8,12 +8,13 @@ from sqlalchemy import (
Integer, Integer,
String, String,
Float, Float,
Boolean,
DateTime, DateTime,
) )
from sqlalchemy.sql import select from sqlalchemy.sql import select
from database import metadata, SessionLocal from database import metadata, SessionLocal
from schemas import ProcessedAgentData, ProcessedAgentDataInDB from schemas import ProcessedAgentData, ProcessedAgentDataInDB, WebSocketData
# FastAPI app setup # FastAPI app setup
app = FastAPI() app = FastAPI()
@@ -30,6 +31,7 @@ processed_agent_data = Table(
Column("latitude", Float), Column("latitude", Float),
Column("longitude", Float), Column("longitude", Float),
Column("timestamp", DateTime), Column("timestamp", DateTime),
Column("visible", Boolean),
) )
# WebSocket subscriptions # WebSocket subscriptions
@@ -46,7 +48,9 @@ async def websocket_endpoint(websocket: WebSocket, user_id: int):
try: try:
# send already available data # send already available data
r = processed_agent_data.select().order_by(processed_agent_data.c.timestamp) r = processed_agent_data.select().order_by(processed_agent_data.c.timestamp)
stored_data = SessionLocal().execute(r).fetchall() session = SessionLocal()
stored_data = session.execute(r).fetchall()
session.close()
jsonable_data = [{c.name: getattr(i, c.name) for c in processed_agent_data.columns} for i in stored_data] jsonable_data = [{c.name: getattr(i, c.name) for c in processed_agent_data.columns} for i in stored_data]
for i in jsonable_data: for i in jsonable_data:
@@ -57,7 +61,24 @@ async def websocket_endpoint(websocket: WebSocket, user_id: int):
# receive forever # receive forever
while True: while True:
await websocket.receive_text() data = await websocket.receive_text()
try:
if (data):
ws_data = WebSocketData.model_validate(json.loads(data))
session = SessionLocal()
update_query = (
processed_agent_data.update()
.where(processed_agent_data.c.id == ws_data.id)
.values(visible=False)
).returning(processed_agent_data)
res = session.execute(update_query).fetchone()
if (not res):
session.rollback()
raise Exception("Error while websocket PUT")
session.commit()
finally:
session.close()
except WebSocketDisconnect: except WebSocketDisconnect:
subscriptions.remove(websocket) subscriptions.remove(websocket)
@@ -81,6 +102,7 @@ def ProcessedAgentData_to_td(data: List[ProcessedAgentData]):
"latitude": item.agent_data.gps.latitude, "latitude": item.agent_data.gps.latitude,
"longitude": item.agent_data.gps.longitude, "longitude": item.agent_data.gps.longitude,
"timestamp": item.agent_data.timestamp, "timestamp": item.agent_data.timestamp,
"visible": True,
} }
for item in data for item in data
] ]
@@ -178,9 +200,13 @@ def update_processed_agent_data(processed_agent_data_id: int, data: ProcessedAge
session.commit() session.commit()
updated_result = session.execute(query).fetchone() updated_result = session.execute(query).fetchone()
return ProcessedAgentDataInDB(**updated_result._mapping) return ProcessedAgentDataInDB(**updated_result._mapping)
except Exception as err:
session.rollback()
print(f"Database error: {err}")
raise HTTPException(status_code=500, detail="Internal Server Error")
finally: finally:
session.close() session.close()
@@ -211,6 +237,11 @@ def delete_processed_agent_data(processed_agent_data_id: int):
return ProcessedAgentDataInDB(**result._mapping) return ProcessedAgentDataInDB(**result._mapping)
except Exception as err:
session.rollback()
print(f"Database error: {err}")
raise HTTPException(status_code=500, detail="Internal Server Error")
finally: finally:
session.close() session.close()

View File

@@ -49,3 +49,7 @@ class AgentData(BaseModel):
class ProcessedAgentData(BaseModel): class ProcessedAgentData(BaseModel):
road_state: str road_state: str
agent_data: AgentData agent_data: AgentData
class WebSocketData(BaseModel):
id: int

View File

@@ -1,3 +1,4 @@
#!/bin/sh #!/bin/sh
PYTHONPATH=$PWD python3 test/main_test.py PYTHONPATH=$PWD python3 test/main_test.py
PYTHONPATH=$PWD python3 -m pytest test_db.py

45
store/test_db.py Normal file
View File

@@ -0,0 +1,45 @@
from fastapi.testclient import TestClient
from main import app, processed_agent_data
from database import SessionLocal
from sqlalchemy import select
from datetime import datetime
client = TestClient(app)
def test_create_processed_agent_data():
db = SessionLocal()
before = db.execute(select(processed_agent_data)).fetchall()
before_count = len(before)
payload = {
"user_id": 123,
"data": [
{
"road_state": "normal",
"agent_data": {
"user_id": 999,
"accelerometer": {
"x": 1.1,
"y": 2.2,
"z": 3.3
},
"gps": {
"latitude": 50.45,
"longitude": 30.52
},
"timestamp": datetime.now().isoformat()
}
}
]
}
response = client.post("/processed_agent_data/", json=payload)
assert response.status_code == 200
after = db.execute(select(processed_agent_data)).fetchall()
after_count = len(after)
assert after_count == before_count + 1
db.close()