Skip to content

Commit

Permalink
Fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
peterrrock2 committed Sep 26, 2024
1 parent 8ed54a0 commit e15fba0
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 31 deletions.
4 changes: 4 additions & 0 deletions gerrydb/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ class CacheInitError(CacheError):
"""Raised when a GerryDB cache cannot be initialized."""


class CacheObjectError(CacheError):
"""Raised when the cache cannot load an object."""


class ViewLoadError(GerryDBError):
"""Raised when a view cannot be loaded (e.g. from a GeoPackage)."""

Expand Down
3 changes: 2 additions & 1 deletion gerrydb/repos/column.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ def all(self) -> list[str]:

@err("Failed to retrieve column")
@online
def get(self, path: str) -> Column:
@namespaced
def get(self, path: str, namespace: str = None) -> Column:
path = normalize_path(path)
response = self.session.client.get(f"/columns/{self.session.namespace}/{path}")
response.raise_for_status()
Expand Down
53 changes: 42 additions & 11 deletions gerrydb/repos/view.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Repository for views."""

import json
import re
import io
import sqlite3
from datetime import datetime
from pathlib import Path
Expand Down Expand Up @@ -73,6 +73,9 @@
def _load_gpkg_geometry(geom: bytes) -> BaseGeometry:
"""Loads a geometry from a raw GeoPackage WKB blob."""
# header format: https://www.geopackage.org/spec/#gpb_format
if geom == None:
raise ValueError("Invalid GeoPackage geometry: empty geometry.")

envelope_flag = (geom[3] & 0b00001110) >> 1
try:
envelope_bytes = _GPKG_ENVELOPE_BYTES[envelope_flag]
Expand Down Expand Up @@ -116,7 +119,14 @@ def __init__(self, meta: ViewMeta, gpkg_path: Path, conn: sqlite3.Connection):
@classmethod
def from_gpkg(cls, path: Path) -> "View":
"""Loads a view from a GeoPackage."""
conn = sqlite3.connect(path)
if isinstance(path, io.BytesIO):
path.seek(0)
conn = sqlite3.connect(
"file:cached_view?mode=memory&cache=shared", uri=True
)
conn.executescript(path.read().decode("utf-8"))
else:
conn = sqlite3.connect(path)

tables = conn.execute(
"SELECT name FROM sqlite_master WHERE "
Expand Down Expand Up @@ -283,25 +293,46 @@ def geographies(self) -> Generator[Geography, None, None]:
raw_geo_meta = self._conn.execute(
"SELECT meta_id, value FROM gerrydb_geo_meta"
).fetchone()
geo_meta = {row[0]: ObjectMeta(**json.loads(row[1])) for row in raw_geo_meta}
geo_meta = {raw_geo_meta[0]: ObjectMeta(**json.loads(raw_geo_meta[1]))}

raw_geos = self._conn.execute(
f"""SELECT {self.path}.path, geography, internal_point, meta_id, valid_from
FROM {self.path}
JOIN {self.path}__internal_points
ON {self.path}.path = {self.path}__internal_points.path
JOIN gerrydb_geo_meta
JOIN gerrydb_geo_attrs
ON {self.path}.path = gerrydb_geo_attrs.path
"""
)
for geo_row in raw_geos:
yield Geography(
path=geo_row[0],
geography=_load_gpkg_geometry(geo_row[1]),
internal_point=_load_gpkg_geometry(geo_row[2]),
meta=geo_meta[geo_row[3]],
valid_from=geo_row[4],
)
if geo_row[2] is not None:
yield Geography(
path=geo_row[0],
geography=_load_gpkg_geometry(geo_row[1]),
internal_point=_load_gpkg_geometry(geo_row[2]),
meta=geo_meta[geo_row[3]],
namespace=self.namespace,
valid_from=geo_row[4],
)
else:
yield Geography(
path=geo_row[0],
geography=_load_gpkg_geometry(geo_row[1]),
meta=geo_meta[geo_row[3]],
namespace=self.namespace,
valid_from=geo_row[4],
)

@property
def values(self) -> list[str]:
raw_paths = self._conn.execute(f"""PRAGMA table_info({self.path})""")
raw_paths = self.to_df().columns

ret = []
for item in raw_paths:
if item not in ["geometry"]:
ret.append(f"/{self.namespace}/{item}")
return ret


class ViewRepo(NamespacedObjectRepo[ViewMeta]):
Expand Down
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ pytest = "^7.2.1"
black = "^24.8.0"
pytest-vcr = "^1.0.2"

[tool.pytest.ini_options]
markers = [
"vcr: mark a test as a vcr test",
]

[tool.isort]
profile = "black"

Expand Down
4 changes: 3 additions & 1 deletion tests/repos/test_column.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ def test_column_repo_set_values(client_ns, column):
col = ctx.columns.create(**column)
with ctx.geo.bulk() as geo_ctx:
geo_ctx.create({str(idx): box(0, 0, 1, 1) for idx in range(n)})
ctx.columns.set_values(col, values={str(idx): idx for idx in range(n)})
ctx.columns.set_values(
path=col.path, values={str(idx): idx for idx in range(n)}
)
18 changes: 12 additions & 6 deletions tests/repos/test_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ def test_view_repo_create__valid(client_with_ia_layer_loc, ia_dataframe):

assert set(geo.path for geo in view.geographies) == set(ia_dataframe.index)
assert set(col.full_path for col in columns.values()) == set(view.values)
assert all(
len(col_values) == len(view.geographies) for col_values in view.values.values()
)
assert view.graph is None


Expand Down Expand Up @@ -86,7 +83,11 @@ def test_view_repo_view_to_graph(ia_view_with_graph, ia_graph):
expected_cols = set(
"/".join(col.split("/")[2:]) for col in ia_view_with_graph.values
)
assert all(set(data) == expected_cols for _, data in view_graph.nodes(data=True))
# Previous tests in the test suite can add some values to the graph nodes.
# so we just check that the expected columns are present.
assert all(
expected_cols - set(data) == set() for _, data in view_graph.nodes(data=True)
)


@pytest.mark.vcr
Expand All @@ -98,5 +99,10 @@ def test_view_repo_view_to_graph_geo(ia_view_with_graph, ia_graph):

expected_cols = set(
"/".join(col.split("/")[2:]) for col in ia_view_with_graph.values
) | {"area", "geometry"}
assert all(set(data) == expected_cols for _, data in view_graph.nodes(data=True))
) | {"internal_point", "geometry"}

# Previous tests in the test suite can add some values to the graph nodes.
# so we just check that the expected columns are present.
assert all(
expected_cols - set(data) == set() for _, data in view_graph.nodes(data=True)
)
2 changes: 1 addition & 1 deletion tests/repos/test_view_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ def test_view_template_repo_create_get__online_columns_only(
view_template = ctx.view_templates.create(
path="pops", members=[pop_col, vap_col], description="Population view."
)
print(view_template)
# print(view_template)
# TODO: more evaluation here.
24 changes: 13 additions & 11 deletions tests/test_cache.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,38 @@
"""Tests for GerryDB's local caching layer."""

