22from __future__
import annotations
24__all__ = [
"ApdbSchemaUpdateTest",
"ApdbTest"]
26from abc
import ABC, abstractmethod
28from typing
import TYPE_CHECKING, Any, ContextManager, Optional
32from lsst.dax.apdb import ApdbConfig, ApdbInsertId, ApdbTableData, ApdbTables, make_apdb
33from lsst.sphgeom import Angle, Circle, Region, UnitVector3d
35from .data_factory
import makeForcedSourceCatalog, makeObjectCatalog, makeSourceCatalog, makeSSObjectCatalog
38def _make_region(xyz: tuple[float, float, float] = (1.0, 1.0, -1.0)) -> Region:
39 """Make a region to use in tests"""
47 """Base class for Apdb tests that can be specialized for concrete
50 This can only be used as a mixin
class for
a unittest.TestCase and it
51 calls various
assert methods.
54 time_partition_tables = False
55 visit_time =
DateTime(
"2021-01-01T00:00:00", DateTime.TAI)
57 fsrc_requires_id_list =
False
58 """Should be set to True if getDiaForcedSources requires object IDs"""
60 use_insert_id: bool =
False
61 """Set to true when support for Insert IDs is configured"""
64 table_column_count = {
65 ApdbTables.DiaObject: 8,
66 ApdbTables.DiaObjectLast: 5,
67 ApdbTables.DiaSource: 10,
68 ApdbTables.DiaForcedSource: 4,
69 ApdbTables.SSObject: 3,
74 """Make config class instance used in all tests."""
75 raise NotImplementedError()
79 """Return type of table returned from getDiaObjects method."""
80 raise NotImplementedError()
82 def assert_catalog(self, catalog: Any, rows: int, table: ApdbTables) ->
None:
83 """Validate catalog type and size
88 Expected type of this is ``pandas.DataFrame``.
90 Expected number of rows
in a catalog.
99 """Validate catalog type and size
104 Expected type of this is `ApdbTableData`.
106 Expected number of rows
in a catalog.
109 extra_columns : `int`
110 Count of additional columns expected
in ``catalog``.
113 n_rows = sum(1 for row
in catalog.rows())
119 """Test for makeing APDB schema."""
121 apdb = make_apdb(config)
130 """Test for getting data from empty database.
132 All get() methods should return empty results, only useful
for
133 checking that code
is not broken.
138 apdb = make_apdb(config)
144 res: Optional[pandas.DataFrame]
147 res = apdb.getDiaObjects(region)
151 res = apdb.getDiaSources(region,
None, visit_time)
154 res = apdb.getDiaSources(region, [], visit_time)
158 res = apdb.getDiaSources(region, [1, 2, 3], visit_time)
162 res = apdb.getDiaForcedSources(region, [], visit_time)
166 res = apdb.getDiaForcedSources(region, [1, 2, 3], visit_time)
172 apdb.getDiaForcedSources(region,
None, visit_time)
174 apdb.getDiaForcedSources(region,
None, visit_time)
178 """Test for getting data from empty database.
180 All get() methods should return empty DataFrame
or None.
184 config = self.
make_config(read_sources_months=0, read_forced_sources_months=0)
185 apdb = make_apdb(config)
191 res: Optional[pandas.DataFrame]
194 res = apdb.getDiaObjects(region)
198 res = apdb.getDiaSources(region,
None, visit_time)
202 res = apdb.getDiaSources(region, [], visit_time)
206 res = apdb.getDiaForcedSources(region, [], visit_time)
210 """Store and retrieve DiaObjects."""
214 apdb = make_apdb(config)
221 catalog = makeObjectCatalog(region, 100, visit_time)
224 apdb.store(visit_time, catalog)
227 res = apdb.getDiaObjects(region)
231 """Store and retrieve DiaSources."""
233 apdb = make_apdb(config)
240 objects = makeObjectCatalog(region, 100, visit_time)
241 oids =
list(objects[
"diaObjectId"])
242 sources = makeSourceCatalog(objects, visit_time)
245 apdb.store(visit_time, objects, sources)
248 res = apdb.getDiaSources(region,
None, visit_time)
252 res = apdb.getDiaSources(region, oids, visit_time)
256 res = apdb.getDiaSources(region, [], visit_time)
260 """Store and retrieve DiaForcedSources."""
263 apdb = make_apdb(config)
270 objects = makeObjectCatalog(region, 100, visit_time)
271 oids =
list(objects[
"diaObjectId"])
272 catalog = makeForcedSourceCatalog(objects, visit_time)
274 apdb.store(visit_time, objects, forced_sources=catalog)
277 res = apdb.getDiaForcedSources(region, oids, visit_time)
278 self.
assert_catalog(res, len(catalog), ApdbTables.DiaForcedSource)
281 res = apdb.getDiaForcedSources(region, [], visit_time)
285 """Store and retrieve catalog history."""
289 apdb = make_apdb(config)
296 objects1 = makeObjectCatalog(region1, nobj, visit_time)
297 objects2 = makeObjectCatalog(region2, nobj, visit_time, start_id=nobj * 2)
300 (
DateTime(
"2021-01-01T00:01:00", DateTime.TAI), objects1),
301 (
DateTime(
"2021-01-01T00:02:00", DateTime.TAI), objects2),
302 (
DateTime(
"2021-01-01T00:03:00", DateTime.TAI), objects1),
303 (
DateTime(
"2021-01-01T00:04:00", DateTime.TAI), objects2),
304 (
DateTime(
"2021-01-01T00:05:00", DateTime.TAI), objects1),
305 (
DateTime(
"2021-01-01T00:06:00", DateTime.TAI), objects2),
306 (
DateTime(
"2021-03-01T00:01:00", DateTime.TAI), objects1),
307 (
DateTime(
"2021-03-01T00:02:00", DateTime.TAI), objects2),
311 for visit_time, objects
in visits:
312 sources = makeSourceCatalog(objects, visit_time, start_id=start_id)
313 fsources = makeForcedSourceCatalog(objects, visit_time, ccdVisitId=start_id)
314 apdb.store(visit_time, objects, sources, fsources)
317 insert_ids = apdb.getInsertIds()
321 with self.
assertRaisesRegex(ValueError,
"APDB is not configured for history retrieval"):
322 apdb.getDiaObjectsHistory([])
325 assert insert_ids
is not None
328 def _check_history(insert_ids: list[ApdbInsertId]) ->
None:
329 n_records = len(insert_ids) * nobj
330 res = apdb.getDiaObjectsHistory(insert_ids)
332 res = apdb.getDiaSourcesHistory(insert_ids)
334 res = apdb.getDiaForcedSourcesHistory(insert_ids)
338 _check_history(insert_ids)
339 _check_history(insert_ids[1:])
340 _check_history(insert_ids[1:-1])
341 _check_history(insert_ids[3:4])
345 apdb.deleteInsertIds(insert_ids[:2])
346 insert_ids = apdb.getInsertIds()
347 assert insert_ids
is not None
350 _check_history(insert_ids)
353 """Store and retrieve SSObjects."""
357 apdb = make_apdb(config)
361 catalog = makeSSObjectCatalog(100, flags=1)
364 apdb.storeSSObjects(catalog)
367 res = apdb.getSSObjects()
371 catalog = makeSSObjectCatalog(100, 51, flags=2)
372 apdb.storeSSObjects(catalog)
373 res = apdb.getSSObjects()
379 """Reassign DiaObjects."""
383 apdb = make_apdb(config)
388 objects = makeObjectCatalog(region, 100, visit_time)
389 oids =
list(objects[
"diaObjectId"])
390 sources = makeSourceCatalog(objects, visit_time)
391 apdb.store(visit_time, objects, sources)
393 catalog = makeSSObjectCatalog(100)
394 apdb.storeSSObjects(catalog)
397 res = apdb.getDiaSources(region, oids, visit_time)
400 apdb.reassignDiaSources({1: 1, 2: 2, 5: 5})
401 res = apdb.getDiaSources(region, oids, visit_time)
405 apdb.reassignDiaSources(
414 """Test for time filtering of DiaSources."""
416 apdb = make_apdb(config)
421 src_time1 =
DateTime(
"2021-01-01T00:00:00", DateTime.TAI)
422 src_time2 =
DateTime(
"2021-01-01T00:00:02", DateTime.TAI)
423 visit_time0 =
DateTime(
"2021-12-26T23:59:59", DateTime.TAI)
424 visit_time1 =
DateTime(
"2021-12-27T00:00:01", DateTime.TAI)
425 visit_time2 =
DateTime(
"2021-12-27T00:00:03", DateTime.TAI)
427 objects = makeObjectCatalog(region, 100, visit_time0)
428 oids =
list(objects[
"diaObjectId"])
429 sources = makeSourceCatalog(objects, src_time1, 0)
430 apdb.store(src_time1, objects, sources)
432 sources = makeSourceCatalog(objects, src_time2, 100)
433 apdb.store(src_time2, objects, sources)
436 res = apdb.getDiaSources(region, oids, src_time2)
440 res = apdb.getDiaSources(region, oids, visit_time0)
444 res = apdb.getDiaSources(region, oids, visit_time1)
448 res = apdb.getDiaSources(region, oids, visit_time2)
452 """Test for time filtering of DiaForcedSources."""
454 apdb = make_apdb(config)
458 src_time1 =
DateTime(
"2021-01-01T00:00:00", DateTime.TAI)
459 src_time2 =
DateTime(
"2021-01-01T00:00:02", DateTime.TAI)
460 visit_time0 =
DateTime(
"2021-12-26T23:59:59", DateTime.TAI)
461 visit_time1 =
DateTime(
"2021-12-27T00:00:01", DateTime.TAI)
462 visit_time2 =
DateTime(
"2021-12-27T00:00:03", DateTime.TAI)
464 objects = makeObjectCatalog(region, 100, visit_time0)
465 oids =
list(objects[
"diaObjectId"])
466 sources = makeForcedSourceCatalog(objects, src_time1, 1)
467 apdb.store(src_time1, objects, forced_sources=sources)
469 sources = makeForcedSourceCatalog(objects, src_time2, 2)
470 apdb.store(src_time2, objects, forced_sources=sources)
473 res = apdb.getDiaForcedSources(region, oids, src_time2)
477 res = apdb.getDiaForcedSources(region, oids, visit_time0)
481 res = apdb.getDiaForcedSources(region, oids, visit_time1)
485 res = apdb.getDiaForcedSources(region, oids, visit_time2)
491 assertEqual: Callable[[Any, Any],
None]
492 assertIs: Callable[[Any, Any],
None]
493 assertIsInstance: Callable[[Any, Any],
None]
494 assertIsNone: Callable[[Any],
None]
495 assertIsNotNone: Callable[[Any],
None]
496 assertRaises: Callable[[Any], ContextManager]
497 assertRaisesRegex: Callable[[Any, Any], ContextManager]
501 """Base class for unit tests that verify how schema changes work."""
503 visit_time =
DateTime(
"2021-01-01T00:00:00", DateTime.TAI)
507 """Make config class instance used in all tests.
509 This method should return configuration that point to the identical
510 database instance on each call (i.e. ``db_url`` must be the same,
511 which also means
for sqlite it has to use on-disk storage).
513 raise NotImplementedError()
516 """Check that new code can work with old schema without history
522 apdb = make_apdb(config)
527 apdb = make_apdb(config)
534 objects = makeObjectCatalog(region, 100, visit_time)
535 sources = makeSourceCatalog(objects, visit_time)
536 fsources = makeForcedSourceCatalog(objects, visit_time)
537 apdb.store(visit_time, objects, sources, fsources)
540 insert_ids = apdb.getInsertIds()
546 assertIsNone: Callable[[Any],
None]
Class for handling dates/times, including MJD, UTC, and TAI.
ApdbConfig make_config(self, **Any kwargs)
None test_schema_add_history(self)
None test_storeSSObjects(self)
ApdbConfig make_config(self, **Any kwargs)
None test_empty_gets(self)
None assert_catalog(self, Any catalog, int rows, ApdbTables table)
None test_storeSources(self)
None test_makeSchema(self)
Callable assertIsInstance
None test_empty_gets_0months(self)
None test_getHistory(self)
None test_storeForcedSources(self)
None test_midpointMjdTai_src(self)
Callable assertRaisesRegex
bool fsrc_requires_id_list
None test_reassignObjects(self)
None assert_table_data(self, Any catalog, int rows, ApdbTables table)
None test_midpointMjdTai_fsrc(self)
ApdbTables getDiaObjects_table(self)
None test_storeObjects(self)
Angle represents an angle in radians.
Circle is a circular region on the unit sphere that contains its boundary.
UnitVector3d is a unit vector in ℝ³ with components stored in double precision.
daf::base::PropertyList * list
Region _make_region(tuple[float, float, float] xyz=(1.0, 1.0, -1.0))