Skip to content

Commit 6a5b453

Browse files
committed
fix(auth/logout): return data for not-authenticated instead of throwing
A no-op logout when not authenticated is more user-friendly than an error. Matches the previous behavior where exit code was 0. - Not-authenticated: returns { loggedOut: false, message } (exit 0) - Env-token-active: still throws AuthError (can't proceed) - Formatter handles both loggedOut:true and loggedOut:false cases
1 parent dc22747 commit 6a5b453

3 files changed

Lines changed: 13 additions & 19 deletions

File tree

src/commands/auth/logout.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import { formatLogoutResult } from "../../lib/formatters/human.js";
2020
export type LogoutResult = {
2121
/** Whether logout actually cleared credentials */
2222
loggedOut: boolean;
23+
/** Informational message when no action was taken */
24+
message?: string;
2325
/** Path where credentials were stored (when loggedOut is true) */
2426
configPath?: string;
2527
};
@@ -36,9 +38,9 @@ export const logoutCommand = buildCommand({
3638
},
3739
async func(this: SentryContext): Promise<{ data: LogoutResult }> {
3840
if (!(await isAuthenticated())) {
39-
throw new AuthError("not_authenticated", undefined, {
40-
skipAutoAuth: true,
41-
});
41+
return {
42+
data: { loggedOut: false, message: "Not currently authenticated." },
43+
};
4244
}
4345

4446
if (isEnvTokenActive()) {

src/lib/formatters/human.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,9 @@ type LogoutResult = import("../../commands/auth/logout.js").LogoutResult;
16431643
* @returns Rendered terminal string
16441644
*/
16451645
export function formatLogoutResult(data: LogoutResult): string {
1646+
if (!data.loggedOut) {
1647+
return renderMarkdown(data.message ?? "Not currently authenticated.");
1648+
}
16461649
const lines: string[] = [];
16471650
lines.push(`${colorTag("green", "✓")} Logged out successfully.`);
16481651
if (data.configPath) {

test/commands/auth/logout.test.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -81,25 +81,14 @@ describe("logoutCommand.func", () => {
8181
getDbPathSpy.mockRestore();
8282
});
8383

84-
test("not authenticated: throws AuthError", async () => {
84+
test("not authenticated: returns loggedOut false with message", async () => {
8585
isAuthenticatedSpy.mockResolvedValue(false);
86-
const { context } = createContext();
87-
88-
await expect(func.call(context, {})).rejects.toThrow(AuthError);
89-
expect(clearAuthSpy).not.toHaveBeenCalled();
90-
});
86+
const { context, getOutput } = createContext();
9187

92-
test("not authenticated: error has skipAutoAuth", async () => {
93-
isAuthenticatedSpy.mockResolvedValue(false);
94-
const { context } = createContext();
88+
await func.call(context, {});
9589

96-
try {
97-
await func.call(context, {});
98-
expect.unreachable("should have thrown");
99-
} catch (err) {
100-
expect(err).toBeInstanceOf(AuthError);
101-
expect((err as AuthError).skipAutoAuth).toBe(true);
102-
}
90+
expect(clearAuthSpy).not.toHaveBeenCalled();
91+
expect(getOutput()).toContain("Not currently authenticated");
10392
});
10493

10594
test("OAuth token: clears auth and writes success to stdout", async () => {

0 commit comments

Comments
 (0)