Skip to content

feat(api): prepare for WebConnectivity A/B testing #792

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
6 changes: 6 additions & 0 deletions api/debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
ooni-api (1.0.89) unstable; urgency=medium

* Start preparing for WebConnectivity v0.4 vs v0.5 A/B testing

-- Simone Basso <[email protected]> Tue, 20 Feb 2024 10:34:00 +0100

ooni-api (1.0.88) unstable; urgency=medium

* Add more logging
Expand Down
13 changes: 10 additions & 3 deletions api/ooniapi/probe_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from urllib.request import urlopen
import ipaddress
import time
import os

import ujson
from flask import Blueprint, current_app, request, Response
Expand Down Expand Up @@ -275,9 +276,15 @@ def check_in() -> Response:
features={"torsf_enabled": True, "vanilla_tor_enabled": True}
)

# set webconnectivity_0.5 feature flag for some probes
octect = extract_probe_ipaddr_octect(1, 0)
if octect in (34, 239):
# WebConnectivity v0.4 vs v0.5 A/B testing. Try to avoid using the new version of
# the experiment in Cambodia and Belarus where there are upcoming elections.
#
# See https://github.com/ooni/probe/issues/2555#issuecomment-1906097413.
if (
probe_cc not in ("BY", "KH") and
software_name == "ooniprobe-android-unattended" and
software_version == "3.8.6"
):
conf["features"]["webconnectivity_0.5"] = True

conf["test_helpers"] = generate_test_helpers_conf()
Expand Down
50 changes: 50 additions & 0 deletions api/tests/integ/test_probe_services_nodb.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,56 @@ def test_check_in_geoip(client, mocks):
assert c["probe_network_name"] is not None


# See https://github.com/ooni/probe/issues/2555 for context. Delete when the A/B testing is done.
def test_check_in_abtesting_2555(client, mocks):
testcases = [{
"name": "when the country is KH and the software name and version would match",
"inputs": {
"probe_cc": "KH",
"software_name": "ooniprobe-android-unattended",
"software_version": "3.8.6",
},
"expect": False,
}, {
"name": "when the country is BY and the software name and version would match",
"inputs": {
"probe_cc": "BY",
"software_name": "ooniprobe-android-unattended",
"software_version": "3.8.6",
},
"expect": False,
}, {
"name": "when the country is neither BY nor KH with unexpected software_name",
"inputs": {
"probe_cc": "IT",
"software_name": "ooniprobe-android",
"software_version": "3.8.6",
},
"expect": False,
}, {
"name": "when the country is neither BY nor KH with unexpected software_version",
"inputs": {
"probe_cc": "IT",
"software_name": "ooniprobe-android-unattended",
"software_version": "3.8.4",
},
"expect": False,
}, {
"name": "when the country is neither BY nor KH with correct software_version and software_version",
"inputs": {
"probe_cc": "IT",
"software_name": "ooniprobe-android-unattended",
"software_version": "3.8.6",
},
"expect": True,
}]
for tc in testcases:
print("running", tc["name"])
j = tc["inputs"]
c = client.post("/api/v1/check-in", json=j).json
assert c.get("conf", {}).get("features", {}).get("webconnectivity_0.5", False) == tc["expect"]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests inside this file are disabled, so we don't run the new test above by default. I managed to run tests locally but I then realized some of them are failing as detailed below:

________________________________ test_check_in _________________________________

client = <FlaskClient <Flask 'ooniapi.app'>>, mocks = None

    def test_check_in(client, mocks):
        j = dict(
            probe_cc="US",
            probe_asn="AS1234",
            on_wifi=True,
            charging=False,
        )
        c = postj(client, "/api/v1/check-in", **j)
        assert c["v"] == 1
        assert c["utc_time"].startswith("20")
        assert c["utc_time"].endswith("Z")
        assert "T" in c["utc_time"]
        assert len(c["utc_time"]) == 20
        urls = c["tests"]["web_connectivity"]["urls"]
