Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/src/components/tools/ToolPanels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ function QrPanel() {
<Card>
{result.error ? (
<ErrorCard label={t('toolPanels.error')} message={result.error} />
) : !result.decoded ? (
<p className="text-[12px] text-text-3">{result.status_reason}</p>
) : (
<div>
<Row label={t('toolPanels.qr.type')} value={result.type} />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ export interface DarkWebResult {
error?: string;
}

export interface QrResult {
export interface QrResult extends ModuleStatusFields {
decoded?: string;
type?: string;
is_url?: boolean;
Expand Down
11 changes: 5 additions & 6 deletions modules/qr_decoder.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import requests
from typing import Dict, Any, Optional, Tuple
from modules import get_proxies
from modules.module_status import annotate, OK, ERROR


class QRDecoder:
Expand Down Expand Up @@ -37,7 +38,7 @@ def _decode_api(self, image_bytes: bytes, filename: str) -> Tuple[Optional[str],
return None, "Unexpected API response"
symbols = data[0].get("symbol", [])
if not symbols:
return None, "No QR code detected in the image"
return None, None
err = symbols[0].get("error")
if err:
return None, err
Expand Down Expand Up @@ -80,13 +81,11 @@ def decode(self, image_bytes: bytes, filename: str = "qr.png") -> Dict[str, Any]
decoded, err = self._decode_api(image_bytes, filename)
result["source"] = "api"
if err:
result["error"] = err
return result
return annotate(result, ERROR, err)

if not decoded:
result["error"] = "No QR code detected in the image"
return result
return annotate(result, OK, "No QR code detected in the image")

result["decoded"] = decoded
result["type"], result["is_url"] = self._classify(decoded)
return result
return annotate(result, OK)
69 changes: 69 additions & 0 deletions tests/test_qr_decoder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from unittest.mock import MagicMock, patch

from modules.module_status import ERROR, OK
from modules.qr_decoder import QRDecoder


def _api_response(payload, status_code=200):
response = MagicMock()
response.status_code = status_code
response.json.return_value = payload
return response


def test_local_decode_is_ok():
decoder = QRDecoder()
with patch.object(decoder, "_decode_local", return_value="https://example.com"):
result = decoder.decode(b"img")
assert result["status"] == OK
assert result["error"] is None
assert result["decoded"] == "https://example.com"
assert result["is_url"] is True
assert result["source"] == "local"


def test_no_qr_code_is_ok_with_reason():
decoder = QRDecoder()
with patch.object(decoder, "_decode_local", return_value=None), patch(
"modules.qr_decoder.requests.post",
return_value=_api_response([{"symbol": []}]),
):
result = decoder.decode(b"img")
assert result["status"] == OK
assert result["status_reason"] == "No QR code detected in the image"
assert result["error"] is None
assert result["decoded"] is None


def test_api_http_error_is_error():
decoder = QRDecoder()
with patch.object(decoder, "_decode_local", return_value=None), patch(
"modules.qr_decoder.requests.post",
return_value=_api_response(None, status_code=500),
):
result = decoder.decode(b"img")
assert result["status"] == ERROR
assert result["error"] == "API returned HTTP 500"


def test_api_exception_is_error():
decoder = QRDecoder()
with patch.object(decoder, "_decode_local", return_value=None), patch(
"modules.qr_decoder.requests.post", side_effect=ConnectionError("network down")
):
result = decoder.decode(b"img")
assert result["status"] == ERROR
assert result["error"] == "network down"


def test_api_decode_is_ok():
decoder = QRDecoder()
with patch.object(decoder, "_decode_local", return_value=None), patch(
"modules.qr_decoder.requests.post",
return_value=_api_response([{"symbol": [{"data": "hello", "error": None}]}]),
):
result = decoder.decode(b"img")
assert result["status"] == OK
assert result["decoded"] == "hello"
assert result["type"] == "Plain text"
assert result["source"] == "api"
Loading