修复音效直连回包越账号刷新钱包
为 SFX 直连生成回包的钱包刷新补上账号门禁,与排队路径和 BGM 分支口径一致。 新增直连回包成功、失败两条切账号回归测试和一条同账号刷新对照测试。
This commit is contained in:
@@ -155,6 +155,31 @@ function createAudioGenerated(overrides = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
function createSoundEffectWalletDialog(
|
||||
id: string,
|
||||
prompt: string,
|
||||
): GenerateDialogState {
|
||||
return {
|
||||
id,
|
||||
mode: 'audio-sound-effect',
|
||||
prompt,
|
||||
status: 'idle',
|
||||
composerOpen: true,
|
||||
soundModel: 'eleven_text_to_sound_v2',
|
||||
soundDurationMode: 'auto',
|
||||
soundDurationSeconds: 5,
|
||||
soundLoop: false,
|
||||
placeholder: {
|
||||
x: 200,
|
||||
y: 160,
|
||||
width: 420,
|
||||
height: 120,
|
||||
originalWidth: 420,
|
||||
originalHeight: 120,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createIconResult(name: string, imageSrc: string) {
|
||||
return {
|
||||
name,
|
||||
@@ -2636,6 +2661,133 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('refreshes the wallet for an inline sound effect response while the account stays current', async () => {
|
||||
const generation =
|
||||
createDeferred<ReturnType<typeof createAudioGenerated>>();
|
||||
const refreshWalletBalance = vi.fn();
|
||||
generateEditorSoundEffectMock.mockReturnValueOnce(generation.promise);
|
||||
render(
|
||||
<SubmissionWorkflowHarness
|
||||
currentUserId="user-a"
|
||||
projectId="project-a"
|
||||
onWalletBalanceMayHaveChanged={refreshWalletBalance}
|
||||
initialDialog={createSoundEffectWalletDialog(
|
||||
'dialog-sfx-wallet-current',
|
||||
'账号 A 的音效',
|
||||
)}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '设置初始对话' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '提交当前生成' }));
|
||||
|
||||
await act(async () => {
|
||||
// 直连回包:不带 queueState,钱包刷新由共享 wrapper 立即触发。
|
||||
generation.resolve(
|
||||
createAudioGenerated({
|
||||
prompt: '账号 A 的音效',
|
||||
audioKind: 'sound-effect',
|
||||
}),
|
||||
);
|
||||
await generation.promise;
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(refreshWalletBalance).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not refresh the previous account wallet when an inline sound effect response lands after the account changes', async () => {
|
||||
const generation =
|
||||
createDeferred<ReturnType<typeof createAudioGenerated>>();
|
||||
const refreshWalletBalance = vi.fn();
|
||||
generateEditorSoundEffectMock.mockReturnValueOnce(generation.promise);
|
||||
const initialDialog = createSoundEffectWalletDialog(
|
||||
'dialog-sfx-wallet-account-switch',
|
||||
'账号 A 的音效',
|
||||
);
|
||||
const view = render(
|
||||
<SubmissionWorkflowHarness
|
||||
currentUserId="user-a"
|
||||
projectId="project-a"
|
||||
onWalletBalanceMayHaveChanged={refreshWalletBalance}
|
||||
initialDialog={initialDialog}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '设置初始对话' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '提交当前生成' }));
|
||||
view.rerender(
|
||||
<SubmissionWorkflowHarness
|
||||
currentUserId="user-b"
|
||||
projectId="project-a"
|
||||
onWalletBalanceMayHaveChanged={refreshWalletBalance}
|
||||
initialDialog={{
|
||||
...initialDialog,
|
||||
prompt: '账号 B 的音效',
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(screen.getByRole('button', { name: '设置初始对话' }));
|
||||
|
||||
await act(async () => {
|
||||
generation.resolve(
|
||||
createAudioGenerated({
|
||||
prompt: '账号 A 的音效',
|
||||
audioKind: 'sound-effect',
|
||||
}),
|
||||
);
|
||||
await generation.promise;
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(refreshWalletBalance).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not refresh the previous account wallet when an inline sound effect request fails after the account changes', async () => {
|
||||
const generation =
|
||||
createDeferred<ReturnType<typeof createAudioGenerated>>();
|
||||
const refreshWalletBalance = vi.fn();
|
||||
generateEditorSoundEffectMock.mockReturnValueOnce(generation.promise);
|
||||
const initialDialog = createSoundEffectWalletDialog(
|
||||
'dialog-sfx-wallet-account-switch-failure',
|
||||
'账号 A 的音效',
|
||||
);
|
||||
const view = render(
|
||||
<SubmissionWorkflowHarness
|
||||
currentUserId="user-a"
|
||||
projectId="project-a"
|
||||
onWalletBalanceMayHaveChanged={refreshWalletBalance}
|
||||
initialDialog={initialDialog}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '设置初始对话' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '提交当前生成' }));
|
||||
view.rerender(
|
||||
<SubmissionWorkflowHarness
|
||||
currentUserId="user-b"
|
||||
projectId="project-a"
|
||||
onWalletBalanceMayHaveChanged={refreshWalletBalance}
|
||||
initialDialog={{
|
||||
...initialDialog,
|
||||
prompt: '账号 B 的音效',
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(screen.getByRole('button', { name: '设置初始对话' }));
|
||||
|
||||
await act(async () => {
|
||||
generation.reject(new Error('音效生成失败'));
|
||||
await generation.promise.catch(() => undefined);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(refreshWalletBalance).not.toHaveBeenCalled();
|
||||
expect(screen.getByTestId('tracked-dialog').textContent).toBe(
|
||||
'audio-sound-effect:账号 B 的音效:idle:open:-',
|
||||
);
|
||||
});
|
||||
|
||||
it('does not write a background music response after the scope changes away and back before acceptance', async () => {
|
||||
const generation =
|
||||
createDeferred<ReturnType<typeof createAudioGenerated>>();
|
||||
|
||||
@@ -2182,6 +2182,20 @@ export function useImageCanvasGenerationSubmissionWorkflow({
|
||||
} else if (submissionPlan.kind === 'audio') {
|
||||
const canvasCompletionPlaceholder =
|
||||
getGeneratingDialogPlaceholder(dialog);
|
||||
// 直连回包的钱包刷新由共享 wrapper 触发,而 wrapper 不持有本次 submission scope。
|
||||
// 这里补上与排队路径和 BGM 分支同一道账号门禁:切号后迟到的旧回包不得刷新新账号钱包。
|
||||
const refreshSoundEffectWalletBalance =
|
||||
soundEffectSubmission && onWalletBalanceMayHaveChanged
|
||||
? () => {
|
||||
if (
|
||||
isBackgroundMusicSubmissionAccountCurrent(
|
||||
soundEffectSubmission,
|
||||
)
|
||||
) {
|
||||
onWalletBalanceMayHaveChanged();
|
||||
}
|
||||
}
|
||||
: onWalletBalanceMayHaveChanged;
|
||||
const generated =
|
||||
submissionPlan.audioKind === 'sound-effect'
|
||||
? await runEditorGenerationWithWalletRefresh(
|
||||
@@ -2201,7 +2215,7 @@ export function useImageCanvasGenerationSubmissionWorkflow({
|
||||
}
|
||||
: {}),
|
||||
}),
|
||||
onWalletBalanceMayHaveChanged,
|
||||
refreshSoundEffectWalletBalance,
|
||||
)
|
||||
: await generateEditorBackgroundMusic({
|
||||
...submissionPlan.input,
|
||||
|
||||
Reference in New Issue
Block a user