Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions __tests__/hooks/account-status-derivation.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* ENG-516 — light headline status fallbacks.
*
* `headlineFromLevel` / `capabilitiesFromLevel` are the client-side stand-ins
* for backends that don't expose `statusHeadline` / `capabilities` yet
* (lnflash/flash#452). They must match the backend derivation exactly:
* verified=L1+, bankPayout=L2+, business=L3, usdAccount orthogonal (Bridge KYC).
*/

import { AccountLevel } from "@app/graphql/level-context"
import {
capabilitiesFromLevel,
headlineFromLevel,
} from "@app/hooks/account-status-derivation"

describe("headlineFromLevel", () => {
it("maps NonAuth and ZERO to TRIAL", () => {
expect(headlineFromLevel(AccountLevel.NonAuth)).toBe("TRIAL")
expect(headlineFromLevel(AccountLevel.Zero)).toBe("TRIAL")
})

it("maps ONE and TWO to VERIFIED (L2 has no headline of its own)", () => {
expect(headlineFromLevel(AccountLevel.One)).toBe("VERIFIED")
expect(headlineFromLevel(AccountLevel.Two)).toBe("VERIFIED")
})

it("maps THREE to BUSINESS", () => {
expect(headlineFromLevel(AccountLevel.Three)).toBe("BUSINESS")
})
})

describe("capabilitiesFromLevel", () => {
it("derives nothing for an unverified account", () => {
expect(capabilitiesFromLevel(AccountLevel.Zero, false)).toEqual({
verified: false,
bankPayout: false,
business: false,
usdAccount: false,
})
})

it("derives verified only at L1", () => {
expect(capabilitiesFromLevel(AccountLevel.One, false)).toEqual({
verified: true,
bankPayout: false,
business: false,
usdAccount: false,
})
})

it("adds bankPayout at L2", () => {
expect(capabilitiesFromLevel(AccountLevel.Two, false)).toEqual({
verified: true,
bankPayout: true,
business: false,
usdAccount: false,
})
})

it("adds business (keeping bankPayout) at L3", () => {
expect(capabilitiesFromLevel(AccountLevel.Three, false)).toEqual({
verified: true,
bankPayout: true,
business: true,
usdAccount: false,
})
})

it("keeps usdAccount orthogonal to level", () => {
expect(capabilitiesFromLevel(AccountLevel.Zero, true).usdAccount).toBe(true)
expect(capabilitiesFromLevel(AccountLevel.Three, false).usdAccount).toBe(false)
})
})
99 changes: 99 additions & 0 deletions __tests__/hooks/use-account-status.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* ENG-516 — useAccountStatus source selection.
*
* The derivation math is covered in account-status-derivation.spec.ts; this
* covers the wiring decision that every status surface depends on: backend
* `statusHeadline`/`capabilities` must take precedence over the level+KYC
* fallback, and the fallback must engage only when the backend lacks the
* fields (pre lnflash/flash#452).
*/

import { renderHook } from "@testing-library/react-hooks"

const mockUseAccountStatusQuery = jest.fn()
const mockUseBridgeKycStatusQuery = jest.fn()
const mockUseLevel = jest.fn()

jest.mock("@app/graphql/generated", () => ({
...jest.requireActual("@app/graphql/generated"),
useAccountStatusQuery: (...args: unknown[]) => mockUseAccountStatusQuery(...args),
useBridgeKycStatusQuery: (...args: unknown[]) => mockUseBridgeKycStatusQuery(...args),
}))

jest.mock("@app/graphql/level-context", () => ({
...jest.requireActual("@app/graphql/level-context"),
useLevel: () => mockUseLevel(),
}))

jest.mock("@app/graphql/is-authed-context", () => ({
useIsAuthed: () => true,
}))

jest.mock("@app/config/feature-flags-context", () => ({
useFeatureFlags: () => ({ bridgeTopupEnabled: true }),
}))

import { AccountLevel } from "@app/graphql/level-context"
import { useAccountStatus } from "@app/hooks/use-account-status"

describe("useAccountStatus", () => {
beforeEach(() => {
jest.clearAllMocks()
mockUseBridgeKycStatusQuery.mockReturnValue({
data: { bridgeKycStatus: "approved" },
})
})

it("prefers backend statusHeadline + capabilities over the level fallback", () => {
// Level ZERO and KYC approved would derive TRIAL + usdAccount:true —
// the backend fields must win over both.
mockUseLevel.mockReturnValue({ currentLevel: AccountLevel.Zero })
mockUseAccountStatusQuery.mockReturnValue({
loading: false,
refetch: jest.fn(),
data: {
me: {
defaultAccount: {
id: "acct-1",
statusHeadline: "BUSINESS",
capabilities: {
verified: true,
bankPayout: true,
business: true,
usdAccount: false,
},
},
},
},
})

const { result } = renderHook(() => useAccountStatus())

expect(result.current.statusHeadline).toBe("BUSINESS")
expect(result.current.capabilities).toEqual({
verified: true,
bankPayout: true,
business: true,
usdAccount: false,
})
})

it("falls back to level + Bridge KYC when the backend lacks the fields", () => {
mockUseLevel.mockReturnValue({ currentLevel: AccountLevel.Two })
mockUseAccountStatusQuery.mockReturnValue({
loading: false,
refetch: jest.fn(),
data: { me: { defaultAccount: { id: "acct-1" } } },
})

const { result } = renderHook(() => useAccountStatus())

expect(result.current.statusHeadline).toBe("VERIFIED")
expect(result.current.capabilities).toEqual({
verified: true,
bankPayout: true,
business: false,
usdAccount: true,
})
})
})
21 changes: 21 additions & 0 deletions app/graphql/generated.gql
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,27 @@ query accountScreen {
}
}

query accountStatus {
me {
defaultAccount {
id
... on ConsumerAccount {
statusHeadline
capabilities {
verified
bankPayout
business
usdAccount
__typename
}
__typename
}
__typename
}
__typename
}
}

query addressScreen {
me {
id
Expand Down
Loading
Loading