>       assert len(urls) == 20, urls
E       AssertionError: [{'category_code': 'NEWS', 'country_code': 'XX', 'url': 'http://ncac.org/'}, {'category_code': 'NEWS', 'country_code':...XX', 'url': 'https://twitter.com/'}, {'category_code': 'CULTR', 'country_code': 'XX', 'url': 'https://www.ushmm.org/'}]
E       assert 5 == 20
E        +  where 5 = len([{'category_code': 'NEWS', 'country_code': 'XX', 'url': 'http://ncac.org/'}, {'category_code': 'NEWS', 'country_code':...XX', 'url': 'https://twitter.com/'}, {'category_code': 'CULTR', 'country_code': 'XX', 'url': 'https://www.ushmm.org/'}])

tests/integ/test_probe_services_nodb.py:143: AssertionError

[...]

_______________________ test_check_in_url_category_news ________________________

client = <FlaskClient <Flask 'ooniapi.app'>>, mocks = None

    def test_check_in_url_category_news(client, mocks):
        j = dict(
            on_wifi=True,
            charging=True,
            web_connectivity=dict(category_codes=["NEWS"]),
        )
        c = postj(client, "/api/v1/check-in", **j)
        assert c["v"] == 1
        urls = c["tests"]["web_connectivity"]["urls"]
>       assert len(urls) == 100, urls
E       AssertionError: [{'category_code': 'NEWS', 'country_code': 'XX', 'url': 'http://ncac.org/'}, {'category_code': 'NEWS', 'country_code': 'XX', 'url': 'https://ncac.org/'}]
E       assert 2 == 100
E        +  where 2 = len([{'category_code': 'NEWS', 'country_code': 'XX', 'url': 'http://ncac.org/'}, {'category_code': 'NEWS', 'country_code': 'XX', 'url': 'https://ncac.org/'}])

tests/integ/test_probe_services_nodb.py:164: AssertionError

[...]

_______________________ test_check_in_url_category_multi _______________________

client = <FlaskClient <Flask 'ooniapi.app'>>, mocks = None

    def test_check_in_url_category_multi(client, mocks):
        j = dict(
            probe_cc="IT",
            on_wifi=True,
            charging=True,
            web_connectivity=dict(category_codes=["NEWS", "MILX", "FILE"]),
        )
        c = postj(client, "/api/v1/check-in", **j)
        assert c["v"] == 1
        urls = c["tests"]["web_connectivity"]["urls"]
>       assert len(urls) == 100, urls
E       AssertionError: [{'category_code': 'NEWS', 'country_code': 'XX', 'url': 'http://ncac.org/'}, {'category_code': 'NEWS', 'country_code': 'XX', 'url': 'https://ncac.org/'}]
E       assert 2 == 100
E        +  where 2 = len([{'category_code': 'NEWS', 'country_code': 'XX', 'url': 'http://ncac.org/'}, {'category_code': 'NEWS', 'country_code': 'XX', 'url': 'https://ncac.org/'}])

tests/integ/test_probe_services_nodb.py:185: AssertionError

[...]

_______________ test_check_in_url_prioritization_category_codes ________________

client = <FlaskClient <Flask 'ooniapi.app'>>, mocks = None

    def test_check_in_url_prioritization_category_codes(client, mocks):
        c = getjson(
            client,
            "/api/v1/test-list/urls?category_codes=NEWS,HUMR&country_code=US&limit=100",
        )
        assert "metadata" in c
>       assert c["metadata"] == {
            "count": 100,
            "current_page": -1,
            "limit": -1,
            "next_url": "",
            "pages": 1,
        }
E       AssertionError: assert {'count': 2, ...url': '', ...} == {'count': 100...url': '', ...}
E         Omitting 4 identical items, use -vv to show
E         Differing items:
E         {'count': 2} != {'count': 100}
E         Use -v to get the full diff

tests/integ/test_probe_services_nodb.py:222: AssertionError

============== 4 failed, 16 passed, 3 skipped, 1 warning in 4.91s ==============

I will see if I can obviously fix them and then re-enabled them. It seems a bit of a bummer to not run tests we have written.



# # Test /api/v1/collectors


Expand Down