#![expect(
clippy::unwrap_used,
reason = "clippy's allow-*-in-tests covers #[test] fns but not the free helper fns in an integration-test crate; the unwraps in these helpers are the intended failure mechanism"
)]
use geo_types::Point;
use geopackage::core::types::GeometryType;
use geopackage::{
Error, GeoPackage, GeometrySpec, SpatialIndexStatus, TableSchemaBuilder, ValueRef,
};
fn read_only_file() -> (tempfile::TempDir, GeoPackage) {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("t.gpkg");
{
let gpkg = GeoPackage::create(&path).unwrap();
for (name, index) in [("pts", true), ("plain", false)] {
gpkg.create_layer(
&TableSchemaBuilder::new(name)
.geometry(GeometrySpec::new(GeometryType::Point, 4326))
.spatial_index(index),
)
.unwrap();
let layer = gpkg.layer(name).unwrap();
let mut w = layer.writer().unwrap();
w.insert(None, &Point::new(1.0, 2.0), &[]).unwrap();
w.insert(None, &Point::new(3.0, 4.0), &[]).unwrap();
w.commit().unwrap();
}
gpkg.connection()
.execute(
"UPDATE gpkg_contents SET min_x = NULL, min_y = NULL, max_x = NULL, max_y = NULL",
[],
)
.unwrap();
gpkg.close().unwrap();
}
let gpkg = GeoPackage::open_read_only(&path).unwrap();
(dir, gpkg)
}
#[test]
fn reads_work_read_only() {
let (_dir, gpkg) = read_only_file();
let layer = gpkg.layer("pts").unwrap();
assert_eq!(layer.features().unwrap().count(), 2);
assert_eq!(
layer.spatial_index_status().unwrap(),
SpatialIndexStatus::Current
);
assert!(layer.has_spatial_index().unwrap());
assert!(layer.audit_spatial_index().unwrap().is_consistent());
assert_eq!(
layer
.features_in(geopackage::BoundingBox::new(0.0, 0.0, 2.0, 3.0))
.unwrap()
.count(),
1
);
}
#[test]
fn extent_measures_without_recording_read_only() {
let (_dir, gpkg) = read_only_file();
let layer = gpkg.layer("pts").unwrap();
assert_eq!(
layer.extent().unwrap(),
Some(geopackage::BoundingBox::new(1.0, 2.0, 3.0, 4.0))
);
assert_eq!(
layer.extent().unwrap(),
Some(geopackage::BoundingBox::new(1.0, 2.0, 3.0, 4.0))
);
}
#[test]
fn writes_fail_read_only() {
let (_dir, gpkg) = read_only_file();
let pts = gpkg.layer("pts").unwrap();
let plain = gpkg.layer("plain").unwrap();
let is_readonly = |what: &str, result: geopackage::Result<()>| match result {
Err(Error::Sqlite(e)) => assert_eq!(
e.sqlite_error_code(),
Some(rusqlite::ErrorCode::ReadOnly),
"{what}: expected a read-only failure, got {e:?}"
),
other => panic!("{what}: expected a read-only failure, got {other:?}"),
};
is_readonly("recompute_extent", pts.recompute_extent().map(|_| ()));
is_readonly("rebuild_spatial_index", pts.rebuild_spatial_index());
is_readonly("create_spatial_index", plain.create_spatial_index());
is_readonly("drop_spatial_index", pts.drop_spatial_index());
let mut writer = plain.writer().unwrap();
is_readonly(
"insert",
writer.insert(None, &Point::new(9.0, 9.0), &[]).map(|_| ()),
);
pts.repair_spatial_index().unwrap();
}
#[test]
fn an_unindexed_layer_answers_without_an_index() {
let (_dir, gpkg) = read_only_file();
let plain = gpkg.layer("plain").unwrap();
assert_eq!(
plain.spatial_index_status().unwrap(),
SpatialIndexStatus::Absent
);
assert!(!plain.has_spatial_index().unwrap());
assert!(matches!(
plain.audit_spatial_index(),
Err(Error::NoSpatialIndex { .. })
));
assert!(matches!(
plain.rebuild_spatial_index(),
Err(Error::NoSpatialIndex { .. })
));
let _ = ValueRef::Null;
assert_eq!(
plain
.features_in(geopackage::BoundingBox::new(0.0, 0.0, 2.0, 3.0))
.unwrap()
.count(),
1
);
}