@@ -37,6 +37,204 @@ module VCAP::CloudController
3737 end
3838 end
3939
40+ describe 'allowed_sources validations' do
41+ context 'when app_to_app_mtls_routing feature flag is disabled' do
42+ it 'does not allow allowed_sources option' do
43+ message = RouteOptionsMessage . new ( { allowed_sources : { apps : [ 'app-guid-1' ] } } )
44+ expect ( message ) . not_to be_valid
45+ expect ( message . errors_on ( :base ) ) . to include ( "Unknown field(s): 'allowed_sources'" )
46+ end
47+ end
48+
49+ context 'when app_to_app_mtls_routing feature flag is enabled' do
50+ before do
51+ VCAP ::CloudController ::FeatureFlag . make ( name : 'app_to_app_mtls_routing' , enabled : true )
52+ end
53+
54+ describe 'structure validation' do
55+ it 'allows valid allowed_sources with apps' do
56+ app = AppModel . make
57+ message = RouteOptionsMessage . new ( { allowed_sources : { 'apps' => [ app . guid ] } } )
58+ expect ( message ) . to be_valid
59+ end
60+
61+ it 'allows valid allowed_sources with spaces' do
62+ space = Space . make
63+ message = RouteOptionsMessage . new ( { allowed_sources : { 'spaces' => [ space . guid ] } } )
64+ expect ( message ) . to be_valid
65+ end
66+
67+ it 'allows valid allowed_sources with orgs' do
68+ org = Organization . make
69+ message = RouteOptionsMessage . new ( { allowed_sources : { 'orgs' => [ org . guid ] } } )
70+ expect ( message ) . to be_valid
71+ end
72+
73+ it 'allows valid allowed_sources with any: true' do
74+ message = RouteOptionsMessage . new ( { allowed_sources : { 'any' => true } } )
75+ expect ( message ) . to be_valid
76+ end
77+
78+ it 'allows valid allowed_sources with any: false' do
79+ message = RouteOptionsMessage . new ( { allowed_sources : { 'any' => false } } )
80+ expect ( message ) . to be_valid
81+ end
82+
83+ it 'allows empty allowed_sources object' do
84+ message = RouteOptionsMessage . new ( { allowed_sources : { } } )
85+ expect ( message ) . to be_valid
86+ end
87+
88+ it 'does not allow non-object allowed_sources' do
89+ message = RouteOptionsMessage . new ( { allowed_sources : 'invalid' } )
90+ expect ( message ) . not_to be_valid
91+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'must be an object' )
92+ end
93+
94+ it 'does not allow array allowed_sources' do
95+ message = RouteOptionsMessage . new ( { allowed_sources : [ 'app-guid-1' ] } )
96+ expect ( message ) . not_to be_valid
97+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'must be an object' )
98+ end
99+
100+ it 'does not allow invalid keys in allowed_sources' do
101+ message = RouteOptionsMessage . new ( { allowed_sources : { 'invalid_key' => 'value' } } )
102+ expect ( message ) . not_to be_valid
103+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'contains invalid keys: invalid_key' )
104+ end
105+
106+ it 'does not allow non-array apps' do
107+ message = RouteOptionsMessage . new ( { allowed_sources : { 'apps' => 'not-an-array' } } )
108+ expect ( message ) . not_to be_valid
109+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'apps must be an array of strings' )
110+ end
111+
112+ it 'does not allow non-string elements in apps array' do
113+ message = RouteOptionsMessage . new ( { allowed_sources : { 'apps' => [ 123 , 456 ] } } )
114+ expect ( message ) . not_to be_valid
115+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'apps must be an array of strings' )
116+ end
117+
118+ it 'does not allow non-array spaces' do
119+ message = RouteOptionsMessage . new ( { allowed_sources : { 'spaces' => 'not-an-array' } } )
120+ expect ( message ) . not_to be_valid
121+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'spaces must be an array of strings' )
122+ end
123+
124+ it 'does not allow non-array orgs' do
125+ message = RouteOptionsMessage . new ( { allowed_sources : { 'orgs' => 'not-an-array' } } )
126+ expect ( message ) . not_to be_valid
127+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'orgs must be an array of strings' )
128+ end
129+
130+ it 'does not allow non-boolean any' do
131+ message = RouteOptionsMessage . new ( { allowed_sources : { 'any' => 'true' } } )
132+ expect ( message ) . not_to be_valid
133+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'any must be a boolean' )
134+ end
135+ end
136+
137+ describe 'any exclusivity validation' do
138+ it 'does not allow any: true with apps list' do
139+ app = AppModel . make
140+ message = RouteOptionsMessage . new ( { allowed_sources : { 'any' => true , 'apps' => [ app . guid ] } } )
141+ expect ( message ) . not_to be_valid
142+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'any is mutually exclusive with apps, spaces, and orgs' )
143+ end
144+
145+ it 'does not allow any: true with spaces list' do
146+ space = Space . make
147+ message = RouteOptionsMessage . new ( { allowed_sources : { 'any' => true , 'spaces' => [ space . guid ] } } )
148+ expect ( message ) . not_to be_valid
149+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'any is mutually exclusive with apps, spaces, and orgs' )
150+ end
151+
152+ it 'does not allow any: true with orgs list' do
153+ org = Organization . make
154+ message = RouteOptionsMessage . new ( { allowed_sources : { 'any' => true , 'orgs' => [ org . guid ] } } )
155+ expect ( message ) . not_to be_valid
156+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'any is mutually exclusive with apps, spaces, and orgs' )
157+ end
158+
159+ it 'allows any: false with apps list' do
160+ app = AppModel . make
161+ message = RouteOptionsMessage . new ( { allowed_sources : { 'any' => false , 'apps' => [ app . guid ] } } )
162+ expect ( message ) . to be_valid
163+ end
164+
165+ it 'allows any: true with empty apps list' do
166+ message = RouteOptionsMessage . new ( { allowed_sources : { 'any' => true , 'apps' => [ ] } } )
167+ expect ( message ) . to be_valid
168+ end
169+ end
170+
171+ describe 'GUID existence validation' do
172+ it 'validates that app GUIDs exist' do
173+ message = RouteOptionsMessage . new ( { allowed_sources : { 'apps' => [ 'non-existent-app-guid' ] } } )
174+ expect ( message ) . not_to be_valid
175+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'apps contains non-existent app GUIDs: non-existent-app-guid' )
176+ end
177+
178+ it 'validates that space GUIDs exist' do
179+ message = RouteOptionsMessage . new ( { allowed_sources : { 'spaces' => [ 'non-existent-space-guid' ] } } )
180+ expect ( message ) . not_to be_valid
181+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'spaces contains non-existent space GUIDs: non-existent-space-guid' )
182+ end
183+
184+ it 'validates that org GUIDs exist' do
185+ message = RouteOptionsMessage . new ( { allowed_sources : { 'orgs' => [ 'non-existent-org-guid' ] } } )
186+ expect ( message ) . not_to be_valid
187+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'orgs contains non-existent organization GUIDs: non-existent-org-guid' )
188+ end
189+
190+ it 'reports multiple non-existent app GUIDs' do
191+ message = RouteOptionsMessage . new ( { allowed_sources : { 'apps' => [ 'guid-1' , 'guid-2' ] } } )
192+ expect ( message ) . not_to be_valid
193+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'apps contains non-existent app GUIDs: guid-1, guid-2' )
194+ end
195+
196+ it 'allows mix of existing apps, spaces, and orgs' do
197+ app = AppModel . make
198+ space = Space . make
199+ org = Organization . make
200+ message = RouteOptionsMessage . new ( {
201+ allowed_sources : {
202+ 'apps' => [ app . guid ] ,
203+ 'spaces' => [ space . guid ] ,
204+ 'orgs' => [ org . guid ]
205+ }
206+ } )
207+ expect ( message ) . to be_valid
208+ end
209+
210+ it 'validates all types of GUIDs when multiple are provided' do
211+ app = AppModel . make
212+ message = RouteOptionsMessage . new ( {
213+ allowed_sources : {
214+ 'apps' => [ app . guid ] ,
215+ 'spaces' => [ 'non-existent-space' ] ,
216+ 'orgs' => [ 'non-existent-org' ]
217+ }
218+ } )
219+ expect ( message ) . not_to be_valid
220+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'spaces contains non-existent space GUIDs: non-existent-space' )
221+ expect ( message . errors_on ( :allowed_sources ) ) . to include ( 'orgs contains non-existent organization GUIDs: non-existent-org' )
222+ end
223+ end
224+
225+ describe 'combined with other options' do
226+ it 'allows allowed_sources with loadbalancing' do
227+ app = AppModel . make
228+ message = RouteOptionsMessage . new ( {
229+ loadbalancing : 'round-robin' ,
230+ allowed_sources : { 'apps' => [ app . guid ] }
231+ } )
232+ expect ( message ) . to be_valid
233+ end
234+ end
235+ end
236+ end
237+
40238 describe 'hash-based routing validations' do
41239 context 'when hash_based_routing feature flag is disabled' do
42240 it 'does not allow hash_header option' do
0 commit comments