@@ -11,6 +11,8 @@ vi.mock('@/lib/internal/oci-vision/operations', () => ({
1111import { OciClientError } from '@/lib/internal/oci/errors'
1212import { executeOciVisionTool } from '@/lib/internal/oci-vision/execute-tool'
1313import type { InternalToolOperationCall } from '@/lib/internal/tool-operations/types'
14+ import { OciVisionBlock } from '@/blocks/blocks/oci_vision'
15+ import { ociVisionAnalyzeImageTool } from '@/tools/oci_vision/analyze_image'
1416
1517function request ( overrides : Partial < InternalToolOperationCall > = { } ) : InternalToolOperationCall {
1618 return {
@@ -29,6 +31,93 @@ describe('OCI Vision internal dispatch', () => {
2931 executeOperation . mockResolvedValue ( { success : true , output : { job : { id : 'job-1' } } } )
3032 } )
3133
34+ it . each ( [ null , '' , undefined ] ) (
35+ 'omits blank and inactive feature controls after the native parameter merge (%s)' ,
36+ async ( blank ) => {
37+ const raw = {
38+ operation : 'analyze_image' ,
39+ oauthCredential : 'selected' ,
40+ source : 'object_storage' ,
41+ namespaceName : 'namespace' ,
42+ bucketName : 'images' ,
43+ imageObjectName : 'image.png' ,
44+ features : [ 'TEXT_DETECTION' ] ,
45+ language : 'ENG' ,
46+ compartmentId : blank ,
47+ faceMaxResults : blank ,
48+ shouldReturnLandmarks : false ,
49+ classificationMaxResults : blank ,
50+ objectDetectionMaxResults : 5 ,
51+ }
52+ const params = {
53+ ...raw ,
54+ ...OciVisionBlock . tools . config ?. params ?.( raw ) ,
55+ accessToken : 'resolved' ,
56+ }
57+ const response = await executeOciVisionTool (
58+ request ( {
59+ toolId : ociVisionAnalyzeImageTool . id ,
60+ input : ociVisionAnalyzeImageTool . operation . input ( params ) ,
61+ } )
62+ )
63+ expect ( response . status ) . toBe ( 200 )
64+ expect ( executeOperation ) . toHaveBeenCalledWith (
65+ expect . objectContaining ( {
66+ credentialId : 'resolved' ,
67+ features : [ 'TEXT_DETECTION' ] ,
68+ language : 'ENG' ,
69+ } ) ,
70+ expect . anything ( )
71+ )
72+ for ( const field of [
73+ 'compartmentId' ,
74+ 'faceMaxResults' ,
75+ 'shouldReturnLandmarks' ,
76+ 'classificationMaxResults' ,
77+ 'objectDetectionMaxResults' ,
78+ ] )
79+ expect ( executeOperation . mock . lastCall ?. [ 0 ] [ field ] ) . toBeUndefined ( )
80+ }
81+ )
82+
83+ it ( 'preserves active false controls and rejects invalid nonempty feature limits' , async ( ) => {
84+ const raw = {
85+ operation : 'analyze_image' ,
86+ source : 'object_storage' ,
87+ namespaceName : 'namespace' ,
88+ bucketName : 'images' ,
89+ imageObjectName : 'image.png' ,
90+ features : [ 'FACE_DETECTION' ] ,
91+ shouldReturnLandmarks : false ,
92+ faceMaxResults : '2' ,
93+ }
94+ const params = {
95+ ...raw ,
96+ ...OciVisionBlock . tools . config ?. params ?.( raw ) ,
97+ accessToken : 'resolved' ,
98+ }
99+ expect (
100+ (
101+ await executeOciVisionTool (
102+ request ( {
103+ toolId : ociVisionAnalyzeImageTool . id ,
104+ input : ociVisionAnalyzeImageTool . operation . input ( params ) ,
105+ } )
106+ )
107+ ) . status
108+ ) . toBe ( 200 )
109+ expect ( executeOperation ) . toHaveBeenCalledWith (
110+ expect . objectContaining ( {
111+ shouldReturnLandmarks : false ,
112+ faceMaxResults : 2 ,
113+ } ) ,
114+ expect . anything ( )
115+ )
116+ expect ( ( ) =>
117+ OciVisionBlock . tools . config ?. params ?.( { ...raw , faceMaxResults : 'invalid' } )
118+ ) . toThrow ( )
119+ } )
120+
32121 it ( 'uses trusted context and ignores workspace or credential aliases in input' , async ( ) => {
33122 const response = await executeOciVisionTool (
34123 request ( {
0 commit comments