@sungen/driver-api 3.1.2-beta.114 → 3.1.2-beta.116

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sungen/driver-api",
3
- "version": "3.1.2-beta.114",
3
+ "version": "3.1.2-beta.116",
4
4
  "description": "Sungen API capability — the @api annotation, API catalog, OpenAPI import + `sungen api import`, and the specs/api.ts runtime template. Plugs into @sun-asterisk/sungen via the capability SPI.",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -13,7 +13,7 @@
13
13
  "author": "eqe team (engineer & quality) — Sun Asterisk",
14
14
  "license": "MIT",
15
15
  "dependencies": {
16
- "@sun-asterisk/sungen": "3.1.2-beta.114",
16
+ "@sun-asterisk/sungen": "3.1.2-beta.116",
17
17
  "commander": "^14.0.2",
18
18
  "yaml": "^2.8.2"
19
19
  },
@@ -52,7 +52,9 @@ const _cache = new Map<string, LoadedApiCatalog>();
52
52
  export function findApiCatalogFiles(screenName: string, cwd: string = process.cwd()): ApiCatalogFiles {
53
53
  const out: ApiCatalogFiles = {};
54
54
  if (!screenName) return out;
55
- const screenDir = screenName.startsWith('flows/')
55
+ // `screenName` is a unit id relative to qa/: `<screen>` (→ qa/screens/<screen>), `flows/<flow>`,
56
+ // or an api-first unit `api/<area>` / `api/flows/<flow>` (→ qa/<screenName>).
57
+ const screenDir = screenName.startsWith('flows/') || screenName.startsWith('api/')
56
58
  ? path.join(cwd, 'qa', screenName)
57
59
  : path.join(cwd, 'qa', 'screens', screenName);
58
60
  const screenFile = path.join(screenDir, 'api', 'apis.yaml');
package/src/cli-import.ts CHANGED
@@ -157,20 +157,29 @@ export function registerApiCommand(program: Command): void {
157
157
  writeCatalog(f, entries, !!opts.merge);
158
158
  written.push(rel(f));
159
159
  } else {
160
- // group by area → qa/api/<area>/api/apis.yaml
160
+ // group by area → qa/api/<area>/api/apis.yaml + a runnable contract-scenario stub.
161
161
  const byArea: Record<string, Record<string, ImportedApiEntry>> = {};
162
162
  for (const [name, e] of Object.entries(entries)) (byArea[e.area || 'root'] ||= {})[name] = e;
163
163
  for (const [area, group] of Object.entries(byArea)) {
164
164
  const f = path.join(process.cwd(), 'qa', 'api', area, 'api', 'apis.yaml');
165
165
  writeCatalog(f, group, !!opts.merge);
166
166
  written.push(`${rel(f)} (${Object.keys(group).length})`);
167
+ // Scaffold the feature so `sungen generate --api <area>` has something to compile — one
168
+ // happy-path contract scenario per endpoint (the 70% baseline). Don't clobber edits.
169
+ const featFile = path.join(process.cwd(), 'qa', 'api', area, 'features', `${area}.feature`);
170
+ if (!fs.existsSync(featFile)) {
171
+ fs.mkdirSync(path.dirname(featFile), { recursive: true });
172
+ fs.writeFileSync(featFile, importedFeatureStub(area, group));
173
+ written.push(`${rel(featFile)} (${Object.keys(group).length} contract scenario(s))`);
174
+ }
167
175
  }
168
176
  }
169
177
 
170
178
  console.log(`\n✓ Imported ${count} endpoint(s) from ${kind} source:`);
171
179
  for (const w of written) console.log(` → ${w}`);
172
180
  if (skipped.length) console.log(`\n⚠ Skipped: ${skipped.slice(0, 10).join('; ')}${skipped.length > 10 ? ` … (+${skipped.length - 10})` : ''}`);
173
- console.log('\nNext: wire the `datasource` base_url + auth in datasources.yaml, then `@api:<name>` in a scenario.\n');
181
+ console.log('\nNext: wire the `datasource` base_url + auth in datasources.yaml, refine the scenarios,');
182
+ console.log('then `sungen generate --api <area>` → `npx playwright test`.\n');
174
183
  } catch (e) { fail(e); }
175
184
  });
176
185
  }
@@ -183,6 +192,29 @@ function upsertEnv(file: string, key: string, value: string): void {
183
192
  fs.writeFileSync(file, content.replace(/\n?$/, '\n') + line + '\n');
184
193
  }
185
194
 
195
+ /** A runnable feature from imported endpoints: one happy-path contract scenario each (the 70% baseline). */
196
+ function importedFeatureStub(area: string, group: Record<string, ImportedApiEntry>): string {
197
+ const lines = [
198
+ `@parallel @area:${area}`,
199
+ `Feature: ${area} API`,
200
+ '',
201
+ ' # Auto-scaffolded from the imported catalog — one contract check per endpoint. Refine these:',
202
+ ' # add error cases with @cases, flows with ordered @api tags, idempotency with @concurrent:N.',
203
+ ' # `sungen audit` flags missing error/idempotency coverage on mutating endpoints.',
204
+ '',
205
+ ];
206
+ let i = 1;
207
+ for (const [name, e] of Object.entries(group)) {
208
+ const status = e.expect?.status ?? 200;
209
+ const seq = String(i++).padStart(3, '0');
210
+ lines.push(` @high @api:${name}`);
211
+ lines.push(` Scenario: VP-API-${seq} ${e.method} ${e.path} returns ${status}`);
212
+ lines.push(` Then expect {{${name}.status}} is ${status}`);
213
+ lines.push('');
214
+ }
215
+ return lines.join('\n');
216
+ }
217
+
186
218
  function featureStub(area: string): string {
187
219
  return [
188
220
  `@area:${area}`,