From d1cb0ea4376c94886ff4087702492fe00eae7d5c Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Wed, 28 Oct 2026 07:32:35 -0700 Subject: [PATCH] test(comfyui): each branch of a shared-prefix foreign run links to its own prompt (#247 CUJ 1) --- TESTING.md | 6 ++-- tests/comfyui/image-attribution.spec.mjs | 35 ++++++++++++++++++++++++ tests/vendor/comfyui-config.test.mjs | 6 ++-- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/TESTING.md b/TESTING.md index 7c457309..dfbe5c2b 100644 --- a/TESTING.md +++ b/TESTING.md @@ -5170,8 +5170,10 @@ settle lookup, a newer due reference kept, evictions logged, one link per file), `tests/test_settings_sources.py` `TestSaveForeignRunsSetting`, and the real-host `tests/comfyui/image-attribution.spec.mjs` case "an output of a workflow without a PromptManager node creates and links its prompt; a second run reuses it" (ComfyUI's -own `CLIPTextEncode`, `KSampler` and `SaveImage`; the fixture route's GET also lists -`foreign`, the waiting foreign files). +own `CLIPTextEncode`, `KSampler` and `SaveImage`), and "each branch of a +shared-prefix workflow without a PromptManager node links to its own prompt after the +run" (two branches, one save prefix: the recorded saver names each file's branch). +The fixture route's GET also lists `foreign`, the waiting foreign files. ## Linked prompt text (#343) diff --git a/tests/comfyui/image-attribution.spec.mjs b/tests/comfyui/image-attribution.spec.mjs index 7a8d8eeb..a6a12b47 100644 --- a/tests/comfyui/image-attribution.spec.mjs +++ b/tests/comfyui/image-attribution.spec.mjs @@ -168,3 +168,38 @@ test('an output of a workflow without a PromptManager node creates and links its // Decided from the graph at settle: nothing waits for the run's recorded outputs. expect((await (await execution.request.get('/pm_execution_fixture/monitor')).json()).foreign).toEqual([]); }); + +// CUJ 1: two branches saved under one prefix, so neither the graph nor the file name +// names a file's branch; the run's recorded saver does, once its outputs are sealed. +function sharedPrefixGraph(texts) { + const nodes = { + '10': { class_type: 'PMHistoryInputs', inputs: {} }, + '30': { class_type: 'CLIPTextEncode', inputs: { text: '', clip: ['10', 0] } }, + }; + texts.forEach((text, index) => { + const base = (index + 1) * 100; + nodes[String(base)] = { class_type: 'CLIPTextEncode', inputs: { text, clip: ['10', 0] } }; + nodes[String(base + 1)] = { class_type: 'KSampler', inputs: { model: ['10', 1], latent_image: ['10', 2], + positive: [String(base), 0], negative: ['30', 0], seed: index + 1, steps: 1, cfg: 1, sampler_name: 'euler', + scheduler: 'normal', denoise: 1 } }; + nodes[String(base + 2)] = { class_type: 'PMHistoryImages', inputs: { value: 64, batch: 1, latent: [String(base + 1), 0] } }; + nodes[String(base + 3)] = { class_type: 'SaveImage', inputs: { images: [String(base + 2), 0], filename_prefix: 'foreign/dataset' } }; + }); + return nodes; +} + +test('each branch of a shared-prefix workflow without a PromptManager node links to its own prompt after the run', async ({ execution }, testInfo) => { + const tag = `${testInfo.project.name} ${randomUUID()}`; + const texts = [`dataset branch one harbor ${tag}`, `dataset branch two meadow ${tag}`]; + const id = randomUUID(); + const response = await execution.request.post('/prompt', { data: { prompt: sharedPrefixGraph(texts), prompt_id: id } }); + expect(response.ok()).toBe(true); + let item; + await expect.poll(async () => { + item = (await (await execution.request.get(`/history/${id}`)).json())[id]; + return item?.status?.messages?.some(([kind]) => kind === 'execution_success') || false; + }, { timeout: 15000 }).toBe(true); + const files = ['103', '203'].map(node => item.outputs[node].images[0].filename); + for (const [index, file] of files.entries()) await linkedTo(execution, file, texts[index]); + expect((await (await execution.request.get('/pm_execution_fixture/monitor')).json()).foreign).toEqual([]); +}); diff --git a/tests/vendor/comfyui-config.test.mjs b/tests/vendor/comfyui-config.test.mjs index a213d302..5057475e 100644 --- a/tests/vendor/comfyui-config.test.mjs +++ b/tests/vendor/comfyui-config.test.mjs @@ -33,14 +33,14 @@ test('execution script discovers only the new matrix and preserves all 78 origin const executions = list('tools/vendor/playwright.execution.config.mjs'); assert.match(executions, /\[classic-start\]/); assert.match(executions, /\[nodes2-start\]/); - // + the fresh-host library location case, + #342 image attribution, + #343 linked text, + #247 foreign workflow - assert.match(executions, /Total: 92 tests in 5 files/); + // + the fresh-host library location case, + #342 image attribution, + #343 linked text, + #247 foreign workflows + assert.match(executions, /Total: 94 tests in 5 files/); for (const project of ['classic-start', 'nodes2-start']) { const cases = executions.split('\n').filter(line => line.includes(`[${project}]`)); assert.equal(cases.filter(line => line.includes('execution-history.spec.mjs')).length, 15); assert.equal(cases.filter(line => line.includes('execution-outputs.spec.mjs')).length, 8); assert.equal(cases.filter(line => line.includes('library-location.spec.mjs')).length, 1); - assert.equal(cases.filter(line => line.includes('image-attribution.spec.mjs')).length, 3); + assert.equal(cases.filter(line => line.includes('image-attribution.spec.mjs')).length, 4); assert.equal(cases.filter(line => line.includes('execution-text-link.spec.mjs')).length, 19); } assert.doesNotMatch(executions, /prompt-manager.spec/);