mirror of
https://github.com/apache/superset.git
synced 2026-07-18 20:55:47 +00:00
VersionDAO carried five distinct concerns under one class — UUID derivation, version metadata queries, change-record loading, single-version snapshot retrieval, and restore orchestration. Bob's "and" test (the clean-code review flagged this as the next structural fix after the dead-code purge) gives ~600 lines of "queries about versioned state of one entity AND the workflow that mutates it." Splits the read and write sides into purpose-built modules: - ``superset/versioning/queries.py`` — UUID derivation (``VERSION_UUID_NAMESPACE``, ``derive_version_uuid``) + read-side helpers (``find_active_by_uuid``, ``current_version_number``, ``current_live_transaction_id``, ``current_live_version_uuid``, ``list_versions``, ``resolve_version_uuid``, ``get_version``, ``list_change_records_batch``). ~475 lines. - ``superset/versioning/restore.py`` — write-side (``restore_version``, ``_stamp_audit_fields_for_restore``, ``_RESTORE_RELATIONS``). ~140 lines. Depends only on ``queries.find_active_by_uuid`` and ``utils.single_flush_scope``. - ``superset/daos/version.py`` — collapsed to an ~85-line backward-compat façade that re-exports both modules under a single ``VersionDAO`` class via ``staticmethod`` aliases. The module also re-exports ``VERSION_UUID_NAMESPACE`` and ``derive_version_uuid`` at module level so the ~10 existing callers (api.py handlers, command classes, the ETag emitter, integration tests) don't have to change their imports. New code is encouraged to import from the sub-modules directly. The functions themselves are unchanged byte-for-byte aside from internal call sites being rewritten from ``VersionDAO.foo`` to the bare function name (since they now live as module-level functions, not class methods). One unit-test mock target moved: ``test_restore_version_returns_none_for_unknown_entity`` now patches ``superset.versioning.restore.find_active_by_uuid`` (the actual call site) instead of ``VersionDAO.find_active_by_uuid`` (which is now just an alias). Each of the three modules now has one reason to change. When the sc-103157 soft-delete pass adds the ``deleted_at IS NULL`` filter to ``find_active_by_uuid``, it touches only ``queries.py``. When a per-entity-type restore Strategy replaces the string-keyed ``_RESTORE_RELATIONS`` dispatch, it touches only ``restore.py``.
98 lines
3.8 KiB
Python
98 lines
3.8 KiB
Python
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance
|
|
# with the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing,
|
|
# software distributed under the License is distributed on an
|
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, either express or implied. See the License for the
|
|
# specific language governing permissions and limitations
|
|
# under the License.
|
|
"""Unit tests for ``VersionDAO``.
|
|
|
|
Exercises the pure helpers (``derive_version_uuid``) and the
|
|
``restore_version`` control-flow branches that can be covered with mocks
|
|
alone. Full round-trip scalar restore / audit stamping / non-destructive
|
|
behaviour is covered by the integration tests in
|
|
``tests/integration_tests/{charts,dashboards,datasets}/version_history_tests.py``
|
|
— those need a real Continuum stack and live DB, which unit tests here
|
|
deliberately avoid.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
from uuid import UUID
|
|
|
|
from superset.daos.version import (
|
|
derive_version_uuid,
|
|
VERSION_UUID_NAMESPACE,
|
|
VersionDAO,
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# derive_version_uuid
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_derive_version_uuid_is_deterministic():
|
|
entity = UUID("14f48794-ebfa-4f60-a26a-582c49132f1b")
|
|
assert derive_version_uuid(entity, 42) == derive_version_uuid(entity, 42)
|
|
|
|
|
|
def test_derive_version_uuid_differs_across_tx():
|
|
entity = UUID("14f48794-ebfa-4f60-a26a-582c49132f1b")
|
|
assert derive_version_uuid(entity, 1) != derive_version_uuid(entity, 2)
|
|
|
|
|
|
def test_derive_version_uuid_differs_across_entities():
|
|
tx = 42
|
|
a = UUID("14f48794-ebfa-4f60-a26a-582c49132f1b")
|
|
b = UUID("b388a396-cbca-4299-a443-3e41e870e2c2")
|
|
assert derive_version_uuid(a, tx) != derive_version_uuid(b, tx)
|
|
|
|
|
|
def test_derive_version_uuid_is_v5():
|
|
"""UUIDs must be version 5 — changing this is a breaking change."""
|
|
entity = UUID("14f48794-ebfa-4f60-a26a-582c49132f1b")
|
|
result = derive_version_uuid(entity, 1)
|
|
assert result.version == 5
|
|
|
|
|
|
def test_derive_version_uuid_uses_fixed_namespace():
|
|
"""Asserts the namespace constant hasn't drifted (changing it
|
|
invalidates every cached version_uuid — see the constant's comment)."""
|
|
assert VERSION_UUID_NAMESPACE == UUID("7a6f5d9b-4c3b-5d8e-9a1c-0e2b4c6d8f10")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# restore_version control-flow — unknown entity / out-of-range version
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@patch("superset.versioning.restore.find_active_by_uuid", return_value=None)
|
|
def test_restore_version_returns_none_for_unknown_entity(mock_find):
|
|
"""Unknown entity UUID → caller raises 404."""
|
|
result = VersionDAO.restore_version(
|
|
MagicMock(__name__="Dashboard"),
|
|
UUID("00000000-0000-0000-0000-000000000000"),
|
|
0,
|
|
)
|
|
assert result is None
|
|
|
|
|
|
# Out-of-range version_num (the lookup query returns None) is verified
|
|
# end-to-end in the integration tests
|
|
# (``test_restore_returns_404_for_unknown_version_uuid`` in the three
|
|
# {charts,dashboards,datasets}/version_history_tests.py suites). A pure
|
|
# unit-level version of that test would require mocking the full
|
|
# SQLAlchemy expression tree — including ``ver_cls.operation_type != 0``
|
|
# — which is fragile and doesn't add coverage beyond what the
|
|
# integration path already provides.
|