@sun-asterisk/sungen 3.2.8 → 3.2.9
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.
- package/dist/orchestrator/templates/ai-src/commands/create-test.md +2 -2
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +1 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +28 -0
- package/dist/orchestrator/templates/specs-db.d.ts +10 -2
- package/dist/orchestrator/templates/specs-db.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-db.js +98 -6
- package/dist/orchestrator/templates/specs-db.js.map +1 -1
- package/dist/orchestrator/templates/specs-db.ts +77 -8
- package/package.json +7 -4
- package/src/orchestrator/templates/ai-src/commands/create-test.md +2 -2
- package/src/orchestrator/templates/ai-src/commands/run-test.md +1 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +28 -0
- package/src/orchestrator/templates/specs-db.ts +77 -8
|
@@ -149,7 +149,7 @@ If the unit is **api-first** (`qa/api/<name>/` or `qa/api/flows/<name>/`), the d
|
|
|
149
149
|
|
|
150
150
|
{{#cap parallel-subagents}}
|
|
151
151
|
4. Follow the `sungen-tc-generation` skill for section identification, viewpoint generation, and output format. **Viewpoint loading discipline:** `sungen-viewpoint` is a **router** — from the page-type (form / list / detail / auth / dashboard …) read **only the matching group file(s)** (e.g. a login screen → group-e-identity; a product list → group-c-data-explore), never all five groups. This keeps the generation context lean. **For flows**, use the "Flow Test Generation" section in the skill. When requirements exist, use the "Requirements-Driven Generation" strategy. **For Tier 1**, apply the **Lightweight Guard** — verify required fields, validation rules, business rules, security checks, and key state transitions all have TCs after generation. **For Tier 2+**, **MUST** apply the full **Mapping Contract** — walk every `spec.md` section top-to-bottom and produce the indicated TCs per Table 1; handle `test-viewpoint.md` per Table 2. Do not silently skip sections.
|
|
152
|
-
5. Generate `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation`. **Partition the work into shards and generate them in parallel** when there are ≥2.
|
|
152
|
+
5. Generate `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation`. **Partition the work into shards and generate them in parallel** when there are ≥2. **If a scenario needs `@query` DB verification**, check the datasource `engine` first — unsupported engine → follow `sungen-gherkin-syntax` § "Unsupported DB engine — fallback" instead of authoring `@query` steps.
|
|
153
153
|
|
|
154
154
|
**5a. Decide the shards.** A shard is one **coverage unit** sized for parallelism — NOT the 5 coarse viewpoint-router groups (a screen loads only 1–2 of those). Use **either**:
|
|
155
155
|
- one **viewpoint theme** per shard — a `VP-` prefix from the viewpoint overview (`VP-SEC`, `VP-ERROR-EMPTY-STATE`, `VP-CAROUSEL`, …) — preferred when the viewpoint overview is rich (test-2/home had 47 items across many themes); **or**
|
|
@@ -192,7 +192,7 @@ If the unit is **api-first** (`qa/api/<name>/` or `qa/api/flows/<name>/`), the d
|
|
|
192
192
|
{{/cap}}
|
|
193
193
|
{{^cap parallel-subagents}}
|
|
194
194
|
4. Follow the `sungen-tc-generation` skill for section identification, viewpoint generation, and output format. **For flows**, use the "Flow Test Generation" section in the skill. When requirements exist, use the "Requirements-Driven Generation" strategy. **For Tier 1**, apply the **Lightweight Guard** — verify required fields, validation rules, business rules, security checks, and key state transitions all have TCs after generation. **For Tier 2+**, **MUST** apply the full **Mapping Contract** — walk every `spec.md` section top-to-bottom and produce the indicated TCs per Table 1; handle `test-viewpoint.md` per Table 2. Do not silently skip sections. Present sections as a numbered list and let user pick.
|
|
195
|
-
5. Generate or update `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation` skills. Generate **group-by-group** (one viewpoint group at a time, tier-by-tier `Write`/`Edit` batches) to stay under the output-token cap. **For flows**: use `[Screen:Element]` namespace format, namespace test-data by phase, add `@flow` tag.
|
|
195
|
+
5. Generate or update `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation` skills. Generate **group-by-group** (one viewpoint group at a time, tier-by-tier `Write`/`Edit` batches) to stay under the output-token cap. **For flows**: use `[Screen:Element]` namespace format, namespace test-data by phase, add `@flow` tag. **If a scenario needs `@query` DB verification**, check the datasource `engine` first — unsupported engine → follow `sungen-gherkin-syntax` § "Unsupported DB engine — fallback" instead of authoring `@query` steps.
|
|
196
196
|
> **No parallel fan-out here.** Copilot has no sub-agents, so generation is sequential (the Claude Code variant fans out one `sungen-generator` per viewpoint group and merges). Same output, no speedup.
|
|
197
197
|
5.4. **Depth self-check (deterministic — BEFORE the audit).** Run `sungen depth-lint --screen ${input:name}`. It splits every shallow business-critical scenario into **DEEPEN IN PLACE** (add a real value assertion — the printed `template` is a theme-keyed hint, apply judgment to the actual claim; never fake one onto a visibility/behavior scenario) and **CROSS-SCREEN** (route to a flow / tag `@manual:Mx` + reason — removes it from the depth denominator honestly). Act on both, re-run until `deepen` is empty (or only honest over-counts remain), THEN gate. Lifts first-pass `businessDepth` mechanically instead of via 2–3 repair rounds.
|
|
198
198
|
5.5. **Quality gate & repair (harness — always run).** Per `sungen-harness-audit`: run `sungen audit --screen ${input:name}` (structural), THEN do an **independent semantic review inline** using the `sungen-reviewer` criteria (does each scenario's steps PROVE its title/viewpoint? observable Thens? business-critical assertion depth?). Merge both sets of issues; if gate FAILs / findings exist, repair (budget 3) and re-audit — GATE missing theme → generate it (cross-screen → **automate it in the flow** via `/sungen:add-flow`, NOT a full `@manual` screen duplicate — `sungen audit` flags an automatable `@manual` as `MANUAL-AUTOMATABLE`; reserve `@manual:Mx` for true judgment/missing-capability); DEPTH → add data assertions; BALANCE → add business-core first; TRACE → align VP ids. Never fake a pass.
|
|
@@ -126,7 +126,7 @@ If the unit is **api-first**, skip every selector/capture phase (an API test has
|
|
|
126
126
|
|
|
127
127
|
## Pre-run (phased — per `sungen-selector-fix` skill)
|
|
128
128
|
|
|
129
|
-
1. Verify `<base>/<name>/` has `.feature` + `test-data.yaml`.
|
|
129
|
+
1. Verify `<base>/<name>/` has `.feature` + `test-data.yaml`. **If the feature has `@query` steps**, check the resolved datasource `engine` in `qa/datasources.yaml` — unsupported engine → follow the `sungen-gherkin-syntax` skill § "Unsupported DB engine — fallback" before compiling (do not attempt a direct connect).
|
|
130
130
|
2. **Phase 0 — Selector Pre-gen**: if `selectors.yaml` is missing/empty or doesn't cover the feature file's `[Reference]`s, apply the following decision tree before running Phase 0 from `sungen-selector-fix`:
|
|
131
131
|
|
|
132
132
|
```
|
|
@@ -118,6 +118,34 @@ Scenario: ...
|
|
|
118
118
|
|
|
119
119
|
Path access on a bound result: `{{q.count}}`/`{{q.length}}`, `{{q.first.col}}`, `{{q.last.col}}`, `{{q[2].col}}`, `{{q.col}}` (= first row's col). `expect A is B` also supports `is at least` / `is at most` / `is not`. Tier-2 declarative (trivial inline, no catalog): `User see [<table>] row where [<col>] is {{v}} [has [<col2>] = "x"]`, `… no row where …`, `… count is {{n}}`. Full grammar + catalog/datasource/secret rules → **Advanced → Database** doc. Only emit DB steps when the project has a `database/` catalog / `datasources.yaml`.
|
|
120
120
|
|
|
121
|
+
### Unsupported DB engine — fallback (ask, don't improvise)
|
|
122
|
+
|
|
123
|
+
Direct SQL verification (`@query`, the Data Driver) only supports these engines: **`{postgres, sqlite, mysql}`**. When a datasource's `engine` (or the DB the user describes) is **outside this set** — e.g. Cosmos NoSQL, MongoDB, Cassandra, DynamoDB:
|
|
124
|
+
|
|
125
|
+
1. **Do not** attempt a direct connect. **Do not** improvise a verification method.
|
|
126
|
+
2. Present a fixed `AskUserQuestion` with exactly these 3 branches (always these, no others invented):
|
|
127
|
+
- **@api** — verify state via the project's internal API (route to the API driver).
|
|
128
|
+
- **@manual** — emit a `@manual` step + manual-check guidance (e.g. Cosmos Data Explorer, `az cosmosdb`, the vendor CLI).
|
|
129
|
+
- **Proxy / read-replica SQL** — if the user can supply a SQL-compatible endpoint (including Azure Cosmos DB **for PostgreSQL** / Citus), treat it as `engine: postgres` (or `mysql`) and use the normal Data Driver path above.
|
|
130
|
+
3. **Recommend, don't decide.** Check the project for signals and put the best-fit branch **first**, tagged **"(Recommended)"** with a one-line reason:
|
|
131
|
+
- a project API driver / `qa/api/` catalog / `PEERCONNE_URL`-style internal API exists → **@api** first;
|
|
132
|
+
- else the user hints at a SQL-compatible replica/proxy → **proxy-SQL** first;
|
|
133
|
+
- else → **@manual** first.
|
|
134
|
+
The other two branches plus an **"Other"** option (user describes their own approach) stay available.
|
|
135
|
+
4. **The user always overrides** — they may pick any branch or "Other" regardless of the recommendation.
|
|
136
|
+
5. **Never auto-run** a branch and never silently pick one on the user's behalf — the question must always be asked.
|
|
137
|
+
|
|
138
|
+
Example:
|
|
139
|
+
```
|
|
140
|
+
AskUserQuestion:
|
|
141
|
+
question: "This datasource's engine (cosmos-nosql) isn't in {postgres, sqlite, mysql} — direct SQL connect isn't supported. How should DB state be verified?"
|
|
142
|
+
options:
|
|
143
|
+
- "@api — verify via the project's internal API (Recommended: qa/api/ catalog found)"
|
|
144
|
+
- "@manual — emit a @manual step + Cosmos Data Explorer / az cosmosdb check"
|
|
145
|
+
- "Proxy / read-replica SQL — supply a SQL-compatible endpoint (e.g. Cosmos DB for PostgreSQL) → engine: postgres"
|
|
146
|
+
- "Other — describe your own verification approach"
|
|
147
|
+
```
|
|
148
|
+
|
|
121
149
|
### API response assertions (optional API Driver)
|
|
122
150
|
|
|
123
151
|
An `@api:<name>` call binds the response to `{{name}}` = `{ status, ok, body, headers }` (+ `ok_count`/`status_counts` under `@concurrent`). Assert with the same `expect A is B` grammar + path access:
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `build()` always emits PG-style `$1..$n` placeholders; mysql2/better-sqlite3 both bind with `?`.
|
|
3
|
+
* Only real placeholders are rewritten — a `$1` sitting inside a single-quoted string literal (e.g.
|
|
4
|
+
* a catalog query `WHERE label = '$1 off'`) is left untouched. SQL escapes an inner quote by doubling
|
|
5
|
+
* it (`''`), which the simple in-string toggle handles (an even number of quotes returns to the same state).
|
|
6
|
+
*/
|
|
7
|
+
export declare function rewritePlaceholders(engine: string, sql: string): string;
|
|
1
8
|
declare class DataSource {
|
|
2
9
|
private configs;
|
|
3
10
|
private engines;
|
|
4
11
|
private tunnels;
|
|
12
|
+
private mysqlPools;
|
|
5
13
|
private cfg;
|
|
6
14
|
private engine;
|
|
7
|
-
/** Close
|
|
15
|
+
/** Close open MySQL pools + SSH tunnels (optional explicit teardown; tunnels are unref'd so the process exits regardless). */
|
|
8
16
|
close(): void;
|
|
9
17
|
private build;
|
|
10
18
|
/** A row matching `filter` must exist; if `expected` given, assert those columns on the first match. */
|
|
@@ -13,7 +21,7 @@ declare class DataSource {
|
|
|
13
21
|
assertNoRow(table: string, filter: Record<string, any>, datasource?: string): Promise<void>;
|
|
14
22
|
/** Exactly `count` rows must match `filter`. */
|
|
15
23
|
assertCount(table: string, filter: Record<string, any>, count: number, datasource?: string): Promise<void>;
|
|
16
|
-
/** Rewrites $1/$2 placeholders to `?` for
|
|
24
|
+
/** Rewrites $1/$2 placeholders to `?` for engines that don't support PG-style positional params. */
|
|
17
25
|
private sqlFor;
|
|
18
26
|
/** Read-only guard (second layer): a named query must be a single SELECT/WITH statement. */
|
|
19
27
|
private assertSelectOnly;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specs-db.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-db.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"specs-db.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-db.ts"],"names":[],"mappings":"AA8HA;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAiBvE;AAID,cAAM,UAAU;IACd,OAAO,CAAC,OAAO,CAAiD;IAChE,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAAoC;IACnD,OAAO,CAAC,UAAU,CAA2C;IAE7D,OAAO,CAAC,GAAG;YAQG,MAAM;IAkEpB,8HAA8H;IAC9H,KAAK,IAAI,IAAI;IAOb,OAAO,CAAC,KAAK;IAOb,wGAAwG;IAClG,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc/H,0CAA0C;IACpC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOjG,gDAAgD;IAC1C,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUhH,oGAAoG;IACpG,OAAO,CAAC,MAAM;IAKd,4FAA4F;IAC5F,OAAO,CAAC,gBAAgB;IASxB;;;;OAIG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;CAKjG;AAMD,eAAO,MAAM,EAAE,YAAmB,CAAC"}
|
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.db = void 0;
|
|
37
|
+
exports.rewritePlaceholders = rewritePlaceholders;
|
|
37
38
|
/* eslint-disable */
|
|
38
39
|
/**
|
|
39
40
|
* Sungen Data Driver — runtime DB-verification helper (auto-generated into specs/db.ts).
|
|
@@ -42,7 +43,7 @@ exports.db = void 0;
|
|
|
42
43
|
* table/column identifiers are validated against a strict allowlist pattern (identifiers
|
|
43
44
|
* can't be bound as parameters). Secrets come from .env.qa / process.env, never inline.
|
|
44
45
|
*
|
|
45
|
-
* Engines: PostgreSQL (`pg`) and SQLite (`better-sqlite3`), lazy-loaded on first use.
|
|
46
|
+
* Engines: PostgreSQL (`pg`), MySQL (`mysql2`), and SQLite (`better-sqlite3`), lazy-loaded on first use.
|
|
46
47
|
* Config: a `datasources.yaml` at the project root (or qa/), with ${VAR} resolved from env.
|
|
47
48
|
*
|
|
48
49
|
* DO NOT EDIT — regenerated by `sungen generate`.
|
|
@@ -56,6 +57,9 @@ const ident = (s) => {
|
|
|
56
57
|
throw new Error(`Unsafe identifier: ${JSON.stringify(s)} (allowed: [A-Za-z_][A-Za-z0-9_]*)`);
|
|
57
58
|
return s;
|
|
58
59
|
};
|
|
60
|
+
// Single source of truth for the "not supported" error message below and for the mysql/sqlite
|
|
61
|
+
// placeholder rewrite condition (`rewritePlaceholders`) — keep engine names in sync with `DataSourceConfig.engine`.
|
|
62
|
+
const SUPPORTED_ENGINES = ['postgres', 'sqlite', 'mysql'];
|
|
59
63
|
/**
|
|
60
64
|
* Open a local TCP forward (127.0.0.1:<ephemeral> → ssh bastion → dstHost:dstPort) for a DB socket.
|
|
61
65
|
* Sockets are unref()'d so a dangling tunnel never keeps the test process alive after the run.
|
|
@@ -144,11 +148,42 @@ function loadConfig() {
|
|
|
144
148
|
}
|
|
145
149
|
return doc.datasources;
|
|
146
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* `build()` always emits PG-style `$1..$n` placeholders; mysql2/better-sqlite3 both bind with `?`.
|
|
153
|
+
* Only real placeholders are rewritten — a `$1` sitting inside a single-quoted string literal (e.g.
|
|
154
|
+
* a catalog query `WHERE label = '$1 off'`) is left untouched. SQL escapes an inner quote by doubling
|
|
155
|
+
* it (`''`), which the simple in-string toggle handles (an even number of quotes returns to the same state).
|
|
156
|
+
*/
|
|
157
|
+
function rewritePlaceholders(engine, sql) {
|
|
158
|
+
if (engine !== 'sqlite' && engine !== 'mysql')
|
|
159
|
+
return sql;
|
|
160
|
+
let out = '';
|
|
161
|
+
let inStr = false;
|
|
162
|
+
for (let i = 0; i < sql.length; i++) {
|
|
163
|
+
const c = sql[i];
|
|
164
|
+
if (c === "'") {
|
|
165
|
+
inStr = !inStr;
|
|
166
|
+
out += c;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (!inStr && c === '$' && sql[i + 1] >= '0' && sql[i + 1] <= '9') {
|
|
170
|
+
let j = i + 1;
|
|
171
|
+
while (j < sql.length && sql[j] >= '0' && sql[j] <= '9')
|
|
172
|
+
j++;
|
|
173
|
+
out += '?';
|
|
174
|
+
i = j - 1;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
out += c;
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
147
181
|
class DataSource {
|
|
148
182
|
constructor() {
|
|
149
183
|
this.configs = null;
|
|
150
184
|
this.engines = new Map();
|
|
151
185
|
this.tunnels = [];
|
|
186
|
+
this.mysqlPools = [];
|
|
152
187
|
}
|
|
153
188
|
cfg(name) {
|
|
154
189
|
if (!this.configs)
|
|
@@ -168,7 +203,7 @@ class DataSource {
|
|
|
168
203
|
let engine;
|
|
169
204
|
if (conf.engine === 'postgres') {
|
|
170
205
|
let connectionString = conf.url;
|
|
171
|
-
if (conf.ssh) { //
|
|
206
|
+
if (conf.ssh) { // Fallback path: tunnel the DB socket through a bastion
|
|
172
207
|
const u = new URL(conf.url);
|
|
173
208
|
const t = await openSshTunnel(conf.ssh, u.hostname, Number(u.port || 5432));
|
|
174
209
|
this.tunnels.push(t);
|
|
@@ -180,6 +215,56 @@ class DataSource {
|
|
|
180
215
|
const pool = new Pool({ connectionString, max: 2, statement_timeout: conf.statement_timeout_ms ?? 4000 });
|
|
181
216
|
engine = { query: async (sql, params) => (await pool.query(sql, params)).rows };
|
|
182
217
|
}
|
|
218
|
+
else if (conf.engine === 'mysql') {
|
|
219
|
+
let connectionString = conf.url;
|
|
220
|
+
if (conf.ssh) { // Fallback path: tunnel the DB socket through a bastion
|
|
221
|
+
const u = new URL(conf.url);
|
|
222
|
+
const t = await openSshTunnel(conf.ssh, u.hostname, Number(u.port || 3306));
|
|
223
|
+
this.tunnels.push(t);
|
|
224
|
+
u.hostname = t.host;
|
|
225
|
+
u.port = String(t.port); // rewrite host:port → 127.0.0.1:<tunnel> (keep user/pass/db/query)
|
|
226
|
+
connectionString = u.toString();
|
|
227
|
+
}
|
|
228
|
+
const mysql = require('mysql2/promise');
|
|
229
|
+
const pool = mysql.createPool({ uri: connectionString, connectionLimit: 2 });
|
|
230
|
+
this.mysqlPools.push(pool);
|
|
231
|
+
// mysql2 ignores the mysqljs per-query `timeout` field, and destroying the connection does
|
|
232
|
+
// not interrupt an in-flight query — so bound the wait client-side by racing the query
|
|
233
|
+
// against a timer. On overrun we reject and destroy the connection (removes it from the pool
|
|
234
|
+
// rather than releasing a still-busy socket); on success we release it back.
|
|
235
|
+
const timeoutMs = conf.statement_timeout_ms ?? 4000;
|
|
236
|
+
engine = {
|
|
237
|
+
query: async (sql, params) => {
|
|
238
|
+
const conn = await pool.getConnection();
|
|
239
|
+
const qp = conn.query(sql, params);
|
|
240
|
+
qp.catch(() => { });
|
|
241
|
+
let timer;
|
|
242
|
+
let timedOut = false;
|
|
243
|
+
const guard = new Promise((_, reject) => {
|
|
244
|
+
timer = setTimeout(() => { timedOut = true; reject(new Error(`Data Driver: mysql query exceeded ${timeoutMs}ms — aborted.`)); }, timeoutMs);
|
|
245
|
+
});
|
|
246
|
+
try {
|
|
247
|
+
const [rows] = await Promise.race([qp, guard]);
|
|
248
|
+
return rows;
|
|
249
|
+
}
|
|
250
|
+
finally {
|
|
251
|
+
clearTimeout(timer);
|
|
252
|
+
if (timedOut) {
|
|
253
|
+
try {
|
|
254
|
+
conn.destroy();
|
|
255
|
+
}
|
|
256
|
+
catch { /* best-effort */ }
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
try {
|
|
260
|
+
conn.release();
|
|
261
|
+
}
|
|
262
|
+
catch { /* already gone */ }
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
}
|
|
183
268
|
else if (conf.engine === 'sqlite') {
|
|
184
269
|
if (conf.ssh)
|
|
185
270
|
console.warn(`Data Driver: datasource "${key}" sets ssh: but engine is sqlite (file-based) — ssh ignored.`);
|
|
@@ -188,13 +273,20 @@ class DataSource {
|
|
|
188
273
|
engine = { query: async (sql, params) => db.prepare(sql).all(...params) };
|
|
189
274
|
}
|
|
190
275
|
else {
|
|
191
|
-
throw new Error(`Data Driver: engine "${conf.engine}" not supported
|
|
276
|
+
throw new Error(`Data Driver: engine "${conf.engine}" not supported (supported: ${SUPPORTED_ENGINES.join(' | ')}). For non-SQL stores (Cosmos NoSQL, Mongo…) verify via @api or @manual.`);
|
|
192
277
|
}
|
|
193
278
|
this.engines.set(key, engine);
|
|
194
279
|
return { engine, conf };
|
|
195
280
|
}
|
|
196
|
-
/** Close
|
|
281
|
+
/** Close open MySQL pools + SSH tunnels (optional explicit teardown; tunnels are unref'd so the process exits regardless). */
|
|
197
282
|
close() {
|
|
283
|
+
for (const p of this.mysqlPools) {
|
|
284
|
+
try {
|
|
285
|
+
void p.end();
|
|
286
|
+
}
|
|
287
|
+
catch { /* best-effort */ }
|
|
288
|
+
}
|
|
289
|
+
this.mysqlPools = [];
|
|
198
290
|
for (const t of this.tunnels)
|
|
199
291
|
t.close();
|
|
200
292
|
this.tunnels = [];
|
|
@@ -236,9 +328,9 @@ class DataSource {
|
|
|
236
328
|
const n = Number(rows[0]?.n ?? rows[0]?.['count(*)'] ?? 0);
|
|
237
329
|
(0, test_1.expect)(n, `Expected ${count} row(s) in "${table}"${cols.length ? ' where ' + desc(filter) : ''} — found ${n}`).toBe(Number(count));
|
|
238
330
|
}
|
|
239
|
-
/** Rewrites $1/$2 placeholders to `?` for
|
|
331
|
+
/** Rewrites $1/$2 placeholders to `?` for engines that don't support PG-style positional params. */
|
|
240
332
|
sqlFor(conf, sql) {
|
|
241
|
-
return conf.engine
|
|
333
|
+
return rewritePlaceholders(conf.engine, sql);
|
|
242
334
|
}
|
|
243
335
|
// --- Named queries (catalog-backed; SQL is resolved + embedded at compile time) -----------
|
|
244
336
|
/** Read-only guard (second layer): a named query must be a single SELECT/WITH statement. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specs-db.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-db.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"specs-db.js","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-db.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* table/column identifiers are validated against a strict allowlist pattern (identifiers
|
|
7
7
|
* can't be bound as parameters). Secrets come from .env.qa / process.env, never inline.
|
|
8
8
|
*
|
|
9
|
-
* Engines: PostgreSQL (`pg`) and SQLite (`better-sqlite3`), lazy-loaded on first use.
|
|
9
|
+
* Engines: PostgreSQL (`pg`), MySQL (`mysql2`), and SQLite (`better-sqlite3`), lazy-loaded on first use.
|
|
10
10
|
* Config: a `datasources.yaml` at the project root (or qa/), with ${VAR} resolved from env.
|
|
11
11
|
*
|
|
12
12
|
* DO NOT EDIT — regenerated by `sungen generate`.
|
|
@@ -21,6 +21,10 @@ const ident = (s: string): string => {
|
|
|
21
21
|
return s;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
// Single source of truth for the "not supported" error message below and for the mysql/sqlite
|
|
25
|
+
// placeholder rewrite condition (`rewritePlaceholders`) — keep engine names in sync with `DataSourceConfig.engine`.
|
|
26
|
+
const SUPPORTED_ENGINES = ['postgres', 'sqlite', 'mysql'] as const;
|
|
27
|
+
|
|
24
28
|
interface SshConfig {
|
|
25
29
|
host: string; // jump host reachable from the runner
|
|
26
30
|
port?: number; // default 22
|
|
@@ -37,8 +41,8 @@ interface DataSourceConfig {
|
|
|
37
41
|
readonly?: boolean;
|
|
38
42
|
statement_timeout_ms?: number;
|
|
39
43
|
max_rows?: number;
|
|
40
|
-
//
|
|
41
|
-
// still run on the runner; only
|
|
44
|
+
// Fallback path: tunnel the DB SOCKET through an SSH bastion. DB-only — the browser/E2E
|
|
45
|
+
// still run on the runner; only DB traffic crosses. See docs/spec/sungen_data_driver_ssh_tunnel_spec.md.
|
|
42
46
|
ssh?: SshConfig;
|
|
43
47
|
}
|
|
44
48
|
|
|
@@ -120,12 +124,38 @@ function loadConfig(): Record<string, DataSourceConfig> {
|
|
|
120
124
|
return doc.datasources;
|
|
121
125
|
}
|
|
122
126
|
|
|
127
|
+
/**
|
|
128
|
+
* `build()` always emits PG-style `$1..$n` placeholders; mysql2/better-sqlite3 both bind with `?`.
|
|
129
|
+
* Only real placeholders are rewritten — a `$1` sitting inside a single-quoted string literal (e.g.
|
|
130
|
+
* a catalog query `WHERE label = '$1 off'`) is left untouched. SQL escapes an inner quote by doubling
|
|
131
|
+
* it (`''`), which the simple in-string toggle handles (an even number of quotes returns to the same state).
|
|
132
|
+
*/
|
|
133
|
+
export function rewritePlaceholders(engine: string, sql: string): string {
|
|
134
|
+
if (engine !== 'sqlite' && engine !== 'mysql') return sql;
|
|
135
|
+
let out = '';
|
|
136
|
+
let inStr = false;
|
|
137
|
+
for (let i = 0; i < sql.length; i++) {
|
|
138
|
+
const c = sql[i];
|
|
139
|
+
if (c === "'") { inStr = !inStr; out += c; continue; }
|
|
140
|
+
if (!inStr && c === '$' && sql[i + 1] >= '0' && sql[i + 1] <= '9') {
|
|
141
|
+
let j = i + 1;
|
|
142
|
+
while (j < sql.length && sql[j] >= '0' && sql[j] <= '9') j++;
|
|
143
|
+
out += '?';
|
|
144
|
+
i = j - 1;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
out += c;
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
|
|
123
152
|
type Engine = { query(sql: string, params: any[]): Promise<any[]>; };
|
|
124
153
|
|
|
125
154
|
class DataSource {
|
|
126
155
|
private configs: Record<string, DataSourceConfig> | null = null;
|
|
127
156
|
private engines = new Map<string, Engine>();
|
|
128
157
|
private tunnels: Array<{ close: () => void }> = [];
|
|
158
|
+
private mysqlPools: Array<{ end: () => Promise<void> }> = [];
|
|
129
159
|
|
|
130
160
|
private cfg(name?: string): { key: string; conf: DataSourceConfig } {
|
|
131
161
|
if (!this.configs) this.configs = loadConfig();
|
|
@@ -142,7 +172,7 @@ class DataSource {
|
|
|
142
172
|
let engine: Engine;
|
|
143
173
|
if (conf.engine === 'postgres') {
|
|
144
174
|
let connectionString = conf.url;
|
|
145
|
-
if (conf.ssh) { //
|
|
175
|
+
if (conf.ssh) { // Fallback path: tunnel the DB socket through a bastion
|
|
146
176
|
const u = new URL(conf.url);
|
|
147
177
|
const t = await openSshTunnel(conf.ssh, u.hostname, Number(u.port || 5432));
|
|
148
178
|
this.tunnels.push(t);
|
|
@@ -152,20 +182,59 @@ class DataSource {
|
|
|
152
182
|
const { Pool } = require('pg');
|
|
153
183
|
const pool = new Pool({ connectionString, max: 2, statement_timeout: conf.statement_timeout_ms ?? 4000 });
|
|
154
184
|
engine = { query: async (sql, params) => (await pool.query(sql, params)).rows };
|
|
185
|
+
} else if (conf.engine === 'mysql') {
|
|
186
|
+
let connectionString = conf.url;
|
|
187
|
+
if (conf.ssh) { // Fallback path: tunnel the DB socket through a bastion
|
|
188
|
+
const u = new URL(conf.url);
|
|
189
|
+
const t = await openSshTunnel(conf.ssh, u.hostname, Number(u.port || 3306));
|
|
190
|
+
this.tunnels.push(t);
|
|
191
|
+
u.hostname = t.host; u.port = String(t.port); // rewrite host:port → 127.0.0.1:<tunnel> (keep user/pass/db/query)
|
|
192
|
+
connectionString = u.toString();
|
|
193
|
+
}
|
|
194
|
+
const mysql = require('mysql2/promise');
|
|
195
|
+
const pool = mysql.createPool({ uri: connectionString, connectionLimit: 2 });
|
|
196
|
+
this.mysqlPools.push(pool);
|
|
197
|
+
// mysql2 ignores the mysqljs per-query `timeout` field, and destroying the connection does
|
|
198
|
+
// not interrupt an in-flight query — so bound the wait client-side by racing the query
|
|
199
|
+
// against a timer. On overrun we reject and destroy the connection (removes it from the pool
|
|
200
|
+
// rather than releasing a still-busy socket); on success we release it back.
|
|
201
|
+
const timeoutMs = conf.statement_timeout_ms ?? 4000;
|
|
202
|
+
engine = {
|
|
203
|
+
query: async (sql, params) => {
|
|
204
|
+
const conn = await pool.getConnection();
|
|
205
|
+
const qp = conn.query(sql, params);
|
|
206
|
+
qp.catch(() => { /* swallow the post-timeout rejection from the abandoned query */ });
|
|
207
|
+
let timer: NodeJS.Timeout | undefined;
|
|
208
|
+
let timedOut = false;
|
|
209
|
+
const guard = new Promise<never>((_, reject) => {
|
|
210
|
+
timer = setTimeout(() => { timedOut = true; reject(new Error(`Data Driver: mysql query exceeded ${timeoutMs}ms — aborted.`)); }, timeoutMs);
|
|
211
|
+
});
|
|
212
|
+
try {
|
|
213
|
+
const [rows] = await Promise.race([qp, guard]);
|
|
214
|
+
return rows as any[];
|
|
215
|
+
} finally {
|
|
216
|
+
clearTimeout(timer);
|
|
217
|
+
if (timedOut) { try { conn.destroy(); } catch { /* best-effort */ } }
|
|
218
|
+
else { try { conn.release(); } catch { /* already gone */ } }
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
};
|
|
155
222
|
} else if (conf.engine === 'sqlite') {
|
|
156
223
|
if (conf.ssh) console.warn(`Data Driver: datasource "${key}" sets ssh: but engine is sqlite (file-based) — ssh ignored.`);
|
|
157
224
|
const Database = require('better-sqlite3');
|
|
158
225
|
const db = new Database(conf.url.replace(/^sqlite:/, ''), { readonly: conf.readonly !== false });
|
|
159
226
|
engine = { query: async (sql, params) => db.prepare(sql).all(...params) };
|
|
160
227
|
} else {
|
|
161
|
-
throw new Error(`Data Driver: engine "${conf.engine}" not supported
|
|
228
|
+
throw new Error(`Data Driver: engine "${conf.engine}" not supported (supported: ${SUPPORTED_ENGINES.join(' | ')}). For non-SQL stores (Cosmos NoSQL, Mongo…) verify via @api or @manual.`);
|
|
162
229
|
}
|
|
163
230
|
this.engines.set(key, engine);
|
|
164
231
|
return { engine, conf };
|
|
165
232
|
}
|
|
166
233
|
|
|
167
|
-
/** Close
|
|
234
|
+
/** Close open MySQL pools + SSH tunnels (optional explicit teardown; tunnels are unref'd so the process exits regardless). */
|
|
168
235
|
close(): void {
|
|
236
|
+
for (const p of this.mysqlPools) { try { void p.end(); } catch { /* best-effort */ } }
|
|
237
|
+
this.mysqlPools = [];
|
|
169
238
|
for (const t of this.tunnels) t.close();
|
|
170
239
|
this.tunnels = [];
|
|
171
240
|
}
|
|
@@ -211,9 +280,9 @@ class DataSource {
|
|
|
211
280
|
expect(n, `Expected ${count} row(s) in "${table}"${cols.length ? ' where ' + desc(filter) : ''} — found ${n}`).toBe(Number(count));
|
|
212
281
|
}
|
|
213
282
|
|
|
214
|
-
/** Rewrites $1/$2 placeholders to `?` for
|
|
283
|
+
/** Rewrites $1/$2 placeholders to `?` for engines that don't support PG-style positional params. */
|
|
215
284
|
private sqlFor(conf: DataSourceConfig, sql: string): string {
|
|
216
|
-
return conf.engine
|
|
285
|
+
return rewritePlaceholders(conf.engine, sql);
|
|
217
286
|
}
|
|
218
287
|
|
|
219
288
|
// --- Named queries (catalog-backed; SQL is resolved + embedded at compile time) -----------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sun-asterisk/sungen",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.9",
|
|
4
4
|
"description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"copy-templates": "mkdir -p dist/generators/test-generator/adapters/playwright/templates/steps && mkdir -p dist/generators/test-generator/templates && mkdir -p dist/orchestrator/templates && mkdir -p dist/dashboard/templates && cp -r src/generators/test-generator/adapters/playwright/templates/*.hbs dist/generators/test-generator/adapters/playwright/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/playwright/templates/steps dist/generators/test-generator/adapters/playwright/templates/ && mkdir -p dist/generators/test-generator/adapters/appium/templates/steps && cp -r src/generators/test-generator/adapters/appium/templates/*.hbs dist/generators/test-generator/adapters/appium/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/appium/templates/steps dist/generators/test-generator/adapters/appium/templates/ && cp src/generators/test-generator/templates/*.hbs dist/generators/test-generator/templates/ 2>/dev/null || true && cp -r src/orchestrator/templates/* dist/orchestrator/templates/ && cp src/dashboard/templates/index.html dist/dashboard/templates/index.html && mkdir -p dist/harness/catalog && cp src/harness/catalog/*.yaml dist/harness/catalog/",
|
|
13
13
|
"build:dashboard": "cd ../../dashboard && npm install --silent && npm run build && cd - && cp ../../dashboard/dist/index.html src/dashboard/templates/index.html",
|
|
14
14
|
"dev": "tsx src/cli/index.ts",
|
|
15
|
-
"test": "tsx tests/golden/run.ts && tsx tests/audit/run.ts && tsx tests/ingest/run.ts && tsx tests/eval/run.ts && tsx tests/exporter/run.ts && tsx tests/exporter/feature-parser-category.run.ts && tsx tests/exporter/api-detail-sheet.run.ts && tsx tests/exporter/overview-sheet.run.ts && tsx tests/exporter/delivery-divider-tcid.run.ts && tsx tests/exporter/note-expected-actual.run.ts && tsx tests/exporter/test-data-vars.run.ts && tsx tests/exporter/mobile-app-id.run.ts && tsx tests/exporter/api-testcase-cells.run.ts && tsx tests/exporter/delivery-cases-result.run.ts && tsx tests/exporter/precondition-auth-role.run.ts && tsx tests/exporter/report-builder-parity.run.ts && tsx tests/exporter/delivery-preflight-testdata.run.ts && tsx tests/dashboard/api-flows-discovery.run.ts && tsx tests/dashboard/status-results-fallback.run.ts && tsx tests/codegen/verb-ref-masking.run.ts && tsx tests/codegen/table-aria-hidden-locators.run.ts && tsx tests/api-runtime/base-path-url-join.run.ts && tsx tests/api-runtime/file-upload-multipart.run.ts && tsx tests/api-runtime/schema-assertion.run.ts && tsx tests/runtime/test-data-cross-ref.run.ts && tsx tests/capabilities/run.ts && tsx tests/openapi/run.ts && tsx tests/api-field-coverage/run.ts && tsx tests/packaging/run.ts && tsx tests/generate-hint/run.ts && tsx tests/template-assertion/run.ts && tsx tests/journey/run.ts && tsx tests/harness/serial-cascade.run.ts && tsx tests/ai-skills/cross-assistant-orphan.run.ts && tsx tests/init/codex-mcp-config.run.ts && tsx src/orchestrator/ai-skills/golden-skills.test.ts",
|
|
15
|
+
"test": "tsx tests/golden/run.ts && tsx tests/audit/run.ts && tsx tests/ingest/run.ts && tsx tests/eval/run.ts && tsx tests/exporter/run.ts && tsx tests/exporter/feature-parser-category.run.ts && tsx tests/exporter/api-detail-sheet.run.ts && tsx tests/exporter/overview-sheet.run.ts && tsx tests/exporter/delivery-divider-tcid.run.ts && tsx tests/exporter/note-expected-actual.run.ts && tsx tests/exporter/test-data-vars.run.ts && tsx tests/exporter/mobile-app-id.run.ts && tsx tests/exporter/api-testcase-cells.run.ts && tsx tests/exporter/delivery-cases-result.run.ts && tsx tests/exporter/precondition-auth-role.run.ts && tsx tests/exporter/report-builder-parity.run.ts && tsx tests/exporter/delivery-preflight-testdata.run.ts && tsx tests/dashboard/api-flows-discovery.run.ts && tsx tests/dashboard/status-results-fallback.run.ts && tsx tests/codegen/verb-ref-masking.run.ts && tsx tests/codegen/table-aria-hidden-locators.run.ts && tsx tests/api-runtime/base-path-url-join.run.ts && tsx tests/api-runtime/file-upload-multipart.run.ts && tsx tests/api-runtime/schema-assertion.run.ts && tsx tests/runtime/test-data-cross-ref.run.ts && tsx tests/capabilities/run.ts && tsx tests/openapi/run.ts && tsx tests/api-field-coverage/run.ts && tsx tests/packaging/run.ts && tsx tests/generate-hint/run.ts && tsx tests/template-assertion/run.ts && tsx tests/journey/run.ts && tsx tests/harness/serial-cascade.run.ts && tsx tests/ai-skills/cross-assistant-orphan.run.ts && tsx tests/init/codex-mcp-config.run.ts && tsx src/orchestrator/ai-skills/golden-skills.test.ts && tsx tests/db-runtime/sql-placeholder-rewrite.run.ts && tsx tests/db-runtime/fallback-flow-codified.run.ts && tsx tests/db-runtime/mysql-integration.opt-in.run.ts",
|
|
16
16
|
"test:update": "tsx tests/golden/run.ts --update && tsx tests/audit/run.ts --update && tsx tests/ingest/run.ts --update",
|
|
17
17
|
"prepublishOnly": "npm run build:dashboard && npm run build"
|
|
18
18
|
},
|
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
"node": ">=18.0.0"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@sungen/driver-ui": "3.2.8",
|
|
37
36
|
"@anthropic-ai/sdk": "^0.71.0",
|
|
38
37
|
"@babel/parser": "^7.28.5",
|
|
39
38
|
"@babel/traverse": "^7.28.5",
|
|
40
39
|
"@babel/types": "^7.28.5",
|
|
41
40
|
"@cucumber/gherkin": "^37.0.0",
|
|
42
41
|
"@cucumber/messages": "^31.0.0",
|
|
42
|
+
"@sungen/driver-ui": "3.2.9",
|
|
43
43
|
"chalk": "^5.6.2",
|
|
44
44
|
"commander": "^14.0.2",
|
|
45
45
|
"dotenv": "^17.2.3",
|
|
@@ -63,5 +63,8 @@
|
|
|
63
63
|
"src",
|
|
64
64
|
"README.md",
|
|
65
65
|
"LICENSE"
|
|
66
|
-
]
|
|
66
|
+
],
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"mysql2": "^3"
|
|
69
|
+
}
|
|
67
70
|
}
|
|
@@ -149,7 +149,7 @@ If the unit is **api-first** (`qa/api/<name>/` or `qa/api/flows/<name>/`), the d
|
|
|
149
149
|
|
|
150
150
|
{{#cap parallel-subagents}}
|
|
151
151
|
4. Follow the `sungen-tc-generation` skill for section identification, viewpoint generation, and output format. **Viewpoint loading discipline:** `sungen-viewpoint` is a **router** — from the page-type (form / list / detail / auth / dashboard …) read **only the matching group file(s)** (e.g. a login screen → group-e-identity; a product list → group-c-data-explore), never all five groups. This keeps the generation context lean. **For flows**, use the "Flow Test Generation" section in the skill. When requirements exist, use the "Requirements-Driven Generation" strategy. **For Tier 1**, apply the **Lightweight Guard** — verify required fields, validation rules, business rules, security checks, and key state transitions all have TCs after generation. **For Tier 2+**, **MUST** apply the full **Mapping Contract** — walk every `spec.md` section top-to-bottom and produce the indicated TCs per Table 1; handle `test-viewpoint.md` per Table 2. Do not silently skip sections.
|
|
152
|
-
5. Generate `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation`. **Partition the work into shards and generate them in parallel** when there are ≥2.
|
|
152
|
+
5. Generate `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation`. **Partition the work into shards and generate them in parallel** when there are ≥2. **If a scenario needs `@query` DB verification**, check the datasource `engine` first — unsupported engine → follow `sungen-gherkin-syntax` § "Unsupported DB engine — fallback" instead of authoring `@query` steps.
|
|
153
153
|
|
|
154
154
|
**5a. Decide the shards.** A shard is one **coverage unit** sized for parallelism — NOT the 5 coarse viewpoint-router groups (a screen loads only 1–2 of those). Use **either**:
|
|
155
155
|
- one **viewpoint theme** per shard — a `VP-` prefix from the viewpoint overview (`VP-SEC`, `VP-ERROR-EMPTY-STATE`, `VP-CAROUSEL`, …) — preferred when the viewpoint overview is rich (test-2/home had 47 items across many themes); **or**
|
|
@@ -192,7 +192,7 @@ If the unit is **api-first** (`qa/api/<name>/` or `qa/api/flows/<name>/`), the d
|
|
|
192
192
|
{{/cap}}
|
|
193
193
|
{{^cap parallel-subagents}}
|
|
194
194
|
4. Follow the `sungen-tc-generation` skill for section identification, viewpoint generation, and output format. **For flows**, use the "Flow Test Generation" section in the skill. When requirements exist, use the "Requirements-Driven Generation" strategy. **For Tier 1**, apply the **Lightweight Guard** — verify required fields, validation rules, business rules, security checks, and key state transitions all have TCs after generation. **For Tier 2+**, **MUST** apply the full **Mapping Contract** — walk every `spec.md` section top-to-bottom and produce the indicated TCs per Table 1; handle `test-viewpoint.md` per Table 2. Do not silently skip sections. Present sections as a numbered list and let user pick.
|
|
195
|
-
5. Generate or update `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation` skills. Generate **group-by-group** (one viewpoint group at a time, tier-by-tier `Write`/`Edit` batches) to stay under the output-token cap. **For flows**: use `[Screen:Element]` namespace format, namespace test-data by phase, add `@flow` tag.
|
|
195
|
+
5. Generate or update `.feature` + `test-data.yaml` following `sungen-gherkin-syntax` and `sungen-tc-generation` skills. Generate **group-by-group** (one viewpoint group at a time, tier-by-tier `Write`/`Edit` batches) to stay under the output-token cap. **For flows**: use `[Screen:Element]` namespace format, namespace test-data by phase, add `@flow` tag. **If a scenario needs `@query` DB verification**, check the datasource `engine` first — unsupported engine → follow `sungen-gherkin-syntax` § "Unsupported DB engine — fallback" instead of authoring `@query` steps.
|
|
196
196
|
> **No parallel fan-out here.** Copilot has no sub-agents, so generation is sequential (the Claude Code variant fans out one `sungen-generator` per viewpoint group and merges). Same output, no speedup.
|
|
197
197
|
5.4. **Depth self-check (deterministic — BEFORE the audit).** Run `sungen depth-lint --screen ${input:name}`. It splits every shallow business-critical scenario into **DEEPEN IN PLACE** (add a real value assertion — the printed `template` is a theme-keyed hint, apply judgment to the actual claim; never fake one onto a visibility/behavior scenario) and **CROSS-SCREEN** (route to a flow / tag `@manual:Mx` + reason — removes it from the depth denominator honestly). Act on both, re-run until `deepen` is empty (or only honest over-counts remain), THEN gate. Lifts first-pass `businessDepth` mechanically instead of via 2–3 repair rounds.
|
|
198
198
|
5.5. **Quality gate & repair (harness — always run).** Per `sungen-harness-audit`: run `sungen audit --screen ${input:name}` (structural), THEN do an **independent semantic review inline** using the `sungen-reviewer` criteria (does each scenario's steps PROVE its title/viewpoint? observable Thens? business-critical assertion depth?). Merge both sets of issues; if gate FAILs / findings exist, repair (budget 3) and re-audit — GATE missing theme → generate it (cross-screen → **automate it in the flow** via `/sungen:add-flow`, NOT a full `@manual` screen duplicate — `sungen audit` flags an automatable `@manual` as `MANUAL-AUTOMATABLE`; reserve `@manual:Mx` for true judgment/missing-capability); DEPTH → add data assertions; BALANCE → add business-core first; TRACE → align VP ids. Never fake a pass.
|
|
@@ -126,7 +126,7 @@ If the unit is **api-first**, skip every selector/capture phase (an API test has
|
|
|
126
126
|
|
|
127
127
|
## Pre-run (phased — per `sungen-selector-fix` skill)
|
|
128
128
|
|
|
129
|
-
1. Verify `<base>/<name>/` has `.feature` + `test-data.yaml`.
|
|
129
|
+
1. Verify `<base>/<name>/` has `.feature` + `test-data.yaml`. **If the feature has `@query` steps**, check the resolved datasource `engine` in `qa/datasources.yaml` — unsupported engine → follow the `sungen-gherkin-syntax` skill § "Unsupported DB engine — fallback" before compiling (do not attempt a direct connect).
|
|
130
130
|
2. **Phase 0 — Selector Pre-gen**: if `selectors.yaml` is missing/empty or doesn't cover the feature file's `[Reference]`s, apply the following decision tree before running Phase 0 from `sungen-selector-fix`:
|
|
131
131
|
|
|
132
132
|
```
|
|
@@ -118,6 +118,34 @@ Scenario: ...
|
|
|
118
118
|
|
|
119
119
|
Path access on a bound result: `{{q.count}}`/`{{q.length}}`, `{{q.first.col}}`, `{{q.last.col}}`, `{{q[2].col}}`, `{{q.col}}` (= first row's col). `expect A is B` also supports `is at least` / `is at most` / `is not`. Tier-2 declarative (trivial inline, no catalog): `User see [<table>] row where [<col>] is {{v}} [has [<col2>] = "x"]`, `… no row where …`, `… count is {{n}}`. Full grammar + catalog/datasource/secret rules → **Advanced → Database** doc. Only emit DB steps when the project has a `database/` catalog / `datasources.yaml`.
|
|
120
120
|
|
|
121
|
+
### Unsupported DB engine — fallback (ask, don't improvise)
|
|
122
|
+
|
|
123
|
+
Direct SQL verification (`@query`, the Data Driver) only supports these engines: **`{postgres, sqlite, mysql}`**. When a datasource's `engine` (or the DB the user describes) is **outside this set** — e.g. Cosmos NoSQL, MongoDB, Cassandra, DynamoDB:
|
|
124
|
+
|
|
125
|
+
1. **Do not** attempt a direct connect. **Do not** improvise a verification method.
|
|
126
|
+
2. Present a fixed `AskUserQuestion` with exactly these 3 branches (always these, no others invented):
|
|
127
|
+
- **@api** — verify state via the project's internal API (route to the API driver).
|
|
128
|
+
- **@manual** — emit a `@manual` step + manual-check guidance (e.g. Cosmos Data Explorer, `az cosmosdb`, the vendor CLI).
|
|
129
|
+
- **Proxy / read-replica SQL** — if the user can supply a SQL-compatible endpoint (including Azure Cosmos DB **for PostgreSQL** / Citus), treat it as `engine: postgres` (or `mysql`) and use the normal Data Driver path above.
|
|
130
|
+
3. **Recommend, don't decide.** Check the project for signals and put the best-fit branch **first**, tagged **"(Recommended)"** with a one-line reason:
|
|
131
|
+
- a project API driver / `qa/api/` catalog / `PEERCONNE_URL`-style internal API exists → **@api** first;
|
|
132
|
+
- else the user hints at a SQL-compatible replica/proxy → **proxy-SQL** first;
|
|
133
|
+
- else → **@manual** first.
|
|
134
|
+
The other two branches plus an **"Other"** option (user describes their own approach) stay available.
|
|
135
|
+
4. **The user always overrides** — they may pick any branch or "Other" regardless of the recommendation.
|
|
136
|
+
5. **Never auto-run** a branch and never silently pick one on the user's behalf — the question must always be asked.
|
|
137
|
+
|
|
138
|
+
Example:
|
|
139
|
+
```
|
|
140
|
+
AskUserQuestion:
|
|
141
|
+
question: "This datasource's engine (cosmos-nosql) isn't in {postgres, sqlite, mysql} — direct SQL connect isn't supported. How should DB state be verified?"
|
|
142
|
+
options:
|
|
143
|
+
- "@api — verify via the project's internal API (Recommended: qa/api/ catalog found)"
|
|
144
|
+
- "@manual — emit a @manual step + Cosmos Data Explorer / az cosmosdb check"
|
|
145
|
+
- "Proxy / read-replica SQL — supply a SQL-compatible endpoint (e.g. Cosmos DB for PostgreSQL) → engine: postgres"
|
|
146
|
+
- "Other — describe your own verification approach"
|
|
147
|
+
```
|
|
148
|
+
|
|
121
149
|
### API response assertions (optional API Driver)
|
|
122
150
|
|
|
123
151
|
An `@api:<name>` call binds the response to `{{name}}` = `{ status, ok, body, headers }` (+ `ok_count`/`status_counts` under `@concurrent`). Assert with the same `expect A is B` grammar + path access:
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* table/column identifiers are validated against a strict allowlist pattern (identifiers
|
|
7
7
|
* can't be bound as parameters). Secrets come from .env.qa / process.env, never inline.
|
|
8
8
|
*
|
|
9
|
-
* Engines: PostgreSQL (`pg`) and SQLite (`better-sqlite3`), lazy-loaded on first use.
|
|
9
|
+
* Engines: PostgreSQL (`pg`), MySQL (`mysql2`), and SQLite (`better-sqlite3`), lazy-loaded on first use.
|
|
10
10
|
* Config: a `datasources.yaml` at the project root (or qa/), with ${VAR} resolved from env.
|
|
11
11
|
*
|
|
12
12
|
* DO NOT EDIT — regenerated by `sungen generate`.
|
|
@@ -21,6 +21,10 @@ const ident = (s: string): string => {
|
|
|
21
21
|
return s;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
// Single source of truth for the "not supported" error message below and for the mysql/sqlite
|
|
25
|
+
// placeholder rewrite condition (`rewritePlaceholders`) — keep engine names in sync with `DataSourceConfig.engine`.
|
|
26
|
+
const SUPPORTED_ENGINES = ['postgres', 'sqlite', 'mysql'] as const;
|
|
27
|
+
|
|
24
28
|
interface SshConfig {
|
|
25
29
|
host: string; // jump host reachable from the runner
|
|
26
30
|
port?: number; // default 22
|
|
@@ -37,8 +41,8 @@ interface DataSourceConfig {
|
|
|
37
41
|
readonly?: boolean;
|
|
38
42
|
statement_timeout_ms?: number;
|
|
39
43
|
max_rows?: number;
|
|
40
|
-
//
|
|
41
|
-
// still run on the runner; only
|
|
44
|
+
// Fallback path: tunnel the DB SOCKET through an SSH bastion. DB-only — the browser/E2E
|
|
45
|
+
// still run on the runner; only DB traffic crosses. See docs/spec/sungen_data_driver_ssh_tunnel_spec.md.
|
|
42
46
|
ssh?: SshConfig;
|
|
43
47
|
}
|
|
44
48
|
|
|
@@ -120,12 +124,38 @@ function loadConfig(): Record<string, DataSourceConfig> {
|
|
|
120
124
|
return doc.datasources;
|
|
121
125
|
}
|
|
122
126
|
|
|
127
|
+
/**
|
|
128
|
+
* `build()` always emits PG-style `$1..$n` placeholders; mysql2/better-sqlite3 both bind with `?`.
|
|
129
|
+
* Only real placeholders are rewritten — a `$1` sitting inside a single-quoted string literal (e.g.
|
|
130
|
+
* a catalog query `WHERE label = '$1 off'`) is left untouched. SQL escapes an inner quote by doubling
|
|
131
|
+
* it (`''`), which the simple in-string toggle handles (an even number of quotes returns to the same state).
|
|
132
|
+
*/
|
|
133
|
+
export function rewritePlaceholders(engine: string, sql: string): string {
|
|
134
|
+
if (engine !== 'sqlite' && engine !== 'mysql') return sql;
|
|
135
|
+
let out = '';
|
|
136
|
+
let inStr = false;
|
|
137
|
+
for (let i = 0; i < sql.length; i++) {
|
|
138
|
+
const c = sql[i];
|
|
139
|
+
if (c === "'") { inStr = !inStr; out += c; continue; }
|
|
140
|
+
if (!inStr && c === '$' && sql[i + 1] >= '0' && sql[i + 1] <= '9') {
|
|
141
|
+
let j = i + 1;
|
|
142
|
+
while (j < sql.length && sql[j] >= '0' && sql[j] <= '9') j++;
|
|
143
|
+
out += '?';
|
|
144
|
+
i = j - 1;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
out += c;
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
|
|
123
152
|
type Engine = { query(sql: string, params: any[]): Promise<any[]>; };
|
|
124
153
|
|
|
125
154
|
class DataSource {
|
|
126
155
|
private configs: Record<string, DataSourceConfig> | null = null;
|
|
127
156
|
private engines = new Map<string, Engine>();
|
|
128
157
|
private tunnels: Array<{ close: () => void }> = [];
|
|
158
|
+
private mysqlPools: Array<{ end: () => Promise<void> }> = [];
|
|
129
159
|
|
|
130
160
|
private cfg(name?: string): { key: string; conf: DataSourceConfig } {
|
|
131
161
|
if (!this.configs) this.configs = loadConfig();
|
|
@@ -142,7 +172,7 @@ class DataSource {
|
|
|
142
172
|
let engine: Engine;
|
|
143
173
|
if (conf.engine === 'postgres') {
|
|
144
174
|
let connectionString = conf.url;
|
|
145
|
-
if (conf.ssh) { //
|
|
175
|
+
if (conf.ssh) { // Fallback path: tunnel the DB socket through a bastion
|
|
146
176
|
const u = new URL(conf.url);
|
|
147
177
|
const t = await openSshTunnel(conf.ssh, u.hostname, Number(u.port || 5432));
|
|
148
178
|
this.tunnels.push(t);
|
|
@@ -152,20 +182,59 @@ class DataSource {
|
|
|
152
182
|
const { Pool } = require('pg');
|
|
153
183
|
const pool = new Pool({ connectionString, max: 2, statement_timeout: conf.statement_timeout_ms ?? 4000 });
|
|
154
184
|
engine = { query: async (sql, params) => (await pool.query(sql, params)).rows };
|
|
185
|
+
} else if (conf.engine === 'mysql') {
|
|
186
|
+
let connectionString = conf.url;
|
|
187
|
+
if (conf.ssh) { // Fallback path: tunnel the DB socket through a bastion
|
|
188
|
+
const u = new URL(conf.url);
|
|
189
|
+
const t = await openSshTunnel(conf.ssh, u.hostname, Number(u.port || 3306));
|
|
190
|
+
this.tunnels.push(t);
|
|
191
|
+
u.hostname = t.host; u.port = String(t.port); // rewrite host:port → 127.0.0.1:<tunnel> (keep user/pass/db/query)
|
|
192
|
+
connectionString = u.toString();
|
|
193
|
+
}
|
|
194
|
+
const mysql = require('mysql2/promise');
|
|
195
|
+
const pool = mysql.createPool({ uri: connectionString, connectionLimit: 2 });
|
|
196
|
+
this.mysqlPools.push(pool);
|
|
197
|
+
// mysql2 ignores the mysqljs per-query `timeout` field, and destroying the connection does
|
|
198
|
+
// not interrupt an in-flight query — so bound the wait client-side by racing the query
|
|
199
|
+
// against a timer. On overrun we reject and destroy the connection (removes it from the pool
|
|
200
|
+
// rather than releasing a still-busy socket); on success we release it back.
|
|
201
|
+
const timeoutMs = conf.statement_timeout_ms ?? 4000;
|
|
202
|
+
engine = {
|
|
203
|
+
query: async (sql, params) => {
|
|
204
|
+
const conn = await pool.getConnection();
|
|
205
|
+
const qp = conn.query(sql, params);
|
|
206
|
+
qp.catch(() => { /* swallow the post-timeout rejection from the abandoned query */ });
|
|
207
|
+
let timer: NodeJS.Timeout | undefined;
|
|
208
|
+
let timedOut = false;
|
|
209
|
+
const guard = new Promise<never>((_, reject) => {
|
|
210
|
+
timer = setTimeout(() => { timedOut = true; reject(new Error(`Data Driver: mysql query exceeded ${timeoutMs}ms — aborted.`)); }, timeoutMs);
|
|
211
|
+
});
|
|
212
|
+
try {
|
|
213
|
+
const [rows] = await Promise.race([qp, guard]);
|
|
214
|
+
return rows as any[];
|
|
215
|
+
} finally {
|
|
216
|
+
clearTimeout(timer);
|
|
217
|
+
if (timedOut) { try { conn.destroy(); } catch { /* best-effort */ } }
|
|
218
|
+
else { try { conn.release(); } catch { /* already gone */ } }
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
};
|
|
155
222
|
} else if (conf.engine === 'sqlite') {
|
|
156
223
|
if (conf.ssh) console.warn(`Data Driver: datasource "${key}" sets ssh: but engine is sqlite (file-based) — ssh ignored.`);
|
|
157
224
|
const Database = require('better-sqlite3');
|
|
158
225
|
const db = new Database(conf.url.replace(/^sqlite:/, ''), { readonly: conf.readonly !== false });
|
|
159
226
|
engine = { query: async (sql, params) => db.prepare(sql).all(...params) };
|
|
160
227
|
} else {
|
|
161
|
-
throw new Error(`Data Driver: engine "${conf.engine}" not supported
|
|
228
|
+
throw new Error(`Data Driver: engine "${conf.engine}" not supported (supported: ${SUPPORTED_ENGINES.join(' | ')}). For non-SQL stores (Cosmos NoSQL, Mongo…) verify via @api or @manual.`);
|
|
162
229
|
}
|
|
163
230
|
this.engines.set(key, engine);
|
|
164
231
|
return { engine, conf };
|
|
165
232
|
}
|
|
166
233
|
|
|
167
|
-
/** Close
|
|
234
|
+
/** Close open MySQL pools + SSH tunnels (optional explicit teardown; tunnels are unref'd so the process exits regardless). */
|
|
168
235
|
close(): void {
|
|
236
|
+
for (const p of this.mysqlPools) { try { void p.end(); } catch { /* best-effort */ } }
|
|
237
|
+
this.mysqlPools = [];
|
|
169
238
|
for (const t of this.tunnels) t.close();
|
|
170
239
|
this.tunnels = [];
|
|
171
240
|
}
|
|
@@ -211,9 +280,9 @@ class DataSource {
|
|
|
211
280
|
expect(n, `Expected ${count} row(s) in "${table}"${cols.length ? ' where ' + desc(filter) : ''} — found ${n}`).toBe(Number(count));
|
|
212
281
|
}
|
|
213
282
|
|
|
214
|
-
/** Rewrites $1/$2 placeholders to `?` for
|
|
283
|
+
/** Rewrites $1/$2 placeholders to `?` for engines that don't support PG-style positional params. */
|
|
215
284
|
private sqlFor(conf: DataSourceConfig, sql: string): string {
|
|
216
|
-
return conf.engine
|
|
285
|
+
return rewritePlaceholders(conf.engine, sql);
|
|
217
286
|
}
|
|
218
287
|
|
|
219
288
|
// --- Named queries (catalog-backed; SQL is resolved + embedded at compile time) -----------
|