|
| 1 | +require 'spec_helper' |
| 2 | +require 'request_spec_shared_examples' |
| 3 | +require_relative 'shared_context' |
| 4 | + |
| 5 | +# Split from spec/request/routes_spec.rb for better test parallelization |
| 6 | + |
| 7 | +RSpec.describe 'Routes Request' do |
| 8 | + include_context 'routes request spec' |
| 9 | + |
| 10 | + describe 'GET /v3/apps/:app_guid/routes' do |
| 11 | + let(:app_model) { VCAP::CloudController::AppModel.make(space:) } |
| 12 | + let(:route1) { VCAP::CloudController::Route.make(space:) } |
| 13 | + let(:route2) { VCAP::CloudController::Route.make(space:) } |
| 14 | + let!(:route3) { VCAP::CloudController::Route.make(space:) } |
| 15 | + let!(:route_mapping1) { VCAP::CloudController::RouteMappingModel.make(app: app_model, route: route1, process_type: 'web') } |
| 16 | + let!(:route_mapping2) { VCAP::CloudController::RouteMappingModel.make(app: app_model, route: route2, process_type: 'admin') } |
| 17 | + let(:api_call) { ->(user_headers) { get "/v3/apps/#{app_model.guid}/routes", nil, user_headers } } |
| 18 | + |
| 19 | + let(:route1_json) do |
| 20 | + { |
| 21 | + guid: route1.guid, |
| 22 | + protocol: route1.domain.protocols[0], |
| 23 | + host: route1.host, |
| 24 | + path: route1.path, |
| 25 | + port: nil, |
| 26 | + url: "#{route1.host}.#{route1.domain.name}#{route1.path}", |
| 27 | + created_at: iso8601, |
| 28 | + updated_at: iso8601, |
| 29 | + destinations: contain_exactly({ |
| 30 | + guid: route_mapping1.guid, |
| 31 | + app: { |
| 32 | + guid: app_model.guid, |
| 33 | + process: { |
| 34 | + type: route_mapping1.process_type |
| 35 | + } |
| 36 | + }, |
| 37 | + weight: route_mapping1.weight, |
| 38 | + port: route_mapping1.presented_port, |
| 39 | + protocol: 'http1', |
| 40 | + created_at: iso8601, |
| 41 | + updated_at: iso8601 |
| 42 | + }), |
| 43 | + relationships: { |
| 44 | + space: { |
| 45 | + data: { guid: route1.space.guid } |
| 46 | + }, |
| 47 | + domain: { |
| 48 | + data: { guid: route1.domain.guid } |
| 49 | + } |
| 50 | + }, |
| 51 | + metadata: { |
| 52 | + labels: {}, |
| 53 | + annotations: {} |
| 54 | + }, |
| 55 | + links: { |
| 56 | + self: { href: %r{#{Regexp.escape(link_prefix)}/v3/routes/#{route1.guid}} }, |
| 57 | + space: { href: %r{#{Regexp.escape(link_prefix)}/v3/spaces/#{route1.space.guid}} }, |
| 58 | + destinations: { href: %r{#{Regexp.escape(link_prefix)}/v3/routes/#{route1.guid}/destinations} }, |
| 59 | + domain: { href: %r{#{Regexp.escape(link_prefix)}/v3/domains/#{route1.domain.guid}} } |
| 60 | + }, |
| 61 | + options: {} |
| 62 | + } |
| 63 | + end |
| 64 | + |
| 65 | + let(:route2_json) do |
| 66 | + { |
| 67 | + guid: route2.guid, |
| 68 | + protocol: route2.domain.protocols[0], |
| 69 | + host: route2.host, |
| 70 | + path: route2.path, |
| 71 | + port: nil, |
| 72 | + url: "#{route2.host}.#{route2.domain.name}#{route2.path}", |
| 73 | + created_at: iso8601, |
| 74 | + updated_at: iso8601, |
| 75 | + destinations: contain_exactly({ |
| 76 | + guid: route_mapping2.guid, |
| 77 | + app: { |
| 78 | + guid: app_model.guid, |
| 79 | + process: { |
| 80 | + type: route_mapping2.process_type |
| 81 | + } |
| 82 | + }, |
| 83 | + weight: route_mapping2.weight, |
| 84 | + port: route_mapping2.presented_port, |
| 85 | + protocol: 'http1', |
| 86 | + created_at: iso8601, |
| 87 | + updated_at: iso8601 |
| 88 | + }), |
| 89 | + relationships: { |
| 90 | + space: { |
| 91 | + data: { guid: route2.space.guid } |
| 92 | + }, |
| 93 | + domain: { |
| 94 | + data: { guid: route2.domain.guid } |
| 95 | + } |
| 96 | + }, |
| 97 | + metadata: { |
| 98 | + labels: {}, |
| 99 | + annotations: {} |
| 100 | + }, |
| 101 | + links: { |
| 102 | + self: { href: %r{#{Regexp.escape(link_prefix)}/v3/routes/#{route2.guid}} }, |
| 103 | + space: { href: %r{#{Regexp.escape(link_prefix)}/v3/spaces/#{route2.space.guid}} }, |
| 104 | + destinations: { href: %r{#{Regexp.escape(link_prefix)}/v3/routes/#{route2.guid}/destinations} }, |
| 105 | + domain: { href: %r{#{Regexp.escape(link_prefix)}/v3/domains/#{route2.domain.guid}} } |
| 106 | + }, |
| 107 | + options: {} |
| 108 | + } |
| 109 | + end |
| 110 | + |
| 111 | + context 'when the user is a member in the app space' do |
| 112 | + let(:expected_codes_and_responses) do |
| 113 | + h = Hash.new( |
| 114 | + { code: 200, |
| 115 | + response_objects: [route1_json, route2_json] }.freeze |
| 116 | + ) |
| 117 | + |
| 118 | + h['org_auditor'] = { code: 404 } |
| 119 | + h['org_billing_manager'] = { code: 404 } |
| 120 | + h['no_role'] = { code: 404 } |
| 121 | + h |
| 122 | + end |
| 123 | + |
| 124 | + it_behaves_like 'permissions for list endpoint', ALL_PERMISSIONS |
| 125 | + end |
| 126 | + |
| 127 | + context 'ports filter' do |
| 128 | + # Don't even think of converting the following hash to symbols ('type' => 'tcp' NOT type: 'tcp'), and you need to set the GUID |
| 129 | + let(:router_group) { VCAP::CloudController::RoutingApi::RouterGroup.new({ 'type' => 'tcp', 'reservable_ports' => '7777,8888,9999', 'guid' => 'some-guid' }) } |
| 130 | + let(:routing_api_client) { instance_double(VCAP::CloudController::RoutingApi::Client) } |
| 131 | + |
| 132 | + before do |
| 133 | + allow_any_instance_of(CloudController::DependencyLocator).to receive(:routing_api_client).and_return(routing_api_client) |
| 134 | + allow(routing_api_client).to receive_messages(enabled?: true, router_group: router_group) |
| 135 | + end |
| 136 | + |
| 137 | + context 'when there are multiple TCP routes with different ports' do |
| 138 | + # The following `let`s depend on the above `before do` |
| 139 | + let(:domain_tcp) { VCAP::CloudController::SharedDomain.make(router_group_guid: router_group.guid, name: 'my.domain') } |
| 140 | + let!(:route_with_ports_0) do |
| 141 | + VCAP::CloudController::Route.make(host: '', space: space, domain: domain_tcp, guid: 'route-with-port-0', port: 7777) |
| 142 | + end |
| 143 | + let!(:route_with_ports_1) do |
| 144 | + VCAP::CloudController::Route.make(host: '', space: space, domain: domain_tcp, guid: 'route-with-port-1', port: 8888) |
| 145 | + end |
| 146 | + let!(:route_with_ports_2) do |
| 147 | + VCAP::CloudController::Route.make(host: '', space: space, domain: domain_tcp, guid: 'route-with-port-2', port: 9999) |
| 148 | + end |
| 149 | + let!(:route_mapping_1) { VCAP::CloudController::RouteMappingModel.make(app: app_model, route: route_with_ports_1, process_type: 'web') } |
| 150 | + let!(:route_mapping_2) { VCAP::CloudController::RouteMappingModel.make(app: app_model, route: route_with_ports_2, process_type: 'web') } |
| 151 | + |
| 152 | + it 'returns routes filtered by ports' do |
| 153 | + get "/v3/apps/#{app_model.guid}/routes?ports=7777,8888", nil, admin_header |
| 154 | + expect(last_response).to have_status_code(200) |
| 155 | + expect(parsed_response['resources'].size).to eq(1) |
| 156 | + expect(parsed_response['resources'].first['port']).to eq(route_with_ports_1.port) |
| 157 | + end |
| 158 | + end |
| 159 | + end |
| 160 | + |
| 161 | + describe 'eager loading' do |
| 162 | + it 'eager loads associated resources that the presenter specifies' do |
| 163 | + expect(VCAP::CloudController::RouteFetcher).to receive(:fetch).with( |
| 164 | + anything, |
| 165 | + hash_including(eager_loaded_associations: %i[domain space route_mappings labels annotations]) |
| 166 | + ).and_call_original |
| 167 | + |
| 168 | + get "/v3/apps/#{app_model.guid}/routes", nil, admin_header |
| 169 | + expect(last_response).to have_status_code(200) |
| 170 | + end |
| 171 | + end |
| 172 | + end |
| 173 | +end |
0 commit comments