CLI: fix config set dry-run coverage gaps
This commit is contained in:
parent
ffe24955c8
commit
ab5aec137c
@ -513,6 +513,26 @@ describe("config cli", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("logs a dry-run note when value mode performs no validation checks", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
gateway: { port: 18789 },
|
||||
};
|
||||
setSnapshot(resolved, resolved);
|
||||
|
||||
await runConfigCommand(["config", "set", "gateway.port", "19001", "--dry-run"]);
|
||||
|
||||
expect(mockWriteConfigFile).not.toHaveBeenCalled();
|
||||
expect(mockResolveSecretRefValue).not.toHaveBeenCalled();
|
||||
expect(mockLog).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
"Dry run note: value mode does not run schema/resolvability checks.",
|
||||
),
|
||||
);
|
||||
expect(mockLog).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Dry run successful: 1 update(s) validated"),
|
||||
);
|
||||
});
|
||||
|
||||
it("supports batch mode for refs/providers in dry-run", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
gateway: { port: 18789 },
|
||||
@ -862,6 +882,56 @@ describe("config cli", () => {
|
||||
);
|
||||
expect(mockError).toHaveBeenCalledWith(expect.stringContaining("provider mismatch"));
|
||||
});
|
||||
|
||||
it("fails dry-run for nested provider edits that make existing refs unresolvable", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
gateway: { port: 18789 },
|
||||
secrets: {
|
||||
providers: {
|
||||
vaultfile: { source: "file", path: "/tmp/secrets.json", mode: "json" },
|
||||
},
|
||||
},
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
enabled: true,
|
||||
apiKey: {
|
||||
source: "file",
|
||||
provider: "vaultfile",
|
||||
id: "/providers/search/apiKey",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as never,
|
||||
};
|
||||
setSnapshot(resolved, resolved);
|
||||
mockResolveSecretRefValue.mockImplementationOnce(async () => {
|
||||
throw new Error("provider mismatch");
|
||||
});
|
||||
|
||||
await expect(
|
||||
runConfigCommand([
|
||||
"config",
|
||||
"set",
|
||||
"secrets.providers.vaultfile.path",
|
||||
'"/tmp/other-secrets.json"',
|
||||
"--strict-json",
|
||||
"--dry-run",
|
||||
]),
|
||||
).rejects.toThrow("__exit__:1");
|
||||
|
||||
expect(mockResolveSecretRefValue).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
provider: "vaultfile",
|
||||
id: "/providers/search/apiKey",
|
||||
}),
|
||||
expect.any(Object),
|
||||
);
|
||||
expect(mockError).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Dry run failed: 1 SecretRef assignment(s) could not be resolved."),
|
||||
);
|
||||
expect(mockError).toHaveBeenCalledWith(expect.stringContaining("provider mismatch"));
|
||||
});
|
||||
});
|
||||
|
||||
describe("path hardening", () => {
|
||||
|
||||
@ -42,6 +42,7 @@ import type {
|
||||
ConfigSetDryRunResult,
|
||||
} from "./config-set-dryrun.js";
|
||||
import {
|
||||
hasBatchMode,
|
||||
hasProviderBuilderOptions,
|
||||
hasRefBuilderOptions,
|
||||
parseBatchSource,
|
||||
@ -593,7 +594,7 @@ function buildRefAssignmentOperation(params: {
|
||||
|
||||
function parseProviderAliasFromTargetPath(path: PathSegment[]): string | null {
|
||||
if (
|
||||
path.length === 3 &&
|
||||
path.length >= 3 &&
|
||||
path[0] === SECRET_PROVIDER_PATH_PREFIX[0] &&
|
||||
path[1] === SECRET_PROVIDER_PATH_PREFIX[1]
|
||||
) {
|
||||
@ -857,12 +858,9 @@ export async function runConfigSet(opts: {
|
||||
}) {
|
||||
const runtime = opts.runtime ?? defaultRuntime;
|
||||
try {
|
||||
const hasBatchMode = Boolean(
|
||||
(opts.cliOptions.batchJson && opts.cliOptions.batchJson.trim().length > 0) ||
|
||||
(opts.cliOptions.batchFile && opts.cliOptions.batchFile.trim().length > 0),
|
||||
);
|
||||
const isBatchMode = hasBatchMode(opts.cliOptions);
|
||||
const modeResolution = resolveConfigSetMode({
|
||||
hasBatchMode,
|
||||
hasBatchMode: isBatchMode,
|
||||
hasRefBuilderOptions: hasRefBuilderOptions(opts.cliOptions),
|
||||
hasProviderBuilderOptions: hasProviderBuilderOptions(opts.cliOptions),
|
||||
strictJson: Boolean(opts.cliOptions.strictJson || opts.cliOptions.json),
|
||||
@ -938,6 +936,13 @@ export async function runConfigSet(opts: {
|
||||
if (opts.cliOptions.json) {
|
||||
runtime.log(JSON.stringify(dryRunResult, null, 2));
|
||||
} else {
|
||||
if (!dryRunResult.checks.schema && !dryRunResult.checks.resolvability) {
|
||||
runtime.log(
|
||||
info(
|
||||
"Dry run note: value mode does not run schema/resolvability checks. Use --strict-json, builder flags, or batch mode to enable validation checks.",
|
||||
),
|
||||
);
|
||||
}
|
||||
runtime.log(
|
||||
info(
|
||||
`Dry run successful: ${operations.length} update(s) validated against ${shortenHomePath(snapshot.path)}.`,
|
||||
@ -1126,7 +1131,11 @@ export function registerConfigCli(program: Command) {
|
||||
.argument("[value]", "Value (JSON5 or raw string)")
|
||||
.option("--strict-json", "Strict JSON5 parsing (error instead of raw string fallback)", false)
|
||||
.option("--json", "Legacy alias for --strict-json", false)
|
||||
.option("--dry-run", "Validate changes without writing openclaw.json", false)
|
||||
.option(
|
||||
"--dry-run",
|
||||
"Validate changes without writing openclaw.json (checks run in builder/json/batch modes)",
|
||||
false,
|
||||
)
|
||||
.option("--ref-provider <alias>", "SecretRef builder: provider alias")
|
||||
.option("--ref-source <source>", "SecretRef builder: source (env|file|exec)")
|
||||
.option("--ref-id <id>", "SecretRef builder: ref id")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user