@solana-mobile/dapp-store-cli 0.15.0 → 0.16.1
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/bin/dapp-store.js +3 -1
- package/lib/CliSetup.js +304 -505
- package/lib/CliUtils.js +6 -376
- package/lib/__tests__/CliSetupTest.js +484 -74
- package/lib/cli/__tests__/parseErrors.test.js +25 -0
- package/lib/cli/__tests__/signer.test.js +436 -0
- package/lib/cli/constants.js +23 -0
- package/lib/cli/messages.js +21 -0
- package/lib/cli/parseErrors.js +41 -0
- package/lib/{commands/publish/PublishCliSupport.js → cli/selfUpdate.js} +72 -38
- package/lib/{commands/publish/PublishCliRemove.js → cli/signer.js} +35 -56
- package/lib/index.js +96 -5
- package/lib/package.json +5 -24
- package/lib/portal/__tests__/releaseMetadata.test.js +647 -0
- package/lib/portal/__tests__/translators.test.js +76 -0
- package/lib/portal/__tests__/workflowClient.test.js +457 -0
- package/lib/portal/attestationClient.js +143 -0
- package/lib/portal/files.js +64 -0
- package/lib/portal/http.js +364 -0
- package/lib/portal/records.js +64 -0
- package/lib/portal/releaseMetadata.js +748 -0
- package/lib/portal/translators.js +460 -0
- package/lib/portal/types.js +1 -0
- package/lib/portal/workflowClient.js +704 -0
- package/lib/publication/PublicationProgressReporter.js +1051 -0
- package/lib/publication/__tests__/PublicationProgressReporter.test.js +174 -0
- package/lib/{commands/ValidateCommand.js → publication/__tests__/fundingPreflight.test.js} +90 -66
- package/lib/publication/__tests__/publicationSummary.test.js +26 -0
- package/lib/publication/cliValidation.js +482 -0
- package/lib/publication/fundingPreflight.js +246 -0
- package/lib/publication/publicationSummary.js +99 -0
- package/lib/{commands/utils.js → publication/runPublicationWorkflow.js} +16 -46
- package/package.json +5 -24
- package/src/CliSetup.ts +370 -505
- package/src/CliUtils.ts +9 -233
- package/src/__tests__/CliSetupTest.ts +272 -120
- package/src/cli/__tests__/parseErrors.test.ts +34 -0
- package/src/cli/__tests__/signer.test.ts +359 -0
- package/src/cli/constants.ts +3 -0
- package/src/cli/messages.ts +27 -0
- package/src/cli/parseErrors.ts +62 -0
- package/src/cli/selfUpdate.ts +59 -0
- package/src/cli/signer.ts +38 -0
- package/src/index.ts +31 -4
- package/src/portal/__tests__/releaseMetadata.test.ts +508 -0
- package/src/portal/__tests__/translators.test.ts +82 -0
- package/src/portal/__tests__/workflowClient.test.ts +278 -0
- package/src/portal/attestationClient.ts +19 -0
- package/src/portal/files.ts +73 -0
- package/src/portal/http.ts +170 -0
- package/src/portal/records.ts +38 -0
- package/src/portal/releaseMetadata.ts +489 -0
- package/src/portal/translators.ts +750 -0
- package/src/portal/types.ts +27 -0
- package/src/portal/workflowClient.ts +575 -0
- package/src/publication/PublicationProgressReporter.ts +1026 -0
- package/src/publication/__tests__/PublicationProgressReporter.test.ts +210 -0
- package/src/publication/__tests__/fundingPreflight.test.ts +78 -0
- package/src/publication/__tests__/publicationSummary.test.ts +30 -0
- package/src/publication/cliValidation.ts +264 -0
- package/src/publication/fundingPreflight.ts +123 -0
- package/src/publication/publicationSummary.ts +26 -0
- package/src/publication/runPublicationWorkflow.ts +46 -0
- package/lib/commands/create/CreateCliApp.js +0 -223
- package/lib/commands/create/CreateCliRelease.js +0 -290
- package/lib/commands/create/index.js +0 -40
- package/lib/commands/index.js +0 -3
- package/lib/commands/publish/PublishCliSubmit.js +0 -208
- package/lib/commands/publish/PublishCliUpdate.js +0 -211
- package/lib/commands/publish/index.js +0 -22
- package/lib/commands/scaffolding/ScaffoldInit.js +0 -15
- package/lib/commands/scaffolding/index.js +0 -1
- package/lib/config/EnvVariables.js +0 -59
- package/lib/config/PublishDetails.js +0 -915
- package/lib/config/S3StorageManager.js +0 -93
- package/lib/config/index.js +0 -2
- package/lib/generated/config_obj.json +0 -1
- package/lib/generated/config_schema.json +0 -1
- package/lib/prebuild_schema/publishing_source.yaml +0 -64
- package/lib/prebuild_schema/schemagen.js +0 -25
- package/lib/upload/CachedStorageDriver.js +0 -293
- package/lib/upload/TurboStorageDriver.js +0 -718
- package/lib/upload/index.js +0 -2
- package/src/commands/ValidateCommand.ts +0 -82
- package/src/commands/create/CreateCliApp.ts +0 -93
- package/src/commands/create/CreateCliRelease.ts +0 -149
- package/src/commands/create/index.ts +0 -47
- package/src/commands/index.ts +0 -3
- package/src/commands/publish/PublishCliRemove.ts +0 -66
- package/src/commands/publish/PublishCliSubmit.ts +0 -93
- package/src/commands/publish/PublishCliSupport.ts +0 -66
- package/src/commands/publish/PublishCliUpdate.ts +0 -101
- package/src/commands/publish/index.ts +0 -29
- package/src/commands/scaffolding/ScaffoldInit.ts +0 -20
- package/src/commands/scaffolding/index.ts +0 -1
- package/src/commands/utils.ts +0 -33
- package/src/config/EnvVariables.ts +0 -39
- package/src/config/PublishDetails.ts +0 -456
- package/src/config/S3StorageManager.ts +0 -47
- package/src/config/index.ts +0 -2
- package/src/prebuild_schema/publishing_source.yaml +0 -64
- package/src/prebuild_schema/schemagen.js +0 -31
- package/src/upload/CachedStorageDriver.ts +0 -99
- package/src/upload/TurboStorageDriver.ts +0 -277
- package/src/upload/index.ts +0 -2
|
@@ -1,183 +1,335 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
createCliCmd,
|
|
5
|
-
createReleaseCliCmd,
|
|
6
|
-
initCliCmd,
|
|
7
|
-
mainCli
|
|
8
|
-
} from "../CliSetup";
|
|
9
|
-
import { Constants } from "../CliUtils";
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
10
4
|
|
|
11
|
-
|
|
12
|
-
const outputHelpReference = "(outputHelp)"
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
13
6
|
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
import { mainCli } from "../CliSetup";
|
|
8
|
+
import {
|
|
9
|
+
DEFAULT_API_KEY_ENV,
|
|
10
|
+
DEFAULT_LOCAL_PORTAL_URL,
|
|
11
|
+
DEFAULT_PRODUCTION_PORTAL_URL,
|
|
12
|
+
UPDATED_PUBLISHING_CLI_DOCS_URL,
|
|
13
|
+
formatUpdatedCliUsageError,
|
|
14
|
+
resolveApiKey,
|
|
15
|
+
resolvePortalTargets,
|
|
16
|
+
validateNewVersionArgs,
|
|
17
|
+
validateResumeArgs,
|
|
18
|
+
} from "../publication/cliValidation";
|
|
19
|
+
|
|
20
|
+
describe("CLI surface", () => {
|
|
21
|
+
const outputHelpReference = "(outputHelp)";
|
|
22
|
+
const trackedEnvKeys = [
|
|
23
|
+
DEFAULT_API_KEY_ENV,
|
|
24
|
+
"ALT_DAPP_STORE_API_KEY",
|
|
25
|
+
"DAPP_STORE_PORTAL_URL",
|
|
26
|
+
"DAPP_STORE_PORTAL_WEB_URL",
|
|
27
|
+
"DAPP_STORE_PORTAL_API_BASE_URL",
|
|
28
|
+
] as const;
|
|
29
|
+
|
|
30
|
+
let errorOutput = "";
|
|
31
|
+
let otherOutput = "";
|
|
32
|
+
let tempDir = "";
|
|
33
|
+
let originalEnv: Record<string, string | undefined> = {};
|
|
16
34
|
|
|
17
35
|
beforeEach(() => {
|
|
18
36
|
errorOutput = "";
|
|
19
37
|
otherOutput = "";
|
|
20
|
-
|
|
38
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "dapp-store-cli-test-"));
|
|
39
|
+
originalEnv = Object.fromEntries(
|
|
40
|
+
trackedEnvKeys.map((key) => [key, process.env[key]])
|
|
41
|
+
);
|
|
42
|
+
for (const key of trackedEnvKeys) {
|
|
43
|
+
delete process.env[key];
|
|
44
|
+
}
|
|
21
45
|
|
|
46
|
+
mainCli.exitOverride();
|
|
22
47
|
mainCli.configureOutput({
|
|
23
|
-
getOutHelpWidth(): number {
|
|
24
|
-
|
|
25
|
-
|
|
48
|
+
getOutHelpWidth(): number {
|
|
49
|
+
return 200;
|
|
50
|
+
},
|
|
51
|
+
getErrHelpWidth(): number {
|
|
52
|
+
return 200;
|
|
53
|
+
},
|
|
26
54
|
writeOut(str: string) {
|
|
27
55
|
otherOutput += str;
|
|
28
56
|
},
|
|
29
|
-
|
|
30
57
|
writeErr(str: string) {
|
|
31
58
|
errorOutput += str;
|
|
32
|
-
}
|
|
59
|
+
},
|
|
33
60
|
});
|
|
61
|
+
process.exitCode = 0;
|
|
34
62
|
});
|
|
35
63
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
64
|
+
afterEach(() => {
|
|
65
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
66
|
+
for (const [key, value] of Object.entries(originalEnv)) {
|
|
67
|
+
if (typeof value === "undefined") {
|
|
68
|
+
delete process.env[key];
|
|
69
|
+
} else {
|
|
70
|
+
process.env[key] = value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
41
73
|
});
|
|
42
74
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
75
|
+
function createTempApkFile(fileName = "app-release.apk") {
|
|
76
|
+
const apkPath = path.join(tempDir, fileName);
|
|
77
|
+
fs.writeFileSync(apkPath, "apk");
|
|
78
|
+
return apkPath;
|
|
79
|
+
}
|
|
48
80
|
|
|
49
|
-
|
|
81
|
+
test("version reports the package version", () => {
|
|
82
|
+
expect(() => {
|
|
83
|
+
mainCli.parse(["node", "dapp-store", "-V"]);
|
|
84
|
+
}).toThrow();
|
|
50
85
|
});
|
|
51
86
|
|
|
52
|
-
test("
|
|
53
|
-
initCliCmd.exitOverride()
|
|
54
|
-
|
|
87
|
+
test("help advertises the default publication surface", () => {
|
|
55
88
|
expect(() => {
|
|
56
|
-
|
|
57
|
-
}).toThrow(outputHelpReference)
|
|
89
|
+
mainCli.parse(["node", "dapp-store", "--help"]);
|
|
90
|
+
}).toThrow(outputHelpReference);
|
|
91
|
+
|
|
92
|
+
expect(otherOutput).not.toContain("--new-version");
|
|
93
|
+
expect(otherOutput).toContain("resume");
|
|
94
|
+
expect(otherOutput).toContain("--apk-file");
|
|
95
|
+
expect(otherOutput).toContain("--apk-url");
|
|
96
|
+
expect(otherOutput).toContain("--keypair");
|
|
97
|
+
expect(otherOutput).toContain("--portal-url");
|
|
98
|
+
expect(otherOutput).not.toContain("--dapp-id");
|
|
99
|
+
expect(otherOutput).not.toContain("--fee-payer-keypair");
|
|
100
|
+
expect(otherOutput).not.toContain("--signer-keypair");
|
|
101
|
+
expect(errorOutput).toBe("");
|
|
102
|
+
});
|
|
58
103
|
|
|
59
|
-
|
|
60
|
-
|
|
104
|
+
test("new-version validation rejects ambiguous APK sources", () => {
|
|
105
|
+
expect(() =>
|
|
106
|
+
validateNewVersionArgs({
|
|
107
|
+
apkFile: createTempApkFile(),
|
|
108
|
+
apkUrl: "https://example.com/app.apk",
|
|
109
|
+
whatsNew: "Fixes",
|
|
110
|
+
keypair: "/tmp/signer.json",
|
|
111
|
+
})
|
|
112
|
+
).toThrow("exactly one of `--apk-file` or `--apk-url`");
|
|
113
|
+
});
|
|
61
114
|
|
|
62
|
-
test("
|
|
63
|
-
|
|
115
|
+
test("new-version validation rejects missing APK source", () => {
|
|
116
|
+
expect(() =>
|
|
117
|
+
validateNewVersionArgs({
|
|
118
|
+
whatsNew: "Fixes",
|
|
119
|
+
keypair: "/tmp/signer.json",
|
|
120
|
+
})
|
|
121
|
+
).toThrow("exactly one of `--apk-file` or `--apk-url`");
|
|
122
|
+
});
|
|
64
123
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
124
|
+
test("new-version validation rejects blank release notes", () => {
|
|
125
|
+
expect(() =>
|
|
126
|
+
validateNewVersionArgs({
|
|
127
|
+
apkUrl: "https://example.com/app.apk",
|
|
128
|
+
whatsNew: " ",
|
|
129
|
+
keypair: "/tmp/signer.json",
|
|
130
|
+
})
|
|
131
|
+
).toThrow("`--whats-new` is required.");
|
|
132
|
+
});
|
|
69
133
|
|
|
70
|
-
|
|
134
|
+
test("new-version validation rejects missing keypair", () => {
|
|
135
|
+
expect(() =>
|
|
136
|
+
validateNewVersionArgs({
|
|
137
|
+
apkUrl: "https://example.com/app.apk",
|
|
138
|
+
whatsNew: "Fixes",
|
|
139
|
+
})
|
|
140
|
+
).toThrow("`--keypair` is required.");
|
|
71
141
|
});
|
|
72
142
|
|
|
73
|
-
test("
|
|
74
|
-
|
|
143
|
+
test("new-version validation rejects a missing local APK file", () => {
|
|
144
|
+
expect(() =>
|
|
145
|
+
validateNewVersionArgs({
|
|
146
|
+
apkFile: path.join(tempDir, "missing.apk"),
|
|
147
|
+
whatsNew: "Fixes",
|
|
148
|
+
keypair: "/tmp/signer.json",
|
|
149
|
+
})
|
|
150
|
+
).toThrow("APK file not found");
|
|
151
|
+
});
|
|
75
152
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
153
|
+
test("new-version validation rejects non-HTTPS APK URLs", () => {
|
|
154
|
+
expect(() =>
|
|
155
|
+
validateNewVersionArgs({
|
|
156
|
+
apkUrl: "http://example.com/app.apk",
|
|
157
|
+
whatsNew: "Fixes",
|
|
158
|
+
keypair: "/tmp/signer.json",
|
|
159
|
+
})
|
|
160
|
+
).toThrow("`--apk-url` must use HTTPS.");
|
|
80
161
|
});
|
|
81
162
|
|
|
82
|
-
test("
|
|
83
|
-
|
|
163
|
+
test("new-version validation accepts an existing APK file", () => {
|
|
164
|
+
expect(() =>
|
|
165
|
+
validateNewVersionArgs({
|
|
166
|
+
apkFile: createTempApkFile(),
|
|
167
|
+
whatsNew: "Fixes",
|
|
168
|
+
keypair: "/tmp/signer.json",
|
|
169
|
+
})
|
|
170
|
+
).not.toThrow();
|
|
171
|
+
});
|
|
84
172
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
173
|
+
test("new-version validation accepts a single HTTPS APK URL", () => {
|
|
174
|
+
expect(() =>
|
|
175
|
+
validateNewVersionArgs({
|
|
176
|
+
apkUrl: "https://example.com/app.apk",
|
|
177
|
+
whatsNew: "Fixes",
|
|
178
|
+
keypair: "/tmp/signer.json",
|
|
179
|
+
})
|
|
180
|
+
).not.toThrow();
|
|
181
|
+
});
|
|
88
182
|
|
|
89
|
-
|
|
183
|
+
test("resume validation requires a single target", () => {
|
|
184
|
+
expect(() =>
|
|
185
|
+
validateResumeArgs({
|
|
186
|
+
releaseId: "release-1",
|
|
187
|
+
sessionId: "session-1",
|
|
188
|
+
keypair: "/tmp/signer.json",
|
|
189
|
+
})
|
|
190
|
+
).toThrow("exactly one of `--release-id` or `--session-id`");
|
|
90
191
|
});
|
|
91
192
|
|
|
92
|
-
test("
|
|
93
|
-
|
|
193
|
+
test("resume validation accepts alias flags", () => {
|
|
194
|
+
expect(() =>
|
|
195
|
+
validateResumeArgs({
|
|
196
|
+
resumeRelease: "release-1",
|
|
197
|
+
keypair: "/tmp/signer.json",
|
|
198
|
+
})
|
|
199
|
+
).not.toThrow();
|
|
200
|
+
|
|
201
|
+
expect(() =>
|
|
202
|
+
validateResumeArgs({
|
|
203
|
+
resumeSession: "session-1",
|
|
204
|
+
keypair: "/tmp/signer.json",
|
|
205
|
+
})
|
|
206
|
+
).not.toThrow();
|
|
207
|
+
});
|
|
94
208
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
209
|
+
test("resume validation rejects conflicting release aliases", () => {
|
|
210
|
+
expect(() =>
|
|
211
|
+
validateResumeArgs({
|
|
212
|
+
releaseId: "release-1",
|
|
213
|
+
resumeRelease: "release-2",
|
|
214
|
+
keypair: "/tmp/signer.json",
|
|
215
|
+
})
|
|
216
|
+
).toThrow(
|
|
217
|
+
"Conflicting values were provided for --release-id and --resume-release."
|
|
218
|
+
);
|
|
99
219
|
});
|
|
100
220
|
|
|
101
|
-
test("
|
|
102
|
-
|
|
221
|
+
test("resume validation accepts a release id", () => {
|
|
222
|
+
expect(() =>
|
|
223
|
+
validateResumeArgs({
|
|
224
|
+
releaseId: "release-1",
|
|
225
|
+
keypair: "/tmp/signer.json",
|
|
226
|
+
})
|
|
227
|
+
).not.toThrow();
|
|
228
|
+
});
|
|
103
229
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}).toThrow(outputHelpReference)
|
|
230
|
+
test("portal targets default to production when unset", () => {
|
|
231
|
+
const targets = resolvePortalTargets({});
|
|
107
232
|
|
|
108
|
-
expect(
|
|
233
|
+
expect(targets.apiBaseUrl).toBe(`${DEFAULT_PRODUCTION_PORTAL_URL}/api`);
|
|
109
234
|
});
|
|
110
235
|
|
|
111
|
-
|
|
236
|
+
test("portal targets default to localhost in local-dev mode", () => {
|
|
237
|
+
const targets = resolvePortalTargets({
|
|
238
|
+
localDev: true,
|
|
239
|
+
});
|
|
112
240
|
|
|
113
|
-
|
|
241
|
+
expect(targets.apiBaseUrl).toBe(`${DEFAULT_LOCAL_PORTAL_URL}/api`);
|
|
242
|
+
});
|
|
114
243
|
|
|
115
|
-
|
|
244
|
+
test("portal targets derive the API base URL from the configured portal URL", () => {
|
|
245
|
+
const targets = resolvePortalTargets({
|
|
246
|
+
portalUrl: "https://staging.publish.solanamobile.com",
|
|
247
|
+
});
|
|
116
248
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
249
|
+
expect(targets.apiBaseUrl).toBe(
|
|
250
|
+
"https://staging.publish.solanamobile.com/api"
|
|
251
|
+
);
|
|
252
|
+
});
|
|
120
253
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
publish Submit a publishing request (\`submit\`, \`update\`, \`remove\`, or \`support\`) to the Solana Mobile dApp publisher portal
|
|
126
|
-
help [command] display help for command
|
|
127
|
-
`;
|
|
254
|
+
test("portal targets preserve portal subpaths when deriving /api", () => {
|
|
255
|
+
const targets = resolvePortalTargets({
|
|
256
|
+
portalUrl: "https://portal.example.com/publishing",
|
|
257
|
+
});
|
|
128
258
|
|
|
129
|
-
|
|
259
|
+
expect(targets.apiBaseUrl).toBe(
|
|
260
|
+
"https://portal.example.com/publishing/api"
|
|
261
|
+
);
|
|
262
|
+
});
|
|
130
263
|
|
|
131
|
-
|
|
264
|
+
test("portal targets honor DAPP_STORE_PORTAL_URL from the environment", () => {
|
|
265
|
+
process.env.DAPP_STORE_PORTAL_URL = "https://env.publish.solanamobile.com";
|
|
132
266
|
|
|
133
|
-
|
|
134
|
-
-h, --help display help for command
|
|
135
|
-
`;
|
|
267
|
+
const targets = resolvePortalTargets({});
|
|
136
268
|
|
|
137
|
-
|
|
269
|
+
expect(targets.apiBaseUrl).toBe("https://env.publish.solanamobile.com/api");
|
|
270
|
+
});
|
|
138
271
|
|
|
139
|
-
|
|
272
|
+
test("portal targets ignore removed legacy portal env vars", () => {
|
|
273
|
+
process.env.DAPP_STORE_PORTAL_WEB_URL =
|
|
274
|
+
"https://legacy-web.publish.solanamobile.com";
|
|
275
|
+
process.env.DAPP_STORE_PORTAL_API_BASE_URL =
|
|
276
|
+
"https://legacy.publish.solanamobile.com/root/api";
|
|
140
277
|
|
|
141
|
-
|
|
278
|
+
const targets = resolvePortalTargets({});
|
|
142
279
|
|
|
143
|
-
|
|
144
|
-
|
|
280
|
+
expect(targets.apiBaseUrl).toBe(`${DEFAULT_PRODUCTION_PORTAL_URL}/api`);
|
|
281
|
+
});
|
|
145
282
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
283
|
+
test("local-dev mode rejects non-local portal URLs", () => {
|
|
284
|
+
expect(() =>
|
|
285
|
+
resolvePortalTargets({
|
|
286
|
+
localDev: true,
|
|
287
|
+
portalUrl: "https://portal.example.com",
|
|
288
|
+
})
|
|
289
|
+
).toThrow("only allows localhost portal endpoints");
|
|
290
|
+
});
|
|
150
291
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
292
|
+
test("non-local portal endpoints must use HTTPS", () => {
|
|
293
|
+
expect(() =>
|
|
294
|
+
resolvePortalTargets({
|
|
295
|
+
portalUrl: "http://portal.example.com",
|
|
296
|
+
})
|
|
297
|
+
).toThrow("Portal endpoints must use HTTPS unless --local-dev is set.");
|
|
298
|
+
});
|
|
154
299
|
|
|
155
|
-
|
|
300
|
+
test("portal targets honor the configured API key env name", () => {
|
|
301
|
+
expect(DEFAULT_API_KEY_ENV).toBe("DAPP_STORE_API_KEY");
|
|
302
|
+
});
|
|
156
303
|
|
|
157
|
-
|
|
304
|
+
test("resolveApiKey reads the default API key env var", async () => {
|
|
305
|
+
process.env.DAPP_STORE_API_KEY = "portal-secret";
|
|
158
306
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
-u, --url <url> RPC URL (default: "https://api.devnet.solana.com")
|
|
162
|
-
-d, --dry-run Flag for dry run. Doesn't mint an NFT
|
|
163
|
-
-s, --storage-config <storage-config> Provide alternative storage configuration details
|
|
164
|
-
-p, --priority-fee-lamports <priority-fee-lamports> Priority Fee lamports
|
|
165
|
-
-h, --help display help for command
|
|
166
|
-
`;
|
|
307
|
+
await expect(resolveApiKey({})).resolves.toBe("portal-secret");
|
|
308
|
+
});
|
|
167
309
|
|
|
168
|
-
|
|
310
|
+
test("resolveApiKey reads a custom API key env var", async () => {
|
|
311
|
+
process.env.ALT_DAPP_STORE_API_KEY = "alt-secret";
|
|
169
312
|
|
|
170
|
-
|
|
313
|
+
await expect(
|
|
314
|
+
resolveApiKey({ apiKeyEnv: "ALT_DAPP_STORE_API_KEY" })
|
|
315
|
+
).resolves.toBe("alt-secret");
|
|
316
|
+
});
|
|
171
317
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
-h, --help display help for command
|
|
181
|
-
`;
|
|
318
|
+
test("resolveApiKey rejects when no API key is available", async () => {
|
|
319
|
+
await expect(resolveApiKey({})).rejects.toThrow(
|
|
320
|
+
"Portal API key is required."
|
|
321
|
+
);
|
|
322
|
+
await expect(resolveApiKey({})).rejects.toThrow(
|
|
323
|
+
UPDATED_PUBLISHING_CLI_DOCS_URL
|
|
324
|
+
);
|
|
325
|
+
});
|
|
182
326
|
|
|
327
|
+
test("formatUpdatedCliUsageError converts unknown-option errors into docs guidance", () => {
|
|
328
|
+
expect(formatUpdatedCliUsageError("error: unknown option '-k'")).toContain(
|
|
329
|
+
"Unknown option '-k'."
|
|
330
|
+
);
|
|
331
|
+
expect(formatUpdatedCliUsageError("error: unknown option '-k'")).toContain(
|
|
332
|
+
UPDATED_PUBLISHING_CLI_DOCS_URL
|
|
333
|
+
);
|
|
334
|
+
});
|
|
183
335
|
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getCommanderUserFacingError,
|
|
5
|
+
isCommanderLifecycleExit,
|
|
6
|
+
} from "../parseErrors";
|
|
7
|
+
import { UPDATED_PUBLISHING_CLI_DOCS_URL } from "../../publication/cliValidation";
|
|
8
|
+
|
|
9
|
+
describe("CLI parse error handling", () => {
|
|
10
|
+
test("legacy parse errors point to the updated publishing docs", () => {
|
|
11
|
+
const error = getCommanderUserFacingError({
|
|
12
|
+
code: "commander.unknownOption",
|
|
13
|
+
exitCode: 1,
|
|
14
|
+
message: "error: unknown option '-k'",
|
|
15
|
+
name: "CommanderError",
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
expect(error).not.toBeNull();
|
|
19
|
+
expect(error?.exitCode).toBe(1);
|
|
20
|
+
expect(error?.message).toContain("Unknown option '-k'.");
|
|
21
|
+
expect(error?.message).toContain(UPDATED_PUBLISHING_CLI_DOCS_URL);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("help exits are ignored", () => {
|
|
25
|
+
expect(
|
|
26
|
+
isCommanderLifecycleExit({
|
|
27
|
+
code: "commander.helpDisplayed",
|
|
28
|
+
exitCode: 0,
|
|
29
|
+
message: "(outputHelp)",
|
|
30
|
+
name: "CommanderError",
|
|
31
|
+
})
|
|
32
|
+
).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
});
|