@@ -301,7 +301,117 @@ describe('useNodePricing', () => {
301
301
expect ( price ) . toBe ( '$0.04-0.12/Run (varies with size & quality)' )
302
302
} )
303
303
} )
304
+ // ============================== OpenAIVideoSora2 ==============================
305
+ describe ( 'dynamic pricing - OpenAIVideoSora2' , ( ) => {
306
+ it ( 'should require model, duration & size when widgets are missing' , ( ) => {
307
+ const { getNodeDisplayPrice } = useNodePricing ( )
308
+ const node = createMockNode ( 'OpenAIVideoSora2' , [ ] )
309
+ expect ( getNodeDisplayPrice ( node ) ) . toBe ( 'Set model, duration & size' )
310
+ } )
311
+
312
+ it ( 'should require duration when duration is invalid or zero' , ( ) => {
313
+ const { getNodeDisplayPrice } = useNodePricing ( )
314
+ const nodeNaN = createMockNode ( 'OpenAIVideoSora2' , [
315
+ { name : 'model' , value : 'sora-2-pro' } ,
316
+ { name : 'duration' , value : 'oops' } ,
317
+ { name : 'size' , value : '720x1280' }
318
+ ] )
319
+ expect ( getNodeDisplayPrice ( nodeNaN ) ) . toBe ( 'Set duration (4/8/12)' )
320
+
321
+ const nodeZero = createMockNode ( 'OpenAIVideoSora2' , [
322
+ { name : 'model' , value : 'sora-2-pro' } ,
323
+ { name : 'duration' , value : 0 } ,
324
+ { name : 'size' , value : '720x1280' }
325
+ ] )
326
+ expect ( getNodeDisplayPrice ( nodeZero ) ) . toBe ( 'Set duration (4/8/12)' )
327
+ } )
328
+
329
+ it ( 'should require size when size is missing' , ( ) => {
330
+ const { getNodeDisplayPrice } = useNodePricing ( )
331
+ const node = createMockNode ( 'OpenAIVideoSora2' , [
332
+ { name : 'model' , value : 'sora-2-pro' } ,
333
+ { name : 'duration' , value : 8 }
334
+ ] )
335
+ expect ( getNodeDisplayPrice ( node ) ) . toBe (
336
+ 'Set size (720x1280, 1280x720, 1024x1792, 1792x1024)'
337
+ )
338
+ } )
339
+
340
+ it ( 'should compute pricing for sora-2-pro with 1024x1792' , ( ) => {
341
+ const { getNodeDisplayPrice } = useNodePricing ( )
342
+ const node = createMockNode ( 'OpenAIVideoSora2' , [
343
+ { name : 'model' , value : 'sora-2-pro' } ,
344
+ { name : 'duration' , value : 8 } ,
345
+ { name : 'size' , value : '1024x1792' }
346
+ ] )
347
+ expect ( getNodeDisplayPrice ( node ) ) . toBe ( '$4.00/Run' ) // 0.5 * 8
348
+ } )
349
+
350
+ it ( 'should compute pricing for sora-2-pro with 720x1280' , ( ) => {
351
+ const { getNodeDisplayPrice } = useNodePricing ( )
352
+ const node = createMockNode ( 'OpenAIVideoSora2' , [
353
+ { name : 'model' , value : 'sora-2-pro' } ,
354
+ { name : 'duration' , value : 12 } ,
355
+ { name : 'size' , value : '720x1280' }
356
+ ] )
357
+ expect ( getNodeDisplayPrice ( node ) ) . toBe ( '$3.60/Run' ) // 0.3 * 12
358
+ } )
359
+
360
+ it ( 'should reject unsupported size for sora-2-pro' , ( ) => {
361
+ const { getNodeDisplayPrice } = useNodePricing ( )
362
+ const node = createMockNode ( 'OpenAIVideoSora2' , [
363
+ { name : 'model' , value : 'sora-2-pro' } ,
364
+ { name : 'duration' , value : 8 } ,
365
+ { name : 'size' , value : '640x640' }
366
+ ] )
367
+ expect ( getNodeDisplayPrice ( node ) ) . toBe (
368
+ 'Size must be 720x1280, 1280x720, 1024x1792, or 1792x1024'
369
+ )
370
+ } )
371
+
372
+ it ( 'should compute pricing for sora-2 (720x1280 only)' , ( ) => {
373
+ const { getNodeDisplayPrice } = useNodePricing ( )
374
+ const node = createMockNode ( 'OpenAIVideoSora2' , [
375
+ { name : 'model' , value : 'sora-2' } ,
376
+ { name : 'duration' , value : 10 } ,
377
+ { name : 'size' , value : '720x1280' }
378
+ ] )
379
+ expect ( getNodeDisplayPrice ( node ) ) . toBe ( '$1.00/Run' ) // 0.1 * 10
380
+ } )
381
+
382
+ it ( 'should reject non-720 sizes for sora-2' , ( ) => {
383
+ const { getNodeDisplayPrice } = useNodePricing ( )
384
+ const node = createMockNode ( 'OpenAIVideoSora2' , [
385
+ { name : 'model' , value : 'sora-2' } ,
386
+ { name : 'duration' , value : 8 } ,
387
+ { name : 'size' , value : '1024x1792' }
388
+ ] )
389
+ expect ( getNodeDisplayPrice ( node ) ) . toBe (
390
+ 'sora-2 supports only 720x1280 or 1280x720'
391
+ )
392
+ } )
393
+ it ( 'should accept duration_s alias for duration' , ( ) => {
394
+ const { getNodeDisplayPrice } = useNodePricing ( )
395
+ const node = createMockNode ( 'OpenAIVideoSora2' , [
396
+ { name : 'model' , value : 'sora-2-pro' } ,
397
+ { name : 'duration_s' , value : 4 } ,
398
+ { name : 'size' , value : '1792x1024' }
399
+ ] )
400
+ expect ( getNodeDisplayPrice ( node ) ) . toBe ( '$2.00/Run' ) // 0.5 * 4
401
+ } )
402
+
403
+ it ( 'should be case-insensitive for model and size' , ( ) => {
404
+ const { getNodeDisplayPrice } = useNodePricing ( )
405
+ const node = createMockNode ( 'OpenAIVideoSora2' , [
406
+ { name : 'model' , value : 'SoRa-2-PrO' } ,
407
+ { name : 'duration' , value : 12 } ,
408
+ { name : 'size' , value : '1280x720' }
409
+ ] )
410
+ expect ( getNodeDisplayPrice ( node ) ) . toBe ( '$3.60/Run' ) // 0.3 * 12
411
+ } )
412
+ } )
304
413
414
+ // ============================== MinimaxHailuoVideoNode ==============================
305
415
describe ( 'dynamic pricing - MinimaxHailuoVideoNode' , ( ) => {
306
416
it ( 'should return $0.28 for 6s duration and 768P resolution' , ( ) => {
307
417
const { getNodeDisplayPrice } = useNodePricing ( )
0 commit comments