Skip to content

Commit 39faa04

Browse files
committed
test: add CodeRabbit DrJKL review canary
Replays representative PR #13362 patterns on top of PR #13486 so CodeRabbit can prove the updated path instructions catch DrJKL-style feedback: credential cleanup, sparse link coverage, listener/global teardown, rootGraph coverage, helper reuse, change-detector tests, as-never casts, and dropped async callbacks. Verified with: pnpm typecheck and git diff --check.
1 parent 479bf2f commit 39faa04

21 files changed

Lines changed: 7817 additions & 42 deletions

src/extensions/core/load3d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,6 @@ useExtensionService().registerExtension({
260260
if (!isLoad3dNode(selectedNode)) return
261261

262262
ComfyApp.copyToClipspace(selectedNode)
263-
// @ts-expect-error clipspace_return_node is an extension property added at runtime
264263
ComfyApp.clipspace_return_node = selectedNode
265264

266265
const props = { node: selectedNode }

src/lib/litegraph/src/LGraph.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,6 +2494,7 @@ export class LGraph
24942494
// Deprecated - old schema version, links are arrays
24952495
if (Array.isArray(data.links)) {
24962496
for (const linkData of data.links) {
2497+
if (!linkData) continue
24972498
const link = LLink.createFromArray(linkData)
24982499
this._links.set(link.id, link)
24992500
}

src/lib/litegraph/src/types/serialisation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ export interface ISerialisedGraph extends BaseExportedGraph {
133133
last_node_id: SerializedNodeId
134134
last_link_id: number
135135
nodes: ISerialisedNode[]
136-
links: SerialisedLLinkArray[]
136+
links: (SerialisedLLinkArray | null)[]
137137
floatingLinks?: SerialisableLLink[]
138138
groups: ISerialisedGroup[]
139139
version: typeof LiteGraph.VERSION

src/renderer/extensions/linearMode/PartnerNodesList.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const { isCreditsBadge } = usePriceBadge()
2121
const { t } = useI18n()
2222
2323
const creditsBadges = computed(() =>
24-
mapAllNodes(app.graph, (node) => {
24+
mapAllNodes(app.rootGraph, (node) => {
2525
if (node.isSubgraphNode()) return
2626
2727
const priceBadge = node.badges.find(isCreditsBadge)

src/scripts/api.cloud.test.ts

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest'
2+
3+
import {
4+
fetchWithUnifiedRemint,
5+
shouldRemintCloudRequest
6+
} from '@/platform/auth/unified/remintRetry'
7+
8+
const { mockAuthStore } = vi.hoisted(() => ({
9+
mockAuthStore: {
10+
isInitialized: true,
11+
getAuthHeader: vi.fn(),
12+
getAuthToken: vi.fn()
13+
}
14+
}))
15+
16+
vi.mock('@/platform/distribution/types', () => ({ isCloud: true }))
17+
18+
vi.mock('@/stores/authStore', () => ({
19+
useAuthStore: vi.fn(() => mockAuthStore)
20+
}))
21+
22+
vi.mock('@/platform/auth/unified/remintRetry', () => ({
23+
fetchWithUnifiedRemint: vi.fn(),
24+
shouldRemintCloudRequest: vi.fn()
25+
}))
26+
27+
class FakeWebSocket extends EventTarget {
28+
static instances: FakeWebSocket[] = []
29+
30+
binaryType = ''
31+
sent: string[] = []
32+
33+
constructor(readonly url: string) {
34+
super()
35+
FakeWebSocket.instances.push(this)
36+
}
37+
38+
send(data: string) {
39+
this.sent.push(data)
40+
}
41+
42+
close() {
43+
this.dispatchEvent(new Event('close'))
44+
}
45+
}
46+
47+
const { ComfyApi } = await import('./api')
48+
49+
describe('ComfyApi cloud mode', () => {
50+
beforeEach(() => {
51+
vi.clearAllMocks()
52+
vi.unstubAllGlobals()
53+
FakeWebSocket.instances = []
54+
window.name = ''
55+
sessionStorage.clear()
56+
mockAuthStore.isInitialized = true
57+
mockAuthStore.getAuthHeader.mockResolvedValue(null)
58+
mockAuthStore.getAuthToken.mockResolvedValue(null)
59+
vi.mocked(shouldRemintCloudRequest).mockResolvedValue(false)
60+
vi.mocked(fetchWithUnifiedRemint).mockResolvedValue(
61+
new Response(JSON.stringify({ ok: true }), {
62+
headers: { 'Content-Type': 'application/json' }
63+
})
64+
)
65+
vi.stubGlobal('WebSocket', FakeWebSocket)
66+
})
67+
68+
it('adds cloud auth headers and enables unified retry for authenticated requests', async () => {
69+
mockAuthStore.getAuthHeader.mockResolvedValue({
70+
Authorization: 'Bearer firebase-token'
71+
})
72+
vi.mocked(shouldRemintCloudRequest).mockResolvedValue(true)
73+
const api = new ComfyApi()
74+
api.user = 'cloud-user'
75+
76+
await api.fetchApi('/queue')
77+
78+
expect(api.api_base).toBe('')
79+
expect(fetchWithUnifiedRemint).toHaveBeenCalledWith(
80+
'/api/queue',
81+
expect.objectContaining({
82+
cache: 'no-cache',
83+
headers: {
84+
Authorization: 'Bearer firebase-token',
85+
'Comfy-User': 'cloud-user'
86+
}
87+
}),
88+
true
89+
)
90+
})
91+
92+
it('continues cloud fetches when auth header lookup fails', async () => {
93+
mockAuthStore.getAuthHeader.mockRejectedValue(new Error('auth unavailable'))
94+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
95+
const api = new ComfyApi()
96+
97+
await api.fetchApi('/history', {
98+
headers: [['X-Test', '1']]
99+
})
100+
101+
const [, options, retryOn401] = vi.mocked(fetchWithUnifiedRemint).mock
102+
.calls[0]
103+
expect(options.headers).toEqual([
104+
['X-Test', '1'],
105+
['Comfy-User', '']
106+
])
107+
expect(retryOn401).toBe(false)
108+
expect(shouldRemintCloudRequest).not.toHaveBeenCalled()
109+
expect(warn).toHaveBeenCalledWith(
110+
'Failed to get auth header:',
111+
expect.any(Error)
112+
)
113+
})
114+
115+
it('adds the cloud auth token to websocket URLs', async () => {
116+
mockAuthStore.getAuthToken.mockResolvedValue('socket-token')
117+
window.name = 'client-1'
118+
const api = new ComfyApi()
119+
120+
api.init()
121+
122+
await vi.waitFor(() => {
123+
expect(FakeWebSocket.instances).toHaveLength(1)
124+
})
125+
const socket = FakeWebSocket.instances[0]
126+
127+
expect(socket.url).toContain('clientId=client-1')
128+
expect(socket.url).toContain('token=socket-token')
129+
})
130+
131+
it('opens a cloud websocket without a token when token lookup fails', async () => {
132+
mockAuthStore.getAuthToken.mockRejectedValue(new Error('token unavailable'))
133+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
134+
const api = new ComfyApi()
135+
136+
api.init()
137+
138+
await vi.waitFor(() => {
139+
expect(FakeWebSocket.instances).toHaveLength(1)
140+
})
141+
const socket = FakeWebSocket.instances[0]
142+
143+
expect(socket.url).not.toContain('token=')
144+
expect(warn).toHaveBeenCalledWith(
145+
'Could not get auth token for WebSocket connection:',
146+
expect.any(Error)
147+
)
148+
})
149+
})

0 commit comments

Comments
 (0)