mcp-server-sqlseed 0.2.4__tar.gz → 0.2.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/.gitignore +10 -0
  2. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/PKG-INFO +16 -9
  3. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/README.md +14 -7
  4. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/README.zh-CN.md +14 -10
  5. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/pyproject.toml +1 -1
  6. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/src/mcp_server_sqlseed/server.py +23 -9
  7. mcp_server_sqlseed-0.2.6/tests/test_default_provider.py +114 -0
  8. mcp_server_sqlseed-0.2.6/tests/test_error_redaction.py +24 -0
  9. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/tests/test_yaml_execution_validation.py +32 -0
  10. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/LICENSE +0 -0
  11. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/src/mcp_server_sqlseed/__init__.py +0 -0
  12. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/src/mcp_server_sqlseed/__main__.py +0 -0
  13. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/src/mcp_server_sqlseed/config.py +0 -0
  14. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/tests/.pylintrc +0 -0
  15. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/tests/__init__.py +0 -0
  16. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/tests/conftest.py +0 -0
  17. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/tests/test_config.py +0 -0
  18. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/tests/test_server.py +0 -0
  19. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/tests/test_stdio_progress.py +0 -0
  20. {mcp_server_sqlseed-0.2.4 → mcp_server_sqlseed-0.2.6}/tests/test_validate_db_path.py +0 -0
@@ -59,6 +59,16 @@ dmypy.json
59
59
  *.db
60
60
  *.sqlite
61
61
  *.sqlite3
62
+ # SQLite runtime sidecars
63
+ *.db-wal
64
+ *.db-shm
65
+ *.db-journal
66
+ *.sqlite-wal
67
+ *.sqlite-shm
68
+ *.sqlite-journal
69
+ *.sqlite3-wal
70
+ *.sqlite3-shm
71
+ *.sqlite3-journal
62
72
  .sqlseed_cache/
63
73
  snapshots/
64
74
  .env
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: mcp-server-sqlseed
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: Offline MCP tools for sqlseed rule-driven YAML configuration and test data generation
5
5
  Project-URL: Documentation, https://sunbos.github.io/sqlseed/guide/
6
6
  Project-URL: Homepage, https://github.com/sunbos/sqlseed
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Programming Language :: Python :: 3.13
18
18
  Requires-Python: >=3.10
19
19
  Requires-Dist: mcp<2,>=1.0
20
- Requires-Dist: sqlseed<0.3,>=0.2.4.dev0
20
+ Requires-Dist: sqlseed<0.3,>=0.2.5.dev0
21
21
  Description-Content-Type: text/markdown
22
22
 
23
23
  # mcp-server-sqlseed
@@ -31,13 +31,15 @@ schema, then generate test data. Both tools use offline Core rules and require n
31
31
 
32
32
  ## Installation
33
33
 