import uuid
from datetime import datetime, timedelta, timezone

import pytest

from gerrydb.cache import CacheInitError, CacheObjectError, CachePolicyError, GerryCache
from gerrydb.schemas import BaseModel, ObjectCachePolicy, ObjectMeta
from gerrydb.cache import CacheInitError, GerryCache
from tempfile import TemporaryDirectory
from pathlib import Path


@pytest.fixture
def cache():
"""An in-memory instance of `GerryCache`."""
return GerryCache(":memory:")
cache_dir = TemporaryDirectory()
return GerryCache(
":memory:",
data_dir=Path(cache_dir.name),
)


def test_gerry_cache_init__no_schema_version(cache):
cache._conn.execute("DELETE FROM cache_meta")
cache._conn.commit()
with pytest.raises(CacheInitError, match="no schema version"):
GerryCache(cache._conn)
GerryCache(cache._conn, cache.data_dir)


def test_gerry_cache_init__bad_schema_version(cache):
cache._conn.execute("UPDATE cache_meta SET value='bad' WHERE key='schema_version'")
cache._conn.commit()
with pytest.raises(CacheInitError, match="expected schema version"):
GerryCache(cache._conn)
GerryCache(cache._conn, cache.data_dir)


def test_gerry_cache_init__missing_table(cache):
cache._conn.execute("DROP TABLE object")
cache._conn.execute("DROP TABLE view")
cache._conn.commit()
with pytest.raises(CacheInitError, match="missing tables"):
GerryCache(cache._conn)
with pytest.raises(CacheInitError, match="missing table"):
GerryCache(cache._conn, cache.data_dir)

0 comments on commit e15fba0

Please sign in to comment.