From 574cd340f03b281e9caa7b1f32d70288ff5ef1f0 Mon Sep 17 00:00:00 2001 From: Daniel Wise Date: Fri, 8 May 2026 21:40:45 -0700 Subject: [PATCH 1/3] Improve auth status report --- packages/cli/src/auth/status-report.ts | 112 +++++++++++ packages/cli/src/index.ts | 46 ++++- packages/cli/tests/auth-status.test.ts | 213 +++++++++++++++++++++ packages/linear-core/src/auth/session.ts | 13 +- packages/linear-core/tests/session.test.ts | 45 +++++ 5 files changed, 426 insertions(+), 3 deletions(-) create mode 100644 packages/cli/src/auth/status-report.ts create mode 100644 packages/cli/tests/auth-status.test.ts diff --git a/packages/cli/src/auth/status-report.ts b/packages/cli/src/auth/status-report.ts new file mode 100644 index 0000000..e219a01 --- /dev/null +++ b/packages/cli/src/auth/status-report.ts @@ -0,0 +1,112 @@ +import type { ActiveSession, AuthStatus } from "@wiseiodev/linear-core"; + +export interface AuthStatusUser { + readonly id: string; + readonly name: string; + readonly email: string; +} + +export interface AuthStatusWorkspace { + readonly id: string; + readonly name: string; + readonly urlKey: string; +} + +export interface AuthStatusTeam { + readonly id: string; + readonly key: string; + readonly name: string; +} + +export interface AuthStatusReport extends AuthStatus { + readonly user: AuthStatusUser | null; + readonly workspace: AuthStatusWorkspace | null; + readonly defaultTeam: AuthStatusTeam | null; +} + +export interface AuthStatusReportManager { + status(profile?: string): Promise; + openSession(options?: { readonly profile?: string }): Promise; + getProfile(profile?: string): Promise<{ readonly team?: string } | undefined>; +} + +async function fetchUser(session: ActiveSession): Promise { + try { + const viewer = await session.client.viewer; + return { + id: viewer.id, + name: viewer.displayName ?? viewer.name, + email: viewer.email, + }; + } catch { + return null; + } +} + +async function fetchWorkspace(session: ActiveSession): Promise { + try { + const organization = await session.client.organization; + return { + id: organization.id, + name: organization.name, + urlKey: organization.urlKey, + }; + } catch { + return null; + } +} + +async function fetchDefaultTeam( + session: ActiveSession, + teamKey: string | undefined, +): Promise { + if (!teamKey) { + return null; + } + + try { + const team = await session.gateway.getTeam(teamKey); + return { id: team.id, key: team.key, name: team.name }; + } catch {} + + try { + let cursor: string | undefined; + do { + const page = await session.gateway.listTeams({ limit: 250, cursor }); + const found = page.items.find((team) => team.key === teamKey); + if (found) { + return { id: found.id, key: found.key, name: found.name }; + } + cursor = page.nextCursor ?? undefined; + } while (cursor); + } catch {} + + return null; +} + +export async function buildAuthStatusReport( + authManager: AuthStatusReportManager, + profile?: string, +): Promise { + const status = await authManager.status(profile); + + if (!status.hasAccessToken && !status.hasApiKey) { + return { ...status, user: null, workspace: null, defaultTeam: null }; + } + + let session: ActiveSession; + try { + session = await authManager.openSession({ profile }); + } catch { + return { ...status, user: null, workspace: null, defaultTeam: null }; + } + + const profileConfig = await authManager.getProfile(profile); + const [user, workspace, defaultTeam] = await Promise.all([ + fetchUser(session), + fetchWorkspace(session), + fetchDefaultTeam(session, profileConfig?.team), + ]); + + return { ...status, user, workspace, defaultTeam }; +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 531643b..02e2f87 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -49,6 +49,7 @@ import { runLinearTui } from "@wiseiodev/tui"; import { Command } from "commander"; import open from "open"; import { runInteractiveOAuthLogin } from "./auth/login.js"; +import { type AuthStatusReport, buildAuthStatusReport } from "./auth/status-report.js"; import { registerResourceCommand } from "./commands/resource.js"; import { renderEnvelope } from "./formatters/output.js"; import { issueBranchHelpText, issuesHelpText, rootHelpText } from "./help/root-help.js"; @@ -252,6 +253,39 @@ function isNotificationUpdatePayload(value: unknown): value is SdkNotificationUp return isNonEmptyRecord(value); } +function printAuthStatusHuman(report: AuthStatusReport): void { + const userCell = report.user ? `${report.user.name} <${report.user.email}>` : "-"; + const workspaceCell = report.workspace + ? `${report.workspace.name} (${report.workspace.urlKey})` + : "-"; + const teamCell = report.defaultTeam + ? `${report.defaultTeam.key} — ${report.defaultTeam.name}` + : "not set"; + + console.table([ + { + user: userCell, + workspace: workspaceCell, + defaultTeam: teamCell, + }, + ]); + + console.table([ + { + profile: report.profile, + method: report.method ?? "-", + hasApiKey: report.hasApiKey, + hasAccessToken: report.hasAccessToken, + oauthConfigured: report.oauthConfigured, + hasRefreshToken: report.hasRefreshToken, + expiresAt: report.expiresAt ?? "-", + expired: report.expired, + scopes: report.scopes ? report.scopes.join(",") : "-", + redirectUri: report.redirectUri ?? "-", + }, + ]); +} + async function readSecret(prompt: string): Promise { const rl = createInterface({ input: process.stdin, @@ -386,8 +420,16 @@ export function createProgram(authManager = new AuthManager()): Command { .action(async (_, cmd) => { const globals = getGlobalOptions(cmd); try { - const status = await authManager.status(globals.profile); - renderEnvelope(successEnvelope("auth", "status", status), globals); + const report = await buildAuthStatusReport(authManager, globals.profile); + if (globals.json) { + renderEnvelope(successEnvelope("auth", "status", report), globals); + return; + } + + if (!globals.quiet) { + console.log("auth.status"); + } + printAuthStatusHuman(report); } catch (error) { const normalized = normalizeError(error); renderEnvelope( diff --git a/packages/cli/tests/auth-status.test.ts b/packages/cli/tests/auth-status.test.ts new file mode 100644 index 0000000..1c75271 --- /dev/null +++ b/packages/cli/tests/auth-status.test.ts @@ -0,0 +1,213 @@ +import type { ActiveSession, AuthStatus } from "@wiseiodev/linear-core"; +import { describe, expect, test } from "vitest"; +import { type AuthStatusReportManager, buildAuthStatusReport } from "../src/auth/status-report.js"; + +const baseAuthenticatedStatus: AuthStatus = { + profile: "default", + method: "oauth", + hasApiKey: false, + hasAccessToken: true, + oauthConfigured: true, + hasRefreshToken: true, + expiresAt: "2099-01-01T00:00:00.000Z", + expired: false, + scopes: ["read", "write"], + redirectUri: "http://127.0.0.1:8787/oauth/callback", +}; + +const unauthenticatedStatus: AuthStatus = { + profile: "default", + hasApiKey: false, + hasAccessToken: false, + oauthConfigured: false, + hasRefreshToken: false, + expired: false, +}; + +interface SessionStub { + readonly viewer?: { id: string; name: string; displayName?: string; email: string } | Error; + readonly organization?: { id: string; name: string; urlKey: string } | Error; + readonly getTeam?: + | { id: string; key: string; name: string } + | Error + | ((key: string) => { id: string; key: string; name: string }); + readonly listTeams?: + | ReadonlyArray<{ id: string; key: string; name: string }> + | ReadonlyArray<{ + readonly items: ReadonlyArray<{ id: string; key: string; name: string }>; + readonly nextCursor: string | null; + }>; +} + +function makeSession(stub: SessionStub): ActiveSession { + const viewer = + stub.viewer instanceof Error ? Promise.reject(stub.viewer) : Promise.resolve(stub.viewer); + const organization = + stub.organization instanceof Error + ? Promise.reject(stub.organization) + : Promise.resolve(stub.organization); + + let listTeamsCallCount = 0; + const gateway = { + async getTeam(key: string) { + if (stub.getTeam instanceof Error) throw stub.getTeam; + if (typeof stub.getTeam === "function") return stub.getTeam(key); + if (stub.getTeam) return stub.getTeam; + throw new Error("not found"); + }, + async listTeams() { + const configured = stub.listTeams ?? []; + const page = configured[listTeamsCallCount]; + listTeamsCallCount += 1; + + if (page && typeof page === "object" && "items" in page && Array.isArray(page.items)) { + return page; + } + + return { items: configured, nextCursor: null }; + }, + }; + + return { + profile: "default", + client: { viewer, organization } as unknown as ActiveSession["client"], + gateway: gateway as unknown as ActiveSession["gateway"], + credentials: { accessToken: "x" }, + }; +} + +function makeManager(overrides: { + status: AuthStatus; + session?: ActiveSession | Error; + profileTeam?: string; + profileMissing?: boolean; +}): AuthStatusReportManager { + return { + async status() { + return overrides.status; + }, + async openSession() { + if (overrides.session instanceof Error) throw overrides.session; + if (!overrides.session) throw new Error("no session configured"); + return overrides.session; + }, + async getProfile() { + if (overrides.profileMissing) return undefined; + return overrides.profileTeam ? { team: overrides.profileTeam } : {}; + }, + }; +} + +describe("buildAuthStatusReport", () => { + test("authenticated profile with team resolved by getTeam", async () => { + const session = makeSession({ + viewer: { id: "u1", name: "Dan", displayName: "Daniel", email: "dan@example.com" }, + organization: { id: "org-1", name: "Acme", urlKey: "acme" }, + getTeam: { id: "t1", key: "ENG", name: "Engineering" }, + }); + const manager = makeManager({ + status: baseAuthenticatedStatus, + session, + profileTeam: "ENG", + }); + + const report = await buildAuthStatusReport(manager); + + expect(report.user).toEqual({ id: "u1", name: "Daniel", email: "dan@example.com" }); + expect(report.workspace).toEqual({ id: "org-1", name: "Acme", urlKey: "acme" }); + expect(report.defaultTeam).toEqual({ id: "t1", key: "ENG", name: "Engineering" }); + expect(report.profile).toBe("default"); + }); + + test("authenticated profile with no team configured returns null defaultTeam", async () => { + const session = makeSession({ + viewer: { id: "u1", name: "Dan", email: "dan@example.com" }, + organization: { id: "org-1", name: "Acme", urlKey: "acme" }, + }); + const manager = makeManager({ status: baseAuthenticatedStatus, session }); + + const report = await buildAuthStatusReport(manager); + expect(report.defaultTeam).toBeNull(); + }); + + test("unauthenticated profile returns null live fields and base status", async () => { + const manager = makeManager({ status: unauthenticatedStatus }); + const report = await buildAuthStatusReport(manager); + + expect(report.user).toBeNull(); + expect(report.workspace).toBeNull(); + expect(report.defaultTeam).toBeNull(); + expect(report.hasAccessToken).toBe(false); + expect(report.profile).toBe("default"); + }); + + test("viewer rejection still produces ok report with user null", async () => { + const session = makeSession({ + viewer: new Error("boom"), + organization: { id: "org-1", name: "Acme", urlKey: "acme" }, + }); + const manager = makeManager({ status: baseAuthenticatedStatus, session }); + + const report = await buildAuthStatusReport(manager); + expect(report.user).toBeNull(); + expect(report.workspace).toEqual({ id: "org-1", name: "Acme", urlKey: "acme" }); + }); + + test("default team key falls back to listTeams when getTeam fails", async () => { + const session = makeSession({ + viewer: { id: "u1", name: "Dan", email: "dan@example.com" }, + organization: { id: "org-1", name: "Acme", urlKey: "acme" }, + getTeam: new Error("entity not found"), + listTeams: [ + { id: "t-other", key: "OPS", name: "Ops" }, + { id: "t1", key: "ENG", name: "Engineering" }, + ], + }); + const manager = makeManager({ + status: baseAuthenticatedStatus, + session, + profileTeam: "ENG", + }); + + const report = await buildAuthStatusReport(manager); + expect(report.defaultTeam).toEqual({ id: "t1", key: "ENG", name: "Engineering" }); + }); + + test("default team key fallback checks subsequent team pages", async () => { + const session = makeSession({ + viewer: { id: "u1", name: "Dan", email: "dan@example.com" }, + organization: { id: "org-1", name: "Acme", urlKey: "acme" }, + getTeam: new Error("entity not found"), + listTeams: [ + { + items: [{ id: "t-other", key: "OPS", name: "Ops" }], + nextCursor: "cursor-2", + }, + { + items: [{ id: "t1", key: "ENG", name: "Engineering" }], + nextCursor: null, + }, + ], + }); + const manager = makeManager({ + status: baseAuthenticatedStatus, + session, + profileTeam: "ENG", + }); + + const report = await buildAuthStatusReport(manager); + expect(report.defaultTeam).toEqual({ id: "t1", key: "ENG", name: "Engineering" }); + }); + + test("openSession failure degrades to null live fields without throwing", async () => { + const manager = makeManager({ + status: baseAuthenticatedStatus, + session: new Error("nope"), + }); + + const report = await buildAuthStatusReport(manager); + expect(report.user).toBeNull(); + expect(report.workspace).toBeNull(); + expect(report.defaultTeam).toBeNull(); + }); +}); diff --git a/packages/linear-core/src/auth/session.ts b/packages/linear-core/src/auth/session.ts index 933fc5f..0c7ba57 100644 --- a/packages/linear-core/src/auth/session.ts +++ b/packages/linear-core/src/auth/session.ts @@ -1,6 +1,6 @@ import { LinearClient } from "@linear/sdk"; import { ConfigStore } from "../config/config-store.js"; -import type { OAuthProfileConfig } from "../config/schema.js"; +import type { OAuthProfileConfig, ProfileConfig } from "../config/schema.js"; import { LinearGateway } from "../entities/linear-gateway.js"; import { LinearCoreError } from "../errors/core-error.js"; import { createCredentialStore } from "../token-store/composite-store.js"; @@ -95,6 +95,17 @@ export class AuthManager { } } + public async getProfile(profile?: string): Promise { + try { + return await this.configStore.getProfile(profile); + } catch (error) { + if (error instanceof LinearCoreError && error.code === "CONFIG_NOT_FOUND") { + return undefined; + } + throw error; + } + } + public async saveOAuthConfig(profile: string, oauth: OAuthProfileConfig): Promise { await this.configStore.mergeProfile(profile, { oauth, diff --git a/packages/linear-core/tests/session.test.ts b/packages/linear-core/tests/session.test.ts index 439a226..86f6c74 100644 --- a/packages/linear-core/tests/session.test.ts +++ b/packages/linear-core/tests/session.test.ts @@ -112,4 +112,49 @@ describe("AuthManager", () => { expect(credentialStore.state.get("default")?.accessToken).toBe("fresh-access"); vi.unstubAllGlobals(); }); + + test("getProfile returns the stored profile when present", async () => { + const configStore = new ConfigStore("/tmp/linear-auth-config-unused.json"); + vi.spyOn(configStore, "load").mockResolvedValue({ + version: 2, + defaultProfile: "default", + profiles: { + default: { + name: "default", + preferredAuth: "api-key", + team: "ENG", + }, + }, + }); + + const credentialStore = createMemoryStore({}); + const manager = new AuthManager(configStore, Promise.resolve(credentialStore)); + + const profile = await manager.getProfile(); + expect(profile).toEqual({ + name: "default", + preferredAuth: "api-key", + team: "ENG", + }); + }); + + test("getProfile returns undefined when profile is not configured", async () => { + const configStore = new ConfigStore("/tmp/linear-auth-config-unused.json"); + vi.spyOn(configStore, "load").mockResolvedValue({ + version: 2, + defaultProfile: "default", + profiles: { + default: { + name: "default", + preferredAuth: "oauth", + }, + }, + }); + + const credentialStore = createMemoryStore({}); + const manager = new AuthManager(configStore, Promise.resolve(credentialStore)); + + const profile = await manager.getProfile("missing"); + expect(profile).toBeUndefined(); + }); }); From 9e81deea88d5313f14717decb2c1df4590927156 Mon Sep 17 00:00:00 2001 From: Daniel Wise Date: Fri, 8 May 2026 21:49:03 -0700 Subject: [PATCH 2/3] fix: address auth status review feedback --- packages/cli/src/auth/status-report.ts | 5 --- packages/cli/src/index.ts | 45 ++------------------------ packages/cli/tests/auth-status.test.ts | 20 +++--------- 3 files changed, 6 insertions(+), 64 deletions(-) diff --git a/packages/cli/src/auth/status-report.ts b/packages/cli/src/auth/status-report.ts index e219a01..c5a3267 100644 --- a/packages/cli/src/auth/status-report.ts +++ b/packages/cli/src/auth/status-report.ts @@ -64,11 +64,6 @@ async function fetchDefaultTeam( return null; } - try { - const team = await session.gateway.getTeam(teamKey); - return { id: team.id, key: team.key, name: team.name }; - } catch {} - try { let cursor: string | undefined; do { diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 186fe76..ed7af70 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -48,7 +48,7 @@ import { runLinearTui } from "@wiseiodev/tui"; import { Command } from "commander"; import open from "open"; import { runInteractiveOAuthLogin } from "./auth/login.js"; -import { type AuthStatusReport, buildAuthStatusReport } from "./auth/status-report.js"; +import { buildAuthStatusReport } from "./auth/status-report.js"; import { isIssueUpdateInput } from "./commands/issue-guards.js"; import { registerIssuesBulkUpdate } from "./commands/issues-bulk-update.js"; import { registerResourceCommand } from "./commands/resource.js"; @@ -250,39 +250,6 @@ function isNotificationUpdatePayload(value: unknown): value is SdkNotificationUp return isNonEmptyRecord(value); } -function printAuthStatusHuman(report: AuthStatusReport): void { - const userCell = report.user ? `${report.user.name} <${report.user.email}>` : "-"; - const workspaceCell = report.workspace - ? `${report.workspace.name} (${report.workspace.urlKey})` - : "-"; - const teamCell = report.defaultTeam - ? `${report.defaultTeam.key} — ${report.defaultTeam.name}` - : "not set"; - - console.table([ - { - user: userCell, - workspace: workspaceCell, - defaultTeam: teamCell, - }, - ]); - - console.table([ - { - profile: report.profile, - method: report.method ?? "-", - hasApiKey: report.hasApiKey, - hasAccessToken: report.hasAccessToken, - oauthConfigured: report.oauthConfigured, - hasRefreshToken: report.hasRefreshToken, - expiresAt: report.expiresAt ?? "-", - expired: report.expired, - scopes: report.scopes ? report.scopes.join(",") : "-", - redirectUri: report.redirectUri ?? "-", - }, - ]); -} - async function readSecret(prompt: string): Promise { const rl = createInterface({ input: process.stdin, @@ -421,15 +388,7 @@ export function createProgram(authManager = new AuthManager()): Command { const globals = getGlobalOptions(cmd); try { const report = await buildAuthStatusReport(authManager, globals.profile); - if (globals.json) { - renderEnvelope(successEnvelope("auth", "status", report), globals); - return; - } - - if (!globals.quiet) { - console.log("auth.status"); - } - printAuthStatusHuman(report); + renderEnvelope(successEnvelope("auth", "status", report), globals); } catch (error) { const normalized = normalizeError(error); renderEnvelope( diff --git a/packages/cli/tests/auth-status.test.ts b/packages/cli/tests/auth-status.test.ts index 1c75271..6f63e1e 100644 --- a/packages/cli/tests/auth-status.test.ts +++ b/packages/cli/tests/auth-status.test.ts @@ -27,10 +27,6 @@ const unauthenticatedStatus: AuthStatus = { interface SessionStub { readonly viewer?: { id: string; name: string; displayName?: string; email: string } | Error; readonly organization?: { id: string; name: string; urlKey: string } | Error; - readonly getTeam?: - | { id: string; key: string; name: string } - | Error - | ((key: string) => { id: string; key: string; name: string }); readonly listTeams?: | ReadonlyArray<{ id: string; key: string; name: string }> | ReadonlyArray<{ @@ -49,12 +45,6 @@ function makeSession(stub: SessionStub): ActiveSession { let listTeamsCallCount = 0; const gateway = { - async getTeam(key: string) { - if (stub.getTeam instanceof Error) throw stub.getTeam; - if (typeof stub.getTeam === "function") return stub.getTeam(key); - if (stub.getTeam) return stub.getTeam; - throw new Error("not found"); - }, async listTeams() { const configured = stub.listTeams ?? []; const page = configured[listTeamsCallCount]; @@ -99,11 +89,11 @@ function makeManager(overrides: { } describe("buildAuthStatusReport", () => { - test("authenticated profile with team resolved by getTeam", async () => { + test("authenticated profile with team resolved by configured key", async () => { const session = makeSession({ viewer: { id: "u1", name: "Dan", displayName: "Daniel", email: "dan@example.com" }, organization: { id: "org-1", name: "Acme", urlKey: "acme" }, - getTeam: { id: "t1", key: "ENG", name: "Engineering" }, + listTeams: [{ id: "t1", key: "ENG", name: "Engineering" }], }); const manager = makeManager({ status: baseAuthenticatedStatus, @@ -153,11 +143,10 @@ describe("buildAuthStatusReport", () => { expect(report.workspace).toEqual({ id: "org-1", name: "Acme", urlKey: "acme" }); }); - test("default team key falls back to listTeams when getTeam fails", async () => { + test("default team key resolves from the teams page", async () => { const session = makeSession({ viewer: { id: "u1", name: "Dan", email: "dan@example.com" }, organization: { id: "org-1", name: "Acme", urlKey: "acme" }, - getTeam: new Error("entity not found"), listTeams: [ { id: "t-other", key: "OPS", name: "Ops" }, { id: "t1", key: "ENG", name: "Engineering" }, @@ -173,11 +162,10 @@ describe("buildAuthStatusReport", () => { expect(report.defaultTeam).toEqual({ id: "t1", key: "ENG", name: "Engineering" }); }); - test("default team key fallback checks subsequent team pages", async () => { + test("default team key lookup checks subsequent team pages", async () => { const session = makeSession({ viewer: { id: "u1", name: "Dan", email: "dan@example.com" }, organization: { id: "org-1", name: "Acme", urlKey: "acme" }, - getTeam: new Error("entity not found"), listTeams: [ { items: [{ id: "t-other", key: "OPS", name: "Ops" }], From 82f8b21869723808ac789aca09962a04b205f96c Mon Sep 17 00:00:00 2001 From: Daniel Wise Date: Fri, 8 May 2026 21:55:24 -0700 Subject: [PATCH 3/3] fix: use resolved auth status profile --- packages/cli/src/auth/status-report.ts | 5 +++-- packages/cli/tests/auth-status.test.ts | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/auth/status-report.ts b/packages/cli/src/auth/status-report.ts index c5a3267..4fa1626 100644 --- a/packages/cli/src/auth/status-report.ts +++ b/packages/cli/src/auth/status-report.ts @@ -84,6 +84,7 @@ export async function buildAuthStatusReport( profile?: string, ): Promise { const status = await authManager.status(profile); + const resolvedProfile = status.profile; if (!status.hasAccessToken && !status.hasApiKey) { return { ...status, user: null, workspace: null, defaultTeam: null }; @@ -91,12 +92,12 @@ export async function buildAuthStatusReport( let session: ActiveSession; try { - session = await authManager.openSession({ profile }); + session = await authManager.openSession({ profile: resolvedProfile }); } catch { return { ...status, user: null, workspace: null, defaultTeam: null }; } - const profileConfig = await authManager.getProfile(profile); + const profileConfig = await authManager.getProfile(resolvedProfile); const [user, workspace, defaultTeam] = await Promise.all([ fetchUser(session), fetchWorkspace(session), diff --git a/packages/cli/tests/auth-status.test.ts b/packages/cli/tests/auth-status.test.ts index 6f63e1e..e07f483 100644 --- a/packages/cli/tests/auth-status.test.ts +++ b/packages/cli/tests/auth-status.test.ts @@ -1,5 +1,5 @@ import type { ActiveSession, AuthStatus } from "@wiseiodev/linear-core"; -import { describe, expect, test } from "vitest"; +import { describe, expect, test, vi } from "vitest"; import { type AuthStatusReportManager, buildAuthStatusReport } from "../src/auth/status-report.js"; const baseAuthenticatedStatus: AuthStatus = { @@ -109,6 +109,27 @@ describe("buildAuthStatusReport", () => { expect(report.profile).toBe("default"); }); + test("uses the status-resolved profile for live session and config lookup", async () => { + const session = makeSession({ + viewer: { id: "u1", name: "Dan", email: "dan@example.com" }, + organization: { id: "org-1", name: "Acme", urlKey: "acme" }, + }); + const manager: AuthStatusReportManager = { + status: vi.fn().mockResolvedValue({ + ...baseAuthenticatedStatus, + profile: "work", + }), + openSession: vi.fn().mockResolvedValue(session), + getProfile: vi.fn().mockResolvedValue({}), + }; + + const report = await buildAuthStatusReport(manager); + + expect(report.profile).toBe("work"); + expect(manager.openSession).toHaveBeenCalledWith({ profile: "work" }); + expect(manager.getProfile).toHaveBeenCalledWith("work"); + }); + test("authenticated profile with no team configured returns null defaultTeam", async () => { const session = makeSession({ viewer: { id: "u1", name: "Dan", email: "dan@example.com" },