From ceb26508c88cd65fdd51597d1b319718ed84c994 Mon Sep 17 00:00:00 2001 From: Aaron Gibson Date: Fri, 16 Jan 2026 16:42:16 +0100 Subject: [PATCH 1/7] Revert "Update api/build_runner.sh to debian trixie, bullseye-backports is no longer available (#1026)" This reverts commit b27578f5089c9bea4c926e7cc1669119ead6690d. --- api/Dockerfile | 2 +- api/build_runner.sh | 2 +- api/ooniapi/app.py | 7 +++---- api/ooniapi/database.py | 2 +- api/tests/conftest.py | 15 +++++++-------- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/api/Dockerfile b/api/Dockerfile index ec4e4568c..fadb56169 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:trixie +FROM debian:bullseye ENV PYTHONUNBUFFERED 1 ENV PYTHONPATH /app/ ENV DEBIAN_FRONTEND noninteractive diff --git a/api/build_runner.sh b/api/build_runner.sh index b8637e619..1cc58c7fb 100755 --- a/api/build_runner.sh +++ b/api/build_runner.sh @@ -7,7 +7,7 @@ set -eu export DEBIAN_FRONTEND=noninteractive -echo 'deb http://deb.debian.org/debian trixie-backports main' \ +echo 'deb http://deb.debian.org/debian bullseye-backports main' \ > /etc/apt/sources.list.d/backports.list # Install ca-certificates and gnupg first diff --git a/api/ooniapi/app.py b/api/ooniapi/app.py index 67105c431..be72ff764 100644 --- a/api/ooniapi/app.py +++ b/api/ooniapi/app.py @@ -3,12 +3,11 @@ import datetime import logging import os -import json import re import sys from collections import deque -from flask import Flask +from flask import Flask, json from flask_cors import CORS # debdeps: python3-flask-cors @@ -33,7 +32,7 @@ APP_DIR = os.path.dirname(__file__) -class JSONEncoderWithDates(json.JSONEncoder): +class FlaskJSONEncoder(json.JSONEncoder): # Special JSON encoder that handles dates def default(self, o): if isinstance(o, datetime.datetime): @@ -177,7 +176,7 @@ def create_app(*args, testmode=False, **kw): from ooniapi import views app = Flask(__name__) - app.json_encoder = JSONEncoderWithDates + app.json_encoder = FlaskJSONEncoder # Order matters init_app(app, testmode=testmode) diff --git a/api/ooniapi/database.py b/api/ooniapi/database.py index d5250a271..7af933923 100644 --- a/api/ooniapi/database.py +++ b/api/ooniapi/database.py @@ -6,7 +6,7 @@ from flask import current_app from sqlalchemy.dialects import postgresql -from sqlalchemy.orm import declarative_base +from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.sql.elements import TextClause from sqlalchemy.sql.selectable import Select diff --git a/api/tests/conftest.py b/api/tests/conftest.py index 2cc59f5f4..d0e70a701 100644 --- a/api/tests/conftest.py +++ b/api/tests/conftest.py @@ -28,7 +28,7 @@ def app(): return app -@pytest.fixture +@pytest.yield_fixture def client(app): """ Overriding the `client` fixture from pytest_flask to fix this bug: @@ -37,13 +37,12 @@ def client(app): with app.test_client() as client: yield client - # deprecated name _request_ctx_stack and marked as not a bug on issue #42 - #while True: - # top = flask._request_ctx_stack.top - # if top is not None and top.preserved: - # top.pop() - # else: - # break + while True: + top = flask._request_ctx_stack.top + if top is not None and top.preserved: + top.pop() + else: + break @pytest.fixture(autouse=True) From 96f4cb1223629d0a263c42675ce35556785d1dca Mon Sep 17 00:00:00 2001 From: Aaron Gibson Date: Fri, 16 Jan 2026 16:56:45 +0100 Subject: [PATCH 2/7] Use next stable release, bookworm --- api/Dockerfile | 2 +- api/build_runner.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/Dockerfile b/api/Dockerfile index fadb56169..297684fdf 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bullseye +FROM debian:bookworm ENV PYTHONUNBUFFERED 1 ENV PYTHONPATH /app/ ENV DEBIAN_FRONTEND noninteractive diff --git a/api/build_runner.sh b/api/build_runner.sh index 1cc58c7fb..c256e0757 100755 --- a/api/build_runner.sh +++ b/api/build_runner.sh @@ -7,7 +7,7 @@ set -eu export DEBIAN_FRONTEND=noninteractive -echo 'deb http://deb.debian.org/debian bullseye-backports main' \ +echo 'deb http://deb.debian.org/debian bookworm-backports main' \ > /etc/apt/sources.list.d/backports.list # Install ca-certificates and gnupg first From 19ca4591490104c431f588837224308bd6d5fe46 Mon Sep 17 00:00:00 2001 From: Aaron Gibson Date: Fri, 16 Jan 2026 16:49:30 +0000 Subject: [PATCH 3/7] unclear how https://github.com/ooni/backend/pull/1048 was tested --- api/tests/unit/test_probe_services.py | 1 - 1 file changed, 1 deletion(-) diff --git a/api/tests/unit/test_probe_services.py b/api/tests/unit/test_probe_services.py index 96d1f4756..e3c9df01c 100644 --- a/api/tests/unit/test_probe_services.py +++ b/api/tests/unit/test_probe_services.py @@ -13,7 +13,6 @@ def test_web_test_helpers(mock): "https://1.th.ooni.org", "https://2.th.ooni.org", "https://3.th.ooni.org", - "https://4.th.ooni.org", ] ) r2 = round_robin_web_test_helpers() From ea978ef36ccd8bbbae7ad5e636dbdea0e5b28c21 Mon Sep 17 00:00:00 2001 From: Aaron Gibson Date: Fri, 16 Jan 2026 17:19:47 +0000 Subject: [PATCH 4/7] fix RuntimeError: Working outside of application context. --- api/tests/integ/test_probe_services.py | 30 +++++++++++++++----------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/api/tests/integ/test_probe_services.py b/api/tests/integ/test_probe_services.py index 1449b5bcd..ae52cf1ee 100644 --- a/api/tests/integ/test_probe_services.py +++ b/api/tests/integ/test_probe_services.py @@ -231,17 +231,19 @@ def test_register(client): assert len(c["client_id"]) == 132 -def test_register_then_login(client): +def test_register_then_login(client, app): pwd = "HLdywVhzVCNqLvHCfmnMhIXqGmUFMTuYjmuGZhNlRTeIyvxeQTnjVJsiRkutHCSw" c = _register(client) assert "client_id" in c assert len(c["client_id"]) == 132 - tok = ooniapi.auth.decode_jwt(c["client_id"], audience="probe_login") + with app.app_context(): + tok = ooniapi.auth.decode_jwt(c["client_id"], audience="probe_login") client_id = c["client_id"] c = postj(client, "/api/v1/login", username=client_id, password=pwd) - tok = ooniapi.auth.decode_jwt(c["token"], audience="probe_token") - assert tok["registration_time"] is not None + with app.app_context(): + tok = ooniapi.auth.decode_jwt(c["token"], audience="probe_token") + assert tok["registration_time"] is not None # Login with a bogus client id emulating probes before 2022 client_id = "BOGUSBOGUS" @@ -249,8 +251,10 @@ def test_register_then_login(client): r = client.post("/api/v1/login", json=j) assert r.status_code == 200 token = r.json["token"] - tok = ooniapi.auth.decode_jwt(token, audience="probe_token") - assert tok["registration_time"] is None # we don't know the reg. time + + with app.app_context(): + tok = ooniapi.auth.decode_jwt(token, audience="probe_token") + assert tok["registration_time"] is None # we don't know the reg. time # Expect failed login resp = client.post("/api/v1/login", json=dict()) @@ -263,15 +267,16 @@ def test_test_helpers(client): @patch("ooniapi.probe_services._load_json") -def test_psiphon(mock_load_json, client): +def test_psiphon(mock_load_json, client, app): # register and login pwd = "HLdywVhzVCNqLvHCfmnMhIXqGmUFMTuYjmuGZhNlRTeIyvxeQTnjVJsiRkutHCSw" client_id = _register(client)["client_id"] c = postj(client, "/api/v1/login", username=client_id, password=pwd) - tok = ooniapi.auth.decode_jwt(c["token"], audience="probe_token") - assert tok["registration_time"] is not None + with app.app_context(): + tok = ooniapi.auth.decode_jwt(c["token"], audience="probe_token") + assert tok["registration_time"] is not None url = "/api/v1/test-list/psiphon-config" # broken token @@ -289,14 +294,15 @@ def test_psiphon(mock_load_json, client): @patch("ooniapi.probe_services._load_json") -def test_tor_targets(mock_load_json, client): +def test_tor_targets(mock_load_json, client, app): # register and login pwd = "HLdywVhzVCNqLvHCfmnMhIXqGmUFMTuYjmuGZhNlRTeIyvxeQTnjVJsiRkutHCSw" client_id = _register(client)["client_id"] c = postj(client, "/api/v1/login", username=client_id, password=pwd) - tok = ooniapi.auth.decode_jwt(c["token"], audience="probe_token") - assert tok["registration_time"] is not None + with app.app_context(): + tok = ooniapi.auth.decode_jwt(c["token"], audience="probe_token") + assert tok["registration_time"] is not None url = "/api/v1/test-list/tor-targets" # broken token From f9bfd07c84d2cad940520873536ff5ae6cdc2375 Mon Sep 17 00:00:00 2001 From: Aaron Gibson Date: Fri, 16 Jan 2026 17:22:41 +0000 Subject: [PATCH 5/7] remove fix for not-a-bug --- api/tests/conftest.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/api/tests/conftest.py b/api/tests/conftest.py index d0e70a701..4047f6ff8 100644 --- a/api/tests/conftest.py +++ b/api/tests/conftest.py @@ -28,22 +28,10 @@ def app(): return app -@pytest.yield_fixture +@pytest.fixture def client(app): - """ - Overriding the `client` fixture from pytest_flask to fix this bug: - https://github.com/pytest-dev/pytest-flask/issues/42 - """ with app.test_client() as client: - yield client - - while True: - top = flask._request_ctx_stack.top - if top is not None and top.preserved: - top.pop() - else: - break - + return client @pytest.fixture(autouse=True) def disable_rate_limits(app): From ff14a45826f590f867a8f2c24259e1bc98788116 Mon Sep 17 00:00:00 2001 From: Aaron Gibson Date: Fri, 16 Jan 2026 17:26:35 +0000 Subject: [PATCH 6/7] fix deprecation warning about flask.json --- api/ooniapi/app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/ooniapi/app.py b/api/ooniapi/app.py index be72ff764..19465a33c 100644 --- a/api/ooniapi/app.py +++ b/api/ooniapi/app.py @@ -2,12 +2,13 @@ import datetime import logging +import json import os import re import sys from collections import deque -from flask import Flask, json +from flask import Flask from flask_cors import CORS # debdeps: python3-flask-cors From 02118841a849ed4cefbeb3676d3e2adaf793f65f Mon Sep 17 00:00:00 2001 From: Aaron Gibson Date: Fri, 16 Jan 2026 17:27:24 +0000 Subject: [PATCH 7/7] use response.get_json() not json --- api/tests/integ/test_integration.py | 2 +- api/tests/integ/test_probe_services.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/tests/integ/test_integration.py b/api/tests/integ/test_integration.py index 41cc9a079..a2c4bdb71 100644 --- a/api/tests/integ/test_integration.py +++ b/api/tests/integ/test_integration.py @@ -304,7 +304,7 @@ def test_get_measurement_meta_not_found(client): resp = client.get(url) # TODO: is this a bug? assert resp.status_code == 200 - assert resp.json == {} + assert resp.get_json() == {} def FIXME_MISSING_MSMT____test_get_measurement_meta_input_none_from_fp(client): diff --git a/api/tests/integ/test_probe_services.py b/api/tests/integ/test_probe_services.py index ae52cf1ee..81c08057b 100644 --- a/api/tests/integ/test_probe_services.py +++ b/api/tests/integ/test_probe_services.py @@ -31,28 +31,28 @@ def getjson(client, url): response = client.get(url) assert response.status_code == 200 assert response.is_json - return response.json + return response.get_json() def getjsonh(client, url, headers=None): response = client.get(url, headers=headers) assert response.status_code == 200 assert response.is_json - return response.json + return response.get_json() def post(client, url, data): response = client.post(url, data=data) assert response.status_code == 200 assert response.is_json - return response.json + return response.get_json() def postj(client, url, **kw): response = client.post(url, json=kw) assert response.status_code == 200 assert response.is_json - return response.json + return response.get_json() def test_index(client):