mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(signal): validate account replacement on new port
This commit is contained in:
@@ -110,7 +110,7 @@ describe("Signal managed setup validation wiring", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps the original account identity when the generic input changes A to B", async () => {
|
||||
it("validates an account replacement on a distinct persisted port", async () => {
|
||||
const originalCfg: OpenClawConfig = {
|
||||
channels: {
|
||||
signal: {
|
||||
@@ -129,7 +129,7 @@ describe("Signal managed setup validation wiring", () => {
|
||||
},
|
||||
};
|
||||
|
||||
await runSetupWizardFinalize({
|
||||
const finalized = await runSetupWizardFinalize({
|
||||
finalize: signalSetupWizard.finalize,
|
||||
cfg: changedCfg,
|
||||
credentialValues,
|
||||
@@ -142,8 +142,12 @@ describe("Signal managed setup validation wiring", () => {
|
||||
account: "+15555550124",
|
||||
reusableConfiguredAccount: "+15555550123",
|
||||
reusableConfiguredTransport: expect.any(String),
|
||||
transport: expect.objectContaining({ httpPort: 8081 }),
|
||||
}),
|
||||
);
|
||||
expect(finalized?.cfg?.channels?.signal?.transport).toEqual(
|
||||
expect.objectContaining({ httpPort: 8081 }),
|
||||
);
|
||||
});
|
||||
|
||||
it("propagates setup cancellation without entering readiness recovery", async () => {
|
||||
|
||||
@@ -208,26 +208,28 @@ describe("probeManagedSignalSetup", () => {
|
||||
expect(mocks.spawnDaemon).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("never reuses the old daemon after the selected account changes", async () => {
|
||||
it("validates a replacement account on its newly allocated port", async () => {
|
||||
const changedAccount = "+15555550124";
|
||||
const replacementTransport = { ...transport, httpPort: 8081 };
|
||||
const cfg = {
|
||||
channels: {
|
||||
signal: { accounts: { work: { account: changedAccount, transport } } },
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
mocks.assertBindAvailable.mockRejectedValueOnce(new Error("address in use (EADDRINUSE)"));
|
||||
|
||||
await expect(
|
||||
probeManagedSignalSetup({
|
||||
...createParams(cfg, transport, account),
|
||||
...createParams(cfg, replacementTransport, account),
|
||||
account: changedAccount,
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
ok: false,
|
||||
error: expect.stringContaining("EADDRINUSE"),
|
||||
).resolves.toMatchObject({ ok: true });
|
||||
expect(mocks.assertBindAvailable).toHaveBeenCalledWith({
|
||||
httpHost: "127.0.0.1",
|
||||
httpPort: 8081,
|
||||
});
|
||||
expect(mocks.probeTransport).not.toHaveBeenCalled();
|
||||
expect(mocks.spawnDaemon).not.toHaveBeenCalled();
|
||||
expect(mocks.spawnDaemon).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ account: changedAccount, httpPort: 8081 }),
|
||||
);
|
||||
});
|
||||
|
||||
it("spawns the selected account on the final port and always stops it", async () => {
|
||||
|
||||
@@ -312,9 +312,17 @@ export const signalSetupWizard: ChannelSetupWizard = {
|
||||
if (!account) {
|
||||
throw new Error("Signal managed setup requires an account number before validation.");
|
||||
}
|
||||
const reusableConfiguredAccount = normalizeSignalAccountInput(
|
||||
params.credentialValues[signalSetupStateKeys.managedReuseAccount],
|
||||
);
|
||||
const transport = prepareSignalManagedNativeTransport({
|
||||
cfg: params.cfg,
|
||||
accountId: params.accountId,
|
||||
// The old account keeps its daemon port until whole-channel reload drains it. Treat an
|
||||
// identity replacement as a new bind so setup validates the exact transport it persists.
|
||||
reserveTargetAccountPorts: Boolean(
|
||||
reusableConfiguredAccount && reusableConfiguredAccount !== account,
|
||||
),
|
||||
overrides:
|
||||
typeof params.credentialValues.cliPath === "string"
|
||||
? { cliPath: params.credentialValues.cliPath }
|
||||
@@ -326,8 +334,7 @@ export const signalSetupWizard: ChannelSetupWizard = {
|
||||
accountId: params.accountId,
|
||||
transport,
|
||||
account,
|
||||
reusableConfiguredAccount:
|
||||
params.credentialValues[signalSetupStateKeys.managedReuseAccount],
|
||||
reusableConfiguredAccount,
|
||||
reusableConfiguredTransport:
|
||||
params.credentialValues[signalSetupStateKeys.managedReuseTransport],
|
||||
runtime: params.runtime,
|
||||
|
||||
@@ -267,6 +267,42 @@ describe("prepareSignalManagedNativeTransport", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("reserves an inherited target port while replacing its account identity", () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
signal: {
|
||||
accounts: {
|
||||
work: {
|
||||
account: "+15555550124",
|
||||
transport: {
|
||||
kind: "managed-native",
|
||||
url: "http://127.0.0.1:8080",
|
||||
httpPort: 8080,
|
||||
},
|
||||
},
|
||||
personal: {
|
||||
account: "+15555550123",
|
||||
transport: { kind: "managed-native", httpPort: 8081 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
expect(
|
||||
prepareSignalManagedNativeTransport({
|
||||
cfg: cfg as never,
|
||||
accountId: "work",
|
||||
reserveTargetAccountPorts: true,
|
||||
}),
|
||||
).toEqual({
|
||||
kind: "managed-native",
|
||||
url: "http://127.0.0.1:8082",
|
||||
httpHost: "127.0.0.1",
|
||||
httpPort: 8082,
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps an aligned managed connection URL on the allocated bind port", () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
|
||||
@@ -156,6 +156,7 @@ export function prepareSignalManagedNativeTransport(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId: string;
|
||||
overrides?: Omit<SignalManagedNativeTransport, "kind">;
|
||||
reserveTargetAccountPorts?: boolean;
|
||||
}): SignalManagedNativeTransport {
|
||||
const existing = resolveConfiguredSignalTransport(params.cfg, params.accountId);
|
||||
const existingManaged = existing?.kind === "managed-native" ? existing : undefined;
|
||||
@@ -221,7 +222,7 @@ export function prepareSignalManagedNativeTransport(params: {
|
||||
const targetAccountId = normalizeAccountId(params.accountId);
|
||||
const reservedPorts = new Set<number>();
|
||||
for (const [accountId, accountPorts] of portsByAccountId) {
|
||||
if (accountId === targetAccountId) {
|
||||
if (accountId === targetAccountId && !params.reserveTargetAccountPorts) {
|
||||
continue;
|
||||
}
|
||||
for (const httpPort of accountPorts) {
|
||||
|
||||
Reference in New Issue
Block a user