summaryrefslogtreecommitdiff
path: root/features
diff options
context:
space:
mode:
authorAdam Jacob <adam@opscode.com>2009-08-01 21:26:21 -0700
committerAdam Jacob <adam@opscode.com>2009-08-20 13:31:58 -0700
commit89779a4d487ac7dd313e16eb68732835bd13d4e4 (patch)
treed8b867f3a5610ed39bd864149152085af05a1c3d /features
parent89a2609242a63d6bc85e0ddfed078319b4d4a81b (diff)
downloadchef-89779a4d487ac7dd313e16eb68732835bd13d4e4.tar.gz
Initial pass at a Chef 0.8.0 alpha
Conflicts: Rakefile chef-server-slice/Rakefile chef-server-slice/app/controllers/application.rb chef-server-slice/app/controllers/exceptions.rb chef-server-slice/app/controllers/nodes.rb chef-server-slice/app/controllers/openid_consumer.rb chef-server-slice/app/controllers/openid_server.rb chef-server-slice/app/helpers/nodes_helper.rb chef-server/Rakefile chef-server/config/dependencies.rb chef-server/config/init.rb chef/Rakefile chef/lib/chef.rb chef/lib/chef/application/indexer.rb chef/lib/chef/client.rb chef/lib/chef/config.rb chef/lib/chef/node.rb chef/lib/chef/queue.rb chef/spec/unit/application/indexer_spec.rb chef/spec/unit/client_spec.rb chef/spec/unit/config_spec.rb cucumber.yml features/api/nodes/create_node_api.feature features/api/nodes/delete_node_api.feature features/api/nodes/list_nodes_api.feature features/api/nodes/show_node_api.feature features/api/nodes/update_node_api.feature features/api/roles/list_roles_api.feature features/steps/fixture_steps.rb
Diffstat (limited to 'features')
-rw-r--r--features/api/cookbooks/list_cookbooks_api.feature19
-rw-r--r--features/api/cookbooks/show_cookbook_api.feature32
-rw-r--r--features/api/cookbooks/show_cookbook_attributes_api.feature21
-rw-r--r--features/api/data/create_data_bag_api.feature26
-rw-r--r--features/api/data/create_data_bag_item_api.feature29
-rw-r--r--features/api/data/delete_data_bag_api.feature34
-rw-r--r--features/api/data/delete_data_bag_item.feature27
-rw-r--r--features/api/data/list_data_bags.feature34
-rw-r--r--features/api/data/show_data_bag_api.feature44
-rw-r--r--features/api/data/show_data_bag_item_api.feature28
-rw-r--r--features/api/nodes/cookbook_sync_api.feature25
-rw-r--r--features/api/nodes/create_node_api.feature18
-rw-r--r--features/api/nodes/delete_node_api.feature13
-rw-r--r--features/api/nodes/list_nodes_api.feature20
-rw-r--r--features/api/nodes/show_node_api.feature15
-rw-r--r--features/api/nodes/update_node_api.feature14
-rw-r--r--features/api/roles/create_role_api.feature16
-rw-r--r--features/api/roles/delete_role_api.feature15
-rw-r--r--features/api/roles/list_roles_api.feature21
-rw-r--r--features/api/roles/show_roles_api.feature16
-rw-r--r--features/api/roles/update_roles_api.feature13
-rw-r--r--features/api/search/list_search.feature29
-rw-r--r--features/api/search/show_search.feature115
-rw-r--r--features/data/config/client.rb12
-rw-r--r--features/data/config/server.rb19
-rw-r--r--features/data/cookbooks/integration_setup/recipes/default.rb2
-rw-r--r--features/data/cookbooks/node_cookbook_sync/README.rdoc8
-rw-r--r--features/data/cookbooks/node_cookbook_sync/attributes/attr_file.rb0
-rw-r--r--features/data/cookbooks/node_cookbook_sync/definitions/def_file.rb0
-rw-r--r--features/data/cookbooks/node_cookbook_sync/libraries/lib_file.rb0
-rw-r--r--features/data/cookbooks/node_cookbook_sync/metadata.rb6
-rw-r--r--features/data/cookbooks/node_cookbook_sync/recipes/default.rb18
-rw-r--r--features/data/cookbooks/search/recipes/search_data.rb15
-rw-r--r--features/data/cookbooks/search/recipes/search_data_noblock.rb (renamed from features/steps/couchdb_steps.rb)31
-rw-r--r--features/data/cookbooks/show_cookbook/README.rdoc8
-rw-r--r--features/data/cookbooks/show_cookbook/attributes/attr_file.rb0
-rw-r--r--features/data/cookbooks/show_cookbook/definitions/def_file.rb0
-rw-r--r--features/data/cookbooks/show_cookbook/files/default/prime_time.txt0
-rw-r--r--features/data/cookbooks/show_cookbook/files/host-latte/prime_time.txt0
-rw-r--r--features/data/cookbooks/show_cookbook/files/mac_os_x-10.5/prime_time.txt0
-rw-r--r--features/data/cookbooks/show_cookbook/files/mac_os_x/prime_time.txt0
-rw-r--r--features/data/cookbooks/show_cookbook/libraries/lib_file.rb0
-rw-r--r--features/data/cookbooks/show_cookbook/metadata.rb6
-rw-r--r--features/data/cookbooks/show_cookbook/recipes/default.rb18
-rw-r--r--features/data/cookbooks/show_cookbook/templates/default/prime_time.txt.erb0
-rw-r--r--features/data/cookbooks/show_cookbook/templates/host-latte/prime_time.txt.erb0
-rw-r--r--features/data/cookbooks/show_cookbook/templates/mac_os_x-10.5/prime_time.txt.erb0
-rw-r--r--features/data/cookbooks/show_cookbook/templates/mac_os_x/prime_time.txt.erb0
-rw-r--r--features/language/delayed_notifications.feature1
-rw-r--r--features/provider/directory/create_directories.feature1
-rw-r--r--features/provider/directory/delete_directories.feature1
-rw-r--r--features/provider/execute/run_commands.feature1
-rw-r--r--features/provider/file/manage_files.feature1
-rw-r--r--features/search/search_data.feature20
-rw-r--r--features/steps/cookbook_steps.rb2
-rw-r--r--features/steps/fixture_steps.rb112
-rw-r--r--features/steps/node_steps.rb4
-rw-r--r--features/steps/request_steps.rb96
-rw-r--r--features/steps/response_steps.rb61
-rw-r--r--features/steps/run_client_steps.rb9
-rw-r--r--features/steps/run_solo.rb2
-rw-r--r--features/steps/webrat_steps.rb2
-rw-r--r--features/support/env.rb110
63 files changed, 1014 insertions, 176 deletions
diff --git a/features/api/cookbooks/list_cookbooks_api.feature b/features/api/cookbooks/list_cookbooks_api.feature
new file mode 100644
index 0000000000..8888cfa3ce
--- /dev/null
+++ b/features/api/cookbooks/list_cookbooks_api.feature
@@ -0,0 +1,19 @@
+@api @cookbooks @list_cookbooks
+Feature: List cookbooks via the REST API
+ In order to know what cookbooks are loaded on the Chef Server
+ As a Developer
+ I want to list all the cookbooks
+
+ Scenario: List cookbooks
+ Given a 'registration' named 'bobo' exists
+ When I 'GET' the path '/cookbooks'
+ Then the inflated responses key 'manage_files' should exist
+ And the inflated responses key 'manage_files' should match 'http://[^/]+/organizations/clownco/cookbooks/manage_files'
+ And the inflated responses key 'delayed_notifications' should exist
+ And the inflated responses key 'delayed_notifications' should match 'http://[^/]+/organizations/clownco/cookbooks/delayed_notifications'
+
+ Scenario: List cookbooks with a wrong private key
+ Given a 'registration' named 'bobo' exists
+ When I 'GET' the path '/cookbooks' using a wrong private key
+ Then I should get a '401 "Unauthorized"' exception
+
diff --git a/features/api/cookbooks/show_cookbook_api.feature b/features/api/cookbooks/show_cookbook_api.feature
new file mode 100644
index 0000000000..48e19a2981
--- /dev/null
+++ b/features/api/cookbooks/show_cookbook_api.feature
@@ -0,0 +1,32 @@
+@api @cookbooks @show_cookbook
+Feature: Show a cookbook via the REST API
+ In order to know what the details are for a cookbook
+ As a Developer
+ I want to show the details for a specific cookbook
+
+ Scenario: Show a cookbook
+ Given a 'registration' named 'bobo' exists
+ When I 'GET' the path '/cookbooks/show_cookbook'
+ Then the inflated responses key 'name' should match 'show_cookbook'
+ Then the inflated responses key 'files' should match '^\[.+\]$' as json
+ Then the inflated responses key 'recipes' should match '^\[.+\]$' as json
+ Then the inflated responses key 'metadata' should match '^\{.+\}$' as json
+ Then the inflated responses key 'attributes' should match '^\[.+\]$' as json
+ Then the inflated responses key 'libraries' should match '^\[.+\]$' as json
+ Then the inflated responses key 'definitions' should match '^\[.+\]$' as json
+ Then the inflated responses key 'templates' should match '^\[.+\]$' as json
+
+ Scenario: Show a missing cookbook
+ Given a 'registration' named 'bobo' exists
+ When I download the 'frabjabtasticaliciousmonkeyman' cookbook
+ Then the response code should be '404'
+
+ Scenario: Show a cookbook with a wrong private key
+ Given a 'registration' named 'bobo' exists
+ When I 'GET' the path '/cookbooks/show_cookbook' using a wrong private key
+ Then I should get a '401 "Unauthorized"' exception
+
+ Scenario: Show a cookbook without authenticating
+ When I 'GET' the path '/cookbooks/show_cookbook'
+ Then I should get a '401 "Unauthorized"' exception
+
diff --git a/features/api/cookbooks/show_cookbook_attributes_api.feature b/features/api/cookbooks/show_cookbook_attributes_api.feature
new file mode 100644
index 0000000000..8e4fc0cd1a
--- /dev/null
+++ b/features/api/cookbooks/show_cookbook_attributes_api.feature
@@ -0,0 +1,21 @@
+@api @cookbooks
+Feature: Show a cookbooks attribute files via the REST API
+ In order to know what the details are for a cookbooks attribute files
+ As a Developer
+ I want to show the attribute files for a specific cookbook
+
+ Scenario: Show a cookbooks attribute files
+ Given a 'registration' named 'bobo' exists
+ When I 'GET' the path '/cookbooks/show_cookbook/attributes'
+ Then the inflated response should match '^[.+]$' as json
+
+ Scenario: Show a missing cookbook
+ Given a 'registration' named 'bobo' exists
+ When I 'GET' the path '/cookbooks/frabjabtasticaliciousmonkeyman/attributes'
+ Then I should get a '404 "Not Found"' exception
+
+ Scenario: Show a cookbooks attribute files with a wrong private key
+ Given a 'registration' named 'bobo' exists
+ When I 'GET' the path '/cookbooks/show_cookbook/attributes' using a wrong private key
+ Then I should get a '401 "Unauthorized"' exception
+
diff --git a/features/api/data/create_data_bag_api.feature b/features/api/data/create_data_bag_api.feature
new file mode 100644
index 0000000000..c76018b2f0
--- /dev/null
+++ b/features/api/data/create_data_bag_api.feature
@@ -0,0 +1,26 @@
+@api @data @api_data
+Feature: Create a data bag via the REST API
+ In order to create data bags programatically
+ As a Devleoper
+ I want to create data bags via the REST API
+
+ Scenario: Create a new data bag
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users'
+ When I authenticate as 'bobo'
+ And I 'POST' the 'data_bag' to the path '/data'
+ And the inflated responses key 'uri' should match '^http://.+/data/users$'
+
+ Scenario: Create a data bag that already exists
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users'
+ When I authenticate as 'bobo'
+ And I 'POST' the 'data_bag' to the path '/data'
+ And I 'POST' the 'data_bag' to the path '/data'
+ Then I should get a '403 "Forbidden"' exception
+
+ Scenario: Create a new data bag without authenticating
+ Given a 'data_bag' named 'webserver'
+ When I 'POST' the 'data_bag' to the path '/data'
+ Then I should get a '401 "Unauthorized"' exception
+
diff --git a/features/api/data/create_data_bag_item_api.feature b/features/api/data/create_data_bag_item_api.feature
new file mode 100644
index 0000000000..ebd9637b2b
--- /dev/null
+++ b/features/api/data/create_data_bag_item_api.feature
@@ -0,0 +1,29 @@
+@api @data @api_data @api_data_item
+Feature: Create a data bag item via the REST API
+ In order to store an item in a data bag programatically
+ As a Devleoper
+ I want to store data bag items via the REST API
+
+ Scenario: Create a new data bag item
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis'
+ When I authenticate as 'bobo'
+ And I 'PUT' the 'data_bag_item' to the path '/data/users/francis'
+ Then the inflated responses key 'id' should match '^francis$'
+
+ Scenario: Update a data bag item that already exists
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ And a 'data_bag_item' named 'francis_extra'
+ When I authenticate as 'bobo'
+ And I 'PUT' the 'data_bag_item' to the path '/data/users/francis'
+ Then the inflated responses key 'id' should match '^francis$'
+ And the inflated responses key 'extra' should match '^majority$'
+
+ Scenario: Create a new data bag without authenticating
+ Given a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis'
+ When I 'PUT' the 'data_bag_item' to the path '/data/users/francis'
+ Then I should get a '401 "Unauthorized"' exception
diff --git a/features/api/data/delete_data_bag_api.feature b/features/api/data/delete_data_bag_api.feature
new file mode 100644
index 0000000000..560d1e3ea6
--- /dev/null
+++ b/features/api/data/delete_data_bag_api.feature
@@ -0,0 +1,34 @@
+@api @data @api_data @api_data_delete
+Feature: Delete a Data Bag via the REST API
+ In order to remove a Data Bag
+ As a Developer
+ I want to delete a Data Bag via the REST API
+
+ Scenario: Delete a Data Bag
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ When I authenticate as 'bobo'
+ And I 'DELETE' the path '/data/users'
+ Then the inflated response should respond to 'name' with 'users'
+
+ Scenario: Delete a Data Bag that does not exist
+ Given a 'registration' named 'bobo' exists
+ And there are no Data Bags
+ When I authenticate as 'bobo'
+ When I 'DELETE' the path '/data/users'
+ Then I should get a '404 "Not Found"' exception
+
+ Scenario: Delete a Data Bag that has items in it
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ When I authenticate as 'bobo'
+ And I 'DELETE' the path '/data/users'
+ Then the inflated response should respond to 'name' with 'users'
+ And the data_bag named 'users' should not have an item named 'francis'
+
+ Scenario: Delete a Data Bag without authenticating
+ Given a 'data_bag' named 'users' exists
+ When I 'DELETE' the path '/data/users'
+ Then I should get a '401 "Unauthorized"' exception
+
diff --git a/features/api/data/delete_data_bag_item.feature b/features/api/data/delete_data_bag_item.feature
new file mode 100644
index 0000000000..057c2e55ec
--- /dev/null
+++ b/features/api/data/delete_data_bag_item.feature
@@ -0,0 +1,27 @@
+@api @data @api_data @api_data_item
+Feature: Delete a Data Bag Item via the REST API
+ In order to remove a Data Bag Item
+ As a Developer
+ I want to delete a Data Bag Item via the REST API
+
+ Scenario: Delete a Data Bag Item
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ When I authenticate as 'bobo'
+ And I 'DELETE' the path '/data/users/francis'
+ Then the inflated responses key 'id' should match '^francis$'
+
+ Scenario: Delete a Data Bag Item that does not exist
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ When I authenticate as 'bobo'
+ When I 'DELETE' the path '/data/users/francis'
+ Then I should get a '404 "Not Found"' exception
+
+ Scenario: Delete a Data Bag Item without authenticating
+ Given a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ When I 'DELETE' the path '/data/users/francis'
+ Then I should get a '401 "Unauthorized"' exception
+
diff --git a/features/api/data/list_data_bags.feature b/features/api/data/list_data_bags.feature
new file mode 100644
index 0000000000..97929d7747
--- /dev/null
+++ b/features/api/data/list_data_bags.feature
@@ -0,0 +1,34 @@
+@api @data @api_data
+Feature: List data bags via the REST API
+ In order to know what data bags exists programatically
+ As a Developer
+ I want to list all the data bags
+
+ Scenario: List data bags when none have been created
+ Given a 'registration' named 'bobo' exists
+ And there are no data bags
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/data'
+ Then the inflated response should be an empty array
+
+ Scenario: List data bags when one has been created
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/data'
+ Then the inflated response should include '^http://.+/data/users$'
+
+ Scenario: List data bags when two have been created
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag' named 'rubies' exists
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/data'
+ Then the inflated response should be '2' items long
+ And the inflated response should include '^http://.+/data/users$'
+ And the inflated response should include '^http://.+/data/rubies$'
+
+ Scenario: List data bags when you are not authenticated
+ When I 'GET' the path '/data'
+ Then I should get a '401 "Unauthorized"' exception
+
diff --git a/features/api/data/show_data_bag_api.feature b/features/api/data/show_data_bag_api.feature
new file mode 100644
index 0000000000..d27f64093a
--- /dev/null
+++ b/features/api/data/show_data_bag_api.feature
@@ -0,0 +1,44 @@
+@api @data @api_data
+Feature: Show a data_bag via the REST API
+ In order to know what the details are for a data_bag
+ As a Developer
+ I want to show the details for a specific data_bag
+
+ Scenario: Show a data_bag with no entries in it
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/data/users'
+ Then the inflated response should be an empty array
+
+ Scenario: Show a data_bag with one entry in it
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/data/users'
+ Then the inflated response should include '/data/users/francis'
+
+ Scenario: Show a data_bag with two entries in it
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ And a 'data_bag_item' named 'axl_rose' exists
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/data/users'
+ Then the inflated response should include '/data/users/francis'
+ And the inflated response should include '/data/users/axl_rose'
+
+ Scenario: Show a missing data_bag
+ Given a 'registration' named 'bobo' exists
+ And there are no data_bags
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/data/users'
+ Then I should get a '404 "Not Found"' exception
+
+ Scenario: Show a data_bag without authenticating
+ Given a 'data_bag' named 'users' exists
+ And I 'GET' the path '/data/users'
+ Then I should get a '401 "Unauthorized"' exception
+
+
diff --git a/features/api/data/show_data_bag_item_api.feature b/features/api/data/show_data_bag_item_api.feature
new file mode 100644
index 0000000000..fd0e474224
--- /dev/null
+++ b/features/api/data/show_data_bag_item_api.feature
@@ -0,0 +1,28 @@
+@api @data @api_data @api_data_item
+Feature: Show a data_bag item via the REST API
+ In order to know what the data is for an item in a data_bag
+ As a Developer
+ I want to retrieve an item from a data_bag
+
+ Scenario: Show a data_bag item
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/data/users/francis'
+ Then the inflated responses key 'id' should match '^francis$'
+
+ Scenario: Show a missing data_bag item
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/data/users/francis'
+ Then I should get a '404 "Not Found"' exception
+
+ Scenario: Show a data_bag item without authenticating
+ Given a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ And I 'GET' the path '/data/users/francis'
+ Then I should get a '401 "Unauthorized"' exception
+
+
diff --git a/features/api/nodes/cookbook_sync_api.feature b/features/api/nodes/cookbook_sync_api.feature
new file mode 100644
index 0000000000..3d39796166
--- /dev/null
+++ b/features/api/nodes/cookbook_sync_api.feature
@@ -0,0 +1,25 @@
+@api @nodes @cookbook_sync @api_nodes
+Feature: Synchronize cookbooks to the edge
+ In order to configure my nodes centrally
+ As a Developer
+ I want to synchronize the cookbooks from the server to the edge nodes
+
+ Scenario: Retrieve the list of cookbook files to synchronize
+ Given a 'registration' named 'bobo' exists
+ And a 'node' named 'sync' exists
+ When I 'GET' the path '/nodes/sync/cookbooks'
+ And the inflated responses key 'node_cookbook_sync' should exist
+ And the inflated responses key 'node_cookbook_sync' should match '"recipes":' as json
+ And the inflated responses key 'node_cookbook_sync' should match 'default.rb' as json
+ And the inflated responses key 'node_cookbook_sync' should match '"definitions":' as json
+ And the inflated responses key 'node_cookbook_sync' should match 'def_file.rb' as json
+ And the inflated responses key 'node_cookbook_sync' should match '"libraries":' as json
+ And the inflated responses key 'node_cookbook_sync' should match 'lib_file.rb' as json
+ And the inflated responses key 'node_cookbook_sync' should match '"attributes":' as json
+ And the inflated responses key 'node_cookbook_sync' should match 'attr_file.rb' as json
+
+ Scenario: Retrieve the list of cookbook files to synchronize with a wrong private key
+ Given a 'registration' named 'bobo' exists
+ And a 'node' named 'sync' exists
+ When I 'GET' the path '/nodes/sync/cookbooks' using a wrong private key
+ Then I should get a '401 "Unauthorized"' exception \ No newline at end of file
diff --git a/features/api/nodes/create_node_api.feature b/features/api/nodes/create_node_api.feature
index 5012a8a357..ec5bbb73be 100644
--- a/features/api/nodes/create_node_api.feature
+++ b/features/api/nodes/create_node_api.feature
@@ -1,4 +1,4 @@
-@api @nodes @nodes_create
+@api @api_nodes @nodes_create
Feature: Create a node via the REST API
In order to create nodes programatically
As a Devleoper
@@ -7,19 +7,19 @@ Feature: Create a node via the REST API
Scenario: Create a new node
Given a 'registration' named 'bobo' exists
And a 'node' named 'webserver'
- When I authenticate as 'bobo'
- And I 'POST' the 'node' to the path '/nodes'
+ When I 'POST' the 'node' to the path '/nodes'
And the inflated responses key 'uri' should match '^http://.+/nodes/webserver$'
Scenario: Create a node that already exists
Given a 'registration' named 'bobo' exists
And an 'node' named 'webserver'
- When I authenticate as 'bobo'
- And I 'POST' the 'node' to the path '/nodes'
+ When I 'POST' the 'node' to the path '/nodes'
And I 'POST' the 'node' to the path '/nodes'
Then I should get a '403 "Forbidden"' exception
-
- Scenario: Create a new node without authenticating
- Given a 'node' named 'webserver'
- When I 'POST' the 'node' to the path '/nodes'
+
+ Scenario: Create a node with a wrong private key
+ Given a 'registration' named 'bobo' exists
+ And an 'node' named 'webserver'
+ When I 'POST' the 'node' to the path '/nodes' using a wrong private key
Then I should get a '401 "Unauthorized"' exception
+
diff --git a/features/api/nodes/delete_node_api.feature b/features/api/nodes/delete_node_api.feature
index 7addc6e790..46c027561d 100644
--- a/features/api/nodes/delete_node_api.feature
+++ b/features/api/nodes/delete_node_api.feature
@@ -1,4 +1,4 @@
-@api @nodes @nodes_delete
+@api @api_nodes @nodes_delete
Feature: Delete a node via the REST API
In order to remove a node
As a Developer
@@ -7,19 +7,18 @@ Feature: Delete a node via the REST API
Scenario: Delete a node
Given a 'registration' named 'bobo' exists
And a 'node' named 'webserver' exists
- When I authenticate as 'bobo'
- And I 'DELETE' the path '/nodes/webserver'
+ When I 'DELETE' the path '/nodes/webserver'
Then the inflated response should respond to 'name' with 'webserver'
Scenario: Delete a node that does not exist
Given a 'registration' named 'bobo' exists
And there are no nodes
- When I authenticate as 'bobo'
When I 'DELETE' the path '/nodes/webserver'
Then I should get a '404 "Not Found"' exception
- Scenario: Delete a node without authenticating
- Given a 'node' named 'webserver'
- When I 'DELETE' the path '/nodes/webserver'
+ Scenario: Delete a node with a wrong private key
+ Given a 'registration' named 'bobo' exists
+ And a 'node' named 'webserver' exists
+ When I 'DELETE' the path '/nodes/webserver' using a wrong private key
Then I should get a '401 "Unauthorized"' exception
diff --git a/features/api/nodes/list_nodes_api.feature b/features/api/nodes/list_nodes_api.feature
index 4489c4d49a..7127720947 100644
--- a/features/api/nodes/list_nodes_api.feature
+++ b/features/api/nodes/list_nodes_api.feature
@@ -1,4 +1,4 @@
-@api @nodes @nodes_list
+@api @api_nodes @nodes_list
Feature: List nodes via the REST API
In order to know what nodes exists programatically
As a Developer
@@ -7,28 +7,26 @@ Feature: List nodes via the REST API
Scenario: List nodes when none have been created
Given a 'registration' named 'bobo' exists
And there are no nodes
- When I authenticate as 'bobo'
- And I 'GET' the path '/nodes'
+ When I 'GET' the path '/nodes'
Then the inflated response should be an empty array
Scenario: List nodes when one has been created
Given a 'registration' named 'bobo' exists
Given a 'node' named 'webserver' exists
- When I authenticate as 'bobo'
- And I 'GET' the path '/nodes'
+ When I 'GET' the path '/nodes'
Then the inflated response should include '^http://.+/nodes/webserver$'
-
+
Scenario: List nodes when two have been created
Given a 'registration' named 'bobo' exists
And a 'node' named 'webserver' exists
And a 'node' named 'dbserver' exists
- When I authenticate as 'bobo'
- And I 'GET' the path '/nodes'
+ When I 'GET' the path '/nodes'
Then the inflated response should be '2' items long
And the inflated response should include '^http://.+/nodes/webserver$'
And the inflated response should include '^http://.+/nodes/dbserver$'
- Scenario: List nodes when you are not authenticated
- When I 'GET' the path '/nodes'
+ Scenario: List nodes none have been created with a wrong private key
+ Given a 'registration' named 'bobo' exists
+ And there are no cookbooks
+ When I 'GET' the path '/nodes' using a wrong private key
Then I should get a '401 "Unauthorized"' exception
-
diff --git a/features/api/nodes/show_node_api.feature b/features/api/nodes/show_node_api.feature
index 8a2ab2990d..e26d127a06 100644
--- a/features/api/nodes/show_node_api.feature
+++ b/features/api/nodes/show_node_api.feature
@@ -1,4 +1,4 @@
-@api @nodes @nodes_show
+@api @api_nodes @nodes_show
Feature: Show a node via the REST API
In order to know what the details are for a node
As a Developer
@@ -7,19 +7,18 @@ Feature: Show a node via the REST API
Scenario: Show a node
Given a 'registration' named 'bobo' exists
And a 'node' named 'webserver' exists
- When I authenticate as 'bobo'
- And I 'GET' the path '/nodes/webserver'
+ When I 'GET' the path '/nodes/webserver'
Then the inflated response should respond to 'name' with 'webserver'
Scenario: Show a missing node
Given a 'registration' named 'bobo' exists
And there are no nodes
- When I authenticate as 'bobo'
- And I 'GET' the path '/nodes/bobo'
+ When I 'GET' the path '/nodes/bobo'
Then I should get a '404 "Not Found"' exception
- Scenario: Show a node without authenticating
- Given a 'node' named 'webserver' exists
- And I 'GET' the path '/nodes/webserver'
+ Scenario: Show a node with a wrong private key
+ Given a 'registration' named 'bobo' exists
+ And a 'node' named 'webserver' exists
+ When I 'GET' the path '/nodes/webserver' using a wrong private key
Then I should get a '401 "Unauthorized"' exception
diff --git a/features/api/nodes/update_node_api.feature b/features/api/nodes/update_node_api.feature
index ecf512f64f..4f6ad41f1b 100644
--- a/features/api/nodes/update_node_api.feature
+++ b/features/api/nodes/update_node_api.feature
@@ -1,4 +1,4 @@
-@api @nodes @nodes_update
+@api @api_nodes @nodes_update
Feature: Update a node
In order to keep my node data up-to-date
As a Developer
@@ -8,7 +8,6 @@ Feature: Update a node
Given a 'registration' named 'bobo' exists
And a 'node' named 'webserver' exists
And sending the method '<method>' to the 'node' with '<updated_value>'
- When I authenticate as 'bobo'
When I 'PUT' the 'node' to the path '/nodes/webserver'
Then the inflated response should respond to '<method>' with '<updated_value>'
When I 'GET' the path '/nodes/webserver'
@@ -19,9 +18,10 @@ Feature: Update a node
| run_list | [ "recipe[one]", "recipe[two]" ] |
| snakes | really arent so bad |
- Scenario: Update a node without authenticating
- Given a 'node' named 'webserver'
- And sending the method 'snakes' to the 'node' with 'night train'
- When I 'PUT' the 'node' to the path '/nodes/webserver'
- Then I should get a '401 "Unauthorized"' exception
+ Scenario Outline: Update a node with a wrong private key
+ Given a 'registration' named 'bobo' exists
+ And a 'node' named 'webserver' exists
+ And sending the method 'run_list' to the 'node' with '[ "recipe[one]", "recipe[two]" ]'
+ When I 'PUT' the 'node' to the path '/nodes/webserver' using a wrong private key
+ Then I should get a '401 "Unauthorized"' exception
diff --git a/features/api/roles/create_role_api.feature b/features/api/roles/create_role_api.feature
index 7b04b64294..4282400c7f 100644
--- a/features/api/roles/create_role_api.feature
+++ b/features/api/roles/create_role_api.feature
@@ -1,4 +1,4 @@
-@api @roles @roles_create
+@api @api_roles @roles_create
Feature: Create a role via the REST API
In order to create roles programatically
As a Devleoper
@@ -7,20 +7,18 @@ Feature: Create a role via the REST API
Scenario: Create a new role
Given a 'registration' named 'bobo' exists
And a 'role' named 'webserver'
- When I authenticate as 'bobo'
- And I 'POST' the 'role' to the path '/roles'
+ When I 'POST' the 'role' to the path '/roles'
And the inflated responses key 'uri' should match '^http://.+/roles/webserver$'
Scenario: Create a role that already exists
Given a 'registration' named 'bobo' exists
And an 'role' named 'webserver'
- When I authenticate as 'bobo'
- And I 'POST' the 'role' to the path '/roles'
+ When I 'POST' the 'role' to the path '/roles'
And I 'POST' the 'role' to the path '/roles'
Then I should get a '403 "Forbidden"' exception
- Scenario: Create a new role without authenticating
- Given a 'role' named 'webserver'
- When I 'POST' the 'role' to the path '/roles'
+ Scenario: Create a new role with a wrong private key
+ Given a 'registration' named 'bobo' exists
+ And a 'role' named 'webserver'
+ When I 'POST' the 'role' to the path '/roles' using a wrong private key
Then I should get a '401 "Unauthorized"' exception
-
diff --git a/features/api/roles/delete_role_api.feature b/features/api/roles/delete_role_api.feature
index c6504741f9..0dab4128af 100644
--- a/features/api/roles/delete_role_api.feature
+++ b/features/api/roles/delete_role_api.feature
@@ -1,4 +1,4 @@
-@api @roles @roles_delete
+@api @api_roles @roles_delete
Feature: Delete a Role via the REST API
In order to remove a role
As a Developer
@@ -7,19 +7,18 @@ Feature: Delete a Role via the REST API
Scenario: Delete a Role
Given a 'registration' named 'bobo' exists
And a 'role' named 'webserver' exists
- When I authenticate as 'bobo'
- And I 'DELETE' the path '/roles/webserver'
+ When I 'DELETE' the path '/roles/webserver'
Then the inflated response should respond to 'name' with 'webserver'
Scenario: Delete a Role that does not exist
Given a 'registration' named 'bobo' exists
And there are no roles
- When I authenticate as 'bobo'
When I 'DELETE' the path '/roles/webserver'
Then I should get a '404 "Not Found"' exception
-
- Scenario: Delete a Role without authenticating
- Given a 'role' named 'webserver'
- When I 'DELETE' the path '/roles/webserver'
+
+ Scenario: Delete a Role with a wrong private key
+ Given a 'registration' named 'bobo' exists
+ And a 'role' named 'webserver' exists
+ When I 'DELETE' the path '/roles/webserver' using a wrong private key
Then I should get a '401 "Unauthorized"' exception
diff --git a/features/api/roles/list_roles_api.feature b/features/api/roles/list_roles_api.feature
index edc96e0869..72251d2251 100644
--- a/features/api/roles/list_roles_api.feature
+++ b/features/api/roles/list_roles_api.feature
@@ -1,4 +1,4 @@
-@api @roles @roles_list
+@api @api_roles @roles_list
Feature: List roles via the REST API
In order to know what roles exists programatically
As a Developer
@@ -6,30 +6,29 @@ Feature: List roles via the REST API
Scenario: List roles when none have been created
Given a 'registration' named 'bobo' exists
- And there are no roles
- When I authenticate as 'bobo'
- And I 'GET' the path '/roles'
- Then the inflated response should be an empty array
+ And there are no roles
+ When I 'GET' the path '/roles'
+ Then the inflated response should be '1' items long
Scenario: List roles when one has been created
Given a 'registration' named 'bobo' exists
Given a 'role' named 'webserver' exists
- When I authenticate as 'bobo'
- And I 'GET' the path '/roles'
+ When I 'GET' the path '/roles'
Then the inflated response should include '^http://.+/roles/webserver$'
Scenario: List roles when two have been created
Given a 'registration' named 'bobo' exists
And a 'role' named 'webserver' exists
And a 'role' named 'db' exists
- When I authenticate as 'bobo'
- And I 'GET' the path '/roles'
+ When I 'GET' the path '/roles'
Then the inflated response should be '3' items long
And the inflated response should include '^http://.+/roles/role_test$'
And the inflated response should include '^http://.+/roles/webserver$'
And the inflated response should include '^http://.+/roles/db$'
- Scenario: List roles when you are not authenticated
- When I 'GET' the path '/roles'
+ Scenario: List roles when none have been created with a wrong private key
+ Given a 'registration' named 'bobo' exists
+ And there are no roles
+ When I 'GET' the path '/roles' using a wrong private key
Then I should get a '401 "Unauthorized"' exception
diff --git a/features/api/roles/show_roles_api.feature b/features/api/roles/show_roles_api.feature
index 70fe4aad60..5a7a5e2734 100644
--- a/features/api/roles/show_roles_api.feature
+++ b/features/api/roles/show_roles_api.feature
@@ -1,4 +1,4 @@
-@api @roles @roles_show
+@api @api_roles @roles_show
Feature: Show a role via the REST API
In order to know what the details are for a Role
As a Developer
@@ -7,20 +7,18 @@ Feature: Show a role via the REST API
Scenario: Show a role
Given a 'registration' named 'bobo' exists
And a 'role' named 'webserver' exists
- When I authenticate as 'bobo'
- And I 'GET' the path '/roles/webserver'
+ When I 'GET' the path '/roles/webserver'
Then the inflated response should respond to 'name' with 'webserver'
Scenario: Show a missing role
Given a 'registration' named 'bobo' exists
And there are no roles
- When I authenticate as 'bobo'
- And I 'GET' the path '/roles/bobo'
+ When I 'GET' the path '/roles/bobo'
Then I should get a '404 "Not Found"' exception
- Scenario: Show a role without authenticating
- Given a 'role' named 'webserver' exists
- And I 'GET' the path '/roles/webserver'
+ Scenario: Show a role with a wrong private key
+ Given a 'registration' named 'bobo' exists
+ And a 'role' named 'webserver' exists
+ When I 'GET' the path '/roles/webserver' using a wrong private key
Then I should get a '401 "Unauthorized"' exception
-
diff --git a/features/api/roles/update_roles_api.feature b/features/api/roles/update_roles_api.feature
index 90a6282577..ea72f68c05 100644
--- a/features/api/roles/update_roles_api.feature
+++ b/features/api/roles/update_roles_api.feature
@@ -1,4 +1,4 @@
-@api @roles @roles_update
+@api @api_roles @roles_update
Feature: Update a role
In order to keep my role data up-to-date
As a Developer
@@ -8,7 +8,6 @@ Feature: Update a role
Given a 'registration' named 'bobo' exists
And a 'role' named 'webserver' exists
And sending the method '<method>' to the 'role' with '<updated_value>'
- When I authenticate as 'bobo'
When I 'PUT' the 'role' to the path '/roles/webserver'
Then the inflated response should respond to '<method>' with '<updated_value>'
When I 'GET' the path '/roles/webserver'
@@ -21,9 +20,9 @@ Feature: Update a role
| default_attributes | { "a": "d" } |
| override_attributes | { "c": "e" } |
- Scenario: Update a role without authenticating
- Given a 'role' named 'webserver'
- And sending the method 'description' to the 'role' with 'Is easy'
- When I 'PUT' the 'role' to the path '/roles/webserver'
+ Scenario Outline: Update a role with a wrong private key
+ Given a 'registration' named 'bobo' exists
+ And a 'role' named 'webserver' exists
+ And sending the method '<method>' to the 'role' with '<updated_value>'
+ When I 'PUT' the 'role' to the path '/roles/webserver' using a wrong private key
Then I should get a '401 "Unauthorized"' exception
-
diff --git a/features/api/search/list_search.feature b/features/api/search/list_search.feature
new file mode 100644
index 0000000000..198f065f12
--- /dev/null
+++ b/features/api/search/list_search.feature
@@ -0,0 +1,29 @@
+@api @data @api_search @api_search_list
+Feature: List search endpoints via the REST API
+ In order to know what search endpoints exist programatically
+ As a Developer
+ I want to list all the search indexes
+
+ Scenario: List search indexes when no data bags have been created
+ Given a 'registration' named 'bobo' exists
+ And there are no data bags
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/search'
+ Then the inflated response should include '^http://(.+)/search/node$'
+ And the inflated response should include '^http://(.+)/search/role$'
+ And the inflated response should be '2' items long
+
+ Scenario: List search indexes when a data bag has been created
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/search'
+ Then the inflated response should include '^http://(.+)/search/node$'
+ And the inflated response should include '^http://(.+)/search/role$'
+ And the inflated response should include '^http://(.+)/search/users$'
+ And the inflated response should be '3' items long
+
+ Scenario: List search indexes when you are not authenticated
+ When I 'GET' the path '/search'
+ Then I should get a '401 "Unauthorized"' exception
+
diff --git a/features/api/search/show_search.feature b/features/api/search/show_search.feature
new file mode 100644
index 0000000000..292d043b0d
--- /dev/null
+++ b/features/api/search/show_search.feature
@@ -0,0 +1,115 @@
+@api @data @api_search @api_search_show
+Feature: Search data via the REST API
+ In order to know about objects in the system
+ As a Developer
+ I want to search the objects
+
+ Scenario: Search for objects when none have been created
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/search/users'
+ Then the inflated responses key 'rows' should be '0' items long
+ And the inflated responses key 'start' should be the integer '0'
+ And the inflated responses key 'total' should be the integer '0'
+
+ Scenario: Search for objects when one has been created
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ And I wait for '10' seconds
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/search/users'
+ Then the inflated responses key 'rows' item '0' should be a kind of 'Chef::DataBagItem'
+ And the inflated responses key 'rows' item '0' key 'id' should be 'francis'
+ And the inflated responses key 'start' should be the integer '0'
+ And the inflated responses key 'total' should be the integer '1'
+
+ Scenario: Search for objects when two have been created
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ And a 'data_bag_item' named 'axl_rose' exists
+ And I wait for '10' seconds
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/search/users'
+ Then the inflated responses key 'rows' item '0' should be a kind of 'Chef::DataBagItem'
+ And the inflated responses key 'rows' item '0' key 'id' should be 'francis'
+ And the inflated responses key 'rows' item '1' should be a kind of 'Chef::DataBagItem'
+ And the inflated responses key 'rows' item '1' key 'id' should be 'axl_rose'
+ And the inflated responses key 'start' should be the integer '0'
+ And the inflated responses key 'total' should be the integer '2'
+
+ Scenario: Search for objects with a manual ascending sort order
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ And a 'data_bag_item' named 'axl_rose' exists
+ And I wait for '10' seconds
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/search/users?sort=id+asc'
+ Then the inflated responses key 'rows' item '0' should be a kind of 'Chef::DataBagItem'
+ And the inflated responses key 'rows' item '0' key 'id' should be 'axl_rose'
+ And the inflated responses key 'rows' item '1' should be a kind of 'Chef::DataBagItem'
+ And the inflated responses key 'rows' item '1' key 'id' should be 'francis'
+ And the inflated responses key 'start' should be the integer '0'
+ And the inflated responses key 'total' should be the integer '2'
+
+ Scenario: Search for objects with a manual descending sort order
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ And a 'data_bag_item' named 'axl_rose' exists
+ And I wait for '10' seconds
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/search/users?sort=id+desc'
+ Then the inflated responses key 'rows' item '0' should be a kind of 'Chef::DataBagItem'
+ And the inflated responses key 'rows' item '0' key 'id' should be 'francis'
+ And the inflated responses key 'rows' item '1' should be a kind of 'Chef::DataBagItem'
+ And the inflated responses key 'rows' item '1' key 'id' should be 'axl_rose'
+ And the inflated responses key 'start' should be the integer '0'
+ And the inflated responses key 'total' should be the integer '2'
+
+ Scenario: Search for objects and page through the results
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ And a 'data_bag_item' named 'axl_rose' exists
+ And I wait for '10' seconds
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/search/users?rows=1&sort=id+asc'
+ Then the inflated responses key 'rows' item '0' should be a kind of 'Chef::DataBagItem'
+ And the inflated responses key 'rows' item '0' key 'id' should be 'axl_rose'
+ And the inflated responses key 'rows' should be '1' items long
+ And the inflated responses key 'start' should be the integer '0'
+ And the inflated responses key 'total' should be the integer '2'
+ When I 'GET' the path '/search/users?rows=1&start=1&sort=id+asc'
+ Then the inflated responses key 'rows' item '0' should be a kind of 'Chef::DataBagItem'
+ And the inflated responses key 'rows' item '0' key 'id' should be 'francis'
+ And the inflated responses key 'rows' should be '1' items long
+ And the inflated responses key 'start' should be the integer '1'
+ And the inflated responses key 'total' should be the integer '2'
+
+ Scenario: Search for a subset of objects
+ Given a 'registration' named 'bobo' exists
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ And a 'data_bag_item' named 'axl_rose' exists
+ And I wait for '10' seconds
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/search/users?q=id:axl_rose'
+ Then the inflated responses key 'rows' item '0' should be a kind of 'Chef::DataBagItem'
+ And the inflated responses key 'rows' item '0' key 'id' should be 'axl_rose'
+ And the inflated responses key 'start' should be the integer '0'
+ And the inflated responses key 'total' should be the integer '1'
+
+ Scenario: Search for a type of object that does not exist
+ Given a 'registration' named 'bobo' exists
+ When I authenticate as 'bobo'
+ And I 'GET' the path '/search/funkensteins'
+ Then I should get a '404 "Not Found"' exception
+
+ Scenario: Search for objects when you are not authenticated
+ When I 'GET' the path '/search/users'
+ Then I should get a '401 "Unauthorized"' exception
+
diff --git a/features/data/config/client.rb b/features/data/config/client.rb
index ab10d750df..5e51b26c82 100644
--- a/features/data/config/client.rb
+++ b/features/data/config/client.rb
@@ -6,9 +6,15 @@ log_location STDOUT
file_cache_path File.join(tmpdir, "cache")
ssl_verify_mode :verify_none
registration_url "http://127.0.0.1:4000"
-openid_url "http://127.0.0.1:4001"
+openid_url "http://127.0.0.1:4000"
template_url "http://127.0.0.1:4000"
remotefile_url "http://127.0.0.1:4000"
search_url "http://127.0.0.1:4000"
-role_url "http://127.0.0.1:4000"
-couchdb_database 'chef_integration'
+role_url "http://127.0.0.1:4000"
+client_url "http://127.0.0.1:4000"
+chef_server_url "http://127.0.0.1:4000"
+validation_client_name "validator"
+systmpdir = File.expand_path(File.join(Dir.tmpdir, "chef_integration"))
+validation_key File.join(systmpdir, "validation.pem")
+client_key File.join(systmpdir, "client.pem")
+
diff --git a/features/data/config/server.rb b/features/data/config/server.rb
index 51a7bacef0..cc7db3a232 100644
--- a/features/data/config/server.rb
+++ b/features/data/config/server.rb
@@ -6,17 +6,30 @@ log_location STDOUT
file_cache_path File.join(tmpdir, "cache")
ssl_verify_mode :verify_none
registration_url "http://127.0.0.1:4000"
-openid_url "http://127.0.0.1:4001"
+openid_url "http://127.0.0.1:4000"
template_url "http://127.0.0.1:4000"
remotefile_url "http://127.0.0.1:4000"
search_url "http://127.0.0.1:4000"
-role_url "http://127.0.0.1:4000"
+role_url "http://127.0.0.1:4000"
+chef_server_url "http://127.0.0.1:4000"
+client_url "http://127.0.0.1:4000"
cookbook_path File.join(supportdir, "cookbooks")
openid_store_path File.join(tmpdir, "openid", "store")
openid_cstore_path File.join(tmpdir, "openid", "cstore")
search_index_path File.join(tmpdir, "search_index")
role_path File.join(supportdir, "roles")
-validation_token 'ceelo'
couchdb_database 'chef_integration'
+systmpdir = File.expand_path(File.join(Dir.tmpdir, "chef_integration"))
+
+validation_client_name "validator"
+validation_key File.join(systmpdir, "validation.pem")
+client_key File.join(systmpdir, "client.pem")
+
+solr_jetty_path File.join(supportdir, "solr", "jetty")
+solr_heap_size "250M"
+solr_data_path File.join(supportdir, "solr", "data")
+solr_home_path File.join(supportdir, "solr", "home")
+solr_heap_size "256M"
+
Chef::Log::Formatter.show_time = true
diff --git a/features/data/cookbooks/integration_setup/recipes/default.rb b/features/data/cookbooks/integration_setup/recipes/default.rb
index 0ada2aa485..fc913b4502 100644
--- a/features/data/cookbooks/integration_setup/recipes/default.rb
+++ b/features/data/cookbooks/integration_setup/recipes/default.rb
@@ -19,7 +19,7 @@
directory node[:int][:tmpdir] do
owner "root"
- mode 1777
+ mode "1777"
action :create
end
diff --git a/features/data/cookbooks/node_cookbook_sync/README.rdoc b/features/data/cookbooks/node_cookbook_sync/README.rdoc
new file mode 100644
index 0000000000..8d774805b9
--- /dev/null
+++ b/features/data/cookbooks/node_cookbook_sync/README.rdoc
@@ -0,0 +1,8 @@
+= DESCRIPTION:
+
+= REQUIREMENTS:
+
+= ATTRIBUTES:
+
+= USAGE:
+
diff --git a/features/data/cookbooks/node_cookbook_sync/attributes/attr_file.rb b/features/data/cookbooks/node_cookbook_sync/attributes/attr_file.rb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/node_cookbook_sync/attributes/attr_file.rb
diff --git a/features/data/cookbooks/node_cookbook_sync/definitions/def_file.rb b/features/data/cookbooks/node_cookbook_sync/definitions/def_file.rb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/node_cookbook_sync/definitions/def_file.rb
diff --git a/features/data/cookbooks/node_cookbook_sync/libraries/lib_file.rb b/features/data/cookbooks/node_cookbook_sync/libraries/lib_file.rb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/node_cookbook_sync/libraries/lib_file.rb
diff --git a/features/data/cookbooks/node_cookbook_sync/metadata.rb b/features/data/cookbooks/node_cookbook_sync/metadata.rb
new file mode 100644
index 0000000000..850a993fbb
--- /dev/null
+++ b/features/data/cookbooks/node_cookbook_sync/metadata.rb
@@ -0,0 +1,6 @@
+maintainer "Opscode"
+maintainer_email "do_not_reply@opscode.com"
+license "Apache 2.0"
+description "Installs/Configures node_cookbook_sync"
+long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc'))
+version "0.1"
diff --git a/features/data/cookbooks/node_cookbook_sync/recipes/default.rb b/features/data/cookbooks/node_cookbook_sync/recipes/default.rb
new file mode 100644
index 0000000000..7912164bf3
--- /dev/null
+++ b/features/data/cookbooks/node_cookbook_sync/recipes/default.rb
@@ -0,0 +1,18 @@
+#
+# Cookbook Name:: node_cookbook_sync
+# Recipe:: default
+#
+# Copyright 2009, Opscode
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
diff --git a/features/data/cookbooks/search/recipes/search_data.rb b/features/data/cookbooks/search/recipes/search_data.rb
index 6113da356f..6deefed9b3 100644
--- a/features/data/cookbooks/search/recipes/search_data.rb
+++ b/features/data/cookbooks/search/recipes/search_data.rb
@@ -17,11 +17,12 @@
# limitations under the License.
#
-node.save
-sleep 5
-search(:node, "*") do |entry|
- Chef::Log.error(entry.inspect)
- entry["search_files"].each do |filename|
- file "#{node[:tmpdir]}/#{filename}"
- end
+# We have to sleep at least 10 seconds to confirm that the data has made it
+# into the index. We can only rely on this because we are in a test environment
+# in real-land Chef, the index is only eventually consistent.. and may take a
+# variable amount of time.
+sleep 10
+search(:users, "*:*") do |entry|
+ file "#{node[:tmpdir]}/#{entry["id"]}"
end
+
diff --git a/features/steps/couchdb_steps.rb b/features/data/cookbooks/search/recipes/search_data_noblock.rb
index be8b84db0a..6fb9de6d81 100644
--- a/features/steps/couchdb_steps.rb
+++ b/features/data/cookbooks/search/recipes/search_data_noblock.rb
@@ -1,7 +1,8 @@
#
-# Author:: Adam Jacob (<adam@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
-# License:: Apache License, Version 2.0
+# Cookbook Name:: search
+# Recipe:: default
+#
+# Copyright 2009, Opscode
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -16,18 +17,16 @@
# limitations under the License.
#
-Before do
- system("mkdir -p #{tmpdir}")
- cdb = Chef::CouchDB.new(Chef::Config[:couchdb_url])
- cdb.create_db
- Chef::Node.create_design_document
- Chef::Role.create_design_document
- Chef::Role.sync_from_disk_to_couchdb
- Chef::OpenIDRegistration.create_design_document
-end
+# We have to sleep at least 10 seconds to confirm that the data has made it
+# into the index. We can only rely on this because we are in a test environment
+# in real-land Chef, the index is only eventually consistent.. and may take a
+# variable amount of time.
+
-After do
- r = Chef::REST.new(Chef::Config[:couchdb_url])
- r.delete_rest("#{Chef::Config[:couchdb_database]}/")
- system("rm -rf #{tmpdir}")
+sleep 10
+objects, start, rows = search(:users, "*:*")
+
+objects.each do |entry|
+ file "#{node[:tmpdir]}/#{entry["id"]}"
end
+
diff --git a/features/data/cookbooks/show_cookbook/README.rdoc b/features/data/cookbooks/show_cookbook/README.rdoc
new file mode 100644
index 0000000000..8d774805b9
--- /dev/null
+++ b/features/data/cookbooks/show_cookbook/README.rdoc
@@ -0,0 +1,8 @@
+= DESCRIPTION:
+
+= REQUIREMENTS:
+
+= ATTRIBUTES:
+
+= USAGE:
+
diff --git a/features/data/cookbooks/show_cookbook/attributes/attr_file.rb b/features/data/cookbooks/show_cookbook/attributes/attr_file.rb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/show_cookbook/attributes/attr_file.rb
diff --git a/features/data/cookbooks/show_cookbook/definitions/def_file.rb b/features/data/cookbooks/show_cookbook/definitions/def_file.rb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/show_cookbook/definitions/def_file.rb
diff --git a/features/data/cookbooks/show_cookbook/files/default/prime_time.txt b/features/data/cookbooks/show_cookbook/files/default/prime_time.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/show_cookbook/files/default/prime_time.txt
diff --git a/features/data/cookbooks/show_cookbook/files/host-latte/prime_time.txt b/features/data/cookbooks/show_cookbook/files/host-latte/prime_time.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/show_cookbook/files/host-latte/prime_time.txt
diff --git a/features/data/cookbooks/show_cookbook/files/mac_os_x-10.5/prime_time.txt b/features/data/cookbooks/show_cookbook/files/mac_os_x-10.5/prime_time.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/show_cookbook/files/mac_os_x-10.5/prime_time.txt
diff --git a/features/data/cookbooks/show_cookbook/files/mac_os_x/prime_time.txt b/features/data/cookbooks/show_cookbook/files/mac_os_x/prime_time.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/show_cookbook/files/mac_os_x/prime_time.txt
diff --git a/features/data/cookbooks/show_cookbook/libraries/lib_file.rb b/features/data/cookbooks/show_cookbook/libraries/lib_file.rb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/show_cookbook/libraries/lib_file.rb
diff --git a/features/data/cookbooks/show_cookbook/metadata.rb b/features/data/cookbooks/show_cookbook/metadata.rb
new file mode 100644
index 0000000000..6f06805f62
--- /dev/null
+++ b/features/data/cookbooks/show_cookbook/metadata.rb
@@ -0,0 +1,6 @@
+maintainer "Opscode"
+maintainer_email "do_not_reply@opscode.com"
+license "Apache 2.0"
+description "Installs/Configures show_cookbook"
+long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc'))
+version "1.0"
diff --git a/features/data/cookbooks/show_cookbook/recipes/default.rb b/features/data/cookbooks/show_cookbook/recipes/default.rb
new file mode 100644
index 0000000000..6f972192cb
--- /dev/null
+++ b/features/data/cookbooks/show_cookbook/recipes/default.rb
@@ -0,0 +1,18 @@
+#
+# Cookbook Name:: show_cookbook
+# Recipe:: default
+#
+# Copyright 2009, Opscode
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
diff --git a/features/data/cookbooks/show_cookbook/templates/default/prime_time.txt.erb b/features/data/cookbooks/show_cookbook/templates/default/prime_time.txt.erb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/show_cookbook/templates/default/prime_time.txt.erb
diff --git a/features/data/cookbooks/show_cookbook/templates/host-latte/prime_time.txt.erb b/features/data/cookbooks/show_cookbook/templates/host-latte/prime_time.txt.erb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/show_cookbook/templates/host-latte/prime_time.txt.erb
diff --git a/features/data/cookbooks/show_cookbook/templates/mac_os_x-10.5/prime_time.txt.erb b/features/data/cookbooks/show_cookbook/templates/mac_os_x-10.5/prime_time.txt.erb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/show_cookbook/templates/mac_os_x-10.5/prime_time.txt.erb
diff --git a/features/data/cookbooks/show_cookbook/templates/mac_os_x/prime_time.txt.erb b/features/data/cookbooks/show_cookbook/templates/mac_os_x/prime_time.txt.erb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/features/data/cookbooks/show_cookbook/templates/mac_os_x/prime_time.txt.erb
diff --git a/features/language/delayed_notifications.feature b/features/language/delayed_notifications.feature
index bc874f0f5e..511711c930 100644
--- a/features/language/delayed_notifications.feature
+++ b/features/language/delayed_notifications.feature
@@ -1,3 +1,4 @@
+@language
Feature: Delayed Notifications
In order to not impact the system we are configuring unduly
As a developer
diff --git a/features/provider/directory/create_directories.feature b/features/provider/directory/create_directories.feature
index 0d2ab35ab9..b2ac1b157c 100644
--- a/features/provider/directory/create_directories.feature
+++ b/features/provider/directory/create_directories.feature
@@ -1,3 +1,4 @@
+@provider @provider_directory
Feature: Create Directories
In order to save time
As a Developer
diff --git a/features/provider/directory/delete_directories.feature b/features/provider/directory/delete_directories.feature
index 1867e6c43c..e98ec2140e 100644
--- a/features/provider/directory/delete_directories.feature
+++ b/features/provider/directory/delete_directories.feature
@@ -1,3 +1,4 @@
+@provider @provider_directory
Feature: Delete Directories
In order to save time
As a Developer
diff --git a/features/provider/execute/run_commands.feature b/features/provider/execute/run_commands.feature
index 96a7a36da9..28dd70482a 100644
--- a/features/provider/execute/run_commands.feature
+++ b/features/provider/execute/run_commands.feature
@@ -1,3 +1,4 @@
+@provider @provider_execute
Feature: Run Commands
In order to utilize the plethora of useful command line utilities
As a Developer
diff --git a/features/provider/file/manage_files.feature b/features/provider/file/manage_files.feature
index 4034fb0f31..762fff3c62 100644
--- a/features/provider/file/manage_files.feature
+++ b/features/provider/file/manage_files.feature
@@ -1,3 +1,4 @@
+@provider @provider_file
Feature: Manage Files
In order to save time
As a Developer
diff --git a/features/search/search_data.feature b/features/search/search_data.feature
index 26b256b077..f6544b7f2e 100644
--- a/features/search/search_data.feature
+++ b/features/search/search_data.feature
@@ -4,11 +4,25 @@ Feature: Search Data
As a Developer
I want to search the data
- Scenario: Search the node index
+ Scenario: Search the user index
Given a validated node
And it includes the recipe 'search::search_data'
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ And a 'data_bag_item' named 'axl_rose' exists
When I run the chef-client
Then the run should exit '0'
- And a file named 'search_one.txt' should exist
- And a file named 'search_two.txt' should exist
+ And a file named 'francis' should exist
+ And a file named 'axl_rose' should exist
+
+ Scenario: Search the user index without a block
+ Given a validated node
+ And it includes the recipe 'search::search_data_noblock'
+ And a 'data_bag' named 'users' exists
+ And a 'data_bag_item' named 'francis' exists
+ And a 'data_bag_item' named 'axl_rose' exists
+ When I run the chef-client
+ Then the run should exit '0'
+ And a file named 'francis' should exist
+ And a file named 'axl_rose' should exist
diff --git a/features/steps/cookbook_steps.rb b/features/steps/cookbook_steps.rb
index 202b4c0eda..1c5c0276ae 100644
--- a/features/steps/cookbook_steps.rb
+++ b/features/steps/cookbook_steps.rb
@@ -1,5 +1,6 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Chris Walters (<cw@opscode.com>)
# Copyright:: Copyright (c) 2008 Opscode, Inc.
# License:: Apache License, Version 2.0
#
@@ -51,3 +52,4 @@ When /^I run the rake task to generate cookbook metadata$/ do
end
end
end
+
diff --git a/features/steps/fixture_steps.rb b/features/steps/fixture_steps.rb
index 6c9e8c9ed1..5239e5d762 100644
--- a/features/steps/fixture_steps.rb
+++ b/features/steps/fixture_steps.rb
@@ -1,19 +1,53 @@
+require 'ostruct'
+
Before do
@fixtures = {
+ 'signing_caller' =>{
+ :user_id=>'bobo', :secret_key => "/tmp/poop.pem"
+ },
'registration' => {
'bobo' => Proc.new do
- r = Chef::OpenIDRegistration.new
- r.name = "bobo"
- r.set_password('tclown')
- r.validated = true
- r.admin = true
- r
+
+ OpenStruct.new({ :save => true })
+ #Chef::CouchDB.new(Chef::Config[:couchdb_url], "chef_integration"))
+ end
+ },
+ 'data_bag' => {
+ 'users' => Proc.new do
+ b = Chef::DataBag.new(Chef::CouchDB.new(nil, "chef_integration"))
+ b.name "users"
+ b
+ end,
+ 'rubies' => Proc.new do
+ b = Chef::DataBag.new(Chef::CouchDB.new(nil, "chef_integration"))
+ b.name "rubies"
+ b
+ end
+ },
+ 'data_bag_item' => {
+ 'francis' => Proc.new do
+ i = Chef::DataBagItem.new(Chef::CouchDB.new(nil, "chef_integration"))
+ i.data_bag "users"
+ i.raw_data = { "id" => "francis" }
+ i
+ end,
+ 'francis_extra' => Proc.new do
+ i = Chef::DataBagItem.new(Chef::CouchDB.new(nil, "chef_integration"))
+ i.data_bag "users"
+ i.raw_data = { "id" => "francis", "extra" => "majority" }
+ i
+ end,
+ 'axl_rose' => Proc.new do
+ i = Chef::DataBagItem.new(Chef::CouchDB.new(nil, "chef_integration"))
+ i.data_bag "users"
+ i.raw_data = { "id" => "axl_rose" }
+ i
end
},
'role' => {
'webserver' => Proc.new do
- r = Chef::Role.new
+ r = Chef::Role.new(Chef::CouchDB.new(nil, "chef_integration"))
r.name "webserver"
r.description "monkey"
r.recipes("role::webserver", "role::base")
@@ -22,7 +56,7 @@ Before do
r
end,
'db' => Proc.new do
- r = Chef::Role.new
+ r = Chef::Role.new(Chef::CouchDB.new(nil, "chef_integration"))
r.name "db"
r.description "monkey"
r.recipes("role::db", "role::base")
@@ -33,7 +67,7 @@ Before do
},
'node' => {
'webserver' => Proc.new do
- n = Chef::Node.new
+ n = Chef::Node.new(Chef::CouchDB.new(nil, "chef_integration"))
n.name 'webserver'
n.run_list << "tacos"
n.snakes "on a plane"
@@ -41,17 +75,34 @@ Before do
n
end,
'dbserver' => Proc.new do
- n = Chef::Node.new
+ n = Chef::Node.new(Chef::CouchDB.new(nil, "chef_integration"))
n.name 'dbserver'
n.run_list << "oracle"
n.just "kidding - who uses oracle?"
n
+ end,
+ 'sync' => Proc.new do
+ n = Chef::Node.new(Chef::CouchDB.new(nil, "chef_integration"))
+ n.name 'sync'
+ n.run_list << "node_cookbook_sync"
+ n
end
}
}
@stash = {}
end
+def sign_request(http_method, private_key, user_id, body = "")
+ timestamp = Time.now.utc.iso8601
+ sign_obj = Mixlib::Auth::SignedHeaderAuth.signing_object(
+ :http_method=>http_method,
+ :body=>body,
+ :user_id=>user_id,
+ :timestamp=>timestamp)
+ signed = sign_obj.sign(private_key).merge({:host => "localhost"})
+ signed.inject({}){|memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1];memo}
+end
+
def get_fixture(stash_name, stash_key)
fixy = @fixtures[stash_name][stash_key]
if fixy.kind_of?(Proc)
@@ -62,20 +113,35 @@ def get_fixture(stash_name, stash_key)
end
Given /^an? '(.+)' named '(.+)'$/ do |stash_name, stash_key|
- @stash[stash_name] = get_fixture(stash_name, stash_key)
+ # BUGBUG: I need to reference fixtures individually, but the fixtures, as written, store under the type, not the fixture's identifier and I don't currently have time to re-write the tests
+
+ key = case stash_name
+ when 'file','hash'
+ stash_key
+ else
+ stash_name
+ end
+ @stash[key] = get_fixture(stash_name, stash_key)
end
-Given /^an? '(.+)' named '(.+)' exists$/ do |stash_name, stash_key|
+Given /^an? '(.+)' named '(.+)' exists$/ do |stash_name, stash_key|
@stash[stash_name] = get_fixture(stash_name, stash_key)
- if @stash[stash_name].respond_to?(:save)
- @stash[stash_name].save
- else
- request("/#{stash_name.pluralize}", {
- :method => "POST",
- "HTTP_ACCEPT" => 'application/json',
- "CONTENT_TYPE" => 'application/json',
- :input => @stash[stash_name].to_json
- })
+
+ if stash_name == 'registration'
+ r = Chef::REST.new(Chef::Config[:registration_url], Chef::Config[:validation_user], Chef::Config[:validation_key])
+ r.register("bobo", "#{tmpdir}/bobo.pem")
+ @rest = Chef::REST.new(Chef::Config[:registration_url], 'bobo', "#{tmpdir}/bobo.pem")
+ else
+ if @stash[stash_name].respond_to?(:save)#stash_name == "registration"
+ @stash[stash_name].save
+ else
+ request("#{stash_name.pluralize}", {
+ :method => "POST",
+ "HTTP_ACCEPT" => 'application/json',
+ "CONTENT_TYPE" => 'application/json',
+ :input => @stash[stash_name].to_json
+ }.merge(sign_request("POST", OpenSSL::PKey::RSA.new(IO.read("#{tmpdir}/client.pem")), "bobo")))
+ end
end
end
@@ -98,3 +164,7 @@ Given /^there are no (.+)$/ do |stash_name|
Chef::Role.list(true).each { |r| r.destroy }
end
end
+
+Given /^I wait for '(\d+)' seconds$/ do |time|
+ sleep time.to_i
+end
diff --git a/features/steps/node_steps.rb b/features/steps/node_steps.rb
index d1aa9a0045..3f4f76b870 100644
--- a/features/steps/node_steps.rb
+++ b/features/steps/node_steps.rb
@@ -20,15 +20,15 @@
# Given
###
Given /^a validated node$/ do
- client.validation_token = Chef::Config[:validation_token] = 'ceelo'
+ client.determine_node_name
client.register
- client.authenticate
client.build_node
client.node.recipes << "integration_setup"
end
Given /^it includes the recipe '(.+)'$/ do |recipe|
self.recipe = recipe
+ Chef::Log.error("It's like this we have: #{Chef::Config[:chef_server_url]}")
client.node.recipes << recipe
client.save_node
end
diff --git a/features/steps/request_steps.rb b/features/steps/request_steps.rb
index 156fc88b3a..892195e80e 100644
--- a/features/steps/request_steps.rb
+++ b/features/steps/request_steps.rb
@@ -1,21 +1,55 @@
-When /^I '(.+)' the path '(.+)'$/ do |http_method, request_uri|
+When /^I '([^']*)' (?:to )?the path '([^']*)'$/ do |http_method, request_uri|
begin
self.response = rest.send("#{http_method}_rest".downcase.to_sym, request_uri)
- self.inflated_response = self.response
+ self.inflated_response = self.response
rescue
+ Chef::Log.debug("Caught exception in request: #{$!.message}")
self.exception = $!
end
end
+When /^I '([^']*)' to the path '(.+)'$/ do |http_method, request_uri|
+ When "I '#{http_method}' the path '#{request_uri}'"
+end
+
+When /^I '(.+)' the path '(.+)' using a wrong private key$/ do |http_method, request_uri|
+ key = OpenSSL::PKey::RSA.generate(2048)
+ File.open(File.join(tmpdir, 'false_key.pem'), "w") { |f| f.print key }
+ @rest = Chef::REST.new(Chef::Config[:chef_server_url], 'snakebite' , File.join(tmpdir, 'false_key.pem'))
+
+ When "I '#{http_method}' the path '#{request_uri}'"
+end
+
When /^I '(.+)' the '(.+)' to the path '(.+)'$/ do |http_method, stash_key, request_uri|
begin
- self.response = rest.send("#{http_method}_rest".downcase.to_sym, request_uri, stash[stash_key])
+ self.response = rest.send("#{http_method.to_s.downcase}_rest".downcase.to_sym, request_uri, stash[stash_key])
self.inflated_response = response
rescue
self.exception = $!
end
end
+When /^I '(.+)' the '(.+)' to the path '(.+)' using a wrong private key$/ do |http_method, stash_key, request_uri|
+ key = OpenSSL::PKey::RSA.generate(2048)
+ File.open(File.join(tmpdir, 'false_key.pem'), "w") { |f| f.print key }
+ @rest = Chef::REST.new(Chef::Config[:chef_server_url], 'snakebite' , File.join(tmpdir, 'false_key.pem'))
+
+ When "I '#{http_method}' the '#{stash_key}' to the path '#{request_uri}'"
+end
+
+When /^I delete local private key/ do
+ Chef::FileCache.delete("private_key.pem")
+end
+
+When /^I register '(.+)'$/ do |user|
+ begin
+ rest = Chef::REST.new(Chef::Config[:registration_url])
+ rest.register("bobo")
+ rescue
+ self.exception = $!
+ end
+end
+
When /^I authenticate as '(.+)'$/ do |reg|
begin
rest.authenticate(reg, 'tclown')
@@ -24,3 +58,59 @@ When /^I authenticate as '(.+)'$/ do |reg|
end
end
+
+
+
+# When /^I '(.+)' the path '(.+)'$/ do |http_method, request_uri|
+# begin
+# #if http_method.downcase == 'get'
+# # self.response = @rest.get_rest(request_uri)
+# #else
+# #puts "test test test \n\n\n\n\n\n\n"
+# @response = @rest.send("#{http_method}_rest".downcase.to_sym, request_uri)
+# #end
+# puts "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+# puts @response
+# puts @response['content-type']
+# #puts self.response
+# #puts self.response.inspect
+# #self.inflated_response = self.response
+# @inflated_response = @response#JSON.parse(response.body.to_s)
+# puts "~~~~~~~~INFLATED RESPONSE~~~~~~~~~~~~"
+# puts @inflated_response
+# rescue
+# self.exception = $!
+# end
+# end
+#
+# When /^I '(.+)' the '(.+)' to the path '(.+)'$/ do |http_method, stash_key, request_uri|
+# begin
+# #if http_method.downcase == 'post'
+# # puts "post request"
+# # self.response = @rest.post_rest(request_uri, @stash[stash_key])
+# # puts self.response
+# #else
+# puts "This is the request -- @stash[stash_key]:"
+# puts @stash[stash_key].to_s
+# @response = @rest.send("#{http_method}_rest".downcase.to_sym, request_uri, @stash[stash_key])
+# #end
+# puts "This is the response:"
+# #puts self.response.body.to_s
+# puts @response
+# #self.inflated_response = response
+# @inflated_response = @response#JSON.parse(self.response.body.to_s)
+# puts "~~~~~~~~INFLATED RESPONSE~~~~~~~~~~~~"
+# puts @inflated_response
+# rescue
+# self.exception = $!
+# end
+# end
+#
+# When /^I authenticate as '(.+)'$/ do |reg|
+# begin
+# rest.authenticate(reg, 'tclown')
+# rescue
+# self.exception = $!
+# end
+# end
+#
diff --git a/features/steps/response_steps.rb b/features/steps/response_steps.rb
index 3f3ef28459..0c0fc2d6ab 100644
--- a/features/steps/response_steps.rb
+++ b/features/steps/response_steps.rb
@@ -3,70 +3,101 @@ Then /^I should get a '(.+)' exception$/ do |exception|
end
Then /^the response code should be '(.+)'$/ do |response_code|
- response.status.should == response_code.to_i
+ self.response.status.should == response_code.to_i
+end
+
+Then /^the inflated responses key '(.+)' should be the integer '(\d+)'$/ do |key, int|
+ inflated_response[key].should == int.to_i
end
Then /^the inflated responses key '(.+)' should match '(.+)'$/ do |key, regex|
- inflated_response[key].should =~ /#{regex}/
+ puts self.inflated_response.inspect if ENV['DEBUG']
+ self.inflated_response[key].should =~ /#{regex}/m
+end
+
+Then /^the inflated responses key '(.+)' should match '(.+)' as json$/ do |key, regex|
+ puts self.inflated_response.inspect if ENV["DEBUG"]
+ self.inflated_response[key].to_json.should =~ /#{regex}/m
+end
+
+Then /^the inflated responses key '(.+)' item '(\d+)' should be a kind of '(.+)'$/ do |key, index, constant|
+ inflated_response[key][index.to_i].should be_a_kind_of(eval(constant))
+end
+
+Then /^the inflated responses key '(.+)' item '(\d+)' key '(.+)' should be '(.+)'$/ do |key, index, sub_key, to_equal|
+ inflated_response[key][index.to_i][sub_key].should == to_equal
+end
+
+Then /^the inflated responses key '(.+)' should be '(\d+)' items long$/ do |key, length|
+ inflated_response[key].length.should == length.to_i
end
Then /^the inflated responses key '(.+)' should not exist$/ do |key|
- inflated_response.has_key?(key).should == false
+ self.inflated_response.has_key?(key).should == false
end
Then /^the inflated responses key '(.+)' should exist$/ do |key|
- inflated_response.has_key?(key).should == true
+ self.inflated_response.has_key?(key).should == true
end
Then /^the inflated response should be an empty array$/ do
- inflated_response.should == []
+ self.inflated_response.should == []
end
Then /^the inflated response should include '(.+)'$/ do |entry|
- inflated_response.detect { |n| n =~ /#{entry}/ }.should be(true)
+ self.inflated_response.detect { |n| n =~ /#{entry}/ }.should be(true)
end
Then /^the inflated response should be '(.+)' items long$/ do |length|
- inflated_response.length.should == length.to_i
+ self.inflated_response.length.should == length.to_i
end
Then /^the '(.+)' header should match '(.+)'$/ do |header, regex|
- response.headers[header].should =~ /#{regex}/
+ self.response.headers[header].should =~ /#{regex}/
end
Then /^the inflated responses key '(.+)' should include '(.+)'$/ do |key, regex|
- inflated_response[key].detect { |n| n =~ /#{regex}/ }.should be(true)
+ self.inflated_response[key].detect { |n| n =~ /#{regex}/ }.should be(true)
end
Then /^the inflated response should match the '(.+)'$/ do |stash_name|
stash[stash_name].each do |k,v|
- inflated_response[k.to_s].should == v
+ self.inflated_response[k.to_s].should == v
end
end
Then /^the inflated response should be the '(.+)'$/ do |stash_key|
- stash[stash_key].should == inflated_response
+ stash[stash_key].should == self.inflated_response
end
Then /^the inflated response should be a kind of '(.+)'$/ do |thing|
- inflated_response.should be_a_kind_of(thing)
+ self.inflated_response.should be_a_kind_of(thing)
end
Then /^the inflated response should respond to '(.+)' with '(.+)'$/ do |method, to_match|
to_match = JSON.parse(to_match) if to_match =~ /^\[|\{/
- inflated_response.send(method.to_sym).should == to_match
+ self.inflated_response.send(method.to_sym).should == to_match
end
Then /^the inflated response should respond to '(.+)' and match '(.+)'$/ do |method, to_match|
- inflated_response.send(method.to_sym).should == to_match
+ self.inflated_response.send(method.to_sym).should == to_match
end
Then /^the fields in the inflated response should match the '(.+)'$/ do |stash_name|
- inflated_response.each do |k,v|
+ self.inflated_response.each do |k,v|
unless k =~ /^_/ || k == 'couchrest-type'
stash[stash_name][k.to_sym].should == v
end
end
end
+Then /^the data_bag named '(.+)' should not have an item named '(.+)'$/ do |data_bag, item|
+ exists = true
+ begin
+ Chef::DataBagItem.load(data_bag, item, @couchdb)
+ rescue
+ exists = false
+ end
+ exists.should == false
+end
diff --git a/features/steps/run_client_steps.rb b/features/steps/run_client_steps.rb
index 815c5c7148..25726f59aa 100644
--- a/features/steps/run_client_steps.rb
+++ b/features/steps/run_client_steps.rb
@@ -24,7 +24,7 @@ When /^I run the chef\-client$/ do
@chef_args ||= ""
@config_file ||= File.expand_path(File.join(File.dirname(__FILE__), '..', 'data', 'config', 'client.rb'))
status = Chef::Mixin::Command.popen4(
- "chef-client -l #{@log_level} -c #{@config_file} #{@chef_args}") do |p, i, o, e|
+ "#{File.join(File.dirname(__FILE__), "..", "..", "chef", "bin", "chef-client")} -l #{@log_level} -c #{@config_file} #{@chef_args}") do |p, i, o, e|
@stdout = o.gets(nil)
@stderr = e.gets(nil)
end
@@ -76,7 +76,7 @@ openid_url "http://127.0.0.1:4001"
template_url "http://127.0.0.1:4000"
remotefile_url "http://127.0.0.1:4000"
search_url "http://127.0.0.1:4000"
-couchdb_database 'chef_integration'
+couchdb_database 'chef'
CONFIG
@config_file = File.expand_path(File.join(File.dirname(__FILE__), '..', 'data', 'config', 'client-with-logging.rb'))
@@ -86,12 +86,11 @@ CONFIG
self.cleanup_files << @config_file
- @status = Chef::Mixin::Command.popen4("chef-client -c #{@config_file}") do |p, i, o, e|
+
+ @status = Chef::Mixin::Command.popen4("#{File.join(File.dirname(__FILE__), "..", "..", "chef", "bin", "chef-client")} -l #{@log_level} -c #{@config_file} #{@chef_args}") do |p, i, o, e|
@stdout = o.gets(nil)
@stderr = e.gets(nil)
end
-
-
end
###
diff --git a/features/steps/run_solo.rb b/features/steps/run_solo.rb
index e5397b853d..b2b244cfb6 100644
--- a/features/steps/run_solo.rb
+++ b/features/steps/run_solo.rb
@@ -26,7 +26,7 @@ When /^I run chef-solo with the '(.+)' recipe$/ do |recipe_name|
cleanup_files << config_file
binary_path = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'chef', 'bin', 'chef-solo'))
- command = "chef-solo -c #{config_file} -j #{dna_file}"
+ command = "#{binary_path} -c #{config_file} -j #{dna_file}"
command += " -l debug" if ENV['LOG_LEVEL'] == 'debug'
# Run it
diff --git a/features/steps/webrat_steps.rb b/features/steps/webrat_steps.rb
index c7f78a5406..b152a1501d 100644
--- a/features/steps/webrat_steps.rb
+++ b/features/steps/webrat_steps.rb
@@ -36,4 +36,4 @@ end
When /^I attach the file at "(.*)" to "(.*)" $/ do |path, field|
attach_file(field, path)
end
- \ No newline at end of file
+
diff --git a/features/support/env.rb b/features/support/env.rb
index 2a5b217d29..ca60753df2 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -16,7 +16,7 @@
# limitations under the License.
#
-%w{chef chef-server chef-server-slice}.each do |inc_dir|
+%w{chef chef-server chef-server-slice chef-solr}.each do |inc_dir|
$: << File.join(File.dirname(__FILE__), '..', '..', inc_dir, 'lib')
end
@@ -25,22 +25,92 @@ require 'spec'
require 'chef'
require 'chef/config'
require 'chef/client'
+require 'chef/data_bag'
+require 'chef/data_bag_item'
+require 'chef/solr'
require 'tmpdir'
require 'merb-core'
require 'merb_cucumber/world/webrat'
+require 'opscode/audit'
+require 'chef/streaming_cookbook_uploader'
def Spec.run? ; true; end
-Chef::Config.from_file(File.join(File.dirname(__FILE__), '..', 'data', 'config', 'server.rb'))
-Chef::Config[:log_level] = :error
-Ohai::Config[:log_level] = :error
+ENV['LOG_LEVEL'] ||= 'error'
+
+def setup_logging
+ Chef::Config.from_file(File.join(File.dirname(__FILE__), '..', 'data', 'config', 'server.rb'))
+ Merb.logger.auto_flush = true
+ if ENV['DEBUG'] == 'true' || ENV['LOG_LEVEL'] == 'debug'
+ Chef::Config[:log_level] = :debug
+ Chef::Log.level(:debug)
+ Merb.logger.set_log(STDOUT, :debug)
+ else
+ Chef::Config[:log_level] = ENV['LOG_LEVEL'].to_sym
+ Chef::Log.level(ENV['LOG_LEVEL'].to_sym)
+ Merb.logger.set_log(STDOUT, ENV['LOG_LEVEL'].to_sym)
+ end
+ Nanite::Log.logger = Mixlib::Auth::Log.logger = Ohai::Log.logger = Chef::Log.logger
+end
+
+def setup_nanite
+ Chef::Config[:nanite_identity] = "chef-integration-test"
+ Chef::Nanite.in_event { Chef::Log.debug("Nanite is up!") }
+ Chef::Log.debug("Waiting for Nanites to register with us as a mapper")
+ sleep 10
+end
+
+def delete_databases
+ c = Chef::REST.new(Chef::Config[:couchdb_url], nil, nil)
+ %w{chef_integration}.each do |db|
+ begin
+ c.delete_rest("#{db}/")
+ rescue
+ end
+ end
+end
-if ENV['DEBUG'] = 'true'
- Merb.logger.set_log(STDOUT, :debug) if ENV['DEBUG'] = 'true'
-else
- Merb.logger.set_log(STDOUT, :error)
+def create_databases
+ Chef::Log.info("Creating bootstrap databases")
+ cdb = Chef::CouchDB.new(Chef::Config[:couchdb_url], "chef_integration")
+ cdb.create_db
+ Chef::Node.create_design_document
+ Chef::Role.create_design_document
+ Chef::DataBag.create_design_document
+ Chef::Role.sync_from_disk_to_couchdb
end
+
+def create_validation
+# TODO: Create the validation certificate here
+ File.open("#{Dir.tmpdir}/validation.pem", "w") do |f|
+ f.print response["private_key"]
+ end
+end
+
+def prepare_replicas
+ c = Chef::REST.new(Chef::Config[:couchdb_url], nil, nil)
+ c.put_rest("chef_integration_safe/", nil)
+ c.post_rest("_replicate", { "source" => "#{Chef::Config[:couchdb_url]}/chef_integration", "target" => "#{Chef::Config[:couchdb_url]}/chef_integration_safe" })
+ c.delete_rest("chef_integration")
+end
+
+at_exit do
+ c = Chef::REST.new(Chef::Config[:couchdb_url], nil, nil)
+ c.delete_rest("chef_integration_safe")
+ File.unlink(File.join(Dir.tmpdir, "validation.pem"))
+end
+
+###
+# Pre-testing setup
+###
+setup_logging
+setup_nanite
+delete_databases
+create_databases
+create_validation
+prepare_replicas
+
Merb.start_environment(
:merb_root => File.join(File.dirname(__FILE__), "..", "..", "chef-server"),
:testing => true,
@@ -55,6 +125,11 @@ Spec::Runner.configure do |config|
config.include(Merb::Test::ControllerHelper)
end
+Chef::Log.info("Ready to run tests")
+
+###
+# The Cucumber World
+###
module ChefWorld
attr_accessor :recipe, :cookbook, :response, :inflated_response, :log_level, :chef_args, :config_file, :stdout, :stderr, :status, :exception
@@ -64,7 +139,7 @@ module ChefWorld
end
def rest
- @rest ||= Chef::REST.new('http://localhost:4000')
+ @rest ||= Chef::REST.new('http://localhost:4000/organizations/clownco', nil, nil)
end
def tmpdir
@@ -91,7 +166,23 @@ end
World(ChefWorld)
+Before do
+ system("mkdir -p #{tmpdir}")
+ system("cp -r #{File.join(Dir.tmpdir, "validation.pem")} #{File.join(tmpdir, "validation.pem")}")
+ Chef::CouchDB.new(Chef::Config[:couchdb_url], "chef_integration").create_db
+ c = Chef::REST.new(Chef::Config[:couchdb_url], nil, nil)
+ c.post_rest("_replicate", {
+ "source" => "#{Chef::Config[:couchdb_url]}/chef_integration_safe",
+ "target" => "#{Chef::Config[:couchdb_url]}/chef_integration"
+ })
+end
+
After do
+ r = Chef::REST.new(Chef::Config[:couchdb_url], nil, nil)
+ r.delete_rest("chef_integration/")
+ s = Chef::Solr.new
+ s.solr_delete_by_query("*:*")
+ s.solr_commit
cleanup_files.each do |file|
system("rm #{file}")
end
@@ -104,5 +195,6 @@ After do
end
data_tmp = File.join(File.dirname(__FILE__), "..", "data", "tmp")
system("rm -rf #{data_tmp}/*")
+ system("rm -rf #{tmpdir}")
end