@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 +2 -2
- package/src/api-catalog.ts +3 -1
- package/src/cli-import.ts +34 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sungen/driver-api",
|
|
3
|
-
"version": "3.1.2-beta.
|
|
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.
|
|
16
|
+
"@sun-asterisk/sungen": "3.1.2-beta.116",
|
|
17
17
|
"commander": "^14.0.2",
|
|
18
18
|
"yaml": "^2.8.2"
|
|
19
19
|
},
|
package/src/api-catalog.ts
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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}`,
|