from types import SimpleNamespace from unittest.mock import patch from django.test import SimpleTestCase from rest_framework.exceptions import ParseError from apps.bi.services import ( exec_dataset, execute_rendered_dataset, render_dataset_sql, ) from apps.bi.views import DatasetViewSet def dataset(**overrides): values = { "code": "output_daily", "sql_query": "select * from output where day = '{day}'", "default_param": {"day": "2026-08-01"}, "test_param": {"day": "2026-08-02"}, "echart_options": "", } values.update(overrides) return SimpleNamespace(**values) class DatasetExecutionServiceTests(SimpleTestCase): def test_render_does_not_mutate_default_parameters(self): item = dataset() sql = render_dataset_sql(item, {"day": "2026-08-10"}) self.assertIn("2026-08-10", sql) self.assertEqual(item.default_param, {"day": "2026-08-01"}) def test_render_reports_missing_parameters(self): item = dataset(default_param={}, sql_query="select '{required}'") with self.assertRaises(ParseError): render_dataset_sql(item) def test_execute_formats_each_statement(self): item = dataset(echart_options='{"series": {ds0}}') with ( patch("apps.bi.services.execute_raw_sql", return_value=([], [])), patch( "apps.bi.services.format_sqldata", return_value=([{"count": 1}], {"count": [1]}), ), ): response, can_cache = execute_rendered_dataset( item, "select 1;select 2" ) self.assertTrue(can_cache) self.assertEqual(set(response["data"]), {"ds0", "ds1"}) self.assertIn('"count": 1', response["echart_options"]) def test_empty_dataset_has_stable_empty_result(self): full_sql, response = exec_dataset(dataset(sql_query="")) self.assertEqual(full_sql, "") self.assertEqual(response, {"data": {}, "data2": {}}) class DatasetViewExecutionTests(SimpleTestCase): @patch("apps.bi.views.cache") @patch("apps.bi.views.execute_rendered_dataset") @patch("apps.bi.views.render_dataset_sql") @patch("apps.bi.views.DatasetSerializer") def test_api_reuses_shared_execution_service( self, serializer_mock, render_mock, execute_mock, cache_mock, ): item = dataset( enabled=True, name="日产量", cache_seconds=10, ) serializer_mock.return_value.data = { "code": item.code, "echart_options": "", } render_mock.return_value = "select 1" execute_mock.return_value = ( {"data": {"ds0": [{"count": 1}]}, "data2": {}}, True, ) cache_mock.get.return_value = None view = DatasetViewSet(basename="dataset") view.get_object = lambda: item request = SimpleNamespace( data={"query": {"day": "2026-08-10"}}, user=SimpleNamespace(id=42, belong_dept=SimpleNamespace(id=7)), ) response = view.exec(request) render_query = render_mock.call_args.args[1] self.assertEqual(render_query["r_user"], 42) self.assertEqual(render_query["r_dept"], 7) self.assertEqual(response.data["data"]["ds0"][0]["count"], 1)