34
- For the 0.2.4 release, use a Python 3.10+ virtual environment:
34
+ These instructions target version 0.2.6. Check [Releases](https://github.com/sunbos/sqlseed/releases)
35
+ for publication status; use the source installation below to test an unpublished candidate.
36
+ Use a Python 3.10+ virtual environment:
35
37
 
36
38
  ```bash
37
- python -m pip install "mcp-server-sqlseed==0.2.4"
39
+ python -m pip install "mcp-server-sqlseed==0.2.6"
38
40
  ```
39
41
 
40
- Core 0.2.3 lacks the target-validation interfaces used here.
42
+ Core 0.2.4 and older lack the shared diagnostic interfaces required by this version.
41
43
  For development, install Core and the required local plugins together from the
42
44
  repository root:
43
45
 
@@ -72,13 +74,15 @@ path. The server runs over stdio. `python -m mcp_server_sqlseed` is also support
72
74
  The `db_path` argument accepts an existing SQLite `.db`/`.sqlite`/`.sqlite3` file or a
73
75
  database URL. For PostgreSQL, install the Core `postgres` extra. Tables must already
74
76
  exist. The schema mapper chooses generators deterministically; no model request is
75
- made by either tool.
77
+ made by either tool. Both tools use the required Faker provider with the `en_US`
78
+ locale, matching the generated template; Mimesis is not required.
76
79
 
77
80
  When `yaml_config` is supplied, it must be a YAML mapping containing the requested
78
81
  table. Empty documents, unknown tables, and configurations for a different table fail
79
82
  before generation. The UTF-8 size limit is 256 KiB. Tool arguments select the database,
80
83
  table, row count, and enrichment; YAML contributes only the matching table's column
81
- rules, seed, and `clear_before` setting.
84
+ rules, seed, and `clear_before` setting. Top-level YAML `provider` and `locale`
85
+ settings do not change this server's engine or locale.
82
86
 
83
87
  A typical client workflow is:
84
88
 
@@ -97,7 +101,7 @@ schema-inspection tool are not provided. For LLM analysis, install the separate
97
101
  AI MCP extra:
98
102
 
99
103
  ```bash
100
- python -m pip install "sqlseed-ai[mcp]==0.2.4"
104
+ python -m pip install "sqlseed-ai[mcp]==0.2.6"
101
105
  mcp-server-sqlseed-ai
102
106
  ```
103
107
 
@@ -108,8 +112,11 @@ Its YAML tool is
108
112
 
109
113
  ## Requirements
110
114
 
115
+ These metadata requirements apply to version 0.2.6 and its source candidates.
116
+ Use local Core and plugins together when developing from source.
117
+
111
118
  - Python `>=3.10`
112
- - `sqlseed>=0.2.4.dev0,<0.3`
119
+ - `sqlseed>=0.2.5.dev0,<0.3`
113
120
  - `mcp>=1.0,<2`
114
121
 
115
122
  See the [user guide](https://sunbos.github.io/sqlseed/guide/),
@@ -9,13 +9,15 @@ schema, then generate test data. Both tools use offline Core rules and require n
9
9
 
10
10
  ## Installation
11
11
 
12
- For the 0.2.4 release, use a Python 3.10+ virtual environment:
12
+ These instructions target version 0.2.6. Check [Releases](https://github.com/sunbos/sqlseed/releases)
13
+ for publication status; use the source installation below to test an unpublished candidate.
14
+ Use a Python 3.10+ virtual environment:
13
15
 
14
16
  ```bash
15
- python -m pip install "mcp-server-sqlseed==0.2.4"
17
+ python -m pip install "mcp-server-sqlseed==0.2.6"
16
18
  ```
17
19
 
18
- Core 0.2.3 lacks the target-validation interfaces used here.
20
+ Core 0.2.4 and older lack the shared diagnostic interfaces required by this version.
19
21
  For development, install Core and the required local plugins together from the
20
22
  repository root:
21
23
 
@@ -50,13 +52,15 @@ path. The server runs over stdio. `python -m mcp_server_sqlseed` is also support
50
52
  The `db_path` argument accepts an existing SQLite `.db`/`.sqlite`/`.sqlite3` file or a
51
53
  database URL. For PostgreSQL, install the Core `postgres` extra. Tables must already
52
54
  exist. The schema mapper chooses generators deterministically; no model request is
53
- made by either tool.
55
+ made by either tool. Both tools use the required Faker provider with the `en_US`
56
+ locale, matching the generated template; Mimesis is not required.
54
57
 
55
58
  When `yaml_config` is supplied, it must be a YAML mapping containing the requested
56
59
  table. Empty documents, unknown tables, and configurations for a different table fail
57
60
  before generation. The UTF-8 size limit is 256 KiB. Tool arguments select the database,
58
61
  table, row count, and enrichment; YAML contributes only the matching table's column
59
- rules, seed, and `clear_before` setting.
62
+ rules, seed, and `clear_before` setting. Top-level YAML `provider` and `locale`
63
+ settings do not change this server's engine or locale.
60
64
 
61
65
  A typical client workflow is:
62
66
 
@@ -75,7 +79,7 @@ schema-inspection tool are not provided. For LLM analysis, install the separate
75
79
  AI MCP extra:
76
80
 
77
81
  ```bash
78
- python -m pip install "sqlseed-ai[mcp]==0.2.4"
82
+ python -m pip install "sqlseed-ai[mcp]==0.2.6"
79
83
  mcp-server-sqlseed-ai
80
84
  ```
81
85
 
@@ -86,8 +90,11 @@ Its YAML tool is
86
90
 
87
91
  ## Requirements
88
92
 
93
+ These metadata requirements apply to version 0.2.6 and its source candidates.
94
+ Use local Core and plugins together when developing from source.
95
+
89
96
  - Python `>=3.10`
90
- - `sqlseed>=0.2.4.dev0,<0.3`
97
+ - `sqlseed>=0.2.5.dev0,<0.3`
91
98
  - `mcp>=1.0,<2`
92
99
 
93
100
  See the [user guide](https://sunbos.github.io/sqlseed/guide/),
@@ -3,19 +3,19 @@
3
3
  [English](https://github.com/sunbos/sqlseed/blob/main/plugins/mcp-server-sqlseed/README.md) |
4
4
  **[中文](https://github.com/sunbos/sqlseed/blob/main/plugins/mcp-server-sqlseed/README.zh-CN.md)**
5
5
 
6
- 为 [sqlseed](https://sunbos.github.io/sqlseed/) 提供
6
+ 为 [sqlseed](https://sunbos.github.io/sqlseed/zh-CN/) 提供
7
7
  [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) 工具:从数据库结构推导
8
8
  YAML 配置,再生成测试数据。两个工具均使用离线 Core 规则,无需 LLM。
9
9
 
10
10
  ## 安装
11
11
 
12
- 安装 0.2.4 版本时,使用 Python 3.10+ 虚拟环境:
12
+ 本文安装说明对应 0.2.6 版本;发布状态以 [Releases](https://github.com/sunbos/sqlseed/releases) 为准。测试尚未发布的候选版本时,使用下方的源码安装方式。请先创建并激活 Python 3.10+ 虚拟环境:
13
13
 
14
14
  ```bash
15
- python -m pip install "mcp-server-sqlseed==0.2.4"
15
+ python -m pip install "mcp-server-sqlseed==0.2.6"
16
16
  ```
17
17
 
18
- Core 0.2.3 缺少当前入口使用的数据库目标校验接口。
18
+ Core 0.2.4 及更早版本缺少本版本使用的共享诊断接口。
19
19
  开发源码时,从仓库根一次安装本地 Core 与 MCP:
20
20
 
21
21
  ```bash
@@ -48,12 +48,14 @@ python -m pip install -e . -e ./plugins/mcp-server-sqlseed
48
48
 
49
49
  `db_path` 接受已存在的 SQLite `.db`、`.sqlite`、`.sqlite3` 文件或数据库 URL。
50
50
  使用 PostgreSQL 时需在同一环境安装 Core 的 `postgres` extra。目标表必须已存在。
51
- 列映射由 Core 规则确定,两个工具都不会向模型发送请求。
51
+ 列映射由 Core 规则确定,两个工具都不会向模型发送请求。两者均使用 Core 必需依赖
52
+ Faker 和 `en_US` locale,与生成的模板一致;无需安装 Mimesis。
52
53
 
53
54
  提供 `yaml_config` 时,其内容必须为 YAML mapping,并包含所请求的表。
54
55
  空文档、未知目标表或不包含目标表的配置会在生成前失败;UTF-8 编码后上限为 256 KiB。
55
56
  工具参数决定数据库、表、生成行数和 enrich;YAML 仅提供匹配表的列规则、seed 与
56
- `clear_before`,不覆盖工具参数指定的范围。
57
+ `clear_before`,不覆盖工具参数指定的范围。YAML 顶层的 `provider` 和 `locale`
58
+ 不会改变此服务器使用的引擎或语言地区。
57
59
 
58
60
  ## 使用示例与失败结果
59
61
 
@@ -77,7 +79,7 @@ MCP 传输成功不等于配置或填充成功,需要检查返回内容。
77
79
  LLM 分析属于另一个 AI MCP 进程,安装 AI extra 后启动:
78
80
 
79
81
  ```bash
80
- python -m pip install "sqlseed-ai[mcp]==0.2.4"
82
+ python -m pip install "sqlseed-ai[mcp]==0.2.6"
81
83
  mcp-server-sqlseed-ai
82
84
  ```
83
85
 
@@ -89,12 +91,14 @@ AI YAML 工具名为 `sqlseed_ai_generate_yaml`,命令入口为 `mcp-server-sq
89
91
 
90
92
  ## 依赖
91
93
 
94
+ 以下为 0.2.6 版本及其源码候选的依赖要求。源码开发时请在同一次解析中安装本地 Core 和插件。
95
+
92
96
  - Python `>=3.10`
93
- - `sqlseed>=0.2.4.dev0,<0.3`
97
+ - `sqlseed>=0.2.5.dev0,<0.3`
94
98
  - `mcp>=1.0,<2`
95
99
 
96
- 更多信息见[用户指南](https://sunbos.github.io/sqlseed/guide/)、
97
- [升级说明](https://sunbos.github.io/sqlseed/migration.zh-CN/)和
100
+ 更多信息见[用户指南](https://sunbos.github.io/sqlseed/zh-CN/guide/)、
101
+ [升级说明](https://sunbos.github.io/sqlseed/zh-CN/migration/)和
98
102
  [服务器源码](https://github.com/sunbos/sqlseed/tree/main/plugins/mcp-server-sqlseed)。
99
103
 
100
104
  许可证:[AGPL-3.0-or-later](https://github.com/sunbos/sqlseed/blob/main/LICENSE)。
@@ -23,7 +23,7 @@ classifiers = [
23
23
  "Programming Language :: Python :: 3.13",
24
24
  ]
25
25
  dependencies = [
26
- "sqlseed>=0.2.4.dev0,<0.3",
26
+ "sqlseed>=0.2.5.dev0,<0.3",
27
27
  "mcp>=1.0,<2",
28
28
  ]
29
29
 
@@ -19,8 +19,10 @@ from sqlseed._utils.logger import get_logger
19
19
  from sqlseed._utils.paths import validate_db_target as _validate_db_target
20
20
  from sqlseed._utils.paths import validate_table_name as _validate_table_name
21
21
  from sqlseed._utils.progress import NullProgressBackend
22
+ from sqlseed._utils.redaction import redact_url_credentials
22
23
  from sqlseed.config.models import GeneratorConfig
23
24
  from sqlseed.core.orchestrator import DataOrchestrator
25
+ from sqlseed.generators import ConfigurationError
24
26
 
25
27
  logger = get_logger(__name__)
26
28
 
@@ -56,7 +58,7 @@ def sqlseed_generate_yaml(db_path: str, table_name: str) -> str:
56
58
  """
57
59
  try:
58
60
  db_path = _validate_db_target(db_path)
59
- with DataOrchestrator(db_path) as orch:
61
+ with DataOrchestrator(db_path, provider_name="faker", locale="en_US") as orch:
60
62
  _validate_table_name(table_name, orch.get_table_names())
61
63
  specs = orch.get_column_mapping(table_name)
62
64
 
@@ -69,9 +71,15 @@ def sqlseed_generate_yaml(db_path: str, table_name: str) -> str:
69
71
  }
70
72
  logger.info("Rule-driven YAML generated", table_name=table_name, columns=len(columns))
71
73
  return str(yaml.dump(output, allow_unicode=True, sort_keys=False, default_flow_style=False))
72
- except (ValueError, RuntimeError, OSError) as e:
73
- logger.error("Failed to generate YAML", db_path=db_path, table_name=table_name, error=str(e))
74
- return f"# Error: {e}"
74
+ except (ValueError, RuntimeError, OSError, ConfigurationError) as e:
75
+ message = redact_url_credentials(str(e))
76
+ logger.error(
77
+ "Failed to generate YAML",
78
+ db_path=redact_url_credentials(db_path),
79
+ table_name=table_name,
80
+ error=message,
81
+ )
82
+ return f"# Error: {message}"
75
83
 
76
84
 
77
85
  @mcp.tool()
@@ -89,7 +97,7 @@ def sqlseed_execute_fill(
89
97
  if yaml_config is not None and len(yaml_config.encode("utf-8")) > _MAX_YAML_CONFIG_SIZE:
90
98
  raise ValueError(f"yaml_config exceeds maximum allowed size of {_MAX_YAML_CONFIG_SIZE} bytes")
91
99
 
92
- with DataOrchestrator(db_path) as orch:
100
+ with DataOrchestrator(db_path, provider_name="faker", locale="en_US") as orch:
93
101
  _validate_table_name(table_name, orch.get_table_names())
94
102
  column_configs = None
95
103
  clear_before = False
@@ -133,8 +141,14 @@ def sqlseed_execute_fill(
133
141
  "table_name": result.table_name,
134
142
  "count": result.count,
135
143
  "elapsed": result.elapsed,
136
- "errors": result.errors,
144
+ "errors": [redact_url_credentials(error) for error in result.errors],
137
145
  }
138
- except (ValueError, RuntimeError, OSError, yaml.YAMLError) as e:
139
- logger.error("Failed to execute fill", db_path=db_path, table_name=table_name, error=str(e))
140
- return {"error": str(e)}
146
+ except (ValueError, RuntimeError, OSError, ConfigurationError, yaml.YAMLError) as e:
147
+ message = redact_url_credentials(str(e))
148
+ logger.error(
149
+ "Failed to execute fill",
150
+ db_path=redact_url_credentials(db_path),
151
+ table_name=table_name,
152
+ error=message,
153
+ )
154
+ return {"error": message}
@@ -0,0 +1,114 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import subprocess
5
+ import sys
6
+ import textwrap
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from pathlib import Path
11
+
12
+
13
+ def test_mcp_uses_faker_en_us_without_mimesis(tmp_path: Path) -> None:
14
+ """Compare real SQLite values with upstream Faker, including default mapping."""
15
+ program = textwrap.dedent(
16
+ """
17
+ import importlib.abc
18
+ import json
19
+ import sys
20
+
21
+ class MissingMimesis(importlib.abc.MetaPathFinder):
22
+ def find_spec(self, fullname, path=None, target=None):
23
+ if fullname == 'mimesis' or fullname.startswith('mimesis.'):
24
+ raise ModuleNotFoundError('Unavailable test dependency: ' + fullname)
25
+ sys.meta_path.insert(0, MissingMimesis())
26
+
27
+ from faker import Faker
28
+ from structlog.testing import capture_logs
29
+ import yaml
30
+
31
+ with capture_logs() as events:
32
+ from mcp_server_sqlseed.server import sqlseed_execute_fill, sqlseed_generate_yaml
33
+ from sqlseed.core.orchestrator import DataOrchestrator
34
+ from sqlseed.generators import registry
35
+ from tests.sqlite_helpers import sqlite_connection
36
+
37
+ database = sys.argv[1]
38
+ seed = 31415
39
+ count = 5
40
+ with sqlite_connection(database) as connection:
41
+ connection.execute('CREATE TABLE people (first_name TEXT NOT NULL)')
42
+
43
+ template = yaml.safe_load(sqlseed_generate_yaml(database, 'people'))
44
+ template_defaults = {key: template[key] for key in ('provider', 'locale')}
45
+ expected_faker = Faker('en_US')
46
+ expected_faker.seed_instance(seed)
47
+ expected = [expected_faker.first_name() for _ in range(count)]
48
+
49
+ def read_names():
50
+ with sqlite_connection(database) as connection:
51
+ return [row[0] for row in connection.execute(
52
+ 'SELECT first_name FROM people ORDER BY rowid'
53
+ )]
54
+
55
+ # No YAML: use the upstream seed API, without replacing the provider.
56
+ Faker.seed(seed)
57
+ default_result = sqlseed_execute_fill(database, 'people', count=count)
58
+ default_names = read_names()
59
+
60
+ table = template['tables'][0]
61
+ table['seed'] = seed
62
+ table['clear_before'] = True
63
+ generated_result = sqlseed_execute_fill(
64
+ database, 'people', count=count, yaml_config=yaml.safe_dump(template)
65
+ )
66
+ generated_names = read_names()
67
+
68
+ # Top-level engine/locale settings remain outside the tool's YAML scope.
69
+ template['provider'] = 'mimesis'
70
+ template['locale'] = 'zh_CN'
71
+ overridden_result = sqlseed_execute_fill(
72
+ database, 'people', count=count, yaml_config=yaml.safe_dump(template)
73
+ )
74
+ overridden_names = read_names()
75
+ tool_warnings = [event for event in events if event['log_level'] == 'warning']
76
+
77
+ # Positive control: the explicit unavailable Core provider still warns.
78
+ events.clear()
79
+ with DataOrchestrator(database, provider_name='mimesis') as orchestrator:
80
+ tables = orchestrator.get_table_names()
81
+ fallback_warnings = [event for event in events if event['log_level'] == 'warning']
82
+
83
+ print(json.dumps({
84
+ 'mimesis_available': registry.HAS_MIMESIS,
85
+ 'template_defaults': template_defaults,
86
+ 'expected': expected,
87
+ 'names': [default_names, generated_names, overridden_names],
88
+ 'results': [default_result, generated_result, overridden_result],
89
+ 'tool_warnings': tool_warnings,
90
+ 'fallback_warnings': fallback_warnings,
91
+ 'tables': tables,
92
+ }))
93
+ """
94
+ )
95
+ result = subprocess.run(
96
+ [sys.executable, "-c", program, str(tmp_path / "faker-default.db")],
97
+ capture_output=True,
98
+ text=True,
99
+ check=True,
100
+ timeout=60,
101
+ )
102
+ status = json.loads(result.stdout)
103
+ assert status["mimesis_available"] is False
104
+ assert status["template_defaults"] == {"provider": "faker", "locale": "en_US"}
105
+ assert status["names"] == [status["expected"]] * 3
106
+ for fill_result in status["results"]:
107
+ assert fill_result["count"] == 5
108
+ assert fill_result["errors"] == []
109
+ assert status["tool_warnings"] == []
110
+ assert status["tables"] == ["people"]
111
+ assert any(
112
+ event["event"] == "Provider not available, falling back to 'base'" and event["provider_name"] == "mimesis"
113
+ for event in status["fallback_warnings"]
114
+ )
@@ -0,0 +1,24 @@
1
+ """Both core MCP error shapes and their stderr diagnostics hide URL credentials."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import subprocess
6
+ import sys
7
+
8
+
9
+ def test_core_mcp_malformed_url_is_redacted_in_results_and_logs() -> None:
10
+ script = """
11
+ from mcp_server_sqlseed.server import sqlseed_generate_yaml, sqlseed_execute_fill
12
+ target='sqlite://audit-user:synthetic-userinfo@host/db?password=synthetic-query'
13
+ for fn in (sqlseed_generate_yaml, sqlseed_execute_fill):
14
+ result=fn(target, 'items')
15
+ message=result if isinstance(result,str) else result['error']
16
+ assert 'Invalid database URL' in message
17
+ assert 'synthetic-userinfo' not in message and 'synthetic-query' not in message
18
+ print(message)
19
+ """
20
+ result = subprocess.run([sys.executable, "-c", script], capture_output=True, text=True, timeout=30, check=True)
21
+ assert "Failed to generate YAML" in result.stderr
22
+ assert "Failed to execute fill" in result.stderr
23
+ assert "synthetic-userinfo" not in result.stdout + result.stderr
24
+ assert "synthetic-query" not in result.stdout + result.stderr
@@ -97,3 +97,35 @@ def test_valid_yaml_keeps_tool_target_count_and_matching_column_rules(tmp_path:
97
97
  with closing(sqlite3.connect(database)) as connection:
98
98
  assert connection.execute("SELECT value FROM users ORDER BY rowid").fetchall() == [(91,), (17,), (17,)]
99
99
  assert connection.execute("SELECT value FROM orders").fetchall() == [(92,)]
100
+
101
+
102
+ def test_invalid_date_range_returns_tool_error_without_appending(tmp_path: Path) -> None:
103
+ database = tmp_path / "invalid-date.db"
104
+ with closing(sqlite3.connect(database)) as connection:
105
+ connection.executescript(
106
+ "CREATE TABLE events (created_at DATE NOT NULL); INSERT INTO events VALUES ('2020-01-01');"
107
+ )
108
+ document = yaml.safe_dump(
109
+ {
110
+ "db_path": str(database),
111
+ "tables": [
112
+ {
113
+ "name": "events",
114
+ "count": 5,
115
+ "columns": [
116
+ {
117
+ "name": "created_at",
118
+ "generator": "date",
119
+ "params": {"start_date": "2026-09-26", "end_date": "2026-09-26", "weekdays": "workdays"},
120
+ }
121
+ ],
122
+ }
123
+ ],
124
+ }
125
+ )
126
+ result = sqlseed_execute_fill(str(database), "events", count=5, yaml_config=document)
127
+ assert set(result) == {"error"}
128
+ assert "created_at" in result["error"]
129
+ assert "date" in result["error"]
130
+ with closing(sqlite3.connect(database)) as connection:
131
+ assert connection.execute("SELECT * FROM events").fetchall() == [("2020-01-01",)]