Skip to content

Commit 223902c

Browse files
committed
Add unit tests for allowed_sources validation
Tests cover: - Feature flag disabled: allowed_sources rejected as unknown field - Structure validation: object type, valid keys, array types, boolean any - any exclusivity: cannot combine any:true with apps/spaces/orgs lists - GUID existence validation: apps, spaces, orgs must exist in database - Combined options: allowed_sources works with loadbalancing
1 parent 5640df7 commit 223902c

1 file changed

Lines changed: 198 additions & 0 deletions

File tree

spec/unit/messages/route_options_message_spec.rb

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)