Google API errors as typed findings, never raw JSON: cli/lib/google-errors.ts parses SERVICE_DISABLED (api-disabled: service, Cloud project, console page), PERMISSION_DENIED (no-app-access: which app, Users and permissions), 401 / invalid_grant (auth-revoked) and 429 (quota); health / analytics print one line with the URL, raw text in detail and the log; desktop Health tab: the line, 'Turn it on in Google Cloud' and 'Check again', raw text in a collapsed Details; Portfolio shows a Cloud project blocking several apps once. Findings exit codes are results: the sidecar reports outcome (ok / findings / declined / failed) and the command's own error line; scan / check / validate / health exit 1 with findings show and cache the findings; no bare 'Exit code N' anywhere. tests/google-errors.test.ts, boot check errors cases (api-disabled, scan-findings, scan-failed) (pitfall 57)
This commit is contained in:
parent
f69c10d3a0
commit
89256f1e80
28 changed files with 666 additions and 47 deletions
64
apps/desktop/src/dev/error-fixtures.ts
Normal file
64
apps/desktop/src/dev/error-fixtures.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// Live-shape failures for the boot check and tests: Google's 403 SERVICE_DISABLED body exactly as the Play Developer
|
||||
// Reporting API answers it (the project id is an example), the health snapshot `storebound health --json` writes for
|
||||
// it, and `storebound scan --json` results: findings with exit 1 (by design) and a real failure with its error line.
|
||||
import { errorBody } from '../../../../cli/lib/http';
|
||||
import { storeReadError } from '../../../../cli/lib/google-errors';
|
||||
import { errorLine } from '../../../../cli/lib/desktop-protocol';
|
||||
|
||||
export const EXAMPLE_CLOUD_PROJECT = '123456789000';
|
||||
export const SERVICE_DISABLED_403 = `{
|
||||
"error": {
|
||||
"code": 403,
|
||||
"message": "Google Play Developer Reporting API has not been used in project ${EXAMPLE_CLOUD_PROJECT} before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/playdeveloperreporting.googleapis.com/overview?project=${EXAMPLE_CLOUD_PROJECT} then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
|
||||
"status": "PERMISSION_DENIED",
|
||||
"details": [
|
||||
{
|
||||
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
|
||||
"reason": "SERVICE_DISABLED",
|
||||
"domain": "googleapis.com",
|
||||
"metadata": {
|
||||
"activationUrl": "https://console.developers.google.com/apis/api/playdeveloperreporting.googleapis.com/overview?project=${EXAMPLE_CLOUD_PROJECT}",
|
||||
"service": "playdeveloperreporting.googleapis.com",
|
||||
"consumer": "projects/${EXAMPLE_CLOUD_PROJECT}",
|
||||
"serviceTitle": "Google Play Developer Reporting API",
|
||||
"containerInfo": "${EXAMPLE_CLOUD_PROJECT}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/google.rpc.LocalizedMessage",
|
||||
"locale": "en-US",
|
||||
"message": "Google Play Developer Reporting API has not been used in project ${EXAMPLE_CLOUD_PROJECT} before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/playdeveloperreporting.googleapis.com/overview?project=${EXAMPLE_CLOUD_PROJECT} then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry."
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/google.rpc.Help",
|
||||
"links": [{ "description": "Google developers console API activation", "url": "https://console.developers.google.com/apis/api/playdeveloperreporting.googleapis.com/overview?project=${EXAMPLE_CLOUD_PROJECT}" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
}`;
|
||||
|
||||
/** The Error message storebound's Google client throws for it (play/google.ts). */
|
||||
export const serviceDisabledMessage = (pkg: string): string => `GET /v1beta1/apps/${pkg}/crashRateMetricSet -> 403 ${errorBody(SERVICE_DISABLED_403, 800)}`;
|
||||
|
||||
/** `storebound health --json` when the Reporting API is off. */
|
||||
export const healthApiDisabled = (pkg: string) => ({ date: '2026-10-13', play: storeReadError(serviceDisabledMessage(pkg), { app: pkg }) });
|
||||
|
||||
/** `storebound scan --json` that found problems: exit 1 by design, the result still printed. */
|
||||
export const SCAN_FINDINGS = {
|
||||
method: 'scan', ok: false, exitCode: 1, dryRun: false, command: 'storebound scan --json', outcome: 'findings' as const,
|
||||
output: '12:00:00 [scan] 41 files\n12:00:00 [scan] 1 blocker, 0 warnings',
|
||||
data: {
|
||||
stacks: ['flutter'], sdks: [], counts: { blocker: 1, high: 0, medium: 0, low: 0, accepted: 0 },
|
||||
declarations: { changed: false, items: [] },
|
||||
findings: [{ rule: 'account-deletion', store: 'apple', severity: 'blocker', guideline: '5.1.1(v)', title: 'Sign-up without in-app account deletion',
|
||||
evidence: [{ file: 'lib/auth/sign_up.dart', line: 12, text: 'createUserWithEmailAndPassword' }], fix: 'Add a "Delete account" action in the app settings', skill: 'account-deletion' }],
|
||||
},
|
||||
};
|
||||
|
||||
/** A real failure: the config does not load; the sidecar sends the command's own error line. */
|
||||
const SCAN_STDERR = '\nerror: storebound.config.ts: app.name is missing';
|
||||
export const SCAN_FAILED = {
|
||||
method: 'scan', ok: false, exitCode: 1, dryRun: false, command: 'storebound scan --json', outcome: 'failed' as const,
|
||||
output: `12:00:00 [scan] loading the config${SCAN_STDERR}`, error: errorLine(SCAN_STDERR, ''),
|
||||
};
|
||||
export const SCAN_FAILED_LINE = 'storebound.config.ts: app.name is missing';
|
||||
Loading…
Add table
Add a link
Reference in a new issue