Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add unit tests for marketplace base code

 - Add test marketplace for unit testing and bvt

 - Update appdirect spec
   -- Removed webmock dependency
   -- Trimmed specs

Change-Id: Ia58a2141f76ff6b39f419ad34ec73033da1bd308
  • Loading branch information...
commit 8172ddec93123a1f1e4c1e8758ca6f28e9167dea 1 parent 5ae6c5f
Harshawardhan Gadgil authored
View
1  marketplace/Gemfile
@@ -15,7 +15,6 @@ gem 'warden-client', :require => ['warden/client'], :git => 'git://github.com/cl
gem 'warden-protocol', :require => ['warden/protocol'], :git => 'git://github.com/cloudfoundry/warden.git', :ref => '21f9a32ab50'
group :test do
- #gem "webmock"
gem "rake"
gem "rack-test"
gem "rspec"
View
5 marketplace/config/test.yml
@@ -0,0 +1,5 @@
+---
+
+classname: "VCAP::Services::Marketplace::Test::TestMarketplace"
+
+node_timeout: 15
View
23 marketplace/config/test_marketplace_gateway.yml
@@ -0,0 +1,23 @@
+---
+cloud_controller_uri: api.vcap.me
+ip_route: localhost
+index: 0
+token: changetestmarketplacetoken
+mbus: nats://localhost:4222/
+logging:
+ level: debug
+
+pid: /var/vcap/sys/run/marketplace.pid
+
+# node_timeout is required by base async gateway class. This is defined in #{marketplace}.yml
+# since this value is specific to interactions with #{marketplace}
+
+marketplace: test
+
+acls:
+ wildcards:
+ - "*@example.com"
+ users: []
+
+external_uri: "http://test-mpgw.vcap.me"
+refresh_interval: 120
View
54 marketplace/lib/base/marketplace_async_gateway.rb
@@ -51,14 +51,19 @@ def setup(opts)
@index = opts[:index] || 0
@hb_interval = opts[:heartbeat_interval] || 60
@cld_ctrl_uri = http_uri(opts[:cloud_controller_uri] || "api.vcap.me")
- @offering_uri = "#{@cld_ctrl_uri}/services/#{API_VERSION}/offerings/"
+ @offering_uri = "#{@cld_ctrl_uri}/services/#{API_VERSION}/offerings"
@service_list_uri = "#{@cld_ctrl_uri}/proxied_services/#{API_VERSION}/offerings"
@proxy_opts = opts[:proxy]
@handle_fetched = true # set to true in order to compatible with base asycn gateway.
@refresh_interval = opts[:refresh_interval] || 300
- @marketplace_client = load_marketplace(opts)
+ @marketplace_client = load_marketplace(opts)
+
+ @component_host = opts[:host]
+ @component_port = opts[:component_port] || VCAP.grab_ephemeral_port
+ @component_user = opts[:user] || VCAP.secure_uuid
+ @component_pass = opts[:password] || VCAP.secure_uuid
@router_register_json = {
:host => @host,
@@ -85,6 +90,11 @@ def setup(opts)
end
end
+ f = Fiber.new do
+ start_nats(opts[:mbus])
+ end
+ f.resume
+
@refresh_timer = EM::PeriodicTimer.new(@refresh_interval) do
refresh_catalog_and_update_cc
end
@@ -94,18 +104,8 @@ def setup(opts)
EM.defer { update_varz }
end
- # Defer 5 seconds to give service a change to wake up
- EM.add_timer(5) do
- EM.defer { update_varz }
- end
-
- f = Fiber.new do
- start_nats(opts[:mbus])
- end
- f.resume
-
refresh_catalog_and_update_cc
- end
+ end
error [JsonMessage::ValidationError, JsonMessage::ParseError] do
error_msg = ServiceError.new(ServiceError::MALFORMATTED_REQ).to_hash
@@ -118,6 +118,7 @@ def setup(opts)
end
def refresh_catalog_and_update_cc
+ @logger.info("Refreshing Catalog...")
f = Fiber.new do
begin
refresh_catalog
@@ -125,11 +126,12 @@ def refresh_catalog_and_update_cc
advertise_services
# Ready to serve
+ update_varz
@logger.info("#{@marketplace_client.name} Marketplace Gateway is ready to serve incoming request.")
rescue => e
@logger.warn("Error when refreshing #{@marketplace_client.name} catalog: #{fmt_error(e)}")
end
- end
+ end
f.resume
end
@@ -179,7 +181,7 @@ def advertise_services(active=true)
@marketplace_gateway_varz_details[:active_offerings] = @catalog_in_marketplace.size
end
- def update_varz()
+ def update_varz
VCAP::Component.varz["marketplace_gateway"] = @marketplace_gateway_varz_details
VCAP::Component.varz[@marketplace_client.name] = @marketplace_client.varz_details if @marketplace_client.varz_details.size > 0
end
@@ -190,8 +192,11 @@ def start_nats(uri)
VCAP::Component.register(
:nats => @nats,
:type => "#{@marketplace_client.name}MarketplaceGateway",
- :host => @host,
- :index => @index
+ :host => @component_host,
+ :port => @component_port,
+ :index => @index,
+ :user => @component_user,
+ :password => @component_pass
)
on_connect_nats;
f.resume
@@ -228,6 +233,21 @@ def on_exit(stop_event_loop=true)
#################### Handlers ###################
+ # Helpers for unit testing
+ post "/marketplace/set/:key/:value" do
+ @logger.info("TEST HELPER ENDPOINT - set: #{params[:key]} = #{params[:value]}")
+ Fiber.new {
+ begin
+ @marketplace_client.set_config(params[:key], params[:value])
+ refresh_catalog_and_update_cc
+ async_reply("")
+ rescue => e
+ async_reply_error(e.inspect)
+ end
+ }.resume
+ async_mode
+ end
+
get "/" do
return {"marketplace" => @marketplace_client.name, "offerings" => @catalog_in_marketplace}.to_json
end
View
4 marketplace/lib/base/marketplace_base.rb
@@ -39,6 +39,10 @@ def varz_details
{}
end
+ def set_config(key, value)
+ raise "set_config is not supported"
+ end
+
end
end
end
View
162 marketplace/lib/marketplaces/test/test_marketplace.rb
@@ -0,0 +1,162 @@
+# Copyright (c) 2009-2012 VMware, Inc.
+require 'fiber'
+require 'service_error'
+require 'uuidtools'
+
+$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', '..', '..')
+require 'base/marketplace_base'
+
+module VCAP
+ module Services
+ module Marketplace
+ module Test
+ class TestMarketplace < VCAP::Services::Marketplace::Base
+
+ include VCAP::Services::Base::Error
+
+ def initialize(opts)
+ super(opts)
+
+ @logger = opts[:logger]
+ @external_uri = opts[:external_uri]
+ @node_timeout = opts[:node_timeout]
+ @acls = opts[:acls]
+
+ @catalog = {}
+ @catalog["testservice-1.0"] =
+ {
+ "id" => "testservice",
+ "version" => "1.0",
+ "name" => "My Test Service",
+ "description" => "My test Service",
+ "plans" => [ { "id" => "free", "name" => "free edition", "description" => "for demo purposes only" } ],
+ "development" => true,
+ "developers" => [ { "email" => "foo@xyz.com" } ],
+ "active" => true,
+ "provider" => "TestProvider"
+ }
+ end
+
+ def set_config(key, value)
+ if key == "enable_foo"
+ if value == "true"
+ @logger.info("Enabling fooservice-1.0")
+ @catalog["fooservice-1.0"] =
+ {
+ "id" => "fooservice",
+ "version" => "1.0",
+ "name" => "Foo Service",
+ "description" => "Foo Service",
+ "plans" => [ { "id" => "free", "name" => "free edition", "description" => "for demo purposes only" } ],
+ "development" => true,
+ "developers" => [ { "email" => "foo@xyz.com" } ],
+ "active" => true,
+ "provider" => "FooProvider"
+ }
+ else
+ @logger.info("Disabling fooservice-1.0")
+ @catalog.delete("fooservice-1.0")
+ end
+ @logger.info("Catalog contains #{@catalog.size} offerings")
+ end
+ end
+
+ def name
+ "Test"
+ end
+
+ def get_catalog
+ @catalog
+ end
+
+ def offering_disabled?(id, offerings_list)
+ @logger.info("Offering: #{id} - Present in offering list: #{offerings_list.include?(id)}")
+ !(offerings_list.include?(id))
+ end
+
+ def generate_cc_advertise_request(name, bsvc, active = true)
+ req = {}
+ req[:label] = "#{name}-#{bsvc["version"]}"
+ req[:active] = active && bsvc["active"]
+ req[:description] = bsvc["description"]
+
+ req[:provider] = bsvc["provider"]
+
+ req[:supported_versions] = [ bsvc["version"] ]
+ req[:version_aliases] = { "current" => bsvc["version"] }
+
+ req[:acls] = {}
+ req[:acls][:wildcards] = @acls[:wildcards]
+
+ users = []
+ users.concat(@acls[:users].dup) if @acls[:users]
+ if bsvc["developers"] and bsvc["developers"].count > 0
+ bsvc["developers"].each do |dev|
+ users << dev["email"]
+ end
+ end
+ req[:acls][:users] = users unless users.empty?
+
+ req[:url] = @external_uri
+
+ if bsvc["plans"] and bsvc["plans"].count > 0
+ req[:plans] = []
+ bsvc["plans"].each do |plan|
+ req[:plans] << plan["id"]
+ # No plan options yet
+ end
+ else
+ req[:plans] = ["default"]
+ end
+
+ req[:tags] = []
+
+ req[:timeout] = 5 + @node_timeout
+ req
+ end
+
+ def provision_service(request_body)
+ request = VCAP::Services::Api::GatewayProvisionRequest.decode(request_body)
+ service_id,version = request.label.split("-")
+ @logger.info("Provision request for label=#{request.label} (service_id=#{service_id}) plan=#{request.plan}, version=#{request.version}")
+ svc = {
+ :configuration => {:plan => request.plan, :name => request.name, :options => {} },
+ :credentials => { "url" => "http://testservice.com/#{UUIDTools::UUID.random_create.to_s}" },
+ :service_id => UUIDTools::UUID.random_create.to_s,
+ }
+ success(svc)
+ end
+
+ def unprovision_service(service_id)
+ @logger.info("Successfully unprovisioned service #{service_id}")
+ return true
+ end
+
+ def bind_service_instance(service_id, request)
+ binding = {
+ :configuration => {:data => {:binding_options => request.binding_options}},
+ :credentials => { "url" => "http://testservice.com/#{UUIDTools::UUID.random_create.to_s}" },
+ :service_id => UUIDTools::UUID.random_create.to_s
+ }
+ @logger.debug("Generated binding for CC: #{binding.inspect}")
+ success(binding)
+ end
+
+ def unbind_service(service_id, binding_id)
+ @logger.info("Successfully unbound service #{service_id} and binding id #{binding_id}")
+ return true
+ end
+
+ def varz_details
+ { :available_services => @catalog.size }
+ end
+
+ def fmt_error(e)
+ "#{e} [#{e.backtrace.join("|")}]"
+ end
+
+ end
+ end
+ end
+ end
+end
View
92 marketplace/spec/appdirect/appdirect_gateway_spec.rb
@@ -1,92 +0,0 @@
-# Copyright (c) 2009-2011 VMware, Inc.
-$:.unshift(File.dirname(__FILE__))
-require 'spec_helper'
-require_relative '../../lib/base/marketplace_async_gateway'
-require_relative '../../lib/marketplaces/appdirect/appdirect_helper'
-require_relative '../../lib/marketplaces/appdirect/appdirect_marketplace'
-
-module VCAP
- module Services
- module Marketplace
- class MarketplaceAsyncServiceGateway
- attr_reader :logger
- end
- end
- end
-end
-
-describe "AppDirect Gateway" do
-
-# Tempoarily disabling these tests until we figure out a better way of waiting for the app to initialize before running the test
-
-=begin
-
- before :all do
- @config = load_config
- @gateway = VCAP::Services::Marketplace::MarketplaceAsyncServiceGateway.new(@config)
- puts "\n\object.methods : "+ @gateway.methods.sort.join("\n").to_s+"\n\n"
-
- while @gateway.ready_to_serve == false
- puts "Initializing..."
- sleep 1
- end
-
- @app_session = Rack::Test::Session.new(Rack::MockSession.new(@gateway))
-
- @rack_env = {
- "CONTENT_TYPE" => Rack::Mime.mime_type('.json'),
- "HTTP_X_VCAP_SERVICE_TOKEN" => @config[:token],
- }
- @api_version = "poc"
- @api = "#{@config[:appdirect][:scheme]}://#{@config[:appdirect][:host]}/api"
- end
-
- before do
- stub_fixture(:get, @api, VCAP::Services::Marketplace::Appdirect::AppdirectHelper::OFFERINGS_PATH, "urbanairship/")
- stub_cc_request(:post, "services/v1/offerings", "urbanairship/")
- stub_fixture(:post, @api, VCAP::Services::Marketplace::Appdirect::AppdirectHelper::SERVICES_PATH, "urbanairship/")
- end
-
-
- it "should add the 2 service offerings" do
- EM.run do
- @app_session.get "/", params = {}, rack_env = @rack_env
- last_response = @app_session.last_response
- last_response.should be_ok
- puts "LAST RESPONSE = #{last_response.inspect}"
- json = JSON.parse(last_response.body)
- json["offerings"].keys.should include "mongolab"
- json["offerings"].keys.should include "urbanairship"
- EM.stop
- end
- end
-
- it "should return 400 unless token" do
- EM.run do
- get "/", params = {}, rack_env = {}
- puts "LAST RESPONSE = #{last_response.inspect}"
- last_response.status.should == 400
- last_response.should_not be_ok
- EM.stop
- end
- end
-
- it "should respond to create_service" do
- EM.run do
- @svc_params = {
- :label => "mongolab-2.0",
- :name => "mymongo",
- :plan => "small",
- :email => "mwilkinson@vmware.com"
- }
- post "/gateway/v1/configurations", params = @svc_params.to_json, rack_env = @rack_env
-
- puts last_response.body.inspect
- last_response.should be_ok
- json = JSON.parse(last_response.body)
- EM.stop
- end
- end
-=end
-
-end
View
135 marketplace/spec/appdirect/appdirect_helper_spec.rb
@@ -1,5 +1,7 @@
$:.unshift(File.dirname(__FILE__))
-require_relative "spec_helper"
+require_relative "../spec_helper"
+require_relative "mocks"
+require_relative "../do"
require_relative "../../lib/marketplaces/appdirect/appdirect_helper"
require_relative "../../lib/marketplaces/appdirect/appdirect_error"
@@ -7,121 +9,41 @@
describe VCAP::Services::Marketplace::Appdirect::AppdirectHelper do
- before do
- @config = load_config
+ include Do
+
+ before :all do
+ @config = load_appdirect_config
@logger = @config[:logger]
- @appdirect = VCAP::Services::Marketplace::Appdirect::AppdirectHelper.new(@config, @logger)
+ @config[:appdirect][:endpoint] = Mocks.get_endpoint
+ @config[:offering_whitelist] = ["mongolab_dev", "mongolab", "asms_dev", "james_dev"]
- @api = "#{@config[:appdirect][:scheme]}://#{@config[:appdirect][:host]}/api"
- @user_id = "1"
+ @appdirect = VCAP::Services::Marketplace::Appdirect::AppdirectHelper.new(@config, @logger)
end
- context "Activity Streams" do
- before do
- stub_fixture(:get, @api, VCAP::Services::Marketplace::Appdirect::AppdirectHelper::OFFERINGS_PATH, "asms_dev/")
- end
-
- it "get_catalog should get Activity Streams in the catalog" do
- EM.run do
- f = Fiber.new do
- @catalog = @appdirect.get_catalog
- @catalog.should_not be_nil
- @catalog.keys.count.should == 4
- @catalog["asms_dev"]["name"].should == "Activity Streams"
- EM.stop
- end
- f.resume
- end
- end
-
- it "purchase_service should not allow creation due to missing code from AppDirect" do
- EM.run do
- f = Fiber.new do
- req = stub_fixture(:post, @api, VCAP::Services::Marketplace::Appdirect::AppdirectHelper::SERVICES_PATH, "asms_dev/")
- lambda { receipt = @appdirect.purchase_service(req)}.should raise_error
- #(VCAP::Services::AppDirect::AppDirectError::APPDIRECT_ERROR_PURCHASE)
- EM.stop
- end
- f.resume
- end
- end
- end
-
- context "MongoLab" do
- before do
- @scenario = "mongolab/"
- end
-
- it "get_catalog should get Mongo in the catalog" do
- EM.run do
+ it "get_catalog should get Activity Streams in the catalog" do
+ EM.run do
+ mep = nil
+ Do.at(0) { mep = Mocks.create_mock_endpoint("asms_dev/") }
+ Do.at(1) {
f = Fiber.new do
@catalog = @appdirect.get_catalog
@catalog.should_not be_nil
@catalog.keys.count.should == 4
- @catalog["mongolab"]["name"].should == "MongoLab"
- EM.stop
- end
- f.resume
- end
- end
-
- it "able to purchase and cancel service" do
- EM.run do
- f = Fiber.new do
- req = stub_fixture(:post, @api, VCAP::Services::Marketplace::Appdirect::AppdirectHelper::SERVICES_PATH, @scenario)
- puts "Posting#{req.inspect}"
- receipt = @appdirect.purchase_service(req)
- receipt.should_not be_nil
- receipt["offering"]["id"].should == "mongolab_dev"
- receipt["uuid"].should_not be_nil
- @order_id = receipt["uuid"]
- receipt["id"].should_not be_nil
-
- puts "Now cancelling the service"
-
- @cancel_receipt = @appdirect.cancel_service(@order_id)
- @cancel_receipt.should be_true
- EM.stop
+ @catalog["asms_dev-1.0"]["name"].should == "Activity Streams"
end
f.resume
- end
- end
-
- it "able to purchase, bind and cancel service" do
- EM.run do
- f = Fiber.new do
- req = stub_fixture(:post, @api, VCAP::Services::Marketplace::Appdirect::AppdirectHelper::SERVICES_PATH, @scenario)
- puts "Posting#{req.inspect}"
- receipt = @appdirect.purchase_service(req)
- receipt.should_not be_nil
- receipt["offering"]["id"].should == "mongolab_dev"
- receipt["uuid"].should_not be_nil
- @order_id = receipt["uuid"]
- receipt["id"].should_not be_nil
-
- puts "Now binding the service"
-
- req = {}
- receipt = @appdirect.bind_service(req, @order_id)
- receipt.should_not be_nil
- receipt["uuid"].should_not be_nil
- receipt["credentials"].should_not be_nil
-
- puts "Got receipt #{receipt.inspect}"
-
- puts "Now cancelling the service"
- @cancel_receipt = @appdirect.cancel_service(@order_id)
- @cancel_receipt.should be_true
- EM.stop
- end
- f.resume
- end
+ }
+ Do.at(2) { mep.stop; EM.stop }
end
+ end
- it "able to purchase, bind, unbind and cancel service" do
- EM.run do
+ it "should be able to purchase, bind, unbind and cancel service" do
+ EM.run do
+ mep = nil
+ Do.at(0) { mep = Mocks.create_mock_endpoint("mongolab/") }
+ Do.at(1) {
+ req = load_fixture("mongolab/#{VCAP::Services::Marketplace::Appdirect::AppdirectHelper::SERVICES_PATH}/post_request.json")
f = Fiber.new do
- req = stub_fixture(:post, @api, VCAP::Services::Marketplace::Appdirect::AppdirectHelper::SERVICES_PATH, @scenario)
puts "Posting#{req.inspect}"
receipt = @appdirect.purchase_service(req)
receipt.should_not be_nil
@@ -140,7 +62,7 @@
@binding_id = receipt["uuid"]
receipt["credentials"].should_not be_nil
- puts "Now unbinding service"
+ puts "Now unbinding service - binding_id: #{@binding_id}"
unbind_receipt = @appdirect.unbind_service(@order_id, @binding_id)
unbind_receipt.should be_true
@@ -148,11 +70,10 @@
puts "Now cancelling the service"
@cancel_receipt = @appdirect.cancel_service(@order_id)
@cancel_receipt.should be_true
- EM.stop
end
f.resume
- end
+ }
+ Do.at(2) { mep.stop; EM.stop }
end
-
end
end
View
1  ...es/60ea08ec-a4d4-4f69-9f70-14db951ddd2d/bindings/2c3d8f56-f269-425f-9831-2faf3d26e686/post_response.json
@@ -0,0 +1 @@
+{}
View
75 marketplace/spec/appdirect/mocks.rb
@@ -0,0 +1,75 @@
+class Mocks
+ HOST = "127.0.0.1"
+ PORT = 15000
+
+ def self.get_endpoint
+ "http://#{HOST}:#{PORT}"
+ end
+
+ def self.create_mock_endpoint(scenario = "")
+ mep = MockEndpoint.new(scenario)
+ mep.start
+ mep
+ end
+
+ class MockEndpoint
+ def initialize(scenario)
+ @server = Thin::Server.new("#{HOST}", PORT, Handler.new(scenario))
+ end
+
+ def start
+ Thread.new { @server.start }
+ while !@server.running?
+ sleep 0.1
+ end
+ end
+
+ def stop
+ @server.stop if @server
+ end
+
+ class Handler < Sinatra::Base
+
+ def initialize(s)
+ @scenario = s
+ end
+
+ get "/*" do
+ path = params[:splat]
+ puts "\n*_*_*- Get: #{path[0]}\n"
+
+ load_fixture("get", path[0])
+ end
+
+ post "/*" do
+ path = params[:splat]
+ data = JSON.parse(request.body.read)
+ puts "\n#_#_#- POST: #{path}\n#{data}\n"
+
+ load_fixture("post", path[0])
+ end
+
+ delete "/*" do
+ path = params[:splat]
+ data = JSON.parse(request.body.read)
+ puts "\n^_^_^- DELETE: #{path}\n#{data}\n"
+
+ load_fixture("post", path[0])
+ end
+
+ helpers do
+
+ def load_fixture(verb, path, resp = '{}')
+ # Remove api/ from path
+ path = path[4, path.size-4]
+
+ fixture = "#{File.dirname(__FILE__)}/fixtures/#{@scenario}#{path}/#{verb}_response.json"
+
+ puts "LOAD Fixture: #{fixture}"
+ File.read(fixture) rescue resp
+ end
+ end
+
+ end
+ end
+end
View
206 marketplace/spec/base/helpers.rb
@@ -0,0 +1,206 @@
+class MarketplaceGatewayHelper
+ CC_PORT = 34567
+
+ GW_PORT = 15000
+ GW_COMPONENT_PORT = 10000
+ LOCALHOST = "127.0.0.1"
+
+ def self.get_config
+ config = load_test_config
+ config[:cloud_controller_uri] = "#{LOCALHOST}:#{CC_PORT}"
+ config[:mbus] = "nats://nats:nats@#{VCAP.local_ip}:4222"
+ config[:host] = LOCALHOST
+ config[:port] = GW_PORT
+ config[:url] = "http://#{LOCALHOST}:#{GW_PORT}"
+ config[:component_port] = GW_COMPONENT_PORT
+ config[:user] = "u"
+ config[:password] = "p"
+ config
+ end
+
+ def self.create_mpgw
+ gw = Gateway.new(get_config)
+ gw.start
+ gw
+ end
+
+ def self.create_cc
+ cc = MockCloudController.new
+ cc.start
+ cc
+ end
+
+ def self.create_client()
+ config = get_config
+ Client.new(config)
+ end
+
+ class MockCloudController
+ def initialize
+ @server = Thin::Server.new("#{LOCALHOST}", CC_PORT, Handler.new)
+ end
+
+ def start
+ Thread.new { @server.start }
+ while !@server.running?
+ sleep 0.1
+ end
+ end
+
+ def stop
+ @server.stop if @server
+ end
+
+ class Handler < Sinatra::Base
+
+ def initialize()
+ @offerings = {}
+ end
+
+ post "/services/v1/offerings" do
+ svc = JSON.parse(request.body.read)
+ @offerings[svc["label"]] = svc
+ puts "\n*#*#*#*#* Registered #{svc["active"] == true ? "*ACTIVE*" : "*INACTIVE*"} offering: #{svc["label"]}\n\n"
+ "{}"
+ end
+
+ get "/proxied_services/v1/offerings" do
+ puts "*#*#*#*#* CC::GET(/proxied_services/v1/offerings): #{request.body.read}"
+ Yajl::Encoder.encode({
+ :proxied_services => @offerings.values
+ })
+ end
+ end
+ end
+
+ class Gateway
+
+ def initialize(cfg)
+ @config = cfg
+ @mpgw = VCAP::Services::Marketplace::MarketplaceAsyncServiceGateway.new(@config)
+ @server = Thin::Server.new(@config[:host], @config[:port], @mpgw)
+ end
+
+ def start
+ Thread.new { @server.start }
+ end
+
+ def stop
+ @server.stop
+ end
+ end
+
+ class Client
+
+ attr_accessor :last_http_code, :last_response
+
+ def initialize(opts)
+ @gw_host = opts[:host]
+ @gw_port = opts[:port]
+ @component_port = opts[:component_port]
+ @credentials = [ opts[:user], opts[:password] ]
+
+ @token = opts[:token]
+ @cc_head = {
+ 'Content-Type' => 'application/json',
+ 'X-VCAP-Service-Token' => @token,
+ }
+ @base_url = "http://#{@gw_host}:#{@gw_port}"
+ @component_base_url = "http://#{@gw_host}:#{@component_port}"
+ end
+
+ def set_token(tok)
+ old_token = @token
+ @token = tok
+ @cc_head['X-VCAP-Service-Token'] = @token
+ old_token
+ end
+
+ def gen_req(body = nil)
+ req = {}
+ req[:head] = @cc_head
+ req[:body] = body if body
+ req
+ end
+
+ def set_last_result(http)
+ puts "Received response: #{http.response_header.status} - #{http.response.inspect}"
+ @last_http_code = http.response_header.status
+ @last_response = http.response
+ end
+
+ def get_varz
+ http = EM::HttpRequest.new("#{@component_base_url}/varz").get :head => {'authorization' => @credentials}
+ http.callback { set_last_result(http) }
+ http.errback { set_last_result(http) }
+ end
+
+ def get_healthz
+ http = EM::HttpRequest.new("#{@component_base_url}/healthz").get :head => {'authorization' => @credentials}
+ http.callback { set_last_result(http) }
+ http.errback { set_last_result(http) }
+ end
+
+ def send_get_request(url, body = nil)
+ puts "Sending request to: #{@base_url}#{url}"
+ http = EM::HttpRequest.new("#{@base_url}#{url}").get(gen_req(body))
+ http.callback { set_last_result(http) }
+ http.errback { set_last_result(http) }
+ end
+
+ def set_config(key, value)
+ url = "#{@base_url}/marketplace/set/#{key}/#{value}"
+ puts "Sending request to: #{url}"
+ http = EM::HttpRequest.new("#{url}").post(gen_req)
+ http.callback { set_last_result(http) }
+ http.errback { set_last_result(http) }
+ end
+
+ def send_provision_request(label, name, email, plan, version)
+ msg = VCAP::Services::Api::GatewayProvisionRequest.new(
+ :label => label,
+ :name => name,
+ :email => email,
+ :plan => plan,
+ :version => version
+ ).encode
+ http = EM::HttpRequest.new("http://#{@gw_host}:#{@gw_port}/gateway/v1/configurations").post(gen_req(msg))
+ http.callback { set_last_result(http) }
+ http.errback { set_last_result(http) }
+ end
+
+ def send_unprovision_request(service_id)
+ raise "Null service id" if service_id.nil?
+ http = EM::HttpRequest.new("http://#{@gw_host}:#{@gw_port}/gateway/v1/configurations/#{service_id}").delete(gen_req)
+ http.callback { set_last_result(http) }
+ http.errback { set_last_result(http) }
+ end
+
+ def send_bind_request(service_id, label, email, opts)
+ raise "Null service id" if service_id.nil?
+ msg = VCAP::Services::Api::GatewayBindRequest.new(
+ :service_id => service_id,
+ :label => label,
+ :email => email,
+ :binding_options => opts
+ ).encode
+
+ http = EM::HttpRequest.new("http://#{@gw_host}:#{@gw_port}/gateway/v1/configurations/#{service_id}/handles").post(gen_req(msg))
+ http.callback { set_last_result(http) }
+ http.errback { set_last_result(http) }
+ end
+
+ def send_unbind_request(service_id, bind_id)
+ raise "Null service id" if service_id.nil?
+ raise "Null bind id" if bind_id.nil?
+ msg = Yajl::Encoder.encode({
+ :service_id => service_id,
+ :handle_id => bind_id,
+ :binding_options => {}
+ })
+ http = EM::HttpRequest.new("http://#{@gw_host}:#{@gw_port}/gateway/v1/configurations/#{service_id}/handles/#{bind_id}").delete(gen_req(msg))
+ http.callback { set_last_result(http) }
+ http.errback { set_last_result(http) }
+ end
+ end
+end
View
169 marketplace/spec/base/marketplace_gateway_spec.rb
@@ -0,0 +1,169 @@
+# Copyright (c) 2009-2011 VMware, Inc.
+
+require 'eventmachine'
+
+$:.unshift(File.dirname(__FILE__))
+require_relative '../spec_helper'
+require 'helpers'
+require_relative '../do'
+require_relative '../../lib/base/marketplace_async_gateway'
+require_relative '../../lib/marketplaces/test/test_marketplace'
+
+describe "MarketplaceGateway" do
+
+ include Do
+
+ it "should add service offerings from marketplace" do
+ EM.run do
+ cc = nil
+ gw = nil
+ client = nil
+
+ Do.at(0) {
+ cc = MarketplaceGatewayHelper.create_cc
+ gw = MarketplaceGatewayHelper.create_mpgw
+ client = MarketplaceGatewayHelper.create_client
+ }
+ Do.at(2) { client.send_get_request("/") }
+ Do.at(3) {
+ client.last_http_code.should == 200
+ json = JSON.parse(client.last_response)
+ json["offerings"].keys.should include "testservice-1.0"
+ }
+ Do.at(4) { cc.stop; gw.stop; EM.stop }
+ end
+ end
+
+ it "should return 401 unless token" do
+ EM.run do
+ cc = nil
+ gw = nil
+ client = nil
+ old_token = nil
+
+ Do.at(0) {
+ cc = MarketplaceGatewayHelper.create_cc
+ gw = MarketplaceGatewayHelper.create_mpgw
+ client = MarketplaceGatewayHelper.create_client
+ old_token = client.set_token("bad_token")
+ }
+ Do.at(2) { client.send_get_request("/") }
+ Do.at(3) {
+ client.last_http_code.should == 401
+ client.set_token(old_token)
+ }
+ Do.at(4) { cc.stop; gw.stop; EM.stop }
+ end
+ end
+
+ it "should be able to provision, bind, unbind, unprovision a service" do
+ EM.run do
+ cc = nil
+ gw = nil
+ client = nil
+
+ Do.at(0) {
+ cc = MarketplaceGatewayHelper.create_cc
+ gw = MarketplaceGatewayHelper.create_mpgw
+ client = MarketplaceGatewayHelper.create_client
+ }
+
+ Do.at(2) { client.send_provision_request("testservice-1.0", "test1", "foo@xyz.com", "small", "1.0") }
+ Do.at(3) {
+ client.last_http_code.should == 200
+ json = JSON.parse(client.last_response)
+ json.keys.should include "credentials"
+ json.keys.should include "service_id"
+ }
+
+ Do.at(4) { client.send_bind_request("foo", "bar", "foo@xyz.com", {}) }
+ Do.at(5) {
+ client.last_http_code.should == 200
+ json = JSON.parse(client.last_response)
+ json.keys.should include "credentials"
+ json.keys.should include "service_id"
+ }
+
+ Do.at(6) { client.send_unbind_request("foo", "bar") }
+ Do.at(7) { client.last_http_code.should == 200 }
+
+ Do.at(8) { client.send_unprovision_request("foo") }
+ Do.at(9) { client.last_http_code.should == 200 }
+
+ Do.at(10) { cc.stop; gw.stop; EM.stop }
+ end
+ end
+
+ it "should expose varz and healthz" do
+ EM.run do
+ cc = nil
+ gw = nil
+ client = nil
+
+ Do.at(0) {
+ cc = MarketplaceGatewayHelper.create_cc
+ gw = MarketplaceGatewayHelper.create_mpgw
+ client = MarketplaceGatewayHelper.create_client
+ }
+
+ Do.at(2) { client.get_healthz }
+ Do.at(3) {
+ client.last_http_code.should == 200
+ client.last_response.should == "ok\n"
+ }
+
+ Do.at(4) { varz = client.get_varz }
+ Do.at(5) {
+ client.last_http_code.should == 200
+ varz = JSON.parse(client.last_response)
+ varz.keys.should include "marketplace_gateway"
+ varz["marketplace_gateway"]["disabled_services"].should == 0
+ varz["marketplace_gateway"]["active_offerings"].should == 1
+
+ varz.keys.should include "Test"
+ varz["Test"]["available_services"].should == 1
+ }
+
+ Do.at(6) { cc.stop; gw.stop; EM.stop }
+ end
+ end
+
+ it "should inactivate disabled offerings" do
+ EM.run do
+ cc = nil
+ gw = nil
+ client = nil
+
+ Do.at(0) {
+ cc = MarketplaceGatewayHelper.create_cc
+ gw = MarketplaceGatewayHelper.create_mpgw
+ client = MarketplaceGatewayHelper.create_client
+ }
+
+ Do.at(2) { client.set_config("enable_foo", "true") }
+ Do.at(3) { client.last_http_code.should == 200 }
+
+ Do.at(4) { varz = client.get_varz }
+ Do.at(5) {
+ client.last_http_code.should == 200
+ varz = JSON.parse(client.last_response)
+ varz["marketplace_gateway"]["disabled_services"].should == 0
+ varz["marketplace_gateway"]["active_offerings"].should == 2
+ }
+
+ Do.at(6) { client.set_config("enable_foo", "false") }
+ Do.at(7) { client.last_http_code.should == 200 }
+
+ Do.at(8) { varz = client.get_varz }
+ Do.at(9) {
+ client.last_http_code.should == 200
+ varz = JSON.parse(client.last_response)
+ varz["marketplace_gateway"]["disabled_services"].should == 1
+ varz["marketplace_gateway"]["active_offerings"].should == 1
+ }
+
+ Do.at(10) { cc.stop; gw.stop; EM.stop }
+ end
+ end
+
+end
View
18 marketplace/spec/do.rb
@@ -0,0 +1,18 @@
+module Do
+ # the tests below do various things then wait for something to
+ # happen -- so there's a potential for a race condition. to
+ # minimize the risk of the race condition, increase this value (0.1
+ # seems to work about 90% of the time); but to make the tests run
+ # faster, decrease it
+ STEP_DELAY = 0.5
+
+ def self.at(index, &blk)
+ EM.add_timer(index*STEP_DELAY) { blk.call if blk }
+ end
+
+ # Respect the real seconds while doing concurrent testing
+ def self.sec(index, &blk)
+ EM.add_timer(index) { blk.call if blk }
+ end
+end
+
View
58 marketplace/spec/appdirect/spec_helper.rb → marketplace/spec/spec_helper.rb
@@ -20,20 +20,12 @@ def format(result)
SimpleCov.start
require "rspec"
-#require "webmock/rspec"
require "bundler/setup"
-#require "vcap_services_base"
-require "rack/test"
require "json"
require "logger"
require "yaml"
-#require "webmock"
-
-
-#include WebMock::API
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "..", "..")
-#require "vcap/common"
def symbolize_keys(hash)
if hash.is_a? Hash
@@ -47,33 +39,10 @@ def symbolize_keys(hash)
end
end
-def stub_fixture(verb, api, path, scenario = "")
- url = "#{api}/#{path}"
- fixture = "#{scenario}#{path}/#{verb.to_s}_response.json"
- req_fixture = "#{scenario}#{path}/#{verb.to_s}_request.json"
- stuff = load_fixture(fixture)
- #stub_request(verb, url).to_return(:body=> stuff)
- JSON.parse(load_fixture(req_fixture))
-end
-
-def stub_cc_request(verb, path, scenario = "")
- stuff = load_fixture("#{scenario}cloudfoundry/#{path}/#{verb.to_s}_response.json", '')
-
- i = 0
- while File.exists?("#{File.dirname(__FILE__)}/fixtures/#{scenario}cloudfoundry/#{path}/#{verb.to_s}_request_#{i}.json")
- req_path = "#{scenario}cloudfoundry/#{path}/#{verb.to_s}_request_#{i}.json"
- req_body = JSON.parse(load_fixture(req_path))
- #stub_http_request(:post, "http://api.vcap.me/#{path}/")
- # .with(:body=> symbolize_keys(req_body), :headers => {'Content-Type'=>'application/json', 'X-Vcap-Service-Token'=> /.+/})
- # .to_return(:status => 200, :body => "", :headers => {})
- i += 1
- end
-end
-
-def load_config()
- config = YAML.load_file(File.join(File.dirname(__FILE__), "..", "..", "config", "marketplace_gateway.yml"))
+def load_appdirect_config()
+ config = YAML.load_file(File.join(File.dirname(__FILE__), "..", "config", "marketplace_gateway.yml"))
config = symbolize_keys(config)
- appdirect_config = YAML.load_file(File.join(File.dirname(__FILE__), "..", "..", "config", "appdirect.yml"))
+ appdirect_config = YAML.load_file(File.join(File.dirname(__FILE__), "..", "config", "appdirect.yml"))
appdirect_config = symbolize_keys(appdirect_config)
config = config.merge(appdirect_config)
@@ -85,6 +54,19 @@ def load_config()
config
end
+def load_test_config()
+ config = YAML.load_file(File.join(File.dirname(__FILE__), "..", "config", "test_marketplace_gateway.yml"))
+ config = symbolize_keys(config)
+
+ test_config = YAML.load_file(File.join(File.dirname(__FILE__), "..", "config", "test.yml"))
+ test_config = symbolize_keys(test_config)
+
+ config = config.merge(test_config)
+ config[:logger] = make_logger()
+
+ config
+end
+
def make_logger()
logger = Logger.new(STDOUT)
logger.level = Logger::DEBUG
@@ -95,10 +77,4 @@ def load_fixture(filename, resp = '{}')
File.read("#{File.dirname(__FILE__)}/fixtures/#{filename}") rescue resp
end
-# http://eigenclass.org/hiki/Changes+in+Ruby+1.9#l156
-# Default Time.to_s changed in 1.9, monkeypatching it back
-class Time
- def to_s
- strftime("%a %b %d %H:%M:%S %Z %Y")
- end
-end
+
Please sign in to comment.
Something went wrong with that request. Please try again.