You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/10/03 08:16:08 UTC
(superset) branch test/opensearch-testcontainers updated (965ab4b3097 -> f24572f47ea)
This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a change to branch test/opensearch-testcontainers
in repository https://gitbox.apache.org/repos/asf/superset.git
omit 965ab4b3097 test(opensearch): cover pagination and Content-Type regression against a real instance
add 62ab4cae23d fix(sql-lab): avoid duplicate generated result column names (#44189)
add 743a27f7ed3 fix(chart): sort Heatmap Y-axis by default when unset (#44588)
add 87bb752814a fix(select): remove Space wrapper from optionRender to fix option label truncation (#44357)
add d6689283942 fix(sql-lab): use function valueGetter for GridTable row numbers (#41574)
add 10356c30edb fix(mcp): stop partial-update tools from advertising null defaults (#44573)
add 6966c1f6ccb fix(cartodiagram): share Pie colors across locations in Explore (#44794)
add 81466987cbb fix(mcp): use create_proxy in simple_proxy for fastmcp 4 compatibility (#44787)
add ea79c63e878 fix(post-processing): stop treating gaps as zero for cumprod, cummin and cummax (#44828)
add f1ae8909d85 fix(import): remove duplicate config redefinition in load_configs (#44932)
new f24572f47ea test(opensearch): cover pagination and Content-Type regression against a real instance
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (965ab4b3097)
\
N -- N -- N refs/heads/test/opensearch-testcontainers (f24572f47ea)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
docs/developer_docs/contributing/howtos.md | 11 ++
.../src/chart/models/ChartProps.ts | 10 +
.../src/components/Select/AsyncSelect.test.tsx | 8 +
.../src/components/Select/AsyncSelect.tsx | 3 +-
.../src/components/Select/Select.test.tsx | 9 +
.../src/components/Select/Select.tsx | 3 +-
.../test/chart/models/ChartProps.test.ts | 22 ++-
.../src/util/transformPropsUtil.ts | 8 +
.../test/util/pieColors.test.ts | 203 +++++++++++++++++++++
.../src/Heatmap/controlPanel.tsx | 12 +-
.../src/Heatmap/transformProps.ts | 17 +-
.../plugin-chart-echarts/src/Heatmap/types.ts | 6 +
.../plugin-chart-echarts/src/Pie/transformProps.ts | 4 +-
.../test/Heatmap/transformProps.test.ts | 67 ++++++-
.../src/components/GridTable/index.tsx | 10 +-
superset/commands/importers/v1/utils.py | 7 +-
superset/mcp_service/chart/schemas.py | 15 +-
superset/mcp_service/dashboard/schemas.py | 3 +-
superset/mcp_service/dataset/schemas.py | 7 +-
superset/mcp_service/simple_proxy.py | 6 +-
superset/mcp_service/utils/schema_utils.py | 27 ++-
superset/result_set.py | 7 +
superset/translations/messages.pot | 3 +
superset/utils/pandas_postprocessing/cum.py | 7 +-
.../mcp_service/test_mcp_tool_registration.py | 106 +++++++++++
tests/unit_tests/pandas_postprocessing/test_cum.py | 35 ++++
tests/unit_tests/result_set_test.py | 42 ++++-
tests/unit_tests/test_mcp_simple_proxy.py | 45 +++++
28 files changed, 668 insertions(+), 35 deletions(-)
create mode 100644 superset-frontend/plugins/plugin-chart-cartodiagram/test/util/pieColors.test.ts
create mode 100644 tests/unit_tests/test_mcp_simple_proxy.py
(superset) 01/01: test(opensearch): cover pagination and Content-Type regression against a real instance
Posted by ru...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a commit to branch test/opensearch-testcontainers
in repository https://gitbox.apache.org/repos/asf/superset.git
commit f24572f47ea8aa49082b35a0a9d2a3f083df8183
Author: Evan Rusackas <ev...@rusackas.com>
AuthorDate: Fri Oct 2 14:59:23 2026 -0700
test(opensearch): cover pagination and Content-Type regression against a real instance
Adds testcontainers coverage for OpenDistroEngineSpec.fetch_data_with_cursor
against a real OpenSearch instance, pinning the regression fixed in
apache/superset#44703: OpenSearch's JDBC-style schema/datarows response shape
(vs Elasticsearch's columns/rows) broke pagination past page 1, and an
explicit Content-Type header duplicated opensearch-py's own, which OpenSearch
rejects outright. Both were already covered by mocked unit tests; this
exercises the same code path against a real server's real response.
Co-Authored-By: Evan Rusackas <ev...@preset.io>
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.github/workflows/testcontainers.yml | 1 +
.../db_engine_specs/test_opensearch.py | 155 +++++++++++++++++++++
2 files changed, 156 insertions(+)
diff --git a/.github/workflows/testcontainers.yml b/.github/workflows/testcontainers.yml
index 51a54d0c685..ae99c39b906 100644
--- a/.github/workflows/testcontainers.yml
+++ b/.github/workflows/testcontainers.yml
@@ -71,6 +71,7 @@ jobs:
{"dialect": "trino", "timeout": 10},
{"dialect": "mssql", "timeout": 10},
{"dialect": "elasticsearch", "timeout": 10},
+ {"dialect": "opensearch", "timeout": 10},
{"dialect": "oracle", "timeout": 15},
{"dialect": "db2", "timeout": 25},
{"dialect": "mariadb", "timeout": 10},
diff --git a/tests/testcontainers/db_engine_specs/test_opensearch.py b/tests/testcontainers/db_engine_specs/test_opensearch.py
new file mode 100644
index 00000000000..a52cc25029c
--- /dev/null
+++ b/tests/testcontainers/db_engine_specs/test_opensearch.py
@@ -0,0 +1,155 @@
+# 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.
+"""
+Tests db_engine_specs.elasticsearch.OpenDistroEngineSpec against a real
+OpenSearch instance, spun up on demand via testcontainers. Run via
+.github/workflows/testcontainers.yml.
+
+OpenSearch forked from Elasticsearch and its SQL plugin answers in a
+different, JDBC-style response shape (``schema``/``datarows`` on the legacy
+``/_opendistro/_sql`` endpoint Superset still targets, vs Elasticsearch's own
+``columns``/``rows``) -- apache/superset#44703 fixed
+``OpenDistroEngineSpec.fetch_data_with_cursor`` reading the wrong keys (an
+empty page past page 1) and sending a duplicate ``Content-Type`` header
+opensearch-py already sets (OpenSearch rejects the request outright). Both
+were already covered by mocked unit tests
+(``tests/unit_tests/db_engine_specs/test_elasticsearch.py``); this exercises
+the same code path against a real server's real response, which is the only
+way to catch a *third* undiscovered shape surprise the mocks don't model.
+
+Unlike the SQL-native dialects in this directory, OpenSearch has no CREATE
+TABLE / INSERT: documents get indexed via its REST API, matching how
+Superset actually encounters it in practice.
+"""
+
+from collections.abc import Iterator
+from typing import Any
+
+import pytest
+import requests
+
+pytestmark = pytest.mark.testcontainers
+
+from ._driver import require_driver # noqa: E402
+
+require_driver("testcontainers.community.opensearch")
+
+import es.opendistro # noqa: E402
+from testcontainers.community.opensearch import OpenSearchContainer # noqa: E402
+
+from superset.db_engine_specs.elasticsearch import OpenDistroEngineSpec # noqa: E402
+
+INDEX = "pilot_pagination"
+
+
+def _index_document(
+ base_url: str, index: str, doc_id: int, body: dict[str, int]
+) -> None:
+ response = requests.put(f"{base_url}/{index}/_doc/{doc_id}", json=body, timeout=10)
+ response.raise_for_status()
+
+
+def _refresh(base_url: str, index: str) -> None:
+ response = requests.post(f"{base_url}/{index}/_refresh", timeout=10)
+ response.raise_for_status()
+
+
+class _RealRawConnection:
+ """
+ Stands in for the one piece of ``Database`` that
+ ``OpenDistroEngineSpec.fetch_data_with_cursor`` actually touches:
+ ``get_raw_connection()`` as a context manager yielding the real
+ elasticsearch-dbapi ``Connection`` -- whose ``.es`` is a genuine
+ ``opensearchpy.OpenSearch`` client already pointed at the container, the
+ same object production code drives via ``conn.es.transport.perform_request``.
+ Nothing here is mocked; this only avoids constructing a full Superset
+ ``Database`` ORM row (and the app/DB-session machinery that implies) for
+ a test that only needs one real DBAPI connection.
+ """
+
+ def __init__(self, host: str, port: int) -> None:
+ self._conn = es.opendistro.connect(host=host, port=port, scheme="http")
+
+ def get_raw_connection(self) -> "_RealRawConnection":
+ return self
+
+ def __enter__(self) -> Any:
+ return self._conn
+
+ def __exit__(self, *exc_info: object) -> None:
+ self._conn.close()
+
+
+@pytest.fixture(scope="module")
+def container() -> Iterator[OpenSearchContainer]:
+ with OpenSearchContainer(
+ "opensearchproject/opensearch:2.4.0", security_enabled=False
+ ) as c:
+ cfg = c.get_config()
+ base_url = f"http://{cfg['host']}:{cfg['port']}"
+
+ for i in range(3):
+ _index_document(base_url, INDEX, i, {"id": i})
+ _refresh(base_url, INDEX)
+
+ yield c
+
+
+def test_fetch_data_with_cursor_reads_later_pages(
+ container: OpenSearchContainer,
+) -> None:
+ """
+ Regression test for apache/superset#44703: reading Elasticsearch's
+ ``columns``/``rows`` keys against OpenSearch's actual ``schema``/
+ ``datarows`` response returned an empty page for anything past page 1.
+ page_size=1 against 3 real documents forces real cursor pagination --
+ this is a live-wire-format check that only a real server can give.
+ """
+ cfg = container.get_config()
+ database = _RealRawConnection(cfg["host"], int(cfg["port"]))
+
+ rows, columns = OpenDistroEngineSpec.fetch_data_with_cursor(
+ database=database, # type: ignore[arg-type]
+ sql=f"SELECT id FROM {INDEX} ORDER BY id", # noqa: S608
+ page_index=2,
+ page_size=1,
+ )
+
+ assert columns == ["id"]
+ assert rows == [[2]]
+
+
+def test_fetch_data_with_cursor_does_not_duplicate_content_type(
+ container: OpenSearchContainer,
+) -> None:
+ """
+ opensearch-py's transport already sets Content-Type; apache/superset#44703
+ stopped also passing an explicit one, which OpenSearch's SQL plugin
+ rejects outright ("only one Content-Type header should be provided").
+ A passing fetch here is itself proof the duplicate header regressed.
+ """
+ cfg = container.get_config()
+ database = _RealRawConnection(cfg["host"], int(cfg["port"]))
+
+ rows, _columns = OpenDistroEngineSpec.fetch_data_with_cursor(
+ database=database, # type: ignore[arg-type]
+ sql=f"SELECT id FROM {INDEX} ORDER BY id", # noqa: S608
+ page_index=0,
+ page_size=10,
+ )
+
+ assert len(rows) == 3