fix(federation): scope Reset-cleanup Remove owns-spaces detection to ownedSpaces payload
This commit is contained in:
+33
-5
@@ -131,14 +131,16 @@ describe('FederationPanel — Reset cleanup', () => {
|
|||||||
await waitFor(() => expect(deleteUser).toHaveBeenCalledWith('acc1'));
|
await waitFor(() => expect(deleteUser).toHaveBeenCalledWith('acc1'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('surfaces "transfer ownership first" when Remove hits owns-spaces 400', async () => {
|
it('surfaces "transfer ownership first" only when the 400 carries an ownedSpaces payload', async () => {
|
||||||
peers.mockResolvedValue({ peers: [] });
|
peers.mockResolvedValue({ peers: [] });
|
||||||
resetEvents.mockResolvedValue({
|
// Isolate the classifier: the fixture account does NOT own spaces locally
|
||||||
events: [resetEvent([orphanedAccount({ ownedSpaces: [{ id: 's1', name: 'My Space' }] })])],
|
// (default ownedSpaces: []), so the transfer-first message can only come
|
||||||
});
|
// from the error's `ownedSpaces` payload — the real server 400 shape.
|
||||||
|
resetEvents.mockResolvedValue({ events: [resetEvent([orphanedAccount()])] });
|
||||||
deleteUser.mockRejectedValue(
|
deleteUser.mockRejectedValue(
|
||||||
Object.assign(new Error('User owns spaces — transfer ownership first'), {
|
Object.assign(new Error('User owns spaces — transfer ownership first'), {
|
||||||
status: 400,
|
error: 'User owns spaces — transfer ownership first',
|
||||||
|
statusCode: 400,
|
||||||
ownedSpaces: [{ id: 's1', name: 'My Space' }],
|
ownedSpaces: [{ id: 's1', name: 'My Space' }],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -159,4 +161,30 @@ describe('FederationPanel — Reset cleanup', () => {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('shows the generic failure toast for a rejection WITHOUT an ownedSpaces payload', async () => {
|
||||||
|
peers.mockResolvedValue({ peers: [] });
|
||||||
|
resetEvents.mockResolvedValue({ events: [resetEvent([orphanedAccount()])] });
|
||||||
|
deleteUser.mockRejectedValue(
|
||||||
|
Object.assign(new Error('Internal server error'), { statusCode: 400 }),
|
||||||
|
);
|
||||||
|
|
||||||
|
render(<FederationPanel />);
|
||||||
|
|
||||||
|
const removeBtn = await screen.findByRole('button', { name: 'Remove' });
|
||||||
|
fireEvent.click(removeBtn);
|
||||||
|
|
||||||
|
const confirmBtn = await screen.findByRole('button', { name: 'Delete permanently' });
|
||||||
|
fireEvent.click(confirmBtn);
|
||||||
|
|
||||||
|
await waitFor(() => expect(deleteUser).toHaveBeenCalledWith('acc1'));
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(addToast).toHaveBeenCalledWith('Internal server error', 'warning'),
|
||||||
|
);
|
||||||
|
// The classifier must NOT mislabel a generic 400 as an ownership problem.
|
||||||
|
expect(addToast).not.toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('transfer ownership first'),
|
||||||
|
'warning',
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -926,11 +926,7 @@ function ResetCleanup() {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (confirmAction.kind === 'remove') {
|
if (confirmAction.kind === 'remove') {
|
||||||
const status = (err as { status?: number } | null | undefined)?.status;
|
const ownsSpaces = err != null && typeof err === 'object' && 'ownedSpaces' in err;
|
||||||
const ownsSpaces =
|
|
||||||
status === 400 ||
|
|
||||||
(err != null && typeof err === 'object' && 'ownedSpaces' in err) ||
|
|
||||||
confirmAction.account.ownedSpaces.length > 0;
|
|
||||||
if (ownsSpaces) {
|
if (ownsSpaces) {
|
||||||
addToast(
|
addToast(
|
||||||
`${confirmAction.account.username} owns spaces — transfer ownership first (Space Settings → Ownership).`,
|
`${confirmAction.account.username} owns spaces — transfer ownership first (Space Settings → Ownership).`,
|
||||||
|
|||||||
Reference in New Issue
Block a user