From b7ffb3382e8dbb6793815aa955a6b0c829825769 Mon Sep 17 00:00:00 2001 From: Saurabh Singh Date: Fri, 20 Feb 2026 03:40:04 +0530 Subject: [PATCH 1/2] fix: add explicit UTF-8 encoding for SQL template reads and cross-platform path comparison --- library/src/iqb/pipeline/pipeline.py | 2 +- library/tests/iqb/cache/cache_test.py | 3 ++- library/tests/iqb/queries_test.py | 24 ++++++++++++------------ 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/library/src/iqb/pipeline/pipeline.py b/library/src/iqb/pipeline/pipeline.py index 55eef72..a903452 100644 --- a/library/src/iqb/pipeline/pipeline.py +++ b/library/src/iqb/pipeline/pipeline.py @@ -192,7 +192,7 @@ def _load_query_template( the SHA256 hash of the original template file. """ query_file = files(queries).joinpath(f"{dataset_name}.sql") - template_text = query_file.read_text() + template_text = query_file.read_text(encoding="utf-8") # Compute hash of the query template template_hash = hashlib.sha256(template_text.encode("utf-8")).hexdigest() diff --git a/library/tests/iqb/cache/cache_test.py b/library/tests/iqb/cache/cache_test.py index 24c5c0e..a6725cf 100644 --- a/library/tests/iqb/cache/cache_test.py +++ b/library/tests/iqb/cache/cache_test.py @@ -1,6 +1,7 @@ """Tests for the iqb.cache.cache module.""" from datetime import datetime +from pathlib import Path from unittest.mock import Mock import pytest @@ -22,7 +23,7 @@ def test_init_with_default_data_dir(self): def test_init_with_custom_data_dir(self): """Test that IQBCache can be instantiated with custom data_dir.""" cache = IQBCache(data_dir="/custom/path") - assert str(cache.data_dir) == "/custom/path" + assert cache.data_dir == Path("/custom/path") def test_init_with_remote_cache(self): """Test that we properly configure a remote cache.""" diff --git a/library/tests/iqb/queries_test.py b/library/tests/iqb/queries_test.py index 2f2bf3b..7bc9d4c 100644 --- a/library/tests/iqb/queries_test.py +++ b/library/tests/iqb/queries_test.py @@ -29,33 +29,33 @@ def test_downloads_by_country_exists(self): def test_downloads_by_country_can_be_read(self): """Test that downloads_by_country.sql can be read.""" query_file = files(iqb.queries).joinpath("downloads_by_country.sql") - content = query_file.read_text() + content = query_file.read_text(encoding="utf-8") assert content is not None assert len(content) > 0 def test_downloads_by_country_has_date_placeholders(self): """Test that downloads_by_country.sql contains date placeholders.""" query_file = files(iqb.queries).joinpath("downloads_by_country.sql") - content = query_file.read_text() + content = query_file.read_text(encoding="utf-8") assert "{START_DATE}" in content assert "{END_DATE}" in content def test_downloads_by_country_queries_unified_downloads_table(self): """Test that downloads_by_country.sql queries the correct table.""" query_file = files(iqb.queries).joinpath("downloads_by_country.sql") - content = query_file.read_text() + content = query_file.read_text(encoding="utf-8") assert "measurement-lab.ndt.unified_downloads" in content def test_downloads_by_country_groups_by_country_code(self): """Test that downloads_by_country.sql groups by country code.""" query_file = files(iqb.queries).joinpath("downloads_by_country.sql") - content = query_file.read_text() + content = query_file.read_text(encoding="utf-8") assert "GROUP BY country_code" in content def test_downloads_by_country_calculates_percentiles(self): """Test that downloads_by_country.sql calculates percentiles.""" query_file = files(iqb.queries).joinpath("downloads_by_country.sql") - content = query_file.read_text() + content = query_file.read_text(encoding="utf-8") # Should calculate p1, p5, p10, p25, p50, p75, p90, p95, p99 assert "APPROX_QUANTILES" in content assert "download_p95" in content or "download_p99" in content @@ -72,33 +72,33 @@ def test_uploads_by_country_exists(self): def test_uploads_by_country_can_be_read(self): """Test that uploads_by_country.sql can be read.""" query_file = files(iqb.queries).joinpath("uploads_by_country.sql") - content = query_file.read_text() + content = query_file.read_text(encoding="utf-8") assert content is not None assert len(content) > 0 def test_uploads_by_country_has_date_placeholders(self): """Test that uploads_by_country.sql contains date placeholders.""" query_file = files(iqb.queries).joinpath("uploads_by_country.sql") - content = query_file.read_text() + content = query_file.read_text(encoding="utf-8") assert "{START_DATE}" in content assert "{END_DATE}" in content def test_uploads_by_country_queries_unified_uploads_table(self): """Test that uploads_by_country.sql queries the correct table.""" query_file = files(iqb.queries).joinpath("uploads_by_country.sql") - content = query_file.read_text() + content = query_file.read_text(encoding="utf-8") assert "measurement-lab.ndt.unified_uploads" in content def test_uploads_by_country_groups_by_country_code(self): """Test that uploads_by_country.sql groups by country code.""" query_file = files(iqb.queries).joinpath("uploads_by_country.sql") - content = query_file.read_text() + content = query_file.read_text(encoding="utf-8") assert "GROUP BY country_code" in content def test_uploads_by_country_calculates_percentiles(self): """Test that uploads_by_country.sql calculates percentiles.""" query_file = files(iqb.queries).joinpath("uploads_by_country.sql") - content = query_file.read_text() + content = query_file.read_text(encoding="utf-8") # Should calculate p1, p5, p10, p25, p50, p75, p90, p95, p99 assert "APPROX_QUANTILES" in content assert "upload_p95" in content or "upload_p99" in content @@ -110,7 +110,7 @@ class TestQueryTemplateSubstitution: def test_downloads_query_date_substitution(self): """Test that date placeholders can be substituted in downloads query.""" query_file = files(iqb.queries).joinpath("downloads_by_country.sql") - template = query_file.read_text() + template = query_file.read_text(encoding="utf-8") # Substitute placeholders query = template.replace("{START_DATE}", "2024-10-01") @@ -125,7 +125,7 @@ def test_downloads_query_date_substitution(self): def test_uploads_query_date_substitution(self): """Test that date placeholders can be substituted in uploads query.""" query_file = files(iqb.queries).joinpath("uploads_by_country.sql") - template = query_file.read_text() + template = query_file.read_text(encoding="utf-8") # Substitute placeholders query = template.replace("{START_DATE}", "2024-10-01") From 9d66025b4a05c086e6c60e71089cd40e5c0983cf Mon Sep 17 00:00:00 2001 From: Saurabh Singh Date: Fri, 20 Feb 2026 16:33:15 +0530 Subject: [PATCH 2/2] ci: add Windows test job to verify cross-platform compatibility --- .github/workflows/ci.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 029c6a6..1e61c1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,6 +84,27 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} slug: m-lab/iqb + test-windows: + name: Test on Windows + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + + - name: Set up Python + run: uv python install 3.13 + + - name: Sync workspace dependencies + run: uv sync --dev + + - name: Run library tests on Windows + working-directory: library + run: uv run pytest --tb=short -q + test-prototype-integration: name: Test Streamlit Prototype Integration runs-on: ubuntu-latest